From lldb-commits at lists.llvm.org Mon May 5 01:09:11 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Mon, 05 May 2025 01:09:11 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fix block address resolution for functions in multiple sections (PR #137955) In-Reply-To: Message-ID: <68187227.050a0220.efbb2.988b@mx.google.com> https://github.com/labath updated https://github.com/llvm/llvm-project/pull/137955 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Mon May 5 01:09:16 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Mon, 05 May 2025 01:09:16 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fix block address resolution for functions in multiple sections (PR #137955) In-Reply-To: Message-ID: <6818722c.170a0220.2f23c7.3abe@mx.google.com> ================ @@ -283,39 +283,42 @@ uint32_t Block::GetRangeIndexContainingAddress(const Address &addr) { return m_ranges.FindEntryIndexThatContains(file_addr - func_file_addr); } +static AddressRange ToAddressRange(const Address &func_addr, + const Block::Range &range) { + assert(func_addr.GetModule()); + return AddressRange(func_addr.GetFileAddress() + range.base, range.size, ---------------- labath wrote: It is. I've added it to the header to make it more visible. https://github.com/llvm/llvm-project/pull/137955 From lldb-commits at lists.llvm.org Mon May 5 01:38:46 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Mon, 05 May 2025 01:38:46 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Fix GetIndexOfChildMemberWithName to handle anonymous structs. (PR #138487) In-Reply-To: Message-ID: <68187916.050a0220.221863.bd13@mx.google.com> labath wrote: I'm not intimately familiar with this code, but I am somewhat suspicious of implementation. Adding the number of base classes to the result makes sense to me. What surprises me is that this should be done only for fields with anonymous types. Can you explain why should the index of a field depend on whether its type is anonymous or not? I am also surprised by the hardcoding of `omit_empty_base_classes` to true in the `GetNumBaseClasses` call. All of the other calls in this function pass the value from the argument. Why should this one be special? https://github.com/llvm/llvm-project/pull/138487 From lldb-commits at lists.llvm.org Mon May 5 02:02:17 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Mon, 05 May 2025 02:02:17 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Inherit DuplicateFileAction(HANDLE, HANDLE) handles on windows (PR #137978) In-Reply-To: Message-ID: <68187e99.170a0220.1dc8c8.479b@mx.google.com> ================ @@ -122,8 +123,14 @@ struct ForkLaunchInfo { ExitWithError(error_fd, "close"); break; case FileAction::eFileActionDuplicate: - if (dup2(action.fd, action.arg) == -1) - ExitWithError(error_fd, "dup2"); + if (action.fd != action.arg) { ---------------- labath wrote: Can you explain your reasoning? This is something I have considered, and I came to the opposite conclusion. The reason is that I think this fits nicely into the `DuplicateFileAction(from, to)` interface. You're saying "I want to take fd `from` from my process and pass it as fd `to` to the child process". That statement makes sense regardless of whether `from` and `to` have the same value or not. The fact that one needs to use a different sequence of calls to achieve this effect is (can be) an implementation detail. Imagine I have some file descriptor open, and for whatever reason, I want to pass it to the child process exactly as fd 47. That's something I can freely do, as I control all of the FDs of the child process, but if this were to be controlled by a separate flag, I would have to write special code to handle the case where the my own copy of that FD happens to be 47: ``` if (fd_I_want_to_pass != 47) info.AppendDuplicateFileAction(47, fd_I_want_to_pass); else info.InheritFileAction(47); ``` >From a windows point of view, I agree that a separate action makes sense, but if we can agree that the above makes sense for posix, then I think it also makes sense to reuse the same thing here (we're sort of doing the same thing, except that we only support the case where the two numbers are the same). That said, since the `from==to` is going to be the only case that is going to work in cross-platform code, I can imagine having some wrapper/helper function, which lets you avoid specifying the same FD twice, but then calls `DuplicateFileAction(fd, fd)` under the hood. Would that address your concerns? https://github.com/llvm/llvm-project/pull/137978 From lldb-commits at lists.llvm.org Mon May 5 02:22:33 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Mon, 05 May 2025 02:22:33 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Inherit DuplicateFileAction(HANDLE, HANDLE) handles on windows (PR #137978) In-Reply-To: Message-ID: <68188359.050a0220.48da2.9985@mx.google.com> ================ @@ -274,10 +274,8 @@ static Status spawn_process(const char *progname, const FileSpec &prog, self_args.AppendArgument(llvm::StringRef("platform")); self_args.AppendArgument(llvm::StringRef("--child-platform-fd")); self_args.AppendArgument(llvm::to_string(shared_socket.GetSendableFD())); -#ifndef _WIN32 launch_info.AppendDuplicateFileAction((int)shared_socket.GetSendableFD(), (int)shared_socket.GetSendableFD()); ---------------- labath wrote: I don't understand what's the problem with that. I'm pretty sure that zero is not a valid handle value (https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443), so it can't conflict with STDIN_FILENO. Relatedly, I had an idea which might alleviate some of your concerns. While I firmly believe that getting right of the `child_process_inherit` constructor argument is the right thing to do, I'm not entirely happy that this results in making *all* pipe handles inheritable. In the description I propose creating a helper function which creates a copy of the handle which can be inherited. On windows this would call `DuplicateHandle` with `bInheritHandle=TRUE` and on posix it would be a no-op (as we're able to change the CLOEXEC flag on the fd later). If we had that, we could also easily ensure that the returned handle does not conflict with our "reserved" values -- if `DuplicateHandle` returns 0/1/2, just call it again until it returns something bigger. It's kinda gross, but I also somehow like it :P https://github.com/llvm/llvm-project/pull/137978 From lldb-commits at lists.llvm.org Mon May 5 02:24:46 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Mon, 05 May 2025 02:24:46 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb/Host] Enable inheriting "non-inheritable" FDs (PR #126935) In-Reply-To: Message-ID: <681883de.170a0220.32cf8f.57ea@mx.google.com> labath wrote: Correct. :) https://github.com/llvm/llvm-project/pull/126935 From lldb-commits at lists.llvm.org Mon May 5 02:32:15 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Mon, 05 May 2025 02:32:15 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Ptrace seize dead process (PR #137041) In-Reply-To: Message-ID: <6818859f.170a0220.5949e.393c@mx.google.com> labath wrote: > Move the Proc Status (not stat) code to the HOST class I'd put this first (in which case it wouldn't be called "move" but "extend" or "refactor"), for two reasons: - it reduces the chance of ending up with two parsers - I'm not very happy with the implementation you have here. I think using structured data is overkill and makes using it more complicated. Since this is an internal API, and we don't have to worry about stability, I think a struct with a bool field (or `optional` if you need to treat "not present" differently) would be better. (That's also more-or-less what the existing implementation does) https://github.com/llvm/llvm-project/pull/137041 From lldb-commits at lists.llvm.org Mon May 5 03:19:47 2025 From: lldb-commits at lists.llvm.org (Hu Jialun via lldb-commits) Date: Mon, 05 May 2025 03:19:47 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Implement `runInTerminal` for Windows (PR #121269) In-Reply-To: Message-ID: <681890c3.170a0220.31a28f.0399@mx.google.com> https://github.com/SuibianP updated https://github.com/llvm/llvm-project/pull/121269 >From f9675fbe23abe59c3cf56a6263b7a78ec20e42d4 Mon Sep 17 00:00:00 2001 From: Jialun Hu Date: Mon, 24 Feb 2025 22:10:17 +0800 Subject: [PATCH] [lldb-dap] Implement runInTerminal for Windows Currently, the named pipe is passed by name and a transient ofstream is constructed at each I/O request. This assumes, - Blocking semantics: FIFO I/O waits for the other side to connect. - Buffered semantics: Closing one side does not discard existing data. The former can be replaced by WaitNamedPipe/ConnectNamedPipe on Win32, but the second cannot be easily worked around. It is also impossible to have another "keep-alive" pipe server instance, as server-client pairs are fixed on connection on Win32 and the client may get connected to it instead of the real one. Refactor FifoFile[IO] to use an open file handles rather than file name. --- Win32 provides no way to replace the process image. Under the hood exec* actually creates a new process with a new PID. DebugActiveProcess also cannot get notified of process creations. Create the new process in a suspended state and resume it after attach. --- lldb/packages/Python/lldbsuite/test/dotest.py | 2 +- .../API/tools/lldb-dap/runInTerminal/Makefile | 2 +- .../runInTerminal/TestDAP_runInTerminal.py | 5 +- .../API/tools/lldb-dap/runInTerminal/main.c | 11 -- .../API/tools/lldb-dap/runInTerminal/main.cpp | 13 ++ lldb/tools/lldb-dap/FifoFiles.cpp | 131 +++++++++++++++--- lldb/tools/lldb-dap/FifoFiles.h | 35 +++-- .../tools/lldb-dap/Handler/RequestHandler.cpp | 8 +- lldb/tools/lldb-dap/JSONUtils.cpp | 2 +- lldb/tools/lldb-dap/RunInTerminal.cpp | 45 +++--- lldb/tools/lldb-dap/RunInTerminal.h | 11 +- lldb/tools/lldb-dap/lldb-dap.cpp | 63 +++++++-- 12 files changed, 244 insertions(+), 84 deletions(-) delete mode 100644 lldb/test/API/tools/lldb-dap/runInTerminal/main.c create mode 100644 lldb/test/API/tools/lldb-dap/runInTerminal/main.cpp diff --git a/lldb/packages/Python/lldbsuite/test/dotest.py b/lldb/packages/Python/lldbsuite/test/dotest.py index 7cc8f2985043e..01b161733d62e 100644 --- a/lldb/packages/Python/lldbsuite/test/dotest.py +++ b/lldb/packages/Python/lldbsuite/test/dotest.py @@ -547,7 +547,7 @@ def setupSysPath(): lldbDir = os.path.dirname(lldbtest_config.lldbExec) - lldbDAPExec = os.path.join(lldbDir, "lldb-dap") + lldbDAPExec = os.path.join(lldbDir, "lldb-dap.exe" if os.name == "nt" else "lldb-dap") if is_exe(lldbDAPExec): os.environ["LLDBDAP_EXEC"] = lldbDAPExec diff --git a/lldb/test/API/tools/lldb-dap/runInTerminal/Makefile b/lldb/test/API/tools/lldb-dap/runInTerminal/Makefile index 10495940055b6..99998b20bcb05 100644 --- a/lldb/test/API/tools/lldb-dap/runInTerminal/Makefile +++ b/lldb/test/API/tools/lldb-dap/runInTerminal/Makefile @@ -1,3 +1,3 @@ -C_SOURCES := main.c +CXX_SOURCES := main.cpp include Makefile.rules diff --git a/lldb/test/API/tools/lldb-dap/runInTerminal/TestDAP_runInTerminal.py b/lldb/test/API/tools/lldb-dap/runInTerminal/TestDAP_runInTerminal.py index 9aab7ca3293db..3a47202c5e0b6 100644 --- a/lldb/test/API/tools/lldb-dap/runInTerminal/TestDAP_runInTerminal.py +++ b/lldb/test/API/tools/lldb-dap/runInTerminal/TestDAP_runInTerminal.py @@ -43,7 +43,6 @@ def isTestSupported(self): except: return False - @skipIfWindows @skipIf(oslist=["linux"], archs=no_match(["x86_64"])) def test_runInTerminal(self): if not self.isTestSupported(): @@ -53,7 +52,7 @@ def test_runInTerminal(self): launch the inferior with the correct environment variables and arguments. """ program = self.getBuildArtifact("a.out") - source = "main.c" + source = "main.cpp" self.build_and_launch( program, runInTerminal=True, args=["foobar"], env=["FOO=bar"] ) @@ -113,7 +112,6 @@ def test_runInTerminalWithObjectEnv(self): self.assertIn("FOO", request_envs) self.assertEqual("BAR", request_envs["FOO"]) - @skipIfWindows @skipIf(oslist=["linux"], archs=no_match(["x86_64"])) def test_runInTerminalInvalidTarget(self): if not self.isTestSupported(): @@ -132,7 +130,6 @@ def test_runInTerminalInvalidTarget(self): response["message"], ) - @skipIfWindows @skipIf(oslist=["linux"], archs=no_match(["x86_64"])) def test_missingArgInRunInTerminalLauncher(self): if not self.isTestSupported(): diff --git a/lldb/test/API/tools/lldb-dap/runInTerminal/main.c b/lldb/test/API/tools/lldb-dap/runInTerminal/main.c deleted file mode 100644 index 676bd830e657b..0000000000000 --- a/lldb/test/API/tools/lldb-dap/runInTerminal/main.c +++ /dev/null @@ -1,11 +0,0 @@ -#include -#include -#include - -int main(int argc, char *argv[]) { - const char *foo = getenv("FOO"); - for (int counter = 1;; counter++) { - sleep(1); // breakpoint - } - return 0; -} diff --git a/lldb/test/API/tools/lldb-dap/runInTerminal/main.cpp b/lldb/test/API/tools/lldb-dap/runInTerminal/main.cpp new file mode 100644 index 0000000000000..4e7812a3b6398 --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/runInTerminal/main.cpp @@ -0,0 +1,13 @@ +#include +#include +#include + +using namespace std; + +int main(int argc, char *argv[]) { + const char *foo = getenv("FOO"); + for (int counter = 1;; counter++) { + this_thread::sleep_for(chrono::seconds(1)); // breakpoint + } + return 0; +} diff --git a/lldb/tools/lldb-dap/FifoFiles.cpp b/lldb/tools/lldb-dap/FifoFiles.cpp index 1f1bba80bd3b1..8069049d78519 100644 --- a/lldb/tools/lldb-dap/FifoFiles.cpp +++ b/lldb/tools/lldb-dap/FifoFiles.cpp @@ -8,8 +8,15 @@ #include "FifoFiles.h" #include "JSONUtils.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/WindowsError.h" -#if !defined(_WIN32) +#if defined(_WIN32) +#include +#include +#include +#else #include #include #include @@ -24,27 +31,76 @@ using namespace llvm; namespace lldb_dap { -FifoFile::FifoFile(StringRef path) : m_path(path) {} +FifoFile::FifoFile(std::string path, FILE *f) : m_path(path), m_file(f) {} + +Expected FifoFile::create(StringRef path) { + auto file = fopen(path.data(), "r+"); + if (file == nullptr) + return createStringError(inconvertibleErrorCode(), + "Failed to open fifo file " + path); + if (setvbuf(file, NULL, _IONBF, 0)) + return createStringError(inconvertibleErrorCode(), + "Failed to setvbuf on fifo file " + path); + return FifoFile(path, file); +} + +FifoFile::FifoFile(StringRef path, FILE *f) : m_path(path), m_file(f) {} +FifoFile::FifoFile(FifoFile &&other) + : m_path(other.m_path), m_file(other.m_file) { + other.m_path.clear(); + other.m_file = nullptr; +} FifoFile::~FifoFile() { + if (m_file) + fclose(m_file); #if !defined(_WIN32) + // Unreferenced named pipes are deleted automatically on Win32 unlink(m_path.c_str()); #endif } -Expected> CreateFifoFile(StringRef path) { -#if defined(_WIN32) - return createStringError(inconvertibleErrorCode(), "Unimplemented"); +// This probably belongs to llvm::sys::fs as another FSEntity type +Error createUniqueNamedPipe(const Twine &prefix, StringRef suffix, + int &result_fd, + SmallVectorImpl &result_path) { + std::error_code EC; +#ifdef _WIN32 + const char *middle = suffix.empty() ? "-%%%%%%" : "-%%%%%%."; + EC = sys::fs::getPotentiallyUniqueFileName( + "\\\\.\\pipe\\LOCAL\\" + prefix + middle + suffix, result_path); +#else + EC = sys::fs::getPotentiallyUniqueTempFileName(prefix, suffix, result_path); +#endif + if (EC) + return errorCodeToError(EC); + result_path.push_back(0); + const char *path = result_path.data(); +#ifdef _WIN32 + HANDLE h = ::CreateNamedPipeA( + path, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 512, 512, 0, NULL); + if (h == INVALID_HANDLE_VALUE) + return createStringError(mapLastWindowsError(), "CreateNamedPipe"); + result_fd = _open_osfhandle((intptr_t)h, _O_TEXT | _O_RDWR); + if (result_fd == -1) + return createStringError(mapLastWindowsError(), "_open_osfhandle"); #else - if (int err = mkfifo(path.data(), 0600)) - return createStringError(std::error_code(err, std::generic_category()), - "Couldn't create fifo file: %s", path.data()); - return std::make_shared(path); + if (mkfifo(path, 0600) == -1) + return createStringError(std::error_code(errno, std::generic_category()), + "mkfifo"); + EC = openFileForWrite(result_path, result_fd, sys::fs::CD_OpenExisting, + sys::fs::OF_None, 0600); + if (EC) + return errorCodeToError(EC); #endif + result_path.pop_back(); + return Error::success(); } -FifoFileIO::FifoFileIO(StringRef fifo_file, StringRef other_endpoint_name) - : m_fifo_file(fifo_file), m_other_endpoint_name(other_endpoint_name) {} +FifoFileIO::FifoFileIO(FifoFile &&fifo_file, StringRef other_endpoint_name) + : m_fifo_file(std::move(fifo_file)), + m_other_endpoint_name(other_endpoint_name) {} Expected FifoFileIO::ReadJSON(std::chrono::milliseconds timeout) { // We use a pointer for this future, because otherwise its normal destructor @@ -52,13 +108,28 @@ Expected FifoFileIO::ReadJSON(std::chrono::milliseconds timeout) { std::optional line; std::future *future = new std::future(std::async(std::launch::async, [&]() { - std::ifstream reader(m_fifo_file, std::ifstream::in); - std::string buffer; - std::getline(reader, buffer); - if (!buffer.empty()) - line = buffer; + rewind(m_fifo_file.m_file); + /* + * The types of messages passing through the fifo are: + * {"kind": "pid", "pid": 9223372036854775807} + * {"kind": "didAttach"} + * {"kind": "error", "error": "error message"} + * + * 512 characters should be more than enough. + */ + constexpr size_t buffer_size = 512; + char buffer[buffer_size]; + char *ptr = fgets(buffer, buffer_size, m_fifo_file.m_file); + if (ptr == nullptr || *ptr == 0) + return; + size_t len = strlen(buffer); + if (len <= 1) + return; + buffer[len - 1] = '\0'; // remove newline + line = buffer; })); - if (future->wait_for(timeout) == std::future_status::timeout || !line) + + if (future->wait_for(timeout) == std::future_status::timeout) // Indeed this is a leak, but it's intentional. "future" obj destructor // will block on waiting for the worker thread to join. And the worker // thread might be stuck in blocking I/O. Intentionally leaking the obj @@ -69,6 +140,11 @@ Expected FifoFileIO::ReadJSON(std::chrono::milliseconds timeout) { return createStringError(inconvertibleErrorCode(), "Timed out trying to get messages from the " + m_other_endpoint_name); + if (!line) { + return createStringError(inconvertibleErrorCode(), + "Failed to get messages from the " + + m_other_endpoint_name); + } delete future; return json::parse(*line); } @@ -78,8 +154,12 @@ Error FifoFileIO::SendJSON(const json::Value &json, bool done = false; std::future *future = new std::future(std::async(std::launch::async, [&]() { - std::ofstream writer(m_fifo_file, std::ofstream::out); - writer << JSONToString(json) << std::endl; + rewind(m_fifo_file.m_file); + auto payload = JSONToString(json); + if (fputs(payload.c_str(), m_fifo_file.m_file) == EOF || + fputc('\n', m_fifo_file.m_file) == EOF) { + return; + } done = true; })); if (future->wait_for(timeout) == std::future_status::timeout || !done) { @@ -98,4 +178,17 @@ Error FifoFileIO::SendJSON(const json::Value &json, return Error::success(); } +Error FifoFileIO::WaitForPeer() { +#ifdef _WIN32 + if (!::ConnectNamedPipe((HANDLE)_get_osfhandle(fileno(m_fifo_file.m_file)), + NULL) && + GetLastError() != ERROR_PIPE_CONNECTED) { + return createStringError(mapLastWindowsError(), + "Failed to connect to the " + + m_other_endpoint_name); + } +#endif + return Error::success(); +} + } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/FifoFiles.h b/lldb/tools/lldb-dap/FifoFiles.h index 633ebeb2aedd4..99cb75e1343d2 100644 --- a/lldb/tools/lldb-dap/FifoFiles.h +++ b/lldb/tools/lldb-dap/FifoFiles.h @@ -11,8 +11,10 @@ #include "llvm/Support/Error.h" #include "llvm/Support/JSON.h" +#include "llvm/Support/raw_ostream.h" #include +#include namespace lldb_dap { @@ -20,22 +22,33 @@ namespace lldb_dap { /// /// The file is destroyed when the destructor is invoked. struct FifoFile { - FifoFile(llvm::StringRef path); + FifoFile(FifoFile &&other); + + FifoFile(const FifoFile &) = delete; + FifoFile &operator=(const FifoFile &) = delete; ~FifoFile(); + static llvm::Expected create(llvm::StringRef path); + FifoFile(llvm::StringRef path, FILE *f); + std::string m_path; + FILE *m_file; + +private: + FifoFile(std::string path, FILE *f); }; -/// Create a fifo file in the filesystem. +/// Create and open a named pipe with a unique name. /// -/// \param[in] path -/// The path for the fifo file. +/// The arguments have identical meanings to those of +/// llvm::sys::fs::createTemporaryFile. /// -/// \return -/// A \a std::shared_ptr if the file could be created, or an -/// \a llvm::Error in case of failures. -llvm::Expected> CreateFifoFile(llvm::StringRef path); +/// Note that the resulting filename is further prepended by \c \\.\pipe\\LOCAL\ +/// on Windows and the native temp directory on other platforms. +llvm::Error createUniqueNamedPipe(const llvm::Twine &prefix, + llvm::StringRef suffix, int &result_fd, + llvm::SmallVectorImpl &result_path); class FifoFileIO { public: @@ -45,7 +58,7 @@ class FifoFileIO { /// \param[in] other_endpoint_name /// A human readable name for the other endpoint that will communicate /// using this file. This is used for error messages. - FifoFileIO(llvm::StringRef fifo_file, llvm::StringRef other_endpoint_name); + FifoFileIO(FifoFile &&fifo_file, llvm::StringRef other_endpoint_name); /// Read the next JSON object from the underlying input fifo file. /// @@ -75,8 +88,10 @@ class FifoFileIO { const llvm::json::Value &json, std::chrono::milliseconds timeout = std::chrono::milliseconds(20000)); + llvm::Error WaitForPeer(); + private: - std::string m_fifo_file; + FifoFile m_fifo_file; std::string m_other_endpoint_name; }; diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp index 7a75cd93abc19..f30915d9958d3 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp @@ -120,8 +120,9 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) { if (!comm_file_or_err) return comm_file_or_err.takeError(); FifoFile &comm_file = *comm_file_or_err.get(); + std::string comm_filename = comm_file.m_path; - RunInTerminalDebugAdapterCommChannel comm_channel(comm_file.m_path); + RunInTerminalDebugAdapterCommChannel comm_channel(comm_file); lldb::pid_t debugger_pid = LLDB_INVALID_PROCESS_ID; #if !defined(_WIN32) @@ -130,10 +131,13 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) { llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest( *arguments.configuration.program, arguments.args, arguments.env, - arguments.cwd.value_or(""), comm_file.m_path, debugger_pid); + arguments.cwd.value_or(""), comm_filename, debugger_pid); dap.SendReverseRequest("runInTerminal", std::move(reverse_request)); + auto err = comm_channel.WaitForLauncher(); + if (err) + return err; if (llvm::Expected pid = comm_channel.GetLauncherPid()) attach_info.SetProcessID(*pid); else diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp index 4409cf5b27e5b..f29ab09e4feb9 100644 --- a/lldb/tools/lldb-dap/JSONUtils.cpp +++ b/lldb/tools/lldb-dap/JSONUtils.cpp @@ -1463,7 +1463,7 @@ llvm::json::Object CreateRunInTerminalReverseRequest( req_args.push_back("--launch-target"); req_args.push_back(program.str()); req_args.insert(req_args.end(), args.begin(), args.end()); - run_in_terminal_args.try_emplace("args", args); + run_in_terminal_args.try_emplace("args", req_args); if (!cwd.empty()) run_in_terminal_args.try_emplace("cwd", cwd); diff --git a/lldb/tools/lldb-dap/RunInTerminal.cpp b/lldb/tools/lldb-dap/RunInTerminal.cpp index 9f309dd78221a..8cee9456e2ba2 100644 --- a/lldb/tools/lldb-dap/RunInTerminal.cpp +++ b/lldb/tools/lldb-dap/RunInTerminal.cpp @@ -9,12 +9,6 @@ #include "RunInTerminal.h" #include "JSONUtils.h" -#if !defined(_WIN32) -#include -#include -#include -#endif - #include #include @@ -96,8 +90,8 @@ static Error ToError(const RunInTerminalMessage &message) { } RunInTerminalLauncherCommChannel::RunInTerminalLauncherCommChannel( - StringRef comm_file) - : m_io(comm_file, "debug adapter") {} + FifoFile &comm_file) + : m_io(std::move(comm_file), "debug adapter") {} Error RunInTerminalLauncherCommChannel::WaitUntilDebugAdapterAttaches( std::chrono::milliseconds timeout) { @@ -111,8 +105,8 @@ Error RunInTerminalLauncherCommChannel::WaitUntilDebugAdapterAttaches( return message.takeError(); } -Error RunInTerminalLauncherCommChannel::NotifyPid() { - return m_io.SendJSON(RunInTerminalMessagePid(getpid()).ToJSON()); +Error RunInTerminalLauncherCommChannel::NotifyPid(lldb::pid_t pid) { + return m_io.SendJSON(RunInTerminalMessagePid(pid).ToJSON()); } void RunInTerminalLauncherCommChannel::NotifyError(StringRef error) { @@ -122,8 +116,12 @@ void RunInTerminalLauncherCommChannel::NotifyError(StringRef error) { } RunInTerminalDebugAdapterCommChannel::RunInTerminalDebugAdapterCommChannel( - StringRef comm_file) - : m_io(comm_file, "runInTerminal launcher") {} + FifoFile &comm_file) + : m_io(std::move(comm_file), "runInTerminal launcher") {} + +Error RunInTerminalDebugAdapterCommChannel::WaitForLauncher() { + return m_io.WaitForPeer(); +} // Can't use \a std::future because it doesn't compile on Windows std::future @@ -158,13 +156,24 @@ std::string RunInTerminalDebugAdapterCommChannel::GetLauncherError() { } Expected> CreateRunInTerminalCommFile() { + int comm_fd; SmallString<256> comm_file; - if (std::error_code EC = sys::fs::getPotentiallyUniqueTempFileName( - "lldb-dap-run-in-terminal-comm", "", comm_file)) - return createStringError(EC, "Error making unique file name for " - "runInTerminal communication files"); - - return CreateFifoFile(comm_file.str()); + if (auto error = createUniqueNamedPipe("lldb-dap-run-in-terminal-comm", "", + comm_fd, comm_file)) + return error; + FILE *cf = fdopen(comm_fd, "r+"); + // There is no portable way to conjure an ofstream from HANDLE, so use FILE * + // llvm::raw_fd_stream does not support getline() and there is no + // llvm::buffer_istream + + if (cf == NULL) + return createStringError(std::error_code(errno, std::generic_category()), + "Error converting file descriptor to C FILE for " + "runInTerminal comm-file"); + if (setvbuf(cf, NULL, _IONBF, 0)) + return createStringError(std::error_code(errno, std::generic_category()), + "Error setting unbuffered mode on C FILE"); + return std::make_shared(comm_file, cf); } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/RunInTerminal.h b/lldb/tools/lldb-dap/RunInTerminal.h index 457850c8ea538..827d556dfc8e7 100644 --- a/lldb/tools/lldb-dap/RunInTerminal.h +++ b/lldb/tools/lldb-dap/RunInTerminal.h @@ -70,7 +70,7 @@ struct RunInTerminalMessageDidAttach : RunInTerminalMessage { class RunInTerminalLauncherCommChannel { public: - RunInTerminalLauncherCommChannel(llvm::StringRef comm_file); + RunInTerminalLauncherCommChannel(FifoFile &comm_file); /// Wait until the debug adapter attaches. /// @@ -84,10 +84,13 @@ class RunInTerminalLauncherCommChannel { /// Notify the debug adapter this process' pid. /// + /// \param[in] pid + /// The PID to be sent to the debug adapter + /// /// \return /// An \a llvm::Error object in case of errors or if this operation times /// out. - llvm::Error NotifyPid(); + llvm::Error NotifyPid(lldb::pid_t pid); /// Notify the debug adapter that there's been an error. void NotifyError(llvm::StringRef error); @@ -98,7 +101,7 @@ class RunInTerminalLauncherCommChannel { class RunInTerminalDebugAdapterCommChannel { public: - RunInTerminalDebugAdapterCommChannel(llvm::StringRef comm_file); + RunInTerminalDebugAdapterCommChannel(FifoFile &comm_file); /// Notify the runInTerminal launcher that it was attached. /// @@ -118,6 +121,8 @@ class RunInTerminalDebugAdapterCommChannel { /// default error message if a certain timeout if reached. std::string GetLauncherError(); + llvm::Error WaitForLauncher(); + private: FifoFileIO m_io; }; diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp index 6e17b13cc9e33..fe12b5b8f1723 100644 --- a/lldb/tools/lldb-dap/lldb-dap.cpp +++ b/lldb/tools/lldb-dap/lldb-dap.cpp @@ -39,6 +39,7 @@ #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" #include "llvm/Support/Threading.h" +#include "llvm/Support/WindowsError.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -207,11 +208,6 @@ static llvm::Error LaunchRunInTerminalTarget(llvm::opt::Arg &target_arg, llvm::StringRef comm_file, lldb::pid_t debugger_pid, char *argv[]) { -#if defined(_WIN32) - return llvm::createStringError( - "runInTerminal is only supported on POSIX systems"); -#else - // On Linux with the Yama security module enabled, a process can only attach // to its descendants by default. In the runInTerminal case the target // process is launched by the client so we need to allow tracing explicitly. @@ -220,9 +216,46 @@ static llvm::Error LaunchRunInTerminalTarget(llvm::opt::Arg &target_arg, (void)prctl(PR_SET_PTRACER, debugger_pid, 0, 0, 0); #endif - RunInTerminalLauncherCommChannel comm_channel(comm_file); - if (llvm::Error err = comm_channel.NotifyPid()) + const char *target = target_arg.getValue(); + +#ifdef _WIN32 + /* Win32 provides no way to replace the process image. exec* are misnomers. + Neither is the adapter notified of new processes due to DebugActiveProcess + semantics. Hence, we create the new process in a suspended state and resume + it after attach. + */ + std::string cmdline; + for (char **arg = argv; *arg != nullptr; ++arg) { + cmdline += *arg; + cmdline += ' '; + } + STARTUPINFOA si = {}; + si.cb = sizeof(si); + PROCESS_INFORMATION pi = {}; + bool res = CreateProcessA(target, cmdline.data(), NULL, NULL, FALSE, + CREATE_SUSPENDED, NULL, NULL, &si, &pi); + if (!res) { + llvm::errs() << "Failed to create process: " << GetLastError() << "\n"; + exit(EXIT_FAILURE); + } +#endif + + auto comm_fifo_file = FifoFile::create(comm_file); + if (!comm_fifo_file) { + llvm::errs() << "Failed to create fifo file: " << comm_fifo_file.takeError() + << "\n"; + exit(EXIT_FAILURE); + } + RunInTerminalLauncherCommChannel comm_channel(*comm_fifo_file); + if (llvm::Error err = comm_channel.NotifyPid( +#ifdef _WIN32 + pi.dwProcessId +#else + getpid() +#endif + )) { return err; + } // We will wait to be attached with a timeout. We don't wait indefinitely // using a signal to prevent being paused forever. @@ -235,15 +268,17 @@ static llvm::Error LaunchRunInTerminalTarget(llvm::opt::Arg &target_arg, std::chrono::milliseconds(timeout_in_ms))) { return err; } - - const char *target = target_arg.getValue(); +#ifdef _WIN32 + if (ResumeThread(pi.hThread) != -1) + exit(EXIT_SUCCESS); + auto error = llvm::mapLastWindowsError(); +#else execvp(target, argv); - - std::string error = std::strerror(errno); - comm_channel.NotifyError(error); - return llvm::createStringError(llvm::inconvertibleErrorCode(), - std::move(error)); + std::error_code error(errno, std::generic_category()); #endif + + comm_channel.NotifyError(error.message()); + return llvm::createStringError(error, "Failed to exec target"); } /// used only by TestVSCode_redirection_to_console.py From lldb-commits at lists.llvm.org Mon May 5 03:22:00 2025 From: lldb-commits at lists.llvm.org (Hu Jialun via lldb-commits) Date: Mon, 05 May 2025 03:22:00 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Add runInTerminal support for Windows (PR #138160) In-Reply-To: Message-ID: <68189148.630a0220.d0c2e.6251@mx.google.com> SuibianP wrote: > Another PR looks abandoned It is actually not (yet). I have been actively waiting for maintainers to drop another review and have pinged & rebased several times over these three months. https://github.com/llvm/llvm-project/pull/138160 From lldb-commits at lists.llvm.org Mon May 5 03:29:01 2025 From: lldb-commits at lists.llvm.org (Dmitry Vasilyev via lldb-commits) Date: Mon, 05 May 2025 03:29:01 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Inherit DuplicateFileAction(HANDLE, HANDLE) handles on windows (PR #137978) In-Reply-To: Message-ID: <681892ed.050a0220.2a5eb4.b9c7@mx.google.com> https://github.com/slydiman approved this pull request. Ok, let's see how it flies in the real world. https://github.com/llvm/llvm-project/pull/137978 From lldb-commits at lists.llvm.org Mon May 5 03:43:22 2025 From: lldb-commits at lists.llvm.org (Santhosh Kumar Ellendula via lldb-commits) Date: Mon, 05 May 2025 03:43:22 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][lldb-dap] Added support for "WriteMemory" request. (PR #131820) In-Reply-To: Message-ID: <6818964a.170a0220.33ff13.1179@mx.google.com> https://github.com/santhoshe447 updated https://github.com/llvm/llvm-project/pull/131820 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Mon May 5 06:08:25 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Mon, 05 May 2025 06:08:25 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][TypeSystemClang] Allow arrays to be dereferenced in C/C++. (PR #135843) In-Reply-To: Message-ID: <6818b849.050a0220.3c24b8.00d2@mx.google.com> ================ @@ -2854,14 +2842,22 @@ ValueObjectSP ValueObject::Dereference(Status &error) { m_deref_valobj = new ValueObjectChild( *this, child_compiler_type, child_name, child_byte_size, child_byte_offset, child_bitfield_bit_size, - child_bitfield_bit_offset, child_is_base_class, - child_is_deref_of_parent, eAddressTypeInvalid, language_flags); + child_bitfield_bit_offset, child_is_base_class, true, + eAddressTypeInvalid, language_flags); } } } - - } else if (IsSynthetic()) { - m_deref_valobj = GetChildMemberWithName("$$dereference$$").get(); + } else { + auto err = child_compiler_type_or_err.takeError(); + if (err.isA()) { + deref_error = llvm::toString(std::move(err)); + LLDB_LOG_ERROR(GetLog(LLDBLog::Types), + llvm::createStringError(deref_error), + "could not find child: {0}"); + } ---------------- labath wrote: Any error can be converted to a string. ```suggestion deref_error = llvm::toString(child_compiler_type_or_err.takeError()); LLDB_LOG(GetLog(LLDBLog::Types), "could not find child: {0}", deref_error); ``` https://github.com/llvm/llvm-project/pull/135843 From lldb-commits at lists.llvm.org Mon May 5 06:08:25 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Mon, 05 May 2025 06:08:25 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][TypeSystemClang] Allow arrays to be dereferenced in C/C++. (PR #135843) In-Reply-To: Message-ID: <6818b849.a70a0220.3d2822.e7a7@mx.google.com> ================ @@ -2794,37 +2794,26 @@ ValueObjectSP ValueObject::Dereference(Status &error) { if (m_deref_valobj) return m_deref_valobj->GetSP(); - const bool is_pointer_or_reference_type = IsPointerOrReferenceType(); - if (is_pointer_or_reference_type) { - bool omit_empty_base_classes = true; - bool ignore_array_bounds = false; - - std::string child_name_str; - uint32_t child_byte_size = 0; - int32_t child_byte_offset = 0; - uint32_t child_bitfield_bit_size = 0; - uint32_t child_bitfield_bit_offset = 0; - bool child_is_base_class = false; - bool child_is_deref_of_parent = false; - const bool transparent_pointers = false; - CompilerType compiler_type = GetCompilerType(); - uint64_t language_flags = 0; + std::string child_name_str; + uint32_t child_byte_size = 0; + int32_t child_byte_offset = 0; + uint32_t child_bitfield_bit_size = 0; + uint32_t child_bitfield_bit_offset = 0; + bool child_is_base_class = false; ---------------- labath wrote: I think `child_is_base_class` should go away as well, and maybe the bitfield fields as well (in principle I can imagine a pointer to a bitfield, but I don't think any current language has those). https://github.com/llvm/llvm-project/pull/135843 From lldb-commits at lists.llvm.org Mon May 5 06:08:26 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Mon, 05 May 2025 06:08:26 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][TypeSystemClang] Allow arrays to be dereferenced in C/C++. (PR #135843) In-Reply-To: Message-ID: <6818b84a.630a0220.916f7.8e63@mx.google.com> ================ @@ -88,7 +88,7 @@ def cleanup(): self.expect( "frame variable *number_not_engaged", error=True, - substrs=["not a pointer or reference type"], + substrs=["dereference failed: not a"], ---------------- labath wrote: What's the full error message here? Would be nice to check that it's reasonable. https://github.com/llvm/llvm-project/pull/135843 From lldb-commits at lists.llvm.org Mon May 5 08:01:04 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Mon, 05 May 2025 08:01:04 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb/docs] Fix/improve the gdb command map for dynamic types (PR #138538) Message-ID: https://github.com/labath created https://github.com/llvm/llvm-project/pull/138538 The setting and option value names were wrong. I'm assuming this changed over time, but I haven't tried to figure out when. Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Mon May 5 08:01:38 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Mon, 05 May 2025 08:01:38 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb/docs] Fix/improve the gdb command map for dynamic types (PR #138538) In-Reply-To: Message-ID: <6818d2d2.170a0220.1c94b1.1a10@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Pavel Labath (labath)
Changes The setting and option value names were wrong. I'm assuming this changed over time, but I haven't tried to figure out when. --- Full diff: https://github.com/llvm/llvm-project/pull/138538.diff 1 Files Affected: - (modified) lldb/docs/use/map.rst (+12-4) ``````````diff diff --git a/lldb/docs/use/map.rst b/lldb/docs/use/map.rst index ed285b2d1f6e9..c648b212006e0 100644 --- a/lldb/docs/use/map.rst +++ b/lldb/docs/use/map.rst @@ -800,16 +800,24 @@ Print the dynamic type of the result of an expression (gdb) p someCPPObjectPtrOrReference (Only works for C++ objects) +LLDB does this automatically if determining the dynamic type does not require +running the target (in C++, running the target is never needed). This default is +controlled by the `target.prefer-dynamic-value` setting. If that is disabled, it +can be re-enabled on a per-command basis: + .. code-block:: shell - (lldb) expr -d 1 -- [SomeClass returnAnObject] - (lldb) expr -d 1 -- someCPPObjectPtrOrReference + (lldb) settings set target.prefer-dynamic-value no-dynamic-values + (lldb) frame variable -d no-run-target someCPPObjectPtrOrReference + (lldb) expr -d no-run-target -- someCPPObjectPtr -or set dynamic type printing to be the default: +Note that printing of the dynamic type of references is not possible with the +`expr` command. The workaround is to take the address of the reference and +instruct lldb to print the children of the resulting pointer. .. code-block:: shell - (lldb) settings set target.prefer-dynamic run-target + (lldb) expr -P1 -d no-run-target -- &someCPPObjectReference Call a function so you can stop at a breakpoint in it ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``````````
https://github.com/llvm/llvm-project/pull/138538 From lldb-commits at lists.llvm.org Mon May 5 08:05:51 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Mon, 05 May 2025 08:05:51 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb/docs] Fix/improve the gdb command map for dynamic types (PR #138538) In-Reply-To: Message-ID: <6818d3cf.170a0220.24a301.d901@mx.google.com> ================ @@ -800,16 +800,24 @@ Print the dynamic type of the result of an expression (gdb) p someCPPObjectPtrOrReference (Only works for C++ objects) +LLDB does this automatically if determining the dynamic type does not require +running the target (in C++, running the target is never needed). This default is +controlled by the `target.prefer-dynamic-value` setting. If that is disabled, it +can be re-enabled on a per-command basis: + .. code-block:: shell - (lldb) expr -d 1 -- [SomeClass returnAnObject] - (lldb) expr -d 1 -- someCPPObjectPtrOrReference + (lldb) settings set target.prefer-dynamic-value no-dynamic-values + (lldb) frame variable -d no-run-target someCPPObjectPtrOrReference + (lldb) expr -d no-run-target -- someCPPObjectPtr -or set dynamic type printing to be the default: +Note that printing of the dynamic type of references is not possible with the +`expr` command. The workaround is to take the address of the reference and +instruct lldb to print the children of the resulting pointer. ---------------- labath wrote: I spent some time trying to make this work, but I came to the conclusion that this would require a relatively big change in how the expression evaluator works. Because we're creating persistent copies of the expression result, it's not just a matter of getting something to recognise the dynamic type of the result -- we would actually need to determine the dynamic type of the object before we create the copy (so that we copy the entire object) -- and then I guess somehow reset the original (non-dynamic) ValueObject to point to the subobject of the result. And that might be too much work to put on the shoulders of the "use-dynamic" setting? https://github.com/llvm/llvm-project/pull/138538 From lldb-commits at lists.llvm.org Mon May 5 08:08:58 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Mon, 05 May 2025 08:08:58 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb/docs] Fix/improve the gdb command map for dynamic types (PR #138538) In-Reply-To: Message-ID: <6818d48a.170a0220.2d9b35.2aa0@mx.google.com> ================ @@ -800,16 +800,24 @@ Print the dynamic type of the result of an expression (gdb) p someCPPObjectPtrOrReference (Only works for C++ objects) +LLDB does this automatically if determining the dynamic type does not require +running the target (in C++, running the target is never needed). This default is +controlled by the `target.prefer-dynamic-value` setting. If that is disabled, it +can be re-enabled on a per-command basis: + .. code-block:: shell - (lldb) expr -d 1 -- [SomeClass returnAnObject] - (lldb) expr -d 1 -- someCPPObjectPtrOrReference + (lldb) settings set target.prefer-dynamic-value no-dynamic-values + (lldb) frame variable -d no-run-target someCPPObjectPtrOrReference + (lldb) expr -d no-run-target -- someCPPObjectPtr -or set dynamic type printing to be the default: +Note that printing of the dynamic type of references is not possible with the +`expr` command. The workaround is to take the address of the reference and +instruct lldb to print the children of the resulting pointer. ---------------- labath wrote: (If you're wondering why we're making a copy of a reference -- but not of a pointer -- I suspect that's because in the expression evaluator we do not differentiate between an object and a reference to it. I suppose we could do it, and then somehow treat references as pointers, but I don't exactly know what would that entail.) https://github.com/llvm/llvm-project/pull/138538 From lldb-commits at lists.llvm.org Mon May 5 08:39:11 2025 From: lldb-commits at lists.llvm.org (Ilia Kuklin via lldb-commits) Date: Mon, 05 May 2025 08:39:11 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][TypeSystemClang] Allow arrays to be dereferenced in C/C++. (PR #135843) In-Reply-To: Message-ID: <6818db9f.050a0220.48da2.8dbf@mx.google.com> ================ @@ -88,7 +88,7 @@ def cleanup(): self.expect( "frame variable *number_not_engaged", error=True, - substrs=["not a pointer or reference type"], + substrs=["dereference failed: not a"], ---------------- kuilpd wrote: I had a thought that a message might be different since it comes from `TypeSystem`, but now I realize it's C++ test and we know exactly which type system. https://github.com/llvm/llvm-project/pull/135843 From lldb-commits at lists.llvm.org Mon May 5 08:39:35 2025 From: lldb-commits at lists.llvm.org (Ilia Kuklin via lldb-commits) Date: Mon, 05 May 2025 08:39:35 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][TypeSystemClang] Allow arrays to be dereferenced in C/C++. (PR #135843) In-Reply-To: Message-ID: <6818dbb7.050a0220.1f904d.996d@mx.google.com> https://github.com/kuilpd updated https://github.com/llvm/llvm-project/pull/135843 >From 7cca4bf228ab2b882a1a6487eb24948cba9e5b12 Mon Sep 17 00:00:00 2001 From: Ilia Kuklin Date: Wed, 16 Apr 2025 00:30:51 +0500 Subject: [PATCH 1/5] [lldb][TypeSystemClang] Add a function `IsValidDereferenceType` to TypeSystem to allow arrays to be dereferenced in C/C++. --- lldb/include/lldb/Symbol/CompilerType.h | 2 ++ lldb/include/lldb/Symbol/TypeSystem.h | 2 ++ lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp | 9 +++++++++ lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h | 2 ++ lldb/source/Symbol/CompilerType.cpp | 7 +++++++ lldb/source/ValueObject/ValueObject.cpp | 7 ++++--- 6 files changed, 26 insertions(+), 3 deletions(-) diff --git a/lldb/include/lldb/Symbol/CompilerType.h b/lldb/include/lldb/Symbol/CompilerType.h index fdbc2057ac10f..3d726fca5a16c 100644 --- a/lldb/include/lldb/Symbol/CompilerType.h +++ b/lldb/include/lldb/Symbol/CompilerType.h @@ -186,6 +186,8 @@ class CompilerType { bool IsReferenceType(CompilerType *pointee_type = nullptr, bool *is_rvalue = nullptr) const; + bool IsValidDereferenceType() const; + bool ShouldTreatScalarValueAsAddress() const; bool IsScalarType() const; diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h index df87fea32b72a..753352b45e2d9 100644 --- a/lldb/include/lldb/Symbol/TypeSystem.h +++ b/lldb/include/lldb/Symbol/TypeSystem.h @@ -489,6 +489,8 @@ class TypeSystem : public PluginInterface, virtual bool IsReferenceType(lldb::opaque_compiler_type_t type, CompilerType *pointee_type, bool *is_rvalue) = 0; + virtual bool IsValidDereferenceType(lldb::opaque_compiler_type_t type) = 0; + virtual bool ShouldTreatScalarValueAsAddress(lldb::opaque_compiler_type_t type) { return IsPointerOrReferenceType(type, nullptr); diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index cb0ecd6ebd406..4165e08a63302 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -3443,6 +3443,13 @@ bool TypeSystemClang::IsReferenceType(lldb::opaque_compiler_type_t type, return false; } +bool TypeSystemClang::IsValidDereferenceType( + lldb::opaque_compiler_type_t type) { + CompilerType compiler_type = GetType(clang::QualType::getFromOpaquePtr(type)); + return compiler_type.IsPointerOrReferenceType() || + compiler_type.IsArrayType(); +} + bool TypeSystemClang::IsFloatingPointType(lldb::opaque_compiler_type_t type, uint32_t &count, bool &is_complex) { if (type) { @@ -6544,6 +6551,8 @@ llvm::Expected TypeSystemClang::GetChildCompilerTypeAtIndex( return size_or_err.takeError(); child_byte_size = *size_or_err; child_byte_offset = (int32_t)idx * (int32_t)child_byte_size; + if (idx == 0) + child_is_deref_of_parent = true; return element_type; } } diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index 93933846d114d..8924cf99e8f7f 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -716,6 +716,8 @@ class TypeSystemClang : public TypeSystem { bool IsReferenceType(lldb::opaque_compiler_type_t type, CompilerType *pointee_type, bool *is_rvalue) override; + bool IsValidDereferenceType(lldb::opaque_compiler_type_t type) override; + bool IsScalarType(lldb::opaque_compiler_type_t type) override; bool IsTypedefType(lldb::opaque_compiler_type_t type) override; diff --git a/lldb/source/Symbol/CompilerType.cpp b/lldb/source/Symbol/CompilerType.cpp index 90c4dbf0c6206..183a6aa0ba6d4 100644 --- a/lldb/source/Symbol/CompilerType.cpp +++ b/lldb/source/Symbol/CompilerType.cpp @@ -233,6 +233,13 @@ bool CompilerType::IsReferenceType(CompilerType *pointee_type, return false; } +bool CompilerType::IsValidDereferenceType() const { + if (IsValid()) + if (auto type_system_sp = GetTypeSystem()) + return type_system_sp->IsValidDereferenceType(m_type); + return false; +} + bool CompilerType::ShouldTreatScalarValueAsAddress() const { if (IsValid()) if (auto type_system_sp = GetTypeSystem()) diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp index d5e0d462c3759..2c95e27c6e2c9 100644 --- a/lldb/source/ValueObject/ValueObject.cpp +++ b/lldb/source/ValueObject/ValueObject.cpp @@ -2794,8 +2794,9 @@ ValueObjectSP ValueObject::Dereference(Status &error) { if (m_deref_valobj) return m_deref_valobj->GetSP(); - const bool is_pointer_or_reference_type = IsPointerOrReferenceType(); - if (is_pointer_or_reference_type) { + const bool is_valid_dereference_type = + GetCompilerType().IsValidDereferenceType(); + if (is_valid_dereference_type) { bool omit_empty_base_classes = true; bool ignore_array_bounds = false; @@ -2871,7 +2872,7 @@ ValueObjectSP ValueObject::Dereference(Status &error) { StreamString strm; GetExpressionPath(strm); - if (is_pointer_or_reference_type) + if (is_valid_dereference_type) error = Status::FromErrorStringWithFormat( "dereference failed: (%s) %s", GetTypeName().AsCString(""), strm.GetData()); >From 1ca8c5fbb44f9ed2909a298ac9994445942983bb Mon Sep 17 00:00:00 2001 From: Ilia Kuklin Date: Thu, 24 Apr 2025 03:34:11 +0500 Subject: [PATCH 2/5] Add a function `GetDereferencedType` instead. --- lldb/include/lldb/Symbol/CompilerType.h | 11 +++- lldb/include/lldb/Symbol/TypeSystem.h | 11 +++- .../TypeSystem/Clang/TypeSystemClang.cpp | 26 +++++--- .../TypeSystem/Clang/TypeSystemClang.h | 11 +++- lldb/source/Symbol/CompilerType.cpp | 26 +++++--- lldb/source/ValueObject/ValueObject.cpp | 59 +++++++++---------- 6 files changed, 94 insertions(+), 50 deletions(-) diff --git a/lldb/include/lldb/Symbol/CompilerType.h b/lldb/include/lldb/Symbol/CompilerType.h index 3d726fca5a16c..9e738cedc72b2 100644 --- a/lldb/include/lldb/Symbol/CompilerType.h +++ b/lldb/include/lldb/Symbol/CompilerType.h @@ -186,8 +186,6 @@ class CompilerType { bool IsReferenceType(CompilerType *pointee_type = nullptr, bool *is_rvalue = nullptr) const; - bool IsValidDereferenceType() const; - bool ShouldTreatScalarValueAsAddress() const; bool IsScalarType() const; @@ -435,6 +433,15 @@ class CompilerType { CompilerDecl GetStaticFieldWithName(llvm::StringRef name) const; + llvm::Expected GetDereferencedType( + ExecutionContext *exe_ctx, bool transparent_pointers, + bool omit_empty_base_classes, bool ignore_array_bounds, + std::string &child_name, uint32_t &child_byte_size, + int32_t &child_byte_offset, uint32_t &child_bitfield_bit_size, + uint32_t &child_bitfield_bit_offset, bool &child_is_base_class, + bool &child_is_deref_of_parent, ValueObject *valobj, + uint64_t &language_flags, bool &type_valid) const; + llvm::Expected GetChildCompilerTypeAtIndex( ExecutionContext *exe_ctx, size_t idx, bool transparent_pointers, bool omit_empty_base_classes, bool ignore_array_bounds, diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h index 753352b45e2d9..211f6e04e444b 100644 --- a/lldb/include/lldb/Symbol/TypeSystem.h +++ b/lldb/include/lldb/Symbol/TypeSystem.h @@ -364,6 +364,15 @@ class TypeSystem : public PluginInterface, return CompilerDecl(); } + virtual llvm::Expected GetDereferencedType( + lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, + bool transparent_pointers, bool omit_empty_base_classes, + bool ignore_array_bounds, std::string &child_name, + uint32_t &child_byte_size, int32_t &child_byte_offset, + uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset, + bool &child_is_base_class, bool &child_is_deref_of_parent, + ValueObject *valobj, uint64_t &language_flags, bool &type_valid) = 0; + virtual llvm::Expected GetChildCompilerTypeAtIndex( lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, size_t idx, bool transparent_pointers, bool omit_empty_base_classes, @@ -489,8 +498,6 @@ class TypeSystem : public PluginInterface, virtual bool IsReferenceType(lldb::opaque_compiler_type_t type, CompilerType *pointee_type, bool *is_rvalue) = 0; - virtual bool IsValidDereferenceType(lldb::opaque_compiler_type_t type) = 0; - virtual bool ShouldTreatScalarValueAsAddress(lldb::opaque_compiler_type_t type) { return IsPointerOrReferenceType(type, nullptr); diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 4165e08a63302..27914da27cea2 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -3443,13 +3443,6 @@ bool TypeSystemClang::IsReferenceType(lldb::opaque_compiler_type_t type, return false; } -bool TypeSystemClang::IsValidDereferenceType( - lldb::opaque_compiler_type_t type) { - CompilerType compiler_type = GetType(clang::QualType::getFromOpaquePtr(type)); - return compiler_type.IsPointerOrReferenceType() || - compiler_type.IsArrayType(); -} - bool TypeSystemClang::IsFloatingPointType(lldb::opaque_compiler_type_t type, uint32_t &count, bool &is_complex) { if (type) { @@ -6187,6 +6180,25 @@ uint32_t TypeSystemClang::GetNumPointeeChildren(clang::QualType type) { return 0; } +llvm::Expected TypeSystemClang::GetDereferencedType( + lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, + bool transparent_pointers, bool omit_empty_base_classes, + bool ignore_array_bounds, std::string &child_name, + uint32_t &child_byte_size, int32_t &child_byte_offset, + uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset, + bool &child_is_base_class, bool &child_is_deref_of_parent, + ValueObject *valobj, uint64_t &language_flags, bool &type_valid) { + type_valid = IsPointerOrReferenceType(type, nullptr) || + IsArrayType(type, nullptr, nullptr, nullptr); + if (!type_valid) + return CompilerType(); + return GetChildCompilerTypeAtIndex( + type, exe_ctx, 0, transparent_pointers, omit_empty_base_classes, + ignore_array_bounds, child_name, child_byte_size, child_byte_offset, + child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, + child_is_deref_of_parent, valobj, language_flags); +} + llvm::Expected TypeSystemClang::GetChildCompilerTypeAtIndex( lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, size_t idx, bool transparent_pointers, bool omit_empty_base_classes, diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index 8924cf99e8f7f..89d93cfeb2d13 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -716,8 +716,6 @@ class TypeSystemClang : public TypeSystem { bool IsReferenceType(lldb::opaque_compiler_type_t type, CompilerType *pointee_type, bool *is_rvalue) override; - bool IsValidDereferenceType(lldb::opaque_compiler_type_t type) override; - bool IsScalarType(lldb::opaque_compiler_type_t type) override; bool IsTypedefType(lldb::opaque_compiler_type_t type) override; @@ -891,6 +889,15 @@ class TypeSystemClang : public TypeSystem { static uint32_t GetNumPointeeChildren(clang::QualType type); + llvm::Expected GetDereferencedType( + lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, + bool transparent_pointers, bool omit_empty_base_classes, + bool ignore_array_bounds, std::string &child_name, + uint32_t &child_byte_size, int32_t &child_byte_offset, + uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset, + bool &child_is_base_class, bool &child_is_deref_of_parent, + ValueObject *valobj, uint64_t &language_flags, bool &type_valid) override; + llvm::Expected GetChildCompilerTypeAtIndex( lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, size_t idx, bool transparent_pointers, bool omit_empty_base_classes, diff --git a/lldb/source/Symbol/CompilerType.cpp b/lldb/source/Symbol/CompilerType.cpp index 183a6aa0ba6d4..5dd0301d9adf9 100644 --- a/lldb/source/Symbol/CompilerType.cpp +++ b/lldb/source/Symbol/CompilerType.cpp @@ -233,13 +233,6 @@ bool CompilerType::IsReferenceType(CompilerType *pointee_type, return false; } -bool CompilerType::IsValidDereferenceType() const { - if (IsValid()) - if (auto type_system_sp = GetTypeSystem()) - return type_system_sp->IsValidDereferenceType(m_type); - return false; -} - bool CompilerType::ShouldTreatScalarValueAsAddress() const { if (IsValid()) if (auto type_system_sp = GetTypeSystem()) @@ -900,6 +893,25 @@ CompilerDecl CompilerType::GetStaticFieldWithName(llvm::StringRef name) const { return CompilerDecl(); } +llvm::Expected CompilerType::GetDereferencedType( + ExecutionContext *exe_ctx, bool transparent_pointers, + bool omit_empty_base_classes, bool ignore_array_bounds, + std::string &child_name, uint32_t &child_byte_size, + int32_t &child_byte_offset, uint32_t &child_bitfield_bit_size, + uint32_t &child_bitfield_bit_offset, bool &child_is_base_class, + bool &child_is_deref_of_parent, ValueObject *valobj, + uint64_t &language_flags, bool &type_valid) const { + if (IsValid()) + if (auto type_system_sp = GetTypeSystem()) + return type_system_sp->GetDereferencedType( + m_type, exe_ctx, transparent_pointers, omit_empty_base_classes, + ignore_array_bounds, child_name, child_byte_size, child_byte_offset, + child_bitfield_bit_size, child_bitfield_bit_offset, + child_is_base_class, child_is_deref_of_parent, valobj, language_flags, + type_valid); + return CompilerType(); +} + llvm::Expected CompilerType::GetChildCompilerTypeAtIndex( ExecutionContext *exe_ctx, size_t idx, bool transparent_pointers, bool omit_empty_base_classes, bool ignore_array_bounds, diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp index 2c95e27c6e2c9..a8b03f9e245de 100644 --- a/lldb/source/ValueObject/ValueObject.cpp +++ b/lldb/source/ValueObject/ValueObject.cpp @@ -2794,38 +2794,38 @@ ValueObjectSP ValueObject::Dereference(Status &error) { if (m_deref_valobj) return m_deref_valobj->GetSP(); - const bool is_valid_dereference_type = - GetCompilerType().IsValidDereferenceType(); - if (is_valid_dereference_type) { - bool omit_empty_base_classes = true; - bool ignore_array_bounds = false; - - std::string child_name_str; - uint32_t child_byte_size = 0; - int32_t child_byte_offset = 0; - uint32_t child_bitfield_bit_size = 0; - uint32_t child_bitfield_bit_offset = 0; - bool child_is_base_class = false; - bool child_is_deref_of_parent = false; - const bool transparent_pointers = false; - CompilerType compiler_type = GetCompilerType(); - uint64_t language_flags = 0; + bool omit_empty_base_classes = true; + bool ignore_array_bounds = false; + std::string child_name_str; + uint32_t child_byte_size = 0; + int32_t child_byte_offset = 0; + uint32_t child_bitfield_bit_size = 0; + uint32_t child_bitfield_bit_offset = 0; + bool child_is_base_class = false; + bool child_is_deref_of_parent = false; + const bool transparent_pointers = false; + CompilerType compiler_type = GetCompilerType(); + uint64_t language_flags = 0; + bool is_valid_dereference_type = false; - ExecutionContext exe_ctx(GetExecutionContextRef()); + ExecutionContext exe_ctx(GetExecutionContextRef()); - CompilerType child_compiler_type; - auto child_compiler_type_or_err = compiler_type.GetChildCompilerTypeAtIndex( - &exe_ctx, 0, transparent_pointers, omit_empty_base_classes, - ignore_array_bounds, child_name_str, child_byte_size, child_byte_offset, - child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, - child_is_deref_of_parent, this, language_flags); - if (!child_compiler_type_or_err) - LLDB_LOG_ERROR(GetLog(LLDBLog::Types), - child_compiler_type_or_err.takeError(), - "could not find child: {0}"); - else - child_compiler_type = *child_compiler_type_or_err; + CompilerType child_compiler_type; + auto child_compiler_type_or_err = compiler_type.GetDereferencedType( + &exe_ctx, transparent_pointers, omit_empty_base_classes, + ignore_array_bounds, child_name_str, child_byte_size, child_byte_offset, + child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, + child_is_deref_of_parent, this, language_flags, + is_valid_dereference_type); + if (!child_compiler_type_or_err && is_valid_dereference_type) + LLDB_LOG_ERROR(GetLog(LLDBLog::Types), + child_compiler_type_or_err.takeError(), + "could not find child: {0}"); + else + child_compiler_type = *child_compiler_type_or_err; + + if (is_valid_dereference_type) { if (child_compiler_type && child_byte_size) { ConstString child_name; if (!child_name_str.empty()) @@ -2860,7 +2860,6 @@ ValueObjectSP ValueObject::Dereference(Status &error) { } } } - } else if (IsSynthetic()) { m_deref_valobj = GetChildMemberWithName("$$dereference$$").get(); } >From 69c0ddc9ab3125857a6de663b3ac3ecc947d4477 Mon Sep 17 00:00:00 2001 From: Ilia Kuklin Date: Thu, 24 Apr 2025 21:31:06 +0500 Subject: [PATCH 3/5] Remove unnecessary arguments, add error output from `GetDereferencedType`. --- lldb/include/lldb/Symbol/CompilerType.h | 15 ++++---- lldb/include/lldb/Symbol/TypeSystem.h | 8 ++--- .../TypeSystem/Clang/TypeSystemClang.cpp | 20 +++++------ .../TypeSystem/Clang/TypeSystemClang.h | 8 ++--- lldb/source/Symbol/CompilerType.cpp | 18 ++++------ lldb/source/ValueObject/ValueObject.cpp | 36 +++++++++---------- .../TestDataFormatterGenericOptional.py | 2 +- 7 files changed, 46 insertions(+), 61 deletions(-) diff --git a/lldb/include/lldb/Symbol/CompilerType.h b/lldb/include/lldb/Symbol/CompilerType.h index 9e738cedc72b2..a2f077dc9d8ac 100644 --- a/lldb/include/lldb/Symbol/CompilerType.h +++ b/lldb/include/lldb/Symbol/CompilerType.h @@ -433,14 +433,13 @@ class CompilerType { CompilerDecl GetStaticFieldWithName(llvm::StringRef name) const; - llvm::Expected GetDereferencedType( - ExecutionContext *exe_ctx, bool transparent_pointers, - bool omit_empty_base_classes, bool ignore_array_bounds, - std::string &child_name, uint32_t &child_byte_size, - int32_t &child_byte_offset, uint32_t &child_bitfield_bit_size, - uint32_t &child_bitfield_bit_offset, bool &child_is_base_class, - bool &child_is_deref_of_parent, ValueObject *valobj, - uint64_t &language_flags, bool &type_valid) const; + llvm::Expected + GetDereferencedType(ExecutionContext *exe_ctx, std::string &child_name, + uint32_t &child_byte_size, int32_t &child_byte_offset, + uint32_t &child_bitfield_bit_size, + uint32_t &child_bitfield_bit_offset, + bool &child_is_base_class, ValueObject *valobj, + uint64_t &language_flags, bool &type_valid) const; llvm::Expected GetChildCompilerTypeAtIndex( ExecutionContext *exe_ctx, size_t idx, bool transparent_pointers, diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h index 211f6e04e444b..cec3ec418dc51 100644 --- a/lldb/include/lldb/Symbol/TypeSystem.h +++ b/lldb/include/lldb/Symbol/TypeSystem.h @@ -366,11 +366,9 @@ class TypeSystem : public PluginInterface, virtual llvm::Expected GetDereferencedType( lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, - bool transparent_pointers, bool omit_empty_base_classes, - bool ignore_array_bounds, std::string &child_name, - uint32_t &child_byte_size, int32_t &child_byte_offset, - uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset, - bool &child_is_base_class, bool &child_is_deref_of_parent, + std::string &child_name, uint32_t &child_byte_size, + int32_t &child_byte_offset, uint32_t &child_bitfield_bit_size, + uint32_t &child_bitfield_bit_offset, bool &child_is_base_class, ValueObject *valobj, uint64_t &language_flags, bool &type_valid) = 0; virtual llvm::Expected GetChildCompilerTypeAtIndex( diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 27914da27cea2..2874eafca66ab 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -6182,21 +6182,19 @@ uint32_t TypeSystemClang::GetNumPointeeChildren(clang::QualType type) { llvm::Expected TypeSystemClang::GetDereferencedType( lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, - bool transparent_pointers, bool omit_empty_base_classes, - bool ignore_array_bounds, std::string &child_name, - uint32_t &child_byte_size, int32_t &child_byte_offset, - uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset, - bool &child_is_base_class, bool &child_is_deref_of_parent, + std::string &child_name, uint32_t &child_byte_size, + int32_t &child_byte_offset, uint32_t &child_bitfield_bit_size, + uint32_t &child_bitfield_bit_offset, bool &child_is_base_class, ValueObject *valobj, uint64_t &language_flags, bool &type_valid) { type_valid = IsPointerOrReferenceType(type, nullptr) || IsArrayType(type, nullptr, nullptr, nullptr); if (!type_valid) - return CompilerType(); + return llvm::createStringError("not a pointer, reference or array type"); + bool child_is_deref_of_parent; return GetChildCompilerTypeAtIndex( - type, exe_ctx, 0, transparent_pointers, omit_empty_base_classes, - ignore_array_bounds, child_name, child_byte_size, child_byte_offset, - child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, - child_is_deref_of_parent, valobj, language_flags); + type, exe_ctx, 0, false, true, false, child_name, child_byte_size, + child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, + child_is_base_class, child_is_deref_of_parent, valobj, language_flags); } llvm::Expected TypeSystemClang::GetChildCompilerTypeAtIndex( @@ -6563,8 +6561,6 @@ llvm::Expected TypeSystemClang::GetChildCompilerTypeAtIndex( return size_or_err.takeError(); child_byte_size = *size_or_err; child_byte_offset = (int32_t)idx * (int32_t)child_byte_size; - if (idx == 0) - child_is_deref_of_parent = true; return element_type; } } diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index 89d93cfeb2d13..3d92a4120321d 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -891,11 +891,9 @@ class TypeSystemClang : public TypeSystem { llvm::Expected GetDereferencedType( lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, - bool transparent_pointers, bool omit_empty_base_classes, - bool ignore_array_bounds, std::string &child_name, - uint32_t &child_byte_size, int32_t &child_byte_offset, - uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset, - bool &child_is_base_class, bool &child_is_deref_of_parent, + std::string &child_name, uint32_t &child_byte_size, + int32_t &child_byte_offset, uint32_t &child_bitfield_bit_size, + uint32_t &child_bitfield_bit_offset, bool &child_is_base_class, ValueObject *valobj, uint64_t &language_flags, bool &type_valid) override; llvm::Expected GetChildCompilerTypeAtIndex( diff --git a/lldb/source/Symbol/CompilerType.cpp b/lldb/source/Symbol/CompilerType.cpp index 5dd0301d9adf9..3311513922707 100644 --- a/lldb/source/Symbol/CompilerType.cpp +++ b/lldb/source/Symbol/CompilerType.cpp @@ -894,21 +894,17 @@ CompilerDecl CompilerType::GetStaticFieldWithName(llvm::StringRef name) const { } llvm::Expected CompilerType::GetDereferencedType( - ExecutionContext *exe_ctx, bool transparent_pointers, - bool omit_empty_base_classes, bool ignore_array_bounds, - std::string &child_name, uint32_t &child_byte_size, - int32_t &child_byte_offset, uint32_t &child_bitfield_bit_size, - uint32_t &child_bitfield_bit_offset, bool &child_is_base_class, - bool &child_is_deref_of_parent, ValueObject *valobj, - uint64_t &language_flags, bool &type_valid) const { + ExecutionContext *exe_ctx, std::string &child_name, + uint32_t &child_byte_size, int32_t &child_byte_offset, + uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset, + bool &child_is_base_class, ValueObject *valobj, uint64_t &language_flags, + bool &type_valid) const { if (IsValid()) if (auto type_system_sp = GetTypeSystem()) return type_system_sp->GetDereferencedType( - m_type, exe_ctx, transparent_pointers, omit_empty_base_classes, - ignore_array_bounds, child_name, child_byte_size, child_byte_offset, + m_type, exe_ctx, child_name, child_byte_size, child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, - child_is_base_class, child_is_deref_of_parent, valobj, language_flags, - type_valid); + child_is_base_class, valobj, language_flags, type_valid); return CompilerType(); } diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp index a8b03f9e245de..5e6dcf7718e16 100644 --- a/lldb/source/ValueObject/ValueObject.cpp +++ b/lldb/source/ValueObject/ValueObject.cpp @@ -2794,16 +2794,12 @@ ValueObjectSP ValueObject::Dereference(Status &error) { if (m_deref_valobj) return m_deref_valobj->GetSP(); - bool omit_empty_base_classes = true; - bool ignore_array_bounds = false; std::string child_name_str; uint32_t child_byte_size = 0; int32_t child_byte_offset = 0; uint32_t child_bitfield_bit_size = 0; uint32_t child_bitfield_bit_offset = 0; bool child_is_base_class = false; - bool child_is_deref_of_parent = false; - const bool transparent_pointers = false; CompilerType compiler_type = GetCompilerType(); uint64_t language_flags = 0; bool is_valid_dereference_type = false; @@ -2812,17 +2808,20 @@ ValueObjectSP ValueObject::Dereference(Status &error) { CompilerType child_compiler_type; auto child_compiler_type_or_err = compiler_type.GetDereferencedType( - &exe_ctx, transparent_pointers, omit_empty_base_classes, - ignore_array_bounds, child_name_str, child_byte_size, child_byte_offset, + &exe_ctx, child_name_str, child_byte_size, child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, - child_is_deref_of_parent, this, language_flags, - is_valid_dereference_type); + this, language_flags, is_valid_dereference_type); - if (!child_compiler_type_or_err && is_valid_dereference_type) - LLDB_LOG_ERROR(GetLog(LLDBLog::Types), - child_compiler_type_or_err.takeError(), - "could not find child: {0}"); - else + std::string deref_error; + if (!child_compiler_type_or_err) { + auto err = child_compiler_type_or_err.takeError(); + if (err.isA()) { + deref_error = llvm::toString(std::move(err)); + LLDB_LOG_ERROR(GetLog(LLDBLog::Types), + llvm::createStringError(deref_error), + "could not find child: {0}"); + } + } else child_compiler_type = *child_compiler_type_or_err; if (is_valid_dereference_type) { @@ -2834,8 +2833,7 @@ ValueObjectSP ValueObject::Dereference(Status &error) { m_deref_valobj = new ValueObjectChild( *this, child_compiler_type, child_name, child_byte_size, child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, - child_is_base_class, child_is_deref_of_parent, eAddressTypeInvalid, - language_flags); + child_is_base_class, true, eAddressTypeInvalid, language_flags); } // In case of incomplete child compiler type, use the pointee type and try @@ -2855,8 +2853,8 @@ ValueObjectSP ValueObject::Dereference(Status &error) { m_deref_valobj = new ValueObjectChild( *this, child_compiler_type, child_name, child_byte_size, child_byte_offset, child_bitfield_bit_size, - child_bitfield_bit_offset, child_is_base_class, - child_is_deref_of_parent, eAddressTypeInvalid, language_flags); + child_bitfield_bit_offset, child_is_base_class, true, + eAddressTypeInvalid, language_flags); } } } @@ -2871,13 +2869,13 @@ ValueObjectSP ValueObject::Dereference(Status &error) { StreamString strm; GetExpressionPath(strm); - if (is_valid_dereference_type) + if (deref_error.empty()) error = Status::FromErrorStringWithFormat( "dereference failed: (%s) %s", GetTypeName().AsCString(""), strm.GetData()); else error = Status::FromErrorStringWithFormat( - "not a pointer or reference type: (%s) %s", + "dereference failed: %s: (%s) %s", deref_error.c_str(), GetTypeName().AsCString(""), strm.GetData()); return ValueObjectSP(); } diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py index 7dc656a7ae225..8f36edea7d727 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py @@ -88,7 +88,7 @@ def cleanup(): self.expect( "frame variable *number_not_engaged", error=True, - substrs=["not a pointer or reference type"], + substrs=["dereference failed: not a"], ) @add_test_categories(["libc++"]) >From a0ebf366e0e1836b45adf2910d87e823ca6f5534 Mon Sep 17 00:00:00 2001 From: Ilia Kuklin Date: Wed, 30 Apr 2025 21:13:44 +0500 Subject: [PATCH 4/5] Rebase & remove the separate type validity argument --- lldb/include/lldb/Symbol/CompilerType.h | 2 +- lldb/include/lldb/Symbol/TypeSystem.h | 2 +- .../TypeSystem/Clang/TypeSystemClang.cpp | 6 ++-- .../TypeSystem/Clang/TypeSystemClang.h | 2 +- lldb/source/Symbol/CompilerType.cpp | 6 ++-- lldb/source/ValueObject/ValueObject.cpp | 28 +++++++++---------- .../TestFrameVarDILPointerArithmetic.py | 6 +--- 7 files changed, 23 insertions(+), 29 deletions(-) diff --git a/lldb/include/lldb/Symbol/CompilerType.h b/lldb/include/lldb/Symbol/CompilerType.h index a2f077dc9d8ac..f4120e042e122 100644 --- a/lldb/include/lldb/Symbol/CompilerType.h +++ b/lldb/include/lldb/Symbol/CompilerType.h @@ -439,7 +439,7 @@ class CompilerType { uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset, bool &child_is_base_class, ValueObject *valobj, - uint64_t &language_flags, bool &type_valid) const; + uint64_t &language_flags) const; llvm::Expected GetChildCompilerTypeAtIndex( ExecutionContext *exe_ctx, size_t idx, bool transparent_pointers, diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h index cec3ec418dc51..33a96463a9c73 100644 --- a/lldb/include/lldb/Symbol/TypeSystem.h +++ b/lldb/include/lldb/Symbol/TypeSystem.h @@ -369,7 +369,7 @@ class TypeSystem : public PluginInterface, std::string &child_name, uint32_t &child_byte_size, int32_t &child_byte_offset, uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset, bool &child_is_base_class, - ValueObject *valobj, uint64_t &language_flags, bool &type_valid) = 0; + ValueObject *valobj, uint64_t &language_flags) = 0; virtual llvm::Expected GetChildCompilerTypeAtIndex( lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, size_t idx, diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 2874eafca66ab..4dbe215718d2a 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -6185,9 +6185,9 @@ llvm::Expected TypeSystemClang::GetDereferencedType( std::string &child_name, uint32_t &child_byte_size, int32_t &child_byte_offset, uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset, bool &child_is_base_class, - ValueObject *valobj, uint64_t &language_flags, bool &type_valid) { - type_valid = IsPointerOrReferenceType(type, nullptr) || - IsArrayType(type, nullptr, nullptr, nullptr); + ValueObject *valobj, uint64_t &language_flags) { + bool type_valid = IsPointerOrReferenceType(type, nullptr) || + IsArrayType(type, nullptr, nullptr, nullptr); if (!type_valid) return llvm::createStringError("not a pointer, reference or array type"); bool child_is_deref_of_parent; diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index 3d92a4120321d..2440e4fbfcd7d 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -894,7 +894,7 @@ class TypeSystemClang : public TypeSystem { std::string &child_name, uint32_t &child_byte_size, int32_t &child_byte_offset, uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset, bool &child_is_base_class, - ValueObject *valobj, uint64_t &language_flags, bool &type_valid) override; + ValueObject *valobj, uint64_t &language_flags) override; llvm::Expected GetChildCompilerTypeAtIndex( lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, size_t idx, diff --git a/lldb/source/Symbol/CompilerType.cpp b/lldb/source/Symbol/CompilerType.cpp index 3311513922707..23f18abfeea7f 100644 --- a/lldb/source/Symbol/CompilerType.cpp +++ b/lldb/source/Symbol/CompilerType.cpp @@ -897,14 +897,14 @@ llvm::Expected CompilerType::GetDereferencedType( ExecutionContext *exe_ctx, std::string &child_name, uint32_t &child_byte_size, int32_t &child_byte_offset, uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset, - bool &child_is_base_class, ValueObject *valobj, uint64_t &language_flags, - bool &type_valid) const { + bool &child_is_base_class, ValueObject *valobj, + uint64_t &language_flags) const { if (IsValid()) if (auto type_system_sp = GetTypeSystem()) return type_system_sp->GetDereferencedType( m_type, exe_ctx, child_name, child_byte_size, child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, - child_is_base_class, valobj, language_flags, type_valid); + child_is_base_class, valobj, language_flags); return CompilerType(); } diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp index 5e6dcf7718e16..e3e2c0b09c9fd 100644 --- a/lldb/source/ValueObject/ValueObject.cpp +++ b/lldb/source/ValueObject/ValueObject.cpp @@ -2802,7 +2802,6 @@ ValueObjectSP ValueObject::Dereference(Status &error) { bool child_is_base_class = false; CompilerType compiler_type = GetCompilerType(); uint64_t language_flags = 0; - bool is_valid_dereference_type = false; ExecutionContext exe_ctx(GetExecutionContextRef()); @@ -2810,21 +2809,11 @@ ValueObjectSP ValueObject::Dereference(Status &error) { auto child_compiler_type_or_err = compiler_type.GetDereferencedType( &exe_ctx, child_name_str, child_byte_size, child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, - this, language_flags, is_valid_dereference_type); + this, language_flags); std::string deref_error; - if (!child_compiler_type_or_err) { - auto err = child_compiler_type_or_err.takeError(); - if (err.isA()) { - deref_error = llvm::toString(std::move(err)); - LLDB_LOG_ERROR(GetLog(LLDBLog::Types), - llvm::createStringError(deref_error), - "could not find child: {0}"); - } - } else + if (child_compiler_type_or_err) { child_compiler_type = *child_compiler_type_or_err; - - if (is_valid_dereference_type) { if (child_compiler_type && child_byte_size) { ConstString child_name; if (!child_name_str.empty()) @@ -2858,8 +2847,17 @@ ValueObjectSP ValueObject::Dereference(Status &error) { } } } - } else if (IsSynthetic()) { - m_deref_valobj = GetChildMemberWithName("$$dereference$$").get(); + } else { + auto err = child_compiler_type_or_err.takeError(); + if (err.isA()) { + deref_error = llvm::toString(std::move(err)); + LLDB_LOG_ERROR(GetLog(LLDBLog::Types), + llvm::createStringError(deref_error), + "could not find child: {0}"); + } + if (IsSynthetic()) { + m_deref_valobj = GetChildMemberWithName("$$dereference$$").get(); + } } if (m_deref_valobj) { diff --git a/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py index d36c5fce6d43d..6753f988c4187 100644 --- a/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py +++ b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py @@ -33,11 +33,7 @@ def test_dereference(self): self.expect_var_path("*offset_pref", True, type="int *") self.expect_var_path("**pp_int0", value="0") self.expect_var_path("&**pp_int0", type="int *") - self.expect( - "frame var '*array'", - error=True, - substrs=["not a pointer or reference type"], - ) + self.expect_var_path("*array", value="0") self.expect( "frame var '&*p_null'", error=True, >From 322a135f929b69695053356cd7a77485e6ae5105 Mon Sep 17 00:00:00 2001 From: Ilia Kuklin Date: Mon, 5 May 2025 20:36:12 +0500 Subject: [PATCH 5/5] Remove more arguments, simplify error logging, adjust test. --- lldb/include/lldb/Symbol/CompilerType.h | 5 +--- lldb/include/lldb/Symbol/TypeSystem.h | 11 ++++--- .../TypeSystem/Clang/TypeSystemClang.cpp | 7 +++-- .../TypeSystem/Clang/TypeSystemClang.h | 11 ++++--- lldb/source/Symbol/CompilerType.cpp | 7 ++--- lldb/source/ValueObject/ValueObject.cpp | 30 +++++++------------ .../TestDataFormatterGenericOptional.py | 2 +- 7 files changed, 28 insertions(+), 45 deletions(-) diff --git a/lldb/include/lldb/Symbol/CompilerType.h b/lldb/include/lldb/Symbol/CompilerType.h index f4120e042e122..c5f4d551bf4f1 100644 --- a/lldb/include/lldb/Symbol/CompilerType.h +++ b/lldb/include/lldb/Symbol/CompilerType.h @@ -436,10 +436,7 @@ class CompilerType { llvm::Expected GetDereferencedType(ExecutionContext *exe_ctx, std::string &child_name, uint32_t &child_byte_size, int32_t &child_byte_offset, - uint32_t &child_bitfield_bit_size, - uint32_t &child_bitfield_bit_offset, - bool &child_is_base_class, ValueObject *valobj, - uint64_t &language_flags) const; + ValueObject *valobj, uint64_t &language_flags) const; llvm::Expected GetChildCompilerTypeAtIndex( ExecutionContext *exe_ctx, size_t idx, bool transparent_pointers, diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h index 33a96463a9c73..35fcf2be29d4f 100644 --- a/lldb/include/lldb/Symbol/TypeSystem.h +++ b/lldb/include/lldb/Symbol/TypeSystem.h @@ -364,12 +364,11 @@ class TypeSystem : public PluginInterface, return CompilerDecl(); } - virtual llvm::Expected GetDereferencedType( - lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, - std::string &child_name, uint32_t &child_byte_size, - int32_t &child_byte_offset, uint32_t &child_bitfield_bit_size, - uint32_t &child_bitfield_bit_offset, bool &child_is_base_class, - ValueObject *valobj, uint64_t &language_flags) = 0; + virtual llvm::Expected + GetDereferencedType(lldb::opaque_compiler_type_t type, + ExecutionContext *exe_ctx, std::string &child_name, + uint32_t &child_byte_size, int32_t &child_byte_offset, + ValueObject *valobj, uint64_t &language_flags) = 0; virtual llvm::Expected GetChildCompilerTypeAtIndex( lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, size_t idx, diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 4dbe215718d2a..f5adc60b5e9f1 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -6183,13 +6183,14 @@ uint32_t TypeSystemClang::GetNumPointeeChildren(clang::QualType type) { llvm::Expected TypeSystemClang::GetDereferencedType( lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, std::string &child_name, uint32_t &child_byte_size, - int32_t &child_byte_offset, uint32_t &child_bitfield_bit_size, - uint32_t &child_bitfield_bit_offset, bool &child_is_base_class, - ValueObject *valobj, uint64_t &language_flags) { + int32_t &child_byte_offset, ValueObject *valobj, uint64_t &language_flags) { bool type_valid = IsPointerOrReferenceType(type, nullptr) || IsArrayType(type, nullptr, nullptr, nullptr); if (!type_valid) return llvm::createStringError("not a pointer, reference or array type"); + uint32_t child_bitfield_bit_size = 0; + uint32_t child_bitfield_bit_offset = 0; + bool child_is_base_class; bool child_is_deref_of_parent; return GetChildCompilerTypeAtIndex( type, exe_ctx, 0, false, true, false, child_name, child_byte_size, diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index 2440e4fbfcd7d..6028d1344dc97 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -889,12 +889,11 @@ class TypeSystemClang : public TypeSystem { static uint32_t GetNumPointeeChildren(clang::QualType type); - llvm::Expected GetDereferencedType( - lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, - std::string &child_name, uint32_t &child_byte_size, - int32_t &child_byte_offset, uint32_t &child_bitfield_bit_size, - uint32_t &child_bitfield_bit_offset, bool &child_is_base_class, - ValueObject *valobj, uint64_t &language_flags) override; + llvm::Expected + GetDereferencedType(lldb::opaque_compiler_type_t type, + ExecutionContext *exe_ctx, std::string &child_name, + uint32_t &child_byte_size, int32_t &child_byte_offset, + ValueObject *valobj, uint64_t &language_flags) override; llvm::Expected GetChildCompilerTypeAtIndex( lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, size_t idx, diff --git a/lldb/source/Symbol/CompilerType.cpp b/lldb/source/Symbol/CompilerType.cpp index 23f18abfeea7f..ec4263b5ab1e5 100644 --- a/lldb/source/Symbol/CompilerType.cpp +++ b/lldb/source/Symbol/CompilerType.cpp @@ -895,16 +895,13 @@ CompilerDecl CompilerType::GetStaticFieldWithName(llvm::StringRef name) const { llvm::Expected CompilerType::GetDereferencedType( ExecutionContext *exe_ctx, std::string &child_name, - uint32_t &child_byte_size, int32_t &child_byte_offset, - uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset, - bool &child_is_base_class, ValueObject *valobj, + uint32_t &child_byte_size, int32_t &child_byte_offset, ValueObject *valobj, uint64_t &language_flags) const { if (IsValid()) if (auto type_system_sp = GetTypeSystem()) return type_system_sp->GetDereferencedType( m_type, exe_ctx, child_name, child_byte_size, child_byte_offset, - child_bitfield_bit_size, child_bitfield_bit_offset, - child_is_base_class, valobj, language_flags); + valobj, language_flags); return CompilerType(); } diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp index e3e2c0b09c9fd..8980e886122f7 100644 --- a/lldb/source/ValueObject/ValueObject.cpp +++ b/lldb/source/ValueObject/ValueObject.cpp @@ -2797,9 +2797,6 @@ ValueObjectSP ValueObject::Dereference(Status &error) { std::string child_name_str; uint32_t child_byte_size = 0; int32_t child_byte_offset = 0; - uint32_t child_bitfield_bit_size = 0; - uint32_t child_bitfield_bit_offset = 0; - bool child_is_base_class = false; CompilerType compiler_type = GetCompilerType(); uint64_t language_flags = 0; @@ -2807,9 +2804,8 @@ ValueObjectSP ValueObject::Dereference(Status &error) { CompilerType child_compiler_type; auto child_compiler_type_or_err = compiler_type.GetDereferencedType( - &exe_ctx, child_name_str, child_byte_size, child_byte_offset, - child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, - this, language_flags); + &exe_ctx, child_name_str, child_byte_size, child_byte_offset, this, + language_flags); std::string deref_error; if (child_compiler_type_or_err) { @@ -2819,10 +2815,10 @@ ValueObjectSP ValueObject::Dereference(Status &error) { if (!child_name_str.empty()) child_name.SetCString(child_name_str.c_str()); - m_deref_valobj = new ValueObjectChild( - *this, child_compiler_type, child_name, child_byte_size, - child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, - child_is_base_class, true, eAddressTypeInvalid, language_flags); + m_deref_valobj = + new ValueObjectChild(*this, child_compiler_type, child_name, + child_byte_size, child_byte_offset, 0, 0, false, + true, eAddressTypeInvalid, language_flags); } // In case of incomplete child compiler type, use the pointee type and try @@ -2841,20 +2837,14 @@ ValueObjectSP ValueObject::Dereference(Status &error) { m_deref_valobj = new ValueObjectChild( *this, child_compiler_type, child_name, child_byte_size, - child_byte_offset, child_bitfield_bit_size, - child_bitfield_bit_offset, child_is_base_class, true, - eAddressTypeInvalid, language_flags); + child_byte_offset, 0, 0, false, true, eAddressTypeInvalid, + language_flags); } } } } else { - auto err = child_compiler_type_or_err.takeError(); - if (err.isA()) { - deref_error = llvm::toString(std::move(err)); - LLDB_LOG_ERROR(GetLog(LLDBLog::Types), - llvm::createStringError(deref_error), - "could not find child: {0}"); - } + deref_error = llvm::toString(child_compiler_type_or_err.takeError()); + LLDB_LOG(GetLog(LLDBLog::Types), "could not find child: {0}", deref_error); if (IsSynthetic()) { m_deref_valobj = GetChildMemberWithName("$$dereference$$").get(); } diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py index 8f36edea7d727..99d79a9f125b1 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py @@ -88,7 +88,7 @@ def cleanup(): self.expect( "frame variable *number_not_engaged", error=True, - substrs=["dereference failed: not a"], + substrs=["dereference failed: not a pointer, reference or array type"], ) @add_test_categories(["libc++"]) From lldb-commits at lists.llvm.org Mon May 5 08:40:59 2025 From: lldb-commits at lists.llvm.org (Ilia Kuklin via lldb-commits) Date: Mon, 05 May 2025 08:40:59 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Fix `ValueObject::AddressOf()` return value (PR #137688) In-Reply-To: Message-ID: <6818dc0b.170a0220.20e047.439d@mx.google.com> kuilpd wrote: @labath Is this okay to merge now? https://github.com/llvm/llvm-project/pull/137688 From lldb-commits at lists.llvm.org Mon May 5 08:50:13 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Mon, 05 May 2025 08:50:13 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Give attach test binaries unique names (PR #138435) In-Reply-To: Message-ID: <6818de35.050a0220.2ad8ca.915d@mx.google.com> https://github.com/ashgti approved this pull request. Does attach normally only check the basename of the executable? Or is it supposed to match the entire path? I see a comment in `TestDAP_attach.test_by_name` that mentions making the path more unique, so I'm wondering if something else has changed or if that previous attempt at making the name more unique was a little off the mark. https://github.com/llvm/llvm-project/pull/138435 From lldb-commits at lists.llvm.org Mon May 5 08:51:17 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Mon, 05 May 2025 08:51:17 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Fix `ValueObject::AddressOf()` return value (PR #137688) In-Reply-To: Message-ID: <6818de75.a70a0220.240215.b74f@mx.google.com> https://github.com/labath approved this pull request. Yeah, yeah. If Jim says it's ok, that's good enough for me. https://github.com/llvm/llvm-project/pull/137688 From lldb-commits at lists.llvm.org Mon May 5 08:53:08 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Mon, 05 May 2025 08:53:08 -0700 (PDT) Subject: [Lldb-commits] [lldb] 0eff410 - [LLDB] Fix `ValueObject::AddressOf()` return value (#137688) Message-ID: <6818dee4.170a0220.2cf8c0.5c21@mx.google.com> Author: Ilia Kuklin Date: 2025-05-05T20:53:04+05:00 New Revision: 0eff4108cb1e0a597ba70a4e1b8e1ce3ae0cfd46 URL: https://github.com/llvm/llvm-project/commit/0eff4108cb1e0a597ba70a4e1b8e1ce3ae0cfd46 DIFF: https://github.com/llvm/llvm-project/commit/0eff4108cb1e0a597ba70a4e1b8e1ce3ae0cfd46.diff LOG: [LLDB] Fix `ValueObject::AddressOf()` return value (#137688) `ValueObject::AddressOf()` used to return address as a value which has it's own address, allowing to do `value.AddressOf().AddressOf()`. This patch makes the return address a simple const value. Added: Modified: lldb/source/ValueObject/ValueObject.cpp lldb/test/API/python_api/sbvalue_const_addrof/main.cpp Removed: ################################################################################ diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp index d5e0d462c3759..e1c66763ff0b8 100644 --- a/lldb/source/ValueObject/ValueObject.cpp +++ b/lldb/source/ValueObject/ValueObject.cpp @@ -2907,10 +2907,13 @@ ValueObjectSP ValueObject::AddressOf(Status &error) { std::string name(1, '&'); name.append(m_name.AsCString("")); ExecutionContext exe_ctx(GetExecutionContextRef()); + + lldb::DataBufferSP buffer( + new lldb_private::DataBufferHeap(&addr, sizeof(lldb::addr_t))); m_addr_of_valobj_sp = ValueObjectConstResult::Create( exe_ctx.GetBestExecutionContextScope(), - compiler_type.GetPointerType(), ConstString(name.c_str()), addr, - eAddressTypeInvalid, m_data.GetAddressByteSize()); + compiler_type.GetPointerType(), ConstString(name.c_str()), buffer, + endian::InlHostByteOrder(), exe_ctx.GetAddressByteSize()); } } break; default: diff --git a/lldb/test/API/python_api/sbvalue_const_addrof/main.cpp b/lldb/test/API/python_api/sbvalue_const_addrof/main.cpp index 318a45bc21a85..ae6abc8613406 100644 --- a/lldb/test/API/python_api/sbvalue_const_addrof/main.cpp +++ b/lldb/test/API/python_api/sbvalue_const_addrof/main.cpp @@ -3,21 +3,21 @@ struct RegisterContext { - uintptr_t r0; - uintptr_t r1; - uintptr_t r2; - uintptr_t r3; - uintptr_t r4; - uintptr_t pc; - uintptr_t fp; - uintptr_t sp; + uintptr_t r0; + uintptr_t r1; + uintptr_t r2; + uintptr_t r3; + uintptr_t r4; + uintptr_t pc; + uintptr_t fp; + uintptr_t sp; }; struct ThreadInfo { - uint32_t tid; - const char *name; - RegisterContext regs; - ThreadInfo *next; + uint32_t tid; + const char *name; + RegisterContext regs; + ThreadInfo *next; }; int main (int argc, char const *argv[], char const *envp[]); @@ -27,14 +27,17 @@ ThreadInfo *g_thread_list_ptr = &g_thread1; int main (int argc, char const *argv[], char const *envp[]) { - printf ("g_thread_list is %p\n", g_thread_list_ptr); - return 0; //% v = self.dbg.GetSelectedTarget().FindFirstGlobalVariable('g_thread_list_ptr') - //% v_gla = v.GetChildMemberWithName('regs').GetLoadAddress() - //% v_aof = v.GetChildMemberWithName('regs').AddressOf().GetValueAsUnsigned(lldb.LLDB_INVALID_ADDRESS) - //% expr = '(%s)0x%x' % (v.GetType().GetName(), v.GetValueAsUnsigned(0)) - //% e = v.CreateValueFromExpression('e', expr) - //% e_gla = e.GetChildMemberWithName('regs').GetLoadAddress() - //% e_aof = e.GetChildMemberWithName('regs').AddressOf().GetValueAsUnsigned(lldb.LLDB_INVALID_ADDRESS) - //% self.assertTrue(v_gla == e_gla, "GetLoadAddress() diff ers") - //% self.assertTrue(v_aof == e_aof, "AddressOf() diff ers") + // clang-format off + printf ("g_thread_list is %p\n", g_thread_list_ptr); + return 0; //% v = self.dbg.GetSelectedTarget().FindFirstGlobalVariable('g_thread_list_ptr') + //% self.assertTrue(v.AddressOf().IsValid()) + //% self.assertFalse(v.AddressOf().AddressOf().IsValid()) + //% v_gla = v.GetChildMemberWithName('regs').GetLoadAddress() + //% v_aof = v.GetChildMemberWithName('regs').AddressOf().GetValueAsUnsigned(lldb.LLDB_INVALID_ADDRESS) + //% expr = '(%s)0x%x' % (v.GetType().GetName(), v.GetValueAsUnsigned(0)) + //% e = v.CreateValueFromExpression('e', expr) + //% e_gla = e.GetChildMemberWithName('regs').GetLoadAddress() + //% e_aof = e.GetChildMemberWithName('regs').AddressOf().GetValueAsUnsigned(lldb.LLDB_INVALID_ADDRESS) + //% self.assertTrue(v_gla == e_gla, "GetLoadAddress() diff ers") + //% self.assertTrue(v_aof == e_aof, "AddressOf() diff ers") } From lldb-commits at lists.llvm.org Mon May 5 08:53:11 2025 From: lldb-commits at lists.llvm.org (Ilia Kuklin via lldb-commits) Date: Mon, 05 May 2025 08:53:11 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Fix `ValueObject::AddressOf()` return value (PR #137688) In-Reply-To: Message-ID: <6818dee7.630a0220.367ecd.47e0@mx.google.com> https://github.com/kuilpd closed https://github.com/llvm/llvm-project/pull/137688 From lldb-commits at lists.llvm.org Mon May 5 08:58:56 2025 From: lldb-commits at lists.llvm.org (Rahul Joshi via lldb-commits) Date: Mon, 05 May 2025 08:58:56 -0700 (PDT) Subject: [Lldb-commits] [clang] [lldb] [llvm] [mlir] [NFC][Support] Add llvm::uninitialized_copy (PR #138174) In-Reply-To: Message-ID: <6818e040.170a0220.148790.4dae@mx.google.com> https://github.com/jurahul updated https://github.com/llvm/llvm-project/pull/138174 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Mon May 5 09:00:10 2025 From: lldb-commits at lists.llvm.org (Rahul Joshi via lldb-commits) Date: Mon, 05 May 2025 09:00:10 -0700 (PDT) Subject: [Lldb-commits] [clang] [lldb] [llvm] [mlir] [NFC][Support] Add llvm::uninitialized_copy (PR #138174) In-Reply-To: Message-ID: <6818e08a.630a0220.30586.5c65@mx.google.com> https://github.com/jurahul edited https://github.com/llvm/llvm-project/pull/138174 From lldb-commits at lists.llvm.org Mon May 5 09:13:38 2025 From: lldb-commits at lists.llvm.org (David Peixotto via lldb-commits) Date: Mon, 05 May 2025 09:13:38 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB][SBSaveCore] Sbsavecore subregions bug (PR #138206) In-Reply-To: Message-ID: <6818e3b2.170a0220.fa24b.44cf@mx.google.com> https://github.com/dmpots approved this pull request. LGTM! https://github.com/llvm/llvm-project/pull/138206 From lldb-commits at lists.llvm.org Mon May 5 09:34:37 2025 From: lldb-commits at lists.llvm.org (Rahul Joshi via lldb-commits) Date: Mon, 05 May 2025 09:34:37 -0700 (PDT) Subject: [Lldb-commits] [clang] [lldb] [llvm] [mlir] [NFC][Support] Add llvm::uninitialized_copy (PR #138174) In-Reply-To: Message-ID: <6818e89d.050a0220.142dbb.d2ad@mx.google.com> https://github.com/jurahul updated https://github.com/llvm/llvm-project/pull/138174 >From 278179a35bacc7c70dd1724e7ef7a41e9cdd999a Mon Sep 17 00:00:00 2001 From: Rahul Joshi Date: Wed, 30 Apr 2025 23:47:37 -0700 Subject: [PATCH] [NFC][Support] Add llvm::uninitialized_copy Add `llvm::uninitialized_copy` that accepts a range instead of two iterators for the source of the copy and adopt it. --- clang/include/clang/AST/DeclCXX.h | 6 +- clang/include/clang/AST/DeclOpenACC.h | 9 +-- clang/include/clang/AST/ExprCXX.h | 6 +- clang/include/clang/AST/OpenACCClause.h | 72 +++++++------------ clang/include/clang/AST/StmtOpenACC.h | 54 +++++++------- clang/include/clang/Sema/ParsedTemplate.h | 4 +- clang/lib/AST/Decl.cpp | 6 +- clang/lib/AST/DeclObjC.cpp | 4 +- clang/lib/AST/DeclTemplate.cpp | 11 ++- clang/lib/AST/Expr.cpp | 8 +-- clang/lib/AST/ExprCXX.cpp | 16 ++--- clang/lib/AST/OpenACCClause.cpp | 13 ++-- clang/lib/AST/StmtOpenACC.cpp | 4 +- clang/lib/AST/Type.cpp | 8 +-- clang/tools/libclang/CXIndexDataConsumer.cpp | 3 +- lldb/source/Utility/Checksum.cpp | 5 +- llvm/include/llvm/ADT/ArrayRef.h | 2 +- llvm/include/llvm/ADT/STLExtras.h | 5 ++ llvm/lib/Analysis/ScalarEvolution.cpp | 10 +-- llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 3 +- llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp | 4 +- llvm/lib/DebugInfo/MSF/MSFBuilder.cpp | 6 +- llvm/lib/IR/AttributeImpl.h | 2 +- llvm/lib/ObjectYAML/MinidumpEmitter.cpp | 2 +- llvm/lib/Support/FoldingSet.cpp | 4 +- llvm/lib/TableGen/Record.cpp | 23 +++--- .../AMDGPU/MCTargetDesc/AMDGPUMCExpr.cpp | 2 +- llvm/lib/Transforms/IPO/LowerTypeTests.cpp | 6 +- .../unittests/Support/TrailingObjectsTest.cpp | 19 ++--- mlir/include/mlir/IR/BuiltinAttributes.td | 2 +- mlir/include/mlir/Support/StorageUniquer.h | 4 +- .../Dialect/Affine/Analysis/NestedMatcher.cpp | 4 +- mlir/lib/IR/AffineMapDetail.h | 3 +- mlir/lib/IR/Location.cpp | 8 +-- mlir/lib/IR/MLIRContext.cpp | 2 +- mlir/lib/IR/TypeDetail.h | 3 +- mlir/lib/Tools/PDLL/AST/Nodes.cpp | 42 ++++------- 37 files changed, 164 insertions(+), 221 deletions(-) diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index dd325815ee28d..20720115bf6c3 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -3861,8 +3861,7 @@ class UsingPackDecl final InstantiatedFrom ? InstantiatedFrom->getDeclName() : DeclarationName()), InstantiatedFrom(InstantiatedFrom), NumExpansions(UsingDecls.size()) { - std::uninitialized_copy(UsingDecls.begin(), UsingDecls.end(), - getTrailingObjects()); + llvm::uninitialized_copy(UsingDecls, getTrailingObjects()); } void anchor() override; @@ -4233,8 +4232,7 @@ class DecompositionDecl final : VarDecl(Decomposition, C, DC, StartLoc, LSquareLoc, nullptr, T, TInfo, SC), NumBindings(Bindings.size()) { - std::uninitialized_copy(Bindings.begin(), Bindings.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Bindings, getTrailingObjects()); for (auto *B : Bindings) { B->setDecomposedDecl(this); if (B->isParameterPack() && B->getBinding()) { diff --git a/clang/include/clang/AST/DeclOpenACC.h b/clang/include/clang/AST/DeclOpenACC.h index 8c612fbf1ec07..905d9bf636ea1 100644 --- a/clang/include/clang/AST/DeclOpenACC.h +++ b/clang/include/clang/AST/DeclOpenACC.h @@ -18,6 +18,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/OpenACCClause.h" #include "clang/Basic/OpenACCKinds.h" +#include "llvm/ADT/STLExtras.h" namespace clang { @@ -85,8 +86,8 @@ class OpenACCDeclareDecl final : OpenACCConstructDecl(OpenACCDeclare, DC, OpenACCDirectiveKind::Declare, StartLoc, DirLoc, EndLoc) { // Initialize the trailing storage. - std::uninitialized_copy(Clauses.begin(), Clauses.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Clauses, + getTrailingObjects()); setClauseList(MutableArrayRef(getTrailingObjects(), Clauses.size())); @@ -136,8 +137,8 @@ class OpenACCRoutineDecl final assert(LParenLoc.isValid() && "Cannot represent implicit name with this declaration"); // Initialize the trailing storage. - std::uninitialized_copy(Clauses.begin(), Clauses.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Clauses, + getTrailingObjects()); setClauseList(MutableArrayRef(getTrailingObjects(), Clauses.size())); } diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 844f6dd90ae1d..15d94a8765dd6 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -41,6 +41,7 @@ #include "clang/Basic/TypeTraits.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/iterator_range.h" #include "llvm/Support/Casting.h" @@ -4417,7 +4418,7 @@ class SizeOfPackExpr final assert((!Length || PartialArgs.empty()) && "have partial args for non-dependent sizeof... expression"); auto *Args = getTrailingObjects(); - std::uninitialized_copy(PartialArgs.begin(), PartialArgs.end(), Args); + llvm::uninitialized_copy(PartialArgs, Args); setDependence(Length ? ExprDependence::None : ExprDependence::ValueInstantiation); } @@ -4522,8 +4523,7 @@ class PackIndexingExpr final FullySubstituted(FullySubstituted) { auto *Exprs = getTrailingObjects(); - std::uninitialized_copy(SubstitutedExprs.begin(), SubstitutedExprs.end(), - Exprs); + llvm::uninitialized_copy(SubstitutedExprs, Exprs); setDependence(computeDependence(this)); if (!isInstantiationDependent()) diff --git a/clang/include/clang/AST/OpenACCClause.h b/clang/include/clang/AST/OpenACCClause.h index 449bcb71f9f32..65377b91f83d3 100644 --- a/clang/include/clang/AST/OpenACCClause.h +++ b/clang/include/clang/AST/OpenACCClause.h @@ -13,9 +13,11 @@ #ifndef LLVM_CLANG_AST_OPENACCCLAUSE_H #define LLVM_CLANG_AST_OPENACCCLAUSE_H + #include "clang/AST/ASTContext.h" #include "clang/AST/StmtIterator.h" #include "clang/Basic/OpenACCKinds.h" +#include "llvm/ADT/STLExtras.h" #include #include @@ -291,8 +293,7 @@ class OpenACCDeviceTypeClause final "Only a single asterisk version is permitted, and must be the " "only one"); - std::uninitialized_copy(Archs.begin(), Archs.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Archs, getTrailingObjects()); } public: @@ -537,10 +538,9 @@ class OpenACCWaitClause final QueuesLoc(QueuesLoc) { // The first element of the trailing storage is always the devnum expr, // whether it is used or not. - std::uninitialized_copy(&DevNumExpr, &DevNumExpr + 1, - getTrailingObjects()); - std::uninitialized_copy(QueueIdExprs.begin(), QueueIdExprs.end(), - getTrailingObjects() + 1); + auto *Exprs = getTrailingObjects(); + llvm::uninitialized_copy(ArrayRef(DevNumExpr), Exprs); + llvm::uninitialized_copy(QueueIdExprs, Exprs + 1); setExprs( MutableArrayRef(getTrailingObjects(), QueueIdExprs.size() + 1)); } @@ -579,8 +579,7 @@ class OpenACCNumGangsClause final ArrayRef IntExprs, SourceLocation EndLoc) : OpenACCClauseWithExprs(OpenACCClauseKind::NumGangs, BeginLoc, LParenLoc, EndLoc) { - std::uninitialized_copy(IntExprs.begin(), IntExprs.end(), - getTrailingObjects()); + llvm::uninitialized_copy(IntExprs, getTrailingObjects()); setExprs(MutableArrayRef(getTrailingObjects(), IntExprs.size())); } @@ -609,8 +608,7 @@ class OpenACCTileClause final ArrayRef SizeExprs, SourceLocation EndLoc) : OpenACCClauseWithExprs(OpenACCClauseKind::Tile, BeginLoc, LParenLoc, EndLoc) { - std::uninitialized_copy(SizeExprs.begin(), SizeExprs.end(), - getTrailingObjects()); + llvm::uninitialized_copy(SizeExprs, getTrailingObjects()); setExprs(MutableArrayRef(getTrailingObjects(), SizeExprs.size())); } @@ -848,8 +846,7 @@ class OpenACCPrivateClause final ArrayRef VarList, SourceLocation EndLoc) : OpenACCClauseWithVarList(OpenACCClauseKind::Private, BeginLoc, LParenLoc, EndLoc) { - std::uninitialized_copy(VarList.begin(), VarList.end(), - getTrailingObjects()); + llvm::uninitialized_copy(VarList, getTrailingObjects()); setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); } @@ -871,8 +868,7 @@ class OpenACCFirstPrivateClause final ArrayRef VarList, SourceLocation EndLoc) : OpenACCClauseWithVarList(OpenACCClauseKind::FirstPrivate, BeginLoc, LParenLoc, EndLoc) { - std::uninitialized_copy(VarList.begin(), VarList.end(), - getTrailingObjects()); + llvm::uninitialized_copy(VarList, getTrailingObjects()); setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); } @@ -894,8 +890,7 @@ class OpenACCDevicePtrClause final ArrayRef VarList, SourceLocation EndLoc) : OpenACCClauseWithVarList(OpenACCClauseKind::DevicePtr, BeginLoc, LParenLoc, EndLoc) { - std::uninitialized_copy(VarList.begin(), VarList.end(), - getTrailingObjects()); + llvm::uninitialized_copy(VarList, getTrailingObjects()); setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); } @@ -917,8 +912,7 @@ class OpenACCAttachClause final ArrayRef VarList, SourceLocation EndLoc) : OpenACCClauseWithVarList(OpenACCClauseKind::Attach, BeginLoc, LParenLoc, EndLoc) { - std::uninitialized_copy(VarList.begin(), VarList.end(), - getTrailingObjects()); + llvm::uninitialized_copy(VarList, getTrailingObjects()); setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); } @@ -940,8 +934,7 @@ class OpenACCDetachClause final ArrayRef VarList, SourceLocation EndLoc) : OpenACCClauseWithVarList(OpenACCClauseKind::Detach, BeginLoc, LParenLoc, EndLoc) { - std::uninitialized_copy(VarList.begin(), VarList.end(), - getTrailingObjects()); + llvm::uninitialized_copy(VarList, getTrailingObjects()); setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); } @@ -963,8 +956,7 @@ class OpenACCDeleteClause final ArrayRef VarList, SourceLocation EndLoc) : OpenACCClauseWithVarList(OpenACCClauseKind::Delete, BeginLoc, LParenLoc, EndLoc) { - std::uninitialized_copy(VarList.begin(), VarList.end(), - getTrailingObjects()); + llvm::uninitialized_copy(VarList, getTrailingObjects()); setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); } @@ -986,8 +978,7 @@ class OpenACCUseDeviceClause final ArrayRef VarList, SourceLocation EndLoc) : OpenACCClauseWithVarList(OpenACCClauseKind::UseDevice, BeginLoc, LParenLoc, EndLoc) { - std::uninitialized_copy(VarList.begin(), VarList.end(), - getTrailingObjects()); + llvm::uninitialized_copy(VarList, getTrailingObjects()); setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); } @@ -1009,8 +1000,7 @@ class OpenACCNoCreateClause final ArrayRef VarList, SourceLocation EndLoc) : OpenACCClauseWithVarList(OpenACCClauseKind::NoCreate, BeginLoc, LParenLoc, EndLoc) { - std::uninitialized_copy(VarList.begin(), VarList.end(), - getTrailingObjects()); + llvm::uninitialized_copy(VarList, getTrailingObjects()); setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); } @@ -1032,8 +1022,7 @@ class OpenACCPresentClause final ArrayRef VarList, SourceLocation EndLoc) : OpenACCClauseWithVarList(OpenACCClauseKind::Present, BeginLoc, LParenLoc, EndLoc) { - std::uninitialized_copy(VarList.begin(), VarList.end(), - getTrailingObjects()); + llvm::uninitialized_copy(VarList, getTrailingObjects()); setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); } @@ -1054,8 +1043,7 @@ class OpenACCHostClause final ArrayRef VarList, SourceLocation EndLoc) : OpenACCClauseWithVarList(OpenACCClauseKind::Host, BeginLoc, LParenLoc, EndLoc) { - std::uninitialized_copy(VarList.begin(), VarList.end(), - getTrailingObjects()); + llvm::uninitialized_copy(VarList, getTrailingObjects()); setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); } @@ -1078,8 +1066,7 @@ class OpenACCDeviceClause final ArrayRef VarList, SourceLocation EndLoc) : OpenACCClauseWithVarList(OpenACCClauseKind::Device, BeginLoc, LParenLoc, EndLoc) { - std::uninitialized_copy(VarList.begin(), VarList.end(), - getTrailingObjects()); + llvm::uninitialized_copy(VarList, getTrailingObjects()); setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); } @@ -1107,8 +1094,7 @@ class OpenACCCopyClause final Spelling == OpenACCClauseKind::PCopy || Spelling == OpenACCClauseKind::PresentOrCopy) && "Invalid clause kind for copy-clause"); - std::uninitialized_copy(VarList.begin(), VarList.end(), - getTrailingObjects()); + llvm::uninitialized_copy(VarList, getTrailingObjects()); setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); } @@ -1142,8 +1128,7 @@ class OpenACCCopyInClause final Spelling == OpenACCClauseKind::PCopyIn || Spelling == OpenACCClauseKind::PresentOrCopyIn) && "Invalid clause kind for copyin-clause"); - std::uninitialized_copy(VarList.begin(), VarList.end(), - getTrailingObjects()); + llvm::uninitialized_copy(VarList, getTrailingObjects()); setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); } @@ -1176,8 +1161,7 @@ class OpenACCCopyOutClause final Spelling == OpenACCClauseKind::PCopyOut || Spelling == OpenACCClauseKind::PresentOrCopyOut) && "Invalid clause kind for copyout-clause"); - std::uninitialized_copy(VarList.begin(), VarList.end(), - getTrailingObjects()); + llvm::uninitialized_copy(VarList, getTrailingObjects()); setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); } @@ -1210,8 +1194,7 @@ class OpenACCCreateClause final Spelling == OpenACCClauseKind::PCreate || Spelling == OpenACCClauseKind::PresentOrCreate) && "Invalid clause kind for create-clause"); - std::uninitialized_copy(VarList.begin(), VarList.end(), - getTrailingObjects()); + llvm::uninitialized_copy(VarList, getTrailingObjects()); setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); } @@ -1241,8 +1224,7 @@ class OpenACCReductionClause final : OpenACCClauseWithVarList(OpenACCClauseKind::Reduction, BeginLoc, LParenLoc, EndLoc), Op(Operator) { - std::uninitialized_copy(VarList.begin(), VarList.end(), - getTrailingObjects()); + llvm::uninitialized_copy(VarList, getTrailingObjects()); setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); } @@ -1268,8 +1250,7 @@ class OpenACCLinkClause final ArrayRef VarList, SourceLocation EndLoc) : OpenACCClauseWithVarList(OpenACCClauseKind::Link, BeginLoc, LParenLoc, EndLoc) { - std::uninitialized_copy(VarList.begin(), VarList.end(), - getTrailingObjects()); + llvm::uninitialized_copy(VarList, getTrailingObjects()); setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); } @@ -1293,8 +1274,7 @@ class OpenACCDeviceResidentClause final ArrayRef VarList, SourceLocation EndLoc) : OpenACCClauseWithVarList(OpenACCClauseKind::DeviceResident, BeginLoc, LParenLoc, EndLoc) { - std::uninitialized_copy(VarList.begin(), VarList.end(), - getTrailingObjects()); + llvm::uninitialized_copy(VarList, getTrailingObjects()); setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); } diff --git a/clang/include/clang/AST/StmtOpenACC.h b/clang/include/clang/AST/StmtOpenACC.h index 39c4c81844911..9aae91372e264 100644 --- a/clang/include/clang/AST/StmtOpenACC.h +++ b/clang/include/clang/AST/StmtOpenACC.h @@ -17,6 +17,7 @@ #include "clang/AST/Stmt.h" #include "clang/Basic/OpenACCKinds.h" #include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/STLExtras.h" #include namespace clang { @@ -159,8 +160,8 @@ class OpenACCComputeConstruct final "represented by this type"); // Initialize the trailing storage. - std::uninitialized_copy(Clauses.begin(), Clauses.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Clauses, + getTrailingObjects()); setClauseList(MutableArrayRef(getTrailingObjects(), Clauses.size())); @@ -272,8 +273,8 @@ class OpenACCCombinedConstruct final "Only parallel loop, serial loop, and kernels loop constructs " "should be represented by this type"); - std::uninitialized_copy(Clauses.begin(), Clauses.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Clauses, + getTrailingObjects()); setClauseList(MutableArrayRef(getTrailingObjects(), Clauses.size())); } @@ -322,8 +323,8 @@ class OpenACCDataConstruct final : OpenACCAssociatedStmtConstruct(OpenACCDataConstructClass, OpenACCDirectiveKind::Data, Start, DirectiveLoc, End, StructuredBlock) { - std::uninitialized_copy(Clauses.begin(), Clauses.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Clauses, + getTrailingObjects()); setClauseList(MutableArrayRef(getTrailingObjects(), Clauses.size())); } @@ -368,8 +369,8 @@ class OpenACCEnterDataConstruct final : OpenACCConstructStmt(OpenACCEnterDataConstructClass, OpenACCDirectiveKind::EnterData, Start, DirectiveLoc, End) { - std::uninitialized_copy(Clauses.begin(), Clauses.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Clauses, + getTrailingObjects()); setClauseList(MutableArrayRef(getTrailingObjects(), Clauses.size())); } @@ -406,8 +407,8 @@ class OpenACCExitDataConstruct final : OpenACCConstructStmt(OpenACCExitDataConstructClass, OpenACCDirectiveKind::ExitData, Start, DirectiveLoc, End) { - std::uninitialized_copy(Clauses.begin(), Clauses.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Clauses, + getTrailingObjects()); setClauseList(MutableArrayRef(getTrailingObjects(), Clauses.size())); } @@ -447,8 +448,8 @@ class OpenACCHostDataConstruct final : OpenACCAssociatedStmtConstruct(OpenACCHostDataConstructClass, OpenACCDirectiveKind::HostData, Start, DirectiveLoc, End, StructuredBlock) { - std::uninitialized_copy(Clauses.begin(), Clauses.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Clauses, + getTrailingObjects()); setClauseList(MutableArrayRef(getTrailingObjects(), Clauses.size())); } @@ -525,11 +526,8 @@ class OpenACCWaitConstruct final "NumExprs should always be >= 1 because the 'devnum' " "expr is represented by a null if necessary"); - std::uninitialized_copy(&DevNumExpr, &DevNumExpr + 1, - getExprPtr()); - std::uninitialized_copy(QueueIdExprs.begin(), QueueIdExprs.end(), - getExprPtr() + 1); - + llvm::uninitialized_copy(ArrayRef(DevNumExpr), getExprPtr()); + llvm::uninitialized_copy(QueueIdExprs, getExprPtr() + 1); std::uninitialized_copy(const_cast(Clauses.begin()), const_cast(Clauses.end()), getTrailingObjects()); @@ -624,7 +622,7 @@ class OpenACCCacheConstruct final ParensLoc(LParenLoc, RParenLoc), ReadOnlyLoc(ReadOnlyLoc), NumVars(VarList.size()) { - std::uninitialized_copy(VarList.begin(), VarList.end(), getVarListPtr()); + llvm::uninitialized_copy(VarList, getVarListPtr()); } Expr **getVarListPtr() const { @@ -690,8 +688,8 @@ class OpenACCInitConstruct final : OpenACCConstructStmt(OpenACCInitConstructClass, OpenACCDirectiveKind::Init, Start, DirectiveLoc, End) { - std::uninitialized_copy(Clauses.begin(), Clauses.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Clauses, + getTrailingObjects()); setClauseList(MutableArrayRef(getTrailingObjects(), Clauses.size())); } @@ -730,8 +728,8 @@ class OpenACCShutdownConstruct final : OpenACCConstructStmt(OpenACCShutdownConstructClass, OpenACCDirectiveKind::Shutdown, Start, DirectiveLoc, End) { - std::uninitialized_copy(Clauses.begin(), Clauses.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Clauses, + getTrailingObjects()); setClauseList(MutableArrayRef(getTrailingObjects(), Clauses.size())); } @@ -770,8 +768,8 @@ class OpenACCSetConstruct final : OpenACCConstructStmt(OpenACCSetConstructClass, OpenACCDirectiveKind::Set, Start, DirectiveLoc, End) { - std::uninitialized_copy(Clauses.begin(), Clauses.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Clauses, + getTrailingObjects()); setClauseList(MutableArrayRef(getTrailingObjects(), Clauses.size())); } @@ -810,8 +808,8 @@ class OpenACCUpdateConstruct final : OpenACCConstructStmt(OpenACCUpdateConstructClass, OpenACCDirectiveKind::Update, Start, DirectiveLoc, End) { - std::uninitialized_copy(Clauses.begin(), Clauses.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Clauses, + getTrailingObjects()); setClauseList(MutableArrayRef(getTrailingObjects(), Clauses.size())); } @@ -859,8 +857,8 @@ class OpenACCAtomicConstruct final DirectiveLoc, End, AssociatedStmt), AtomicKind(AtKind) { // Initialize the trailing storage. - std::uninitialized_copy(Clauses.begin(), Clauses.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Clauses, + getTrailingObjects()); setClauseList(MutableArrayRef(getTrailingObjects(), Clauses.size())); diff --git a/clang/include/clang/Sema/ParsedTemplate.h b/clang/include/clang/Sema/ParsedTemplate.h index ac4dbbf294caf..cff7f04666358 100644 --- a/clang/include/clang/Sema/ParsedTemplate.h +++ b/clang/include/clang/Sema/ParsedTemplate.h @@ -19,6 +19,7 @@ #include "clang/Basic/TemplateKinds.h" #include "clang/Sema/DeclSpec.h" #include "clang/Sema/Ownership.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include #include @@ -249,8 +250,7 @@ namespace clang { Kind(TemplateKind), LAngleLoc(LAngleLoc), RAngleLoc(RAngleLoc), NumArgs(TemplateArgs.size()), ArgsInvalid(ArgsInvalid) { - std::uninitialized_copy(TemplateArgs.begin(), TemplateArgs.end(), - getTemplateArgs()); + llvm::uninitialized_copy(TemplateArgs, getTemplateArgs()); } ~TemplateIdAnnotation() = default; }; diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 61d497999b669..e6e9414746ede 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -3123,8 +3123,7 @@ FunctionDecl::DefaultedOrDeletedFunctionInfo::Create( Info->NumLookups = Lookups.size(); Info->HasDeletedMessage = DeletedMessage != nullptr; - std::uninitialized_copy(Lookups.begin(), Lookups.end(), - Info->getTrailingObjects()); + llvm::uninitialized_copy(Lookups, Info->getTrailingObjects()); if (DeletedMessage) *Info->getTrailingObjects() = DeletedMessage; return Info; @@ -5868,8 +5867,7 @@ ImportDecl::ImportDecl(DeclContext *DC, SourceLocation StartLoc, NextLocalImportAndComplete(nullptr, true) { assert(getNumModuleIdentifiers(Imported) == IdentifierLocs.size()); auto *StoredLocs = getTrailingObjects(); - std::uninitialized_copy(IdentifierLocs.begin(), IdentifierLocs.end(), - StoredLocs); + llvm::uninitialized_copy(IdentifierLocs, StoredLocs); } ImportDecl::ImportDecl(DeclContext *DC, SourceLocation StartLoc, diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp index 5c107325df30c..596262e217984 100644 --- a/clang/lib/AST/DeclObjC.cpp +++ b/clang/lib/AST/DeclObjC.cpp @@ -929,8 +929,8 @@ void ObjCMethodDecl::setParamsAndSelLocs(ASTContext &C, unsigned Size = sizeof(ParmVarDecl *) * NumParams + sizeof(SourceLocation) * SelLocs.size(); ParamsAndSelLocs = C.Allocate(Size); - std::uninitialized_copy(Params.begin(), Params.end(), getParams()); - std::uninitialized_copy(SelLocs.begin(), SelLocs.end(), getStoredSelLocs()); + llvm::uninitialized_copy(Params, getParams()); + llvm::uninitialized_copy(SelLocs, getStoredSelLocs()); } void ObjCMethodDecl::getSelectorLocs( diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 79a36109276f0..d058831b9f6bf 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -884,9 +884,8 @@ TemplateTemplateParmDecl::TemplateTemplateParmDecl( : TemplateDecl(TemplateTemplateParm, DC, L, Id, Params), TemplateParmPosition(D, P), Typename(Typename), ParameterPack(true), ExpandedParameterPack(true), NumExpandedParams(Expansions.size()) { - if (!Expansions.empty()) - std::uninitialized_copy(Expansions.begin(), Expansions.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Expansions, + getTrailingObjects()); } TemplateTemplateParmDecl * @@ -944,8 +943,7 @@ void TemplateTemplateParmDecl::setDefaultArgument( //===----------------------------------------------------------------------===// TemplateArgumentList::TemplateArgumentList(ArrayRef Args) : NumArguments(Args.size()) { - std::uninitialized_copy(Args.begin(), Args.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Args, getTrailingObjects()); } TemplateArgumentList * @@ -1172,8 +1170,7 @@ ImplicitConceptSpecializationDecl::CreateDeserialized( void ImplicitConceptSpecializationDecl::setTemplateArguments( ArrayRef Converted) { assert(Converted.size() == NumTemplateArgs); - std::uninitialized_copy(Converted.begin(), Converted.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Converted, getTrailingObjects()); } //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 59c0e47c7c195..a4483a285ed4f 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -2111,8 +2111,8 @@ ImplicitCastExpr *ImplicitCastExpr::Create(const ASTContext &C, QualType T, ImplicitCastExpr *E = new (Buffer) ImplicitCastExpr(T, Kind, Operand, PathSize, FPO, VK); if (PathSize) - std::uninitialized_copy_n(BasePath->data(), BasePath->size(), - E->getTrailingObjects()); + llvm::uninitialized_copy(*BasePath, + E->getTrailingObjects()); return E; } @@ -2138,8 +2138,8 @@ CStyleCastExpr *CStyleCastExpr::Create(const ASTContext &C, QualType T, CStyleCastExpr *E = new (Buffer) CStyleCastExpr(T, VK, K, Op, PathSize, FPO, WrittenTy, L, R); if (PathSize) - std::uninitialized_copy_n(BasePath->data(), BasePath->size(), - E->getTrailingObjects()); + llvm::uninitialized_copy(*BasePath, + E->getTrailingObjects()); return E; } diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index 169f11b611066..00bddce3a1ee2 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -772,8 +772,8 @@ CXXStaticCastExpr::Create(const ASTContext &C, QualType T, ExprValueKind VK, auto *E = new (Buffer) CXXStaticCastExpr(T, VK, K, Op, PathSize, WrittenTy, FPO, L, RParenLoc, AngleBrackets); if (PathSize) - std::uninitialized_copy_n(BasePath->data(), BasePath->size(), - E->getTrailingObjects()); + llvm::uninitialized_copy(*BasePath, + E->getTrailingObjects()); return E; } @@ -800,8 +800,8 @@ CXXDynamicCastExpr *CXXDynamicCastExpr::Create(const ASTContext &C, QualType T, new (Buffer) CXXDynamicCastExpr(T, VK, K, Op, PathSize, WrittenTy, L, RParenLoc, AngleBrackets); if (PathSize) - std::uninitialized_copy_n(BasePath->data(), BasePath->size(), - E->getTrailingObjects()); + llvm::uninitialized_copy(*BasePath, + E->getTrailingObjects()); return E; } @@ -863,8 +863,8 @@ CXXReinterpretCastExpr::Create(const ASTContext &C, QualType T, new (Buffer) CXXReinterpretCastExpr(T, VK, K, Op, PathSize, WrittenTy, L, RParenLoc, AngleBrackets); if (PathSize) - std::uninitialized_copy_n(BasePath->data(), BasePath->size(), - E->getTrailingObjects()); + llvm::uninitialized_copy(*BasePath, + E->getTrailingObjects()); return E; } @@ -911,8 +911,8 @@ CXXFunctionalCastExpr *CXXFunctionalCastExpr::Create( auto *E = new (Buffer) CXXFunctionalCastExpr(T, VK, Written, K, Op, PathSize, FPO, L, R); if (PathSize) - std::uninitialized_copy_n(BasePath->data(), BasePath->size(), - E->getTrailingObjects()); + llvm::uninitialized_copy(*BasePath, + E->getTrailingObjects()); return E; } diff --git a/clang/lib/AST/OpenACCClause.cpp b/clang/lib/AST/OpenACCClause.cpp index 9d3645e6da1ca..526ea89a2cee3 100644 --- a/clang/lib/AST/OpenACCClause.cpp +++ b/clang/lib/AST/OpenACCClause.cpp @@ -114,8 +114,7 @@ OpenACCSelfClause::OpenACCSelfClause(SourceLocation BeginLoc, : OpenACCClauseWithParams(OpenACCClauseKind::Self, BeginLoc, LParenLoc, EndLoc), HasConditionExpr(std::nullopt), NumExprs(VarList.size()) { - std::uninitialized_copy(VarList.begin(), VarList.end(), - getTrailingObjects()); + llvm::uninitialized_copy(VarList, getTrailingObjects()); } OpenACCSelfClause::OpenACCSelfClause(SourceLocation BeginLoc, @@ -127,8 +126,8 @@ OpenACCSelfClause::OpenACCSelfClause(SourceLocation BeginLoc, assert((!ConditionExpr || ConditionExpr->isInstantiationDependent() || ConditionExpr->getType()->isScalarType()) && "Condition expression type not scalar/dependent"); - std::uninitialized_copy(&ConditionExpr, &ConditionExpr + 1, - getTrailingObjects()); + llvm::uninitialized_copy(ArrayRef(ConditionExpr), + getTrailingObjects()); } OpenACCClause::child_range OpenACCClause::children() { @@ -167,11 +166,9 @@ OpenACCGangClause::OpenACCGangClause(SourceLocation BeginLoc, : OpenACCClauseWithExprs(OpenACCClauseKind::Gang, BeginLoc, LParenLoc, EndLoc) { assert(GangKinds.size() == IntExprs.size() && "Mismatch exprs/kind?"); - std::uninitialized_copy(IntExprs.begin(), IntExprs.end(), - getTrailingObjects()); + llvm::uninitialized_copy(IntExprs, getTrailingObjects()); setExprs(MutableArrayRef(getTrailingObjects(), IntExprs.size())); - std::uninitialized_copy(GangKinds.begin(), GangKinds.end(), - getTrailingObjects()); + llvm::uninitialized_copy(GangKinds, getTrailingObjects()); } OpenACCNumWorkersClause * diff --git a/clang/lib/AST/StmtOpenACC.cpp b/clang/lib/AST/StmtOpenACC.cpp index c45eca92dc874..268e411cee9c6 100644 --- a/clang/lib/AST/StmtOpenACC.cpp +++ b/clang/lib/AST/StmtOpenACC.cpp @@ -61,8 +61,8 @@ OpenACCLoopConstruct::OpenACCLoopConstruct( assert((Loop == nullptr || isa(Loop)) && "Associated Loop not a for loop?"); // Initialize the trailing storage. - std::uninitialized_copy(Clauses.begin(), Clauses.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Clauses, + getTrailingObjects()); setClauseList(MutableArrayRef(getTrailingObjects(), Clauses.size())); diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index fbd09141bc541..70258d7a69876 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -3807,13 +3807,13 @@ FunctionProtoType::FunctionProtoType(QualType result, ArrayRef params, ArrayRef SrcFX = epi.FunctionEffects.effects(); auto *DestFX = getTrailingObjects(); - std::uninitialized_copy(SrcFX.begin(), SrcFX.end(), DestFX); + llvm::uninitialized_copy(SrcFX, DestFX); ArrayRef SrcConds = epi.FunctionEffects.conditions(); if (!SrcConds.empty()) { ExtraBits.EffectsHaveConditions = true; auto *DestConds = getTrailingObjects(); - std::uninitialized_copy(SrcConds.begin(), SrcConds.end(), DestConds); + llvm::uninitialized_copy(SrcConds, DestConds); assert(std::any_of(SrcConds.begin(), SrcConds.end(), [](const EffectConditionExpr &EC) { if (const Expr *E = EC.getCondition()) @@ -4157,9 +4157,7 @@ PackIndexingType::PackIndexingType(const ASTContext &Context, computeDependence(Pattern, IndexExpr, Expansions)), Context(Context), Pattern(Pattern), IndexExpr(IndexExpr), Size(Expansions.size()), FullySubstituted(FullySubstituted) { - - std::uninitialized_copy(Expansions.begin(), Expansions.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Expansions, getTrailingObjects()); } UnsignedOrNone PackIndexingType::getSelectedIndex() const { diff --git a/clang/tools/libclang/CXIndexDataConsumer.cpp b/clang/tools/libclang/CXIndexDataConsumer.cpp index ced94e13baf12..2b2e70d60d1d6 100644 --- a/clang/tools/libclang/CXIndexDataConsumer.cpp +++ b/clang/tools/libclang/CXIndexDataConsumer.cpp @@ -15,6 +15,7 @@ #include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclVisitor.h" #include "clang/Frontend/ASTUnit.h" +#include "llvm/ADT/STLExtras.h" using namespace clang; using namespace clang::index; @@ -409,7 +410,7 @@ const char *ScratchAlloc::toCStr(StringRef Str) { const char *ScratchAlloc::copyCStr(StringRef Str) { char *buf = IdxCtx.StrScratch.Allocate(Str.size() + 1); - std::uninitialized_copy(Str.begin(), Str.end(), buf); + llvm::uninitialized_copy(Str, buf); buf[Str.size()] = '\0'; return buf; } diff --git a/lldb/source/Utility/Checksum.cpp b/lldb/source/Utility/Checksum.cpp index 8943b4e128520..343785ed33954 100644 --- a/lldb/source/Utility/Checksum.cpp +++ b/lldb/source/Utility/Checksum.cpp @@ -21,10 +21,7 @@ Checksum &Checksum::operator=(const Checksum &checksum) { return *this; } -void Checksum::SetMD5(llvm::MD5::MD5Result md5) { - const constexpr size_t md5_length = 16; - std::uninitialized_copy_n(md5.begin(), md5_length, m_checksum.begin()); -} +void Checksum::SetMD5(llvm::MD5::MD5Result md5) { m_checksum = md5; } Checksum::operator bool() const { return !llvm::equal(m_checksum, g_sentinel); } diff --git a/llvm/include/llvm/ADT/ArrayRef.h b/llvm/include/llvm/ADT/ArrayRef.h index 1f2433b9a7667..4819c88471345 100644 --- a/llvm/include/llvm/ADT/ArrayRef.h +++ b/llvm/include/llvm/ADT/ArrayRef.h @@ -190,7 +190,7 @@ namespace llvm { // copy - Allocate copy in Allocator and return ArrayRef to it. template MutableArrayRef copy(Allocator &A) { T *Buff = A.template Allocate(Length); - std::uninitialized_copy(begin(), end(), Buff); + llvm::uninitialized_copy(*this, Buff); return MutableArrayRef(Buff, Length); } diff --git a/llvm/include/llvm/ADT/STLExtras.h b/llvm/include/llvm/ADT/STLExtras.h index dc0443c9244be..8926489faf391 100644 --- a/llvm/include/llvm/ADT/STLExtras.h +++ b/llvm/include/llvm/ADT/STLExtras.h @@ -2038,6 +2038,11 @@ template auto mismatch(R1 &&Range1, R2 &&Range2) { adl_end(Range2)); } +template +auto uninitialized_copy(R &&Src, IterTy Dst) { + return std::uninitialized_copy(adl_begin(Src), adl_end(Src), Dst); +} + template void stable_sort(R &&Range) { std::stable_sort(adl_begin(Range), adl_end(Range)); diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp index 43d200f1153d0..ac69ad598a65a 100644 --- a/llvm/lib/Analysis/ScalarEvolution.cpp +++ b/llvm/lib/Analysis/ScalarEvolution.cpp @@ -2981,7 +2981,7 @@ ScalarEvolution::getOrCreateAddExpr(ArrayRef Ops, static_cast(UniqueSCEVs.FindNodeOrInsertPos(ID, IP)); if (!S) { const SCEV **O = SCEVAllocator.Allocate(Ops.size()); - std::uninitialized_copy(Ops.begin(), Ops.end(), O); + llvm::uninitialized_copy(Ops, O); S = new (SCEVAllocator) SCEVAddExpr(ID.Intern(SCEVAllocator), O, Ops.size()); UniqueSCEVs.InsertNode(S, IP); @@ -3004,7 +3004,7 @@ ScalarEvolution::getOrCreateAddRecExpr(ArrayRef Ops, static_cast(UniqueSCEVs.FindNodeOrInsertPos(ID, IP)); if (!S) { const SCEV **O = SCEVAllocator.Allocate(Ops.size()); - std::uninitialized_copy(Ops.begin(), Ops.end(), O); + llvm::uninitialized_copy(Ops, O); S = new (SCEVAllocator) SCEVAddRecExpr(ID.Intern(SCEVAllocator), O, Ops.size(), L); UniqueSCEVs.InsertNode(S, IP); @@ -3027,7 +3027,7 @@ ScalarEvolution::getOrCreateMulExpr(ArrayRef Ops, static_cast(UniqueSCEVs.FindNodeOrInsertPos(ID, IP)); if (!S) { const SCEV **O = SCEVAllocator.Allocate(Ops.size()); - std::uninitialized_copy(Ops.begin(), Ops.end(), O); + llvm::uninitialized_copy(Ops, O); S = new (SCEVAllocator) SCEVMulExpr(ID.Intern(SCEVAllocator), O, Ops.size()); UniqueSCEVs.InsertNode(S, IP); @@ -3932,7 +3932,7 @@ const SCEV *ScalarEvolution::getMinMaxExpr(SCEVTypes Kind, if (ExistingSCEV) return ExistingSCEV; const SCEV **O = SCEVAllocator.Allocate(Ops.size()); - std::uninitialized_copy(Ops.begin(), Ops.end(), O); + llvm::uninitialized_copy(Ops, O); SCEV *S = new (SCEVAllocator) SCEVMinMaxExpr(ID.Intern(SCEVAllocator), Kind, O, Ops.size()); @@ -4319,7 +4319,7 @@ ScalarEvolution::getSequentialMinMaxExpr(SCEVTypes Kind, return ExistingSCEV; const SCEV **O = SCEVAllocator.Allocate(Ops.size()); - std::uninitialized_copy(Ops.begin(), Ops.end(), O); + llvm::uninitialized_copy(Ops, O); SCEV *S = new (SCEVAllocator) SCEVSequentialMinMaxExpr(ID.Intern(SCEVAllocator), Kind, O, Ops.size()); diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index 4074ed65885c7..b533731e8dda3 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -542,8 +542,7 @@ class BitcodeConstant final : public Value, : Value(Ty, SubclassID), Opcode(Info.Opcode), Flags(Info.Flags), NumOperands(OpIDs.size()), BlockAddressBB(Info.BlockAddressBB), SrcElemTy(Info.SrcElemTy), InRange(Info.InRange) { - std::uninitialized_copy(OpIDs.begin(), OpIDs.end(), - getTrailingObjects()); + llvm::uninitialized_copy(OpIDs, getTrailingObjects()); } BitcodeConstant &operator=(const BitcodeConstant &) = delete; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp index 55a3bfa459c3c..08b58669b3eb3 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp @@ -340,8 +340,8 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, return createStringError( errc::invalid_argument, "failed to parse file entry because the MD5 hash is invalid"); - std::uninitialized_copy_n(Value.getAsBlock()->begin(), 16, - FileEntry.Checksum.begin()); + llvm::uninitialized_copy(*Value.getAsBlock(), + FileEntry.Checksum.begin()); break; default: break; diff --git a/llvm/lib/DebugInfo/MSF/MSFBuilder.cpp b/llvm/lib/DebugInfo/MSF/MSFBuilder.cpp index ed2d14dd79e45..bb3411bb9568e 100644 --- a/llvm/lib/DebugInfo/MSF/MSFBuilder.cpp +++ b/llvm/lib/DebugInfo/MSF/MSFBuilder.cpp @@ -286,8 +286,7 @@ Expected MSFBuilder::generateLayout() { SB->NumBlocks = FreeBlocks.size(); ulittle32_t *DirBlocks = Allocator.Allocate(NumDirectoryBlocks); - std::uninitialized_copy_n(DirectoryBlocks.begin(), NumDirectoryBlocks, - DirBlocks); + llvm::uninitialized_copy(DirectoryBlocks, DirBlocks); L.DirectoryBlocks = ArrayRef(DirBlocks, NumDirectoryBlocks); // The stream sizes should be re-allocated as a stable pointer and the stream @@ -300,8 +299,7 @@ Expected MSFBuilder::generateLayout() { Sizes[I] = StreamData[I].first; ulittle32_t *BlockList = Allocator.Allocate(StreamData[I].second.size()); - std::uninitialized_copy_n(StreamData[I].second.begin(), - StreamData[I].second.size(), BlockList); + llvm::uninitialized_copy(StreamData[I].second, BlockList); L.StreamMap[I] = ArrayRef(BlockList, StreamData[I].second.size()); } diff --git a/llvm/lib/IR/AttributeImpl.h b/llvm/lib/IR/AttributeImpl.h index 59cc489ade40d..98d1bad7680ab 100644 --- a/llvm/lib/IR/AttributeImpl.h +++ b/llvm/lib/IR/AttributeImpl.h @@ -258,7 +258,7 @@ class ConstantRangeListAttributeImpl final : EnumAttributeImpl(ConstantRangeListAttrEntry, Kind), Size(Val.size()) { assert(Size > 0); ConstantRange *TrailingCR = getTrailingObjects(); - std::uninitialized_copy(Val.begin(), Val.end(), TrailingCR); + llvm::uninitialized_copy(Val, TrailingCR); } ~ConstantRangeListAttributeImpl() { diff --git a/llvm/lib/ObjectYAML/MinidumpEmitter.cpp b/llvm/lib/ObjectYAML/MinidumpEmitter.cpp index 44cdfbdd80ea5..b27155162be6b 100644 --- a/llvm/lib/ObjectYAML/MinidumpEmitter.cpp +++ b/llvm/lib/ObjectYAML/MinidumpEmitter.cpp @@ -86,7 +86,7 @@ std::pair> BlobAllocator::allocateNewArray(const iterator_range &Range) { size_t Num = std::distance(Range.begin(), Range.end()); MutableArrayRef Array(Temporaries.Allocate(Num), Num); - std::uninitialized_copy(Range.begin(), Range.end(), Array.begin()); + llvm::uninitialized_copy(Range, Array.begin()); return {allocateArray(Array), Array}; } diff --git a/llvm/lib/Support/FoldingSet.cpp b/llvm/lib/Support/FoldingSet.cpp index 419bf67407684..977e4ca8c26ef 100644 --- a/llvm/lib/Support/FoldingSet.cpp +++ b/llvm/lib/Support/FoldingSet.cpp @@ -131,7 +131,7 @@ bool FoldingSetNodeID::operator<(FoldingSetNodeIDRef RHS) const { FoldingSetNodeIDRef FoldingSetNodeID::Intern(BumpPtrAllocator &Allocator) const { unsigned *New = Allocator.Allocate(Bits.size()); - std::uninitialized_copy(Bits.begin(), Bits.end(), New); + llvm::uninitialized_copy(Bits, New); return FoldingSetNodeIDRef(New, Bits.size()); } @@ -142,7 +142,7 @@ FoldingSetNodeID::Intern(BumpPtrAllocator &Allocator) const { /// singly-linked-list. In order to make deletion more efficient, we make /// the list circular, so we can delete a node without computing its hash. /// The problem with this is that the start of the hash buckets are not -/// Nodes. If NextInBucketPtr is a bucket pointer, this method returns null: +/// Nodes. If NextInBucketPtr is a bucket pointer, this method returns null: /// use GetBucketPtr when this happens. static FoldingSetBase::Node *GetNextPtr(void *NextInBucketPtr) { // The low bit is set if this is the pointer back to the bucket. diff --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp index 67f237cb7b634..f3d54e6083e48 100644 --- a/llvm/lib/TableGen/Record.cpp +++ b/llvm/lib/TableGen/Record.cpp @@ -240,8 +240,7 @@ static void ProfileRecordRecTy(FoldingSetNodeID &ID, RecordRecTy::RecordRecTy(RecordKeeper &RK, ArrayRef Classes) : RecTy(RecordRecTyKind, RK), NumClasses(Classes.size()) { - std::uninitialized_copy(Classes.begin(), Classes.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Classes, getTrailingObjects()); } const RecordRecTy *RecordRecTy::get(RecordKeeper &RK, @@ -474,8 +473,7 @@ static void ProfileBitsInit(FoldingSetNodeID &ID, BitsInit::BitsInit(RecordKeeper &RK, ArrayRef Bits) : TypedInit(IK_BitsInit, BitsRecTy::get(RK, Bits.size())), NumBits(Bits.size()) { - std::uninitialized_copy(Bits.begin(), Bits.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Bits, getTrailingObjects()); } BitsInit *BitsInit::get(RecordKeeper &RK, ArrayRef Bits) { @@ -708,8 +706,7 @@ static void ProfileListInit(FoldingSetNodeID &ID, ArrayRef Range, ListInit::ListInit(ArrayRef Elements, const RecTy *EltTy) : TypedInit(IK_ListInit, ListRecTy::get(EltTy)), NumValues(Elements.size()) { - std::uninitialized_copy(Elements.begin(), Elements.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Elements, getTrailingObjects()); } const ListInit *ListInit::get(ArrayRef Elements, @@ -2435,8 +2432,7 @@ VarDefInit::VarDefInit(SMLoc Loc, const Record *Class, ArrayRef Args) : TypedInit(IK_VarDefInit, RecordRecTy::get(Class)), Loc(Loc), Class(Class), NumArgs(Args.size()) { - std::uninitialized_copy(Args.begin(), Args.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Args, getTrailingObjects()); } const VarDefInit *VarDefInit::get(SMLoc Loc, const Record *Class, @@ -2621,9 +2617,8 @@ CondOpInit::CondOpInit(ArrayRef Conds, ArrayRef Values, const RecTy *Type) : TypedInit(IK_CondOpInit, Type), NumConds(Conds.size()), ValType(Type) { auto *TrailingObjects = getTrailingObjects(); - std::uninitialized_copy(Conds.begin(), Conds.end(), TrailingObjects); - std::uninitialized_copy(Values.begin(), Values.end(), - TrailingObjects + NumConds); + llvm::uninitialized_copy(Conds, TrailingObjects); + llvm::uninitialized_copy(Values, TrailingObjects + NumConds); } void CondOpInit::Profile(FoldingSetNodeID &ID) const { @@ -2757,10 +2752,8 @@ DagInit::DagInit(const Init *V, const StringInit *VN, ArrayRef ArgNames) : TypedInit(IK_DagInit, DagRecTy::get(V->getRecordKeeper())), Val(V), ValName(VN), NumArgs(Args.size()) { - std::uninitialized_copy(Args.begin(), Args.end(), - getTrailingObjects()); - std::uninitialized_copy(ArgNames.begin(), ArgNames.end(), - getTrailingObjects()); + llvm::uninitialized_copy(Args, getTrailingObjects()); + llvm::uninitialized_copy(ArgNames, getTrailingObjects()); } const DagInit *DagInit::get(const Init *V, const StringInit *VN, diff --git a/llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUMCExpr.cpp b/llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUMCExpr.cpp index 678a7be1f2456..e5601dca17b6a 100644 --- a/llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUMCExpr.cpp +++ b/llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUMCExpr.cpp @@ -36,7 +36,7 @@ AMDGPUMCExpr::AMDGPUMCExpr(VariantKind Kind, ArrayRef Args, // allocation (e.g., through SmallVector's grow). RawArgs = static_cast( Ctx.allocate(sizeof(const MCExpr *) * Args.size())); - std::uninitialized_copy(Args.begin(), Args.end(), RawArgs); + llvm::uninitialized_copy(Args, RawArgs); this->Args = ArrayRef(RawArgs, Args.size()); } diff --git a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp index c93568943e833..d855647095550 100644 --- a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp +++ b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp @@ -297,8 +297,7 @@ class GlobalTypeMember final : TrailingObjects { GTM->NTypes = Types.size(); GTM->IsJumpTableCanonical = IsJumpTableCanonical; GTM->IsExported = IsExported; - std::uninitialized_copy(Types.begin(), Types.end(), - GTM->getTrailingObjects()); + llvm::copy(Types, GTM->getTrailingObjects()); return GTM; } @@ -330,8 +329,7 @@ struct ICallBranchFunnel final Call->CI = CI; Call->UniqueId = UniqueId; Call->NTargets = Targets.size(); - std::uninitialized_copy(Targets.begin(), Targets.end(), - Call->getTrailingObjects()); + llvm::copy(Targets, Call->getTrailingObjects()); return Call; } diff --git a/llvm/unittests/Support/TrailingObjectsTest.cpp b/llvm/unittests/Support/TrailingObjectsTest.cpp index e2656b2229ca6..e36979e75d7f7 100644 --- a/llvm/unittests/Support/TrailingObjectsTest.cpp +++ b/llvm/unittests/Support/TrailingObjectsTest.cpp @@ -7,6 +7,8 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/TrailingObjects.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" #include "gtest/gtest.h" using namespace llvm; @@ -23,17 +25,16 @@ class Class1 final : protected TrailingObjects { protected: size_t numTrailingObjects(OverloadToken) const { return NumShorts; } - Class1(int *ShortArray, unsigned NumShorts) : NumShorts(NumShorts) { - std::uninitialized_copy(ShortArray, ShortArray + NumShorts, - getTrailingObjects()); + Class1(ArrayRef ShortArray) : NumShorts(ShortArray.size()) { + llvm::copy(ShortArray, getTrailingObjects()); } public: - static Class1 *create(int *ShortArray, unsigned NumShorts) { - void *Mem = ::operator new(totalSizeToAlloc(NumShorts)); - return new (Mem) Class1(ShortArray, NumShorts); + static Class1 *create(ArrayRef ShortArray) { + void *Mem = ::operator new(totalSizeToAlloc(ShortArray.size())); + return new (Mem) Class1(ShortArray); } - void operator delete(void *p) { ::operator delete(p); } + void operator delete(void *Ptr) { ::operator delete(Ptr); } short get(unsigned Num) const { return getTrailingObjects()[Num]; } @@ -81,7 +82,7 @@ class Class2 final : protected TrailingObjects { *C->getTrailingObjects() = D; return C; } - void operator delete(void *p) { ::operator delete(p); } + void operator delete(void *Ptr) { ::operator delete(Ptr); } short getShort() const { if (!HasShort) @@ -106,7 +107,7 @@ class Class2 final : protected TrailingObjects { TEST(TrailingObjects, OneArg) { int arr[] = {1, 2, 3}; - Class1 *C = Class1::create(arr, 3); + Class1 *C = Class1::create(arr); EXPECT_EQ(sizeof(Class1), sizeof(unsigned)); EXPECT_EQ(Class1::additionalSizeToAlloc(1), sizeof(short)); EXPECT_EQ(Class1::additionalSizeToAlloc(3), sizeof(short) * 3); diff --git a/mlir/include/mlir/IR/BuiltinAttributes.td b/mlir/include/mlir/IR/BuiltinAttributes.td index 0169f4b38bbe0..1ff9a8d36cd22 100644 --- a/mlir/include/mlir/IR/BuiltinAttributes.td +++ b/mlir/include/mlir/IR/BuiltinAttributes.td @@ -158,7 +158,7 @@ def Builtin_DenseArrayRawDataParameter : ArrayRefParameter< if (!$_self.empty()) { auto *alloc = static_cast( $_allocator.allocate($_self.size(), alignof(uint64_t))); - std::uninitialized_copy($_self.begin(), $_self.end(), alloc); + llvm::uninitialized_copy($_self, alloc); $_dst = ArrayRef(alloc, $_self.size()); } }]; diff --git a/mlir/include/mlir/Support/StorageUniquer.h b/mlir/include/mlir/Support/StorageUniquer.h index 26bdf09abba21..6756c4390276f 100644 --- a/mlir/include/mlir/Support/StorageUniquer.h +++ b/mlir/include/mlir/Support/StorageUniquer.h @@ -99,7 +99,7 @@ class StorageUniquer { if (elements.empty()) return std::nullopt; auto result = allocator.Allocate(elements.size()); - std::uninitialized_copy(elements.begin(), elements.end(), result); + llvm::uninitialized_copy(elements, result); return ArrayRef(result, elements.size()); } @@ -110,7 +110,7 @@ class StorageUniquer { return StringRef(); char *result = allocator.Allocate(str.size() + 1); - std::uninitialized_copy(str.begin(), str.end(), result); + llvm::uninitialized_copy(str, result); result[str.size()] = 0; return StringRef(result, str.size()); } diff --git a/mlir/lib/Dialect/Affine/Analysis/NestedMatcher.cpp b/mlir/lib/Dialect/Affine/Analysis/NestedMatcher.cpp index be13a89c7ab4f..c158b98381a82 100644 --- a/mlir/lib/Dialect/Affine/Analysis/NestedMatcher.cpp +++ b/mlir/lib/Dialect/Affine/Analysis/NestedMatcher.cpp @@ -28,7 +28,7 @@ NestedMatch NestedMatch::build(Operation *operation, ArrayRef nestedMatches) { auto *result = allocator()->Allocate(); auto *children = allocator()->Allocate(nestedMatches.size()); - std::uninitialized_copy(nestedMatches.begin(), nestedMatches.end(), children); + llvm::uninitialized_copy(nestedMatches, children); new (result) NestedMatch(); result->matchedOperation = operation; result->matchedChildren = @@ -46,7 +46,7 @@ void NestedPattern::copyNestedToThis(ArrayRef nested) { return; auto *newNested = allocator()->Allocate(nested.size()); - std::uninitialized_copy(nested.begin(), nested.end(), newNested); + llvm::uninitialized_copy(nested, newNested); nestedPatterns = ArrayRef(newNested, nested.size()); } diff --git a/mlir/lib/IR/AffineMapDetail.h b/mlir/lib/IR/AffineMapDetail.h index 732c7fd1d3a12..32c9734f23a36 100644 --- a/mlir/lib/IR/AffineMapDetail.h +++ b/mlir/lib/IR/AffineMapDetail.h @@ -56,8 +56,7 @@ struct AffineMapStorage final res->numDims = std::get<0>(key); res->numSymbols = std::get<1>(key); res->numResults = results.size(); - std::uninitialized_copy(results.begin(), results.end(), - res->getTrailingObjects()); + llvm::uninitialized_copy(results, res->getTrailingObjects()); return res; } }; diff --git a/mlir/lib/IR/Location.cpp b/mlir/lib/IR/Location.cpp index 506a6c1fc16fb..8ae33022be24f 100644 --- a/mlir/lib/IR/Location.cpp +++ b/mlir/lib/IR/Location.cpp @@ -58,11 +58,11 @@ struct FileLineColRangeAttrStorage final auto *result = ::new (rawMem) FileLineColRangeAttrStorage( std::move(std::get<0>(tblgenKey)), locEnc - 1); if (numInArray > 0) { - result->startLine = std::get<1>(tblgenKey)[0]; + ArrayRef elements = std::get<1>(tblgenKey); + result->startLine = elements[0]; // Copy in the element types into the trailing storage. - std::uninitialized_copy(std::next(std::get<1>(tblgenKey).begin()), - std::get<1>(tblgenKey).end(), - result->getTrailingObjects()); + llvm::uninitialized_copy(elements.drop_front(), + result->getTrailingObjects()); } return result; } diff --git a/mlir/lib/IR/MLIRContext.cpp b/mlir/lib/IR/MLIRContext.cpp index 87782e84dd6e4..d43dcc5a5e2fd 100644 --- a/mlir/lib/IR/MLIRContext.cpp +++ b/mlir/lib/IR/MLIRContext.cpp @@ -362,7 +362,7 @@ template static ArrayRef copyArrayRefInto(llvm::BumpPtrAllocator &allocator, ArrayRef elements) { auto result = allocator.Allocate(elements.size()); - std::uninitialized_copy(elements.begin(), elements.end(), result); + llvm::uninitialized_copy(elements, result); return ArrayRef(result, elements.size()); } diff --git a/mlir/lib/IR/TypeDetail.h b/mlir/lib/IR/TypeDetail.h index 1d65fccb82b8e..19f3690c3d2dc 100644 --- a/mlir/lib/IR/TypeDetail.h +++ b/mlir/lib/IR/TypeDetail.h @@ -116,8 +116,7 @@ struct TupleTypeStorage final auto *result = ::new (rawMem) TupleTypeStorage(key.size()); // Copy in the element types into the trailing storage. - std::uninitialized_copy(key.begin(), key.end(), - result->getTrailingObjects()); + llvm::uninitialized_copy(key, result->getTrailingObjects()); return result; } diff --git a/mlir/lib/Tools/PDLL/AST/Nodes.cpp b/mlir/lib/Tools/PDLL/AST/Nodes.cpp index ee2fe0fb9e3c3..159ce6235662b 100644 --- a/mlir/lib/Tools/PDLL/AST/Nodes.cpp +++ b/mlir/lib/Tools/PDLL/AST/Nodes.cpp @@ -195,8 +195,7 @@ CompoundStmt *CompoundStmt::create(Context &ctx, SMRange loc, void *rawData = ctx.getAllocator().Allocate(allocSize, alignof(CompoundStmt)); CompoundStmt *stmt = new (rawData) CompoundStmt(loc, children.size()); - std::uninitialized_copy(children.begin(), children.end(), - stmt->getChildren().begin()); + llvm::uninitialized_copy(children, stmt->getChildren().begin()); return stmt; } @@ -230,8 +229,7 @@ ReplaceStmt *ReplaceStmt::create(Context &ctx, SMRange loc, Expr *rootOp, void *rawData = ctx.getAllocator().Allocate(allocSize, alignof(ReplaceStmt)); ReplaceStmt *stmt = new (rawData) ReplaceStmt(loc, rootOp, replExprs.size()); - std::uninitialized_copy(replExprs.begin(), replExprs.end(), - stmt->getReplExprs().begin()); + llvm::uninitialized_copy(replExprs, stmt->getReplExprs().begin()); return stmt; } @@ -276,8 +274,7 @@ CallExpr *CallExpr::create(Context &ctx, SMRange loc, Expr *callable, CallExpr *expr = new (rawData) CallExpr(loc, resultType, callable, arguments.size(), isNegated); - std::uninitialized_copy(arguments.begin(), arguments.end(), - expr->getArguments().begin()); + llvm::uninitialized_copy(arguments, expr->getArguments().begin()); return expr; } @@ -321,12 +318,9 @@ OperationExpr::create(Context &ctx, SMRange loc, const ods::Operation *odsOp, OperationExpr *opExpr = new (rawData) OperationExpr(loc, resultType, name, operands.size(), resultTypes.size(), attributes.size(), name->getLoc()); - std::uninitialized_copy(operands.begin(), operands.end(), - opExpr->getOperands().begin()); - std::uninitialized_copy(resultTypes.begin(), resultTypes.end(), - opExpr->getResultTypes().begin()); - std::uninitialized_copy(attributes.begin(), attributes.end(), - opExpr->getAttributes().begin()); + llvm::uninitialized_copy(operands, opExpr->getOperands().begin()); + llvm::uninitialized_copy(resultTypes, opExpr->getResultTypes().begin()); + llvm::uninitialized_copy(attributes, opExpr->getAttributes().begin()); return opExpr; } @@ -344,8 +338,7 @@ RangeExpr *RangeExpr::create(Context &ctx, SMRange loc, void *rawData = ctx.getAllocator().Allocate(allocSize, alignof(TupleExpr)); RangeExpr *expr = new (rawData) RangeExpr(loc, type, elements.size()); - std::uninitialized_copy(elements.begin(), elements.end(), - expr->getElements().begin()); + llvm::uninitialized_copy(elements, expr->getElements().begin()); return expr; } @@ -364,8 +357,7 @@ TupleExpr *TupleExpr::create(Context &ctx, SMRange loc, TupleType type = TupleType::get(ctx, llvm::to_vector(elementTypes), names); TupleExpr *expr = new (rawData) TupleExpr(loc, type); - std::uninitialized_copy(elements.begin(), elements.end(), - expr->getElements().begin()); + llvm::uninitialized_copy(elements, expr->getElements().begin()); return expr; } @@ -482,10 +474,8 @@ UserConstraintDecl *UserConstraintDecl::createImpl( UserConstraintDecl *decl = new (rawData) UserConstraintDecl(name, inputs.size(), hasNativeInputTypes, results.size(), codeBlock, body, resultType); - std::uninitialized_copy(inputs.begin(), inputs.end(), - decl->getInputs().begin()); - std::uninitialized_copy(results.begin(), results.end(), - decl->getResults().begin()); + llvm::uninitialized_copy(inputs, decl->getInputs().begin()); + llvm::uninitialized_copy(results, decl->getResults().begin()); if (hasNativeInputTypes) { StringRef *nativeInputTypesPtr = decl->getTrailingObjects(); for (unsigned i = 0, e = inputs.size(); i < e; ++i) @@ -547,10 +537,8 @@ UserRewriteDecl *UserRewriteDecl::createImpl(Context &ctx, const Name &name, UserRewriteDecl *decl = new (rawData) UserRewriteDecl( name, inputs.size(), results.size(), codeBlock, body, resultType); - std::uninitialized_copy(inputs.begin(), inputs.end(), - decl->getInputs().begin()); - std::uninitialized_copy(results.begin(), results.end(), - decl->getResults().begin()); + llvm::uninitialized_copy(inputs, decl->getInputs().begin()); + llvm::uninitialized_copy(results, decl->getResults().begin()); return decl; } @@ -567,8 +555,7 @@ VariableDecl *VariableDecl::create(Context &ctx, const Name &name, Type type, VariableDecl *varDecl = new (rawData) VariableDecl(name, type, initExpr, constraints.size()); - std::uninitialized_copy(constraints.begin(), constraints.end(), - varDecl->getConstraints().begin()); + llvm::uninitialized_copy(constraints, varDecl->getConstraints().begin()); return varDecl; } @@ -581,7 +568,6 @@ Module *Module::create(Context &ctx, SMLoc loc, ArrayRef children) { void *rawData = ctx.getAllocator().Allocate(allocSize, alignof(Module)); Module *module = new (rawData) Module(loc, children.size()); - std::uninitialized_copy(children.begin(), children.end(), - module->getChildren().begin()); + llvm::uninitialized_copy(children, module->getChildren().begin()); return module; } From lldb-commits at lists.llvm.org Mon May 5 09:35:00 2025 From: lldb-commits at lists.llvm.org (Rahul Joshi via lldb-commits) Date: Mon, 05 May 2025 09:35:00 -0700 (PDT) Subject: [Lldb-commits] [clang] [lldb] [llvm] [mlir] [NFC][Support] Add llvm::uninitialized_copy (PR #138174) In-Reply-To: Message-ID: <6818e8b4.050a0220.17264b.c9c0@mx.google.com> https://github.com/jurahul edited https://github.com/llvm/llvm-project/pull/138174 From lldb-commits at lists.llvm.org Mon May 5 09:41:51 2025 From: lldb-commits at lists.llvm.org (Ilia Kuklin via lldb-commits) Date: Mon, 05 May 2025 09:41:51 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) Message-ID: https://github.com/kuilpd created https://github.com/llvm/llvm-project/pull/138551 None >From cfe7359bd16c1e87932e2ebb8bcdfc88130e9729 Mon Sep 17 00:00:00 2001 From: Ilia Kuklin Date: Wed, 30 Apr 2025 22:03:50 +0500 Subject: [PATCH] [LLDB] Add array subscription and integer parsing to DIL --- lldb/docs/dil-expr-lang.ebnf | 12 +- lldb/include/lldb/ValueObject/DILAST.h | 46 +++++ lldb/include/lldb/ValueObject/DILEval.h | 6 + lldb/include/lldb/ValueObject/DILLexer.h | 3 + lldb/include/lldb/ValueObject/DILParser.h | 3 + lldb/source/ValueObject/DILAST.cpp | 10 ++ lldb/source/ValueObject/DILEval.cpp | 159 ++++++++++++++++++ lldb/source/ValueObject/DILLexer.cpp | 43 ++++- lldb/source/ValueObject/DILParser.cpp | 79 ++++++++- .../var-dil/basics/ArraySubscript/Makefile | 3 + .../TestFrameVarDILArraySubscript.py | 88 ++++++++++ .../var-dil/basics/ArraySubscript/main.cpp | 31 ++++ lldb/unittests/ValueObject/DILLexerTests.cpp | 34 +++- 13 files changed, 506 insertions(+), 11 deletions(-) create mode 100644 lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/Makefile create mode 100644 lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py create mode 100644 lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf index c8bf4231b3e4a..0cbb5403785db 100644 --- a/lldb/docs/dil-expr-lang.ebnf +++ b/lldb/docs/dil-expr-lang.ebnf @@ -6,16 +6,20 @@ expression = unary_expression ; unary_expression = unary_operator expression - | primary_expression ; + | postfix_expression ; unary_operator = "*" | "&" ; -primary_expression = id_expression +postfix_expression = primary_expression + | postfix_expression "[" expression "]"; + +primary_expression = numeric_literal + | id_expression | "(" expression ")"; id_expression = unqualified_id | qualified_id - | register ; + | register ; unqualified_id = identifier ; @@ -24,6 +28,8 @@ qualified_id = ["::"] [nested_name_specifier] unqualified_id identifier = ? C99 Identifier ? ; +numeric_literal = ? C99 Integer constant ? ; + register = "$" ? Register name ? ; nested_name_specifier = type_name "::" diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h index fe3827ef0516a..6908deed7aee3 100644 --- a/lldb/include/lldb/ValueObject/DILAST.h +++ b/lldb/include/lldb/ValueObject/DILAST.h @@ -18,8 +18,10 @@ namespace lldb_private::dil { /// The various types DIL AST nodes (used by the DIL parser). enum class NodeKind { + eArraySubscriptNode, eErrorNode, eIdentifierNode, + eScalarLiteralNode, eUnaryOpNode, }; @@ -71,6 +73,26 @@ class ErrorNode : public ASTNode { } }; +class ScalarLiteralNode : public ASTNode { +public: + ScalarLiteralNode(uint32_t location, lldb::BasicType type, Scalar value) + : ASTNode(location, NodeKind::eScalarLiteralNode), m_type(type), + m_value(value) {} + + llvm::Expected Accept(Visitor *v) const override; + + lldb::BasicType GetType() const { return m_type; } + Scalar GetValue() const & { return m_value; } + + static bool classof(const ASTNode *node) { + return node->GetKind() == NodeKind::eScalarLiteralNode; + } + +private: + lldb::BasicType m_type; + Scalar m_value; +}; + class IdentifierNode : public ASTNode { public: IdentifierNode(uint32_t location, std::string name) @@ -108,6 +130,26 @@ class UnaryOpNode : public ASTNode { ASTNodeUP m_operand; }; +class ArraySubscriptNode : public ASTNode { +public: + ArraySubscriptNode(uint32_t location, ASTNodeUP lhs, ASTNodeUP rhs) + : ASTNode(location, NodeKind::eArraySubscriptNode), m_lhs(std::move(lhs)), + m_rhs(std::move(rhs)) {} + + llvm::Expected Accept(Visitor *v) const override; + + ASTNode *lhs() const { return m_lhs.get(); } + ASTNode *rhs() const { return m_rhs.get(); } + + static bool classof(const ASTNode *node) { + return node->GetKind() == NodeKind::eArraySubscriptNode; + } + +private: + ASTNodeUP m_lhs; + ASTNodeUP m_rhs; +}; + /// 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 @@ -116,9 +158,13 @@ class Visitor { public: virtual ~Visitor() = default; virtual llvm::Expected + Visit(const ScalarLiteralNode *node) = 0; + virtual llvm::Expected Visit(const IdentifierNode *node) = 0; virtual llvm::Expected Visit(const UnaryOpNode *node) = 0; + virtual llvm::Expected + Visit(const ArraySubscriptNode *node) = 0; }; } // namespace lldb_private::dil diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h index b1dd3fdb49739..e3df80862b082 100644 --- a/lldb/include/lldb/ValueObject/DILEval.h +++ b/lldb/include/lldb/ValueObject/DILEval.h @@ -47,9 +47,15 @@ class Interpreter : Visitor { llvm::Expected Evaluate(const ASTNode *node); private: + llvm::Expected + Visit(const ScalarLiteralNode *node) override; llvm::Expected Visit(const IdentifierNode *node) override; llvm::Expected Visit(const UnaryOpNode *node) override; + llvm::Expected + Visit(const ArraySubscriptNode *node) override; + + lldb::ValueObjectSP PointerAdd(lldb::ValueObjectSP lhs, int64_t offset); // 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 3508b8b7a85c6..0176db73835e9 100644 --- a/lldb/include/lldb/ValueObject/DILLexer.h +++ b/lldb/include/lldb/ValueObject/DILLexer.h @@ -29,7 +29,10 @@ class Token { eof, identifier, l_paren, + l_square, + numeric_constant, r_paren, + r_square, star, }; diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h index f5c00b1040ef4..af237ece0228d 100644 --- a/lldb/include/lldb/ValueObject/DILParser.h +++ b/lldb/include/lldb/ValueObject/DILParser.h @@ -84,12 +84,15 @@ class DILParser { ASTNodeUP ParseExpression(); ASTNodeUP ParseUnaryExpression(); + ASTNodeUP ParsePostfixExpression(); ASTNodeUP ParsePrimaryExpression(); std::string ParseNestedNameSpecifier(); std::string ParseIdExpression(); std::string ParseUnqualifiedId(); + ASTNodeUP ParseNumericLiteral(); + ASTNodeUP ParseNumericConstant(); void BailOut(const std::string &error, uint32_t loc, uint16_t err_len); diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp index ea847587501ee..ceb4a4aa99c4f 100644 --- a/lldb/source/ValueObject/DILAST.cpp +++ b/lldb/source/ValueObject/DILAST.cpp @@ -15,6 +15,11 @@ llvm::Expected ErrorNode::Accept(Visitor *v) const { llvm_unreachable("Attempting to Visit a DIL ErrorNode."); } +llvm::Expected +ScalarLiteralNode::Accept(Visitor *v) const { + return v->Visit(this); +} + llvm::Expected IdentifierNode::Accept(Visitor *v) const { return v->Visit(this); } @@ -23,4 +28,9 @@ llvm::Expected UnaryOpNode::Accept(Visitor *v) const { return v->Visit(this); } +llvm::Expected +ArraySubscriptNode::Accept(Visitor *v) const { + return v->Visit(this); +} + } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index 15a66d4866305..527017da7c019 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -18,6 +18,22 @@ namespace lldb_private::dil { +static lldb::ValueObjectSP +ArrayToPointerConversion(lldb::ValueObjectSP valobj, + std::shared_ptr ctx) { + assert(valobj->IsArrayType() && + "an argument to array-to-pointer conversion must be an array"); + + uint64_t addr = valobj->GetLoadAddress(); + llvm::StringRef name = "result"; + ExecutionContext exe_ctx; + ctx->CalculateExecutionContext(exe_ctx); + return ValueObject::CreateValueObjectFromAddress( + name, addr, exe_ctx, + valobj->GetCompilerType().GetArrayElementType(ctx.get()).GetPointerType(), + /* do_deref */ false); +} + static lldb::ValueObjectSP LookupStaticIdentifier( VariableList &variable_list, std::shared_ptr exe_scope, llvm::StringRef name_ref, llvm::StringRef unqualified_name) { @@ -214,6 +230,45 @@ llvm::Expected Interpreter::Evaluate(const ASTNode *node) { return value_or_error; } +static CompilerType GetBasicType(std::shared_ptr ctx, + lldb::BasicType basic_type) { + static std::unordered_map basic_types; + auto type = basic_types.find(basic_type); + if (type != basic_types.end()) { + std::string type_name((type->second).GetTypeName().AsCString()); + // Only return the found type if it's valid. + if (type_name != "") + return type->second; + } + + lldb::TargetSP target_sp = ctx->CalculateTarget(); + if (target_sp) { + for (auto type_system_sp : target_sp->GetScratchTypeSystems()) + if (auto compiler_type = + type_system_sp->GetBasicTypeFromAST(basic_type)) { + basic_types.insert({basic_type, compiler_type}); + return compiler_type; + } + } + CompilerType empty_type; + return empty_type; +} + +llvm::Expected +Interpreter::Visit(const ScalarLiteralNode *node) { + CompilerType result_type = GetBasicType(m_exe_ctx_scope, node->GetType()); + Scalar value = node->GetValue(); + + if (result_type.IsInteger() || result_type.IsNullPtrType() || + result_type.IsPointerType()) { + llvm::APInt val = value.GetAPSInt(); + return ValueObject::CreateValueObjectFromAPInt(m_target, val, result_type, + "result"); + } + + return lldb::ValueObjectSP(); +} + llvm::Expected Interpreter::Visit(const IdentifierNode *node) { lldb::DynamicValueType use_dynamic = m_default_dynamic; @@ -272,4 +327,108 @@ Interpreter::Visit(const UnaryOpNode *node) { m_expr, "invalid ast: unexpected binary operator", node->GetLocation()); } +lldb::ValueObjectSP Interpreter::PointerAdd(lldb::ValueObjectSP lhs, + int64_t offset) { + uint64_t byte_size = 0; + if (auto temp = lhs->GetCompilerType().GetPointeeType().GetByteSize( + lhs->GetTargetSP().get())) + byte_size = *temp; + uintptr_t addr = lhs->GetValueAsUnsigned(0) + offset * byte_size; + + llvm::StringRef name = "result"; + ExecutionContext exe_ctx(m_target.get(), false); + return ValueObject::CreateValueObjectFromAddress(name, addr, exe_ctx, + lhs->GetCompilerType(), + /* do_deref */ false); +} + +llvm::Expected +Interpreter::Visit(const ArraySubscriptNode *node) { + auto lhs_or_err = Evaluate(node->lhs()); + if (!lhs_or_err) { + return lhs_or_err; + } + lldb::ValueObjectSP base = *lhs_or_err; + auto rhs_or_err = Evaluate(node->rhs()); + if (!rhs_or_err) { + return rhs_or_err; + } + lldb::ValueObjectSP index = *rhs_or_err; + + Status error; + if (base->GetCompilerType().IsReferenceType()) { + base = base->Dereference(error); + if (error.Fail()) + return error.ToError(); + } + if (index->GetCompilerType().IsReferenceType()) { + index = index->Dereference(error); + if (error.Fail()) + return error.ToError(); + } + + auto index_type = index->GetCompilerType(); + if (!index_type.IsIntegerOrUnscopedEnumerationType()) + return llvm::make_error( + m_expr, "array subscript is not an integer", node->GetLocation()); + + // Check to see if 'base' has a synthetic value; if so, try using that. + if (base->HasSyntheticValue()) { + lldb::ValueObjectSP synthetic = base->GetSyntheticValue(); + if (synthetic && synthetic != base) { + uint32_t num_children = synthetic->GetNumChildrenIgnoringErrors(); + // Verify that the 'index' is not out-of-range for the declared type. + if (index->GetValueAsSigned(0) >= num_children) { + auto message = + llvm::formatv("array index {0} is not valid for \"({1}) {2}\"", + index->GetValueAsSigned(0), + base->GetTypeName().AsCString(""), + base->GetName().AsCString()); + return llvm::make_error(m_expr, message, + node->GetLocation()); + } + + uint64_t child_idx = index->GetValueAsUnsigned(0); + if (static_cast(child_idx) < + synthetic->GetNumChildrenIgnoringErrors()) { + lldb::ValueObjectSP child_valobj_sp = + synthetic->GetChildAtIndex(child_idx); + if (child_valobj_sp) { + return child_valobj_sp; + } + } + } + } + + auto base_type = base->GetCompilerType(); + if (!base_type.IsPointerType() && !base_type.IsArrayType()) + return llvm::make_error( + m_expr, "subscripted value is not an array or pointer", + node->GetLocation()); + if (base_type.IsPointerToVoid()) + return llvm::make_error( + m_expr, "subscript of pointer to incomplete type 'void'", + node->GetLocation()); + + if (base_type.IsArrayType()) + base = ArrayToPointerConversion(base, m_exe_ctx_scope); + + CompilerType item_type = base->GetCompilerType().GetPointeeType(); + lldb::addr_t base_addr = base->GetValueAsUnsigned(0); + + llvm::StringRef name = "result"; + ExecutionContext exe_ctx(m_target.get(), false); + // Create a pointer and add the index, i.e. "base + index". + lldb::ValueObjectSP value = + PointerAdd(ValueObject::CreateValueObjectFromAddress( + name, base_addr, exe_ctx, item_type.GetPointerType(), + /*do_deref=*/false), + index->GetValueAsSigned(0)); + + lldb::ValueObjectSP val2 = value->Dereference(error); + if (error.Fail()) + return error.ToError(); + return val2; +} + } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp index b9c2e7971e3b4..3222032feef19 100644 --- a/lldb/source/ValueObject/DILLexer.cpp +++ b/lldb/source/ValueObject/DILLexer.cpp @@ -13,6 +13,7 @@ #include "lldb/ValueObject/DILLexer.h" #include "lldb/Utility/Status.h" +#include "lldb/ValueObject/DILParser.h" #include "llvm/ADT/StringSwitch.h" namespace lldb_private::dil { @@ -29,8 +30,14 @@ llvm::StringRef Token::GetTokenName(Kind kind) { return "identifier"; case Kind::l_paren: return "l_paren"; + case Kind::l_square: + return "l_square"; + case Kind::numeric_constant: + return "numeric_constant"; case Kind::r_paren: return "r_paren"; + case Kind::r_square: + return "r_square"; case Token::star: return "star"; } @@ -57,6 +64,29 @@ static std::optional IsWord(llvm::StringRef expr, return candidate; } +static void ConsumeNumberBody(char &prev_ch, llvm::StringRef::iterator &cur_pos, + llvm::StringRef expr) { + while (cur_pos != expr.end() && + (IsDigit(*cur_pos) || IsLetter(*cur_pos) || *cur_pos == '_')) { + prev_ch = *cur_pos; + cur_pos++; + } +} + +static std::optional IsNumber(llvm::StringRef expr, + llvm::StringRef &remainder) { + llvm::StringRef::iterator cur_pos = remainder.begin(); + llvm::StringRef::iterator start = cur_pos; + char prev_ch = 0; + if (IsDigit(*start)) { + ConsumeNumberBody(prev_ch, cur_pos, expr); + llvm::StringRef number = expr.substr(start - expr.begin(), cur_pos - start); + if (remainder.consume_front(number)) + return number; + } + return std::nullopt; +} + llvm::Expected DILLexer::Create(llvm::StringRef expr) { std::vector tokens; llvm::StringRef remainder = expr; @@ -81,13 +111,19 @@ llvm::Expected DILLexer::Lex(llvm::StringRef expr, return Token(Token::eof, "", (uint32_t)expr.size()); uint32_t position = cur_pos - expr.begin(); + std::optional maybe_number = IsNumber(expr, remainder); + if (maybe_number) { + std::string number = (*maybe_number).str(); + return Token(Token::numeric_constant, number, position); + } std::optional maybe_word = IsWord(expr, remainder); if (maybe_word) return Token(Token::identifier, maybe_word->str(), position); constexpr std::pair operators[] = { - {Token::amp, "&"}, {Token::coloncolon, "::"}, {Token::l_paren, "("}, - {Token::r_paren, ")"}, {Token::star, "*"}, + {Token::amp, "&"}, {Token::coloncolon, "::"}, {Token::l_paren, "("}, + {Token::l_square, "["}, {Token::r_paren, ")"}, {Token::r_square, "]"}, + {Token::star, "*"}, }; for (auto [kind, str] : operators) { if (remainder.consume_front(str)) @@ -95,7 +131,8 @@ llvm::Expected DILLexer::Lex(llvm::StringRef expr, } // Unrecognized character(s) in string; unable to lex it. - return llvm::createStringError("Unable to lex input string"); + return llvm::make_error(expr, "unrecognized token", + position); } } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp index 2c78eae8cf6bf..24a6f0c909a0a 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -111,7 +111,36 @@ ASTNodeUP DILParser::ParseUnaryExpression() { llvm_unreachable("invalid token kind"); } } - return ParsePrimaryExpression(); + return ParsePostfixExpression(); +} + +// Parse a postfix_expression. +// +// postfix_expression: +// primary_expression +// postfix_expression "[" expression "]" +// +ASTNodeUP DILParser::ParsePostfixExpression() { + ASTNodeUP lhs = ParsePrimaryExpression(); + while (CurToken().Is(Token::l_square)) { + uint32_t loc = CurToken().GetLocation(); + Token token = CurToken(); + switch (token.GetKind()) { + case Token::l_square: { + m_dil_lexer.Advance(); + auto rhs = ParseExpression(); + Expect(Token::r_square); + m_dil_lexer.Advance(); + lhs = std::make_unique(loc, std::move(lhs), + std::move(rhs)); + break; + } + default: + llvm_unreachable("invalid token"); + } + } + + return lhs; } // Parse a primary_expression. @@ -121,6 +150,8 @@ ASTNodeUP DILParser::ParseUnaryExpression() { // "(" expression ")" // ASTNodeUP DILParser::ParsePrimaryExpression() { + if (CurToken().Is(Token::numeric_constant)) + return ParseNumericLiteral(); if (CurToken().IsOneOf({Token::coloncolon, Token::identifier})) { // Save the source location for the diagnostics message. uint32_t loc = CurToken().GetLocation(); @@ -280,6 +311,52 @@ void DILParser::BailOut(const std::string &error, uint32_t loc, m_dil_lexer.ResetTokenIdx(m_dil_lexer.NumLexedTokens() - 1); } +// Parse a numeric_literal. +// +// numeric_literal: +// ? Token::numeric_constant ? +// +ASTNodeUP DILParser::ParseNumericLiteral() { + Expect(Token::numeric_constant); + ASTNodeUP numeric_constant = ParseNumericConstant(); + if (numeric_constant->GetKind() == NodeKind::eErrorNode) { + BailOut(llvm::formatv("Failed to parse token as numeric-constant: {0}", + CurToken()), + CurToken().GetLocation(), CurToken().GetSpelling().length()); + return std::make_unique(); + } + m_dil_lexer.Advance(); + return numeric_constant; +} + +static constexpr std::pair type_suffixes[] = { + {"ull", lldb::eBasicTypeUnsignedLongLong}, + {"ul", lldb::eBasicTypeUnsignedLong}, + {"u", lldb::eBasicTypeUnsignedInt}, + {"ll", lldb::eBasicTypeLongLong}, + {"l", lldb::eBasicTypeLong}, +}; + +ASTNodeUP DILParser::ParseNumericConstant() { + Token token = CurToken(); + auto spelling = token.GetSpelling(); + llvm::StringRef spelling_ref = spelling; + lldb::BasicType type = lldb::eBasicTypeInt; + for (auto [suffix, t] : type_suffixes) { + if (spelling_ref.consume_back_insensitive(suffix)) { + type = t; + break; + } + } + llvm::APInt raw_value; + if (!spelling_ref.getAsInteger(0, raw_value)) { + Scalar scalar_value(raw_value); + return std::make_unique(token.GetLocation(), type, + scalar_value); + } + return std::make_unique(); +} + void DILParser::Expect(Token::Kind kind) { if (CurToken().IsNot(kind)) { BailOut(llvm::formatv("expected {0}, got: {1}", kind, CurToken()), diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/Makefile b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py new file mode 100644 index 0000000000000..e142889124613 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py @@ -0,0 +1,88 @@ +""" +Test DIL array subscript. +""" + +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +from lldbsuite.test import lldbutil + + +class TestFrameVarDILGlobalVariableLookup(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def expect_var_path(self, expr, compare_to_framevar=False, value=None, type=None): + value_dil = super().expect_var_path(expr, value=value, type=type) + if compare_to_framevar: + self.runCmd("settings set target.experimental.use-DIL false") + value_frv = super().expect_var_path(expr, value=value, type=type) + self.runCmd("settings set target.experimental.use-DIL true") + self.assertEqual(value_dil.GetValue(), value_frv.GetValue()) + + def test_dereference(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp") + ) + + self.runCmd("settings set target.experimental.use-DIL true") + + # Test int[] and int* + self.expect_var_path("int_arr[0]", True, value="1") + self.expect_var_path("int_ptr[1]", True, value="2") + self.expect_var_path("int_arr[enum_one]", value="2") + + # Test when base and index are references. + self.expect_var_path("int_arr[0]", True, value="1") + self.expect_var_path("int_arr[idx_1_ref]", value="2") + self.expect_var_path("int_arr[enum_ref]", value="2") + self.expect_var_path("int_arr_ref[0]", value="1") + self.expect_var_path("int_arr_ref[idx_1_ref]", value="2") + self.expect_var_path("int_arr_ref[enum_ref]", value="2") + + # Test when base and index are typedefs. + self.expect_var_path("td_int_arr[0]", True, value="1") + self.expect_var_path("td_int_arr[td_int_idx_1]", value="2") + self.expect_var_path("td_int_arr[td_td_int_idx_2]", value="3") + self.expect_var_path("td_int_ptr[0]", True, value="1") + self.expect_var_path("td_int_ptr[td_int_idx_1]", value="2") + self.expect_var_path("td_int_ptr[td_td_int_idx_2]", value="3") + + # Both typedefs and refs + self.expect_var_path("td_int_arr_ref[td_int_idx_1_ref]", value="2") + + # Test for index out of bounds. + self.expect_var_path("int_arr[42]", True, type="int") + self.expect_var_path("int_arr[100]", True, type="int") + + # Test address-of of the subscripted value. + self.expect_var_path("*(&int_arr[1])", value="2") + + # Test synthetic value subscription + self.expect_var_path("vector[1]", value="2") + + # Test for negative index. + self.expect( + "frame var 'int_arr[-1]'", + error=True, + substrs=["unrecognized token"], + ) + + # Test for floating point index + self.expect( + "frame var 'int_arr[1.0]'", + error=True, + substrs=["unrecognized token"], + ) + + # Base should be a "pointer to T" and index should be of an integral type. + self.expect( + "frame var 'int_arr[int_ptr]'", + error=True, + substrs=["array subscript is not an integer"], + ) + self.expect( + "frame var '1[2]'", + error=True, + substrs=["subscripted value is not an array or pointer"], + ) diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp new file mode 100644 index 0000000000000..b34e4670b9db6 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp @@ -0,0 +1,31 @@ +#include + +int main(int argc, char **argv) { + int int_arr[] = {1, 2, 3}; + int *int_ptr = int_arr; + int(&int_arr_ref)[3] = int_arr; + + int idx_1 = 1; + const int &idx_1_ref = idx_1; + + typedef int td_int_t; + typedef td_int_t td_td_int_t; + typedef int *td_int_ptr_t; + typedef int &td_int_ref_t; + + td_int_t td_int_idx_1 = 1; + td_td_int_t td_td_int_idx_2 = 2; + + td_int_t td_int_arr[3] = {1, 2, 3}; + td_int_ptr_t td_int_ptr = td_int_arr; + + td_int_ref_t td_int_idx_1_ref = td_int_idx_1; + td_int_t(&td_int_arr_ref)[3] = td_int_arr; + + enum Enum { kZero, kOne } enum_one = kOne; + Enum &enum_ref = enum_one; + + std::vector vector = {1, 2, 3}; + + return 0; // Set a breakpoint here +} diff --git a/lldb/unittests/ValueObject/DILLexerTests.cpp b/lldb/unittests/ValueObject/DILLexerTests.cpp index 9afa957901ae7..203763b91afc4 100644 --- a/lldb/unittests/ValueObject/DILLexerTests.cpp +++ b/lldb/unittests/ValueObject/DILLexerTests.cpp @@ -121,11 +121,11 @@ TEST(DILLexerTests, IdentifiersTest) { "a_b", "this", "self", "a", "MyName", "namespace"}; // The lexer can lex these strings, but they should not be identifiers. - std::vector invalid_identifiers = {"", "::", "(", ")"}; + std::vector invalid_identifiers = {"", "::", "(", ")", "0abc"}; // The lexer is expected to fail attempting to lex these strings (it cannot // create valid tokens out of them). - std::vector invalid_tok_strings = {"234", "2a", "2", "1MyName"}; + std::vector invalid_tok_strings = {"#include", "a at a"}; // Verify that all of the valid identifiers come out as identifier tokens. for (auto &str : valid_identifiers) { @@ -150,7 +150,33 @@ TEST(DILLexerTests, IdentifiersTest) { DILLexer lexer(*maybe_lexer); Token token = lexer.GetCurrentToken(); EXPECT_TRUE(token.IsNot(Token::identifier)); - EXPECT_TRUE(token.IsOneOf( - {Token::eof, Token::coloncolon, Token::l_paren, Token::r_paren})); + EXPECT_TRUE(token.IsOneOf({Token::eof, Token::coloncolon, Token::l_paren, + Token::r_paren, Token::numeric_constant})); + } +} + +TEST(DILLexerTests, NumbersTest) { + // These strings should lex into number tokens. + std::vector valid_numbers = {"123", "0x123", "0123", "0b101", + "1_000"}; + + // The lexer can lex these strings, but they should not be numbers. + std::vector invalid_numbers = {"", "x123", "b123"}; + + for (auto &str : valid_numbers) { + SCOPED_TRACE(str); + EXPECT_THAT_EXPECTED(ExtractTokenData(str), + llvm::HasValue(testing::ElementsAre( + testing::Pair(Token::numeric_constant, str)))); + } + // Verify that none of the invalid numbers come out as numeric tokens. + for (auto &str : invalid_numbers) { + SCOPED_TRACE(str); + llvm::Expected maybe_lexer = DILLexer::Create(str); + EXPECT_THAT_EXPECTED(maybe_lexer, llvm::Succeeded()); + DILLexer lexer(*maybe_lexer); + Token token = lexer.GetCurrentToken(); + EXPECT_TRUE(token.IsNot(Token::numeric_constant)); + EXPECT_TRUE(token.IsOneOf({Token::eof, Token::identifier})); } } From lldb-commits at lists.llvm.org Mon May 5 09:42:15 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Mon, 05 May 2025 09:42:15 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <6818ea67.170a0220.2edbfb.ea3e@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Ilia Kuklin (kuilpd)
Changes --- Patch is 27.37 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/138551.diff 13 Files Affected: - (modified) lldb/docs/dil-expr-lang.ebnf (+9-3) - (modified) lldb/include/lldb/ValueObject/DILAST.h (+46) - (modified) lldb/include/lldb/ValueObject/DILEval.h (+6) - (modified) lldb/include/lldb/ValueObject/DILLexer.h (+3) - (modified) lldb/include/lldb/ValueObject/DILParser.h (+3) - (modified) lldb/source/ValueObject/DILAST.cpp (+10) - (modified) lldb/source/ValueObject/DILEval.cpp (+159) - (modified) lldb/source/ValueObject/DILLexer.cpp (+40-3) - (modified) lldb/source/ValueObject/DILParser.cpp (+78-1) - (added) lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/Makefile (+3) - (added) lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py (+88) - (added) lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp (+31) - (modified) lldb/unittests/ValueObject/DILLexerTests.cpp (+30-4) ``````````diff diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf index c8bf4231b3e4a..0cbb5403785db 100644 --- a/lldb/docs/dil-expr-lang.ebnf +++ b/lldb/docs/dil-expr-lang.ebnf @@ -6,16 +6,20 @@ expression = unary_expression ; unary_expression = unary_operator expression - | primary_expression ; + | postfix_expression ; unary_operator = "*" | "&" ; -primary_expression = id_expression +postfix_expression = primary_expression + | postfix_expression "[" expression "]"; + +primary_expression = numeric_literal + | id_expression | "(" expression ")"; id_expression = unqualified_id | qualified_id - | register ; + | register ; unqualified_id = identifier ; @@ -24,6 +28,8 @@ qualified_id = ["::"] [nested_name_specifier] unqualified_id identifier = ? C99 Identifier ? ; +numeric_literal = ? C99 Integer constant ? ; + register = "$" ? Register name ? ; nested_name_specifier = type_name "::" diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h index fe3827ef0516a..6908deed7aee3 100644 --- a/lldb/include/lldb/ValueObject/DILAST.h +++ b/lldb/include/lldb/ValueObject/DILAST.h @@ -18,8 +18,10 @@ namespace lldb_private::dil { /// The various types DIL AST nodes (used by the DIL parser). enum class NodeKind { + eArraySubscriptNode, eErrorNode, eIdentifierNode, + eScalarLiteralNode, eUnaryOpNode, }; @@ -71,6 +73,26 @@ class ErrorNode : public ASTNode { } }; +class ScalarLiteralNode : public ASTNode { +public: + ScalarLiteralNode(uint32_t location, lldb::BasicType type, Scalar value) + : ASTNode(location, NodeKind::eScalarLiteralNode), m_type(type), + m_value(value) {} + + llvm::Expected Accept(Visitor *v) const override; + + lldb::BasicType GetType() const { return m_type; } + Scalar GetValue() const & { return m_value; } + + static bool classof(const ASTNode *node) { + return node->GetKind() == NodeKind::eScalarLiteralNode; + } + +private: + lldb::BasicType m_type; + Scalar m_value; +}; + class IdentifierNode : public ASTNode { public: IdentifierNode(uint32_t location, std::string name) @@ -108,6 +130,26 @@ class UnaryOpNode : public ASTNode { ASTNodeUP m_operand; }; +class ArraySubscriptNode : public ASTNode { +public: + ArraySubscriptNode(uint32_t location, ASTNodeUP lhs, ASTNodeUP rhs) + : ASTNode(location, NodeKind::eArraySubscriptNode), m_lhs(std::move(lhs)), + m_rhs(std::move(rhs)) {} + + llvm::Expected Accept(Visitor *v) const override; + + ASTNode *lhs() const { return m_lhs.get(); } + ASTNode *rhs() const { return m_rhs.get(); } + + static bool classof(const ASTNode *node) { + return node->GetKind() == NodeKind::eArraySubscriptNode; + } + +private: + ASTNodeUP m_lhs; + ASTNodeUP m_rhs; +}; + /// 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 @@ -116,9 +158,13 @@ class Visitor { public: virtual ~Visitor() = default; virtual llvm::Expected + Visit(const ScalarLiteralNode *node) = 0; + virtual llvm::Expected Visit(const IdentifierNode *node) = 0; virtual llvm::Expected Visit(const UnaryOpNode *node) = 0; + virtual llvm::Expected + Visit(const ArraySubscriptNode *node) = 0; }; } // namespace lldb_private::dil diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h index b1dd3fdb49739..e3df80862b082 100644 --- a/lldb/include/lldb/ValueObject/DILEval.h +++ b/lldb/include/lldb/ValueObject/DILEval.h @@ -47,9 +47,15 @@ class Interpreter : Visitor { llvm::Expected Evaluate(const ASTNode *node); private: + llvm::Expected + Visit(const ScalarLiteralNode *node) override; llvm::Expected Visit(const IdentifierNode *node) override; llvm::Expected Visit(const UnaryOpNode *node) override; + llvm::Expected + Visit(const ArraySubscriptNode *node) override; + + lldb::ValueObjectSP PointerAdd(lldb::ValueObjectSP lhs, int64_t offset); // 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 3508b8b7a85c6..0176db73835e9 100644 --- a/lldb/include/lldb/ValueObject/DILLexer.h +++ b/lldb/include/lldb/ValueObject/DILLexer.h @@ -29,7 +29,10 @@ class Token { eof, identifier, l_paren, + l_square, + numeric_constant, r_paren, + r_square, star, }; diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h index f5c00b1040ef4..af237ece0228d 100644 --- a/lldb/include/lldb/ValueObject/DILParser.h +++ b/lldb/include/lldb/ValueObject/DILParser.h @@ -84,12 +84,15 @@ class DILParser { ASTNodeUP ParseExpression(); ASTNodeUP ParseUnaryExpression(); + ASTNodeUP ParsePostfixExpression(); ASTNodeUP ParsePrimaryExpression(); std::string ParseNestedNameSpecifier(); std::string ParseIdExpression(); std::string ParseUnqualifiedId(); + ASTNodeUP ParseNumericLiteral(); + ASTNodeUP ParseNumericConstant(); void BailOut(const std::string &error, uint32_t loc, uint16_t err_len); diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp index ea847587501ee..ceb4a4aa99c4f 100644 --- a/lldb/source/ValueObject/DILAST.cpp +++ b/lldb/source/ValueObject/DILAST.cpp @@ -15,6 +15,11 @@ llvm::Expected ErrorNode::Accept(Visitor *v) const { llvm_unreachable("Attempting to Visit a DIL ErrorNode."); } +llvm::Expected +ScalarLiteralNode::Accept(Visitor *v) const { + return v->Visit(this); +} + llvm::Expected IdentifierNode::Accept(Visitor *v) const { return v->Visit(this); } @@ -23,4 +28,9 @@ llvm::Expected UnaryOpNode::Accept(Visitor *v) const { return v->Visit(this); } +llvm::Expected +ArraySubscriptNode::Accept(Visitor *v) const { + return v->Visit(this); +} + } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index 15a66d4866305..527017da7c019 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -18,6 +18,22 @@ namespace lldb_private::dil { +static lldb::ValueObjectSP +ArrayToPointerConversion(lldb::ValueObjectSP valobj, + std::shared_ptr ctx) { + assert(valobj->IsArrayType() && + "an argument to array-to-pointer conversion must be an array"); + + uint64_t addr = valobj->GetLoadAddress(); + llvm::StringRef name = "result"; + ExecutionContext exe_ctx; + ctx->CalculateExecutionContext(exe_ctx); + return ValueObject::CreateValueObjectFromAddress( + name, addr, exe_ctx, + valobj->GetCompilerType().GetArrayElementType(ctx.get()).GetPointerType(), + /* do_deref */ false); +} + static lldb::ValueObjectSP LookupStaticIdentifier( VariableList &variable_list, std::shared_ptr exe_scope, llvm::StringRef name_ref, llvm::StringRef unqualified_name) { @@ -214,6 +230,45 @@ llvm::Expected Interpreter::Evaluate(const ASTNode *node) { return value_or_error; } +static CompilerType GetBasicType(std::shared_ptr ctx, + lldb::BasicType basic_type) { + static std::unordered_map basic_types; + auto type = basic_types.find(basic_type); + if (type != basic_types.end()) { + std::string type_name((type->second).GetTypeName().AsCString()); + // Only return the found type if it's valid. + if (type_name != "") + return type->second; + } + + lldb::TargetSP target_sp = ctx->CalculateTarget(); + if (target_sp) { + for (auto type_system_sp : target_sp->GetScratchTypeSystems()) + if (auto compiler_type = + type_system_sp->GetBasicTypeFromAST(basic_type)) { + basic_types.insert({basic_type, compiler_type}); + return compiler_type; + } + } + CompilerType empty_type; + return empty_type; +} + +llvm::Expected +Interpreter::Visit(const ScalarLiteralNode *node) { + CompilerType result_type = GetBasicType(m_exe_ctx_scope, node->GetType()); + Scalar value = node->GetValue(); + + if (result_type.IsInteger() || result_type.IsNullPtrType() || + result_type.IsPointerType()) { + llvm::APInt val = value.GetAPSInt(); + return ValueObject::CreateValueObjectFromAPInt(m_target, val, result_type, + "result"); + } + + return lldb::ValueObjectSP(); +} + llvm::Expected Interpreter::Visit(const IdentifierNode *node) { lldb::DynamicValueType use_dynamic = m_default_dynamic; @@ -272,4 +327,108 @@ Interpreter::Visit(const UnaryOpNode *node) { m_expr, "invalid ast: unexpected binary operator", node->GetLocation()); } +lldb::ValueObjectSP Interpreter::PointerAdd(lldb::ValueObjectSP lhs, + int64_t offset) { + uint64_t byte_size = 0; + if (auto temp = lhs->GetCompilerType().GetPointeeType().GetByteSize( + lhs->GetTargetSP().get())) + byte_size = *temp; + uintptr_t addr = lhs->GetValueAsUnsigned(0) + offset * byte_size; + + llvm::StringRef name = "result"; + ExecutionContext exe_ctx(m_target.get(), false); + return ValueObject::CreateValueObjectFromAddress(name, addr, exe_ctx, + lhs->GetCompilerType(), + /* do_deref */ false); +} + +llvm::Expected +Interpreter::Visit(const ArraySubscriptNode *node) { + auto lhs_or_err = Evaluate(node->lhs()); + if (!lhs_or_err) { + return lhs_or_err; + } + lldb::ValueObjectSP base = *lhs_or_err; + auto rhs_or_err = Evaluate(node->rhs()); + if (!rhs_or_err) { + return rhs_or_err; + } + lldb::ValueObjectSP index = *rhs_or_err; + + Status error; + if (base->GetCompilerType().IsReferenceType()) { + base = base->Dereference(error); + if (error.Fail()) + return error.ToError(); + } + if (index->GetCompilerType().IsReferenceType()) { + index = index->Dereference(error); + if (error.Fail()) + return error.ToError(); + } + + auto index_type = index->GetCompilerType(); + if (!index_type.IsIntegerOrUnscopedEnumerationType()) + return llvm::make_error( + m_expr, "array subscript is not an integer", node->GetLocation()); + + // Check to see if 'base' has a synthetic value; if so, try using that. + if (base->HasSyntheticValue()) { + lldb::ValueObjectSP synthetic = base->GetSyntheticValue(); + if (synthetic && synthetic != base) { + uint32_t num_children = synthetic->GetNumChildrenIgnoringErrors(); + // Verify that the 'index' is not out-of-range for the declared type. + if (index->GetValueAsSigned(0) >= num_children) { + auto message = + llvm::formatv("array index {0} is not valid for \"({1}) {2}\"", + index->GetValueAsSigned(0), + base->GetTypeName().AsCString(""), + base->GetName().AsCString()); + return llvm::make_error(m_expr, message, + node->GetLocation()); + } + + uint64_t child_idx = index->GetValueAsUnsigned(0); + if (static_cast(child_idx) < + synthetic->GetNumChildrenIgnoringErrors()) { + lldb::ValueObjectSP child_valobj_sp = + synthetic->GetChildAtIndex(child_idx); + if (child_valobj_sp) { + return child_valobj_sp; + } + } + } + } + + auto base_type = base->GetCompilerType(); + if (!base_type.IsPointerType() && !base_type.IsArrayType()) + return llvm::make_error( + m_expr, "subscripted value is not an array or pointer", + node->GetLocation()); + if (base_type.IsPointerToVoid()) + return llvm::make_error( + m_expr, "subscript of pointer to incomplete type 'void'", + node->GetLocation()); + + if (base_type.IsArrayType()) + base = ArrayToPointerConversion(base, m_exe_ctx_scope); + + CompilerType item_type = base->GetCompilerType().GetPointeeType(); + lldb::addr_t base_addr = base->GetValueAsUnsigned(0); + + llvm::StringRef name = "result"; + ExecutionContext exe_ctx(m_target.get(), false); + // Create a pointer and add the index, i.e. "base + index". + lldb::ValueObjectSP value = + PointerAdd(ValueObject::CreateValueObjectFromAddress( + name, base_addr, exe_ctx, item_type.GetPointerType(), + /*do_deref=*/false), + index->GetValueAsSigned(0)); + + lldb::ValueObjectSP val2 = value->Dereference(error); + if (error.Fail()) + return error.ToError(); + return val2; +} + } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp index b9c2e7971e3b4..3222032feef19 100644 --- a/lldb/source/ValueObject/DILLexer.cpp +++ b/lldb/source/ValueObject/DILLexer.cpp @@ -13,6 +13,7 @@ #include "lldb/ValueObject/DILLexer.h" #include "lldb/Utility/Status.h" +#include "lldb/ValueObject/DILParser.h" #include "llvm/ADT/StringSwitch.h" namespace lldb_private::dil { @@ -29,8 +30,14 @@ llvm::StringRef Token::GetTokenName(Kind kind) { return "identifier"; case Kind::l_paren: return "l_paren"; + case Kind::l_square: + return "l_square"; + case Kind::numeric_constant: + return "numeric_constant"; case Kind::r_paren: return "r_paren"; + case Kind::r_square: + return "r_square"; case Token::star: return "star"; } @@ -57,6 +64,29 @@ static std::optional IsWord(llvm::StringRef expr, return candidate; } +static void ConsumeNumberBody(char &prev_ch, llvm::StringRef::iterator &cur_pos, + llvm::StringRef expr) { + while (cur_pos != expr.end() && + (IsDigit(*cur_pos) || IsLetter(*cur_pos) || *cur_pos == '_')) { + prev_ch = *cur_pos; + cur_pos++; + } +} + +static std::optional IsNumber(llvm::StringRef expr, + llvm::StringRef &remainder) { + llvm::StringRef::iterator cur_pos = remainder.begin(); + llvm::StringRef::iterator start = cur_pos; + char prev_ch = 0; + if (IsDigit(*start)) { + ConsumeNumberBody(prev_ch, cur_pos, expr); + llvm::StringRef number = expr.substr(start - expr.begin(), cur_pos - start); + if (remainder.consume_front(number)) + return number; + } + return std::nullopt; +} + llvm::Expected DILLexer::Create(llvm::StringRef expr) { std::vector tokens; llvm::StringRef remainder = expr; @@ -81,13 +111,19 @@ llvm::Expected DILLexer::Lex(llvm::StringRef expr, return Token(Token::eof, "", (uint32_t)expr.size()); uint32_t position = cur_pos - expr.begin(); + std::optional maybe_number = IsNumber(expr, remainder); + if (maybe_number) { + std::string number = (*maybe_number).str(); + return Token(Token::numeric_constant, number, position); + } std::optional maybe_word = IsWord(expr, remainder); if (maybe_word) return Token(Token::identifier, maybe_word->str(), position); constexpr std::pair operators[] = { - {Token::amp, "&"}, {Token::coloncolon, "::"}, {Token::l_paren, "("}, - {Token::r_paren, ")"}, {Token::star, "*"}, + {Token::amp, "&"}, {Token::coloncolon, "::"}, {Token::l_paren, "("}, + {Token::l_square, "["}, {Token::r_paren, ")"}, {Token::r_square, "]"}, + {Token::star, "*"}, }; for (auto [kind, str] : operators) { if (remainder.consume_front(str)) @@ -95,7 +131,8 @@ llvm::Expected DILLexer::Lex(llvm::StringRef expr, } // Unrecognized character(s) in string; unable to lex it. - return llvm::createStringError("Unable to lex input string"); + return llvm::make_error(expr, "unrecognized token", + position); } } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp index 2c78eae8cf6bf..24a6f0c909a0a 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -111,7 +111,36 @@ ASTNodeUP DILParser::ParseUnaryExpression() { llvm_unreachable("invalid token kind"); } } - return ParsePrimaryExpression(); + return ParsePostfixExpression(); +} + +// Parse a postfix_expression. +// +// postfix_expression: +// primary_expression +// postfix_expression "[" expression "]" +// +ASTNodeUP DILParser::ParsePostfixExpression() { + ASTNodeUP lhs = ParsePrimaryExpression(); + while (CurToken().Is(Token::l_square)) { + uint32_t loc = CurToken().GetLocation(); + Token token = CurToken(); + switch (token.GetKind()) { + case Token::l_square: { + m_dil_lexer.Advance(); + auto rhs = ParseExpression(); + Expect(Token::r_square); + m_dil_lexer.Advance(); + lhs = std::make_unique(loc, std::move(lhs), + std::move(rhs)); + break; + } + default: + llvm_unreachable("invalid token"); + } + } + + return lhs; } // Parse a primary_expression. @@ -121,6 +150,8 @@ ASTNodeUP DILParser::ParseUnaryExpression() { // "(" expression ")" // ASTNodeUP DILParser::ParsePrimaryExpression() { + if (CurToken().Is(Token::numeric_constant)) + return ParseNumericLiteral(); if (CurToken().IsOneOf({Token::coloncolon, Token::identifier})) { // Save the source location for the diagnostics message. uint32_t loc = CurToken().GetLocation(); @@ -280,6 +311,52 @@ void DILParser::BailOut(const std::string &error, uint32_t loc, m_dil_lexer.ResetTokenIdx(m_dil_lexer.NumLexedTokens() - 1); } +// Parse a numeric_literal. +// +// numeric_literal: +// ? Token::numeric_constant ? +// +ASTNodeUP DILParser::ParseNumericLiteral() { + Expect(Token::numeric_constant); + ASTNodeUP numeric_constant = ParseNumericConstant(); + if (numeric_constant->GetKind() == NodeKind::eErrorNode) { + BailOut(llvm::formatv("Failed to parse token as numeric-constant: {0}", + CurToken()), + CurToken().GetLocation(), CurToken().GetSpelling().length()); + return std::make_unique(); + } + m_dil_lexer.Advance(); + return numeric_constant; +} + +static constexpr std::pair type_suffixes[] = { + {"ull", lldb::eBasicTypeUnsignedLongLong}, + {"ul", lldb::eBasicTypeUnsignedLong}, + {"u", lldb::eBasicTypeUnsignedInt}, + {"ll", lldb::eBasicTypeLongLong}, + {"l", lldb::eBasicTypeLong}, +}; + +ASTNodeUP DILParser::ParseNumericConstant() { + Token token = CurToken(); + auto spelling = token.GetSpelling(); + llvm::StringRef spelling_ref = spelling; + lldb::BasicType type = lldb::eBasicTypeInt; + for (auto [suffix, t] : type_suffixes) { + if (spelling_ref.consume_back_insensitive(suffix)) { + type = t; + break; + } + } + llvm::APInt raw_value; + if (!spelling_ref.getAsInteger(0, raw_value)) { + Scalar scalar_value(raw_value); + return std::make_unique(token.GetLocation(), type, + scalar_value); + } + return std::make_unique(); +} + void DILParser::Expect(Token::Kind kind) { if (CurToken().IsNot(kind)) { BailOut(llvm::formatv("expected {0}, got: {1}", kind, CurToken()), diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/Makefile b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/Makefile new file mode 100644 index 0000000000000..99998b... [truncated] ``````````
https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Mon May 5 09:46:40 2025 From: lldb-commits at lists.llvm.org (Ilia Kuklin via lldb-commits) Date: Mon, 05 May 2025 09:46:40 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <6818eb70.050a0220.1b0382.b881@mx.google.com> kuilpd wrote: One thing I'm not sure about here: will converting array type to a pointer, adding the index and then dereferencing work for Swift? I tried just doing `base->GetChildAtIndex(index)`, but it doesn't work when the base is a pointer and it also returns an error when index is out of bounds, which shouldn't happen with C++. https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Mon May 5 09:47:24 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Mon, 05 May 2025 09:47:24 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB][SBSaveCore] Sbsavecore subregions bug (PR #138206) In-Reply-To: Message-ID: <6818eb9c.170a0220.1d4faa.6b6c@mx.google.com> https://github.com/Jlalond updated https://github.com/llvm/llvm-project/pull/138206 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Mon May 5 09:47:36 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Mon, 05 May 2025 09:47:36 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB][SBSaveCore] Sbsavecore subregions bug (PR #138206) In-Reply-To: Message-ID: <6818eba8.170a0220.192cb7.4edb@mx.google.com> Jlalond wrote: Updated the tests to skip on Windows. It would otherwise fail there. https://github.com/llvm/llvm-project/pull/138206 From lldb-commits at lists.llvm.org Mon May 5 09:50:52 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Mon, 05 May 2025 09:50:52 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <6818ec6c.a70a0220.1f25b0.b899@mx.google.com> labath wrote: > One thing I'm not sure about here: will converting array type to a pointer, adding the index and then dereferencing work for Swift? I tried just doing `base->GetChildAtIndex(index)`, but it doesn't work when the base is a pointer and it also returns an error when index is out of bounds, which shouldn't happen with C++. I don't have an answer to that, but I do know that it's possible to index pointers in the current implementation. I suggest checking out how it achieves that and seeing if it can be translated to here. https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Mon May 5 09:53:49 2025 From: lldb-commits at lists.llvm.org (Ilia Kuklin via lldb-commits) Date: Mon, 05 May 2025 09:53:49 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <6818ed1d.170a0220.36a881.6134@mx.google.com> kuilpd wrote: > I don't have an answer to that, but I do know that it's possible to index pointers in the current implementation. I suggest checking out how it achieves that and seeing if it can be translated to here. Ah, I will look into this further, thanks. https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Mon May 5 09:55:35 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Mon, 05 May 2025 09:55:35 -0700 (PDT) Subject: [Lldb-commits] [lldb] 1ba89ad - Handle step-in over a Darwin "branch island". (#138330) Message-ID: <6818ed87.170a0220.9c653.5ca6@mx.google.com> Author: jimingham Date: 2025-05-05T09:55:32-07:00 New Revision: 1ba89ad2c6e405bd5ac0c44e2ee5aa5504c7aba1 URL: https://github.com/llvm/llvm-project/commit/1ba89ad2c6e405bd5ac0c44e2ee5aa5504c7aba1 DIFF: https://github.com/llvm/llvm-project/commit/1ba89ad2c6e405bd5ac0c44e2ee5aa5504c7aba1.diff LOG: Handle step-in over a Darwin "branch island". (#138330) Added: lldb/test/API/macosx/branch-islands/Makefile lldb/test/API/macosx/branch-islands/TestBranchIslands.py lldb/test/API/macosx/branch-islands/foo.c lldb/test/API/macosx/branch-islands/main.c lldb/test/API/macosx/branch-islands/padding1.s lldb/test/API/macosx/branch-islands/padding2.s Modified: lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp Removed: ################################################################################ diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index e25c4ff55e408..52c50cd88902a 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -26,6 +26,7 @@ #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Utility/DataBuffer.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/LLDBLog.h" @@ -923,15 +924,15 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, if (current_symbol != nullptr) { std::vector
addresses; + ConstString current_name = + current_symbol->GetMangled().GetName(Mangled::ePreferMangled); if (current_symbol->IsTrampoline()) { - ConstString trampoline_name = - current_symbol->GetMangled().GetName(Mangled::ePreferMangled); - if (trampoline_name) { + if (current_name) { const ModuleList &images = target_sp->GetImages(); SymbolContextList code_symbols; - images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeCode, + images.FindSymbolsWithNameAndType(current_name, eSymbolTypeCode, code_symbols); for (const SymbolContext &context : code_symbols) { Address addr = context.GetFunctionOrSymbolAddress(); @@ -945,8 +946,8 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, } SymbolContextList reexported_symbols; - images.FindSymbolsWithNameAndType( - trampoline_name, eSymbolTypeReExported, reexported_symbols); + images.FindSymbolsWithNameAndType(current_name, eSymbolTypeReExported, + reexported_symbols); for (const SymbolContext &context : reexported_symbols) { if (context.symbol) { Symbol *actual_symbol = @@ -968,7 +969,7 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, } SymbolContextList indirect_symbols; - images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeResolver, + images.FindSymbolsWithNameAndType(current_name, eSymbolTypeResolver, indirect_symbols); for (const SymbolContext &context : indirect_symbols) { @@ -1028,6 +1029,18 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, thread_plan_sp = std::make_shared( thread, load_addrs, stop_others); } + // One more case we have to consider is "branch islands". These are regular + // TEXT symbols but their names end in .island. They are to allow arm64 + // code to branch further than the size of the address slot allows. We + // just need to single-instruction step in that case. + if (!thread_plan_sp && current_name.GetStringRef().ends_with(".island")) { + thread_plan_sp = std::make_shared( + thread, + /* step_over= */ false, /* stop_others */ false, eVoteNoOpinion, + eVoteNoOpinion); + LLDB_LOG(log, "Stepping one instruction over branch island: '{0}'.", + current_name); + } } else { LLDB_LOGF(log, "Could not find symbol for step through."); } diff --git a/lldb/test/API/macosx/branch-islands/Makefile b/lldb/test/API/macosx/branch-islands/Makefile new file mode 100644 index 0000000000000..8675bbf6f85de --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/Makefile @@ -0,0 +1,13 @@ +C_SOURCES := main.c foo.c +CFLAGS_EXTRAS := -std=c99 + +include Makefile.rules + +a.out: main.o padding1.o padding2.o foo.o + ${CC} ${LDFLAGS} foo.o padding1.o padding2.o main.o -o a.out + +padding1.o: padding1.s + ${CC} -c $(SRCDIR)/padding1.s + +padding2.o: padding2.s + ${CC} -c $(SRCDIR)/padding2.s diff --git a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py new file mode 100644 index 0000000000000..b397e0c229b08 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py @@ -0,0 +1,35 @@ +""" +Make sure that we can step in across an arm64 branch island +""" + + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * + + +class TestBranchIslandStepping(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + @skipUnlessDarwin + def test_step_in_branch_island(self): + """Make sure we can step in across a branch island""" + self.build() + self.main_source_file = lldb.SBFileSpec("main.c") + self.do_test() + + def do_test(self): + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "Set a breakpoint here", self.main_source_file + ) + + # Make sure that we did manage to generate a branch island for foo: + syms = target.FindSymbols("foo.island", lldb.eSymbolTypeCode) + self.assertEqual(len(syms), 1, "We did generate an island for foo") + + thread.StepInto() + stop_frame = thread.frames[0] + self.assertIn("foo", stop_frame.name, "Stepped into foo") + var = stop_frame.FindVariable("a_variable_in_foo") + self.assertTrue(var.IsValid(), "Found the variable in foo") diff --git a/lldb/test/API/macosx/branch-islands/foo.c b/lldb/test/API/macosx/branch-islands/foo.c new file mode 100644 index 0000000000000..a5dd2e59e1d82 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/foo.c @@ -0,0 +1,6 @@ +#include + +void foo() { + int a_variable_in_foo = 10; + printf("I am foo: %d.\n", a_variable_in_foo); +} diff --git a/lldb/test/API/macosx/branch-islands/main.c b/lldb/test/API/macosx/branch-islands/main.c new file mode 100644 index 0000000000000..b5578bdd715df --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/main.c @@ -0,0 +1,6 @@ +extern void foo(); + +int main() { + foo(); // Set a breakpoint here + return 0; +} diff --git a/lldb/test/API/macosx/branch-islands/padding1.s b/lldb/test/API/macosx/branch-islands/padding1.s new file mode 100644 index 0000000000000..e3bdf7007d757 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding1.s @@ -0,0 +1,3 @@ +.text +_junk1: +.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding2.s b/lldb/test/API/macosx/branch-islands/padding2.s new file mode 100644 index 0000000000000..187a2c3ebd117 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding2.s @@ -0,0 +1,3 @@ +.text +_junk2: +.space 120*1024*1024 From lldb-commits at lists.llvm.org Mon May 5 09:55:38 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Mon, 05 May 2025 09:55:38 -0700 (PDT) Subject: [Lldb-commits] [lldb] Handle step-in over a Darwin "branch island". (PR #138330) In-Reply-To: Message-ID: <6818ed8a.050a0220.78303.c410@mx.google.com> https://github.com/jimingham closed https://github.com/llvm/llvm-project/pull/138330 From lldb-commits at lists.llvm.org Mon May 5 10:05:43 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Mon, 05 May 2025 10:05:43 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <6818efe7.050a0220.2f01fa.de47@mx.google.com> jimingham wrote: > One thing I'm not sure about here: will converting array type to a pointer, adding the index and then dereferencing work for Swift? I tried just doing `base->GetChildAtIndex(index)`, but it doesn't work when the base is a pointer and it also returns an error when index is out of bounds, which shouldn't happen with C++. Swift doesn't have pointers really. It does pass some things by value (called structs) and others by reference (called classes), but it doesn't let you know how. So this ambiguity between "pointer to object" and "pointer to contiguous buffer of objects" doesn't come up. https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Mon May 5 10:07:36 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Mon, 05 May 2025 10:07:36 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Give attach test binaries unique names (PR #138435) In-Reply-To: Message-ID: <6818f058.170a0220.289717.5f97@mx.google.com> JDevlieghere wrote: > I see a comment in `TestDAP_attach.test_by_name` that mentions making the path more unique, so I'm wondering if something else has changed or if that previous attempt at making the name more unique was a little off the mark. Excellent question. We are indeed only using the executable name for the attach. Note how the AttachRequestHandler isn't setting the path in the attach info. We only use the program to create the target, which means we go down this path in `Target::Attach`: ``` // If no process info was specified, then use the target executable name as // the process to attach to by default if (!attach_info.ProcessInfoSpecified()) { if (old_exec_module_sp) attach_info.GetExecutableFile().SetFilename( old_exec_module_sp->GetPlatformFileSpec().GetFilename()); [...] } ``` I still think the unique test names are preferable over the old logic to change the directory, but we should fix this too. https://github.com/llvm/llvm-project/pull/138435 From lldb-commits at lists.llvm.org Mon May 5 10:09:19 2025 From: lldb-commits at lists.llvm.org (Ilia Kuklin via lldb-commits) Date: Mon, 05 May 2025 10:09:19 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <6818f0bf.630a0220.31ff65.6e2e@mx.google.com> kuilpd wrote: > Swift doesn't have pointers really. It does pass some things by value (called structs) and others by reference (called classes), but it doesn't let you know how. So this ambiguity between "pointer to object" and "pointer to contiguous buffer of objects" doesn't come up. So what happens if I use `ArrayToPointerConversion` function I added on a Swift array? https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Mon May 5 10:20:42 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Mon, 05 May 2025 10:20:42 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Specify the executable path in the attach info (PR #138557) Message-ID: https://github.com/JDevlieghere created https://github.com/llvm/llvm-project/pull/138557 Currently, we are only using the executable name when attaching. The AttachRequestHandler isn't setting the path in the attach info, which means that we rely on the target when attaching by name. When wo go down this path, we only look at the executable's filename, not its full path. Since we know the full path from the attach arguments, we should specify it in the attach info. Fixes #138197 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Mon May 5 10:20:52 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Mon, 05 May 2025 10:20:52 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Give attach test binaries unique names (PR #138435) In-Reply-To: Message-ID: <6818f374.170a0220.3d1563.8aa0@mx.google.com> JDevlieghere wrote: https://github.com/llvm/llvm-project/pull/138557 https://github.com/llvm/llvm-project/pull/138435 From lldb-commits at lists.llvm.org Mon May 5 10:21:14 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Mon, 05 May 2025 10:21:14 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Specify the executable path in the attach info (PR #138557) In-Reply-To: Message-ID: <6818f38a.170a0220.f9c2e.6ba9@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Jonas Devlieghere (JDevlieghere)
Changes Currently, we are only using the executable name when attaching. The AttachRequestHandler isn't setting the path in the attach info, which means that we rely on the target when attaching by name. When wo go down this path, we only look at the executable's filename, not its full path. Since we know the full path from the attach arguments, we should specify it in the attach info. Fixes #138197 --- Full diff: https://github.com/llvm/llvm-project/pull/138557.diff 1 Files Affected: - (modified) lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp (+7-5) ``````````diff diff --git a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp index 7084d30f2625b..a509aa09a385c 100644 --- a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp @@ -49,7 +49,6 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { llvm::json::Object response; lldb::SBError error; FillResponse(request, response); - lldb::SBAttachInfo attach_info; const int invalid_port = 0; const auto *arguments = request.getObject("arguments"); const lldb::pid_t pid = @@ -58,10 +57,7 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { GetInteger(arguments, "gdb-remote-port").value_or(invalid_port); const auto gdb_remote_hostname = GetString(arguments, "gdb-remote-hostname").value_or("localhost"); - if (pid != LLDB_INVALID_PROCESS_ID) - attach_info.SetProcessID(pid); const auto wait_for = GetBoolean(arguments, "waitFor").value_or(false); - attach_info.SetWaitForLaunch(wait_for, false /*async*/); dap.configuration.initCommands = GetStrings(arguments, "initCommands"); dap.configuration.preRunCommands = GetStrings(arguments, "preRunCommands"); dap.configuration.postRunCommands = GetStrings(arguments, "postRunCommands"); @@ -161,7 +157,13 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", error); } else { - // Attach by process name or id. + // Attach by pid or process name. + lldb::SBAttachInfo attach_info; + if (pid != LLDB_INVALID_PROCESS_ID) + attach_info.SetProcessID(pid); + if (dap.configuration.program.has_value()) + attach_info.SetExecutable(dap.configuration.program->data()); + attach_info.SetWaitForLaunch(wait_for, false /*async*/); dap.target.Attach(attach_info, error); } } else { ``````````
https://github.com/llvm/llvm-project/pull/138557 From lldb-commits at lists.llvm.org Mon May 5 10:22:39 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Mon, 05 May 2025 10:22:39 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <6818f3df.630a0220.40269.f795@mx.google.com> jimingham wrote: > > Swift doesn't have pointers really. It does pass some things by value (called structs) and others by reference (called classes), but it doesn't let you know how. So this ambiguity between "pointer to object" and "pointer to contiguous buffer of objects" doesn't come up. > > So what happens if I use `ArrayToPointerConversion` function I added on a Swift array? Swift has an out for dealing with pointers of classes (for instance to help pass objects back and forth between swift & C-based languages). So asking for the pointer type for a class type doesn't get None (which you would expect for a language that doesn't have the notion of pointers.) Instead you get `Swift.UnsafePointer`. But that's a pointer to the swift object, not to some buffer containing the array objects. I don't think indexing that will get what you want. We probably need to ask the language "Are Arrays contiguous buffers of objects for you", and return an error from here if they are not. https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Mon May 5 10:49:16 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Mon, 05 May 2025 10:49:16 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Change the launch sequence (PR #138219) In-Reply-To: Message-ID: <6818fa1c.630a0220.5a599.9842@mx.google.com> https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/138219 >From afd0475ea23ed79f074c3f4143b86e6efb557d37 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Fri, 2 May 2025 09:59:01 -0700 Subject: [PATCH] [lldb-dap] Change the launch sequence This PR changes how we treat the launch sequence in lldb-dap. - Send the initialized event after we finish handling the initialize request, rather than after we finish attaching or launching. - Delay handling the launch and attach request until we have handled the configurationDone request. The latter is now largely a NO-OP and only exists to signal lldb-dap that it can handle the launch and attach requests. - Delay handling the initial threads requests until we have handled the launch or attach request. - Make all attaching and launching asynchronous, including when we have attach or launch commands. That removes the need to synchronize between the request and event thread. Background: https://discourse.llvm.org/t/reliability-of-the-lldb-dap-tests/86125 --- .../test/tools/lldb-dap/dap_server.py | 48 ++++---- .../test/tools/lldb-dap/lldbdap_testcase.py | 7 ++ .../tools/lldb-dap/attach/TestDAP_attach.py | 3 +- .../attach/TestDAP_attachByPortNum.py | 8 +- .../breakpoint/TestDAP_breakpointLocations.py | 3 +- .../breakpoint/TestDAP_setBreakpoints.py | 3 +- .../lldb-dap/commands/TestDAP_commands.py | 3 +- .../completions/TestDAP_completions.py | 6 +- .../tools/lldb-dap/console/TestDAP_console.py | 2 +- .../disassemble/TestDAP_disassemble.py | 3 +- .../lldb-dap/disconnect/TestDAP_disconnect.py | 6 +- .../lldb-dap/evaluate/TestDAP_evaluate.py | 3 +- .../tools/lldb-dap/launch/TestDAP_launch.py | 4 +- .../tools/lldb-dap/memory/TestDAP_memory.py | 3 +- .../lldb-dap/progress/TestDAP_Progress.py | 2 +- .../repl-mode/TestDAP_repl_mode_detection.py | 2 +- .../tools/lldb-dap/restart/TestDAP_restart.py | 1 - .../restart/TestDAP_restart_runInTerminal.py | 1 - .../lldb-dap/stop-hooks/TestDAP_stop_hooks.py | 2 +- .../lldb-dap/variables/TestDAP_variables.py | 3 +- lldb/tools/lldb-dap/DAP.cpp | 39 +++++-- lldb/tools/lldb-dap/DAP.h | 8 +- lldb/tools/lldb-dap/EventHelper.cpp | 2 +- .../lldb-dap/Handler/AttachRequestHandler.cpp | 103 ++++++++++-------- .../ConfigurationDoneRequestHandler.cpp | 14 +-- .../Handler/InitializeRequestHandler.cpp | 44 +++----- .../lldb-dap/Handler/LaunchRequestHandler.cpp | 7 +- .../tools/lldb-dap/Handler/RequestHandler.cpp | 67 +++++++----- lldb/tools/lldb-dap/Handler/RequestHandler.h | 1 + 29 files changed, 222 insertions(+), 176 deletions(-) diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py index 6d9ab770684f1..93420e858bc4f 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py @@ -591,6 +591,7 @@ def request_attach( attachCommands=None, terminateCommands=None, coreFile=None, + stopOnAttach=True, postRunCommands=None, sourceMap=None, gdbRemotePort=None, @@ -620,6 +621,8 @@ def request_attach( args_dict["attachCommands"] = attachCommands if coreFile: args_dict["coreFile"] = coreFile + if stopOnAttach: + args_dict["stopOnEntry"] = stopOnAttach if postRunCommands: args_dict["postRunCommands"] = postRunCommands if sourceMap: @@ -632,7 +635,7 @@ def request_attach( response = self.send_recv(command_dict) if response["success"]: - self.wait_for_events(["process", "initialized"]) + self.wait_for_event("process") return response def request_breakpointLocations( @@ -666,10 +669,6 @@ def request_configurationDone(self): response = self.send_recv(command_dict) if response: self.configuration_done_sent = True - # Client requests the baseline of currently existing threads after - # a successful launch or attach. - # Kick off the threads request that follows - self.request_threads() return response def _process_stopped(self): @@ -887,7 +886,7 @@ def request_launch( response = self.send_recv(command_dict) if response["success"]: - self.wait_for_events(["process", "initialized"]) + self.wait_for_event("process") return response def request_next(self, threadId, granularity="statement"): @@ -1325,6 +1324,26 @@ def attach_options_specified(options): def run_vscode(dbg, args, options): dbg.request_initialize(options.sourceInitFile) + + if options.sourceBreakpoints: + source_to_lines = {} + for file_line in options.sourceBreakpoints: + (path, line) = file_line.split(":") + if len(path) == 0 or len(line) == 0: + print('error: invalid source with line "%s"' % (file_line)) + + else: + if path in source_to_lines: + source_to_lines[path].append(int(line)) + else: + source_to_lines[path] = [int(line)] + for source in source_to_lines: + dbg.request_setBreakpoints(source, source_to_lines[source]) + if options.funcBreakpoints: + dbg.request_setFunctionBreakpoints(options.funcBreakpoints) + + dbg.request_configurationDone() + if attach_options_specified(options): response = dbg.request_attach( program=options.program, @@ -1353,23 +1372,6 @@ def run_vscode(dbg, args, options): ) if response["success"]: - if options.sourceBreakpoints: - source_to_lines = {} - for file_line in options.sourceBreakpoints: - (path, line) = file_line.split(":") - if len(path) == 0 or len(line) == 0: - print('error: invalid source with line "%s"' % (file_line)) - - else: - if path in source_to_lines: - source_to_lines[path].append(int(line)) - else: - source_to_lines[path] = [int(line)] - for source in source_to_lines: - dbg.request_setBreakpoints(source, source_to_lines[source]) - if options.funcBreakpoints: - dbg.request_setFunctionBreakpoints(options.funcBreakpoints) - dbg.request_configurationDone() dbg.wait_for_stopped() else: if "message" in response: diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py index ee5272850b9a8..104474e9f48aa 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py @@ -333,6 +333,7 @@ def attach( exitCommands=None, attachCommands=None, coreFile=None, + stopOnAttach=True, disconnectAutomatically=True, terminateCommands=None, postRunCommands=None, @@ -357,6 +358,8 @@ def cleanup(): self.addTearDownHook(cleanup) # Initialize and launch the program self.dap_server.request_initialize(sourceInitFile) + self.dap_server.wait_for_event("initialized") + self.dap_server.request_configurationDone() response = self.dap_server.request_attach( program=program, pid=pid, @@ -369,6 +372,7 @@ def cleanup(): attachCommands=attachCommands, terminateCommands=terminateCommands, coreFile=coreFile, + stopOnAttach=stopOnAttach, postRunCommands=postRunCommands, sourceMap=sourceMap, gdbRemotePort=gdbRemotePort, @@ -427,6 +431,9 @@ def cleanup(): # Initialize and launch the program self.dap_server.request_initialize(sourceInitFile) + self.dap_server.wait_for_event("initialized") + self.dap_server.request_configurationDone() + response = self.dap_server.request_launch( program, args=args, diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py index 6f70316821c8c..01fba0e5694d4 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py @@ -25,9 +25,10 @@ def spawn_and_wait(program, delay): process.wait() - at skipIf class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase): def set_and_hit_breakpoint(self, continueToExit=True): + self.dap_server.wait_for_stopped() + source = "main.c" breakpoint1_line = line_number(source, "// breakpoint 1") lines = [breakpoint1_line] diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py index 51f62b79f3f4f..4f2298a9b73b6 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py @@ -19,17 +19,17 @@ import socket - at skip class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase): default_timeout = 20 def set_and_hit_breakpoint(self, continueToExit=True): + self.dap_server.wait_for_stopped() + source = "main.c" - main_source_path = os.path.join(os.getcwd(), source) - breakpoint1_line = line_number(main_source_path, "// breakpoint 1") + breakpoint1_line = line_number(source, "// breakpoint 1") lines = [breakpoint1_line] # Set breakpoint in the thread function so we can step the threads - breakpoint_ids = self.set_source_breakpoints(main_source_path, lines) + breakpoint_ids = self.set_source_breakpoints(source, lines) self.assertEqual( len(breakpoint_ids), len(lines), "expect correct number of breakpoints" ) diff --git a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_breakpointLocations.py b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_breakpointLocations.py index 4a99cacc761a3..1058157e2c668 100644 --- a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_breakpointLocations.py +++ b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_breakpointLocations.py @@ -11,8 +11,7 @@ import lldbdap_testcase import os -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip + class TestDAP_breakpointLocations(lldbdap_testcase.DAPTestCaseBase): def setUp(self): lldbdap_testcase.DAPTestCaseBase.setUp(self) diff --git a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py index 6c6681804f250..26df2573555df 100644 --- a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py +++ b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py @@ -11,8 +11,7 @@ import lldbdap_testcase import os -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip + class TestDAP_setBreakpoints(lldbdap_testcase.DAPTestCaseBase): def setUp(self): lldbdap_testcase.DAPTestCaseBase.setUp(self) diff --git a/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py b/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py index 8398eeab7bba2..25ecbb5cf106b 100644 --- a/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py +++ b/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py @@ -5,8 +5,7 @@ from lldbsuite.test import lldbtest, lldbutil from lldbsuite.test.decorators import * -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip + class TestDAP_commands(lldbdap_testcase.DAPTestCaseBase): def test_command_directive_quiet_on_success(self): program = self.getBuildArtifact("a.out") diff --git a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py index 210e591bff426..455ac84168baf 100644 --- a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py +++ b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py @@ -44,9 +44,9 @@ def verify_completions(self, actual_list, expected_list, not_expected_list=[]): self.assertNotIn(not_expected_item, actual_list) - def setup_debugee(self): + def setup_debugee(self, stopOnEntry=False): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=stopOnEntry) source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") @@ -235,7 +235,7 @@ def test_auto_completions(self): """ Tests completion requests in "repl-mode=auto" """ - self.setup_debugee() + self.setup_debugee(stopOnEntry=True) res = self.dap_server.request_evaluate( "`lldb-dap repl-mode auto", context="repl" diff --git a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py index b07c4f871d73b..65a1bc04c7cd7 100644 --- a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py +++ b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py @@ -167,7 +167,7 @@ def test_exit_status_message_ok(self): def test_diagnositcs(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) core = self.getBuildArtifact("minidump.core") self.yaml2obj("minidump.yaml", core) diff --git a/lldb/test/API/tools/lldb-dap/disassemble/TestDAP_disassemble.py b/lldb/test/API/tools/lldb-dap/disassemble/TestDAP_disassemble.py index ebecb349ac177..9e8ef5b289f2e 100644 --- a/lldb/test/API/tools/lldb-dap/disassemble/TestDAP_disassemble.py +++ b/lldb/test/API/tools/lldb-dap/disassemble/TestDAP_disassemble.py @@ -10,8 +10,7 @@ import lldbdap_testcase import os -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip + class TestDAP_disassemble(lldbdap_testcase.DAPTestCaseBase): @skipIfWindows def test_disassemble(self): diff --git a/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py b/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py index 0cb792d662a80..09e3f62f0eead 100644 --- a/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py +++ b/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py @@ -31,7 +31,7 @@ def test_launch(self): created. """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program, disconnectAutomatically=False) + self.build_and_launch(program, stopOnEntry=True, disconnectAutomatically=False) # We set a breakpoint right before the side effect file is created self.set_source_breakpoints( @@ -39,7 +39,11 @@ def test_launch(self): ) self.continue_to_next_stop() + # verify we haven't produced the side effect file yet + self.assertFalse(os.path.exists(program + ".side_effect")) + self.dap_server.request_disconnect() + # verify we didn't produce the side effect file time.sleep(1) self.assertFalse(os.path.exists(program + ".side_effect")) diff --git a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py index d97fda730c46a..e2f843bd337a6 100644 --- a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py +++ b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py @@ -10,8 +10,7 @@ from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip + class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase): def assertEvaluate(self, expression, regex): self.assertRegex( diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py index 931456299e03e..604a41678500c 100644 --- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py +++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py @@ -88,8 +88,8 @@ def test_stopOnEntry(self): """ program = self.getBuildArtifact("a.out") self.build_and_launch(program, stopOnEntry=True) - self.set_function_breakpoints(["main"]) - stopped_events = self.continue_to_next_stop() + + stopped_events = self.dap_server.wait_for_stopped() for stopped_event in stopped_events: if "body" in stopped_event: body = stopped_event["body"] diff --git a/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py b/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py index c71ba871b8a22..ea43fccf016a7 100644 --- a/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py +++ b/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py @@ -10,8 +10,7 @@ import lldbdap_testcase import os -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip + class TestDAP_memory(lldbdap_testcase.DAPTestCaseBase): def test_memory_refs_variables(self): """ diff --git a/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py b/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py index fee63655de0da..0f94b50c31fba 100755 --- a/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py +++ b/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py @@ -50,7 +50,7 @@ def verify_progress_events( @skipIfWindows def test(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) progress_emitter = os.path.join(os.getcwd(), "Progress_emitter.py") self.dap_server.request_evaluate( f"`command script import {progress_emitter}", context="repl" diff --git a/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py b/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py index c6f59949d668e..81edcdf4bd0f9 100644 --- a/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py +++ b/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py @@ -20,7 +20,7 @@ def assertEvaluate(self, expression, regex): def test_completions(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") diff --git a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py index 36fa0bd40183f..5f95c7bfb1556 100644 --- a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py +++ b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py @@ -22,7 +22,6 @@ def test_basic_functionality(self): [bp_A, bp_B] = self.set_source_breakpoints("main.c", [line_A, line_B]) # Verify we hit A, then B. - self.dap_server.request_configurationDone() self.verify_breakpoint_hit([bp_A]) self.dap_server.request_continue() self.verify_breakpoint_hit([bp_B]) diff --git a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py index a94c9860c1508..eed769a5a0cc6 100644 --- a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py +++ b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py @@ -74,7 +74,6 @@ def test_stopOnEntry(self): program = self.getBuildArtifact("a.out") self.build_and_launch(program, runInTerminal=True, stopOnEntry=True) [bp_main] = self.set_function_breakpoints(["main"]) - self.dap_server.request_configurationDone() # When using stopOnEntry, configurationDone doesn't result in a running # process, we should immediately get a stopped event instead. diff --git a/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py b/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py index 70c11a63a79f7..7e28a5af4331c 100644 --- a/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py +++ b/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py @@ -19,7 +19,7 @@ def test_stop_hooks_before_run(self): self.build_and_launch(program, stopOnEntry=True, preRunCommands=preRunCommands) # The first stop is on entry. - self.continue_to_next_stop() + self.dap_server.wait_for_stopped() breakpoint_ids = self.set_function_breakpoints(["main"]) # This request hangs if the race happens, because, in that case, the diff --git a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py index 901c260d7d413..286bf3390a440 100644 --- a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py +++ b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py @@ -17,8 +17,7 @@ def make_buffer_verify_dict(start_idx, count, offset=0): verify_dict["[%i]" % (i)] = {"type": "int", "value": str(i + offset)} return verify_dict -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip + class TestDAP_variables(lldbdap_testcase.DAPTestCaseBase): def verify_values(self, verify_dict, actual, varref_dict=None, expression=None): if "equals" in verify_dict: diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 4cb0d8e49004c..0ab16f16d30bf 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -84,8 +84,8 @@ DAP::DAP(Log *log, const ReplMode default_repl_mode, : log(log), transport(transport), broadcaster("lldb-dap"), exception_breakpoints(), focus_tid(LLDB_INVALID_THREAD_ID), stop_at_entry(false), is_attach(false), - restarting_process_id(LLDB_INVALID_PROCESS_ID), - configuration_done_sent(false), waiting_for_run_in_terminal(false), + restarting_process_id(LLDB_INVALID_PROCESS_ID), configuration_done(false), + waiting_for_run_in_terminal(false), progress_event_reporter( [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }), reverse_request_seq(0), repl_mode(default_repl_mode) { @@ -893,10 +893,19 @@ llvm::Error DAP::Loop() { return errWrapper; } + // The launch sequence is special and we need to carefully handle + // packets in the right order. Until we've handled configurationDone, + bool add_to_pending_queue = false; + if (const protocol::Request *req = - std::get_if(&*next); - req && req->command == "disconnect") { - disconnecting = true; + std::get_if(&*next)) { + llvm::StringRef command = req->command; + if (command == "disconnect") + disconnecting = true; + if (!configuration_done) + add_to_pending_queue = + command != "initialize" && command != "configurationDone" && + command != "disconnect" && !command.ends_with("Breakpoints"); } const std::optional cancel_args = @@ -924,7 +933,8 @@ llvm::Error DAP::Loop() { { std::lock_guard guard(m_queue_mutex); - m_queue.push_back(std::move(*next)); + auto &queue = add_to_pending_queue ? m_pending_queue : m_queue; + queue.push_back(std::move(*next)); } m_queue_cv.notify_one(); } @@ -938,16 +948,19 @@ llvm::Error DAP::Loop() { StopEventHandlers(); }); - while (!disconnecting || !m_queue.empty()) { + while (true) { std::unique_lock lock(m_queue_mutex); m_queue_cv.wait(lock, [&] { return disconnecting || !m_queue.empty(); }); - if (m_queue.empty()) + if (disconnecting && m_queue.empty()) break; Message next = m_queue.front(); m_queue.pop_front(); + // Unlock while we're processing the event. + lock.unlock(); + if (!HandleObject(next)) return llvm::createStringError(llvm::inconvertibleErrorCode(), "unhandled packet"); @@ -1221,6 +1234,16 @@ void DAP::SetConfiguration(const protocol::Configuration &config, SetThreadFormat(*configuration.customThreadFormat); } +void DAP::SetConfigurationDone() { + { + std::lock_guard guard(m_queue_mutex); + std::copy(m_pending_queue.begin(), m_pending_queue.end(), + std::front_inserter(m_queue)); + configuration_done = true; + } + m_queue_cv.notify_all(); +} + void DAP::SetFrameFormat(llvm::StringRef format) { if (format.empty()) return; diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index 88eedb0860cf1..b581ae759b1bc 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -188,7 +188,7 @@ struct DAP { // shutting down the entire adapter. When we're restarting, we keep the id of // the old process here so we can detect this case and keep running. lldb::pid_t restarting_process_id; - bool configuration_done_sent; + bool configuration_done; llvm::StringMap> request_handlers; bool waiting_for_run_in_terminal; ProgressEventReporter progress_event_reporter; @@ -251,6 +251,8 @@ struct DAP { /// Configures the debug adapter for launching/attaching. void SetConfiguration(const protocol::Configuration &confing, bool is_attach); + void SetConfigurationDone(); + /// Configure source maps based on the current `DAPConfiguration`. void ConfigureSourceMaps(); @@ -417,8 +419,10 @@ struct DAP { lldb::SBMutex GetAPIMutex() const { return target.GetAPIMutex(); } private: - std::mutex m_queue_mutex; + /// Queue for all incoming messages. std::deque m_queue; + std::deque m_pending_queue; + std::mutex m_queue_mutex; std::condition_variable m_queue_cv; std::mutex m_cancelled_requests_mutex; diff --git a/lldb/tools/lldb-dap/EventHelper.cpp b/lldb/tools/lldb-dap/EventHelper.cpp index 2c659f39f4b66..ed2d8700c26b0 100644 --- a/lldb/tools/lldb-dap/EventHelper.cpp +++ b/lldb/tools/lldb-dap/EventHelper.cpp @@ -222,7 +222,7 @@ void SendContinuedEvent(DAP &dap) { // If the focus thread is not set then we haven't reported any thread status // to the client, so nothing to report. - if (!dap.configuration_done_sent || dap.focus_tid == LLDB_INVALID_THREAD_ID) { + if (!dap.configuration_done || dap.focus_tid == LLDB_INVALID_THREAD_ID) { return; } diff --git a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp index 7084d30f2625b..89d603432f579 100644 --- a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp @@ -137,55 +137,64 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { dap.SendOutput(OutputType::Console, llvm::StringRef(attach_msg, attach_msg_len)); } - if (attachCommands.empty()) { - // No "attachCommands", just attach normally. - // Disable async events so the attach will be successful when we return from - // the launch call and the launch will happen synchronously + { + // Perform the launch in synchronous mode so that we don't have to worry + // about process state changes during the launch. ScopeSyncMode scope_sync_mode(dap.debugger); - - if (core_file.empty()) { - if ((pid != LLDB_INVALID_PROCESS_ID) && - (gdb_remote_port != invalid_port)) { - // If both pid and port numbers are specified. - error.SetErrorString("The user can't specify both pid and port"); - } else if (gdb_remote_port != invalid_port) { - // If port is specified and pid is not. - lldb::SBListener listener = dap.debugger.GetListener(); - - // If the user hasn't provided the hostname property, default localhost - // being used. - std::string connect_url = - llvm::formatv("connect://{0}:", gdb_remote_hostname); - connect_url += std::to_string(gdb_remote_port); - dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", - error); + if (attachCommands.empty()) { + // No "attachCommands", just attach normally. + if (core_file.empty()) { + if ((pid != LLDB_INVALID_PROCESS_ID) && + (gdb_remote_port != invalid_port)) { + // If both pid and port numbers are specified. + error.SetErrorString("The user can't specify both pid and port"); + } else if (gdb_remote_port != invalid_port) { + // If port is specified and pid is not. + lldb::SBListener listener = dap.debugger.GetListener(); + + // If the user hasn't provided the hostname property, default + // localhost being used. + std::string connect_url = + llvm::formatv("connect://{0}:", gdb_remote_hostname); + connect_url += std::to_string(gdb_remote_port); + dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", + error); + } else { + // Attach by process name or id. + dap.target.Attach(attach_info, error); + } } else { - // Attach by process name or id. - dap.target.Attach(attach_info, error); + dap.target.LoadCore(core_file.data(), error); } } else { - dap.target.LoadCore(core_file.data(), error); - } - } else { - // We have "attachCommands" that are a set of commands that are expected - // to execute the commands after which a process should be created. If there - // is no valid process after running these commands, we have failed. - if (llvm::Error err = dap.RunAttachCommands(attachCommands)) { - response["success"] = false; - EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; + // We have "attachCommands" that are a set of commands that are expected + // to execute the commands after which a process should be created. If + // there is no valid process after running these commands, we have failed. + if (llvm::Error err = dap.RunAttachCommands(attachCommands)) { + response["success"] = false; + EmplaceSafeString(response, "message", llvm::toString(std::move(err))); + dap.SendJSON(llvm::json::Value(std::move(response))); + return; + } + // The custom commands might have created a new target so we should use + // the selected target after these commands are run. + dap.target = dap.debugger.GetSelectedTarget(); } - // The custom commands might have created a new target so we should use the - // selected target after these commands are run. - dap.target = dap.debugger.GetSelectedTarget(); - - // Make sure the process is attached and stopped before proceeding as the - // the launch commands are not run using the synchronous mode. - error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds)); } + // Make sure the process is attached and stopped. + error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds)); + + // Clients can request a baseline of currently existing threads after + // we acknowledge the configurationDone request. + // Client requests the baseline of currently existing threads after + // a successful or attach by sending a 'threads' request + // right after receiving the configurationDone response. + // Obtain the list of threads before we resume the process + dap.initial_thread_list = + GetThreads(dap.target.GetProcess(), dap.thread_format); + if (error.Success() && core_file.empty()) { auto attached_pid = dap.target.GetProcess().GetProcessID(); if (attached_pid == LLDB_INVALID_PROCESS_ID) { @@ -204,9 +213,17 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { } dap.SendJSON(llvm::json::Value(std::move(response))); + + // FIXME: Move this into PostRun. if (error.Success()) { - SendProcessEvent(dap, Attach); - dap.SendJSON(CreateEventObject("initialized")); + if (dap.target.GetProcess().IsValid()) { + SendProcessEvent(dap, Attach); + + if (dap.stop_at_entry) + SendThreadStoppedEvent(dap); + else + dap.target.GetProcess().Continue(); + } } } diff --git a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp index f39bbdefdbb95..802c28d7b8904 100644 --- a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp @@ -47,21 +47,11 @@ namespace lldb_dap { void ConfigurationDoneRequestHandler::operator()( const llvm::json::Object &request) const { + dap.SetConfigurationDone(); + llvm::json::Object response; FillResponse(request, response); dap.SendJSON(llvm::json::Value(std::move(response))); - dap.configuration_done_sent = true; - if (dap.stop_at_entry) - SendThreadStoppedEvent(dap); - else { - // Client requests the baseline of currently existing threads after - // a successful launch or attach by sending a 'threads' request - // right after receiving the configurationDone response. - // Obtain the list of threads before we resume the process - dap.initial_thread_list = - GetThreads(dap.target.GetProcess(), dap.thread_format); - dap.target.GetProcess().Continue(); - } } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp index ce34c52bcc334..aa947d3cb5ab9 100644 --- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp @@ -140,43 +140,28 @@ static void EventThreadFunction(DAP &dap) { lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event); if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) { auto state = lldb::SBProcess::GetStateFromEvent(event); + + DAP_LOG(dap.log, "State = {0}", state); switch (state) { + case lldb::eStateConnected: + case lldb::eStateDetached: case lldb::eStateInvalid: - // Not a state event - break; case lldb::eStateUnloaded: break; - case lldb::eStateConnected: - break; case lldb::eStateAttaching: - break; - case lldb::eStateLaunching: - break; - case lldb::eStateStepping: - break; case lldb::eStateCrashed: - break; - case lldb::eStateDetached: - break; - case lldb::eStateSuspended: - break; + case lldb::eStateLaunching: case lldb::eStateStopped: - // We launch and attach in synchronous mode then the first stop - // event will not be delivered. If we use "launchCommands" during a - // launch or "attachCommands" during an attach we might some process - // stop events which we do not want to send an event for. We will - // manually send a stopped event in request_configurationDone(...) - // so don't send any before then. - if (dap.configuration_done_sent) { - // Only report a stopped event if the process was not - // automatically restarted. - if (!lldb::SBProcess::GetRestartedFromEvent(event)) { - SendStdOutStdErr(dap, process); - SendThreadStoppedEvent(dap); - } + case lldb::eStateSuspended: + // Only report a stopped event if the process was not + // automatically restarted. + if (!lldb::SBProcess::GetRestartedFromEvent(event)) { + SendStdOutStdErr(dap, process); + SendThreadStoppedEvent(dap); } break; case lldb::eStateRunning: + case lldb::eStateStepping: dap.WillContinue(); SendContinuedEvent(dap); break; @@ -284,6 +269,7 @@ llvm::Expected InitializeRequestHandler::Run( // Do not source init files until in/out/err are configured. dap.debugger = lldb::SBDebugger::Create(false); dap.debugger.SetInputFile(dap.in); + dap.target = dap.debugger.GetDummyTarget(); llvm::Expected out_fd = dap.out.GetWriteFileDescriptor(); if (!out_fd) @@ -338,4 +324,8 @@ llvm::Expected InitializeRequestHandler::Run( return dap.GetCapabilities(); } +void InitializeRequestHandler::PostRun() const { + dap.SendJSON(CreateEventObject("initialized")); +} + } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp index 3e4532e754ec6..7e0e76935dd02 100644 --- a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp @@ -71,9 +71,12 @@ void LaunchRequestHandler::PostRun() const { if (dap.target.GetProcess().IsValid()) { // Attach happens when launching with runInTerminal. SendProcessEvent(dap, dap.is_attach ? Attach : Launch); - } - dap.SendJSON(CreateEventObject("initialized")); + if (dap.stop_at_entry) + SendThreadStoppedEvent(dap); + else + dap.target.GetProcess().Continue(); + } } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp index 7a75cd93abc19..282c5f4ab15a5 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp @@ -8,6 +8,7 @@ #include "Handler/RequestHandler.h" #include "DAP.h" +#include "EventHelper.h" #include "Handler/ResponseHandler.h" #include "JSONUtils.h" #include "LLDBUtils.h" @@ -162,7 +163,7 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) { dap.target.GetProcess().Continue(); // Now that the actual target is just starting (i.e. exec was just invoked), - // we return the debugger to its async state. + // we return the debugger to its sync state. scope_sync_mode.reset(); // If sending the notification failed, the launcher should be dead by now and @@ -238,35 +239,47 @@ llvm::Error BaseRequestHandler::LaunchProcess( launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug | lldb::eLaunchFlagStopAtEntry); - if (arguments.runInTerminal) { - if (llvm::Error err = RunInTerminal(dap, arguments)) - return err; - } else if (launchCommands.empty()) { - lldb::SBError error; - // Disable async events so the launch will be successful when we return from - // the launch call and the launch will happen synchronously + { + // Perform the launch in synchronous mode so that we don't have to worry + // about process state changes during the launch. ScopeSyncMode scope_sync_mode(dap.debugger); - dap.target.Launch(launch_info, error); - if (error.Fail()) - return llvm::make_error(error.GetCString()); - } else { - // Set the launch info so that run commands can access the configured - // launch details. - dap.target.SetLaunchInfo(launch_info); - if (llvm::Error err = dap.RunLaunchCommands(launchCommands)) - return err; - - // The custom commands might have created a new target so we should use the - // selected target after these commands are run. - dap.target = dap.debugger.GetSelectedTarget(); - // Make sure the process is launched and stopped at the entry point before - // proceeding as the launch commands are not run using the synchronous - // mode. - lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout); - if (error.Fail()) - return llvm::make_error(error.GetCString()); + + if (arguments.runInTerminal) { + if (llvm::Error err = RunInTerminal(dap, arguments)) + return err; + } else if (launchCommands.empty()) { + lldb::SBError error; + dap.target.Launch(launch_info, error); + if (error.Fail()) + return llvm::make_error(error.GetCString()); + } else { + // Set the launch info so that run commands can access the configured + // launch details. + dap.target.SetLaunchInfo(launch_info); + if (llvm::Error err = dap.RunLaunchCommands(launchCommands)) + return err; + + // The custom commands might have created a new target so we should use + // the selected target after these commands are run. + dap.target = dap.debugger.GetSelectedTarget(); + } } + // Make sure the process is launched and stopped at the entry point before + // proceeding. + lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout); + if (error.Fail()) + return llvm::make_error(error.GetCString()); + + // Clients can request a baseline of currently existing threads after + // we acknowledge the configurationDone request. + // Client requests the baseline of currently existing threads after + // a successful or attach by sending a 'threads' request + // right after receiving the configurationDone response. + // Obtain the list of threads before we resume the process + dap.initial_thread_list = + GetThreads(dap.target.GetProcess(), dap.thread_format); + return llvm::Error::success(); } diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index fa3d76ed4a125..dec35c5dce1e5 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.h +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h @@ -282,6 +282,7 @@ class InitializeRequestHandler static llvm::StringLiteral GetCommand() { return "initialize"; } llvm::Expected Run(const protocol::InitializeRequestArguments &args) const override; + void PostRun() const override; }; class LaunchRequestHandler From lldb-commits at lists.llvm.org Mon May 5 11:04:58 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Mon, 05 May 2025 11:04:58 -0700 (PDT) Subject: [Lldb-commits] [lldb] c50cba6 - [LLDB][SBSaveCore] Sbsavecore subregions bug (#138206) Message-ID: <6818fdca.170a0220.1d5598.7844@mx.google.com> Author: Jacob Lalonde Date: 2025-05-05T11:04:55-07:00 New Revision: c50cba6275271fba69be661b9ec0665b2be88dbc URL: https://github.com/llvm/llvm-project/commit/c50cba6275271fba69be661b9ec0665b2be88dbc DIFF: https://github.com/llvm/llvm-project/commit/c50cba6275271fba69be661b9ec0665b2be88dbc.diff LOG: [LLDB][SBSaveCore] Sbsavecore subregions bug (#138206) Custom regions in Process::GetUserSpecifiedCoreFileSaveRanges originally used `FindEntryThatContains`. This made sense on my first attempt, but what we really want are *intersecting* regions. This is so the user can specify arbitrary memory, and if it's available we output it to the core (Minidump or MachO). Added: lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidumpYaml.py lldb/test/API/functionalities/process_save_core_minidump/minidump_mem64.yaml Modified: lldb/include/lldb/Utility/RangeMap.h lldb/source/Target/Process.cpp Removed: ################################################################################ diff --git a/lldb/include/lldb/Utility/RangeMap.h b/lldb/include/lldb/Utility/RangeMap.h index 8af690e813c4a..2f7711c1eb11e 100644 --- a/lldb/include/lldb/Utility/RangeMap.h +++ b/lldb/include/lldb/Utility/RangeMap.h @@ -380,6 +380,25 @@ template class RangeVector { return nullptr; } + const Entry *FindEntryThatIntersects(const Entry &range) const { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert(IsSorted()); +#endif + if (!m_entries.empty()) { + typename Collection::const_iterator begin = m_entries.begin(); + typename Collection::const_iterator end = m_entries.end(); + typename Collection::const_iterator pos = + std::lower_bound(begin, end, range, BaseLessThan); + + while (pos != begin && pos[-1].DoesIntersect(range)) + --pos; + + if (pos != end && pos->DoesIntersect(range)) + return &(*pos); + } + return nullptr; + } + using const_iterator = typename Collection::const_iterator; const_iterator begin() const { return m_entries.begin(); } const_iterator end() const { return m_entries.end(); } diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index ce64b44846a5d..13ff12b4ff953 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -6706,6 +6706,18 @@ static void GetCoreFileSaveRangesStackOnly(Process &process, } } +// TODO: We should refactor CoreFileMemoryRanges to use the lldb range type, and +// then add an intersect method on it, or MemoryRegionInfo. +static MemoryRegionInfo Intersect(const MemoryRegionInfo &lhs, + const MemoryRegionInfo::RangeType &rhs) { + + MemoryRegionInfo region_info; + region_info.SetLLDBPermissions(lhs.GetLLDBPermissions()); + region_info.GetRange() = lhs.GetRange().Intersect(rhs); + + return region_info; +} + static void GetUserSpecifiedCoreFileSaveRanges(Process &process, const MemoryRegionInfos ®ions, const SaveCoreOptions &options, @@ -6715,9 +6727,15 @@ static void GetUserSpecifiedCoreFileSaveRanges(Process &process, return; for (const auto &range : regions) { - auto entry = option_ranges.FindEntryThatContains(range.GetRange()); - if (entry) - AddRegion(range, true, ranges); + auto *entry = option_ranges.FindEntryThatIntersects(range.GetRange()); + if (entry) { + if (*entry != range.GetRange()) { + AddRegion(Intersect(range, *entry), true, ranges); + } else { + // If they match, add the range directly. + AddRegion(range, true, ranges); + } + } } } diff --git a/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidumpYaml.py b/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidumpYaml.py new file mode 100644 index 0000000000000..c0572e34d7746 --- /dev/null +++ b/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidumpYaml.py @@ -0,0 +1,247 @@ +""" +Test saving a mini dump, from yamilized examples. +""" + +import os +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class AddressRange: + begin: int + end: int + + def __init__(self, begin, end): + self.begin = begin + self.end = end + + +# We skip all these tests on Windows because on Windows Minidumps +# are not generated by LLDB. +class ProcessSaveCoreMinidumpTestCaseYaml(TestBase): + def process_from_yaml(self, yaml_file): + minidump_path = self.getBuildArtifact(os.path.basename(yaml_file) + ".dmp") + self.yaml2obj(yaml_file, minidump_path) + self.target = self.dbg.CreateTarget(None) + self.process = self.target.LoadCore(minidump_path) + return self.process + + @skipIfWindows + def validate_regions_saved_correctly( + self, core_process, expected_region, expected_invalid_region=None + ): + """Validate that the expected_region is saved in the core_proc, and that the expected invalid region is not saved, if not not none.""" + + # Validate we can read the entire expected_region + error = lldb.SBError() + core_process.ReadMemory( + expected_region.begin, expected_region.end - expected_region.begin, error + ) + self.assertTrue(error.Success(), error.GetCString()) + + # Validate we can't read before and after the expected_region + core_process.ReadMemory(expected_region.begin - 1, 1, error) + self.assertTrue(error.Fail(), error.GetCString()) + + core_process.ReadMemory(expected_region.end + 1, 1, error) + self.assertTrue(error.Fail(), error.GetCString()) + + if expected_invalid_region is None: + return + + # Validate we can't read the original_region + core_process.ReadMemory( + expected_invalid_region.begin, + expected_invalid_region.end - expected_invalid_region.begin, + error, + ) + self.assertTrue(error.Fail(), error.GetCString()) + + @skipIfWindows + def test_saving_sub_memory_range(self): + """ + Validate we can save a Minidump for a subsection of a memory range. + I.E. + If our memory range is 0x2000-0x2020 and the user specifies 0x2000-0x2008 + we should still capture 0x2000-0x2008 + """ + yaml = "minidump_mem64.yaml" + proc = self.process_from_yaml(yaml) + new_minidump_path = self.getBuildArtifact(__name__ + ".dmp") + options = lldb.SBSaveCoreOptions() + options.SetOutputFile(lldb.SBFileSpec(new_minidump_path)) + options.SetPluginName("minidump") + options.SetStyle(lldb.eSaveCoreCustomOnly) + + size = 8 + begin = 0x2000 + end = begin + size + custom_range = lldb.SBMemoryRegionInfo("", begin, end, 3, True, False) + options.AddMemoryRegionToSave(custom_range) + + error = proc.SaveCore(options) + self.assertTrue(error.Success(), error.GetCString()) + core_target = self.dbg.CreateTarget(None) + core_process = core_target.LoadCore(new_minidump_path) + + expected_address_range = AddressRange(begin, end) + expected_invalid_range = AddressRange(begin, 0x2020) + self.validate_regions_saved_correctly( + core_process, expected_address_range, expected_invalid_range + ) + + @skipIfWindows + def test_saving_super_memory_range(self): + """ + Validate we can save a Minidump for a subsection of a memory range. + I.E. + If our memory range is 0x1000-0x1100 and the user specifies 0x900-x1200 + we should still capture 0x1000-0x1100 + """ + yaml = "minidump_mem64.yaml" + proc = self.process_from_yaml(yaml) + new_minidump_path = self.getBuildArtifact(__name__ + ".dmp") + options = lldb.SBSaveCoreOptions() + options.SetOutputFile(lldb.SBFileSpec(new_minidump_path)) + options.SetPluginName("minidump") + options.SetStyle(lldb.eSaveCoreCustomOnly) + + size = 0x100 + begin = 0x1000 + end = begin + size + custom_range = lldb.SBMemoryRegionInfo("", begin - 16, end + 16, 3, True, False) + options.AddMemoryRegionToSave(custom_range) + + error = proc.SaveCore(options) + self.assertTrue(error.Success(), error.GetCString()) + core_target = self.dbg.CreateTarget(None) + core_process = core_target.LoadCore(new_minidump_path) + + expected_address_range = AddressRange(begin, end) + expected_invalid_range = AddressRange(begin - 16, end + 16) + self.validate_regions_saved_correctly( + core_process, expected_address_range, expected_invalid_range + ) + + @skipIfWindows + def test_region_that_goes_out_of_bounds(self): + """ + Validate we can save a Minidump for a custom region + that includes an end that enters an invalid (---) page. + """ + yaml = "minidump_mem64.yaml" + proc = self.process_from_yaml(yaml) + new_minidump_path = self.getBuildArtifact(__name__ + ".dmp") + options = lldb.SBSaveCoreOptions() + options.SetOutputFile(lldb.SBFileSpec(new_minidump_path)) + options.SetPluginName("minidump") + options.SetStyle(lldb.eSaveCoreCustomOnly) + + size = 0x120 + begin = 0x1000 + end = begin + size + custom_range = lldb.SBMemoryRegionInfo("", begin, end, 3, True, False) + options.AddMemoryRegionToSave(custom_range) + + error = proc.SaveCore(options) + self.assertTrue(error.Success(), error.GetCString()) + core_target = self.dbg.CreateTarget(None) + core_process = core_target.LoadCore(new_minidump_path) + + expected_address_range = AddressRange(begin, end) + expected_invalid_range = AddressRange(begin - 16, end + 16) + self.validate_regions_saved_correctly( + core_process, expected_address_range, expected_invalid_range + ) + + @skipIfWindows + def test_region_that_starts_out_of_bounds(self): + """ + Validate we can save a Minidump for a custom region + that includes a start in a (---) page but ends in a valid page. + """ + yaml = "minidump_mem64.yaml" + proc = self.process_from_yaml(yaml) + new_minidump_path = self.getBuildArtifact(__name__ + ".dmp") + options = lldb.SBSaveCoreOptions() + options.SetOutputFile(lldb.SBFileSpec(new_minidump_path)) + options.SetPluginName("minidump") + options.SetStyle(lldb.eSaveCoreCustomOnly) + + size = 0x20 + begin = 0x2000 + end = begin + size + custom_range = lldb.SBMemoryRegionInfo("", begin - 16, end, 3, True, False) + options.AddMemoryRegionToSave(custom_range) + + error = proc.SaveCore(options) + self.assertTrue(error.Success(), error.GetCString()) + core_target = self.dbg.CreateTarget(None) + core_process = core_target.LoadCore(new_minidump_path) + + expected_address_range = AddressRange(begin, end) + expected_invalid_range = AddressRange(begin - 16, end) + self.validate_regions_saved_correctly( + core_process, expected_address_range, expected_invalid_range + ) + + @skipIfWindows + def test_region_spans_multiple_regions(self): + """ + Validate we can save a Minidump for a custom region + that includes a start in a (---) page but ends in a valid page. + """ + yaml = "minidump_mem64.yaml" + proc = self.process_from_yaml(yaml) + new_minidump_path = self.getBuildArtifact(__name__ + ".dmp") + options = lldb.SBSaveCoreOptions() + options.SetOutputFile(lldb.SBFileSpec(new_minidump_path)) + options.SetPluginName("minidump") + options.SetStyle(lldb.eSaveCoreCustomOnly) + + size = 0x1000 + begin = 0x5000 + end = begin + size + custom_range = lldb.SBMemoryRegionInfo("", begin, end, 3, True, False) + options.AddMemoryRegionToSave(custom_range) + + error = proc.SaveCore(options) + self.assertTrue(error.Success(), error.GetCString()) + core_target = self.dbg.CreateTarget(None) + core_process = core_target.LoadCore(new_minidump_path) + + expected_address_range = AddressRange(begin, end) + self.validate_regions_saved_correctly(core_process, expected_address_range) + + @skipIfWindows + def test_region_spans_multiple_regions_with_one_subrange(self): + """ + Validate we can save a Minidump for a custom region + that includes a start in a (---) page but ends in a valid page. + """ + yaml = "minidump_mem64.yaml" + proc = self.process_from_yaml(yaml) + new_minidump_path = self.getBuildArtifact(__name__ + ".dmp") + options = lldb.SBSaveCoreOptions() + options.SetOutputFile(lldb.SBFileSpec(new_minidump_path)) + options.SetPluginName("minidump") + options.SetStyle(lldb.eSaveCoreCustomOnly) + + size = 0x800 + begin = 0x5000 + end = begin + size + custom_range = lldb.SBMemoryRegionInfo("", begin, end, 3, True, False) + options.AddMemoryRegionToSave(custom_range) + + error = proc.SaveCore(options) + self.assertTrue(error.Success(), error.GetCString()) + core_target = self.dbg.CreateTarget(None) + core_process = core_target.LoadCore(new_minidump_path) + + expected_address_range = AddressRange(begin, end) + expected_invalid_range = AddressRange(begin, begin + 0x1000) + self.validate_regions_saved_correctly( + core_process, expected_address_range, expected_invalid_range + ) diff --git a/lldb/test/API/functionalities/process_save_core_minidump/minidump_mem64.yaml b/lldb/test/API/functionalities/process_save_core_minidump/minidump_mem64.yaml new file mode 100644 index 0000000000000..d04ca1ae0dc12 --- /dev/null +++ b/lldb/test/API/functionalities/process_save_core_minidump/minidump_mem64.yaml @@ -0,0 +1,38 @@ +--- !minidump +Streams: + - Type: SystemInfo + Processor Arch: AMD64 + Processor Level: 6 + Processor Revision: 15876 + Number of Processors: 40 + Platform ID: Linux + CSD Version: 'Linux 3.13.0-91-generic' + CPU: + Vendor ID: GenuineIntel + Version Info: 0x00000000 + Feature Info: 0x00000000 + - Type: ThreadList + Threads: + - Thread Id: 0x2896BB + Context: 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000700100000000000FFFFFFFF0000FFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B040A812FF7F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050D0A75BBA7F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + Stack: + Start of Memory Range: 0x0 + Content: '' + - Type: Memory64List + Memory Ranges: + - Start of Memory Range: 0x1000 + Data Size: 0x100 + Content : '' + - Start of Memory Range: 0x2000 + Data Size: 0x20 + Content : '' + - Start of Memory Range: 0x3000 + Data Size: 0x400 + Content : '' + - Start of Memory Range: 0x5000 + Data Size: 0x500 + Content : '' + - Start of Memory Range: 0x5500 + Data Size: 0x500 + Content : '' +... From lldb-commits at lists.llvm.org Mon May 5 11:05:04 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Mon, 05 May 2025 11:05:04 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB][SBSaveCore] Sbsavecore subregions bug (PR #138206) In-Reply-To: Message-ID: <6818fdd0.170a0220.3b20fd.8d3a@mx.google.com> https://github.com/Jlalond closed https://github.com/llvm/llvm-project/pull/138206 From lldb-commits at lists.llvm.org Mon May 5 11:26:43 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Mon, 05 May 2025 11:26:43 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Specify the executable path in the attach info (PR #138557) In-Reply-To: Message-ID: <681902e3.170a0220.1e2423.9273@mx.google.com> ================ @@ -161,7 +157,13 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", error); } else { - // Attach by process name or id. + // Attach by pid or process name. + lldb::SBAttachInfo attach_info; + if (pid != LLDB_INVALID_PROCESS_ID) + attach_info.SetProcessID(pid); + if (dap.configuration.program.has_value()) ---------------- Jlalond wrote: just a nit, but wouldn't an `if else` make sense here, because if we have the pid we don't even need to send the executable path in the attach info? https://github.com/llvm/llvm-project/pull/138557 From lldb-commits at lists.llvm.org Mon May 5 12:34:17 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Mon, 05 May 2025 12:34:17 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Specify the executable path in the attach info (PR #138557) In-Reply-To: Message-ID: <681912b9.170a0220.2f8a6e.a2bb@mx.google.com> https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/138557 >From 8289d760034dc18315d116aaeddda9033ff1390a Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Mon, 5 May 2025 10:18:12 -0700 Subject: [PATCH 1/2] [lldb-dap] Specify the executable path in the attach info Currently, we are only using the executable name when attaching. The AttachRequestHandler isn't setting the path in the attach info, which means that we rely on the target when attaching by name. When wo go down this path, we only look at the executable's filename, not its full path. Since we know the full path from the attach arguments, we should specify it in the attach info. Fixes #138197 --- lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp index 7084d30f2625b..a509aa09a385c 100644 --- a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp @@ -49,7 +49,6 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { llvm::json::Object response; lldb::SBError error; FillResponse(request, response); - lldb::SBAttachInfo attach_info; const int invalid_port = 0; const auto *arguments = request.getObject("arguments"); const lldb::pid_t pid = @@ -58,10 +57,7 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { GetInteger(arguments, "gdb-remote-port").value_or(invalid_port); const auto gdb_remote_hostname = GetString(arguments, "gdb-remote-hostname").value_or("localhost"); - if (pid != LLDB_INVALID_PROCESS_ID) - attach_info.SetProcessID(pid); const auto wait_for = GetBoolean(arguments, "waitFor").value_or(false); - attach_info.SetWaitForLaunch(wait_for, false /*async*/); dap.configuration.initCommands = GetStrings(arguments, "initCommands"); dap.configuration.preRunCommands = GetStrings(arguments, "preRunCommands"); dap.configuration.postRunCommands = GetStrings(arguments, "postRunCommands"); @@ -161,7 +157,13 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", error); } else { - // Attach by process name or id. + // Attach by pid or process name. + lldb::SBAttachInfo attach_info; + if (pid != LLDB_INVALID_PROCESS_ID) + attach_info.SetProcessID(pid); + if (dap.configuration.program.has_value()) + attach_info.SetExecutable(dap.configuration.program->data()); + attach_info.SetWaitForLaunch(wait_for, false /*async*/); dap.target.Attach(attach_info, error); } } else { >From 67cf8207acca1856c05c84fd6d3b82efd1409483 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Mon, 5 May 2025 12:33:58 -0700 Subject: [PATCH 2/2] Address Jacob's feedback --- lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp index a509aa09a385c..7a0f091128e4a 100644 --- a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp @@ -161,7 +161,7 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { lldb::SBAttachInfo attach_info; if (pid != LLDB_INVALID_PROCESS_ID) attach_info.SetProcessID(pid); - if (dap.configuration.program.has_value()) + else if (dap.configuration.program.has_value()) attach_info.SetExecutable(dap.configuration.program->data()); attach_info.SetWaitForLaunch(wait_for, false /*async*/); dap.target.Attach(attach_info, error); From lldb-commits at lists.llvm.org Mon May 5 12:45:21 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Mon, 05 May 2025 12:45:21 -0700 (PDT) Subject: [Lldb-commits] [lldb] 1ff2953 - Revert "Handle step-in over a Darwin "branch island". (#138330)" (#138569) Message-ID: <68191551.050a0220.2166c1.2628@mx.google.com> Author: jimingham Date: 2025-05-05T12:45:17-07:00 New Revision: 1ff2953f5e393eb8634ea3c4ccc85221e76dfcb9 URL: https://github.com/llvm/llvm-project/commit/1ff2953f5e393eb8634ea3c4ccc85221e76dfcb9 DIFF: https://github.com/llvm/llvm-project/commit/1ff2953f5e393eb8634ea3c4ccc85221e76dfcb9.diff LOG: Revert "Handle step-in over a Darwin "branch island". (#138330)" (#138569) This reverts commit 1ba89ad2c6e405bd5ac0c44e2ee5aa5504c7aba1. This was failing on the Green Dragon bot, which has an older OS than have on hand, so I'll have to dig up one and see why it's failing there. Added: Modified: lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp Removed: lldb/test/API/macosx/branch-islands/Makefile lldb/test/API/macosx/branch-islands/TestBranchIslands.py lldb/test/API/macosx/branch-islands/foo.c lldb/test/API/macosx/branch-islands/main.c lldb/test/API/macosx/branch-islands/padding1.s lldb/test/API/macosx/branch-islands/padding2.s ################################################################################ diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index 52c50cd88902a..e25c4ff55e408 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -26,7 +26,6 @@ #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Target/ThreadPlanRunToAddress.h" -#include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Utility/DataBuffer.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/LLDBLog.h" @@ -924,15 +923,15 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, if (current_symbol != nullptr) { std::vector
addresses; - ConstString current_name = - current_symbol->GetMangled().GetName(Mangled::ePreferMangled); if (current_symbol->IsTrampoline()) { + ConstString trampoline_name = + current_symbol->GetMangled().GetName(Mangled::ePreferMangled); - if (current_name) { + if (trampoline_name) { const ModuleList &images = target_sp->GetImages(); SymbolContextList code_symbols; - images.FindSymbolsWithNameAndType(current_name, eSymbolTypeCode, + images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeCode, code_symbols); for (const SymbolContext &context : code_symbols) { Address addr = context.GetFunctionOrSymbolAddress(); @@ -946,8 +945,8 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, } SymbolContextList reexported_symbols; - images.FindSymbolsWithNameAndType(current_name, eSymbolTypeReExported, - reexported_symbols); + images.FindSymbolsWithNameAndType( + trampoline_name, eSymbolTypeReExported, reexported_symbols); for (const SymbolContext &context : reexported_symbols) { if (context.symbol) { Symbol *actual_symbol = @@ -969,7 +968,7 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, } SymbolContextList indirect_symbols; - images.FindSymbolsWithNameAndType(current_name, eSymbolTypeResolver, + images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeResolver, indirect_symbols); for (const SymbolContext &context : indirect_symbols) { @@ -1029,18 +1028,6 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, thread_plan_sp = std::make_shared( thread, load_addrs, stop_others); } - // One more case we have to consider is "branch islands". These are regular - // TEXT symbols but their names end in .island. They are to allow arm64 - // code to branch further than the size of the address slot allows. We - // just need to single-instruction step in that case. - if (!thread_plan_sp && current_name.GetStringRef().ends_with(".island")) { - thread_plan_sp = std::make_shared( - thread, - /* step_over= */ false, /* stop_others */ false, eVoteNoOpinion, - eVoteNoOpinion); - LLDB_LOG(log, "Stepping one instruction over branch island: '{0}'.", - current_name); - } } else { LLDB_LOGF(log, "Could not find symbol for step through."); } diff --git a/lldb/test/API/macosx/branch-islands/Makefile b/lldb/test/API/macosx/branch-islands/Makefile deleted file mode 100644 index 8675bbf6f85de..0000000000000 --- a/lldb/test/API/macosx/branch-islands/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -C_SOURCES := main.c foo.c -CFLAGS_EXTRAS := -std=c99 - -include Makefile.rules - -a.out: main.o padding1.o padding2.o foo.o - ${CC} ${LDFLAGS} foo.o padding1.o padding2.o main.o -o a.out - -padding1.o: padding1.s - ${CC} -c $(SRCDIR)/padding1.s - -padding2.o: padding2.s - ${CC} -c $(SRCDIR)/padding2.s diff --git a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py deleted file mode 100644 index b397e0c229b08..0000000000000 --- a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py +++ /dev/null @@ -1,35 +0,0 @@ -""" -Make sure that we can step in across an arm64 branch island -""" - - -import lldb -import lldbsuite.test.lldbutil as lldbutil -from lldbsuite.test.lldbtest import * -from lldbsuite.test.decorators import * - - -class TestBranchIslandStepping(TestBase): - NO_DEBUG_INFO_TESTCASE = True - - @skipUnlessDarwin - def test_step_in_branch_island(self): - """Make sure we can step in across a branch island""" - self.build() - self.main_source_file = lldb.SBFileSpec("main.c") - self.do_test() - - def do_test(self): - (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( - self, "Set a breakpoint here", self.main_source_file - ) - - # Make sure that we did manage to generate a branch island for foo: - syms = target.FindSymbols("foo.island", lldb.eSymbolTypeCode) - self.assertEqual(len(syms), 1, "We did generate an island for foo") - - thread.StepInto() - stop_frame = thread.frames[0] - self.assertIn("foo", stop_frame.name, "Stepped into foo") - var = stop_frame.FindVariable("a_variable_in_foo") - self.assertTrue(var.IsValid(), "Found the variable in foo") diff --git a/lldb/test/API/macosx/branch-islands/foo.c b/lldb/test/API/macosx/branch-islands/foo.c deleted file mode 100644 index a5dd2e59e1d82..0000000000000 --- a/lldb/test/API/macosx/branch-islands/foo.c +++ /dev/null @@ -1,6 +0,0 @@ -#include - -void foo() { - int a_variable_in_foo = 10; - printf("I am foo: %d.\n", a_variable_in_foo); -} diff --git a/lldb/test/API/macosx/branch-islands/main.c b/lldb/test/API/macosx/branch-islands/main.c deleted file mode 100644 index b5578bdd715df..0000000000000 --- a/lldb/test/API/macosx/branch-islands/main.c +++ /dev/null @@ -1,6 +0,0 @@ -extern void foo(); - -int main() { - foo(); // Set a breakpoint here - return 0; -} diff --git a/lldb/test/API/macosx/branch-islands/padding1.s b/lldb/test/API/macosx/branch-islands/padding1.s deleted file mode 100644 index e3bdf7007d757..0000000000000 --- a/lldb/test/API/macosx/branch-islands/padding1.s +++ /dev/null @@ -1,3 +0,0 @@ -.text -_junk1: -.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding2.s b/lldb/test/API/macosx/branch-islands/padding2.s deleted file mode 100644 index 187a2c3ebd117..0000000000000 --- a/lldb/test/API/macosx/branch-islands/padding2.s +++ /dev/null @@ -1,3 +0,0 @@ -.text -_junk2: -.space 120*1024*1024 From lldb-commits at lists.llvm.org Mon May 5 12:45:23 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Mon, 05 May 2025 12:45:23 -0700 (PDT) Subject: [Lldb-commits] [lldb] Revert "Handle step-in over a Darwin "branch island". (#138330)" (PR #138569) In-Reply-To: Message-ID: <68191553.050a0220.3c3295.1aac@mx.google.com> https://github.com/jimingham closed https://github.com/llvm/llvm-project/pull/138569 From lldb-commits at lists.llvm.org Mon May 5 12:45:53 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Mon, 05 May 2025 12:45:53 -0700 (PDT) Subject: [Lldb-commits] [lldb] Revert "Handle step-in over a Darwin "branch island". (#138330)" (PR #138569) Message-ID: https://github.com/jimingham created https://github.com/llvm/llvm-project/pull/138569 This reverts commit 1ba89ad2c6e405bd5ac0c44e2ee5aa5504c7aba1. This was failing on the Green Dragon bot, which has an older OS than have on hand, so I'll have to dig up one and see why it's failing there. Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Mon May 5 12:46:24 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Mon, 05 May 2025 12:46:24 -0700 (PDT) Subject: [Lldb-commits] [lldb] Revert "Handle step-in over a Darwin "branch island". (#138330)" (PR #138569) In-Reply-To: Message-ID: <68191590.170a0220.ce49c.9d81@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: None (jimingham)
Changes This reverts commit 1ba89ad2c6e405bd5ac0c44e2ee5aa5504c7aba1. This was failing on the Green Dragon bot, which has an older OS than have on hand, so I'll have to dig up one and see why it's failing there. --- Full diff: https://github.com/llvm/llvm-project/pull/138569.diff 7 Files Affected: - (modified) lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp (+7-20) - (removed) lldb/test/API/macosx/branch-islands/Makefile (-13) - (removed) lldb/test/API/macosx/branch-islands/TestBranchIslands.py (-35) - (removed) lldb/test/API/macosx/branch-islands/foo.c (-6) - (removed) lldb/test/API/macosx/branch-islands/main.c (-6) - (removed) lldb/test/API/macosx/branch-islands/padding1.s (-3) - (removed) lldb/test/API/macosx/branch-islands/padding2.s (-3) ``````````diff diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index 52c50cd88902a..e25c4ff55e408 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -26,7 +26,6 @@ #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Target/ThreadPlanRunToAddress.h" -#include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Utility/DataBuffer.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/LLDBLog.h" @@ -924,15 +923,15 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, if (current_symbol != nullptr) { std::vector
addresses; - ConstString current_name = - current_symbol->GetMangled().GetName(Mangled::ePreferMangled); if (current_symbol->IsTrampoline()) { + ConstString trampoline_name = + current_symbol->GetMangled().GetName(Mangled::ePreferMangled); - if (current_name) { + if (trampoline_name) { const ModuleList &images = target_sp->GetImages(); SymbolContextList code_symbols; - images.FindSymbolsWithNameAndType(current_name, eSymbolTypeCode, + images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeCode, code_symbols); for (const SymbolContext &context : code_symbols) { Address addr = context.GetFunctionOrSymbolAddress(); @@ -946,8 +945,8 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, } SymbolContextList reexported_symbols; - images.FindSymbolsWithNameAndType(current_name, eSymbolTypeReExported, - reexported_symbols); + images.FindSymbolsWithNameAndType( + trampoline_name, eSymbolTypeReExported, reexported_symbols); for (const SymbolContext &context : reexported_symbols) { if (context.symbol) { Symbol *actual_symbol = @@ -969,7 +968,7 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, } SymbolContextList indirect_symbols; - images.FindSymbolsWithNameAndType(current_name, eSymbolTypeResolver, + images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeResolver, indirect_symbols); for (const SymbolContext &context : indirect_symbols) { @@ -1029,18 +1028,6 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, thread_plan_sp = std::make_shared( thread, load_addrs, stop_others); } - // One more case we have to consider is "branch islands". These are regular - // TEXT symbols but their names end in .island. They are to allow arm64 - // code to branch further than the size of the address slot allows. We - // just need to single-instruction step in that case. - if (!thread_plan_sp && current_name.GetStringRef().ends_with(".island")) { - thread_plan_sp = std::make_shared( - thread, - /* step_over= */ false, /* stop_others */ false, eVoteNoOpinion, - eVoteNoOpinion); - LLDB_LOG(log, "Stepping one instruction over branch island: '{0}'.", - current_name); - } } else { LLDB_LOGF(log, "Could not find symbol for step through."); } diff --git a/lldb/test/API/macosx/branch-islands/Makefile b/lldb/test/API/macosx/branch-islands/Makefile deleted file mode 100644 index 8675bbf6f85de..0000000000000 --- a/lldb/test/API/macosx/branch-islands/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -C_SOURCES := main.c foo.c -CFLAGS_EXTRAS := -std=c99 - -include Makefile.rules - -a.out: main.o padding1.o padding2.o foo.o - ${CC} ${LDFLAGS} foo.o padding1.o padding2.o main.o -o a.out - -padding1.o: padding1.s - ${CC} -c $(SRCDIR)/padding1.s - -padding2.o: padding2.s - ${CC} -c $(SRCDIR)/padding2.s diff --git a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py deleted file mode 100644 index b397e0c229b08..0000000000000 --- a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py +++ /dev/null @@ -1,35 +0,0 @@ -""" -Make sure that we can step in across an arm64 branch island -""" - - -import lldb -import lldbsuite.test.lldbutil as lldbutil -from lldbsuite.test.lldbtest import * -from lldbsuite.test.decorators import * - - -class TestBranchIslandStepping(TestBase): - NO_DEBUG_INFO_TESTCASE = True - - @skipUnlessDarwin - def test_step_in_branch_island(self): - """Make sure we can step in across a branch island""" - self.build() - self.main_source_file = lldb.SBFileSpec("main.c") - self.do_test() - - def do_test(self): - (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( - self, "Set a breakpoint here", self.main_source_file - ) - - # Make sure that we did manage to generate a branch island for foo: - syms = target.FindSymbols("foo.island", lldb.eSymbolTypeCode) - self.assertEqual(len(syms), 1, "We did generate an island for foo") - - thread.StepInto() - stop_frame = thread.frames[0] - self.assertIn("foo", stop_frame.name, "Stepped into foo") - var = stop_frame.FindVariable("a_variable_in_foo") - self.assertTrue(var.IsValid(), "Found the variable in foo") diff --git a/lldb/test/API/macosx/branch-islands/foo.c b/lldb/test/API/macosx/branch-islands/foo.c deleted file mode 100644 index a5dd2e59e1d82..0000000000000 --- a/lldb/test/API/macosx/branch-islands/foo.c +++ /dev/null @@ -1,6 +0,0 @@ -#include - -void foo() { - int a_variable_in_foo = 10; - printf("I am foo: %d.\n", a_variable_in_foo); -} diff --git a/lldb/test/API/macosx/branch-islands/main.c b/lldb/test/API/macosx/branch-islands/main.c deleted file mode 100644 index b5578bdd715df..0000000000000 --- a/lldb/test/API/macosx/branch-islands/main.c +++ /dev/null @@ -1,6 +0,0 @@ -extern void foo(); - -int main() { - foo(); // Set a breakpoint here - return 0; -} diff --git a/lldb/test/API/macosx/branch-islands/padding1.s b/lldb/test/API/macosx/branch-islands/padding1.s deleted file mode 100644 index e3bdf7007d757..0000000000000 --- a/lldb/test/API/macosx/branch-islands/padding1.s +++ /dev/null @@ -1,3 +0,0 @@ -.text -_junk1: -.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding2.s b/lldb/test/API/macosx/branch-islands/padding2.s deleted file mode 100644 index 187a2c3ebd117..0000000000000 --- a/lldb/test/API/macosx/branch-islands/padding2.s +++ /dev/null @@ -1,3 +0,0 @@ -.text -_junk2: -.space 120*1024*1024 ``````````
https://github.com/llvm/llvm-project/pull/138569 From lldb-commits at lists.llvm.org Mon May 5 12:52:45 2025 From: lldb-commits at lists.llvm.org (David Peixotto via lldb-commits) Date: Mon, 05 May 2025 12:52:45 -0700 (PDT) Subject: [Lldb-commits] [lldb] Add commands to list/enable/disable plugins (PR #134418) In-Reply-To: Message-ID: <6819170d.630a0220.37da94.ad44@mx.google.com> dmpots wrote: ping @clayborg @JDevlieghere @jimingham. I updated the PR to include plugin info in the stats. Please take a look when you get a chance. Thanks! https://github.com/llvm/llvm-project/pull/134418 From lldb-commits at lists.llvm.org Mon May 5 12:59:26 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Mon, 05 May 2025 12:59:26 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Specify the executable path in the attach info (PR #138557) In-Reply-To: Message-ID: <6819189e.170a0220.2cabd8.8013@mx.google.com> https://github.com/ashgti approved this pull request. LGTM! https://github.com/llvm/llvm-project/pull/138557 From lldb-commits at lists.llvm.org Mon May 5 13:34:33 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Mon, 05 May 2025 13:34:33 -0700 (PDT) Subject: [Lldb-commits] [lldb] 9544943 - [lldb-dap] Specify the executable path in the attach info (#138557) Message-ID: <681920d9.630a0220.3b01e.b720@mx.google.com> Author: Jonas Devlieghere Date: 2025-05-05T13:34:29-07:00 New Revision: 9544943e2458597dc2cdcbed6de2a8d50da0d382 URL: https://github.com/llvm/llvm-project/commit/9544943e2458597dc2cdcbed6de2a8d50da0d382 DIFF: https://github.com/llvm/llvm-project/commit/9544943e2458597dc2cdcbed6de2a8d50da0d382.diff LOG: [lldb-dap] Specify the executable path in the attach info (#138557) Currently, we are only using the executable name when attaching. The AttachRequestHandler isn't setting the path in the attach info, which means that we rely on the target when attaching by name. When wo go down this path, we only look at the executable's filename, not its full path. Since we know the full path from the attach arguments, we should specify it in the attach info. Fixes #138197 Added: Modified: lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp Removed: ################################################################################ diff --git a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp index 7084d30f2625b..7a0f091128e4a 100644 --- a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp @@ -49,7 +49,6 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { llvm::json::Object response; lldb::SBError error; FillResponse(request, response); - lldb::SBAttachInfo attach_info; const int invalid_port = 0; const auto *arguments = request.getObject("arguments"); const lldb::pid_t pid = @@ -58,10 +57,7 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { GetInteger(arguments, "gdb-remote-port").value_or(invalid_port); const auto gdb_remote_hostname = GetString(arguments, "gdb-remote-hostname").value_or("localhost"); - if (pid != LLDB_INVALID_PROCESS_ID) - attach_info.SetProcessID(pid); const auto wait_for = GetBoolean(arguments, "waitFor").value_or(false); - attach_info.SetWaitForLaunch(wait_for, false /*async*/); dap.configuration.initCommands = GetStrings(arguments, "initCommands"); dap.configuration.preRunCommands = GetStrings(arguments, "preRunCommands"); dap.configuration.postRunCommands = GetStrings(arguments, "postRunCommands"); @@ -161,7 +157,13 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", error); } else { - // Attach by process name or id. + // Attach by pid or process name. + lldb::SBAttachInfo attach_info; + if (pid != LLDB_INVALID_PROCESS_ID) + attach_info.SetProcessID(pid); + else if (dap.configuration.program.has_value()) + attach_info.SetExecutable(dap.configuration.program->data()); + attach_info.SetWaitForLaunch(wait_for, false /*async*/); dap.target.Attach(attach_info, error); } } else { From lldb-commits at lists.llvm.org Mon May 5 13:34:36 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Mon, 05 May 2025 13:34:36 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Specify the executable path in the attach info (PR #138557) In-Reply-To: Message-ID: <681920dc.630a0220.2fd782.b276@mx.google.com> https://github.com/JDevlieghere closed https://github.com/llvm/llvm-project/pull/138557 From lldb-commits at lists.llvm.org Mon May 5 13:35:39 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Mon, 05 May 2025 13:35:39 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add IsCoreDumping to ProcessInstanceInfo (PR #138580) Message-ID: https://github.com/Jlalond created https://github.com/llvm/llvm-project/pull/138580 This is the first useful patch in the series related to enabling `PTRACE_SEIZE` for processes Coredumping. In order to make the decision if we want to seize or attach, we need to expose that in processinfo. We currently select a few entries from `/proc/pid/status`, and I now also extract CoreDumping. Note that in status it is `CoreDumping` not `Coredumping`, so I kept with that, even if I prefer `Coredumping` >From 03d5449b30f414e2a3f322f102101dc4b2c05c4f Mon Sep 17 00:00:00 2001 From: Jacob Lalonde Date: Mon, 5 May 2025 11:33:05 -0700 Subject: [PATCH 1/2] Add IsCoredumping to ProcessInfo to use in the future of the ptrace_seize code --- lldb/include/lldb/Utility/ProcessInfo.h | 6 ++++++ lldb/source/Host/linux/Host.cpp | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/lldb/include/lldb/Utility/ProcessInfo.h b/lldb/include/lldb/Utility/ProcessInfo.h index 78ade4bbb1ee6..24041faad80bf 100644 --- a/lldb/include/lldb/Utility/ProcessInfo.h +++ b/lldb/include/lldb/Utility/ProcessInfo.h @@ -247,6 +247,11 @@ class ProcessInstanceInfo : public ProcessInfo { std::optional IsZombie() const { return m_zombie; } + // proc/../status specifies CoreDumping as the field + // so we match the case here. + void SetIsCoreDumping(bool is_coredumping) { m_coredumping = is_coredumping; } + std::optional IsCoreDumping() const { return m_coredumping; } + void Dump(Stream &s, UserIDResolver &resolver) const; static void DumpTableHeader(Stream &s, bool show_args, bool verbose); @@ -266,6 +271,7 @@ class ProcessInstanceInfo : public ProcessInfo { struct timespec m_cumulative_system_time; std::optional m_priority_value = std::nullopt; std::optional m_zombie = std::nullopt; + std::optional m_coredumping = std::nullopt; }; typedef std::vector ProcessInstanceInfoList; diff --git a/lldb/source/Host/linux/Host.cpp b/lldb/source/Host/linux/Host.cpp index 8b475a7ab5003..97e8024da6c07 100644 --- a/lldb/source/Host/linux/Host.cpp +++ b/lldb/source/Host/linux/Host.cpp @@ -213,6 +213,11 @@ static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo, } else if (Line.consume_front("Tgid:")) { Line = Line.ltrim(); Line.consumeInteger(10, Tgid); + } else if (Line.consume_front("CoreDumping:")) { + uint32_t coredumping; + Line = Line.ltrim(); + Line.consumeInteger(1, coredumping); + ProcessInfo.SetIsCoredumping(coredumping); } } return true; >From 7e2b11ff8d63260d34ff63e9eeb2d519d887742c Mon Sep 17 00:00:00 2001 From: Jacob Lalonde Date: Mon, 5 May 2025 13:18:31 -0700 Subject: [PATCH 2/2] Add test --- lldb/source/Host/linux/Host.cpp | 2 +- lldb/unittests/Host/posix/HostTest.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lldb/source/Host/linux/Host.cpp b/lldb/source/Host/linux/Host.cpp index 97e8024da6c07..2e2d4fdd84097 100644 --- a/lldb/source/Host/linux/Host.cpp +++ b/lldb/source/Host/linux/Host.cpp @@ -217,7 +217,7 @@ static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo, uint32_t coredumping; Line = Line.ltrim(); Line.consumeInteger(1, coredumping); - ProcessInfo.SetIsCoredumping(coredumping); + ProcessInfo.SetIsCoreDumping(coredumping); } } return true; diff --git a/lldb/unittests/Host/posix/HostTest.cpp b/lldb/unittests/Host/posix/HostTest.cpp index 5d50de3524d1e..082edccf4e774 100644 --- a/lldb/unittests/Host/posix/HostTest.cpp +++ b/lldb/unittests/Host/posix/HostTest.cpp @@ -115,5 +115,8 @@ TEST_F(HostTest, GetProcessInfoSetsPriority) { } ASSERT_TRUE(Info.IsZombie().has_value()); ASSERT_FALSE(Info.IsZombie().value()); + + ASSERT_TRUE(Info.IsCoreDumping().has_value()); + ASSERT_FALSE(Info.IsCoreDumping().value()); } #endif From lldb-commits at lists.llvm.org Mon May 5 13:36:09 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Mon, 05 May 2025 13:36:09 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add IsCoreDumping to ProcessInstanceInfo (PR #138580) In-Reply-To: Message-ID: <68192139.170a0220.226231.96a9@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Jacob Lalonde (Jlalond)
Changes This is the first useful patch in the series related to enabling `PTRACE_SEIZE` for processes Coredumping. In order to make the decision if we want to seize or attach, we need to expose that in processinfo. We currently select a few entries from `/proc/pid/status`, and I now also extract CoreDumping. Note that in status it is `CoreDumping` not `Coredumping`, so I kept with that, even if I prefer `Coredumping` --- Full diff: https://github.com/llvm/llvm-project/pull/138580.diff 3 Files Affected: - (modified) lldb/include/lldb/Utility/ProcessInfo.h (+6) - (modified) lldb/source/Host/linux/Host.cpp (+5) - (modified) lldb/unittests/Host/posix/HostTest.cpp (+3) ``````````diff diff --git a/lldb/include/lldb/Utility/ProcessInfo.h b/lldb/include/lldb/Utility/ProcessInfo.h index 78ade4bbb1ee6..24041faad80bf 100644 --- a/lldb/include/lldb/Utility/ProcessInfo.h +++ b/lldb/include/lldb/Utility/ProcessInfo.h @@ -247,6 +247,11 @@ class ProcessInstanceInfo : public ProcessInfo { std::optional IsZombie() const { return m_zombie; } + // proc/../status specifies CoreDumping as the field + // so we match the case here. + void SetIsCoreDumping(bool is_coredumping) { m_coredumping = is_coredumping; } + std::optional IsCoreDumping() const { return m_coredumping; } + void Dump(Stream &s, UserIDResolver &resolver) const; static void DumpTableHeader(Stream &s, bool show_args, bool verbose); @@ -266,6 +271,7 @@ class ProcessInstanceInfo : public ProcessInfo { struct timespec m_cumulative_system_time; std::optional m_priority_value = std::nullopt; std::optional m_zombie = std::nullopt; + std::optional m_coredumping = std::nullopt; }; typedef std::vector ProcessInstanceInfoList; diff --git a/lldb/source/Host/linux/Host.cpp b/lldb/source/Host/linux/Host.cpp index 8b475a7ab5003..2e2d4fdd84097 100644 --- a/lldb/source/Host/linux/Host.cpp +++ b/lldb/source/Host/linux/Host.cpp @@ -213,6 +213,11 @@ static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo, } else if (Line.consume_front("Tgid:")) { Line = Line.ltrim(); Line.consumeInteger(10, Tgid); + } else if (Line.consume_front("CoreDumping:")) { + uint32_t coredumping; + Line = Line.ltrim(); + Line.consumeInteger(1, coredumping); + ProcessInfo.SetIsCoreDumping(coredumping); } } return true; diff --git a/lldb/unittests/Host/posix/HostTest.cpp b/lldb/unittests/Host/posix/HostTest.cpp index 5d50de3524d1e..082edccf4e774 100644 --- a/lldb/unittests/Host/posix/HostTest.cpp +++ b/lldb/unittests/Host/posix/HostTest.cpp @@ -115,5 +115,8 @@ TEST_F(HostTest, GetProcessInfoSetsPriority) { } ASSERT_TRUE(Info.IsZombie().has_value()); ASSERT_FALSE(Info.IsZombie().value()); + + ASSERT_TRUE(Info.IsCoreDumping().has_value()); + ASSERT_FALSE(Info.IsCoreDumping().value()); } #endif ``````````
https://github.com/llvm/llvm-project/pull/138580 From lldb-commits at lists.llvm.org Mon May 5 13:36:23 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Mon, 05 May 2025 13:36:23 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add IsCoreDumping to ProcessInstanceInfo (PR #138580) In-Reply-To: Message-ID: <68192147.170a0220.24a301.6176@mx.google.com> https://github.com/Jlalond edited https://github.com/llvm/llvm-project/pull/138580 From lldb-commits at lists.llvm.org Mon May 5 14:12:56 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Mon, 05 May 2025 14:12:56 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Upstream lldb-rpc-gen and LLDB RPC server-side emitters (PR #136748) In-Reply-To: Message-ID: <681929d8.170a0220.32287a.9f9a@mx.google.com> ================ @@ -0,0 +1,535 @@ +//===-- 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/Mangle.h" +#include "clang/Lex/Lexer.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +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", // What is this used for? + "SBInputReader", // What is this used for? + "SBCommandPluginInterface", // This is hard to support, we can do it if + // really needed though. + "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. +}; + +// We intentionally avoid generating certain methods either because they are +// difficult to support correctly or they aren't really used much from C++. +// FIXME: We should be able to annotate these methods instead of maintaining a +// list in the generator itself. +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", ---------------- chelcassanova wrote: I believe that the mangling should be stable on platforms using Clang. Since this is a ClangTool itself, I'm actually currently unsure as to how this would compile using GCC. By the way, we can continue this line of discussion on one of the smaller patches I put up to upstream this tool and its shared common code: https://github.com/llvm/llvm-project/pull/138031 https://github.com/llvm/llvm-project/pull/136748 From lldb-commits at lists.llvm.org Mon May 5 14:19:19 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Mon, 05 May 2025 14:19:19 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Upstream lldb-rpc-gen and LLDB RPC server-side emitters (PR #136748) In-Reply-To: Message-ID: <68192b57.a70a0220.1be032.fdc3@mx.google.com> https://github.com/chelcassanova closed https://github.com/llvm/llvm-project/pull/136748 From lldb-commits at lists.llvm.org Mon May 5 14:19:21 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Mon, 05 May 2025 14:19:21 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Upstream lldb-rpc-gen and LLDB RPC server-side emitters (PR #136748) In-Reply-To: Message-ID: <68192b59.170a0220.c5ef.a055@mx.google.com> chelcassanova wrote: I split this large patch up into 3 smaller ones: This patch upstreams the `lldb-rpc-gen` tool itself as well as its common code: https://github.com/llvm/llvm-project/pull/138031 This upstreams the server-side emitter: https://github.com/llvm/llvm-project/pull/138032 This upstreams the Python scripts used to modify headers: https://github.com/llvm/llvm-project/pull/138028. This current patch itself is very very large, so I'll close it and let discussion continue on the above 3. https://github.com/llvm/llvm-project/pull/136748 From lldb-commits at lists.llvm.org Mon May 5 14:50:26 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Mon, 05 May 2025 14:50:26 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Give attach test binaries unique names (PR #138435) In-Reply-To: Message-ID: <681932a2.170a0220.1d2a6e.a014@mx.google.com> JDevlieghere wrote: This is still necessary, at least on Darwin, because `vAttachWait` only looks at the basename. https://github.com/llvm/llvm-project/pull/138435 From lldb-commits at lists.llvm.org Mon May 5 14:52:25 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Mon, 05 May 2025 14:52:25 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Give attach test binaries unique names (PR #138435) In-Reply-To: Message-ID: <68193319.170a0220.42b75.ac60@mx.google.com> https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/138435 >From 50c11b4360cf8781c99ab207b70b8de291e5a36e Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Wed, 2 Apr 2025 00:47:44 -0700 Subject: [PATCH] [lldb-dap] Give attach test binaries unique names Give the test binaries used for attaching unique names to avoid accidentally attaching to the wrong binary. Fixes #138197 --- .../test/tools/lldb-dap/lldbdap_testcase.py | 13 ++++-- .../tools/lldb-dap/attach/TestDAP_attach.py | 40 +++++-------------- .../attach/TestDAP_attachByPortNum.py | 13 ++---- .../lldb-dap/commands/TestDAP_commands.py | 4 +- 4 files changed, 25 insertions(+), 45 deletions(-) diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py index ee5272850b9a8..2c14bb35162b5 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py @@ -1,6 +1,6 @@ import os import time -import subprocess +import uuid import dap_server from lldbsuite.test.lldbtest import * @@ -28,10 +28,17 @@ def create_debug_adapter(self, lldbDAPEnv=None, connection=None): env=lldbDAPEnv, ) - def build_and_create_debug_adapter(self, lldbDAPEnv=None): - self.build() + def build_and_create_debug_adapter(self, lldbDAPEnv=None, dictionary=None): + self.build(dictionary=dictionary) self.create_debug_adapter(lldbDAPEnv) + def build_and_create_debug_adapter_for_attach(self): + """Variant of build_and_create_debug_adapter that builds a uniquely + named binary.""" + unique_name = str(uuid.uuid4()) + self.build_and_create_debug_adapter(dictionary={"EXE": unique_name}) + return self.getBuildArtifact(unique_name) + def set_source_breakpoints(self, source_path, lines, data=None): """Sets source breakpoints and returns an array of strings containing the breakpoint IDs ("1", "2") for each breakpoint that was set. diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py index 6f70316821c8c..f48d5a7db3c50 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py @@ -2,7 +2,6 @@ Test lldb-dap attach request """ - import dap_server from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * @@ -25,7 +24,7 @@ def spawn_and_wait(program, delay): process.wait() - at skipIf + at skip class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase): def set_and_hit_breakpoint(self, continueToExit=True): source = "main.c" @@ -45,8 +44,7 @@ def test_by_pid(self): """ Tests attaching to a process by process ID. """ - self.build_and_create_debug_adapter() - program = self.getBuildArtifact("a.out") + program = self.build_and_create_debug_adapter_for_attach() self.process = subprocess.Popen( [program], stdin=subprocess.PIPE, @@ -61,34 +59,15 @@ def test_by_name(self): """ Tests attaching to a process by process name. """ - self.build_and_create_debug_adapter() - orig_program = self.getBuildArtifact("a.out") - # Since we are going to attach by process name, we need a unique - # process name that has minimal chance to match a process that is - # already running. To do this we use tempfile.mktemp() to give us a - # full path to a location where we can copy our executable. We then - # run this copy to ensure we don't get the error "more that one - # process matches 'a.out'". - program = tempfile.mktemp() - shutil.copyfile(orig_program, program) - shutil.copymode(orig_program, program) + program = self.build_and_create_debug_adapter_for_attach() # Use a file as a synchronization point between test and inferior. pid_file_path = lldbutil.append_to_process_working_directory( self, "pid_file_%d" % (int(time.time())) ) - def cleanup(): - if os.path.exists(program): - os.unlink(program) - self.run_platform_command("rm %s" % (pid_file_path)) - - # Execute the cleanup function during test case tear down. - self.addTearDownHook(cleanup) - popen = self.spawnSubprocess(program, [pid_file_path]) - - pid = lldbutil.wait_for_file_on_target(self, pid_file_path) + lldbutil.wait_for_file_on_target(self, pid_file_path) self.attach(program=program) self.set_and_hit_breakpoint(continueToExit=True) @@ -101,8 +80,7 @@ def test_by_name_waitFor(self): next instance of a process to be launched, ingoring all current ones. """ - self.build_and_create_debug_adapter() - program = self.getBuildArtifact("a.out") + program = self.build_and_create_debug_adapter_for_attach() self.spawn_thread = threading.Thread( target=spawn_and_wait, args=( @@ -136,8 +114,8 @@ def test_commands(self): "terminateCommands" are a list of LLDB commands that get executed when the debugger session terminates. """ - self.build_and_create_debug_adapter() - program = self.getBuildArtifact("a.out") + program = self.build_and_create_debug_adapter_for_attach() + # Here we just create a target and launch the process as a way to test # if we are able to use attach commands to create any kind of a target # and use it for debugging @@ -209,8 +187,8 @@ def test_terminate_commands(self): Tests that the "terminateCommands", that can be passed during attach, are run when the debugger is disconnected. """ - self.build_and_create_debug_adapter() - program = self.getBuildArtifact("a.out") + program = self.build_and_create_debug_adapter_for_attach() + # Here we just create a target and launch the process as a way to test # if we are able to use attach commands to create any kind of a target # and use it for debugging diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py index 51f62b79f3f4f..7f93b9f2a3a22 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py @@ -2,7 +2,6 @@ Test lldb-dap "port" configuration to "attach" request """ - import dap_server from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * @@ -59,8 +58,7 @@ def test_by_port(self): """ Tests attaching to a process by port. """ - self.build_and_create_debug_adapter() - program = self.getBuildArtifact("a.out") + program = self.build_and_create_debug_adapter_for_attach() debug_server_tool = self.getBuiltinDebugServerTool() @@ -91,8 +89,7 @@ def test_by_port_and_pid(self): """ Tests attaching to a process by process ID and port number. """ - self.build_and_create_debug_adapter() - program = self.getBuildArtifact("a.out") + program = self.build_and_create_debug_adapter_for_attach() # It is not necessary to launch "lldb-server" to obtain the actual port and pid for attaching. # However, when providing the port number and pid directly, "lldb-dap" throws an error message, which is expected. @@ -119,8 +116,7 @@ def test_by_invalid_port(self): """ Tests attaching to a process by invalid port number 0. """ - self.build_and_create_debug_adapter() - program = self.getBuildArtifact("a.out") + program = self.build_and_create_debug_adapter_for_attach() port = 0 response = self.attach( @@ -138,8 +134,7 @@ def test_by_illegal_port(self): """ Tests attaching to a process by illegal/greater port number 65536 """ - self.build_and_create_debug_adapter() - program = self.getBuildArtifact("a.out") + program = self.build_and_create_debug_adapter_for_attach() port = 65536 args = [program] diff --git a/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py b/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py index 8398eeab7bba2..4aecf9a665c06 100644 --- a/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py +++ b/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py @@ -5,6 +5,7 @@ from lldbsuite.test import lldbtest, lldbutil from lldbsuite.test.decorators import * + # DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. @skip class TestDAP_commands(lldbdap_testcase.DAPTestCaseBase): @@ -71,12 +72,11 @@ def test_command_directive_abort_on_error_post_run_commands(self): self.do_test_abort_on_error(use_post_run_commands=True) def test_command_directive_abort_on_error_attach_commands(self): - program = self.getBuildArtifact("a.out") command_quiet = ( "settings set target.show-hex-variable-values-with-leading-zeroes false" ) command_abort_on_error = "settings set foo bar" - self.build_and_create_debug_adapter() + program = self.build_and_create_debug_adapter_for_attach() self.attach( program, attachCommands=["?!" + command_quiet, "!" + command_abort_on_error], From lldb-commits at lists.llvm.org Mon May 5 15:14:55 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Mon, 05 May 2025 15:14:55 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Don't error out when the process is in eStateUnloaded (PR #138601) Message-ID: https://github.com/JDevlieghere created https://github.com/llvm/llvm-project/pull/138601 DAP::WaitForProcessToStop treats the process in eStateUnloaded as an error. The process is in this state when it has just been created (before an attach or launch) or when it's restarted. Neither should be treated as errors. The current implementation can trigger this error (and a corresponding test failure) when we call WaitForProcessToStop after attaching in asynchronous mode (for example when using ConnectRemote which is always asynchronous (due to a bug). Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Mon May 5 15:15:28 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Mon, 05 May 2025 15:15:28 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][docs] Update instructions to build standalone (PR #137383) In-Reply-To: Message-ID: <68193880.170a0220.d17cd.a81e@mx.google.com> https://github.com/chelcassanova updated https://github.com/llvm/llvm-project/pull/137383 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Mon May 5 15:15:31 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Mon, 05 May 2025 15:15:31 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Don't error out when the process is in eStateUnloaded (PR #138601) In-Reply-To: Message-ID: <68193883.170a0220.1c4af9.a98c@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Jonas Devlieghere (JDevlieghere)
Changes DAP::WaitForProcessToStop treats the process in eStateUnloaded as an error. The process is in this state when it has just been created (before an attach or launch) or when it's restarted. Neither should be treated as errors. The current implementation can trigger this error (and a corresponding test failure) when we call WaitForProcessToStop after attaching in asynchronous mode (for example when using ConnectRemote which is always asynchronous (due to a bug). --- Full diff: https://github.com/llvm/llvm-project/pull/138601.diff 1 Files Affected: - (modified) lldb/tools/lldb-dap/DAP.cpp (+1-3) ``````````diff diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 4cb0d8e49004c..4b631484c9fab 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -968,6 +968,7 @@ lldb::SBError DAP::WaitForProcessToStop(std::chrono::seconds seconds) { while (std::chrono::steady_clock::now() < timeout_time) { const auto state = process.GetState(); switch (state) { + case lldb::eStateUnloaded: case lldb::eStateAttaching: case lldb::eStateConnected: case lldb::eStateInvalid: @@ -982,9 +983,6 @@ lldb::SBError DAP::WaitForProcessToStop(std::chrono::seconds seconds) { case lldb::eStateExited: error.SetErrorString("process exited during launch or attach"); return error; - case lldb::eStateUnloaded: - error.SetErrorString("process unloaded during launch or attach"); - return error; case lldb::eStateCrashed: case lldb::eStateStopped: return lldb::SBError(); // Success! ``````````
https://github.com/llvm/llvm-project/pull/138601 From lldb-commits at lists.llvm.org Mon May 5 15:16:02 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Mon, 05 May 2025 15:16:02 -0700 (PDT) Subject: [Lldb-commits] [lldb] 3ceec26 - [lldb-dap] Give attach test binaries unique names (#138435) Message-ID: <681938a2.170a0220.17dffe.abc7@mx.google.com> Author: Jonas Devlieghere Date: 2025-05-05T15:15:58-07:00 New Revision: 3ceec268413860b466b14600ce67d8bbd09ff75c URL: https://github.com/llvm/llvm-project/commit/3ceec268413860b466b14600ce67d8bbd09ff75c DIFF: https://github.com/llvm/llvm-project/commit/3ceec268413860b466b14600ce67d8bbd09ff75c.diff LOG: [lldb-dap] Give attach test binaries unique names (#138435) Give the test binaries used for attaching unique names to avoid accidentally attaching to the wrong binary. Fixes #138197 Added: Modified: lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py Removed: ################################################################################ diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py index ee5272850b9a8..2c14bb35162b5 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py @@ -1,6 +1,6 @@ import os import time -import subprocess +import uuid import dap_server from lldbsuite.test.lldbtest import * @@ -28,10 +28,17 @@ def create_debug_adapter(self, lldbDAPEnv=None, connection=None): env=lldbDAPEnv, ) - def build_and_create_debug_adapter(self, lldbDAPEnv=None): - self.build() + def build_and_create_debug_adapter(self, lldbDAPEnv=None, dictionary=None): + self.build(dictionary=dictionary) self.create_debug_adapter(lldbDAPEnv) + def build_and_create_debug_adapter_for_attach(self): + """Variant of build_and_create_debug_adapter that builds a uniquely + named binary.""" + unique_name = str(uuid.uuid4()) + self.build_and_create_debug_adapter(dictionary={"EXE": unique_name}) + return self.getBuildArtifact(unique_name) + def set_source_breakpoints(self, source_path, lines, data=None): """Sets source breakpoints and returns an array of strings containing the breakpoint IDs ("1", "2") for each breakpoint that was set. diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py index 6f70316821c8c..f48d5a7db3c50 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py @@ -2,7 +2,6 @@ Test lldb-dap attach request """ - import dap_server from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * @@ -25,7 +24,7 @@ def spawn_and_wait(program, delay): process.wait() - at skipIf + at skip class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase): def set_and_hit_breakpoint(self, continueToExit=True): source = "main.c" @@ -45,8 +44,7 @@ def test_by_pid(self): """ Tests attaching to a process by process ID. """ - self.build_and_create_debug_adapter() - program = self.getBuildArtifact("a.out") + program = self.build_and_create_debug_adapter_for_attach() self.process = subprocess.Popen( [program], stdin=subprocess.PIPE, @@ -61,34 +59,15 @@ def test_by_name(self): """ Tests attaching to a process by process name. """ - self.build_and_create_debug_adapter() - orig_program = self.getBuildArtifact("a.out") - # Since we are going to attach by process name, we need a unique - # process name that has minimal chance to match a process that is - # already running. To do this we use tempfile.mktemp() to give us a - # full path to a location where we can copy our executable. We then - # run this copy to ensure we don't get the error "more that one - # process matches 'a.out'". - program = tempfile.mktemp() - shutil.copyfile(orig_program, program) - shutil.copymode(orig_program, program) + program = self.build_and_create_debug_adapter_for_attach() # Use a file as a synchronization point between test and inferior. pid_file_path = lldbutil.append_to_process_working_directory( self, "pid_file_%d" % (int(time.time())) ) - def cleanup(): - if os.path.exists(program): - os.unlink(program) - self.run_platform_command("rm %s" % (pid_file_path)) - - # Execute the cleanup function during test case tear down. - self.addTearDownHook(cleanup) - popen = self.spawnSubprocess(program, [pid_file_path]) - - pid = lldbutil.wait_for_file_on_target(self, pid_file_path) + lldbutil.wait_for_file_on_target(self, pid_file_path) self.attach(program=program) self.set_and_hit_breakpoint(continueToExit=True) @@ -101,8 +80,7 @@ def test_by_name_waitFor(self): next instance of a process to be launched, ingoring all current ones. """ - self.build_and_create_debug_adapter() - program = self.getBuildArtifact("a.out") + program = self.build_and_create_debug_adapter_for_attach() self.spawn_thread = threading.Thread( target=spawn_and_wait, args=( @@ -136,8 +114,8 @@ def test_commands(self): "terminateCommands" are a list of LLDB commands that get executed when the debugger session terminates. """ - self.build_and_create_debug_adapter() - program = self.getBuildArtifact("a.out") + program = self.build_and_create_debug_adapter_for_attach() + # Here we just create a target and launch the process as a way to test # if we are able to use attach commands to create any kind of a target # and use it for debugging @@ -209,8 +187,8 @@ def test_terminate_commands(self): Tests that the "terminateCommands", that can be passed during attach, are run when the debugger is disconnected. """ - self.build_and_create_debug_adapter() - program = self.getBuildArtifact("a.out") + program = self.build_and_create_debug_adapter_for_attach() + # Here we just create a target and launch the process as a way to test # if we are able to use attach commands to create any kind of a target # and use it for debugging diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py index 51f62b79f3f4f..7f93b9f2a3a22 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py @@ -2,7 +2,6 @@ Test lldb-dap "port" configuration to "attach" request """ - import dap_server from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * @@ -59,8 +58,7 @@ def test_by_port(self): """ Tests attaching to a process by port. """ - self.build_and_create_debug_adapter() - program = self.getBuildArtifact("a.out") + program = self.build_and_create_debug_adapter_for_attach() debug_server_tool = self.getBuiltinDebugServerTool() @@ -91,8 +89,7 @@ def test_by_port_and_pid(self): """ Tests attaching to a process by process ID and port number. """ - self.build_and_create_debug_adapter() - program = self.getBuildArtifact("a.out") + program = self.build_and_create_debug_adapter_for_attach() # It is not necessary to launch "lldb-server" to obtain the actual port and pid for attaching. # However, when providing the port number and pid directly, "lldb-dap" throws an error message, which is expected. @@ -119,8 +116,7 @@ def test_by_invalid_port(self): """ Tests attaching to a process by invalid port number 0. """ - self.build_and_create_debug_adapter() - program = self.getBuildArtifact("a.out") + program = self.build_and_create_debug_adapter_for_attach() port = 0 response = self.attach( @@ -138,8 +134,7 @@ def test_by_illegal_port(self): """ Tests attaching to a process by illegal/greater port number 65536 """ - self.build_and_create_debug_adapter() - program = self.getBuildArtifact("a.out") + program = self.build_and_create_debug_adapter_for_attach() port = 65536 args = [program] diff --git a/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py b/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py index 8398eeab7bba2..4aecf9a665c06 100644 --- a/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py +++ b/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py @@ -5,6 +5,7 @@ from lldbsuite.test import lldbtest, lldbutil from lldbsuite.test.decorators import * + # DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. @skip class TestDAP_commands(lldbdap_testcase.DAPTestCaseBase): @@ -71,12 +72,11 @@ def test_command_directive_abort_on_error_post_run_commands(self): self.do_test_abort_on_error(use_post_run_commands=True) def test_command_directive_abort_on_error_attach_commands(self): - program = self.getBuildArtifact("a.out") command_quiet = ( "settings set target.show-hex-variable-values-with-leading-zeroes false" ) command_abort_on_error = "settings set foo bar" - self.build_and_create_debug_adapter() + program = self.build_and_create_debug_adapter_for_attach() self.attach( program, attachCommands=["?!" + command_quiet, "!" + command_abort_on_error], From lldb-commits at lists.llvm.org Mon May 5 15:16:04 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Mon, 05 May 2025 15:16:04 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Give attach test binaries unique names (PR #138435) In-Reply-To: Message-ID: <681938a4.170a0220.1b1bd.a481@mx.google.com> https://github.com/JDevlieghere closed https://github.com/llvm/llvm-project/pull/138435 From lldb-commits at lists.llvm.org Mon May 5 15:39:05 2025 From: lldb-commits at lists.llvm.org (Muhammad Omair Javaid via lldb-commits) Date: Mon, 05 May 2025 15:39:05 -0700 (PDT) Subject: [Lldb-commits] [lldb] b86b529 - [lldb][test] Mark DynamicValueTestCase XFAIL on Windows Message-ID: <68193e09.170a0220.26b8db.a7da@mx.google.com> Author: Muhammad Omair Javaid Date: 2025-05-06T03:37:54+05:00 New Revision: b86b5296cb649c06abbb6471d6f0f777b91a29c9 URL: https://github.com/llvm/llvm-project/commit/b86b5296cb649c06abbb6471d6f0f777b91a29c9 DIFF: https://github.com/llvm/llvm-project/commit/b86b5296cb649c06abbb6471d6f0f777b91a29c9.diff LOG: [lldb][test] Mark DynamicValueTestCase XFAIL on Windows The newly added test test_from_forward_decl in TestDynamicValue.py by PR #137974 is failing on Windows due to issues with dynamic type resolution. This is a known issue tracked in PR24663. LLDB Windows on Arm Buildbot Failure: https://lab.llvm.org/buildbot/#/builders/141/builds/8391 This change marks the test as XFAIL on Windows using the consistent with how similar tests in the same file are handled. Added: Modified: lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py Removed: ################################################################################ diff --git a/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py b/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py index 3952b88a6c28e..634bd13d7c71a 100644 --- a/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py +++ b/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py @@ -268,6 +268,7 @@ def examine_value_object_of_this_ptr( self.assertLess(contained_b_addr, contained_b_static_addr) @no_debug_info_test + @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24663") def test_from_forward_decl(self): """Test fetching C++ dynamic values forward-declared types. It's imperative that this is a separate test so that we don't end up parsing From lldb-commits at lists.llvm.org Mon May 5 15:39:27 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Mon, 05 May 2025 15:39:27 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Change synthetic symbol names to have file address (PR #138416) In-Reply-To: Message-ID: <68193e1f.170a0220.1dc1c.1569@mx.google.com> JDevlieghere wrote: > @felipepiovezan Hopefully this should fix the problem in the macOS tests ([#137512 (comment)](https://github.com/llvm/llvm-project/pull/137512#issuecomment-2846104161)) though I don't know how to trigger these tests from the PR :| Unfortunately there's no pre-commit testing for Darwin. I applied your patch locally and ran the test suite and the test is still failing because `strip` on Darwin doesn't have a `--keep-symbol` flag. I think you'lll want to use the `-s` flag which allows you to specify a file with symbols to "save" (keep): ``` -s filename Save the symbol table entries for the global symbols listed in filename. The symbol names listed in filename must be one per line. Leading and trailing white space are not part of the symbol name. Lines starting with # are ignored, as are lines with only white space. ``` https://github.com/llvm/llvm-project/pull/138416 From lldb-commits at lists.llvm.org Mon May 5 15:58:40 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Mon, 05 May 2025 15:58:40 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Support alternatives for scope format entries (PR #137751) In-Reply-To: Message-ID: <681942a0.170a0220.35dbc3.b01c@mx.google.com> JDevlieghere wrote: @Michael137 I looked into adding the diagnostic you asked about and while the implementation is relatively straightforward, I'm even more convinced that I don't think it belongs there because it's a semantic rather than a syntax error. We can detect a scope followed by an alternative scope, i.e. something like `{{foo}|bar}` but what about something like `{foo|bar}`. Just like a scope, `foo` will always resolve, so technically `bar` is unreachable. Should we diagnose this too? And what about `{${frame.pc}||bar}`. The empty string between the two pipes will always resolve. In other words, I don't think this warrants special casing as there are plenty of other scenarios that trigger similar behavior that would be much harder to diagnose. https://github.com/llvm/llvm-project/pull/137751 From lldb-commits at lists.llvm.org Mon May 5 16:09:39 2025 From: lldb-commits at lists.llvm.org (Michael Buch via lldb-commits) Date: Mon, 05 May 2025 16:09:39 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Support alternatives for scope format entries (PR #137751) In-Reply-To: Message-ID: <68194533.050a0220.38447c.3810@mx.google.com> Michael137 wrote: > @Michael137 I looked into adding the diagnostic you asked about and while the implementation is relatively straightforward, I'm even more convinced that I don't think it belongs there because it's a semantic rather than a syntax error. > > We can detect a scope followed by an alternative scope, i.e. something like `{{foo}|bar}` but what about something like `{foo|bar}`. Just like a scope, `foo` will always resolve, so technically `bar` is unreachable. Should we diagnose this too? And what about `{${frame.pc}||bar}`. The empty string between the two pipes will always resolve. In other words, I don't think this warrants special casing as there are plenty of other scenarios that trigger similar behavior that would be much harder to diagnose. Ok thanks for giving it a shot. I agree it doesn't quite fit the existing syntax diagnostics. We can always choose to revisit if this becomes a big point of confusion for people https://github.com/llvm/llvm-project/pull/137751 From lldb-commits at lists.llvm.org Mon May 5 16:09:49 2025 From: lldb-commits at lists.llvm.org (Michael Buch via lldb-commits) Date: Mon, 05 May 2025 16:09:49 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Support alternatives for scope format entries (PR #137751) In-Reply-To: Message-ID: <6819453d.a70a0220.1b36a8.1888@mx.google.com> https://github.com/Michael137 approved this pull request. https://github.com/llvm/llvm-project/pull/137751 From lldb-commits at lists.llvm.org Mon May 5 16:11:01 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Mon, 05 May 2025 16:11:01 -0700 (PDT) Subject: [Lldb-commits] [lldb] 3e235a7 - [lldb] Support alternatives for scope format entries (#137751) Message-ID: <68194585.a70a0220.31c217.0891@mx.google.com> Author: Jonas Devlieghere Date: 2025-05-05T16:10:57-07:00 New Revision: 3e235a7c601d80d6e8a0392ebec859068659ec19 URL: https://github.com/llvm/llvm-project/commit/3e235a7c601d80d6e8a0392ebec859068659ec19 DIFF: https://github.com/llvm/llvm-project/commit/3e235a7c601d80d6e8a0392ebec859068659ec19.diff LOG: [lldb] Support alternatives for scope format entries (#137751) This PR implements support for specifying multiple alternatives for scope format entries. Scopes are used to enclose things that should only be printed when everything in the scope resolves. For example, the following scope only resolves if both `${line.file.basename}` and `${line.number}` resolve. ` ``` { at ${line.file.basename}:${line.number}} ``` However, the current implementation doesn't let you specify what to print when they don't resolve. This PR adds support for specifying multiple alternative scopes, which are evaluated left-to-right. For example: ``` { at ${line.file.basename}:${line.number}| in ${function.name}| } ``` This will resolve to: - ` at ${line.file.basename}:${line.number}` if the corresponding variables resolve. - Otherwise, this resolves to ` in ${function.name}` if `${function.name}` resolves. - Otherwise, this resolves to ` ` which always resolves. This PR makes the `|` character a special character within a scope, but allows it to be escaped. I ended up with this approach because it fit quite nicely in the existing architecture of the format entries and by limiting the functionality to scopes, it sidesteps some complexity, like dealing with recursion. Added: Modified: lldb/include/lldb/Core/FormatEntity.h lldb/source/Core/FormatEntity.cpp lldb/unittests/Core/FormatEntityTest.cpp Removed: ################################################################################ diff --git a/lldb/include/lldb/Core/FormatEntity.h b/lldb/include/lldb/Core/FormatEntity.h index 97065afe19bfe..6acf6fbe43239 100644 --- a/lldb/include/lldb/Core/FormatEntity.h +++ b/lldb/include/lldb/Core/FormatEntity.h @@ -11,10 +11,10 @@ #include "lldb/lldb-enumerations.h" #include "lldb/lldb-types.h" +#include "llvm/ADT/SmallVector.h" #include #include #include - #include #include @@ -158,9 +158,7 @@ struct Entry { } Entry(Type t = Type::Invalid, const char *s = nullptr, - const char *f = nullptr) - : string(s ? s : ""), printf_format(f ? f : ""), type(t) {} - + const char *f = nullptr); Entry(llvm::StringRef s); Entry(char ch); @@ -170,15 +168,19 @@ struct Entry { void AppendText(const char *cstr); - void AppendEntry(const Entry &&entry) { children.push_back(entry); } + void AppendEntry(const Entry &&entry); + + void StartAlternative(); void Clear() { string.clear(); printf_format.clear(); - children.clear(); + children_stack.clear(); + children_stack.emplace_back(); type = Type::Invalid; fmt = lldb::eFormatDefault; number = 0; + level = 0; deref = false; } @@ -191,7 +193,7 @@ struct Entry { return false; if (printf_format != rhs.printf_format) return false; - if (children != rhs.children) + if (children_stack != rhs.children_stack) return false; if (type != rhs.type) return false; @@ -202,9 +204,18 @@ struct Entry { return true; } + std::vector &GetChildren(); + std::string string; std::string printf_format; - std::vector children; + + /// A stack of children entries, used by Scope entries to provide alterantive + /// children. All other entries have a stack of size 1. + /// @{ + llvm::SmallVector, 1> children_stack; + size_t level = 0; + /// @} + Type type; lldb::Format fmt = lldb::eFormatDefault; lldb::addr_t number = 0; diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp index f1c831b7ccc1e..4f2d39873c7fb 100644 --- a/lldb/source/Core/FormatEntity.cpp +++ b/lldb/source/Core/FormatEntity.cpp @@ -60,6 +60,7 @@ #include "llvm/Support/Regex.h" #include "llvm/TargetParser/Triple.h" +#include #include #include #include @@ -281,31 +282,53 @@ constexpr Definition g_top_level_entries[] = { constexpr Definition g_root = Entry::DefinitionWithChildren( "", EntryType::Root, g_top_level_entries); +FormatEntity::Entry::Entry(Type t, const char *s, const char *f) + : string(s ? s : ""), printf_format(f ? f : ""), children_stack({{}}), + type(t) {} + FormatEntity::Entry::Entry(llvm::StringRef s) - : string(s.data(), s.size()), printf_format(), children(), - type(Type::String) {} + : string(s.data(), s.size()), children_stack({{}}), type(Type::String) {} FormatEntity::Entry::Entry(char ch) - : string(1, ch), printf_format(), children(), type(Type::String) {} + : string(1, ch), printf_format(), children_stack({{}}), type(Type::String) { +} + +std::vector &FormatEntity::Entry::GetChildren() { + assert(level < children_stack.size()); + return children_stack[level]; +} void FormatEntity::Entry::AppendChar(char ch) { - if (children.empty() || children.back().type != Entry::Type::String) - children.push_back(Entry(ch)); + auto &entries = GetChildren(); + if (entries.empty() || entries.back().type != Entry::Type::String) + entries.push_back(Entry(ch)); else - children.back().string.append(1, ch); + entries.back().string.append(1, ch); } void FormatEntity::Entry::AppendText(const llvm::StringRef &s) { - if (children.empty() || children.back().type != Entry::Type::String) - children.push_back(Entry(s)); + auto &entries = GetChildren(); + if (entries.empty() || entries.back().type != Entry::Type::String) + entries.push_back(Entry(s)); else - children.back().string.append(s.data(), s.size()); + entries.back().string.append(s.data(), s.size()); } void FormatEntity::Entry::AppendText(const char *cstr) { return AppendText(llvm::StringRef(cstr)); } +void FormatEntity::Entry::AppendEntry(const Entry &&entry) { + auto &entries = GetChildren(); + entries.push_back(entry); +} + +void FormatEntity::Entry::StartAlternative() { + assert(type == Entry::Type::Scope); + children_stack.emplace_back(); + level++; +} + #define ENUM_TO_CSTR(eee) \ case FormatEntity::Entry::Type::eee: \ return #eee @@ -405,8 +428,9 @@ void FormatEntity::Entry::Dump(Stream &s, int depth) const { if (deref) s.Printf("deref = true, "); s.EOL(); - for (const auto &child : children) { - child.Dump(s, depth + 1); + for (const auto &children : children_stack) { + for (const auto &child : children) + child.Dump(s, depth + 1); } } @@ -1308,7 +1332,7 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, return true; case Entry::Type::Root: - for (const auto &child : entry.children) { + for (const auto &child : entry.children_stack[0]) { if (!Format(child, s, sc, exe_ctx, addr, valobj, function_changed, initial_function)) { return false; // If any item of root fails, then the formatting fails @@ -1322,19 +1346,26 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, case Entry::Type::Scope: { StreamString scope_stream; - bool success = false; - for (const auto &child : entry.children) { - success = Format(child, scope_stream, sc, exe_ctx, addr, valobj, - function_changed, initial_function); - if (!success) - break; + auto format_children = [&](const std::vector &children) { + scope_stream.Clear(); + for (const auto &child : children) { + if (!Format(child, scope_stream, sc, exe_ctx, addr, valobj, + function_changed, initial_function)) + return false; + } + return true; + }; + + for (auto &children : entry.children_stack) { + if (format_children(children)) { + s.Write(scope_stream.GetString().data(), + scope_stream.GetString().size()); + return true; + } } - // Only if all items in a scope succeed, then do we print the output into - // the main stream - if (success) - s.Write(scope_stream.GetString().data(), scope_stream.GetString().size()); - } + return true; // Scopes always successfully print themselves + } case Entry::Type::Variable: case Entry::Type::VariableSynthetic: @@ -2124,7 +2155,7 @@ static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry, uint32_t depth) { Status error; while (!format.empty() && error.Success()) { - const size_t non_special_chars = format.find_first_of("${}\\"); + const size_t non_special_chars = format.find_first_of("${}\\|"); if (non_special_chars == llvm::StringRef::npos) { // No special characters, just string bytes so add them and we are done @@ -2161,6 +2192,14 @@ static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry, .drop_front(); // Skip the '}' as we are at the end of the scope return error; + case '|': + format = format.drop_front(); // Skip the '|' + if (parent_entry.type == Entry::Type::Scope) + parent_entry.StartAlternative(); + else + parent_entry.AppendChar('|'); + break; + case '\\': { format = format.drop_front(); // Skip the '\' character if (format.empty()) { diff --git a/lldb/unittests/Core/FormatEntityTest.cpp b/lldb/unittests/Core/FormatEntityTest.cpp index 5983c9de99ef7..2cddf8ff4e900 100644 --- a/lldb/unittests/Core/FormatEntityTest.cpp +++ b/lldb/unittests/Core/FormatEntityTest.cpp @@ -8,15 +8,30 @@ #include "lldb/Core/FormatEntity.h" #include "lldb/Utility/Status.h" - +#include "lldb/Utility/StreamString.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include "llvm/Testing/Support/Error.h" #include "gtest/gtest.h" using namespace lldb_private; +using namespace llvm; using Definition = FormatEntity::Entry::Definition; using Entry = FormatEntity::Entry; +static Expected Format(StringRef format_str) { + StreamString stream; + FormatEntity::Entry format; + Status status = FormatEntity::Parse(format_str, format); + if (status.Fail()) + return status.ToError(); + + FormatEntity::Format(format, stream, nullptr, nullptr, nullptr, nullptr, + false, false); + return stream.GetString().str(); +} + TEST(FormatEntityTest, DefinitionConstructionNameAndType) { Definition d("foo", FormatEntity::Entry::Type::Invalid); @@ -153,10 +168,37 @@ constexpr llvm::StringRef lookupStrings[] = { "${target.file.fullpath}", "${var.dummy-var-to-test-wildcard}"}; -TEST(FormatEntity, LookupAllEntriesInTree) { +TEST(FormatEntityTest, LookupAllEntriesInTree) { for (const llvm::StringRef testString : lookupStrings) { Entry e; EXPECT_TRUE(FormatEntity::Parse(testString, e).Success()) << "Formatting " << testString << " did not succeed"; } } + +TEST(FormatEntityTest, Scope) { + // Scope with one alternative. + EXPECT_THAT_EXPECTED(Format("{${frame.pc}|foo}"), HasValue("foo")); + + // Scope with multiple alternatives. + EXPECT_THAT_EXPECTED(Format("{${frame.pc}|${function.name}|foo}"), + HasValue("foo")); + + // Escaped pipe inside a scope. + EXPECT_THAT_EXPECTED(Format("{foo\\|bar}"), HasValue("foo|bar")); + + // Unescaped pipe outside a scope. + EXPECT_THAT_EXPECTED(Format("foo|bar"), HasValue("foo|bar")); + + // Nested scopes. Note that scopes always resolve. + EXPECT_THAT_EXPECTED(Format("{{${frame.pc}|foo}|{bar}}"), HasValue("foo")); + EXPECT_THAT_EXPECTED(Format("{{${frame.pc}}|{bar}}"), HasValue("")); + + // Pipe between scopes. + EXPECT_THAT_EXPECTED(Format("{foo}|{bar}"), HasValue("foo|bar")); + EXPECT_THAT_EXPECTED(Format("{foo}||{bar}"), HasValue("foo||bar")); + + // Empty space between pipes. + EXPECT_THAT_EXPECTED(Format("{{foo}||{bar}}"), HasValue("foo")); + EXPECT_THAT_EXPECTED(Format("{${frame.pc}||{bar}}"), HasValue("")); +} From lldb-commits at lists.llvm.org Mon May 5 16:11:03 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Mon, 05 May 2025 16:11:03 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Support alternatives for scope format entries (PR #137751) In-Reply-To: Message-ID: <68194587.630a0220.1e987a.bec6@mx.google.com> https://github.com/JDevlieghere closed https://github.com/llvm/llvm-project/pull/137751 From lldb-commits at lists.llvm.org Mon May 5 16:23:29 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Mon, 05 May 2025 16:23:29 -0700 (PDT) Subject: [Lldb-commits] [lldb] [DRAFT][lldb][RPC] Design doc for upstreaming PR (PR #138612) Message-ID: https://github.com/chelcassanova created https://github.com/llvm/llvm-project/pull/138612 This mainly adds as design doc to help follow with the current PRs up for upstreaming the `lldb-rpc-gen` tool and emitters. Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Mon May 5 16:24:05 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Mon, 05 May 2025 16:24:05 -0700 (PDT) Subject: [Lldb-commits] [lldb] [DRAFT][lldb][RPC] Design doc for upstreaming PR (PR #138612) In-Reply-To: Message-ID: <68194895.050a0220.28b292.2a56@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Chelsea Cassanova (chelcassanova)
Changes This mainly adds as design doc to help follow with the current PRs up for upstreaming the `lldb-rpc-gen` tool and emitters. --- Full diff: https://github.com/llvm/llvm-project/pull/138612.diff 1 Files Affected: - (added) lldb/docs/rpc-design-doc.rst (+94) ``````````diff diff --git a/lldb/docs/rpc-design-doc.rst b/lldb/docs/rpc-design-doc.rst new file mode 100644 index 0000000000000..97b51e1dd4854 --- /dev/null +++ b/lldb/docs/rpc-design-doc.rst @@ -0,0 +1,94 @@ +LLDB RPC Upstreaming Design Doc +=============================== + +This document aims to explain the general structure of the upstreaming patches for adding LLDB RPC. The 2 primary concepts explained here will be: + +* How LLDB RPC is used +* How the ``lldb-rpc-gen`` works and what it outputs + +LLDB RPC +********* + +LLDB RPC is a framework by which processes can communicate with LLDB out of process while maintaining compatibility with the SB API. More details are explained in the `RFC`_ for upstreaming LLDB RPC, but the main focus in this doc for this section will be how exactly the code is structured for the PRs that will upstream this code. + +The ``lldb-rpc-gen`` tool +************************* + +``lldb-rpc-gen`` is the tool that generates the main client and server interfaces for LLDB RPC. It is a ``ClangTool`` that reads all SB API header files and their functions and outputs the client/server interfaces and certain other pieces of code, such as RPC-specfic versions of Python bindings used for the test suite. There's 3 main components behind ``lldb-rpc-gen``: + +1. The ``lldb-rpc-gen`` tool itself, which contains the main driver that uses the ``ClangTool``. +2. The code that generates all interfaces, which we call "emitters". All generated code for the interfaces are in C++, so the server side has one emitter for its generated source code and another for its generated header code. The client side has the same. +3. All common code shared between all emitters, such as helper methods and information about exceptions to take when emitting. + +The `current PR`_ up for upstreaming LLDB RPC upstreams a subset of the code used for the tool. It upstreams the ``lldb-rpc-gen`` tool and all code needed for the server side emitters. Here's an example of what ``lldb-rpc-gen`` will output for the server side interface: + +Input +----- + +We'll use ``SBDebugger::CreateTarget(const char *filename)`` as an example. ``lldb-rpc-gen`` will read this method from ``SBDebugger.h``. The output is as follows. + +Source Code Output +------------------ + +:: + + bool rpc_server::_ZN4lldb10SBDebugger12CreateTargetEPKc::HandleRPCCall(rpc_common::Connection &connection, RPCStream &send, RPCStream &response) { + // 1) Make local storage for incoming function arguments + lldb::SBDebugger *this_ptr = nullptr; + rpc_common::ConstCharPointer filename; + // 2) Decode all function arguments + this_ptr = RPCServerObjectDecoder(send, rpc_common::RPCPacket::ValueType::Argument); + if (!this_ptr) + return false; + if (!RPCValueDecoder(send, rpc_common::RPCPacket::ValueType::Argument, filename)) + return false; + // 3) Call the method and encode the return value + lldb::SBTarget && __result = this_ptr->CreateTarget(filename.c_str()); + RPCServerObjectEncoder(response, rpc_common::RPCPacket::ValueType::ReturnValue, std::move(__result)); + return true; + } + +Function signature +~~~~~~~~~~~~~~~~~~ + +All server-side source code functions have a function signature that take the format ``bool rpc_server::::HandleRPCCall(rpc_common::Connection &connection, RPCStream &send, RPCStream &response)``. Here the ``connection`` is what's maintained between the client and server. The ``send`` variable is a byte stream that carries information sent from the client. ``response`` is also a byte stream that will be populated with the return value obtained from the call into the SB API function that will be sent back to the client. + +Local variable storage +~~~~~~~~~~~~~~~~~~~~~~ + +First, variables are created to hold all arguments coming in from the client side. These variables will be a pointer for the SB API class in question, and corresponding variables for all parameters that the function has. Since this signature for ``SBDebugger::CreateTarget()`` only has one parameter, a ``const char *``, 2 local variables get created. A pointer for an ``SBDebugger`` object, and an ``RPCCommon::ConstCharPointer`` for the ``const char * filename`` parameter. The ``ConstCharPointer`` is a typedef over ``const char *`` in the main RPC core code. + +Incoming stream decoding +~~~~~~~~~~~~~~~~~~~~~~~~ + +Following this, ``RPCServerObjectDecoder`` is used to decode the ``send`` byte stream. In this case, we're decoding this stream into the ``SBDebugger`` pointer we created earlier. We then decode the ``send`` stream again to obtain the ``const char * filename`` sent by the client. Each decoded argument from the client is checked for validity and the function will exit early if any are invalid. + +SB API function call +~~~~~~~~~~~~~~~~~~~~ + +Once all arguments have been decoded, the underlying SB API function called with the decoded arguments. ``RPCServerObjectEncoder`` is then used to encode the return value from the SB API call into the ``response`` stream, and this is then sent back to the client. + +Header Code Output +------------------ +:: + + class _ZN4lldb10SBDebugger12CreateTargetEPKc : public rpc_common::RPCFunctionInstance { + public: + _ZN4lldb10SBDebugger12CreateTargetEPKc() : RPCFunctionInstance("_ZN4lldb10SBDebugger12CreateTargetEPKc") {} + ~_ZN4lldb10SBDebugger12CreateTargetEPKc() override {} + bool HandleRPCCall(rpc_common::Connection &connection, rpc_common::RPCStream &send, rpc_common::RPCStream &response) override; + }; + +Class definition and ``HandleRPCCall`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +ALL RPC server side functions are subclasses of ``RPCFunctionInstance``. Each class will then define their ``HandleRPCCall`` function that is seen in the source code above. This subclassing and ``HandleRPCCall`` definition is what is emitted in the header code for server. + +The ``lldb-rpc-gen`` emitters +***************************** + +The bulk of the code is generated using the emitters. For the server side, we have ``RPCServerSourceEmitter`` and ``RPCServerHeaderEmitter``. The former handles generation of the source code and the latter handles generation of the header code seen above. + +Emitters largely have similar structure. Constituent sections of code, such as function headers, function bodies and others are typically given their own method. As an example, the function to emit a function header is ``EmitFunctionHeader()`` and the function to emit a function body is ``EmitFunctionBody()``. Information that will be written to the output file is written using ``EmitLine()``, which uses ``llvm::raw_string_ostreams`` and is defined in ``RPCCommon.h``. + +Since this is a ``ClangTool``, information about each method is obtained from Clang itself and stored in the ``Method`` struct located in ``RPCCommon.h`` in ``lldb-rpc-gen``'s directory. ``Method`` is used for simplicity and abstraction, and other information that would be needed from the SB API is obtained from Clang directly. ``````````
https://github.com/llvm/llvm-project/pull/138612 From lldb-commits at lists.llvm.org Mon May 5 17:27:51 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Mon, 05 May 2025 17:27:51 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Don't error out when the process is in eStateUnloaded (PR #138601) In-Reply-To: Message-ID: <68195787.050a0220.1f904d.209f@mx.google.com> https://github.com/ashgti approved this pull request. https://github.com/llvm/llvm-project/pull/138601 From lldb-commits at lists.llvm.org Mon May 5 17:29:44 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Mon, 05 May 2025 17:29:44 -0700 (PDT) Subject: [Lldb-commits] [lldb] 1c1238d - [lldb-dap] Don't error out when the process is in eStateUnloaded (#138601) Message-ID: <681957f8.170a0220.1dfb43.cc34@mx.google.com> Author: Jonas Devlieghere Date: 2025-05-05T17:29:40-07:00 New Revision: 1c1238d3615a7e1a99570d1e02de3b538d2e0669 URL: https://github.com/llvm/llvm-project/commit/1c1238d3615a7e1a99570d1e02de3b538d2e0669 DIFF: https://github.com/llvm/llvm-project/commit/1c1238d3615a7e1a99570d1e02de3b538d2e0669.diff LOG: [lldb-dap] Don't error out when the process is in eStateUnloaded (#138601) DAP::WaitForProcessToStop treats the process in eStateUnloaded as an error. The process is in this state when it has just been created (before an attach or launch) or when it's restarted. Neither should be treated as errors. The current implementation can trigger this error (and a corresponding test failure) when we call WaitForProcessToStop after attaching in asynchronous mode (for example when using ConnectRemote which is always asynchronous (due to a bug). Added: Modified: lldb/tools/lldb-dap/DAP.cpp Removed: ################################################################################ diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 4cb0d8e49004c..4b631484c9fab 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -968,6 +968,7 @@ lldb::SBError DAP::WaitForProcessToStop(std::chrono::seconds seconds) { while (std::chrono::steady_clock::now() < timeout_time) { const auto state = process.GetState(); switch (state) { + case lldb::eStateUnloaded: case lldb::eStateAttaching: case lldb::eStateConnected: case lldb::eStateInvalid: @@ -982,9 +983,6 @@ lldb::SBError DAP::WaitForProcessToStop(std::chrono::seconds seconds) { case lldb::eStateExited: error.SetErrorString("process exited during launch or attach"); return error; - case lldb::eStateUnloaded: - error.SetErrorString("process unloaded during launch or attach"); - return error; case lldb::eStateCrashed: case lldb::eStateStopped: return lldb::SBError(); // Success! From lldb-commits at lists.llvm.org Mon May 5 17:29:46 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Mon, 05 May 2025 17:29:46 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Don't error out when the process is in eStateUnloaded (PR #138601) In-Reply-To: Message-ID: <681957fa.630a0220.37da94.c701@mx.google.com> https://github.com/JDevlieghere closed https://github.com/llvm/llvm-project/pull/138601 From lldb-commits at lists.llvm.org Mon May 5 17:31:25 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Mon, 05 May 2025 17:31:25 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Change the launch sequence (PR #138219) In-Reply-To: Message-ID: <6819585d.170a0220.15ed3b.bcf7@mx.google.com> https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/138219 >From b7391070942219a691a691822477e2a7616be1ab Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Fri, 2 May 2025 09:59:01 -0700 Subject: [PATCH] [lldb-dap] Change the launch sequence This PR changes how we treat the launch sequence in lldb-dap. - Send the initialized event after we finish handling the initialize request, rather than after we finish attaching or launching. - Delay handling the launch and attach request until we have handled the configurationDone request. The latter is now largely a NO-OP and only exists to signal lldb-dap that it can handle the launch and attach requests. - Delay handling the initial threads requests until we have handled the launch or attach request. - Make all attaching and launching asynchronous, including when we have attach or launch commands. That removes the need to synchronize between the request and event thread. Background: https://discourse.llvm.org/t/reliability-of-the-lldb-dap-tests/86125 --- .../test/tools/lldb-dap/dap_server.py | 65 +++++----- .../test/tools/lldb-dap/lldbdap_testcase.py | 7 ++ .../tools/lldb-dap/attach/TestDAP_attach.py | 3 +- .../attach/TestDAP_attachByPortNum.py | 8 +- .../TestDAP_breakpointEvents.py | 60 +++------ .../breakpoint/TestDAP_breakpointLocations.py | 3 +- .../breakpoint/TestDAP_setBreakpoints.py | 3 +- .../lldb-dap/commands/TestDAP_commands.py | 2 - .../completions/TestDAP_completions.py | 6 +- .../tools/lldb-dap/console/TestDAP_console.py | 2 +- .../disassemble/TestDAP_disassemble.py | 3 +- .../lldb-dap/disconnect/TestDAP_disconnect.py | 6 +- .../lldb-dap/evaluate/TestDAP_evaluate.py | 3 +- .../tools/lldb-dap/launch/TestDAP_launch.py | 4 +- .../tools/lldb-dap/memory/TestDAP_memory.py | 3 +- .../lldb-dap/progress/TestDAP_Progress.py | 2 +- .../repl-mode/TestDAP_repl_mode_detection.py | 2 +- .../tools/lldb-dap/restart/TestDAP_restart.py | 1 - .../restart/TestDAP_restart_runInTerminal.py | 1 - .../lldb-dap/stop-hooks/TestDAP_stop_hooks.py | 2 +- .../lldb-dap/variables/TestDAP_variables.py | 3 +- lldb/tools/lldb-dap/DAP.cpp | 39 ++++-- lldb/tools/lldb-dap/DAP.h | 8 +- lldb/tools/lldb-dap/EventHelper.cpp | 2 +- .../lldb-dap/Handler/AttachRequestHandler.cpp | 115 ++++++++++-------- .../ConfigurationDoneRequestHandler.cpp | 14 +-- .../Handler/InitializeRequestHandler.cpp | 44 +++---- .../lldb-dap/Handler/LaunchRequestHandler.cpp | 7 +- .../tools/lldb-dap/Handler/RequestHandler.cpp | 67 ++++++---- lldb/tools/lldb-dap/Handler/RequestHandler.h | 1 + 30 files changed, 252 insertions(+), 234 deletions(-) diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py index 6d9ab770684f1..e10342b72f4f0 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py @@ -132,7 +132,6 @@ def __init__(self, recv, send, init_commands, log_file=None): self.exit_status = None self.initialize_body = None self.thread_stop_reasons = {} - self.breakpoint_events = [] self.progress_events = [] self.reverse_requests = [] self.module_events = [] @@ -244,13 +243,6 @@ def handle_recv_packet(self, packet): self._process_stopped() tid = body["threadId"] self.thread_stop_reasons[tid] = body - elif event == "breakpoint": - # Breakpoint events come in when a breakpoint has locations - # added or removed. Keep track of them so we can look for them - # in tests. - self.breakpoint_events.append(packet) - # no need to add 'breakpoint' event packets to our packets list - return keepGoing elif event.startswith("progress"): # Progress events come in as 'progressStart', 'progressUpdate', # and 'progressEnd' events. Keep these around in case test @@ -412,6 +404,15 @@ def wait_for_stopped(self, timeout=None): self.threads = [] return stopped_events + def wait_for_breakpoint_events(self, timeout=None): + breakpoint_events = [] + while True: + event = self.wait_for_event("breakpoint", timeout=timeout) + if not event: + break + breakpoint_events.append(event) + return breakpoint_events + def wait_for_exited(self): event_dict = self.wait_for_event("exited") if event_dict is None: @@ -591,6 +592,7 @@ def request_attach( attachCommands=None, terminateCommands=None, coreFile=None, + stopOnAttach=True, postRunCommands=None, sourceMap=None, gdbRemotePort=None, @@ -620,6 +622,8 @@ def request_attach( args_dict["attachCommands"] = attachCommands if coreFile: args_dict["coreFile"] = coreFile + if stopOnAttach: + args_dict["stopOnEntry"] = stopOnAttach if postRunCommands: args_dict["postRunCommands"] = postRunCommands if sourceMap: @@ -632,7 +636,7 @@ def request_attach( response = self.send_recv(command_dict) if response["success"]: - self.wait_for_events(["process", "initialized"]) + self.wait_for_event("process") return response def request_breakpointLocations( @@ -666,10 +670,6 @@ def request_configurationDone(self): response = self.send_recv(command_dict) if response: self.configuration_done_sent = True - # Client requests the baseline of currently existing threads after - # a successful launch or attach. - # Kick off the threads request that follows - self.request_threads() return response def _process_stopped(self): @@ -887,7 +887,7 @@ def request_launch( response = self.send_recv(command_dict) if response["success"]: - self.wait_for_events(["process", "initialized"]) + self.wait_for_event("process") return response def request_next(self, threadId, granularity="statement"): @@ -1325,6 +1325,26 @@ def attach_options_specified(options): def run_vscode(dbg, args, options): dbg.request_initialize(options.sourceInitFile) + + if options.sourceBreakpoints: + source_to_lines = {} + for file_line in options.sourceBreakpoints: + (path, line) = file_line.split(":") + if len(path) == 0 or len(line) == 0: + print('error: invalid source with line "%s"' % (file_line)) + + else: + if path in source_to_lines: + source_to_lines[path].append(int(line)) + else: + source_to_lines[path] = [int(line)] + for source in source_to_lines: + dbg.request_setBreakpoints(source, source_to_lines[source]) + if options.funcBreakpoints: + dbg.request_setFunctionBreakpoints(options.funcBreakpoints) + + dbg.request_configurationDone() + if attach_options_specified(options): response = dbg.request_attach( program=options.program, @@ -1353,23 +1373,6 @@ def run_vscode(dbg, args, options): ) if response["success"]: - if options.sourceBreakpoints: - source_to_lines = {} - for file_line in options.sourceBreakpoints: - (path, line) = file_line.split(":") - if len(path) == 0 or len(line) == 0: - print('error: invalid source with line "%s"' % (file_line)) - - else: - if path in source_to_lines: - source_to_lines[path].append(int(line)) - else: - source_to_lines[path] = [int(line)] - for source in source_to_lines: - dbg.request_setBreakpoints(source, source_to_lines[source]) - if options.funcBreakpoints: - dbg.request_setFunctionBreakpoints(options.funcBreakpoints) - dbg.request_configurationDone() dbg.wait_for_stopped() else: if "message" in response: diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py index 2c14bb35162b5..958c7268c0c72 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py @@ -340,6 +340,7 @@ def attach( exitCommands=None, attachCommands=None, coreFile=None, + stopOnAttach=True, disconnectAutomatically=True, terminateCommands=None, postRunCommands=None, @@ -364,6 +365,8 @@ def cleanup(): self.addTearDownHook(cleanup) # Initialize and launch the program self.dap_server.request_initialize(sourceInitFile) + self.dap_server.wait_for_event("initialized") + self.dap_server.request_configurationDone() response = self.dap_server.request_attach( program=program, pid=pid, @@ -376,6 +379,7 @@ def cleanup(): attachCommands=attachCommands, terminateCommands=terminateCommands, coreFile=coreFile, + stopOnAttach=stopOnAttach, postRunCommands=postRunCommands, sourceMap=sourceMap, gdbRemotePort=gdbRemotePort, @@ -434,6 +438,9 @@ def cleanup(): # Initialize and launch the program self.dap_server.request_initialize(sourceInitFile) + self.dap_server.wait_for_event("initialized") + self.dap_server.request_configurationDone() + response = self.dap_server.request_launch( program, args=args, diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py index f48d5a7db3c50..b5569642f9d34 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py @@ -24,9 +24,10 @@ def spawn_and_wait(program, delay): process.wait() - at skip class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase): def set_and_hit_breakpoint(self, continueToExit=True): + self.dap_server.wait_for_stopped() + source = "main.c" breakpoint1_line = line_number(source, "// breakpoint 1") lines = [breakpoint1_line] diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py index 7f93b9f2a3a22..7250e67ebcd8c 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py @@ -18,17 +18,17 @@ import socket - at skip class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase): default_timeout = 20 def set_and_hit_breakpoint(self, continueToExit=True): + self.dap_server.wait_for_stopped() + source = "main.c" - main_source_path = os.path.join(os.getcwd(), source) - breakpoint1_line = line_number(main_source_path, "// breakpoint 1") + breakpoint1_line = line_number(source, "// breakpoint 1") lines = [breakpoint1_line] # Set breakpoint in the thread function so we can step the threads - breakpoint_ids = self.set_source_breakpoints(main_source_path, lines) + breakpoint_ids = self.set_source_breakpoints(source, lines) self.assertEqual( len(breakpoint_ids), len(lines), "expect correct number of breakpoints" ) diff --git a/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py b/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py index e5590e1b332a0..cdfd3317303e9 100644 --- a/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py +++ b/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py @@ -81,52 +81,24 @@ def test_breakpoint_events(self): breakpoint["verified"], "expect foo breakpoint to not be verified" ) - # Get the stop at the entry point - self.continue_to_next_stop() - - # We are now stopped at the entry point to the program. Shared - # libraries are not loaded yet (at least on macOS they aren't) and only - # the breakpoint in the main executable should be resolved. - self.assertEqual(len(self.dap_server.breakpoint_events), 1) - event = self.dap_server.breakpoint_events[0] - body = event["body"] - self.assertEqual( - body["reason"], "changed", "breakpoint event should say changed" - ) - breakpoint = body["breakpoint"] - self.assertEqual(breakpoint["id"], main_bp_id) - self.assertTrue(breakpoint["verified"], "main breakpoint should be resolved") - - # Clear the list of breakpoint events so we don't see this one again. - self.dap_server.breakpoint_events.clear() + # Flush the breakpoint events. + self.dap_server.wait_for_breakpoint_events(timeout=5) # Continue to the breakpoint self.continue_to_breakpoints(dap_breakpoint_ids) - # When the process launches, we first expect to see both the main and - # foo breakpoint as unresolved. - for event in self.dap_server.breakpoint_events[:2]: - body = event["body"] - self.assertEqual( - body["reason"], "changed", "breakpoint event should say changed" - ) - breakpoint = body["breakpoint"] - self.assertIn(str(breakpoint["id"]), dap_breakpoint_ids) - self.assertFalse(breakpoint["verified"], "breakpoint should be unresolved") + verified_breakpoint_ids = [] + unverified_breakpoint_ids = [] + for breakpoint_event in self.dap_server.wait_for_breakpoint_events(timeout=5): + breakpoint = breakpoint_event["body"]["breakpoint"] + id = breakpoint["id"] + if breakpoint["verified"]: + verified_breakpoint_ids.append(id) + else: + unverified_breakpoint_ids.append(id) - # Then, once the dynamic loader has given us a load address, they - # should show up as resolved again. - for event in self.dap_server.breakpoint_events[3:]: - body = event["body"] - self.assertEqual( - body["reason"], "changed", "breakpoint event should say changed" - ) - breakpoint = body["breakpoint"] - self.assertIn(str(breakpoint["id"]), dap_breakpoint_ids) - self.assertTrue(breakpoint["verified"], "breakpoint should be resolved") - self.assertNotIn( - "source", - breakpoint, - "breakpoint event should not return a source object", - ) - self.assertIn("line", breakpoint, "breakpoint event should have line") + self.assertIn(main_bp_id, unverified_breakpoint_ids) + self.assertIn(foo_bp_id, unverified_breakpoint_ids) + + self.assertIn(main_bp_id, verified_breakpoint_ids) + self.assertIn(foo_bp_id, verified_breakpoint_ids) diff --git a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_breakpointLocations.py b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_breakpointLocations.py index 4a99cacc761a3..1058157e2c668 100644 --- a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_breakpointLocations.py +++ b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_breakpointLocations.py @@ -11,8 +11,7 @@ import lldbdap_testcase import os -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip + class TestDAP_breakpointLocations(lldbdap_testcase.DAPTestCaseBase): def setUp(self): lldbdap_testcase.DAPTestCaseBase.setUp(self) diff --git a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py index 6c6681804f250..26df2573555df 100644 --- a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py +++ b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py @@ -11,8 +11,7 @@ import lldbdap_testcase import os -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip + class TestDAP_setBreakpoints(lldbdap_testcase.DAPTestCaseBase): def setUp(self): lldbdap_testcase.DAPTestCaseBase.setUp(self) diff --git a/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py b/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py index 4aecf9a665c06..223258fbdd3dc 100644 --- a/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py +++ b/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py @@ -6,8 +6,6 @@ from lldbsuite.test.decorators import * -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip class TestDAP_commands(lldbdap_testcase.DAPTestCaseBase): def test_command_directive_quiet_on_success(self): program = self.getBuildArtifact("a.out") diff --git a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py index 210e591bff426..455ac84168baf 100644 --- a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py +++ b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py @@ -44,9 +44,9 @@ def verify_completions(self, actual_list, expected_list, not_expected_list=[]): self.assertNotIn(not_expected_item, actual_list) - def setup_debugee(self): + def setup_debugee(self, stopOnEntry=False): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=stopOnEntry) source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") @@ -235,7 +235,7 @@ def test_auto_completions(self): """ Tests completion requests in "repl-mode=auto" """ - self.setup_debugee() + self.setup_debugee(stopOnEntry=True) res = self.dap_server.request_evaluate( "`lldb-dap repl-mode auto", context="repl" diff --git a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py index b07c4f871d73b..65a1bc04c7cd7 100644 --- a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py +++ b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py @@ -167,7 +167,7 @@ def test_exit_status_message_ok(self): def test_diagnositcs(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) core = self.getBuildArtifact("minidump.core") self.yaml2obj("minidump.yaml", core) diff --git a/lldb/test/API/tools/lldb-dap/disassemble/TestDAP_disassemble.py b/lldb/test/API/tools/lldb-dap/disassemble/TestDAP_disassemble.py index ebecb349ac177..9e8ef5b289f2e 100644 --- a/lldb/test/API/tools/lldb-dap/disassemble/TestDAP_disassemble.py +++ b/lldb/test/API/tools/lldb-dap/disassemble/TestDAP_disassemble.py @@ -10,8 +10,7 @@ import lldbdap_testcase import os -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip + class TestDAP_disassemble(lldbdap_testcase.DAPTestCaseBase): @skipIfWindows def test_disassemble(self): diff --git a/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py b/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py index 0cb792d662a80..09e3f62f0eead 100644 --- a/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py +++ b/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py @@ -31,7 +31,7 @@ def test_launch(self): created. """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program, disconnectAutomatically=False) + self.build_and_launch(program, stopOnEntry=True, disconnectAutomatically=False) # We set a breakpoint right before the side effect file is created self.set_source_breakpoints( @@ -39,7 +39,11 @@ def test_launch(self): ) self.continue_to_next_stop() + # verify we haven't produced the side effect file yet + self.assertFalse(os.path.exists(program + ".side_effect")) + self.dap_server.request_disconnect() + # verify we didn't produce the side effect file time.sleep(1) self.assertFalse(os.path.exists(program + ".side_effect")) diff --git a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py index d97fda730c46a..e2f843bd337a6 100644 --- a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py +++ b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py @@ -10,8 +10,7 @@ from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip + class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase): def assertEvaluate(self, expression, regex): self.assertRegex( diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py index 931456299e03e..604a41678500c 100644 --- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py +++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py @@ -88,8 +88,8 @@ def test_stopOnEntry(self): """ program = self.getBuildArtifact("a.out") self.build_and_launch(program, stopOnEntry=True) - self.set_function_breakpoints(["main"]) - stopped_events = self.continue_to_next_stop() + + stopped_events = self.dap_server.wait_for_stopped() for stopped_event in stopped_events: if "body" in stopped_event: body = stopped_event["body"] diff --git a/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py b/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py index c71ba871b8a22..ea43fccf016a7 100644 --- a/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py +++ b/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py @@ -10,8 +10,7 @@ import lldbdap_testcase import os -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip + class TestDAP_memory(lldbdap_testcase.DAPTestCaseBase): def test_memory_refs_variables(self): """ diff --git a/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py b/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py index fee63655de0da..0f94b50c31fba 100755 --- a/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py +++ b/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py @@ -50,7 +50,7 @@ def verify_progress_events( @skipIfWindows def test(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) progress_emitter = os.path.join(os.getcwd(), "Progress_emitter.py") self.dap_server.request_evaluate( f"`command script import {progress_emitter}", context="repl" diff --git a/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py b/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py index c6f59949d668e..81edcdf4bd0f9 100644 --- a/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py +++ b/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py @@ -20,7 +20,7 @@ def assertEvaluate(self, expression, regex): def test_completions(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") diff --git a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py index 36fa0bd40183f..5f95c7bfb1556 100644 --- a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py +++ b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py @@ -22,7 +22,6 @@ def test_basic_functionality(self): [bp_A, bp_B] = self.set_source_breakpoints("main.c", [line_A, line_B]) # Verify we hit A, then B. - self.dap_server.request_configurationDone() self.verify_breakpoint_hit([bp_A]) self.dap_server.request_continue() self.verify_breakpoint_hit([bp_B]) diff --git a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py index a94c9860c1508..eed769a5a0cc6 100644 --- a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py +++ b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py @@ -74,7 +74,6 @@ def test_stopOnEntry(self): program = self.getBuildArtifact("a.out") self.build_and_launch(program, runInTerminal=True, stopOnEntry=True) [bp_main] = self.set_function_breakpoints(["main"]) - self.dap_server.request_configurationDone() # When using stopOnEntry, configurationDone doesn't result in a running # process, we should immediately get a stopped event instead. diff --git a/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py b/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py index 70c11a63a79f7..7e28a5af4331c 100644 --- a/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py +++ b/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py @@ -19,7 +19,7 @@ def test_stop_hooks_before_run(self): self.build_and_launch(program, stopOnEntry=True, preRunCommands=preRunCommands) # The first stop is on entry. - self.continue_to_next_stop() + self.dap_server.wait_for_stopped() breakpoint_ids = self.set_function_breakpoints(["main"]) # This request hangs if the race happens, because, in that case, the diff --git a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py index 901c260d7d413..286bf3390a440 100644 --- a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py +++ b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py @@ -17,8 +17,7 @@ def make_buffer_verify_dict(start_idx, count, offset=0): verify_dict["[%i]" % (i)] = {"type": "int", "value": str(i + offset)} return verify_dict -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip + class TestDAP_variables(lldbdap_testcase.DAPTestCaseBase): def verify_values(self, verify_dict, actual, varref_dict=None, expression=None): if "equals" in verify_dict: diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 4b631484c9fab..62c60cc3a9b3b 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -84,8 +84,8 @@ DAP::DAP(Log *log, const ReplMode default_repl_mode, : log(log), transport(transport), broadcaster("lldb-dap"), exception_breakpoints(), focus_tid(LLDB_INVALID_THREAD_ID), stop_at_entry(false), is_attach(false), - restarting_process_id(LLDB_INVALID_PROCESS_ID), - configuration_done_sent(false), waiting_for_run_in_terminal(false), + restarting_process_id(LLDB_INVALID_PROCESS_ID), configuration_done(false), + waiting_for_run_in_terminal(false), progress_event_reporter( [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }), reverse_request_seq(0), repl_mode(default_repl_mode) { @@ -893,10 +893,19 @@ llvm::Error DAP::Loop() { return errWrapper; } + // The launch sequence is special and we need to carefully handle + // packets in the right order. Until we've handled configurationDone, + bool add_to_pending_queue = false; + if (const protocol::Request *req = - std::get_if(&*next); - req && req->command == "disconnect") { - disconnecting = true; + std::get_if(&*next)) { + llvm::StringRef command = req->command; + if (command == "disconnect") + disconnecting = true; + if (!configuration_done) + add_to_pending_queue = + command != "initialize" && command != "configurationDone" && + command != "disconnect" && !command.ends_with("Breakpoints"); } const std::optional cancel_args = @@ -924,7 +933,8 @@ llvm::Error DAP::Loop() { { std::lock_guard guard(m_queue_mutex); - m_queue.push_back(std::move(*next)); + auto &queue = add_to_pending_queue ? m_pending_queue : m_queue; + queue.push_back(std::move(*next)); } m_queue_cv.notify_one(); } @@ -938,16 +948,19 @@ llvm::Error DAP::Loop() { StopEventHandlers(); }); - while (!disconnecting || !m_queue.empty()) { + while (true) { std::unique_lock lock(m_queue_mutex); m_queue_cv.wait(lock, [&] { return disconnecting || !m_queue.empty(); }); - if (m_queue.empty()) + if (disconnecting && m_queue.empty()) break; Message next = m_queue.front(); m_queue.pop_front(); + // Unlock while we're processing the event. + lock.unlock(); + if (!HandleObject(next)) return llvm::createStringError(llvm::inconvertibleErrorCode(), "unhandled packet"); @@ -1219,6 +1232,16 @@ void DAP::SetConfiguration(const protocol::Configuration &config, SetThreadFormat(*configuration.customThreadFormat); } +void DAP::SetConfigurationDone() { + { + std::lock_guard guard(m_queue_mutex); + std::copy(m_pending_queue.begin(), m_pending_queue.end(), + std::front_inserter(m_queue)); + configuration_done = true; + } + m_queue_cv.notify_all(); +} + void DAP::SetFrameFormat(llvm::StringRef format) { if (format.empty()) return; diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index 88eedb0860cf1..b581ae759b1bc 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -188,7 +188,7 @@ struct DAP { // shutting down the entire adapter. When we're restarting, we keep the id of // the old process here so we can detect this case and keep running. lldb::pid_t restarting_process_id; - bool configuration_done_sent; + bool configuration_done; llvm::StringMap> request_handlers; bool waiting_for_run_in_terminal; ProgressEventReporter progress_event_reporter; @@ -251,6 +251,8 @@ struct DAP { /// Configures the debug adapter for launching/attaching. void SetConfiguration(const protocol::Configuration &confing, bool is_attach); + void SetConfigurationDone(); + /// Configure source maps based on the current `DAPConfiguration`. void ConfigureSourceMaps(); @@ -417,8 +419,10 @@ struct DAP { lldb::SBMutex GetAPIMutex() const { return target.GetAPIMutex(); } private: - std::mutex m_queue_mutex; + /// Queue for all incoming messages. std::deque m_queue; + std::deque m_pending_queue; + std::mutex m_queue_mutex; std::condition_variable m_queue_cv; std::mutex m_cancelled_requests_mutex; diff --git a/lldb/tools/lldb-dap/EventHelper.cpp b/lldb/tools/lldb-dap/EventHelper.cpp index 2c659f39f4b66..ed2d8700c26b0 100644 --- a/lldb/tools/lldb-dap/EventHelper.cpp +++ b/lldb/tools/lldb-dap/EventHelper.cpp @@ -222,7 +222,7 @@ void SendContinuedEvent(DAP &dap) { // If the focus thread is not set then we haven't reported any thread status // to the client, so nothing to report. - if (!dap.configuration_done_sent || dap.focus_tid == LLDB_INVALID_THREAD_ID) { + if (!dap.configuration_done || dap.focus_tid == LLDB_INVALID_THREAD_ID) { return; } diff --git a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp index 7a0f091128e4a..5dc9c3f9772e3 100644 --- a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp @@ -133,61 +133,70 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { dap.SendOutput(OutputType::Console, llvm::StringRef(attach_msg, attach_msg_len)); } - if (attachCommands.empty()) { - // No "attachCommands", just attach normally. - // Disable async events so the attach will be successful when we return from - // the launch call and the launch will happen synchronously + { + // Perform the launch in synchronous mode so that we don't have to worry + // about process state changes during the launch. ScopeSyncMode scope_sync_mode(dap.debugger); - - if (core_file.empty()) { - if ((pid != LLDB_INVALID_PROCESS_ID) && - (gdb_remote_port != invalid_port)) { - // If both pid and port numbers are specified. - error.SetErrorString("The user can't specify both pid and port"); - } else if (gdb_remote_port != invalid_port) { - // If port is specified and pid is not. - lldb::SBListener listener = dap.debugger.GetListener(); - - // If the user hasn't provided the hostname property, default localhost - // being used. - std::string connect_url = - llvm::formatv("connect://{0}:", gdb_remote_hostname); - connect_url += std::to_string(gdb_remote_port); - dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", - error); + if (attachCommands.empty()) { + // No "attachCommands", just attach normally. + if (core_file.empty()) { + if ((pid != LLDB_INVALID_PROCESS_ID) && + (gdb_remote_port != invalid_port)) { + // If both pid and port numbers are specified. + error.SetErrorString("The user can't specify both pid and port"); + } else if (gdb_remote_port != invalid_port) { + // If port is specified and pid is not. + lldb::SBListener listener = dap.debugger.GetListener(); + + // If the user hasn't provided the hostname property, default + // localhost being used. + std::string connect_url = + llvm::formatv("connect://{0}:", gdb_remote_hostname); + connect_url += std::to_string(gdb_remote_port); + dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", + error); + } else { + // Attach by pid or process name. + lldb::SBAttachInfo attach_info; + if (pid != LLDB_INVALID_PROCESS_ID) + attach_info.SetProcessID(pid); + else if (dap.configuration.program.has_value()) + attach_info.SetExecutable(dap.configuration.program->data()); + attach_info.SetWaitForLaunch(wait_for, false /*async*/); + dap.target.Attach(attach_info, error); + } } else { - // Attach by pid or process name. - lldb::SBAttachInfo attach_info; - if (pid != LLDB_INVALID_PROCESS_ID) - attach_info.SetProcessID(pid); - else if (dap.configuration.program.has_value()) - attach_info.SetExecutable(dap.configuration.program->data()); - attach_info.SetWaitForLaunch(wait_for, false /*async*/); - dap.target.Attach(attach_info, error); + dap.target.LoadCore(core_file.data(), error); } } else { - dap.target.LoadCore(core_file.data(), error); - } - } else { - // We have "attachCommands" that are a set of commands that are expected - // to execute the commands after which a process should be created. If there - // is no valid process after running these commands, we have failed. - if (llvm::Error err = dap.RunAttachCommands(attachCommands)) { - response["success"] = false; - EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; + // We have "attachCommands" that are a set of commands that are expected + // to execute the commands after which a process should be created. If + // there is no valid process after running these commands, we have failed. + if (llvm::Error err = dap.RunAttachCommands(attachCommands)) { + response["success"] = false; + EmplaceSafeString(response, "message", llvm::toString(std::move(err))); + dap.SendJSON(llvm::json::Value(std::move(response))); + return; + } + // The custom commands might have created a new target so we should use + // the selected target after these commands are run. + dap.target = dap.debugger.GetSelectedTarget(); } - // The custom commands might have created a new target so we should use the - // selected target after these commands are run. - dap.target = dap.debugger.GetSelectedTarget(); - - // Make sure the process is attached and stopped before proceeding as the - // the launch commands are not run using the synchronous mode. - error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds)); } + // Make sure the process is attached and stopped. + error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds)); + + // Clients can request a baseline of currently existing threads after + // we acknowledge the configurationDone request. + // Client requests the baseline of currently existing threads after + // a successful or attach by sending a 'threads' request + // right after receiving the configurationDone response. + // Obtain the list of threads before we resume the process + dap.initial_thread_list = + GetThreads(dap.target.GetProcess(), dap.thread_format); + if (error.Success() && core_file.empty()) { auto attached_pid = dap.target.GetProcess().GetProcessID(); if (attached_pid == LLDB_INVALID_PROCESS_ID) { @@ -206,9 +215,17 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { } dap.SendJSON(llvm::json::Value(std::move(response))); + + // FIXME: Move this into PostRun. if (error.Success()) { - SendProcessEvent(dap, Attach); - dap.SendJSON(CreateEventObject("initialized")); + if (dap.target.GetProcess().IsValid()) { + SendProcessEvent(dap, Attach); + + if (dap.stop_at_entry) + SendThreadStoppedEvent(dap); + else + dap.target.GetProcess().Continue(); + } } } diff --git a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp index f39bbdefdbb95..802c28d7b8904 100644 --- a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp @@ -47,21 +47,11 @@ namespace lldb_dap { void ConfigurationDoneRequestHandler::operator()( const llvm::json::Object &request) const { + dap.SetConfigurationDone(); + llvm::json::Object response; FillResponse(request, response); dap.SendJSON(llvm::json::Value(std::move(response))); - dap.configuration_done_sent = true; - if (dap.stop_at_entry) - SendThreadStoppedEvent(dap); - else { - // Client requests the baseline of currently existing threads after - // a successful launch or attach by sending a 'threads' request - // right after receiving the configurationDone response. - // Obtain the list of threads before we resume the process - dap.initial_thread_list = - GetThreads(dap.target.GetProcess(), dap.thread_format); - dap.target.GetProcess().Continue(); - } } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp index ce34c52bcc334..aa947d3cb5ab9 100644 --- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp @@ -140,43 +140,28 @@ static void EventThreadFunction(DAP &dap) { lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event); if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) { auto state = lldb::SBProcess::GetStateFromEvent(event); + + DAP_LOG(dap.log, "State = {0}", state); switch (state) { + case lldb::eStateConnected: + case lldb::eStateDetached: case lldb::eStateInvalid: - // Not a state event - break; case lldb::eStateUnloaded: break; - case lldb::eStateConnected: - break; case lldb::eStateAttaching: - break; - case lldb::eStateLaunching: - break; - case lldb::eStateStepping: - break; case lldb::eStateCrashed: - break; - case lldb::eStateDetached: - break; - case lldb::eStateSuspended: - break; + case lldb::eStateLaunching: case lldb::eStateStopped: - // We launch and attach in synchronous mode then the first stop - // event will not be delivered. If we use "launchCommands" during a - // launch or "attachCommands" during an attach we might some process - // stop events which we do not want to send an event for. We will - // manually send a stopped event in request_configurationDone(...) - // so don't send any before then. - if (dap.configuration_done_sent) { - // Only report a stopped event if the process was not - // automatically restarted. - if (!lldb::SBProcess::GetRestartedFromEvent(event)) { - SendStdOutStdErr(dap, process); - SendThreadStoppedEvent(dap); - } + case lldb::eStateSuspended: + // Only report a stopped event if the process was not + // automatically restarted. + if (!lldb::SBProcess::GetRestartedFromEvent(event)) { + SendStdOutStdErr(dap, process); + SendThreadStoppedEvent(dap); } break; case lldb::eStateRunning: + case lldb::eStateStepping: dap.WillContinue(); SendContinuedEvent(dap); break; @@ -284,6 +269,7 @@ llvm::Expected InitializeRequestHandler::Run( // Do not source init files until in/out/err are configured. dap.debugger = lldb::SBDebugger::Create(false); dap.debugger.SetInputFile(dap.in); + dap.target = dap.debugger.GetDummyTarget(); llvm::Expected out_fd = dap.out.GetWriteFileDescriptor(); if (!out_fd) @@ -338,4 +324,8 @@ llvm::Expected InitializeRequestHandler::Run( return dap.GetCapabilities(); } +void InitializeRequestHandler::PostRun() const { + dap.SendJSON(CreateEventObject("initialized")); +} + } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp index 3e4532e754ec6..7e0e76935dd02 100644 --- a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp @@ -71,9 +71,12 @@ void LaunchRequestHandler::PostRun() const { if (dap.target.GetProcess().IsValid()) { // Attach happens when launching with runInTerminal. SendProcessEvent(dap, dap.is_attach ? Attach : Launch); - } - dap.SendJSON(CreateEventObject("initialized")); + if (dap.stop_at_entry) + SendThreadStoppedEvent(dap); + else + dap.target.GetProcess().Continue(); + } } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp index 7a75cd93abc19..282c5f4ab15a5 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp @@ -8,6 +8,7 @@ #include "Handler/RequestHandler.h" #include "DAP.h" +#include "EventHelper.h" #include "Handler/ResponseHandler.h" #include "JSONUtils.h" #include "LLDBUtils.h" @@ -162,7 +163,7 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) { dap.target.GetProcess().Continue(); // Now that the actual target is just starting (i.e. exec was just invoked), - // we return the debugger to its async state. + // we return the debugger to its sync state. scope_sync_mode.reset(); // If sending the notification failed, the launcher should be dead by now and @@ -238,35 +239,47 @@ llvm::Error BaseRequestHandler::LaunchProcess( launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug | lldb::eLaunchFlagStopAtEntry); - if (arguments.runInTerminal) { - if (llvm::Error err = RunInTerminal(dap, arguments)) - return err; - } else if (launchCommands.empty()) { - lldb::SBError error; - // Disable async events so the launch will be successful when we return from - // the launch call and the launch will happen synchronously + { + // Perform the launch in synchronous mode so that we don't have to worry + // about process state changes during the launch. ScopeSyncMode scope_sync_mode(dap.debugger); - dap.target.Launch(launch_info, error); - if (error.Fail()) - return llvm::make_error(error.GetCString()); - } else { - // Set the launch info so that run commands can access the configured - // launch details. - dap.target.SetLaunchInfo(launch_info); - if (llvm::Error err = dap.RunLaunchCommands(launchCommands)) - return err; - - // The custom commands might have created a new target so we should use the - // selected target after these commands are run. - dap.target = dap.debugger.GetSelectedTarget(); - // Make sure the process is launched and stopped at the entry point before - // proceeding as the launch commands are not run using the synchronous - // mode. - lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout); - if (error.Fail()) - return llvm::make_error(error.GetCString()); + + if (arguments.runInTerminal) { + if (llvm::Error err = RunInTerminal(dap, arguments)) + return err; + } else if (launchCommands.empty()) { + lldb::SBError error; + dap.target.Launch(launch_info, error); + if (error.Fail()) + return llvm::make_error(error.GetCString()); + } else { + // Set the launch info so that run commands can access the configured + // launch details. + dap.target.SetLaunchInfo(launch_info); + if (llvm::Error err = dap.RunLaunchCommands(launchCommands)) + return err; + + // The custom commands might have created a new target so we should use + // the selected target after these commands are run. + dap.target = dap.debugger.GetSelectedTarget(); + } } + // Make sure the process is launched and stopped at the entry point before + // proceeding. + lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout); + if (error.Fail()) + return llvm::make_error(error.GetCString()); + + // Clients can request a baseline of currently existing threads after + // we acknowledge the configurationDone request. + // Client requests the baseline of currently existing threads after + // a successful or attach by sending a 'threads' request + // right after receiving the configurationDone response. + // Obtain the list of threads before we resume the process + dap.initial_thread_list = + GetThreads(dap.target.GetProcess(), dap.thread_format); + return llvm::Error::success(); } diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index fa3d76ed4a125..dec35c5dce1e5 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.h +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h @@ -282,6 +282,7 @@ class InitializeRequestHandler static llvm::StringLiteral GetCommand() { return "initialize"; } llvm::Expected Run(const protocol::InitializeRequestArguments &args) const override; + void PostRun() const override; }; class LaunchRequestHandler From lldb-commits at lists.llvm.org Mon May 5 21:45:26 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Mon, 05 May 2025 21:45:26 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Fix GetIndexOfChildMemberWithName to handle anonymous structs. (PR #138487) In-Reply-To: Message-ID: <681993e6.170a0220.19e1a2.e429@mx.google.com> cmtice wrote: > I'm not intimately familiar with this code, but I am somewhat suspicious of implementation. > > Adding the number of base classes to the result makes sense to me. What surprises me is that this should be done only for fields with anonymous types. Can you explain why should the index of a field depend on whether its type is anonymous or not? I knew that I needed to add it for anonymous structs; I wasn't sure about other cases. I've tested it more now, and it seems good to add it in all cases, so I will do that. > > I am also surprised by the hardcoding of `omit_empty_base_classes` to true in the `GetNumBaseClasses` call. All of the other calls in this function pass the value from the argument. Why should this one be special? I knew that in the case of anonymous structs we did not want to count empty base classes; since this line was for anonymous structs, I hard-coded it to make sure. Now that I've updated the code to always add the number of base classes, I'm removing that hard-coding. https://github.com/llvm/llvm-project/pull/138487 From lldb-commits at lists.llvm.org Mon May 5 21:56:54 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Mon, 05 May 2025 21:56:54 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Fix GetIndexOfChildMemberWithName to handle anonymous structs. (PR #138487) In-Reply-To: Message-ID: <68199696.050a0220.24945.421a@mx.google.com> https://github.com/cmtice updated https://github.com/llvm/llvm-project/pull/138487 >From 780371cf111c97e2bce5f030625ff557bb2f40b6 Mon Sep 17 00:00:00 2001 From: Caroline Tice Date: Sun, 4 May 2025 23:43:28 -0700 Subject: [PATCH 1/2] [LLDB] Fix GetIndexOfChildMemberWithName to handle anonymous structs. When handling anonymous structs, GetIndexOfChildMemberWithName needs to add the number of non-empty base classes to the child index, to get the actual correct index. It was not doing so. This fixes that. --- .../TypeSystem/Clang/TypeSystemClang.cpp | 9 ++++- .../lang/cpp/type_lookup_anon_struct/Makefile | 3 ++ .../TestCppTypeLookupAnonStruct.py | 27 +++++++++++++++ .../lang/cpp/type_lookup_anon_struct/main.cpp | 33 +++++++++++++++++++ 4 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 lldb/test/API/lang/cpp/type_lookup_anon_struct/Makefile create mode 100644 lldb/test/API/lang/cpp/type_lookup_anon_struct/TestCppTypeLookupAnonStruct.py create mode 100644 lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 1a2b3d4133e51..dd8d144510056 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -6743,7 +6743,14 @@ size_t TypeSystemClang::GetIndexOfChildMemberWithName( if (field_name.empty()) { CompilerType field_type = GetType(field->getType()); std::vector save_indices = child_indexes; - child_indexes.push_back(child_idx); + if (field_type.IsAnonymousType()) + // We have to add on the number of non-empty base classes to this + // index! + child_indexes.push_back( + child_idx + + TypeSystemClang::GetNumBaseClasses(cxx_record_decl, true)); + else + child_indexes.push_back(child_idx); if (field_type.GetIndexOfChildMemberWithName( name, omit_empty_base_classes, child_indexes)) return child_indexes.size(); diff --git a/lldb/test/API/lang/cpp/type_lookup_anon_struct/Makefile b/lldb/test/API/lang/cpp/type_lookup_anon_struct/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/lang/cpp/type_lookup_anon_struct/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/lang/cpp/type_lookup_anon_struct/TestCppTypeLookupAnonStruct.py b/lldb/test/API/lang/cpp/type_lookup_anon_struct/TestCppTypeLookupAnonStruct.py new file mode 100644 index 0000000000000..2295ecfe81ff7 --- /dev/null +++ b/lldb/test/API/lang/cpp/type_lookup_anon_struct/TestCppTypeLookupAnonStruct.py @@ -0,0 +1,27 @@ +""" +Test that we properly print multiple types. +""" + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +from lldbsuite.test import decorators + + +class TestTypeLookupAnonStruct(TestBase): + def test_lookup_anon_struct(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, '// Set breakpoint here', lldb.SBFileSpec('main.cpp') + ) + + self.expect_var_path('unnamed_derived.y', value='2') + self.expect_var_path('unnamed_derived.z', value='13') + self.expect('frame variable "derb.x"', error=True, + substrs=['"x" is not a member of "(DerivedB) derb"']) + self.expect('frame variable "derb.y"', error=True, + substrs=['"y" is not a member of "(DerivedB) derb"']) + self.expect_var_path('derb.w', value='14') + self.expect_var_path('derb.k', value='15') + self.expect_var_path('derb.a.x', value='1') + self.expect_var_path('derb.a.y', value='2') diff --git a/lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp b/lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp new file mode 100644 index 0000000000000..18dd997ea9563 --- /dev/null +++ b/lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp @@ -0,0 +1,33 @@ +int main(int argc, char** argv) { + struct A { + struct { + int x = 1; + }; + int y = 2; + } a; + + struct B { + // Anonymous struct inherits another struct. + struct : public A { + int z = 3; + }; + int w = 4; + A a; + } b; + + struct : public A { + struct { + int z = 13; + }; + } unnamed_derived; + + struct DerivedB : public B { + struct { + // `w` in anonymous struct overrides `w` from `B`. + int w = 14; + int k = 15; + }; + } derb; + + return 0; // Set breakpoint here +} >From 81870ff46edc647695720d5635e700e77c7ca620 Mon Sep 17 00:00:00 2001 From: Caroline Tice Date: Mon, 5 May 2025 21:45:50 -0700 Subject: [PATCH 2/2] Update to always add the number of base classes (not just for anonymous structs). Remove hard-codimg for omitting empty base classes. --- .../Plugins/TypeSystem/Clang/TypeSystemClang.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index dd8d144510056..71d4385447624 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -6743,14 +6743,11 @@ size_t TypeSystemClang::GetIndexOfChildMemberWithName( if (field_name.empty()) { CompilerType field_type = GetType(field->getType()); std::vector save_indices = child_indexes; - if (field_type.IsAnonymousType()) - // We have to add on the number of non-empty base classes to this - // index! - child_indexes.push_back( - child_idx + - TypeSystemClang::GetNumBaseClasses(cxx_record_decl, true)); - else - child_indexes.push_back(child_idx); + // We have to add on the number of non-empty base classes to this + // index! + child_indexes.push_back( + child_idx + TypeSystemClang::GetNumBaseClasses( + cxx_record_decl, omit_empty_base_classes)); if (field_type.GetIndexOfChildMemberWithName( name, omit_empty_base_classes, child_indexes)) return child_indexes.size(); From lldb-commits at lists.llvm.org Mon May 5 22:49:03 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Mon, 05 May 2025 22:49:03 -0700 (PDT) Subject: [Lldb-commits] [lldb] Extending LLDB to work on AIX (PR #102601) In-Reply-To: Message-ID: <6819a2cf.170a0220.249b01.0d01@mx.google.com> https://github.com/ravindra-shinde2 updated https://github.com/llvm/llvm-project/pull/102601 >From 39d395f75c306a0d932a783eef039fd93d66e246 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Wed, 7 Aug 2024 12:10:43 -0500 Subject: [PATCH 01/46] LLDB Support for AIX --- clang/lib/CodeGen/CGObjCMac.cpp | 6 +- lldb/CMakeLists.txt | 4 + lldb/cmake/modules/LLDBConfig.cmake | 2 +- lldb/include/lldb/Core/Module.h | 3 + lldb/include/lldb/Core/ModuleSpec.h | 23 +- lldb/include/lldb/Host/HostGetOpt.h | 2 +- lldb/include/lldb/Host/HostInfo.h | 3 + lldb/include/lldb/Host/HostInfoBase.h | 2 +- lldb/include/lldb/Host/XML.h | 5 + lldb/include/lldb/Host/aix/AbstractSocket.h | 25 + lldb/include/lldb/Host/aix/Host.h | 22 + lldb/include/lldb/Host/aix/HostInfoAIX.h | 42 + lldb/include/lldb/Host/aix/Ptrace.h | 62 + lldb/include/lldb/Host/aix/Support.h | 29 + lldb/include/lldb/Host/aix/Uio.h | 23 + lldb/include/lldb/Host/common/GetOptInc.h | 6 +- lldb/include/lldb/Symbol/ObjectFile.h | 5 + lldb/include/lldb/Target/ABI.h | 6 + lldb/include/lldb/Target/DynamicLoader.h | 6 + lldb/include/lldb/Target/Process.h | 14 + .../lldb/Target/RegisterContextUnwind.h | 4 + .../lldb/Target/ThreadPlanCallFunction.h | 6 + .../lldb/Utility/StringExtractorGDBRemote.h | 1 + lldb/include/lldb/lldb-private-enumerations.h | 1 + lldb/source/API/CMakeLists.txt | 108 + lldb/source/API/SBBreakpoint.cpp | 6 +- lldb/source/API/SBBreakpointLocation.cpp | 6 +- lldb/source/API/SBBreakpointName.cpp | 4 +- lldb/source/Core/DynamicLoader.cpp | 10 + lldb/source/Core/Mangled.cpp | 2 + lldb/source/Core/Module.cpp | 12 + lldb/source/Core/Section.cpp | 4 + lldb/source/Expression/DWARFExpression.cpp | 10 +- lldb/source/Host/CMakeLists.txt | 13 + lldb/source/Host/aix/AbstractSocket.cpp | 21 + lldb/source/Host/aix/Host.cpp | 304 +++ lldb/source/Host/aix/HostInfoAIX.cpp | 215 ++ lldb/source/Host/aix/Support.cpp | 44 + lldb/source/Host/common/GetOptInc.cpp | 2 +- lldb/source/Host/common/Host.cpp | 180 +- .../source/Host/common/LICENSE.aix-netbsd.txt | 125 + lldb/source/Host/common/XML.cpp | 3 + .../posix/ConnectionFileDescriptorPosix.cpp | 2 + lldb/source/Host/posix/FileSystemPosix.cpp | 2 + lldb/source/Host/posix/MainLoopPosix.cpp | 17 + .../Host/posix/ProcessLauncherPosixFork.cpp | 5 + lldb/source/Initialization/CMakeLists.txt | 2 +- .../SystemInitializerCommon.cpp | 4 +- .../Plugins/ABI/PowerPC/ABISysV_ppc64.cpp | 131 +- .../Plugins/ABI/PowerPC/ABISysV_ppc64.h | 6 + .../DynamicLoader/AIX-DYLD/CMakeLists.txt | 11 + .../AIX-DYLD/DynamicLoaderAIXDYLD.cpp | 272 +++ .../AIX-DYLD/DynamicLoaderAIXDYLD.h | 55 + .../Plugins/DynamicLoader/CMakeLists.txt | 1 + .../DynamicLoaderDarwinKernel.cpp | 4 +- .../MacOSX-DYLD/DynamicLoaderDarwin.cpp | 2 +- .../PPC64/EmulateInstructionPPC64.cpp | 196 +- .../PPC64/EmulateInstructionPPC64.h | 14 + ...nstrumentationRuntimeMainThreadChecker.cpp | 2 +- .../TSan/InstrumentationRuntimeTSan.cpp | 14 +- .../UBSan/InstrumentationRuntimeUBSan.cpp | 2 +- .../Plugins/JITLoader/GDB/JITLoaderGDB.cpp | 4 + lldb/source/Plugins/Language/ObjC/Cocoa.cpp | 2 + .../MemoryHistory/asan/MemoryHistoryASan.cpp | 2 +- .../BSD-Archive/ObjectContainerBSDArchive.cpp | 2 +- .../Big-Archive/CMakeLists.txt | 10 + .../Big-Archive/ObjectContainerBigArchive.cpp | 522 +++++ .../Big-Archive/ObjectContainerBigArchive.h | 177 ++ .../Plugins/ObjectContainer/CMakeLists.txt | 1 + lldb/source/Plugins/ObjectFile/CMakeLists.txt | 1 + .../ObjectFile/Mach-O/ObjectFileMachO.cpp | 6 +- .../Minidump/ObjectFileMinidump.cpp | 2 + .../Plugins/ObjectFile/PDB/ObjectFilePDB.cpp | 15 +- .../ObjectFile/PECOFF/ObjectFilePECOFF.cpp | 18 +- .../Plugins/ObjectFile/XCOFF/CMakeLists.txt | 13 + .../ObjectFile/XCOFF/ObjectFileXCOFF.cpp | 780 +++++++ .../ObjectFile/XCOFF/ObjectFileXCOFF.h | 243 ++ .../Python/OperatingSystemPython.cpp | 2 +- .../Plugins/Platform/AIX/CMakeLists.txt | 13 + .../Plugins/Platform/AIX/PlatformAIX.cpp | 471 ++++ .../source/Plugins/Platform/AIX/PlatformAIX.h | 74 + lldb/source/Plugins/Platform/CMakeLists.txt | 1 + .../source/Plugins/Process/AIX/CMakeLists.txt | 19 + .../Plugins/Process/AIX/NativeProcessAIX.cpp | 2048 +++++++++++++++++ .../Plugins/Process/AIX/NativeProcessAIX.h | 283 +++ .../Process/AIX/NativeRegisterContextAIX.cpp | 157 ++ .../Process/AIX/NativeRegisterContextAIX.h | 133 ++ .../AIX/NativeRegisterContextAIX_ppc64.cpp | 744 ++++++ .../AIX/NativeRegisterContextAIX_ppc64.h | 138 ++ .../Plugins/Process/AIX/NativeThreadAIX.cpp | 526 +++++ .../Plugins/Process/AIX/NativeThreadAIX.h | 126 + lldb/source/Plugins/Process/CMakeLists.txt | 3 + .../Process/Utility/InferiorCallPOSIX.cpp | 33 + .../Utility/RegisterInfoPOSIX_ppc64le.cpp | 4 + .../Plugins/Process/Utility/ThreadMemory.cpp | 2 +- .../Plugins/Process/gdb-remote/CMakeLists.txt | 5 + .../GDBRemoteCommunicationClient.cpp | 30 + .../gdb-remote/GDBRemoteCommunicationClient.h | 7 + .../GDBRemoteCommunicationServerLLGS.cpp | 28 + .../GDBRemoteCommunicationServerLLGS.h | 2 + .../Process/gdb-remote/ProcessGDBRemote.cpp | 13 +- .../Process/gdb-remote/ProcessGDBRemote.h | 8 + .../Process/mach-core/ProcessMachCore.cpp | 8 +- .../ScriptInterpreter/Python/CMakeLists.txt | 5 + .../SymbolFile/DWARF/DWARFFormValue.cpp | 4 + .../Plugins/SymbolFile/DWARF/DWARFUnit.cpp | 12 +- .../MacOSX/AppleGetThreadItemInfoHandler.cpp | 2 +- lldb/source/Symbol/DWARFCallFrameInfo.cpp | 4 +- lldb/source/Target/ABI.cpp | 9 + lldb/source/Target/CMakeLists.txt | 5 + lldb/source/Target/Process.cpp | 10 + lldb/source/Target/RegisterContextUnwind.cpp | 46 + lldb/source/Target/ThreadPlanCallFunction.cpp | 34 + lldb/source/Target/UnwindLLDB.cpp | 15 + lldb/source/Utility/ArchSpec.cpp | 18 +- .../Utility/StringExtractorGDBRemote.cpp | 2 + lldb/test/CMakeLists.txt | 2 +- lldb/test/Shell/Expr/TestIRMemoryMap.test | 2 +- lldb/test/Shell/Process/TestEnvironment.test | 2 +- lldb/tools/driver/CMakeLists.txt | 5 + lldb/tools/driver/Driver.cpp | 5 +- lldb/tools/lldb-dap/CMakeLists.txt | 4 + lldb/tools/lldb-server/CMakeLists.txt | 7 + .../lldb-server/SystemInitializerLLGS.cpp | 15 + lldb/tools/lldb-server/lldb-gdbserver.cpp | 4 + lldb/unittests/Host/FileSystemTest.cpp | 2 +- lldb/unittests/Host/posix/TerminalTest.cpp | 4 + llvm/include/llvm/Object/XCOFFObjectFile.h | 4 +- llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp | 15 +- 129 files changed, 8950 insertions(+), 76 deletions(-) create mode 100644 lldb/include/lldb/Host/aix/AbstractSocket.h create mode 100644 lldb/include/lldb/Host/aix/Host.h create mode 100644 lldb/include/lldb/Host/aix/HostInfoAIX.h create mode 100644 lldb/include/lldb/Host/aix/Ptrace.h create mode 100644 lldb/include/lldb/Host/aix/Support.h create mode 100644 lldb/include/lldb/Host/aix/Uio.h create mode 100644 lldb/source/Host/aix/AbstractSocket.cpp create mode 100644 lldb/source/Host/aix/Host.cpp create mode 100644 lldb/source/Host/aix/HostInfoAIX.cpp create mode 100644 lldb/source/Host/aix/Support.cpp create mode 100644 lldb/source/Host/common/LICENSE.aix-netbsd.txt create mode 100644 lldb/source/Plugins/DynamicLoader/AIX-DYLD/CMakeLists.txt create mode 100644 lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp create mode 100644 lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h create mode 100644 lldb/source/Plugins/ObjectContainer/Big-Archive/CMakeLists.txt create mode 100644 lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.cpp create mode 100644 lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.h create mode 100644 lldb/source/Plugins/ObjectFile/XCOFF/CMakeLists.txt create mode 100644 lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp create mode 100644 lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.h create mode 100644 lldb/source/Plugins/Platform/AIX/CMakeLists.txt create mode 100644 lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp create mode 100644 lldb/source/Plugins/Platform/AIX/PlatformAIX.h create mode 100644 lldb/source/Plugins/Process/AIX/CMakeLists.txt create mode 100644 lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp create mode 100644 lldb/source/Plugins/Process/AIX/NativeProcessAIX.h create mode 100644 lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.cpp create mode 100644 lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.h create mode 100644 lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.cpp create mode 100644 lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.h create mode 100644 lldb/source/Plugins/Process/AIX/NativeThreadAIX.cpp create mode 100644 lldb/source/Plugins/Process/AIX/NativeThreadAIX.h diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index 30f3911a8b03c..fc91981db68c1 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -5052,10 +5052,14 @@ std::string CGObjCCommonMac::GetSectionName(StringRef Section, case llvm::Triple::COFF: assert(Section.starts_with("__") && "expected the name to begin with __"); return ("." + Section.substr(2) + "$B").str(); + case llvm::Triple::XCOFF: + // Hack to allow "p 10+1" on AIX for lldb + assert(Section.substr(0, 2) == "__" && + "expected the name to begin with __"); + return Section.substr(2).str(); case llvm::Triple::Wasm: case llvm::Triple::GOFF: case llvm::Triple::SPIRV: - case llvm::Triple::XCOFF: case llvm::Triple::DXContainer: llvm::report_fatal_error( "Objective-C support is unimplemented for object file format"); diff --git a/lldb/CMakeLists.txt b/lldb/CMakeLists.txt index 59cdc4593463c..2e9ae0d0b3221 100644 --- a/lldb/CMakeLists.txt +++ b/lldb/CMakeLists.txt @@ -38,6 +38,10 @@ endif() include(LLDBConfig) include(AddLLDB) +if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX") + add_definitions("-D__AIX__") +endif() + # Define the LLDB_CONFIGURATION_xxx matching the build type. if(uppercase_CMAKE_BUILD_TYPE STREQUAL "DEBUG" ) add_definitions(-DLLDB_CONFIGURATION_DEBUG) diff --git a/lldb/cmake/modules/LLDBConfig.cmake b/lldb/cmake/modules/LLDBConfig.cmake index a60921990cf77..a0f118a11984c 100644 --- a/lldb/cmake/modules/LLDBConfig.cmake +++ b/lldb/cmake/modules/LLDBConfig.cmake @@ -299,7 +299,7 @@ endif() # Figure out if lldb could use lldb-server. If so, then we'll # ensure we build lldb-server when an lldb target is being built. -if (CMAKE_SYSTEM_NAME MATCHES "Android|Darwin|FreeBSD|Linux|NetBSD|Windows") +if (CMAKE_SYSTEM_NAME MATCHES "Android|Darwin|FreeBSD|Linux|NetBSD|Windows|AIX") set(LLDB_CAN_USE_LLDB_SERVER ON) else() set(LLDB_CAN_USE_LLDB_SERVER OFF) diff --git a/lldb/include/lldb/Core/Module.h b/lldb/include/lldb/Core/Module.h index 5589c1c9a350d..3829386562795 100644 --- a/lldb/include/lldb/Core/Module.h +++ b/lldb/include/lldb/Core/Module.h @@ -196,6 +196,9 @@ class Module : public std::enable_shared_from_this, bool SetLoadAddress(Target &target, lldb::addr_t value, bool value_is_offset, bool &changed); + bool SetLoadAddressByType(Target &target, lldb::addr_t value, + bool value_is_offset, bool &changed, int type_id); + /// \copydoc SymbolContextScope::CalculateSymbolContext(SymbolContext*) /// /// \see SymbolContextScope diff --git a/lldb/include/lldb/Core/ModuleSpec.h b/lldb/include/lldb/Core/ModuleSpec.h index 4cbbbfa8a26e1..4fe06412b6b0b 100644 --- a/lldb/include/lldb/Core/ModuleSpec.h +++ b/lldb/include/lldb/Core/ModuleSpec.h @@ -21,6 +21,7 @@ #include #include +#include namespace lldb_private { @@ -41,8 +42,26 @@ class ModuleSpec { } ModuleSpec(const FileSpec &file_spec, const ArchSpec &arch) - : m_file(file_spec), m_arch(arch), m_object_offset(0), - m_object_size(FileSystem::Instance().GetByteSize(file_spec)) {} + : m_arch(arch), m_object_offset(0) { + // parse object inside module format for example: /usr/ccs/lib/libc.a(shr_64.o) + llvm::SmallString<256> path_with_object; + file_spec.GetPath(path_with_object); + if (strstr(path_with_object.c_str(), "(") != nullptr) { + char *part; + char *str = (char *)path_with_object.c_str(); + part = strtok(str, "()"); + assert(part); + llvm::StringRef file_name(part); + part = strtok(nullptr, "()"); + assert(part); + m_object_name = ConstString(part); + m_file = FileSpec(file_name); + m_object_size = FileSystem::Instance().GetByteSize(m_file); + } else { + m_file = file_spec; + m_object_size = FileSystem::Instance().GetByteSize(file_spec); + } + } FileSpec *GetFileSpecPtr() { return (m_file ? &m_file : nullptr); } diff --git a/lldb/include/lldb/Host/HostGetOpt.h b/lldb/include/lldb/Host/HostGetOpt.h index 52cfdf4dbb89c..f450e561d6afb 100644 --- a/lldb/include/lldb/Host/HostGetOpt.h +++ b/lldb/include/lldb/Host/HostGetOpt.h @@ -9,7 +9,7 @@ #ifndef LLDB_HOST_HOSTGETOPT_H #define LLDB_HOST_HOSTGETOPT_H -#if !defined(_MSC_VER) && !defined(__NetBSD__) +#if !defined(_MSC_VER) && !defined(__NetBSD__) && !defined(__AIX__) #include #include diff --git a/lldb/include/lldb/Host/HostInfo.h b/lldb/include/lldb/Host/HostInfo.h index b7010d69d88e7..156df8cf6901d 100644 --- a/lldb/include/lldb/Host/HostInfo.h +++ b/lldb/include/lldb/Host/HostInfo.h @@ -55,6 +55,9 @@ #elif defined(__APPLE__) #include "lldb/Host/macosx/HostInfoMacOSX.h" #define HOST_INFO_TYPE HostInfoMacOSX +#elif defined(__AIX__) +#include "lldb/Host/aix/HostInfoAIX.h" +#define HOST_INFO_TYPE HostInfoAIX #else #include "lldb/Host/posix/HostInfoPosix.h" #define HOST_INFO_TYPE HostInfoPosix diff --git a/lldb/include/lldb/Host/HostInfoBase.h b/lldb/include/lldb/Host/HostInfoBase.h index 705aad559f3b7..29e6acf39bfb2 100644 --- a/lldb/include/lldb/Host/HostInfoBase.h +++ b/lldb/include/lldb/Host/HostInfoBase.h @@ -149,6 +149,7 @@ class HostInfoBase { return {}; } + static bool ComputeSharedLibraryDirectory(FileSpec &file_spec); /// Returns the distribution id of the host /// /// This will be something like "ubuntu", "fedora", etc. on Linux. @@ -158,7 +159,6 @@ class HostInfoBase { static llvm::StringRef GetDistributionId() { return llvm::StringRef(); } protected: - static bool ComputeSharedLibraryDirectory(FileSpec &file_spec); static bool ComputeSupportExeDirectory(FileSpec &file_spec); static bool ComputeProcessTempFileDirectory(FileSpec &file_spec); static bool ComputeGlobalTempFileDirectory(FileSpec &file_spec); diff --git a/lldb/include/lldb/Host/XML.h b/lldb/include/lldb/Host/XML.h index da0f9cd7aa8c0..cf359f7726d5d 100644 --- a/lldb/include/lldb/Host/XML.h +++ b/lldb/include/lldb/Host/XML.h @@ -11,6 +11,11 @@ #include "lldb/Host/Config.h" +#if defined(__AIX__) +//FIXME for AIX +#undef LLDB_ENABLE_LIBXML2 +#endif + #if LLDB_ENABLE_LIBXML2 #include #endif diff --git a/lldb/include/lldb/Host/aix/AbstractSocket.h b/lldb/include/lldb/Host/aix/AbstractSocket.h new file mode 100644 index 0000000000000..78a567a6b9095 --- /dev/null +++ b/lldb/include/lldb/Host/aix/AbstractSocket.h @@ -0,0 +1,25 @@ +//===-- AbstractSocket.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AbstractSocket_h_ +#define liblldb_AbstractSocket_h_ + +#include "lldb/Host/posix/DomainSocket.h" + +namespace lldb_private { +class AbstractSocket : public DomainSocket { +public: + AbstractSocket(bool child_processes_inherit); + +protected: + size_t GetNameOffset() const override; + void DeleteSocketFile(llvm::StringRef name) override; +}; +} + +#endif // ifndef liblldb_AbstractSocket_h_ diff --git a/lldb/include/lldb/Host/aix/Host.h b/lldb/include/lldb/Host/aix/Host.h new file mode 100644 index 0000000000000..1e3487752995f --- /dev/null +++ b/lldb/include/lldb/Host/aix/Host.h @@ -0,0 +1,22 @@ +//===-- Host.h --------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_HOST_AIX_HOST_H +#define LLDB_HOST_AIX_HOST_H + +#include "lldb/lldb-types.h" +#include + +namespace lldb_private { + +// Get PID (i.e. the primary thread ID) corresponding to the specified TID. +std::optional getPIDForTID(lldb::pid_t tid); + +} // namespace lldb_private + +#endif // #ifndef LLDB_HOST_AIX_HOST_H diff --git a/lldb/include/lldb/Host/aix/HostInfoAIX.h b/lldb/include/lldb/Host/aix/HostInfoAIX.h new file mode 100644 index 0000000000000..ced4cf34d38a8 --- /dev/null +++ b/lldb/include/lldb/Host/aix/HostInfoAIX.h @@ -0,0 +1,42 @@ +//===-- HostInfoAIX.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_Host_aix_HostInfoAIX_h_ +#define lldb_Host_aix_HostInfoAIX_h_ + +#include "lldb/Host/posix/HostInfoPosix.h" +#include "lldb/Utility/FileSpec.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/VersionTuple.h" + +#include + +namespace lldb_private { + +class HostInfoAIX : public HostInfoPosix { + friend class HostInfoBase; + +public: + static void Initialize(SharedLibraryDirectoryHelper *helper = nullptr); + static void Terminate(); + + static llvm::VersionTuple GetOSVersion(); + static std::optional GetOSBuildString(); + static llvm::StringRef GetDistributionId(); + static FileSpec GetProgramFileSpec(); + +protected: + static bool ComputeSupportExeDirectory(FileSpec &file_spec); + static bool ComputeSystemPluginsDirectory(FileSpec &file_spec); + static bool ComputeUserPluginsDirectory(FileSpec &file_spec); + static void ComputeHostArchitectureSupport(ArchSpec &arch_32, + ArchSpec &arch_64); +}; +} + +#endif diff --git a/lldb/include/lldb/Host/aix/Ptrace.h b/lldb/include/lldb/Host/aix/Ptrace.h new file mode 100644 index 0000000000000..88928f18102d7 --- /dev/null +++ b/lldb/include/lldb/Host/aix/Ptrace.h @@ -0,0 +1,62 @@ +//===-- Ptrace.h ------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// This file defines ptrace functions & structures + +#ifndef liblldb_Host_aix_Ptrace_h_ +#define liblldb_Host_aix_Ptrace_h_ + +#include + +#define DEBUG_PTRACE_MAXBYTES 20 + +// Support ptrace extensions even when compiled without required kernel support +#ifndef PTRACE_GETREGS +#define PTRACE_GETREGS (PT_COMMAND_MAX+1) +#endif +#ifndef PTRACE_SETREGS +#define PTRACE_SETREGS (PT_COMMAND_MAX+2) +#endif +#ifndef PTRACE_GETFPREGS +#define PTRACE_GETFPREGS (PT_COMMAND_MAX+3) +#endif +#ifndef PTRACE_SETFPREGS +#define PTRACE_SETFPREGS (PT_COMMAND_MAX+4) +#endif +#ifndef PTRACE_GETREGSET +#define PTRACE_GETREGSET 0x4204 +#endif +#ifndef PTRACE_SETREGSET +#define PTRACE_SETREGSET 0x4205 +#endif +#ifndef PTRACE_GET_THREAD_AREA +#define PTRACE_GET_THREAD_AREA (PT_COMMAND_MAX+5) +#endif +#ifndef PTRACE_ARCH_PRCTL +#define PTRACE_ARCH_PRCTL (PT_COMMAND_MAX+6) +#endif +#ifndef ARCH_GET_FS +#define ARCH_SET_GS 0x1001 +#define ARCH_SET_FS 0x1002 +#define ARCH_GET_FS 0x1003 +#define ARCH_GET_GS 0x1004 +#endif +#ifndef PTRACE_PEEKMTETAGS +#define PTRACE_PEEKMTETAGS (PT_COMMAND_MAX+7) +#endif +#ifndef PTRACE_POKEMTETAGS +#define PTRACE_POKEMTETAGS (PT_COMMAND_MAX+8) +#endif +#ifndef PTRACE_GETVRREGS +#define PTRACE_GETVRREGS (PT_COMMAND_MAX+9) +#endif +#ifndef PTRACE_GETVSRREGS +#define PTRACE_GETVSRREGS (PT_COMMAND_MAX+10) +#endif + +#endif // liblldb_Host_aix_Ptrace_h_ diff --git a/lldb/include/lldb/Host/aix/Support.h b/lldb/include/lldb/Host/aix/Support.h new file mode 100644 index 0000000000000..27d6c2b50a35b --- /dev/null +++ b/lldb/include/lldb/Host/aix/Support.h @@ -0,0 +1,29 @@ +//===-- Support.h -----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_HOST_AIX_SUPPORT_H +#define LLDB_HOST_AIX_SUPPORT_H + +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/MemoryBuffer.h" +#include + +namespace lldb_private { + +llvm::ErrorOr> +getProcFile(::pid_t pid, ::pid_t tid, const llvm::Twine &file); + +llvm::ErrorOr> +getProcFile(::pid_t pid, const llvm::Twine &file); + +llvm::ErrorOr> +getProcFile(const llvm::Twine &file); + +} // namespace lldb_private + +#endif // #ifndef LLDB_HOST_AIX_SUPPORT_H diff --git a/lldb/include/lldb/Host/aix/Uio.h b/lldb/include/lldb/Host/aix/Uio.h new file mode 100644 index 0000000000000..acf79ecc6a1d0 --- /dev/null +++ b/lldb/include/lldb/Host/aix/Uio.h @@ -0,0 +1,23 @@ +//===-- Uio.h ---------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Host_aix_Uio_h_ +#define liblldb_Host_aix_Uio_h_ + +#include "lldb/Host/Config.h" +#include + +// We shall provide our own implementation of process_vm_readv if it is not +// present +#if !HAVE_PROCESS_VM_READV +ssize_t process_vm_readv(::pid_t pid, const struct iovec *local_iov, + unsigned long liovcnt, const struct iovec *remote_iov, + unsigned long riovcnt, unsigned long flags); +#endif + +#endif // liblldb_Host_aix_Uio_h_ diff --git a/lldb/include/lldb/Host/common/GetOptInc.h b/lldb/include/lldb/Host/common/GetOptInc.h index 3fb9add479541..ebb475bfaf6b8 100644 --- a/lldb/include/lldb/Host/common/GetOptInc.h +++ b/lldb/include/lldb/Host/common/GetOptInc.h @@ -11,11 +11,11 @@ #include "lldb/lldb-defines.h" -#if defined(_MSC_VER) +#if defined(_MSC_VER) || defined(__AIX__) #define REPLACE_GETOPT #define REPLACE_GETOPT_LONG #endif -#if defined(_MSC_VER) || defined(__NetBSD__) +#if defined(_MSC_VER) || defined(__NetBSD__) || defined(__AIX__) #define REPLACE_GETOPT_LONG_ONLY #endif @@ -35,7 +35,7 @@ struct option { int val; }; -int getopt(int argc, char *const argv[], const char *optstring); +int getopt(int argc, char *const argv[], const char *optstring) throw(); // from getopt.h extern char *optarg; diff --git a/lldb/include/lldb/Symbol/ObjectFile.h b/lldb/include/lldb/Symbol/ObjectFile.h index 8592323322e38..bf66ccec263d2 100644 --- a/lldb/include/lldb/Symbol/ObjectFile.h +++ b/lldb/include/lldb/Symbol/ObjectFile.h @@ -401,6 +401,11 @@ class ObjectFile : public std::enable_shared_from_this, return false; } + virtual bool SetLoadAddressByType(Target &target, lldb::addr_t value, + bool value_is_offset, int type_id) { + return false; + } + /// Gets whether endian swapping should occur when extracting data from this /// object file. /// diff --git a/lldb/include/lldb/Target/ABI.h b/lldb/include/lldb/Target/ABI.h index 7b646d743346b..281a89951ef88 100644 --- a/lldb/include/lldb/Target/ABI.h +++ b/lldb/include/lldb/Target/ABI.h @@ -47,6 +47,12 @@ class ABI : public PluginInterface { lldb::addr_t returnAddress, llvm::ArrayRef args) const = 0; + virtual bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t tocAddress, + lldb::addr_t returnAddress, + llvm::ArrayRef args) const; + // Prepare trivial call used from ThreadPlanFunctionCallUsingABI // AD: // . Because i don't want to change other ABI's this is not declared pure diff --git a/lldb/include/lldb/Target/DynamicLoader.h b/lldb/include/lldb/Target/DynamicLoader.h index 0629e2faae7e9..7dccd317c2dca 100644 --- a/lldb/include/lldb/Target/DynamicLoader.h +++ b/lldb/include/lldb/Target/DynamicLoader.h @@ -359,6 +359,12 @@ class DynamicLoader : public PluginInterface { lldb::addr_t base_addr, bool base_addr_is_offset); + virtual void UpdateLoadedSectionsByType(lldb::ModuleSP module, + lldb::addr_t link_map_addr, + lldb::addr_t base_addr, + bool base_addr_is_offset, + int type_id); + // Utility method so base classes can share implementation of // UpdateLoadedSections void UpdateLoadedSectionsCommon(lldb::ModuleSP module, lldb::addr_t base_addr, diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index cf16fbc812aa4..886ca766112c8 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -63,6 +63,10 @@ #include "llvm/Support/Threading.h" #include "llvm/Support/VersionTuple.h" +#if defined(__AIX__) +struct ld_xinfo; +#endif + namespace lldb_private { template struct Range; @@ -1915,6 +1919,10 @@ class Process : public std::enable_shared_from_this, Status GetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo &range_info); +#if defined(__AIX__) + Status GetLDXINFO(struct ld_xinfo *info_ptr); +#endif + /// Obtain all the mapped memory regions within this process. /// /// \param[out] region_list @@ -2855,6 +2863,12 @@ void PruneThreadPlans(); return Status("Process::DoGetMemoryRegionInfo() not supported"); } +#if defined(__AIX__) + virtual Status DoGetLDXINFO(struct ld_xinfo *info_ptr) { + return Status("Process::DoGetLDXINFO() not supported"); + } +#endif + /// Provide an override value in the subclass for lldb's /// CPU-based logic for whether watchpoint exceptions are /// received before or after an instruction executes. diff --git a/lldb/include/lldb/Target/RegisterContextUnwind.h b/lldb/include/lldb/Target/RegisterContextUnwind.h index ef8ae88403866..00a95853800ed 100644 --- a/lldb/include/lldb/Target/RegisterContextUnwind.h +++ b/lldb/include/lldb/Target/RegisterContextUnwind.h @@ -67,6 +67,10 @@ class RegisterContextUnwind : public lldb_private::RegisterContext { bool ReadPC(lldb::addr_t &start_pc); +#ifdef __AIX__ + bool ReadLR(lldb::addr_t &lr); +#endif + // Indicates whether this frame *behaves* like frame zero -- the currently // executing frame -- or not. This can be true in the middle of the stack // above asynchronous trap handlers (sigtramp) for instance. diff --git a/lldb/include/lldb/Target/ThreadPlanCallFunction.h b/lldb/include/lldb/Target/ThreadPlanCallFunction.h index cb6e7caebb4ad..7880db1592e04 100644 --- a/lldb/include/lldb/Target/ThreadPlanCallFunction.h +++ b/lldb/include/lldb/Target/ThreadPlanCallFunction.h @@ -27,6 +27,12 @@ class ThreadPlanCallFunction : public ThreadPlan { llvm::ArrayRef args, const EvaluateExpressionOptions &options); + ThreadPlanCallFunction(Thread &thread, const Address &function, + const Address &toc, + const CompilerType &return_type, + llvm::ArrayRef args, + const EvaluateExpressionOptions &options); + ThreadPlanCallFunction(Thread &thread, const Address &function, const EvaluateExpressionOptions &options); diff --git a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h index dd468ef5bddef..9953bd6c24588 100644 --- a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h +++ b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h @@ -61,6 +61,7 @@ class StringExtractorGDBRemote : public StringExtractor { eServerPacketType_qQueryGDBServer, eServerPacketType_qKillSpawnedProcess, eServerPacketType_qLaunchSuccess, + eServerPacketType_qLDXINFO, eServerPacketType_qModuleInfo, eServerPacketType_qProcessInfoPID, eServerPacketType_qSpeedTest, diff --git a/lldb/include/lldb/lldb-private-enumerations.h b/lldb/include/lldb/lldb-private-enumerations.h index c24a3538f58da..98c1e956bf8f7 100644 --- a/lldb/include/lldb/lldb-private-enumerations.h +++ b/lldb/include/lldb/lldb-private-enumerations.h @@ -65,6 +65,7 @@ enum ArchitectureType { eArchTypeMachO, eArchTypeELF, eArchTypeCOFF, + eArchTypeXCOFF, kNumArchTypes }; diff --git a/lldb/source/API/CMakeLists.txt b/lldb/source/API/CMakeLists.txt index a32bc58507d8e..3ecdb11daef7d 100644 --- a/lldb/source/API/CMakeLists.txt +++ b/lldb/source/API/CMakeLists.txt @@ -40,6 +40,113 @@ add_custom_target(lldb-sbapi-dwarf-enums DEPENDS ${sb_languages_file}) set_target_properties(lldb-sbapi-dwarf-enums PROPERTIES FOLDER "LLDB/Tablegenning") +if(CMAKE_SYSTEM_NAME MATCHES "AIX") +add_lldb_library(liblldb STATIC ${option_framework} + SBAddress.cpp + SBAddressRange.cpp + SBAddressRangeList.cpp + SBAttachInfo.cpp + SBBlock.cpp + SBBreakpoint.cpp + SBBreakpointLocation.cpp + SBBreakpointName.cpp + SBBreakpointOptionCommon.cpp + SBBroadcaster.cpp + SBCommandInterpreter.cpp + SBCommandInterpreterRunOptions.cpp + SBCommandReturnObject.cpp + SBCommunication.cpp + SBCompileUnit.cpp + SBSaveCoreOptions.cpp + SBData.cpp + SBDebugger.cpp + SBDeclaration.cpp + SBEnvironment.cpp + SBError.cpp + SBEvent.cpp + SBExecutionContext.cpp + SBExpressionOptions.cpp + SBFileSpec.cpp + SBFile.cpp + SBFileSpecList.cpp + SBFormat.cpp + SBFrame.cpp + SBFunction.cpp + SBHostOS.cpp + SBInstruction.cpp + SBInstructionList.cpp + SBLanguageRuntime.cpp + SBLaunchInfo.cpp + SBLineEntry.cpp + SBListener.cpp + SBMemoryRegionInfo.cpp + SBMemoryRegionInfoList.cpp + SBModule.cpp + SBModuleSpec.cpp + SBPlatform.cpp + SBProcess.cpp + SBProcessInfo.cpp + SBProcessInfoList.cpp + SBQueue.cpp + SBQueueItem.cpp + SBReproducer.cpp + SBScriptObject.cpp + SBSection.cpp + SBSourceManager.cpp + SBStatisticsOptions.cpp + SBStream.cpp + SBStringList.cpp + SBStructuredData.cpp + SBSymbol.cpp + SBSymbolContext.cpp + SBSymbolContextList.cpp + SBTarget.cpp + SBThread.cpp + SBThreadCollection.cpp + SBThreadPlan.cpp + SBTrace.cpp + SBTraceCursor.cpp + SBType.cpp + SBTypeCategory.cpp + SBTypeEnumMember.cpp + SBTypeFilter.cpp + SBTypeFormat.cpp + SBTypeNameSpecifier.cpp + SBTypeSummary.cpp + SBTypeSynthetic.cpp + SBValue.cpp + SBValueList.cpp + SBVariablesOptions.cpp + SBWatchpoint.cpp + SBWatchpointOptions.cpp + SBUnixSignals.cpp + SystemInitializerFull.cpp + ${lldb_python_wrapper} + ${lldb_lua_wrapper} + + DEPENDS + lldb-sbapi-dwarf-enums + + LINK_LIBS + lldbBreakpoint + lldbCore + lldbDataFormatters + lldbExpression + lldbHost + lldbInitialization + lldbInterpreter + lldbSymbol + lldbTarget + lldbUtility + lldbVersion + ${LLDB_ALL_PLUGINS} + LINK_COMPONENTS + Support + + ${option_install_prefix} +) + +else() add_lldb_library(liblldb SHARED ${option_framework} SBAddress.cpp SBAddressRange.cpp @@ -144,6 +251,7 @@ add_lldb_library(liblldb SHARED ${option_framework} ${option_install_prefix} ) +endif() # lib/pythonX.Y/dist-packages/lldb/_lldb.so is a symlink to lib/liblldb.so, # which depends on lib/libLLVM*.so (BUILD_SHARED_LIBS) or lib/libLLVM-10git.so diff --git a/lldb/source/API/SBBreakpoint.cpp b/lldb/source/API/SBBreakpoint.cpp index 3d908047f9455..728fe04d14d92 100644 --- a/lldb/source/API/SBBreakpoint.cpp +++ b/lldb/source/API/SBBreakpoint.cpp @@ -342,7 +342,7 @@ uint32_t SBBreakpoint::GetIgnoreCount() const { return count; } -void SBBreakpoint::SetThreadID(tid_t tid) { +void SBBreakpoint::SetThreadID(lldb::tid_t tid) { LLDB_INSTRUMENT_VA(this, tid); BreakpointSP bkpt_sp = GetSP(); @@ -353,10 +353,10 @@ void SBBreakpoint::SetThreadID(tid_t tid) { } } -tid_t SBBreakpoint::GetThreadID() { +lldb::tid_t SBBreakpoint::GetThreadID() { LLDB_INSTRUMENT_VA(this); - tid_t tid = LLDB_INVALID_THREAD_ID; + lldb::tid_t tid = LLDB_INVALID_THREAD_ID; BreakpointSP bkpt_sp = GetSP(); if (bkpt_sp) { std::lock_guard guard( diff --git a/lldb/source/API/SBBreakpointLocation.cpp b/lldb/source/API/SBBreakpointLocation.cpp index 75b66364d4f1a..fad9a4076a54f 100644 --- a/lldb/source/API/SBBreakpointLocation.cpp +++ b/lldb/source/API/SBBreakpointLocation.cpp @@ -302,7 +302,7 @@ bool SBBreakpointLocation::GetCommandLineCommands(SBStringList &commands) { return has_commands; } -void SBBreakpointLocation::SetThreadID(tid_t thread_id) { +void SBBreakpointLocation::SetThreadID(lldb::tid_t thread_id) { LLDB_INSTRUMENT_VA(this, thread_id); BreakpointLocationSP loc_sp = GetSP(); @@ -313,10 +313,10 @@ void SBBreakpointLocation::SetThreadID(tid_t thread_id) { } } -tid_t SBBreakpointLocation::GetThreadID() { +lldb::tid_t SBBreakpointLocation::GetThreadID() { LLDB_INSTRUMENT_VA(this); - tid_t tid = LLDB_INVALID_THREAD_ID; + lldb::tid_t tid = LLDB_INVALID_THREAD_ID; BreakpointLocationSP loc_sp = GetSP(); if (loc_sp) { std::lock_guard guard( diff --git a/lldb/source/API/SBBreakpointName.cpp b/lldb/source/API/SBBreakpointName.cpp index 7f63aaf6fa7d5..5c7c0a8f6504b 100644 --- a/lldb/source/API/SBBreakpointName.cpp +++ b/lldb/source/API/SBBreakpointName.cpp @@ -347,7 +347,7 @@ bool SBBreakpointName::GetAutoContinue() { return bp_name->GetOptions().IsAutoContinue(); } -void SBBreakpointName::SetThreadID(tid_t tid) { +void SBBreakpointName::SetThreadID(lldb::tid_t tid) { LLDB_INSTRUMENT_VA(this, tid); BreakpointName *bp_name = GetBreakpointName(); @@ -361,7 +361,7 @@ void SBBreakpointName::SetThreadID(tid_t tid) { UpdateName(*bp_name); } -tid_t SBBreakpointName::GetThreadID() { +lldb::tid_t SBBreakpointName::GetThreadID() { LLDB_INSTRUMENT_VA(this); BreakpointName *bp_name = GetBreakpointName(); diff --git a/lldb/source/Core/DynamicLoader.cpp b/lldb/source/Core/DynamicLoader.cpp index 7758a87403b5a..ea43a7f98b69f 100644 --- a/lldb/source/Core/DynamicLoader.cpp +++ b/lldb/source/Core/DynamicLoader.cpp @@ -113,6 +113,16 @@ void DynamicLoader::UpdateLoadedSections(ModuleSP module, addr_t link_map_addr, UpdateLoadedSectionsCommon(module, base_addr, base_addr_is_offset); } +void DynamicLoader::UpdateLoadedSectionsByType(lldb::ModuleSP module, + lldb::addr_t link_map_addr, + lldb::addr_t base_addr, + bool base_addr_is_offset, + int type_id) { + bool changed; + module->SetLoadAddressByType(m_process->GetTarget(), base_addr, base_addr_is_offset, + changed, type_id); +} + void DynamicLoader::UpdateLoadedSectionsCommon(ModuleSP module, addr_t base_addr, bool base_addr_is_offset) { diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp index 387c4fac6b0f8..43c5b043ef7a2 100644 --- a/lldb/source/Core/Mangled.cpp +++ b/lldb/source/Core/Mangled.cpp @@ -167,12 +167,14 @@ static char *GetItaniumDemangledStr(const char *M) { "Expected demangled_size to return length including trailing null"); } +#if !defined(__AIX__) if (Log *log = GetLog(LLDBLog::Demangle)) { if (demangled_cstr) LLDB_LOGF(log, "demangled itanium: %s -> \"%s\"", M, demangled_cstr); else LLDB_LOGF(log, "demangled itanium: %s -> error: failed to demangle", M); } +#endif return demangled_cstr; } diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp index f9d7832254f46..044a5d29978e8 100644 --- a/lldb/source/Core/Module.cpp +++ b/lldb/source/Core/Module.cpp @@ -1510,6 +1510,18 @@ bool Module::SetLoadAddress(Target &target, lldb::addr_t value, return false; } +bool Module::SetLoadAddressByType(Target &target, lldb::addr_t value, + bool value_is_offset, bool &changed, int type_id) { + ObjectFile *object_file = GetObjectFile(); + if (object_file != nullptr) { + changed = object_file->SetLoadAddressByType(target, value, value_is_offset, type_id); + return true; + } else { + changed = false; + } + return false; +} + bool Module::MatchesModuleSpec(const ModuleSpec &module_ref) { const UUID &uuid = module_ref.GetUUID(); diff --git a/lldb/source/Core/Section.cpp b/lldb/source/Core/Section.cpp index 0763e88d4608f..9ed55853930a6 100644 --- a/lldb/source/Core/Section.cpp +++ b/lldb/source/Core/Section.cpp @@ -263,6 +263,10 @@ bool Section::ResolveContainedAddress(addr_t offset, Address &so_addr, bool Section::ContainsFileAddress(addr_t vm_addr) const { const addr_t file_addr = GetFileAddress(); +#ifdef __AIX__ + if (file_addr == 0) + return false; +#endif if (file_addr != LLDB_INVALID_ADDRESS && !IsThreadSpecific()) { if (file_addr <= vm_addr) { const addr_t offset = (vm_addr - file_addr) * m_target_byte_size; diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp index 444e44b392891..c1feec990f989 100644 --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -130,7 +130,7 @@ static llvm::Error ReadRegisterValueAsScalar(RegisterContext *reg_ctx, /// Return the length in bytes of the set of operands for \p op. No guarantees /// are made on the state of \p data after this call. -static offset_t GetOpcodeDataSize(const DataExtractor &data, +static lldb::offset_t GetOpcodeDataSize(const DataExtractor &data, const lldb::offset_t data_offset, const uint8_t op, const DWARFUnit *dwarf_cu) { lldb::offset_t offset = data_offset; @@ -358,7 +358,7 @@ lldb::addr_t DWARFExpression::GetLocation_DW_OP_addr(const DWARFUnit *dwarf_cu, error = true; break; } - const offset_t op_arg_size = + const lldb::offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op, dwarf_cu); if (op_arg_size == LLDB_INVALID_OFFSET) { error = true; @@ -418,7 +418,7 @@ bool DWARFExpression::Update_DW_OP_addr(const DWARFUnit *dwarf_cu, m_data.SetData(encoder.GetDataBuffer()); return true; } - const offset_t op_arg_size = + const lldb::offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op, dwarf_cu); if (op_arg_size == LLDB_INVALID_OFFSET) break; @@ -435,7 +435,7 @@ bool DWARFExpression::ContainsThreadLocalStorage( if (op == DW_OP_form_tls_address || op == DW_OP_GNU_push_tls_address) return true; - const offset_t op_arg_size = + const lldb::offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op, dwarf_cu); if (op_arg_size == LLDB_INVALID_OFFSET) return false; @@ -515,7 +515,7 @@ bool DWARFExpression::LinkThreadLocalStorage( } if (!decoded_data) { - const offset_t op_arg_size = + const lldb::offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op, dwarf_cu); if (op_arg_size == LLDB_INVALID_OFFSET) return false; diff --git a/lldb/source/Host/CMakeLists.txt b/lldb/source/Host/CMakeLists.txt index c2e091ee8555b..5374b16881950 100644 --- a/lldb/source/Host/CMakeLists.txt +++ b/lldb/source/Host/CMakeLists.txt @@ -7,6 +7,11 @@ if (APPLE AND LLVM_ENABLE_LOCAL_SUBMODULE_VISIBILITY) endif() endif() +if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX") + remove_definitions("-D_XOPEN_SOURCE=700") + add_definitions("-D_ALL_SOURCE") +endif() + macro(add_host_subdirectory group) list(APPEND HOST_SOURCES ${ARGN}) source_group(${group} FILES ${ARGN}) @@ -133,6 +138,14 @@ else() openbsd/Host.cpp openbsd/HostInfoOpenBSD.cpp ) + + elseif (CMAKE_SYSTEM_NAME MATCHES "AIX") + add_host_subdirectory(aix + aix/AbstractSocket.cpp + aix/Host.cpp + aix/HostInfoAIX.cpp + aix/Support.cpp + ) endif() endif() diff --git a/lldb/source/Host/aix/AbstractSocket.cpp b/lldb/source/Host/aix/AbstractSocket.cpp new file mode 100644 index 0000000000000..bfb67d452f7ec --- /dev/null +++ b/lldb/source/Host/aix/AbstractSocket.cpp @@ -0,0 +1,21 @@ +//===-- AbstractSocket.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 "lldb/Host/aix/AbstractSocket.h" + +#include "llvm/ADT/StringRef.h" + +using namespace lldb; +using namespace lldb_private; + +AbstractSocket::AbstractSocket(bool child_processes_inherit) + : DomainSocket(ProtocolUnixAbstract, child_processes_inherit) {} + +size_t AbstractSocket::GetNameOffset() const { return 1; } + +void AbstractSocket::DeleteSocketFile(llvm::StringRef name) {} diff --git a/lldb/source/Host/aix/Host.cpp b/lldb/source/Host/aix/Host.cpp new file mode 100644 index 0000000000000..d82cb9049d389 --- /dev/null +++ b/lldb/source/Host/aix/Host.cpp @@ -0,0 +1,304 @@ +//===-- source/Host/aix/Host.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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/ScopedPrinter.h" + +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/ProcessInfo.h" +#include "lldb/Utility/Status.h" + +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/aix/Host.h" +#include "lldb/Host/aix/Support.h" +#include "lldb/Utility/DataExtractor.h" +#include "llvm/BinaryFormat/XCOFF.h" + +#include +#include + +using namespace llvm; +using namespace lldb; +using namespace lldb_private; + +namespace { +enum class ProcessState { + Unknown, + Dead, + DiskSleep, + Idle, + Paging, + Parked, + Running, + Sleeping, + TracedOrStopped, + Zombie, +}; +} + +namespace lldb_private { +class ProcessLaunchInfo; +} + +static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo, + ProcessState &State, ::pid_t &TracerPid, + ::pid_t &Tgid) { + Log *log = GetLog(LLDBLog::Host); + + auto BufferOrError = getProcFile(Pid, "status"); + if (!BufferOrError) + return false; + + llvm::StringRef Rest = BufferOrError.get()->getBuffer(); + while (!Rest.empty()) { + llvm::StringRef Line; + std::tie(Line, Rest) = Rest.split('\n'); + + if (Line.consume_front("Gid:")) { + // Real, effective, saved set, and file system GIDs. Read the first two. + Line = Line.ltrim(); + uint32_t RGid, EGid; + Line.consumeInteger(10, RGid); + Line = Line.ltrim(); + Line.consumeInteger(10, EGid); + + ProcessInfo.SetGroupID(RGid); + ProcessInfo.SetEffectiveGroupID(EGid); + } else if (Line.consume_front("Uid:")) { + // Real, effective, saved set, and file system UIDs. Read the first two. + Line = Line.ltrim(); + uint32_t RUid, EUid; + Line.consumeInteger(10, RUid); + Line = Line.ltrim(); + Line.consumeInteger(10, EUid); + + ProcessInfo.SetUserID(RUid); + ProcessInfo.SetEffectiveUserID(EUid); + } else if (Line.consume_front("PPid:")) { + ::pid_t PPid; + Line.ltrim().consumeInteger(10, PPid); + ProcessInfo.SetParentProcessID(PPid); + } else if (Line.consume_front("State:")) { + State = llvm::StringSwitch(Line.ltrim().take_front(1)) + .Case("D", ProcessState::DiskSleep) + .Case("I", ProcessState::Idle) + .Case("R", ProcessState::Running) + .Case("S", ProcessState::Sleeping) + .CaseLower("T", ProcessState::TracedOrStopped) + .Case("W", ProcessState::Paging) + .Case("P", ProcessState::Parked) + .Case("X", ProcessState::Dead) + .Case("Z", ProcessState::Zombie) + .Default(ProcessState::Unknown); + if (State == ProcessState::Unknown) { + LLDB_LOG(log, "Unknown process state {0}", Line); + } + } else if (Line.consume_front("TracerPid:")) { + Line = Line.ltrim(); + Line.consumeInteger(10, TracerPid); + } else if (Line.consume_front("Tgid:")) { + Line = Line.ltrim(); + Line.consumeInteger(10, Tgid); + } + } + return true; +} + +static bool IsDirNumeric(const char *dname) { + for (; *dname; dname++) { + if (!isdigit(*dname)) + return false; + } + return true; +} + +static void GetProcessArgs(::pid_t pid, ProcessInstanceInfo &process_info) { + auto BufferOrError = getProcFile(pid, "cmdline"); + if (!BufferOrError) + return; + std::unique_ptr Cmdline = std::move(*BufferOrError); + + llvm::StringRef Arg0, Rest; + std::tie(Arg0, Rest) = Cmdline->getBuffer().split('\0'); + process_info.SetArg0(Arg0); + while (!Rest.empty()) { + llvm::StringRef Arg; + std::tie(Arg, Rest) = Rest.split('\0'); + process_info.GetArguments().AppendArgument(Arg); + } +} + +static void GetExePathAndArch(::pid_t pid, ProcessInstanceInfo &process_info) { + Log *log = GetLog(LLDBLog::Process); + std::string ExePath(PATH_MAX, '\0'); + std::string Basename(PATH_MAX, '\0'); + struct psinfo psinfoData; + + // We can't use getProcFile here because proc/[pid]/exe is a symbolic link. + llvm::SmallString<64> ProcExe; + (llvm::Twine("/proc/") + llvm::Twine(pid) + "/cwd").toVector(ProcExe); + + ssize_t len = readlink(ProcExe.c_str(), &ExePath[0], PATH_MAX); + if (len > 0) { + ExePath.resize(len); + + //FIXME: hack to get basename + struct stat statData; + + std::ostringstream oss; + + oss << "/proc/" << std::dec << pid << "/psinfo"; + assert(stat(oss.str().c_str(), &statData) == 0); + + const int fd = open(oss.str().c_str(), O_RDONLY); + assert (fd >= 0); + + ssize_t readNum = read(fd, &psinfoData, sizeof(psinfoData)); + assert (readNum >= 0); + + close (fd); + } else { + LLDB_LOG(log, "failed to read link exe link for {0}: {1}", pid, + Status(errno, eErrorTypePOSIX)); + ExePath.resize(0); + } + + llvm::StringRef PathRef = std::string(&(psinfoData.pr_psargs[0])); + + if (!PathRef.empty()) { + process_info.GetExecutableFile().SetFile(PathRef, FileSpec::Style::native); + ArchSpec arch_spec = ArchSpec(); + arch_spec.SetArchitecture(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE, llvm::Triple::AIX); + process_info.SetArchitecture(arch_spec); + } +} + +static void GetProcessEnviron(::pid_t pid, ProcessInstanceInfo &process_info) { + // Get the process environment. + auto BufferOrError = getProcFile(pid, "environ"); + if (!BufferOrError) + return; + + std::unique_ptr Environ = std::move(*BufferOrError); + llvm::StringRef Rest = Environ->getBuffer(); + while (!Rest.empty()) { + llvm::StringRef Var; + std::tie(Var, Rest) = Rest.split('\0'); + process_info.GetEnvironment().insert(Var); + } +} + +static bool GetProcessAndStatInfo(::pid_t pid, + ProcessInstanceInfo &process_info, + ProcessState &State, ::pid_t &tracerpid) { + ::pid_t tgid; + tracerpid = 0; + process_info.Clear(); + + process_info.SetProcessID(pid); + + GetExePathAndArch(pid, process_info); + GetProcessArgs(pid, process_info); + GetProcessEnviron(pid, process_info); + + // Get User and Group IDs and get tracer pid. + if (!GetStatusInfo(pid, process_info, State, tracerpid, tgid)) + return false; + + return true; +} + +uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) { + static const char procdir[] = "/proc/"; + + DIR *dirproc = opendir(procdir); + if (dirproc) { + struct dirent *direntry = nullptr; + const uid_t our_uid = getuid(); + const lldb::pid_t our_pid = getpid(); + bool all_users = match_info.GetMatchAllUsers(); + + while ((direntry = readdir(dirproc)) != nullptr) { + /* + if (direntry->d_type != DT_DIR || !IsDirNumeric(direntry->d_name)) + continue; + */ + + lldb::pid_t pid = atoi(direntry->d_name); + + // Skip this process. + if (pid == our_pid) + continue; + + ::pid_t tracerpid; + ProcessState State; + ProcessInstanceInfo process_info; + + if (!GetProcessAndStatInfo(pid, process_info, State, tracerpid)) + continue; + + // Skip if process is being debugged. + if (tracerpid != 0) + continue; + + if (State == ProcessState::Zombie) + continue; + + // Check for user match if we're not matching all users and not running + // as root. + if (!all_users && (our_uid != 0) && (process_info.GetUserID() != our_uid)) + continue; + + if (match_info.Matches(process_info)) { + process_infos.push_back(process_info); + } + } + + closedir(dirproc); + } + + return process_infos.size(); +} + +bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) { + ::pid_t tracerpid; + ProcessState State; + return GetProcessAndStatInfo(pid, process_info, State, tracerpid); +} + +Environment Host::GetEnvironment() { return Environment(environ); } + +Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) { + return Status("unimplemented"); +} + +std::optional lldb_private::getPIDForTID(lldb::pid_t tid) { + ::pid_t tracerpid, tgid = LLDB_INVALID_PROCESS_ID; + ProcessInstanceInfo process_info; + ProcessState state; + + if (!GetStatusInfo(tid, process_info, state, tracerpid, tgid) || + tgid == LLDB_INVALID_PROCESS_ID) + return std::nullopt; + return tgid; +} diff --git a/lldb/source/Host/aix/HostInfoAIX.cpp b/lldb/source/Host/aix/HostInfoAIX.cpp new file mode 100644 index 0000000000000..8bda09e01741b --- /dev/null +++ b/lldb/source/Host/aix/HostInfoAIX.cpp @@ -0,0 +1,215 @@ +//===-- HostInfoAIX.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 "lldb/Host/aix/HostInfoAIX.h" +#include "lldb/Host/Config.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" + +#include "llvm/Support/Threading.h" + +#include +#include +#include +#include +#include + +#include +#include + +using namespace lldb_private; + +namespace { +struct HostInfoAIXFields { + llvm::once_flag m_distribution_once_flag; + std::string m_distribution_id; + llvm::once_flag m_os_version_once_flag; + llvm::VersionTuple m_os_version; +}; +} // namespace + +static HostInfoAIXFields *g_fields = nullptr; + +void HostInfoAIX::Initialize(SharedLibraryDirectoryHelper *helper) { + HostInfoPosix::Initialize(helper); + + g_fields = new HostInfoAIXFields(); +} + +void HostInfoAIX::Terminate() { + assert(g_fields && "Missing call to Initialize?"); + delete g_fields; + g_fields = nullptr; + HostInfoBase::Terminate(); +} + +llvm::VersionTuple HostInfoAIX::GetOSVersion() { + assert(g_fields && "Missing call to Initialize?"); + llvm::call_once(g_fields->m_os_version_once_flag, []() { + struct utsname un; + if (uname(&un) != 0) + return; + + llvm::StringRef release = un.release; + // The kernel release string can include a lot of stuff (e.g. + // 4.9.0-6-amd64). We're only interested in the numbered prefix. + release = release.substr(0, release.find_first_not_of("0123456789.")); + g_fields->m_os_version.tryParse(release); + }); + + return g_fields->m_os_version; +} + +std::optional HostInfoAIX::GetOSBuildString() { + struct utsname un; + ::memset(&un, 0, sizeof(utsname)); + + if (uname(&un) < 0) + return std::nullopt; + + return std::string(un.release); +} + +llvm::StringRef HostInfoAIX::GetDistributionId() { + assert(g_fields && "Missing call to Initialize?"); + // Try to run 'lbs_release -i', and use that response for the distribution + // id. + llvm::call_once(g_fields->m_distribution_once_flag, []() { + Log *log = GetLog(LLDBLog::Host); + LLDB_LOGF(log, "attempting to determine AIX distribution..."); + + // check if the lsb_release command exists at one of the following paths + const char *const exe_paths[] = {"/bin/lsb_release", + "/usr/bin/lsb_release"}; + + for (size_t exe_index = 0; + exe_index < sizeof(exe_paths) / sizeof(exe_paths[0]); ++exe_index) { + const char *const get_distribution_info_exe = exe_paths[exe_index]; + if (access(get_distribution_info_exe, F_OK)) { + // this exe doesn't exist, move on to next exe + LLDB_LOGF(log, "executable doesn't exist: %s", + get_distribution_info_exe); + continue; + } + + // execute the distribution-retrieval command, read output + std::string get_distribution_id_command(get_distribution_info_exe); + get_distribution_id_command += " -i"; + + FILE *file = popen(get_distribution_id_command.c_str(), "r"); + if (!file) { + LLDB_LOGF(log, + "failed to run command: \"%s\", cannot retrieve " + "platform information", + get_distribution_id_command.c_str()); + break; + } + + // retrieve the distribution id string. + char distribution_id[256] = {'\0'}; + if (fgets(distribution_id, sizeof(distribution_id) - 1, file) != + nullptr) { + LLDB_LOGF(log, "distribution id command returned \"%s\"", + distribution_id); + + const char *const distributor_id_key = "Distributor ID:\t"; + if (strstr(distribution_id, distributor_id_key)) { + // strip newlines + std::string id_string(distribution_id + strlen(distributor_id_key)); + id_string.erase(std::remove(id_string.begin(), id_string.end(), '\n'), + id_string.end()); + + // lower case it and convert whitespace to underscores + std::transform( + id_string.begin(), id_string.end(), id_string.begin(), + [](char ch) { return tolower(isspace(ch) ? '_' : ch); }); + + g_fields->m_distribution_id = id_string; + LLDB_LOGF(log, "distribution id set to \"%s\"", + g_fields->m_distribution_id.c_str()); + } else { + LLDB_LOGF(log, "failed to find \"%s\" field in \"%s\"", + distributor_id_key, distribution_id); + } + } else { + LLDB_LOGF(log, + "failed to retrieve distribution id, \"%s\" returned no" + " lines", + get_distribution_id_command.c_str()); + } + + // clean up the file + pclose(file); + } + }); + + return g_fields->m_distribution_id; +} + +FileSpec HostInfoAIX::GetProgramFileSpec() { + static FileSpec g_program_filespec; + + if (!g_program_filespec) { + char exe_path[PATH_MAX]; + ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1); + if (len > 0) { + exe_path[len] = 0; + g_program_filespec.SetFile(exe_path, FileSpec::Style::native); + } + } + + return g_program_filespec; +} + +bool HostInfoAIX::ComputeSupportExeDirectory(FileSpec &file_spec) { + if (HostInfoPosix::ComputeSupportExeDirectory(file_spec) && + file_spec.IsAbsolute() && FileSystem::Instance().Exists(file_spec)) + return true; + file_spec.SetDirectory(GetProgramFileSpec().GetDirectory()); + return !file_spec.GetDirectory().IsEmpty(); +} + +bool HostInfoAIX::ComputeSystemPluginsDirectory(FileSpec &file_spec) { + FileSpec temp_file("/usr/" LLDB_INSTALL_LIBDIR_BASENAME "/lldb/plugins"); + FileSystem::Instance().Resolve(temp_file); + file_spec.SetDirectory(temp_file.GetPath()); + return true; +} + +bool HostInfoAIX::ComputeUserPluginsDirectory(FileSpec &file_spec) { + // XDG Base Directory Specification + // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html If + // XDG_DATA_HOME exists, use that, otherwise use ~/.local/share/lldb. + const char *xdg_data_home = getenv("XDG_DATA_HOME"); + if (xdg_data_home && xdg_data_home[0]) { + std::string user_plugin_dir(xdg_data_home); + user_plugin_dir += "/lldb"; + file_spec.SetDirectory(user_plugin_dir.c_str()); + } else + file_spec.SetDirectory("~/.local/share/lldb"); + return true; +} + +void HostInfoAIX::ComputeHostArchitectureSupport(ArchSpec &arch_32, + ArchSpec &arch_64) { + HostInfoPosix::ComputeHostArchitectureSupport(arch_32, arch_64); + + const char *distribution_id = GetDistributionId().data(); + + // On Linux, "unknown" in the vendor slot isn't what we want for the default + // triple. It's probably an artifact of config.guess. + if (arch_32.IsValid()) { + if (arch_32.GetTriple().getVendor() == llvm::Triple::UnknownVendor) + arch_32.GetTriple().setVendorName(llvm::StringRef()); + } + if (arch_64.IsValid()) { + if (arch_64.GetTriple().getVendor() == llvm::Triple::UnknownVendor) + arch_64.GetTriple().setVendorName(llvm::StringRef()); + } +} diff --git a/lldb/source/Host/aix/Support.cpp b/lldb/source/Host/aix/Support.cpp new file mode 100644 index 0000000000000..1bf2662190127 --- /dev/null +++ b/lldb/source/Host/aix/Support.cpp @@ -0,0 +1,44 @@ +//===-- Support.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 "lldb/Host/aix/Support.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "llvm/Support/MemoryBuffer.h" + +llvm::ErrorOr> +lldb_private::getProcFile(::pid_t pid, ::pid_t tid, const llvm::Twine &file) { + Log *log = GetLog(LLDBLog::Host); + std::string File = + ("/proc/" + llvm::Twine(pid) + "/task/" + llvm::Twine(tid) + "/" + file) + .str(); + auto Ret = llvm::MemoryBuffer::getFileAsStream(File); + if (!Ret) + LLDB_LOG(log, "Failed to open {0}: {1}", File, Ret.getError().message()); + return Ret; +} + +llvm::ErrorOr> +lldb_private::getProcFile(::pid_t pid, const llvm::Twine &file) { + Log *log = GetLog(LLDBLog::Host); + std::string File = ("/proc/" + llvm::Twine(pid) + "/" + file).str(); + auto Ret = llvm::MemoryBuffer::getFileAsStream(File); + if (!Ret) + LLDB_LOG(log, "Failed to open {0}: {1}", File, Ret.getError().message()); + return Ret; +} + +llvm::ErrorOr> +lldb_private::getProcFile(const llvm::Twine &file) { + Log *log = GetLog(LLDBLog::Host); + std::string File = ("/proc/" + file).str(); + auto Ret = llvm::MemoryBuffer::getFileAsStream(File); + if (!Ret) + LLDB_LOG(log, "Failed to open {0}: {1}", File, Ret.getError().message()); + return Ret; +} diff --git a/lldb/source/Host/common/GetOptInc.cpp b/lldb/source/Host/common/GetOptInc.cpp index c2044b6873221..e0ae2aa1774b3 100644 --- a/lldb/source/Host/common/GetOptInc.cpp +++ b/lldb/source/Host/common/GetOptInc.cpp @@ -409,7 +409,7 @@ static int getopt_internal(int nargc, char *const *nargv, const char *options, * [eventually this will replace the BSD getopt] */ #if defined(REPLACE_GETOPT) -int getopt(int nargc, char *const *nargv, const char *options) { +int getopt(int nargc, char *const *nargv, const char *options) throw() { /* * We don't pass FLAG_PERMUTE to getopt_internal() since diff --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp index e03d36e9cad4a..2fd7111a94fb2 100644 --- a/lldb/source/Host/common/Host.cpp +++ b/lldb/source/Host/common/Host.cpp @@ -357,9 +357,183 @@ bool Host::ResolveExecutableInBundle(FileSpec &file) { return false; } #ifndef _WIN32 +#if defined(__AIX__) + +#include +extern char **p_xargv; + +/* Fix missing Dl_info & dladdr in AIX + * The code is taken from netbsd.org (src/crypto/external/bsd/openssl/dist/crypto/dso/dso_dlfcn.c) + * except strlcpy & strlcat (those are taken from openbsd.org (src/lib/libc/string)) + */ +/*- + * See IBM's AIX Version 7.2, Technical Reference: + * Base Operating System and Extensions, Volume 1 and 2 + * https://www.ibm.com/support/knowledgecenter/ssw_aix_72/com.ibm.aix.base/technicalreferences.htm + */ +#include +#include + +/* strlcpy: + * Copy string src to buffer dst of size dsize. At most dsize-1 + * chars will be copied. Always NUL terminates (unless dsize == 0). + * Returns strlen(src); if retval >= dsize, truncation occurred. + */ +size_t strlcpy(char *dst, const char *src, size_t dsize) +{ + const char *osrc = src; + size_t nleft = dsize; + + /* Copy as many bytes as will fit. */ + if (nleft != 0) { + while (--nleft != 0) { + if ((*dst++ = *src++) == '\0') { + break; + } + } + } + + /* Not enough room in dst, add NUL and traverse rest of src. */ + if (nleft == 0) { + if (dsize != 0) { + *dst = '\0'; /* NUL-terminate dst */ + } + while (*src++) { + ; + } + } + + return src - osrc - 1; /* count does not include NUL */ +} + +/* strlcat: + * Appends src to string dst of size dsize (unlike strncat, dsize is the + * full size of dst, not space left). At most dsize-1 characters + * will be copied. Always NUL terminates (unless dsize <= strlen(dst)). + * Returns strlen(src) + MIN(dsize, strlen(initial dst)). + * If retval >= dsize, truncation occurred. + */ +size_t strlcat(char *dst, const char *src, size_t dsize) +{ + const char *odst = dst; + const char *osrc = src; + size_t n = dsize; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end. */ + while (n-- != 0 && *dst != '\0') { + dst++; + } + dlen = dst - odst; + n = dsize - dlen; + + if (n-- == 0) { + return dlen + strlen(src); + } + while (*src != '\0') { + if (n != 0) { + *dst++ = *src; + n--; + } + src++; + } + *dst = '\0'; + + return dlen + src - osrc; /* count does not include NUL */ +} + +/* ~ 64 * (sizeof(struct ld_info) + _XOPEN_PATH_MAX + _XOPEN_NAME_MAX) */ +# define DLFCN_LDINFO_SIZE 86976 +typedef struct Dl_info { + const char *dli_fname; +} Dl_info; +/* + * This dladdr()-implementation will also find the ptrgl (Pointer Glue) virtual + * address of a function, which is just located in the DATA segment instead of + * the TEXT segment. + */ +static int dladdr(const void *ptr, Dl_info *dl) +{ + uintptr_t addr = (uintptr_t)ptr; + struct ld_info *ldinfos; + struct ld_info *next_ldi; + struct ld_info *this_ldi; + + if ((ldinfos = (struct ld_info *)malloc(DLFCN_LDINFO_SIZE)) == NULL) { + dl->dli_fname = NULL; + return 0; + } + + if ((loadquery(L_GETINFO, (void *)ldinfos, DLFCN_LDINFO_SIZE)) < 0) { + /*- + * Error handling is done through errno and dlerror() reading errno: + * ENOMEM (ldinfos buffer is too small), + * EINVAL (invalid flags), + * EFAULT (invalid ldinfos ptr) + */ + free((void *)ldinfos); + dl->dli_fname = NULL; + return 0; + } + next_ldi = ldinfos; + + do { + this_ldi = next_ldi; + if (((addr >= (uintptr_t)this_ldi->ldinfo_textorg) + && (addr < ((uintptr_t)this_ldi->ldinfo_textorg + + this_ldi->ldinfo_textsize))) + || ((addr >= (uintptr_t)this_ldi->ldinfo_dataorg) + && (addr < ((uintptr_t)this_ldi->ldinfo_dataorg + + this_ldi->ldinfo_datasize)))) { + char *buffer = NULL; + char *member = NULL; + size_t buffer_sz; + size_t member_len; + + buffer_sz = strlen(this_ldi->ldinfo_filename) + 1; + member = this_ldi->ldinfo_filename + buffer_sz; + if ((member_len = strlen(member)) > 0) { + buffer_sz += 1 + member_len + 1; + } + if ((buffer = (char *)malloc(buffer_sz)) != NULL) { + strlcpy(buffer, this_ldi->ldinfo_filename, buffer_sz); + if (member_len > 0) { + /* + * Need to respect a possible member name and not just + * returning the path name in this case. See docs: + * sys/ldr.h, loadquery() and dlopen()/RTLD_MEMBER. + */ + strlcat(buffer, "(", buffer_sz); + strlcat(buffer, member, buffer_sz); + strlcat(buffer, ")", buffer_sz); + } + dl->dli_fname = buffer; + } + break; + } else { + next_ldi = (struct ld_info *)((uintptr_t)this_ldi + + this_ldi->ldinfo_next); + } + } while (this_ldi->ldinfo_next); + free((void *)ldinfos); + return dl->dli_fname != NULL; +} + +#endif + FileSpec Host::GetModuleFileSpecForHostAddress(const void *host_addr) { FileSpec module_filespec; #if !defined(__ANDROID__) +#ifdef __AIX__ + if (host_addr == reinterpret_cast(HostInfoBase::ComputeSharedLibraryDirectory)) { + // FIXME: AIX dladdr return "lldb" for this case + if (p_xargv[0]) { + module_filespec.SetFile(p_xargv[0], FileSpec::Style::native); + FileSystem::Instance().Resolve(module_filespec); + return module_filespec; + } + } +#endif Dl_info info; if (::dladdr(host_addr, &info)) { if (info.dli_fname) { @@ -373,12 +547,6 @@ FileSpec Host::GetModuleFileSpecForHostAddress(const void *host_addr) { #endif -#if !defined(__linux__) -bool Host::FindProcessThreads(const lldb::pid_t pid, TidMap &tids_to_attach) { - return false; -} -#endif - struct ShellInfo { ShellInfo() : process_reaped(false) {} diff --git a/lldb/source/Host/common/LICENSE.aix-netbsd.txt b/lldb/source/Host/common/LICENSE.aix-netbsd.txt new file mode 100644 index 0000000000000..9601ab43575f9 --- /dev/null +++ b/lldb/source/Host/common/LICENSE.aix-netbsd.txt @@ -0,0 +1,125 @@ + + LICENSE ISSUES + ============== + + The OpenSSL toolkit stays under a double license, i.e. both the conditions of + the OpenSSL License and the original SSLeay license apply to the toolkit. + See below for the actual license texts. + + OpenSSL License + --------------- + +/* ==================================================================== + * Copyright (c) 1998-2019 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core at openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay at cryptsoft.com). This product includes software written by Tim + * Hudson (tjh at cryptsoft.com). + * + */ + + Original SSLeay License + ----------------------- + +/* Copyright (C) 1995-1998 Eric Young (eay at cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay at cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh at cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay at cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh at cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + diff --git a/lldb/source/Host/common/XML.cpp b/lldb/source/Host/common/XML.cpp index f480ef3166a44..62cac78aaac23 100644 --- a/lldb/source/Host/common/XML.cpp +++ b/lldb/source/Host/common/XML.cpp @@ -10,6 +10,9 @@ #include "lldb/Host/XML.h" #include "llvm/ADT/StringExtras.h" +#if defined(__AIX__) +#undef LLDB_ENABLE_LIBXML2 +#endif using namespace lldb; using namespace lldb_private; diff --git a/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp b/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp index fceeff08ed9d3..143254bb12901 100644 --- a/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp +++ b/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp @@ -721,6 +721,7 @@ ConnectionFileDescriptor::ConnectFD(llvm::StringRef s, ConnectionStatus ConnectionFileDescriptor::ConnectFile( llvm::StringRef s, socket_id_callback_type socket_id_callback, Status *error_ptr) { +#if !defined(__AIX__) #if LLDB_ENABLE_POSIX std::string addr_str = s.str(); // file:///PATH @@ -753,6 +754,7 @@ ConnectionStatus ConnectionFileDescriptor::ConnectFile( m_io_sp = std::make_shared(fd, File::eOpenOptionReadWrite, true); return eConnectionStatusSuccess; #endif // LLDB_ENABLE_POSIX +#endif llvm_unreachable("this function should be only called w/ LLDB_ENABLE_POSIX"); } diff --git a/lldb/source/Host/posix/FileSystemPosix.cpp b/lldb/source/Host/posix/FileSystemPosix.cpp index cdb76da626bc9..a7c50f6a3c835 100644 --- a/lldb/source/Host/posix/FileSystemPosix.cpp +++ b/lldb/source/Host/posix/FileSystemPosix.cpp @@ -11,7 +11,9 @@ // C includes #include #include +#if !defined(__AIX__) #include +#endif #include #include #include diff --git a/lldb/source/Host/posix/MainLoopPosix.cpp b/lldb/source/Host/posix/MainLoopPosix.cpp index 5fe4d015251c8..e5be0db4cf19b 100644 --- a/lldb/source/Host/posix/MainLoopPosix.cpp +++ b/lldb/source/Host/posix/MainLoopPosix.cpp @@ -179,9 +179,21 @@ Status MainLoopPosix::RunImpl::Poll() { read_fds.push_back(pfd); } +#if defined(__AIX__) + sigset_t origmask; + int timeout; + + timeout = -1; + pthread_sigmask(SIG_SETMASK, &sigmask, &origmask); + int ready = poll(read_fds.data(), read_fds.size(), timeout); + pthread_sigmask(SIG_SETMASK, &origmask, nullptr); + if (ready == -1 && errno != EINTR) + return Status(errno, eErrorTypePOSIX); +#else if (ppoll(read_fds.data(), read_fds.size(), nullptr, &sigmask) == -1 && errno != EINTR) return Status(errno, eErrorTypePOSIX); +#endif return Status(); } @@ -312,8 +324,13 @@ MainLoopPosix::RegisterSignal(int signo, const Callback &callback, // If we're using kqueue, the signal needs to be unblocked in order to // receive it. If using pselect/ppoll, we need to block it, and later unblock // it as a part of the system call. +#if defined(__AIX__) + //FIXME: where is signal unblocked? + ret = pthread_sigmask(SIG_UNBLOCK, &new_action.sa_mask, &old_set); +#else ret = pthread_sigmask(HAVE_SYS_EVENT_H ? SIG_UNBLOCK : SIG_BLOCK, &new_action.sa_mask, &old_set); +#endif assert(ret == 0 && "pthread_sigmask failed"); info.was_blocked = sigismember(&old_set, signo); auto insert_ret = m_signals.insert({signo, info}); diff --git a/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp b/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp index 0a832ebad13a7..cd106f605b1f4 100644 --- a/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp +++ b/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp @@ -193,8 +193,13 @@ struct ForkLaunchInfo { } // Start tracing this child that is about to exec. +#if !defined(__AIX__) if (ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1) ExitWithError(error_fd, "ptrace"); +#else + if (ptrace64(PT_TRACE_ME, 0, 0, 0, nullptr) == -1) + ExitWithError(error_fd, "ptrace"); +#endif } // Execute. We should never return... diff --git a/lldb/source/Initialization/CMakeLists.txt b/lldb/source/Initialization/CMakeLists.txt index c1a167826f76f..9f94830c8509f 100644 --- a/lldb/source/Initialization/CMakeLists.txt +++ b/lldb/source/Initialization/CMakeLists.txt @@ -1,4 +1,4 @@ -if ( CMAKE_SYSTEM_NAME MATCHES "Linux|Android|FreeBSD|NetBSD" ) +if ( CMAKE_SYSTEM_NAME MATCHES "Linux|Android|FreeBSD|NetBSD|AIX" ) list(APPEND EXTRA_PLUGINS lldbPluginProcessPOSIX) endif() diff --git a/lldb/source/Initialization/SystemInitializerCommon.cpp b/lldb/source/Initialization/SystemInitializerCommon.cpp index 1a172a95aa147..4b01442a94bac 100644 --- a/lldb/source/Initialization/SystemInitializerCommon.cpp +++ b/lldb/source/Initialization/SystemInitializerCommon.cpp @@ -19,7 +19,7 @@ #include "lldb/Version/Version.h" #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ - defined(__OpenBSD__) + defined(__OpenBSD__) || defined(__AIX__) #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #endif @@ -79,7 +79,7 @@ llvm::Error SystemInitializerCommon::Initialize() { process_gdb_remote::ProcessGDBRemoteLog::Initialize(); #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ - defined(__OpenBSD__) + defined(__OpenBSD__) || defined(__AIX__) ProcessPOSIXLog::Initialize(); #endif #if defined(_WIN32) diff --git a/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.cpp b/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.cpp index eac058701313b..feb0d7c0e09be 100644 --- a/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.cpp +++ b/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.cpp @@ -156,6 +156,9 @@ bool ABISysV_ppc64::PrepareTrivialCall(Thread &thread, addr_t sp, if (!reg_ctx->WriteRegisterFromUnsigned(r12_reg_info, func_addr)) return false; +#if defined(__AIX__) + assert(0); +#else // Read TOC pointer value. reg_value = reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0); @@ -171,6 +174,132 @@ bool ABISysV_ppc64::PrepareTrivialCall(Thread &thread, addr_t sp, (uint64_t)reg_value); if (!process_sp->WritePointerToMemory(sp + stack_offset, reg_value, error)) return false; +#endif + + // Read the current SP value. + reg_value = reg_ctx->ReadRegisterAsUnsigned(sp_reg_info, 0); + + // Save current SP onto the stack. + LLDB_LOGF(log, "Writing SP at SP(0x%" PRIx64 ")+0: 0x%" PRIx64, (uint64_t)sp, + (uint64_t)reg_value); + if (!process_sp->WritePointerToMemory(sp, reg_value, error)) + return false; + + // %r1 is set to the actual stack value. + LLDB_LOGF(log, "Writing SP: 0x%" PRIx64, (uint64_t)sp); + + if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_info, sp)) + return false; + + // %pc is set to the address of the called function. + + LLDB_LOGF(log, "Writing IP: 0x%" PRIx64, (uint64_t)func_addr); + + if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_info, func_addr)) + return false; + + return true; +} + +bool ABISysV_ppc64::PrepareTrivialCall(Thread &thread, addr_t sp, + addr_t func_addr, addr_t toc_addr, + addr_t return_addr, + llvm::ArrayRef args) const { + Log *log = GetLog(LLDBLog::Expressions); + + if (log) { + StreamString s; + s.Printf("ABISysV_ppc64::PrepareTrivialCall (tid = 0x%" PRIx64 + ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64 + ", return_addr = 0x%" PRIx64, + thread.GetID(), (uint64_t)sp, (uint64_t)func_addr, + (uint64_t)return_addr); + + for (size_t i = 0; i < args.size(); ++i) + s.Printf(", arg%" PRIu64 " = 0x%" PRIx64, static_cast(i + 1), + args[i]); + s.PutCString(")"); + log->PutString(s.GetString()); + } + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + if (!reg_ctx) + return false; + + const RegisterInfo *reg_info = nullptr; + + if (args.size() > 8) // TODO handle more than 8 arguments + return false; + + for (size_t i = 0; i < args.size(); ++i) { + reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_ARG1 + i); + LLDB_LOGF(log, "About to write arg%" PRIu64 " (0x%" PRIx64 ") into %s", + static_cast(i + 1), args[i], reg_info->name); + if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i])) + return false; + } + + // First, align the SP + + LLDB_LOGF(log, "16-byte aligning SP: 0x%" PRIx64 " to 0x%" PRIx64, + (uint64_t)sp, (uint64_t)(sp & ~0xfull)); + + sp &= ~(0xfull); // 16-byte alignment + + sp -= 544; // allocate frame to save TOC, RA and SP. + + Status error; + uint64_t reg_value; + const RegisterInfo *pc_reg_info = + reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + const RegisterInfo *sp_reg_info = + reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + ProcessSP process_sp(thread.GetProcess()); + const RegisterInfo *lr_reg_info = + reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA); + const RegisterInfo *r2_reg_info = reg_ctx->GetRegisterInfoAtIndex(2); + const RegisterInfo *r12_reg_info = reg_ctx->GetRegisterInfoAtIndex(12); + + // Save return address onto the stack. + LLDB_LOGF(log, + "Pushing the return address onto the stack: 0x%" PRIx64 + "(+16): 0x%" PRIx64, + (uint64_t)sp, (uint64_t)return_addr); + if (!process_sp->WritePointerToMemory(sp + 16, return_addr, error)) + return false; + + // Write the return address to link register. + LLDB_LOGF(log, "Writing LR: 0x%" PRIx64, (uint64_t)return_addr); + if (!reg_ctx->WriteRegisterFromUnsigned(lr_reg_info, return_addr)) + return false; + + // Write target address to %r12 register. + LLDB_LOGF(log, "Writing R12: 0x%" PRIx64, (uint64_t)func_addr); + if (!reg_ctx->WriteRegisterFromUnsigned(r12_reg_info, func_addr)) + return false; + +#if defined(__AIX__) + LLDB_LOGF(log, "Writing R2: 0x%" PRIx64, (uint64_t)toc_addr); + if (!reg_ctx->WriteRegisterFromUnsigned(r2_reg_info, toc_addr)) + return false; +#else + // Read TOC pointer value. + reg_value = reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0); + + // Write TOC pointer onto the stack. + uint64_t stack_offset; + if (GetByteOrder() == lldb::eByteOrderLittle) + stack_offset = 24; + else + stack_offset = 40; + + LLDB_LOGF(log, "Writing R2 (TOC) at SP(0x%" PRIx64 ")+%d: 0x%" PRIx64, + (uint64_t)(sp + stack_offset), (int)stack_offset, + (uint64_t)reg_value); + if (!process_sp->WritePointerToMemory(sp + stack_offset, reg_value, error)) + return false; +#endif // Read the current SP value. reg_value = reg_ctx->ReadRegisterAsUnsigned(sp_reg_info, 0); @@ -641,7 +770,7 @@ class ReturnValueExtractor { DataExtractor de(&raw_data, sizeof(raw_data), m_byte_order, m_addr_size); - offset_t offset = 0; + lldb::offset_t offset = 0; std::optional byte_size = type.GetByteSize(m_process_sp.get()); if (!byte_size) return {}; diff --git a/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.h b/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.h index bfa96cc0df703..d752a8ded9748 100644 --- a/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.h +++ b/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.h @@ -23,6 +23,12 @@ class ABISysV_ppc64 : public lldb_private::RegInfoBasedABI { lldb::addr_t returnAddress, llvm::ArrayRef args) const override; + bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t tocAddress, + lldb::addr_t returnAddress, + llvm::ArrayRef args) const override; + bool GetArgumentValues(lldb_private::Thread &thread, lldb_private::ValueList &values) const override; diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/CMakeLists.txt b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/CMakeLists.txt new file mode 100644 index 0000000000000..02fe0d617955a --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/CMakeLists.txt @@ -0,0 +1,11 @@ +add_definitions("-D_ALL_SOURCE") + +add_lldb_library(lldbPluginDynamicLoaderAIXDYLD PLUGIN + DynamicLoaderAIXDYLD.cpp + + LINK_LIBS + lldbCore + lldbTarget + LINK_COMPONENTS + Support + ) diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp new file mode 100644 index 0000000000000..62663974134b0 --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp @@ -0,0 +1,272 @@ +//===-- DynamicLoaderAIXDYLD.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 "DynamicLoaderAIXDYLD.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadPlanStepInstruction.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#if defined(__AIX__) +#include +#endif + +/*#include "llvm/ADT/Triple.h" +*/ + +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(DynamicLoaderAIXDYLD) + +DynamicLoaderAIXDYLD::DynamicLoaderAIXDYLD(Process *process) + : DynamicLoader(process) {} + +DynamicLoaderAIXDYLD::~DynamicLoaderAIXDYLD() = default; + +void DynamicLoaderAIXDYLD::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); +} + +void DynamicLoaderAIXDYLD::Terminate() {} + +llvm::StringRef DynamicLoaderAIXDYLD::GetPluginDescriptionStatic() { + return "Dynamic loader plug-in that watches for shared library " + "loads/unloads in AIX processes."; +} + +DynamicLoader *DynamicLoaderAIXDYLD::CreateInstance(Process *process, + bool force) { + bool should_create = force; + if (!should_create) { + const llvm::Triple &triple_ref = + process->GetTarget().GetArchitecture().GetTriple(); + if (triple_ref.getOS() == llvm::Triple::AIX) + should_create = true; + } + + if (should_create) + return new DynamicLoaderAIXDYLD(process); + + return nullptr; +} + +void DynamicLoaderAIXDYLD::OnLoadModule(lldb::ModuleSP module_sp, + const ModuleSpec module_spec, + lldb::addr_t module_addr) { + + // Resolve the module unless we already have one. + if (!module_sp) { + Status error; + module_sp = m_process->GetTarget().GetOrCreateModule(module_spec, + true /* notify */, &error); + if (error.Fail()) + return; + } + + m_loaded_modules[module_sp] = module_addr; + UpdateLoadedSectionsCommon(module_sp, module_addr, false); + ModuleList module_list; + module_list.Append(module_sp); + m_process->GetTarget().ModulesDidLoad(module_list); +} + +void DynamicLoaderAIXDYLD::OnUnloadModule(lldb::addr_t module_addr) { + Address resolved_addr; + if (!m_process->GetTarget().ResolveLoadAddress(module_addr, resolved_addr)) + return; + + ModuleSP module_sp = resolved_addr.GetModule(); + if (module_sp) { + m_loaded_modules.erase(module_sp); + UnloadSectionsCommon(module_sp); + ModuleList module_list; + module_list.Append(module_sp); + m_process->GetTarget().ModulesDidUnload(module_list, false); + } +} + +lldb::addr_t DynamicLoaderAIXDYLD::GetLoadAddress(ModuleSP executable) { + // First, see if the load address is already cached. + auto it = m_loaded_modules.find(executable); + if (it != m_loaded_modules.end() && it->second != LLDB_INVALID_ADDRESS) + return it->second; + + lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; + + // Second, try to get it through the process plugins. For a remote process, + // the remote platform will be responsible for providing it. + FileSpec file_spec(executable->GetPlatformFileSpec()); + bool is_loaded = false; + Status status = + m_process->GetFileLoadAddress(file_spec, is_loaded, load_addr); + // Servers other than lldb server could respond with a bogus address. + if (status.Success() && is_loaded && load_addr != LLDB_INVALID_ADDRESS) { + m_loaded_modules[executable] = load_addr; + return load_addr; + } + + //// Hack to try set breakpoint + //Breakpoint *dyld_break = m_process->GetTarget().CreateBreakpoint(0x100000638, true, false).get(); + //dyld_break->SetCallback(DynamicLoaderAIXDYLD::NotifyBreakpointHit, this, true); + //dyld_break->SetBreakpointKind("hack-debug"); + + return LLDB_INVALID_ADDRESS; +} + +bool DynamicLoaderAIXDYLD::NotifyBreakpointHit( + void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, + lldb::user_id_t break_loc_id) { +} + +void DynamicLoaderAIXDYLD::DidAttach() { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); + + ModuleSP executable = GetTargetExecutable(); + + if (!executable.get()) + return; + + // Try to fetch the load address of the file from the process, since there + // could be randomization of the load address. + lldb::addr_t load_addr = GetLoadAddress(executable); + if (load_addr == LLDB_INVALID_ADDRESS) + return; + + // Request the process base address. + lldb::addr_t image_base = m_process->GetImageInfoAddress(); + if (image_base == load_addr) + return; + + // Rebase the process's modules if there is a mismatch. + UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_addr, false); + + ModuleList module_list; + module_list.Append(executable); + m_process->GetTarget().ModulesDidLoad(module_list); + auto error = m_process->LoadModules(); + LLDB_LOG_ERROR(log, std::move(error), "failed to load modules: {0}"); + +#if defined(__AIX__) + // Get struct ld_xinfo (FIXME) + struct ld_xinfo ldinfo[64]; + Status status = m_process->GetLDXINFO(&(ldinfo[0])); + if (status.Fail()) { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOG(log, "LDXINFO failed: {0}", status); + return; + } + struct ld_xinfo *ptr = &(ldinfo[0]); + bool skip_current = true; + while (ptr != nullptr) { + char *pathName = (char *)ptr + ptr->ldinfo_filename; + char *memberName = pathName + (strlen(pathName) + 1); + if (!skip_current) { + // FIXME: buffer size + char pathWithMember[128] = {0}; + if (strlen(memberName) > 0) { + sprintf(pathWithMember, "%s(%s)", pathName, memberName); + } else { + sprintf(pathWithMember, "%s", pathName); + } + FileSpec file(pathWithMember); + ModuleSpec module_spec(file, m_process->GetTarget().GetArchitecture()); + if (ModuleSP module_sp = m_process->GetTarget().GetOrCreateModule(module_spec, true /* notify */)) { + UpdateLoadedSectionsByType(module_sp, LLDB_INVALID_ADDRESS, (lldb::addr_t)ptr->ldinfo_textorg, false, 1); + UpdateLoadedSectionsByType(module_sp, LLDB_INVALID_ADDRESS, (lldb::addr_t)ptr->ldinfo_dataorg, false, 2); + // FIXME: .tdata, .bss + } + } else { + skip_current = false; + } + if (ptr->ldinfo_next == 0) { + ptr = nullptr; + } else { + ptr = (struct ld_xinfo *)((char *)ptr + ptr->ldinfo_next); + } + } +#endif +} + +void DynamicLoaderAIXDYLD::DidLaunch() { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); + + ModuleSP executable = GetTargetExecutable(); + if (!executable.get()) + return; + + lldb::addr_t load_addr = GetLoadAddress(executable); + if (load_addr != LLDB_INVALID_ADDRESS) { + // Update the loaded sections so that the breakpoints can be resolved. + UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_addr, false); + + ModuleList module_list; + module_list.Append(executable); + m_process->GetTarget().ModulesDidLoad(module_list); + auto error = m_process->LoadModules(); + LLDB_LOG_ERROR(log, std::move(error), "failed to load modules: {0}"); + } + +#if defined(__AIX__) + // Get struct ld_xinfo (FIXME) + struct ld_xinfo ldinfo[64]; + Status status = m_process->GetLDXINFO(&(ldinfo[0])); + if (status.Fail()) { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOG(log, "LDXINFO failed: {0}", status); + return; + } + struct ld_xinfo *ptr = &(ldinfo[0]); + bool skip_current = true; + while (ptr != nullptr) { + char *pathName = (char *)ptr + ptr->ldinfo_filename; + char *memberName = pathName + (strlen(pathName) + 1); + if (!skip_current) { + // FIXME: buffer size + char pathWithMember[128] = {0}; + if (strlen(memberName) > 0) { + sprintf(pathWithMember, "%s(%s)", pathName, memberName); + } else { + sprintf(pathWithMember, "%s", pathName); + } + FileSpec file(pathWithMember); + ModuleSpec module_spec(file, m_process->GetTarget().GetArchitecture()); + if (ModuleSP module_sp = m_process->GetTarget().GetOrCreateModule(module_spec, true /* notify */)) { + UpdateLoadedSectionsByType(module_sp, LLDB_INVALID_ADDRESS, (lldb::addr_t)ptr->ldinfo_textorg, false, 1); + UpdateLoadedSectionsByType(module_sp, LLDB_INVALID_ADDRESS, (lldb::addr_t)ptr->ldinfo_dataorg, false, 2); + // FIXME: .tdata, .bss + } + } else { + skip_current = false; + } + if (ptr->ldinfo_next == 0) { + ptr = nullptr; + } else { + ptr = (struct ld_xinfo *)((char *)ptr + ptr->ldinfo_next); + } + } +#endif +} + +Status DynamicLoaderAIXDYLD::CanLoadImage() { return Status(); } + +ThreadPlanSP +DynamicLoaderAIXDYLD::GetStepThroughTrampolinePlan(Thread &thread, + bool stop) { + //FIXME + return ThreadPlanSP(); +} diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h new file mode 100644 index 0000000000000..ae4b7aca66dcc --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h @@ -0,0 +1,55 @@ +//===-- DynamicLoaderAIXDYLD.h ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_DYNAMICLOADER_AIX_DYLD_DYNAMICLOADERAIXDYLD_H +#define LLDB_SOURCE_PLUGINS_DYNAMICLOADER_AIX_DYLD_DYNAMICLOADERAIXDYLD_H + +#include "lldb/Target/DynamicLoader.h" +#include "lldb/lldb-forward.h" + +#include + +namespace lldb_private { + +class DynamicLoaderAIXDYLD : public DynamicLoader { +public: + DynamicLoaderAIXDYLD(Process *process); + + ~DynamicLoaderAIXDYLD() override; + + static void Initialize(); + static void Terminate(); + static llvm::StringRef GetPluginNameStatic() { return "windows-dyld"; } + static llvm::StringRef GetPluginDescriptionStatic(); + + static DynamicLoader *CreateInstance(Process *process, bool force); + + void OnLoadModule(lldb::ModuleSP module_sp, const ModuleSpec module_spec, + lldb::addr_t module_addr); + void OnUnloadModule(lldb::addr_t module_addr); + + void DidAttach() override; + void DidLaunch() override; + Status CanLoadImage() override; + lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread, + bool stop) override; + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + static bool NotifyBreakpointHit(void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id); + +protected: + lldb::addr_t GetLoadAddress(lldb::ModuleSP executable); + +private: + std::map m_loaded_modules; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_DYNAMICLOADER_AIX_DYLD_DYNAMICLOADERWAIXDYLD_H diff --git a/lldb/source/Plugins/DynamicLoader/CMakeLists.txt b/lldb/source/Plugins/DynamicLoader/CMakeLists.txt index 30607159acdc0..4f3fb693faae1 100644 --- a/lldb/source/Plugins/DynamicLoader/CMakeLists.txt +++ b/lldb/source/Plugins/DynamicLoader/CMakeLists.txt @@ -5,4 +5,5 @@ add_subdirectory(POSIX-DYLD) add_subdirectory(Static) add_subdirectory(Hexagon-DYLD) add_subdirectory(Windows-DYLD) +add_subdirectory(AIX-DYLD) add_subdirectory(wasm-DYLD) diff --git a/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp b/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp index 20e5652c65bf8..26abea0fdd24d 100644 --- a/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp +++ b/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp @@ -256,7 +256,7 @@ DynamicLoaderDarwinKernel::SearchForKernelWithDebugHints(Process *process) { if (process->ReadMemoryFromInferior (kernel_addresses_64[i], uval, 8, read_err) == 8) { DataExtractor data (&uval, 8, process->GetByteOrder(), process->GetAddressByteSize()); - offset_t offset = 0; + lldb::offset_t offset = 0; uint64_t addr = data.GetU64 (&offset); if (CheckForKernelImageAtAddress(addr, process).IsValid()) { return addr; @@ -270,7 +270,7 @@ DynamicLoaderDarwinKernel::SearchForKernelWithDebugHints(Process *process) { if (process->ReadMemoryFromInferior (kernel_addresses_32[i], uval, 4, read_err) == 4) { DataExtractor data (&uval, 4, process->GetByteOrder(), process->GetAddressByteSize()); - offset_t offset = 0; + lldb::offset_t offset = 0; uint32_t addr = data.GetU32 (&offset); if (CheckForKernelImageAtAddress(addr, process).IsValid()) { return addr; diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index 3863b6b3520db..624848dee6ec3 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -1151,7 +1151,7 @@ DynamicLoaderDarwin::GetThreadLocalData(const lldb::ModuleSP module_sp, // TLS data for the pthread_key on a specific thread yet. If we have we // can re-use it since its location will not change unless the process // execs. - const tid_t tid = thread_sp->GetID(); + const lldb::tid_t tid = thread_sp->GetID(); auto tid_pos = m_tid_to_tls_map.find(tid); if (tid_pos != m_tid_to_tls_map.end()) { auto tls_pos = tid_pos->second.find(key); diff --git a/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.cpp b/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.cpp index 3035c51341778..d14ae2daeb47d 100644 --- a/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.cpp +++ b/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.cpp @@ -146,7 +146,25 @@ EmulateInstructionPPC64::GetOpcodeForInstruction(uint32_t opcode) { {0xfc000000, 0x38000000, &EmulateInstructionPPC64::EmulateADDI, "addi RT, RA, SI"}, {0xfc000003, 0xe8000000, &EmulateInstructionPPC64::EmulateLD, - "ld RT, DS(RA)"}}; + "ld RT, DS(RA)"}, +// {0xffff0003, 0x40820000, &EmulateInstructionPPC64::EmulateBNE, +// "bne TARGET"}, + {0xfc000002, 0x48000000, &EmulateInstructionPPC64::EmulateB, + "b TARGET"}, + {0xfc000003, 0x48000002, &EmulateInstructionPPC64::EmulateBA, + "ba TARGET"}, + {0xfc000003, 0x48000003, &EmulateInstructionPPC64::EmulateBLA, + "bla TARGET"}, + {0xfc000002, 0x40000000, &EmulateInstructionPPC64::EmulateBC, + "bc BO,BI,TARGET"}, + {0xfc000002, 0x40000002, &EmulateInstructionPPC64::EmulateBCA, + "bca BO,BI,TARGET"}, + {0xfc0007fe, 0x4c000020, &EmulateInstructionPPC64::EmulateBCLR, + "bclr BO,BI,BH"}, + {0xfc0007fe, 0x4c000420, &EmulateInstructionPPC64::EmulateBCCTR, + "bcctr BO,BI,BH"}, + {0xfc0007fe, 0x4c000460, &EmulateInstructionPPC64::EmulateBCTAR, + "bctar BO,BI,BH"}}; static const size_t k_num_ppc_opcodes = std::size(g_opcodes); for (size_t i = 0; i < k_num_ppc_opcodes; ++i) { @@ -169,12 +187,13 @@ bool EmulateInstructionPPC64::EvaluateInstruction(uint32_t evaluate_options) { bool success = false; - uint32_t orig_pc_value = 0; + uint64_t orig_pc_value = 0; if (auto_advance_pc) { orig_pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); if (!success) return false; + LLDB_LOG(GetLog(LLDBLog::Unwind), "orig_pc_value:{0}", orig_pc_value); } // Call the Emulate... function. @@ -183,11 +202,13 @@ bool EmulateInstructionPPC64::EvaluateInstruction(uint32_t evaluate_options) { return false; if (auto_advance_pc) { - uint32_t new_pc_value = + uint64_t new_pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); if (!success) return false; + LLDB_LOG(GetLog(LLDBLog::Unwind), "new_pc_value:{0}", new_pc_value); + if (new_pc_value == orig_pc_value) { EmulateInstruction::Context context; context.type = eContextAdvancePC; @@ -389,5 +410,174 @@ bool EmulateInstructionPPC64::EmulateADDI(uint32_t opcode) { return false; WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_r1_ppc64le, r1 + si_val); LLDB_LOG(log, "EmulateADDI: success!"); + + // FIX the next-pc + uint64_t pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); + uint64_t next_pc = pc_value + 4; + ctx.type = eContextAdjustPC; + WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_pc_ppc64le, next_pc); + + return true; +} + +bool EmulateInstructionPPC64::EmulateBC(uint32_t opcode) { + // FIXME:32bit M + uint32_t M = 0; + uint32_t target32 = Bits32(opcode, 15, 2) << 2; + uint64_t target = (uint64_t)target32 + ((target32 & 0x8000) ? 0xffffffffffff0000UL : 0); + uint32_t BO = Bits32(opcode, 25, 21); + bool success; + uint64_t ctr_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_ctr_ppc64le, 0, &success); + if ((~BO) & (1U << 2)) + ctr_value = ctr_value - 1; + bool ctr_ok = (bool)(BO & (1U << 2)) | ((bool)(ctr_value != 0) ^ (bool)(BO & (1U << 1))); + uint64_t cr_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_cr_ppc64le, 0, &success); + uint32_t BI = Bits32(opcode, 20, 16); + bool cond_ok = (bool)(BO & (1U << 4)) | (bool)(((cr_value >> (63 - (BI + 32))) & 1U) == ((BO >> 3) & 1U)); + + uint64_t pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); + uint64_t next_pc = pc_value + 4; + if (ctr_ok & cond_ok) + next_pc = pc_value + target; + + Context ctx; + ctx.type = eContextAdjustPC; + WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_pc_ppc64le, next_pc); + Log *log = GetLog(LLDBLog::Unwind); + LLDB_LOG(log, "EmulateBC: success!"); + return true; +} + +bool EmulateInstructionPPC64::EmulateBCA(uint32_t opcode) { + // FIXME:32bit M + uint32_t M = 0; + uint32_t target32 = Bits32(opcode, 15, 2) << 2; + uint64_t target = (uint64_t)target32 + ((target32 & 0x8000) ? 0xffffffffffff0000UL : 0); + uint32_t BO = Bits32(opcode, 25, 21); + bool success; + uint64_t ctr_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_ctr_ppc64le, 0, &success); + if ((~BO) & (1U << 2)) + ctr_value = ctr_value - 1; + bool ctr_ok = (bool)(BO & (1U << 2)) | ((bool)(ctr_value != 0) ^ (bool)(BO & (1U << 1))); + uint64_t cr_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_cr_ppc64le, 0, &success); + uint32_t BI = Bits32(opcode, 20, 16); + bool cond_ok = (bool)(BO & (1U << 4)) | (bool)(((cr_value >> (63 - (BI + 32))) & 1U) == ((BO >> 3) & 1U)); + + uint64_t pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); + uint64_t next_pc = pc_value + 4; + if (ctr_ok & cond_ok) + next_pc = target; + + Context ctx; + ctx.type = eContextAdjustPC; + WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_pc_ppc64le, next_pc); + Log *log = GetLog(LLDBLog::Unwind); + LLDB_LOG(log, "EmulateBCA: success!"); + return true; +} + +bool EmulateInstructionPPC64::EmulateBCLR(uint32_t opcode) { + // FIXME:32bit M + uint32_t M = 0; + uint32_t BO = Bits32(opcode, 25, 21); + bool success; + uint64_t ctr_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_ctr_ppc64le, 0, &success); + if ((~BO) & (1U << 2)) + ctr_value = ctr_value - 1; + bool ctr_ok = (bool)(BO & (1U << 2)) | ((bool)(ctr_value != 0) ^ (bool)(BO & (1U << 1))); + uint64_t cr_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_cr_ppc64le, 0, &success); + uint32_t BI = Bits32(opcode, 20, 16); + bool cond_ok = (bool)(BO & (1U << 4)) | (bool)(((cr_value >> (63 - (BI + 32))) & 1U) == ((BO >> 3) & 1U)); + + uint64_t pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); + uint64_t next_pc = pc_value + 4; + if (ctr_ok & cond_ok) { + next_pc = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_lr_ppc64le, 0, &success); + next_pc &= ~((1UL << 2) - 1); + } + + Context ctx; + ctx.type = eContextAdjustPC; + WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_pc_ppc64le, next_pc); + Log *log = GetLog(LLDBLog::Unwind); + LLDB_LOG(log, "EmulateBCLR: success!"); + return true; +} + +bool EmulateInstructionPPC64::EmulateBCCTR(uint32_t opcode) { + // FIXME:32bit M + uint32_t M = 0; + uint32_t BO = Bits32(opcode, 25, 21); + bool success; + uint64_t cr_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_cr_ppc64le, 0, &success); + uint32_t BI = Bits32(opcode, 20, 16); + bool cond_ok = (bool)(BO & (1U << 4)) | (bool)(((cr_value >> (63 - (BI + 32))) & 1U) == ((BO >> 3) & 1U)); + + Log *log = GetLog(LLDBLog::Unwind); + uint64_t pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); + uint64_t next_pc = pc_value + 4; + if (cond_ok) { + next_pc = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_ctr_ppc64le, 0, &success); + next_pc &= ~((1UL << 2) - 1); + if (next_pc < 0x4000000) { + LLDB_LOGF(log, "EmulateBCCTR: next address %lx out of range, emulate by goto LR!"); + next_pc = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_lr_ppc64le, 0, &success); + } + } + + Context ctx; + ctx.type = eContextAdjustPC; + WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_pc_ppc64le, next_pc); + LLDB_LOG(log, "EmulateBCCTR: success!"); + return true; +} + +bool EmulateInstructionPPC64::EmulateBCTAR(uint32_t opcode) { + // Not supported yet. + LLDB_LOG(GetLog(LLDBLog::Unwind), "EmulateBCTAR: not supported!"); + assert(0); + return false; +} + +bool EmulateInstructionPPC64::EmulateB(uint32_t opcode) { + uint32_t target32 = Bits32(opcode, 25, 2) << 2; + uint64_t target = (uint64_t)target32 + ((target32 & 0x2000000) ? 0xfffffffffc000000UL : 0); + + bool success; + uint64_t pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); + uint64_t next_pc = pc_value + target; + + Context ctx; + ctx.type = eContextAdjustPC; + WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_pc_ppc64le, next_pc); + Log *log = GetLog(LLDBLog::Unwind); + LLDB_LOG(log, "EmulateB: success!"); + return true; +} + +bool EmulateInstructionPPC64::EmulateBA(uint32_t opcode) { + Log *log = GetLog(LLDBLog::Unwind); + + bool success; + uint64_t next_pc = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_lr_ppc64le, 0, &success); + + Context ctx; + ctx.type = eContextAdjustPC; + WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_pc_ppc64le, next_pc); + LLDB_LOG(log, "EmulateBA: emulate by branch to lr!"); + return true; +} + +bool EmulateInstructionPPC64::EmulateBLA(uint32_t opcode) { + Log *log = GetLog(LLDBLog::Unwind); + + bool success; + uint64_t pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); + uint64_t next_pc = pc_value + 4; + + Context ctx; + ctx.type = eContextAdjustPC; + WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_pc_ppc64le, next_pc); + LLDB_LOG(log, "EmulateBLA: emulate by branch to lr!"); return true; } diff --git a/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h b/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h index a9424f16b0ad0..1576c9700e557 100644 --- a/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h +++ b/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h @@ -39,6 +39,12 @@ class EmulateInstructionPPC64 : public EmulateInstruction { return true; case eInstructionTypePCModifying: +#if defined(__AIX__) + return true; +#else + return false; +#endif + case eInstructionTypeAll: return false; } @@ -84,6 +90,14 @@ class EmulateInstructionPPC64 : public EmulateInstruction { bool EmulateSTD(uint32_t opcode); bool EmulateOR(uint32_t opcode); bool EmulateADDI(uint32_t opcode); + bool EmulateB(uint32_t opcode); + bool EmulateBA(uint32_t opcode); + bool EmulateBLA(uint32_t opcode); + bool EmulateBC(uint32_t opcode); + bool EmulateBCA(uint32_t opcode); + bool EmulateBCLR(uint32_t opcode); + bool EmulateBCCTR(uint32_t opcode); + bool EmulateBCTAR(uint32_t opcode); }; } // namespace lldb_private diff --git a/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/InstrumentationRuntimeMainThreadChecker.cpp b/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/InstrumentationRuntimeMainThreadChecker.cpp index b7cd2b1ac6bf6..876e74056face 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/InstrumentationRuntimeMainThreadChecker.cpp +++ b/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/InstrumentationRuntimeMainThreadChecker.cpp @@ -261,7 +261,7 @@ InstrumentationRuntimeMainThreadChecker::GetBacktracesFromExtendedStopInfo( StructuredData::ObjectSP thread_id_obj = info->GetObjectForDotSeparatedPath("tid"); - tid_t tid = thread_id_obj ? thread_id_obj->GetUnsignedIntegerValue() : 0; + lldb::tid_t tid = thread_id_obj ? thread_id_obj->GetUnsignedIntegerValue() : 0; // We gather symbolication addresses above, so no need for HistoryThread to // try to infer the call addresses. diff --git a/lldb/source/Plugins/InstrumentationRuntime/TSan/InstrumentationRuntimeTSan.cpp b/lldb/source/Plugins/InstrumentationRuntime/TSan/InstrumentationRuntimeTSan.cpp index b2781aa5e7db1..7a827a3ea76f9 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/TSan/InstrumentationRuntimeTSan.cpp +++ b/lldb/source/Plugins/InstrumentationRuntime/TSan/InstrumentationRuntimeTSan.cpp @@ -770,13 +770,13 @@ std::string InstrumentationRuntimeTSan::GetLocationDescription( Sprintf("Location is a %ld-byte heap object at 0x%llx", size, addr); } } else if (type == "stack") { - tid_t tid = loc->GetAsDictionary() + lldb::tid_t tid = loc->GetAsDictionary() ->GetValueForKey("thread_id") ->GetUnsignedIntegerValue(); result = Sprintf("Location is stack of thread %d", tid); } else if (type == "tls") { - tid_t tid = loc->GetAsDictionary() + lldb::tid_t tid = loc->GetAsDictionary() ->GetValueForKey("thread_id") ->GetUnsignedIntegerValue(); @@ -948,7 +948,7 @@ static std::string GenerateThreadName(const std::string &path, if (path == "mops") { size_t size = o->GetObjectForDotSeparatedPath("size")->GetUnsignedIntegerValue(); - tid_t thread_id = + lldb::tid_t thread_id = o->GetObjectForDotSeparatedPath("thread_id")->GetUnsignedIntegerValue(); bool is_write = o->GetObjectForDotSeparatedPath("is_write")->GetBooleanValue(); @@ -979,7 +979,7 @@ static std::string GenerateThreadName(const std::string &path, } if (path == "threads") { - tid_t thread_id = + lldb::tid_t thread_id = o->GetObjectForDotSeparatedPath("thread_id")->GetUnsignedIntegerValue(); result = Sprintf("Thread %zu created", thread_id); } @@ -987,7 +987,7 @@ static std::string GenerateThreadName(const std::string &path, if (path == "locs") { std::string type = std::string( o->GetAsDictionary()->GetValueForKey("type")->GetStringValue()); - tid_t thread_id = + lldb::tid_t thread_id = o->GetObjectForDotSeparatedPath("thread_id")->GetUnsignedIntegerValue(); int fd = o->GetObjectForDotSeparatedPath("file_descriptor") ->GetSignedIntegerValue(); @@ -1007,7 +1007,7 @@ static std::string GenerateThreadName(const std::string &path, } if (path == "stacks") { - tid_t thread_id = + lldb::tid_t thread_id = o->GetObjectForDotSeparatedPath("thread_id")->GetUnsignedIntegerValue(); result = Sprintf("Thread %" PRIu64, thread_id); } @@ -1034,7 +1034,7 @@ static void AddThreadsForPath(const std::string &path, StructuredData::ObjectSP thread_id_obj = o->GetObjectForDotSeparatedPath("thread_os_id"); - tid_t tid = + lldb::tid_t tid = thread_id_obj ? thread_id_obj->GetUnsignedIntegerValue() : 0; ThreadSP new_thread_sp = diff --git a/lldb/source/Plugins/InstrumentationRuntime/UBSan/InstrumentationRuntimeUBSan.cpp b/lldb/source/Plugins/InstrumentationRuntime/UBSan/InstrumentationRuntimeUBSan.cpp index 1c58922e8d36c..de9719ad4a89e 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/UBSan/InstrumentationRuntimeUBSan.cpp +++ b/lldb/source/Plugins/InstrumentationRuntime/UBSan/InstrumentationRuntimeUBSan.cpp @@ -321,7 +321,7 @@ InstrumentationRuntimeUBSan::GetBacktracesFromExtendedStopInfo( StructuredData::ObjectSP thread_id_obj = info->GetObjectForDotSeparatedPath("tid"); - tid_t tid = thread_id_obj ? thread_id_obj->GetUnsignedIntegerValue() : 0; + lldb::tid_t tid = thread_id_obj ? thread_id_obj->GetUnsignedIntegerValue() : 0; // We gather symbolication addresses above, so no need for HistoryThread to // try to infer the call addresses. diff --git a/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp b/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp index 1688fb27430a7..690fb0d60a09a 100644 --- a/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp +++ b/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp @@ -194,6 +194,10 @@ void JITLoaderGDB::SetJITBreakpoint(lldb_private::ModuleList &module_list) { if (jit_addr == LLDB_INVALID_ADDRESS) return; +#if defined(__AIX__) + return; +#endif + m_jit_descriptor_addr = GetSymbolAddress( module_list, ConstString("__jit_debug_descriptor"), eSymbolTypeData); if (m_jit_descriptor_addr == LLDB_INVALID_ADDRESS) { diff --git a/lldb/source/Plugins/Language/ObjC/Cocoa.cpp b/lldb/source/Plugins/Language/ObjC/Cocoa.cpp index 341923108e321..fb5bc2c58e6fb 100644 --- a/lldb/source/Plugins/Language/ObjC/Cocoa.cpp +++ b/lldb/source/Plugins/Language/ObjC/Cocoa.cpp @@ -1227,6 +1227,7 @@ bool lldb_private::formatters::ObjCSELSummaryProvider( time_t lldb_private::formatters::GetOSXEpoch() { static time_t epoch = 0; if (!epoch) { +#if !defined(__AIX__) #ifndef _WIN32 tzset(); tm tm_epoch; @@ -1240,6 +1241,7 @@ time_t lldb_private::formatters::GetOSXEpoch() { tm_epoch.tm_gmtoff = 0; tm_epoch.tm_zone = nullptr; epoch = timegm(&tm_epoch); +#endif #endif } return epoch; diff --git a/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp b/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp index 6efd2516578ff..fe6c5a0544be3 100644 --- a/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp +++ b/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp @@ -107,7 +107,7 @@ static void CreateHistoryThreadFromValueObject(ProcessSP process_sp, return; int count = count_sp->GetValueAsUnsigned(0); - tid_t tid = tid_sp->GetValueAsUnsigned(0) + 1; + lldb::tid_t tid = tid_sp->GetValueAsUnsigned(0) + 1; if (count <= 0) return; diff --git a/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp index 7aa5b8d81890a..5ea55772c3aba 100644 --- a/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp +++ b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp @@ -8,7 +8,7 @@ #include "ObjectContainerBSDArchive.h" -#if defined(_WIN32) || defined(__ANDROID__) +#if defined(_WIN32) || defined(__ANDROID__) || defined(__AIX__) // Defines from ar, missing on Windows #define SARMAG 8 #define ARFMAG "`\n" diff --git a/lldb/source/Plugins/ObjectContainer/Big-Archive/CMakeLists.txt b/lldb/source/Plugins/ObjectContainer/Big-Archive/CMakeLists.txt new file mode 100644 index 0000000000000..612a36265b536 --- /dev/null +++ b/lldb/source/Plugins/ObjectContainer/Big-Archive/CMakeLists.txt @@ -0,0 +1,10 @@ +add_lldb_library(lldbPluginObjectContainerBigArchive PLUGIN + ObjectContainerBigArchive.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbSymbol + LINK_COMPONENTS + Support + ) diff --git a/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.cpp b/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.cpp new file mode 100644 index 0000000000000..050ad73f1d19a --- /dev/null +++ b/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.cpp @@ -0,0 +1,522 @@ +//===-- ObjectContainerBigArchive.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 "ObjectContainerBigArchive.h" + +#if defined(_WIN32) || defined(__ANDROID__) || defined(__AIX__) +// Defines from ar, missing on Windows +#define ARMAG "!\n" +#define SARMAG 8 +#define ARFMAG "`\n" + +typedef struct ar_hdr { + char ar_name[16]; + char ar_date[12]; + char ar_uid[6], ar_gid[6]; + char ar_mode[8]; + char ar_size[10]; + char ar_fmag[2]; +} ar_hdr; +#else +#include +#endif + +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/Timer.h" + +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Object/Archive.h" +#include "llvm/Support/Chrono.h" + +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(ObjectContainerBigArchive) + +ObjectContainerBigArchive::Object::Object() : ar_name() {} + +void ObjectContainerBigArchive::Object::Clear() { + ar_name.Clear(); + modification_time = 0; + uid = 0; + gid = 0; + mode = 0; + size = 0; + file_offset = 0; + file_size = 0; +} + +lldb::offset_t +ObjectContainerBigArchive::Object::Extract(const DataExtractor &data, + lldb::offset_t offset) { + size_t ar_name_len = 0; + std::string str; + char *err; + + // File header + // + // The common format is as follows. + // + // Offset Length Name Format + // 0 16 File name ASCII right padded with spaces (no spaces + // allowed in file name) + // 16 12 File mod Decimal as cstring right padded with + // spaces + // 28 6 Owner ID Decimal as cstring right padded with + // spaces + // 34 6 Group ID Decimal as cstring right padded with + // spaces + // 40 8 File mode Octal as cstring right padded with + // spaces + // 48 10 File byte size Decimal as cstring right padded with + // spaces + // 58 2 File magic 0x60 0x0A + + // Make sure there is enough data for the file header and bail if not + if (!data.ValidOffsetForDataOfSize(offset, 60)) + return LLDB_INVALID_OFFSET; + + str.assign((const char *)data.GetData(&offset, 16), 16); + if (llvm::StringRef(str).starts_with("#1/")) { + // If the name is longer than 16 bytes, or contains an embedded space then + // it will use this format where the length of the name is here and the + // name characters are after this header. + ar_name_len = strtoul(str.c_str() + 3, &err, 10); + } else { + // Strip off any trailing spaces. + const size_t last_pos = str.find_last_not_of(' '); + if (last_pos != std::string::npos) { + if (last_pos + 1 < 16) + str.erase(last_pos + 1); + } + ar_name.SetCString(str.c_str()); + } + + str.assign((const char *)data.GetData(&offset, 12), 12); + modification_time = strtoul(str.c_str(), &err, 10); + + str.assign((const char *)data.GetData(&offset, 6), 6); + uid = strtoul(str.c_str(), &err, 10); + + str.assign((const char *)data.GetData(&offset, 6), 6); + gid = strtoul(str.c_str(), &err, 10); + + str.assign((const char *)data.GetData(&offset, 8), 8); + mode = strtoul(str.c_str(), &err, 8); + + str.assign((const char *)data.GetData(&offset, 10), 10); + size = strtoul(str.c_str(), &err, 10); + + str.assign((const char *)data.GetData(&offset, 2), 2); + if (str == ARFMAG) { + if (ar_name_len > 0) { + const void *ar_name_ptr = data.GetData(&offset, ar_name_len); + // Make sure there was enough data for the string value and bail if not + if (ar_name_ptr == nullptr) + return LLDB_INVALID_OFFSET; + str.assign((const char *)ar_name_ptr, ar_name_len); + ar_name.SetCString(str.c_str()); + } + file_offset = offset; + file_size = size - ar_name_len; + return offset; + } + return LLDB_INVALID_OFFSET; +} + +ObjectContainerBigArchive::Archive::Archive(const lldb_private::ArchSpec &arch, + const llvm::sys::TimePoint<> &time, + lldb::offset_t file_offset, + lldb_private::DataExtractor &data) + : m_arch(arch), m_modification_time(time), m_file_offset(file_offset), + m_objects(), m_data(data) {} + +ObjectContainerBigArchive::Archive::~Archive() = default; + +size_t ObjectContainerBigArchive::Archive::ParseObjects() { + DataExtractor &data = m_data; + std::string str; + lldb::offset_t offset = 0; + str.assign((const char *)data.GetData(&offset, (sizeof(llvm::object::BigArchiveMagic) - 1)), + (sizeof(llvm::object::BigArchiveMagic) - 1)); + if (str == llvm::object::BigArchiveMagic) { + llvm::Error err = llvm::Error::success(); + llvm::object::BigArchive bigAr(llvm::MemoryBufferRef(toStringRef(m_data.GetData()), llvm::StringRef("")), err); + if (err) + return 0; + + for (const llvm::object::Archive::Child &child : bigAr.children(err)) { + if (err) + continue; + if (!child.getParent()) + continue; + Object obj; + obj.Clear(); + // FIXME: check errors + llvm::Expected childNameOrErr = child.getName(); + if (!childNameOrErr) + continue; + obj.ar_name.SetCString(childNameOrErr->str().c_str()); + llvm::Expected> lastModifiedOrErr = child.getLastModified(); + if (!lastModifiedOrErr) + continue; + obj.modification_time = (uint32_t)llvm::sys::toTimeT(*(lastModifiedOrErr)); + llvm::Expected getUIDOrErr = child.getUID(); + if (!getUIDOrErr) + continue; + obj.uid = (uint16_t)*getUIDOrErr; + llvm::Expected getGIDOrErr = child.getGID(); + if (!getGIDOrErr) + continue; + obj.gid = (uint16_t)*getGIDOrErr; + llvm::Expected getAccessModeOrErr = child.getAccessMode(); + if (!getAccessModeOrErr) + continue; + obj.mode = (uint16_t)*getAccessModeOrErr; + llvm::Expected getRawSizeOrErr = child.getRawSize(); + if (!getRawSizeOrErr) + continue; + obj.size = (uint32_t)*getRawSizeOrErr; + + obj.file_offset = (lldb::offset_t)child.getDataOffset(); + + llvm::Expected getSizeOrErr = child.getSize(); + if (!getSizeOrErr) + continue; + obj.file_size = (lldb::offset_t)*getSizeOrErr; + + size_t obj_idx = m_objects.size(); + m_objects.push_back(obj); + // Insert all of the C strings out of order for now... + m_object_name_to_index_map.Append(obj.ar_name, obj_idx); + } + if (err) + return 0; + + // Now sort all of the object name pointers + m_object_name_to_index_map.Sort(); + } + return m_objects.size(); +} + +ObjectContainerBigArchive::Object * +ObjectContainerBigArchive::Archive::FindObject( + ConstString object_name, const llvm::sys::TimePoint<> &object_mod_time) { + const ObjectNameToIndexMap::Entry *match = + m_object_name_to_index_map.FindFirstValueForName(object_name); + if (!match) + return nullptr; + if (object_mod_time == llvm::sys::TimePoint<>()) + return &m_objects[match->value]; + + const uint64_t object_modification_date = llvm::sys::toTimeT(object_mod_time); + if (m_objects[match->value].modification_time == object_modification_date) + return &m_objects[match->value]; + + const ObjectNameToIndexMap::Entry *next_match = + m_object_name_to_index_map.FindNextValueForName(match); + while (next_match) { + if (m_objects[next_match->value].modification_time == + object_modification_date) + return &m_objects[next_match->value]; + next_match = m_object_name_to_index_map.FindNextValueForName(next_match); + } + + return nullptr; +} + +ObjectContainerBigArchive::Archive::shared_ptr +ObjectContainerBigArchive::Archive::FindCachedArchive( + const FileSpec &file, const ArchSpec &arch, + const llvm::sys::TimePoint<> &time, lldb::offset_t file_offset) { + std::lock_guard guard(Archive::GetArchiveCacheMutex()); + shared_ptr archive_sp; + Archive::Map &archive_map = Archive::GetArchiveCache(); + Archive::Map::iterator pos = archive_map.find(file); + // Don't cache a value for "archive_map.end()" below since we might delete an + // archive entry... + while (pos != archive_map.end() && pos->first == file) { + bool match = true; + if (arch.IsValid() && + !pos->second->GetArchitecture().IsCompatibleMatch(arch)) + match = false; + else if (file_offset != LLDB_INVALID_OFFSET && + pos->second->GetFileOffset() != file_offset) + match = false; + if (match) { + if (pos->second->GetModificationTime() == time) { + return pos->second; + } else { + // We have a file at the same path with the same architecture whose + // modification time doesn't match. It doesn't make sense for us to + // continue to use this Big archive since we cache only the object info + // which consists of file time info and also the file offset and file + // size of any contained objects. Since this information is now out of + // date, we won't get the correct information if we go and extract the + // file data, so we should remove the old and outdated entry. + archive_map.erase(pos); + pos = archive_map.find(file); + continue; // Continue to next iteration so we don't increment pos + // below... + } + } + ++pos; + } + return archive_sp; +} + +ObjectContainerBigArchive::Archive::shared_ptr +ObjectContainerBigArchive::Archive::ParseAndCacheArchiveForFile( + const FileSpec &file, const ArchSpec &arch, + const llvm::sys::TimePoint<> &time, lldb::offset_t file_offset, + DataExtractor &data) { + shared_ptr archive_sp(new Archive(arch, time, file_offset, data)); + if (archive_sp) { + const size_t num_objects = archive_sp->ParseObjects(); + if (num_objects > 0) { + std::lock_guard guard( + Archive::GetArchiveCacheMutex()); + Archive::GetArchiveCache().insert(std::make_pair(file, archive_sp)); + } else { + archive_sp.reset(); + } + } + return archive_sp; +} + +ObjectContainerBigArchive::Archive::Map & +ObjectContainerBigArchive::Archive::GetArchiveCache() { + static Archive::Map g_archive_map; + return g_archive_map; +} + +std::recursive_mutex & +ObjectContainerBigArchive::Archive::GetArchiveCacheMutex() { + static std::recursive_mutex g_archive_map_mutex; + return g_archive_map_mutex; +} + +void ObjectContainerBigArchive::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + GetModuleSpecifications); +} + +void ObjectContainerBigArchive::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +ObjectContainer *ObjectContainerBigArchive::CreateInstance( + const lldb::ModuleSP &module_sp, DataBufferSP &data_sp, + lldb::offset_t data_offset, const FileSpec *file, + lldb::offset_t file_offset, lldb::offset_t length) { + ConstString object_name(module_sp->GetObjectName()); + if (!object_name) + return nullptr; + + if (data_sp) { + // We have data, which means this is the first 512 bytes of the file Check + // to see if the magic bytes match and if they do, read the entire table of + // contents for the archive and cache it + DataExtractor data; + data.SetData(data_sp, data_offset, length); + if (file && data_sp && ObjectContainerBigArchive::MagicBytesMatch(data)) { + LLDB_SCOPED_TIMERF( + "ObjectContainerBigArchive::CreateInstance (module = %s, file = " + "%p, file_offset = 0x%8.8" PRIx64 ", file_size = 0x%8.8" PRIx64 ")", + module_sp->GetFileSpec().GetPath().c_str(), + static_cast(file), static_cast(file_offset), + static_cast(length)); + + // Map the entire .a file to be sure that we don't lose any data if the + // file gets updated by a new build while this .a file is being used for + // debugging + DataBufferSP archive_data_sp = + FileSystem::Instance().CreateDataBuffer(*file, length, file_offset); + if (!archive_data_sp) + return nullptr; + + lldb::offset_t archive_data_offset = 0; + + Archive::shared_ptr archive_sp(Archive::FindCachedArchive( + *file, module_sp->GetArchitecture(), module_sp->GetModificationTime(), + file_offset)); + std::unique_ptr container_up( + new ObjectContainerBigArchive(module_sp, archive_data_sp, + archive_data_offset, file, file_offset, + length)); + + if (container_up) { + if (archive_sp) { + // We already have this archive in our cache, use it + container_up->SetArchive(archive_sp); + return container_up.release(); + } else if (container_up->ParseHeader()) + return container_up.release(); + } + } + } else { + // No data, just check for a cached archive + Archive::shared_ptr archive_sp(Archive::FindCachedArchive( + *file, module_sp->GetArchitecture(), module_sp->GetModificationTime(), + file_offset)); + if (archive_sp) { + std::unique_ptr container_up( + new ObjectContainerBigArchive(module_sp, data_sp, data_offset, file, + file_offset, length)); + + if (container_up) { + // We already have this archive in our cache, use it + container_up->SetArchive(archive_sp); + return container_up.release(); + } + } + } + return nullptr; +} + +bool ObjectContainerBigArchive::MagicBytesMatch(const DataExtractor &data) { + uint32_t offset = 0; + const char *armag = (const char *)data.PeekData(offset, (sizeof(llvm::object::BigArchiveMagic) - 1)); + if (armag && ::strncmp(armag, llvm::object::BigArchiveMagic, (sizeof(llvm::object::BigArchiveMagic) - 1)) == 0) + return true; + return false; +} + +ObjectContainerBigArchive::ObjectContainerBigArchive( + const lldb::ModuleSP &module_sp, DataBufferSP &data_sp, + lldb::offset_t data_offset, const lldb_private::FileSpec *file, + lldb::offset_t file_offset, lldb::offset_t size) + : ObjectContainer(module_sp, file, file_offset, size, data_sp, data_offset), + m_archive_sp() {} +void ObjectContainerBigArchive::SetArchive(Archive::shared_ptr &archive_sp) { + m_archive_sp = archive_sp; +} + +ObjectContainerBigArchive::~ObjectContainerBigArchive() = default; + +bool ObjectContainerBigArchive::ParseHeader() { + if (m_archive_sp.get() == nullptr) { + if (m_data.GetByteSize() > 0) { + ModuleSP module_sp(GetModule()); + if (module_sp) { + m_archive_sp = Archive::ParseAndCacheArchiveForFile( + m_file, module_sp->GetArchitecture(), + module_sp->GetModificationTime(), m_offset, m_data); + } + // Clear the m_data that contains the entire archive data and let our + // m_archive_sp hold onto the data. + m_data.Clear(); + } + } + return m_archive_sp.get() != nullptr; +} + +void ObjectContainerBigArchive::Object::Dump(Stream *s) const { + printf("name = \"%s\"\n", ar_name.GetCString()); + printf("mtime = 0x%8.8" PRIx32 "\n", modification_time); + printf("size = 0x%8.8" PRIx32 " (%" PRIu32 ")\n", size, size); + printf("file_offset = 0x%16.16" PRIx64 " (%" PRIu64 ")\n", file_offset, + file_offset); + printf("file_size = 0x%16.16" PRIx64 " (%" PRIu64 ")\n\n", file_size, + file_size); +} + +ObjectFileSP ObjectContainerBigArchive::GetObjectFile(const FileSpec *file) { + ModuleSP module_sp(GetModule()); + if (module_sp) { + if (module_sp->GetObjectName() && m_archive_sp) { + Object *object = m_archive_sp->FindObject( + module_sp->GetObjectName(), module_sp->GetObjectModificationTime()); + if (object) { + lldb::offset_t data_offset = object->file_offset; + return ObjectFile::FindPlugin( + module_sp, file, m_offset + object->file_offset, object->file_size, + m_archive_sp->GetData().GetSharedDataBuffer(), data_offset); + } + } + } + return ObjectFileSP(); +} + +size_t ObjectContainerBigArchive::GetModuleSpecifications( + const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, lldb::offset_t file_offset, + lldb::offset_t file_size, lldb_private::ModuleSpecList &specs) { + + // We have data, which means this is the first 512 bytes of the file Check to + // see if the magic bytes match and if they do, read the entire table of + // contents for the archive and cache it + DataExtractor data; + data.SetData(data_sp, data_offset, data_sp->GetByteSize()); + if (!file || !data_sp || !ObjectContainerBigArchive::MagicBytesMatch(data)) + return 0; + + const size_t initial_count = specs.GetSize(); + llvm::sys::TimePoint<> file_mod_time = FileSystem::Instance().GetModificationTime(file); + Archive::shared_ptr archive_sp( + Archive::FindCachedArchive(file, ArchSpec(), file_mod_time, file_offset)); + bool set_archive_arch = false; + if (!archive_sp) { + set_archive_arch = true; + data_sp = + FileSystem::Instance().CreateDataBuffer(file, file_size, file_offset); + if (data_sp) { + data.SetData(data_sp, 0, data_sp->GetByteSize()); + archive_sp = Archive::ParseAndCacheArchiveForFile( + file, ArchSpec(), file_mod_time, file_offset, data); + } + } + + if (archive_sp) { + const size_t num_objects = archive_sp->GetNumObjects(); + for (size_t idx = 0; idx < num_objects; ++idx) { + const Object *object = archive_sp->GetObjectAtIndex(idx); + if (object) { + const lldb::offset_t object_file_offset = + file_offset + object->file_offset; + if (object->file_offset < file_size && file_size > object_file_offset) { + if (ObjectFile::GetModuleSpecifications( + file, object_file_offset, file_size - object_file_offset, + specs)) { + ModuleSpec &spec = + specs.GetModuleSpecRefAtIndex(specs.GetSize() - 1); + llvm::sys::TimePoint<> object_mod_time( + std::chrono::seconds(object->modification_time)); + spec.GetObjectName() = object->ar_name; + spec.SetObjectOffset(object_file_offset); + spec.SetObjectSize(file_size - object_file_offset); + spec.GetObjectModificationTime() = object_mod_time; + } + } + } + } + } + const size_t end_count = specs.GetSize(); + size_t num_specs_added = end_count - initial_count; + if (set_archive_arch && num_specs_added > 0) { + // The archive was created but we didn't have an architecture so we need to + // set it + for (size_t i = initial_count; i < end_count; ++i) { + ModuleSpec module_spec; + if (specs.GetModuleSpecAtIndex(i, module_spec)) { + if (module_spec.GetArchitecture().IsValid()) { + archive_sp->SetArchitecture(module_spec.GetArchitecture()); + break; + } + } + } + } + return num_specs_added; +} diff --git a/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.h b/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.h new file mode 100644 index 0000000000000..ad9b814048a87 --- /dev/null +++ b/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.h @@ -0,0 +1,177 @@ +//===-- ObjectContainerBigArchive.h -----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_OBJECTCONTAINER_BIG_ARCHIVE_OBJECTCONTAINERBIGARCHIVE_H +#define LLDB_SOURCE_PLUGINS_OBJECTCONTAINER_BIG_ARCHIVE_OBJECTCONTAINERBIGARCHIVE_H + +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Symbol/ObjectContainer.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/FileSpec.h" + +#include "llvm/Support/Chrono.h" + +#include +#include +#include + +class ObjectContainerBigArchive : public lldb_private::ObjectContainer { +public: + ObjectContainerBigArchive(const lldb::ModuleSP &module_sp, + lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec *file, + lldb::offset_t offset, lldb::offset_t length); + + ~ObjectContainerBigArchive() override; + + // Static Functions + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "big-archive"; } + + static llvm::StringRef GetPluginDescriptionStatic() { + return "Big Archive object container reader."; + } + + static lldb_private::ObjectContainer * + CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, const lldb_private::FileSpec *file, + lldb::offset_t offset, lldb::offset_t length); + + static size_t GetModuleSpecifications(const lldb_private::FileSpec &file, + lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb_private::ModuleSpecList &specs); + + static bool MagicBytesMatch(const lldb_private::DataExtractor &data); + + // Member Functions + bool ParseHeader() override; + + size_t GetNumObjects() const override { + if (m_archive_sp) + return m_archive_sp->GetNumObjects(); + return 0; + } + + lldb::ObjectFileSP GetObjectFile(const lldb_private::FileSpec *file) override; + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + +protected: + struct Object { + Object(); + + void Clear(); + + lldb::offset_t Extract(const lldb_private::DataExtractor &data, + lldb::offset_t offset); + /// Object name in the archive. + lldb_private::ConstString ar_name; + + /// Object modification time in the archive. + uint32_t modification_time = 0; + + /// Object user id in the archive. + uint16_t uid = 0; + + /// Object group id in the archive. + uint16_t gid = 0; + + /// Object octal file permissions in the archive. + uint16_t mode = 0; + + /// Object size in bytes in the archive. + uint32_t size = 0; + + /// File offset in bytes from the beginning of the file of the object data. + lldb::offset_t file_offset = 0; + + /// Length of the object data. + lldb::offset_t file_size = 0; + + void Dump(lldb_private::Stream *s) const; + }; + + class Archive { + public: + typedef std::shared_ptr shared_ptr; + typedef std::multimap Map; + + Archive(const lldb_private::ArchSpec &arch, + const llvm::sys::TimePoint<> &mod_time, lldb::offset_t file_offset, + lldb_private::DataExtractor &data); + + ~Archive(); + + static Map &GetArchiveCache(); + + static std::recursive_mutex &GetArchiveCacheMutex(); + + static Archive::shared_ptr FindCachedArchive( + const lldb_private::FileSpec &file, const lldb_private::ArchSpec &arch, + const llvm::sys::TimePoint<> &mod_time, lldb::offset_t file_offset); + + static Archive::shared_ptr ParseAndCacheArchiveForFile( + const lldb_private::FileSpec &file, const lldb_private::ArchSpec &arch, + const llvm::sys::TimePoint<> &mod_time, lldb::offset_t file_offset, + lldb_private::DataExtractor &data); + + size_t GetNumObjects() const { return m_objects.size(); } + + const Object *GetObjectAtIndex(size_t idx) { + if (idx < m_objects.size()) + return &m_objects[idx]; + return nullptr; + } + + size_t ParseObjects(); + + Object *FindObject(lldb_private::ConstString object_name, + const llvm::sys::TimePoint<> &object_mod_time); + + lldb::offset_t GetFileOffset() const { return m_file_offset; } + + const llvm::sys::TimePoint<> &GetModificationTime() { + return m_modification_time; + } + + const lldb_private::ArchSpec &GetArchitecture() const { return m_arch; } + + void SetArchitecture(const lldb_private::ArchSpec &arch) { m_arch = arch; } + + bool HasNoExternalReferences() const; + + lldb_private::DataExtractor &GetData() { return m_data; } + + protected: + typedef lldb_private::UniqueCStringMap ObjectNameToIndexMap; + // Member Variables + lldb_private::ArchSpec m_arch; + llvm::sys::TimePoint<> m_modification_time; + lldb::offset_t m_file_offset; + std::vector m_objects; + ObjectNameToIndexMap m_object_name_to_index_map; + lldb_private::DataExtractor m_data; ///< The data for this object container + ///so we don't lose data if the .a files + ///gets modified + }; + + void SetArchive(Archive::shared_ptr &archive_sp); + + Archive::shared_ptr m_archive_sp; +}; + +#endif // LLDB_SOURCE_PLUGINS_OBJECTCONTAINER_BIG_ARCHIVE_OBJECTCONTAINERBIGARCHIVE_H diff --git a/lldb/source/Plugins/ObjectContainer/CMakeLists.txt b/lldb/source/Plugins/ObjectContainer/CMakeLists.txt index cda0c8151dd8a..2492798bb13ef 100644 --- a/lldb/source/Plugins/ObjectContainer/CMakeLists.txt +++ b/lldb/source/Plugins/ObjectContainer/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(BSD-Archive) +add_subdirectory(Big-Archive) add_subdirectory(Universal-Mach-O) add_subdirectory(Mach-O-Fileset) diff --git a/lldb/source/Plugins/ObjectFile/CMakeLists.txt b/lldb/source/Plugins/ObjectFile/CMakeLists.txt index 773241c8944c8..7abd0c96f4fd7 100644 --- a/lldb/source/Plugins/ObjectFile/CMakeLists.txt +++ b/lldb/source/Plugins/ObjectFile/CMakeLists.txt @@ -6,5 +6,6 @@ add_subdirectory(Mach-O) add_subdirectory(Minidump) add_subdirectory(PDB) add_subdirectory(PECOFF) +add_subdirectory(XCOFF) add_subdirectory(Placeholder) add_subdirectory(wasm) diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp index ce095bcc48374..bcb6330cbb1f9 100644 --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -5673,7 +5673,7 @@ bool ObjectFileMachO::GetCorefileMainBinaryInfo(addr_t &value, return false; } -bool ObjectFileMachO::GetCorefileThreadExtraInfos(std::vector &tids) { +bool ObjectFileMachO::GetCorefileThreadExtraInfos(std::vector &tids) { tids.clear(); ModuleSP module_sp(GetModule()); if (module_sp) { @@ -5724,8 +5724,8 @@ bool ObjectFileMachO::GetCorefileThreadExtraInfos(std::vector &tids) { return false; } StructuredData::Dictionary *thread = *maybe_thread; - tid_t tid = LLDB_INVALID_THREAD_ID; - if (thread->GetValueForKeyAsInteger("thread_id", tid)) + lldb::tid_t tid = LLDB_INVALID_THREAD_ID; + if (thread->GetValueForKeyAsInteger("thread_id", tid)) if (tid == 0) tid = LLDB_INVALID_THREAD_ID; tids.push_back(tid); diff --git a/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp b/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp index faa144bfb5f6a..d27cdfc60de85 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp +++ b/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp @@ -51,7 +51,9 @@ size_t ObjectFileMinidump::GetModuleSpecifications( const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp, lldb::offset_t data_offset, lldb::offset_t file_offset, lldb::offset_t length, lldb_private::ModuleSpecList &specs) { +#if !defined(__AIX__) specs.Clear(); +#endif return 0; } diff --git a/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp b/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp index f0832dbf07347..75cc54e4f0d48 100644 --- a/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp +++ b/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp @@ -116,18 +116,31 @@ size_t ObjectFilePDB::GetModuleSpecifications( ModuleSpec module_spec(file); llvm::BumpPtrAllocator allocator; std::unique_ptr pdb_file = loadPDBFile(file.GetPath(), allocator); - if (!pdb_file) + if (!pdb_file){ +#if !defined(__AIX__) return initial_count; +#else + return specs.GetSize() - initial_count; +#endif + } auto info_stream = pdb_file->getPDBInfoStream(); if (!info_stream) { llvm::consumeError(info_stream.takeError()); +#if !defined(__AIX__) return initial_count; +#else + return specs.GetSize() - initial_count; +#endif } auto dbi_stream = pdb_file->getPDBDbiStream(); if (!dbi_stream) { llvm::consumeError(dbi_stream.takeError()); +#if !defined(__AIX__) return initial_count; +#else + return specs.GetSize() - initial_count; +#endif } lldb_private::UUID &uuid = module_spec.GetUUID(); diff --git a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp index bda691ade8af0..db8fa78043fdc 100644 --- a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp @@ -252,8 +252,13 @@ size_t ObjectFilePECOFF::GetModuleSpecifications( lldb::offset_t data_offset, lldb::offset_t file_offset, lldb::offset_t length, lldb_private::ModuleSpecList &specs) { const size_t initial_count = specs.GetSize(); - if (!data_sp || !ObjectFilePECOFF::MagicBytesMatch(data_sp)) + if (!data_sp || !ObjectFilePECOFF::MagicBytesMatch(data_sp)){ +#if !defined(__AIX__) return initial_count; +#else + return specs.GetSize() - initial_count; +#endif + } Log *log = GetLog(LLDBLog::Object); @@ -266,12 +271,21 @@ size_t ObjectFilePECOFF::GetModuleSpecifications( if (!binary) { LLDB_LOG_ERROR(log, binary.takeError(), "Failed to create binary for file ({1}): {0}", file); +#if !defined(__AIX__) return initial_count; +#else + return specs.GetSize() - initial_count; +#endif } auto *COFFObj = llvm::dyn_cast(binary->get()); - if (!COFFObj) + if (!COFFObj){ +#if !defined(__AIX__) return initial_count; +#else + return specs.GetSize() - initial_count; +#endif + } ModuleSpec module_spec(file); ArchSpec &spec = module_spec.GetArchitecture(); diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/CMakeLists.txt b/lldb/source/Plugins/ObjectFile/XCOFF/CMakeLists.txt new file mode 100644 index 0000000000000..8840248574c88 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/XCOFF/CMakeLists.txt @@ -0,0 +1,13 @@ +add_lldb_library(lldbPluginObjectFileXCOFF PLUGIN + ObjectFileXCOFF.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbSymbol + lldbTarget + LINK_COMPONENTS + BinaryFormat + Object + Support + ) diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp new file mode 100644 index 0000000000000..a4d9ea295b4c3 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp @@ -0,0 +1,780 @@ +//===-- ObjectFileXCOFF.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 "ObjectFileXCOFF.h" + +#include +#include +#include +#include + +#include "lldb/Utility/FileSpecList.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Progress.h" +#include "lldb/Core/Section.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/LZMA.h" +#include "lldb/Symbol/DWARFCallFrameInfo.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RangeMap.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/Timer.h" +#include "llvm/ADT/IntervalMap.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/XCOFF.h" +#include "llvm/Object/Decompressor.h" +#include "llvm/Support/CRC.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Object/XCOFFObjectFile.h" + +using namespace llvm; +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(ObjectFileXCOFF) + +char ObjectFileXCOFF::ID; + +// FIXME: target 64bit at this moment. + +// Static methods. +void ObjectFileXCOFF::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + CreateMemoryInstance, GetModuleSpecifications); +} + +void ObjectFileXCOFF::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +bool UGLY_FLAG_FOR_AIX __attribute__((weak)) = false; + +ObjectFile *ObjectFileXCOFF::CreateInstance(const lldb::ModuleSP &module_sp, + DataBufferSP data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec *file, + lldb::offset_t file_offset, + lldb::offset_t length) { + if (!data_sp) { + data_sp = MapFileData(*file, length, file_offset); + if (!data_sp) + return nullptr; + data_offset = 0; + } + + if (!ObjectFileXCOFF::MagicBytesMatch(data_sp, data_offset, length)) + return nullptr; + + // Update the data to contain the entire file if it doesn't already + if (data_sp->GetByteSize() < length) { + data_sp = MapFileData(*file, length, file_offset); + if (!data_sp) + return nullptr; + data_offset = 0; + } + auto objfile_up = std::make_unique( + module_sp, data_sp, data_offset, file, file_offset, length); + if (!objfile_up) + return nullptr; + + // Cache xcoff binary. + if (!objfile_up->CreateBinary()) + return nullptr; + + if (!objfile_up->ParseHeader()) + //FIXME objfile leak + return nullptr; + + UGLY_FLAG_FOR_AIX = true; + return objfile_up.release(); +} + +bool ObjectFileXCOFF::CreateBinary() { + if (m_binary) + return true; + + Log *log = GetLog(LLDBLog::Object); + + auto binary = llvm::object::XCOFFObjectFile::createObjectFile(llvm::MemoryBufferRef( + toStringRef(m_data.GetData()), m_file.GetFilename().GetStringRef()), + file_magic::xcoff_object_64); + if (!binary) { + LLDB_LOG_ERROR(log, binary.takeError(), + "Failed to create binary for file ({1}): {0}", m_file); + return false; + } + + // Make sure we only handle COFF format. + m_binary = + llvm::unique_dyn_cast(std::move(*binary)); + if (!m_binary) + return false; + + LLDB_LOG(log, "this = {0}, module = {1} ({2}), file = {3}, binary = {4}", + this, GetModule().get(), GetModule()->GetSpecificationDescription(), + m_file.GetPath(), m_binary.get()); + return true; +} + +ObjectFile *ObjectFileXCOFF::CreateMemoryInstance( + const lldb::ModuleSP &module_sp, WritableDataBufferSP data_sp, + const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) { + return nullptr; +} + +size_t ObjectFileXCOFF::GetModuleSpecifications( + const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, lldb::offset_t file_offset, + lldb::offset_t length, lldb_private::ModuleSpecList &specs) { + const size_t initial_count = specs.GetSize(); + + if (ObjectFileXCOFF::MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) { + ArchSpec arch_spec = ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); + ModuleSpec spec(file, arch_spec); + spec.GetArchitecture().SetArchitecture(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE, llvm::Triple::AIX); + specs.Append(spec); + } + return specs.GetSize() - initial_count; +} + +static uint32_t XCOFFHeaderSizeFromMagic(uint32_t magic) { + switch (magic) { + /* TODO: 32bit not supported yet + case XCOFF::XCOFF32: + return sizeof(struct llvm::object::XCOFFFileHeader32); + */ + + case XCOFF::XCOFF64: + return sizeof(struct llvm::object::XCOFFFileHeader64); + break; + + default: + break; + } + return 0; +} + +bool ObjectFileXCOFF::MagicBytesMatch(DataBufferSP &data_sp, + lldb::addr_t data_offset, + lldb::addr_t data_length) { + lldb_private::DataExtractor data; + data.SetData(data_sp, data_offset, data_length); + lldb::offset_t offset = 0; + uint16_t magic = data.GetU16(&offset); + return XCOFFHeaderSizeFromMagic(magic) != 0; +} + +bool ObjectFileXCOFF::ParseHeader() { + ModuleSP module_sp(GetModule()); + if (module_sp) { + std::lock_guard guard(module_sp->GetMutex()); + m_sect_headers.clear(); + lldb::offset_t offset = 0; + + if (ParseXCOFFHeader(m_data, &offset, m_xcoff_header)) { + m_data.SetAddressByteSize(GetAddressByteSize()); + if (m_xcoff_header.auxhdrsize > 0) + ParseXCOFFOptionalHeader(m_data, &offset); + ParseSectionHeaders(offset); + } + return true; + } + + return false; +} + +bool ObjectFileXCOFF::ParseXCOFFHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset_ptr, + xcoff_header_t &xcoff_header) { + //FIXME: data.ValidOffsetForDataOfSize + xcoff_header.magic = data.GetU16(offset_ptr); + xcoff_header.nsects = data.GetU16(offset_ptr); + xcoff_header.modtime = data.GetU32(offset_ptr); + xcoff_header.symoff = data.GetU64(offset_ptr); + xcoff_header.auxhdrsize = data.GetU16(offset_ptr); + xcoff_header.flags = data.GetU16(offset_ptr); + xcoff_header.nsyms = data.GetU32(offset_ptr); + return true; +} + +bool ObjectFileXCOFF::ParseXCOFFOptionalHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset_ptr) { + lldb::offset_t init_offset = *offset_ptr; + //FIXME: data.ValidOffsetForDataOfSize + m_xcoff_aux_header.AuxMagic = data.GetU16(offset_ptr); + m_xcoff_aux_header.Version = data.GetU16(offset_ptr); + m_xcoff_aux_header.ReservedForDebugger = data.GetU32(offset_ptr); + m_xcoff_aux_header.TextStartAddr = data.GetU64(offset_ptr); + m_xcoff_aux_header.DataStartAddr = data.GetU64(offset_ptr); + m_xcoff_aux_header.TOCAnchorAddr = data.GetU64(offset_ptr); + m_xcoff_aux_header.SecNumOfEntryPoint = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfText = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfData = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfTOC = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfLoader = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfBSS = data.GetU16(offset_ptr); + m_xcoff_aux_header.MaxAlignOfText = data.GetU16(offset_ptr); + m_xcoff_aux_header.MaxAlignOfData = data.GetU16(offset_ptr); + m_xcoff_aux_header.ModuleType = data.GetU16(offset_ptr); + m_xcoff_aux_header.CpuFlag = data.GetU8(offset_ptr); + m_xcoff_aux_header.CpuType = data.GetU8(offset_ptr); + m_xcoff_aux_header.TextPageSize = data.GetU8(offset_ptr); + m_xcoff_aux_header.DataPageSize = data.GetU8(offset_ptr); + m_xcoff_aux_header.StackPageSize = data.GetU8(offset_ptr); + m_xcoff_aux_header.FlagAndTDataAlignment = data.GetU8(offset_ptr); + m_xcoff_aux_header.TextSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.InitDataSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.BssDataSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.EntryPointAddr = data.GetU64(offset_ptr); + m_xcoff_aux_header.MaxStackSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.MaxDataSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.SecNumOfTData = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfTBSS = data.GetU16(offset_ptr); + m_xcoff_aux_header.XCOFF64Flag = data.GetU16(offset_ptr); + lldb::offset_t last_offset = *offset_ptr; + if ((last_offset - init_offset) < m_xcoff_header.auxhdrsize) + *offset_ptr += (m_xcoff_header.auxhdrsize - (last_offset - init_offset)); + return true; +} + +bool ObjectFileXCOFF::ParseSectionHeaders( + uint32_t section_header_data_offset) { + const uint32_t nsects = m_xcoff_header.nsects; + m_sect_headers.clear(); + + if (nsects > 0) { + const size_t section_header_byte_size = nsects * m_binary->getSectionHeaderSize(); + lldb_private::DataExtractor section_header_data = + ReadImageData(section_header_data_offset, section_header_byte_size); + + lldb::offset_t offset = 0; + //FIXME: section_header_data.ValidOffsetForDataOfSize + m_sect_headers.resize(nsects); + + for (uint32_t idx = 0; idx < nsects; ++idx) { + const void *name_data = section_header_data.GetData(&offset, 8); + if (name_data) { + memcpy(m_sect_headers[idx].name, name_data, 8); + m_sect_headers[idx].phyaddr = section_header_data.GetU64(&offset); + m_sect_headers[idx].vmaddr = section_header_data.GetU64(&offset); + m_sect_headers[idx].size = section_header_data.GetU64(&offset); + m_sect_headers[idx].offset = section_header_data.GetU64(&offset); + m_sect_headers[idx].reloff = section_header_data.GetU64(&offset); + m_sect_headers[idx].lineoff = section_header_data.GetU64(&offset); + m_sect_headers[idx].nreloc = section_header_data.GetU32(&offset); + m_sect_headers[idx].nline = section_header_data.GetU32(&offset); + m_sect_headers[idx].flags = section_header_data.GetU32(&offset); + offset += 4; + } else { + offset += (m_binary->getSectionHeaderSize() - 8); + } + } + } + + return !m_sect_headers.empty(); +} + +lldb_private::DataExtractor ObjectFileXCOFF::ReadImageData(uint32_t offset, size_t size) { + if (!size) + return {}; + + if (m_data.ValidOffsetForDataOfSize(offset, size)) + return lldb_private::DataExtractor(m_data, offset, size); + + assert(0); + ProcessSP process_sp(m_process_wp.lock()); + lldb_private::DataExtractor data; + if (process_sp) { + auto data_up = std::make_unique(size, 0); + Status readmem_error; + size_t bytes_read = + process_sp->ReadMemory(offset, data_up->GetBytes(), + data_up->GetByteSize(), readmem_error); + if (bytes_read == size) { + DataBufferSP buffer_sp(data_up.release()); + data.SetData(buffer_sp, 0, buffer_sp->GetByteSize()); + } + } + return data; +} + +bool ObjectFileXCOFF::SetLoadAddress(Target &target, lldb::addr_t value, + bool value_is_offset) { + bool changed = false; + ModuleSP module_sp = GetModule(); + if (module_sp) { + size_t num_loaded_sections = 0; + SectionList *section_list = GetSectionList(); + if (section_list) { + const size_t num_sections = section_list->GetSize(); + size_t sect_idx = 0; + + for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) { + // Iterate through the object file sections to find all of the sections + // that have SHF_ALLOC in their flag bits. + SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); + if (section_sp && !section_sp->IsThreadSpecific()) { + bool use_offset = false; + if (strcmp(section_sp->GetName().AsCString(), ".text") == 0 || + strcmp(section_sp->GetName().AsCString(), ".data") == 0 || + strcmp(section_sp->GetName().AsCString(), ".bss") == 0) + use_offset = true; + + if (target.GetSectionLoadList().SetSectionLoadAddress( + section_sp, (use_offset ? + (section_sp->GetFileOffset() + value) : (section_sp->GetFileAddress() + value)))) + ++num_loaded_sections; + } + } + changed = num_loaded_sections > 0; + } + } + return changed; +} + +bool ObjectFileXCOFF::SetLoadAddressByType(Target &target, lldb::addr_t value, + bool value_is_offset, int type_id) { + bool changed = false; + ModuleSP module_sp = GetModule(); + if (module_sp) { + size_t num_loaded_sections = 0; + SectionList *section_list = GetSectionList(); + if (section_list) { + const size_t num_sections = section_list->GetSize(); + size_t sect_idx = 0; + + for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) { + // Iterate through the object file sections to find all of the sections + // that have SHF_ALLOC in their flag bits. + SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); + if (type_id == 1 && section_sp && strcmp(section_sp->GetName().AsCString(), ".text") == 0) { + if (!section_sp->IsThreadSpecific()) { + if (target.GetSectionLoadList().SetSectionLoadAddress( + section_sp, section_sp->GetFileOffset() + value)) + ++num_loaded_sections; + } + } else if (type_id == 2 && section_sp && strcmp(section_sp->GetName().AsCString(), ".data") == 0) { + if (!section_sp->IsThreadSpecific()) { + if (target.GetSectionLoadList().SetSectionLoadAddress( + section_sp, section_sp->GetFileAddress() + value)) + ++num_loaded_sections; + } + } + } + changed = num_loaded_sections > 0; + } + } + return changed; +} + +ByteOrder ObjectFileXCOFF::GetByteOrder() const { + return eByteOrderBig; +} + +bool ObjectFileXCOFF::IsExecutable() const { + return true; +} + +uint32_t ObjectFileXCOFF::GetAddressByteSize() const { + if (m_xcoff_header.magic == XCOFF::XCOFF64) + return 8; + else if (m_xcoff_header.magic == XCOFF::XCOFF32) + return 4; + return 4; +} + +AddressClass ObjectFileXCOFF::GetAddressClass(addr_t file_addr) { + return AddressClass::eUnknown; +} + +lldb::SymbolType ObjectFileXCOFF::MapSymbolType(llvm::object::SymbolRef::Type sym_type) { + if (sym_type == llvm::object::SymbolRef::ST_Function) + return lldb::eSymbolTypeCode; + else if (sym_type == llvm::object::SymbolRef::ST_Data) + return lldb::eSymbolTypeData; + return lldb::eSymbolTypeInvalid; +} + +void ObjectFileXCOFF::ParseSymtab(Symtab &lldb_symtab) { + SectionList *sect_list = GetSectionList(); + const uint32_t num_syms = m_xcoff_header.nsyms; + uint32_t sidx = 0; + if (num_syms > 0 && m_xcoff_header.symoff > 0) { + const uint32_t symbol_size = XCOFF::SymbolTableEntrySize; + const size_t symbol_data_size = num_syms * symbol_size; + lldb_private::DataExtractor symtab_data = + ReadImageData(m_xcoff_header.symoff, symbol_data_size); + + lldb::offset_t offset = 0; + std::string symbol_name; + Symbol *symbols = lldb_symtab.Resize(num_syms); + llvm::object::symbol_iterator SI = m_binary->symbol_begin(); + for (uint32_t i = 0; i < num_syms; ++i, ++SI) { + xcoff_symbol_t symbol; + const uint32_t symbol_offset = offset; + symbol.value = symtab_data.GetU64(&offset); + symbol.offset = symtab_data.GetU32(&offset); + Expected symbol_name_or_err = m_binary->getStringTableEntry(symbol.offset); + if (!symbol_name_or_err) { + consumeError(symbol_name_or_err.takeError()); + return; + } + StringRef symbol_name_str = symbol_name_or_err.get(); + symbol_name.assign(symbol_name_str.data()); + symbol.sect = symtab_data.GetU16(&offset); + symbol.type = symtab_data.GetU16(&offset); + symbol.storage = symtab_data.GetU8(&offset); + symbol.naux = symtab_data.GetU8(&offset); + // Allow C_HIDEXT TOC symbol, and check others. + if (symbol.storage == XCOFF::C_HIDEXT && strcmp(symbol_name.c_str(), "TOC") != 0) { + if (symbol.naux == 0) + continue; + if (symbol.naux > 1) { + i += symbol.naux; + offset += symbol.naux * symbol_size; + continue; + } + /* Allow XCOFF::C_HIDEXT with following SMC and AT: + StorageMappingClass: XMC_PR (0x0) + Auxiliary Type: AUX_CSECT (0xFB) + */ + xcoff_sym_csect_aux_entry_t symbol_aux; + symbol_aux.section_or_len_low_byte = symtab_data.GetU32(&offset); + symbol_aux.parameter_hash_index = symtab_data.GetU32(&offset); + symbol_aux.type_check_sect_num = symtab_data.GetU16(&offset); + symbol_aux.symbol_alignment_and_type = symtab_data.GetU8(&offset); + symbol_aux.storage_mapping_class = symtab_data.GetU8(&offset); + symbol_aux.section_or_len_high_byte = symtab_data.GetU32(&offset); + symbol_aux.pad = symtab_data.GetU8(&offset); + symbol_aux.aux_type = symtab_data.GetU8(&offset); + offset -= symbol.naux * symbol_size; + if (symbol_aux.storage_mapping_class != XCOFF::XMC_PR || symbol_aux.aux_type != XCOFF::AUX_CSECT) { + i += symbol.naux; + offset += symbol.naux * symbol_size; + continue; + } + } + // Remove the dot prefix for demangle + if (symbol_name_str.size() > 1 && symbol_name_str.data()[0] == '.') { + symbols[sidx].GetMangled().SetValue(ConstString(symbol_name.c_str() + 1)); + } else { + symbols[sidx].GetMangled().SetValue(ConstString(symbol_name.c_str())); + } + if ((int16_t)symbol.sect >= 1) { + Address symbol_addr(sect_list->GetSectionAtIndex((size_t)(symbol.sect - 1)), + (symbol.value - sect_list->GetSectionAtIndex((size_t)(symbol.sect - 1))->GetFileAddress())); + symbols[sidx].GetAddressRef() = symbol_addr; + + Expected sym_type_or_err = SI->getType(); + if (!sym_type_or_err) { + consumeError(sym_type_or_err.takeError()); + return; + } + symbols[sidx].SetType(MapSymbolType(sym_type_or_err.get())); + } + ++sidx; + + if (symbol.naux > 0) { + i += symbol.naux; + offset += symbol.naux * symbol_size; + } + } + lldb_symtab.Resize(sidx); + } +} + +bool ObjectFileXCOFF::IsStripped() { + return false; +} + +void ObjectFileXCOFF::CreateSections(SectionList &unified_section_list) { + if (m_sections_up) + return; + m_sections_up = std::make_unique(); + ModuleSP module_sp(GetModule()); + if (module_sp) { + std::lock_guard guard(module_sp->GetMutex()); + + const uint32_t nsects = m_sect_headers.size(); + ModuleSP module_sp(GetModule()); + for (uint32_t idx = 0; idx < nsects; ++idx) { + llvm::StringRef sect_name = GetSectionName(m_sect_headers[idx]); + ConstString const_sect_name(sect_name); + SectionType section_type = GetSectionType(sect_name, m_sect_headers[idx]); + + SectionSP section_sp(new Section( + module_sp, // Module to which this section belongs + this, // Object file to which this section belongs + idx + 1, // Section ID is the 1 based section index. + const_sect_name, // Name of this section + section_type, + m_sect_headers[idx].vmaddr, // File VM address == addresses as + // they are found in the object file + m_sect_headers[idx].size, // VM size in bytes of this section + m_sect_headers[idx].offset, // Offset to the data for this section in the file + m_sect_headers[idx].size, // Size in bytes of this section as found in the file + 0, // FIXME: alignment + m_sect_headers[idx].flags)); // Flags for this section + + // FIXME + uint32_t permissions = 0; + permissions |= ePermissionsReadable; + if (m_sect_headers[idx].flags & (XCOFF::STYP_DATA | XCOFF::STYP_BSS)) + permissions |= ePermissionsWritable; + if (m_sect_headers[idx].flags & XCOFF::STYP_TEXT) + permissions |= ePermissionsExecutable; + section_sp->SetPermissions(permissions); + + m_sections_up->AddSection(section_sp); + unified_section_list.AddSection(section_sp); + } + } +} + +llvm::StringRef ObjectFileXCOFF::GetSectionName(const section_header_t §) { + llvm::StringRef hdr_name(sect.name, std::size(sect.name)); + hdr_name = hdr_name.split('\0').first; + if (hdr_name.consume_front("/")) { + lldb::offset_t stroff; + if (!to_integer(hdr_name, stroff, 10)) + return ""; + lldb::offset_t string_file_offset = + m_xcoff_header.symoff + (m_xcoff_header.nsyms * static_cast(XCOFF::SymbolTableEntrySize)) + stroff; + if (const char *name = m_data.GetCStr(&string_file_offset)) + return name; + return ""; + } + return hdr_name; +} + +SectionType ObjectFileXCOFF::GetSectionType(llvm::StringRef sect_name, + const section_header_t §) { + if (sect.flags & XCOFF::STYP_TEXT) + return eSectionTypeCode; + if (sect.flags & XCOFF::STYP_DATA) + return eSectionTypeData; + if (sect.flags & XCOFF::STYP_BSS) + return eSectionTypeZeroFill; + if (sect.flags & XCOFF::STYP_DWARF) { + SectionType section_type = + llvm::StringSwitch(sect_name) + .Case(".dwinfo", eSectionTypeDWARFDebugInfo) + .Case(".dwline", eSectionTypeDWARFDebugLine) + .Case(".dwabrev", eSectionTypeDWARFDebugAbbrev) + .Default(eSectionTypeInvalid); + + if (section_type != eSectionTypeInvalid) + return section_type; + } + return eSectionTypeOther; +} + +void ObjectFileXCOFF::Dump(Stream *s) { +} + +ArchSpec ObjectFileXCOFF::GetArchitecture() { + ArchSpec arch_spec = ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); + return arch_spec; +} + +UUID ObjectFileXCOFF::GetUUID() { + return UUID(); +} + +std::optional ObjectFileXCOFF::GetDebugLink() { + return std::nullopt; +} + +uint32_t ObjectFileXCOFF::ParseDependentModules() { + ModuleSP module_sp(GetModule()); + if (!module_sp) + return 0; + + std::lock_guard guard(module_sp->GetMutex()); + if (m_deps_filespec) + return m_deps_filespec->GetSize(); + + // Cache coff binary if it is not done yet. + if (!CreateBinary()) + return 0; + + Log *log = GetLog(LLDBLog::Object); + LLDB_LOG(log, "this = {0}, module = {1} ({2}), file = {3}, binary = {4}", + this, GetModule().get(), GetModule()->GetSpecificationDescription(), + m_file.GetPath(), m_binary.get()); + + m_deps_filespec = FileSpecList(); + + auto ImportFilesOrError = m_binary->getImportFileTable(); + if (!ImportFilesOrError) { + consumeError(ImportFilesOrError.takeError()); + return 0; + } + +#if 0 + StringRef ImportFileTable = ImportFilesOrError.get(); + const char *CurrentStr = ImportFileTable.data(); + const char *TableEnd = ImportFileTable.end(); + const char *Basename = nullptr; + + for (size_t StrIndex = 0; CurrentStr < TableEnd; + ++StrIndex, CurrentStr += strlen(CurrentStr) + 1) { + if (StrIndex >= 3 && StrIndex % 3 == 1) { + // base_name + llvm::StringRef dll_name(CurrentStr); + Basename = CurrentStr; + + // At this moment we only have the base name of the DLL. The full path can + // only be seen after the dynamic loading. Our best guess is Try to get it + // with the help of the object file's directory. + llvm::SmallString<128> dll_fullpath; + FileSpec dll_specs(dll_name); + // FIXME: hack to get libc.a loaded + if (strcmp(CurrentStr, "libc.a") == 0) { + dll_specs.GetDirectory().SetString("/usr/lib"); + } else { + dll_specs.GetDirectory().SetString(m_file.GetDirectory().GetCString()); + } + + if (!llvm::sys::fs::real_path(dll_specs.GetPath(), dll_fullpath)) + //m_deps_filespec->EmplaceBack(dll_fullpath); + m_deps_filespec->EmplaceBack("/usr/lib/libc.a(shr_64.o)"); + else { + // Known DLLs or DLL not found in the object file directory. + m_deps_filespec->EmplaceBack(dll_name); + } + } else if (StrIndex >= 3 && StrIndex % 3 == 2) { + // archive_member_name + if (strcmp(CurrentStr, "") == 0) { + continue; + } + assert(strcmp(Basename, "") != 0); + std::map>::iterator iter = m_deps_base_members.find(std::string(Basename)); + if (iter == m_deps_base_members.end()) { + m_deps_base_members[std::string(Basename)] = std::vector(); + iter = m_deps_base_members.find(std::string(Basename)); + } + iter->second.push_back(std::string(CurrentStr)); + } + } +#endif + return m_deps_filespec->GetSize(); +} + +uint32_t ObjectFileXCOFF::GetDependentModules(FileSpecList &files) { + auto num_modules = ParseDependentModules(); + auto original_size = files.GetSize(); + + for (unsigned i = 0; i < num_modules; ++i) + files.AppendIfUnique(m_deps_filespec->GetFileSpecAtIndex(i)); + + return files.GetSize() - original_size; +} + +Address ObjectFileXCOFF::GetImageInfoAddress(Target *target) { + return Address(); +} + +lldb_private::Address ObjectFileXCOFF::GetEntryPointAddress() { + if (m_entry_point_address.IsValid()) + return m_entry_point_address; + + if (!ParseHeader() || !IsExecutable()) + return m_entry_point_address; + + SectionList *section_list = GetSectionList(); + addr_t vm_addr = m_xcoff_aux_header.EntryPointAddr; + SectionSP section_sp( + section_list->FindSectionContainingFileAddress(vm_addr)); + if (section_sp) { + lldb::offset_t offset_ptr = section_sp->GetFileOffset() + (vm_addr - section_sp->GetFileAddress()); + vm_addr = m_data.GetU64(&offset_ptr); + } + + if (!section_list) + m_entry_point_address.SetOffset(vm_addr); + else + m_entry_point_address.ResolveAddressUsingFileSections(vm_addr, + section_list); + + return m_entry_point_address; +} + +lldb_private::Address ObjectFileXCOFF::GetBaseAddress() { + return lldb_private::Address(); +} + +ObjectFile::Type ObjectFileXCOFF::CalculateType() { + if (m_xcoff_header.flags & XCOFF::F_EXEC) + return eTypeExecutable; + else if (m_xcoff_header.flags & XCOFF::F_SHROBJ) + return eTypeSharedLibrary; + return eTypeUnknown; +} + +ObjectFile::Strata ObjectFileXCOFF::CalculateStrata() { + return eStrataUnknown; +} + +llvm::StringRef +ObjectFileXCOFF::StripLinkerSymbolAnnotations(llvm::StringRef symbol_name) const { + return llvm::StringRef(); +} + +void ObjectFileXCOFF::RelocateSection(lldb_private::Section *section) +{ +} + +std::vector +ObjectFileXCOFF::GetLoadableData(Target &target) { + std::vector loadables; + return loadables; +} + +lldb::WritableDataBufferSP +ObjectFileXCOFF::MapFileDataWritable(const FileSpec &file, uint64_t Size, + uint64_t Offset) { + return FileSystem::Instance().CreateWritableDataBuffer(file.GetPath(), Size, + Offset); +} + +ObjectFileXCOFF::ObjectFileXCOFF(const lldb::ModuleSP &module_sp, + DataBufferSP data_sp, lldb::offset_t data_offset, + const FileSpec *file, lldb::offset_t file_offset, + lldb::offset_t length) + : ObjectFile(module_sp, file, file_offset, length, data_sp, data_offset), + m_xcoff_header(), m_sect_headers(), m_deps_filespec(), m_deps_base_members(), + m_entry_point_address() { + ::memset(&m_xcoff_header, 0, sizeof(m_xcoff_header)); + if (file) + m_file = *file; +} + +ObjectFileXCOFF::ObjectFileXCOFF(const lldb::ModuleSP &module_sp, + DataBufferSP header_data_sp, + const lldb::ProcessSP &process_sp, + addr_t header_addr) + : ObjectFile(module_sp, process_sp, header_addr, header_data_sp), + m_xcoff_header(), m_sect_headers(), m_deps_filespec(), m_deps_base_members(), + m_entry_point_address() { + ::memset(&m_xcoff_header, 0, sizeof(m_xcoff_header)); +} diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.h b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.h new file mode 100644 index 0000000000000..5a12d16886489 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.h @@ -0,0 +1,243 @@ +//===-- ObjectFileXCOFF.h --------------------------------------- -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_OBJECTFILE_XCOFF_OBJECTFILEXCOFF_H +#define LLDB_SOURCE_PLUGINS_OBJECTFILE_XCOFF_OBJECTFILEXCOFF_H + +#include + +#include + +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/UUID.h" +#include "lldb/lldb-private.h" +#include "llvm/Object/XCOFFObjectFile.h" + +/// \class ObjectFileXCOFF +/// Generic XCOFF object file reader. +/// +/// This class provides a generic XCOFF (32/64 bit) reader plugin implementing +/// the ObjectFile protocol. +class ObjectFileXCOFF : public lldb_private::ObjectFile { +public: + // Static Functions + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "xcoff"; } + + static llvm::StringRef GetPluginDescriptionStatic() { + return "XCOFF object file reader."; + } + + static lldb_private::ObjectFile * + CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP data_sp, + lldb::offset_t data_offset, const lldb_private::FileSpec *file, + lldb::offset_t file_offset, lldb::offset_t length); + + static lldb_private::ObjectFile *CreateMemoryInstance( + const lldb::ModuleSP &module_sp, lldb::WritableDataBufferSP data_sp, + const lldb::ProcessSP &process_sp, lldb::addr_t header_addr); + + static size_t GetModuleSpecifications(const lldb_private::FileSpec &file, + lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb_private::ModuleSpecList &specs); + + static bool MagicBytesMatch(lldb::DataBufferSP &data_sp, lldb::addr_t offset, + lldb::addr_t length); + + static lldb::SymbolType MapSymbolType(llvm::object::SymbolRef::Type sym_type); + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + // LLVM RTTI support + static char ID; + bool isA(const void *ClassID) const override { + return ClassID == &ID || ObjectFile::isA(ClassID); + } + static bool classof(const ObjectFile *obj) { return obj->isA(&ID); } + + // ObjectFile Protocol. + bool ParseHeader() override; + + bool SetLoadAddress(lldb_private::Target &target, lldb::addr_t value, + bool value_is_offset) override; + + bool SetLoadAddressByType(lldb_private::Target &target, lldb::addr_t value, + bool value_is_offset, int type_id) override; + + lldb::ByteOrder GetByteOrder() const override; + + bool IsExecutable() const override; + + uint32_t GetAddressByteSize() const override; + + lldb_private::AddressClass GetAddressClass(lldb::addr_t file_addr) override; + + void ParseSymtab(lldb_private::Symtab &symtab) override; + + bool IsStripped() override; + + void CreateSections(lldb_private::SectionList &unified_section_list) override; + + void Dump(lldb_private::Stream *s) override; + + lldb_private::ArchSpec GetArchitecture() override; + + lldb_private::UUID GetUUID() override; + + /// Return the contents of the .gnu_debuglink section, if the object file + /// contains it. + std::optional GetDebugLink(); + + uint32_t GetDependentModules(lldb_private::FileSpecList &files) override; + + lldb_private::Address + GetImageInfoAddress(lldb_private::Target *target) override; + + lldb_private::Address GetEntryPointAddress() override; + + lldb_private::Address GetBaseAddress() override; + + ObjectFile::Type CalculateType() override; + + ObjectFile::Strata CalculateStrata() override; + + llvm::StringRef + StripLinkerSymbolAnnotations(llvm::StringRef symbol_name) const override; + + void RelocateSection(lldb_private::Section *section) override; + + lldb_private::DataExtractor ReadImageData(uint32_t offset, size_t size); + + ObjectFileXCOFF(const lldb::ModuleSP &module_sp, lldb::DataBufferSP data_sp, + lldb::offset_t data_offset, const lldb_private::FileSpec *file, + lldb::offset_t offset, lldb::offset_t length); + + ObjectFileXCOFF(const lldb::ModuleSP &module_sp, + lldb::DataBufferSP header_data_sp, + const lldb::ProcessSP &process_sp, lldb::addr_t header_addr); + +protected: + + typedef struct xcoff_header { + uint16_t magic; + uint16_t nsects; + uint32_t modtime; + uint64_t symoff; + uint32_t nsyms; + uint16_t auxhdrsize; + uint16_t flags; + } xcoff_header_t; + + typedef struct xcoff_aux_header { + uint16_t AuxMagic; + uint16_t Version; + uint32_t ReservedForDebugger; + uint64_t TextStartAddr; + uint64_t DataStartAddr; + uint64_t TOCAnchorAddr; + uint16_t SecNumOfEntryPoint; + uint16_t SecNumOfText; + uint16_t SecNumOfData; + uint16_t SecNumOfTOC; + uint16_t SecNumOfLoader; + uint16_t SecNumOfBSS; + uint16_t MaxAlignOfText; + uint16_t MaxAlignOfData; + uint16_t ModuleType; + uint8_t CpuFlag; + uint8_t CpuType; + uint8_t TextPageSize; + uint8_t DataPageSize; + uint8_t StackPageSize; + uint8_t FlagAndTDataAlignment; + uint64_t TextSize; + uint64_t InitDataSize; + uint64_t BssDataSize; + uint64_t EntryPointAddr; + uint64_t MaxStackSize; + uint64_t MaxDataSize; + uint16_t SecNumOfTData; + uint16_t SecNumOfTBSS; + uint16_t XCOFF64Flag; + } xcoff_aux_header_t; + + typedef struct section_header { + char name[8]; + uint64_t phyaddr; // Physical Addr + uint64_t vmaddr; // Virtual Addr + uint64_t size; // Section size + uint64_t offset; // File offset to raw data + uint64_t reloff; // Offset to relocations + uint64_t lineoff; // Offset to line table entries + uint32_t nreloc; // Number of relocation entries + uint32_t nline; // Number of line table entries + uint32_t flags; + } section_header_t; + + typedef struct xcoff_symbol { + uint64_t value; + uint32_t offset; + uint16_t sect; + uint16_t type; + uint8_t storage; + uint8_t naux; + } xcoff_symbol_t; + + typedef struct xcoff_sym_csect_aux_entry { + uint32_t section_or_len_low_byte; + uint32_t parameter_hash_index; + uint16_t type_check_sect_num; + uint8_t symbol_alignment_and_type; + uint8_t storage_mapping_class; + uint32_t section_or_len_high_byte; + uint8_t pad; + uint8_t aux_type; + } xcoff_sym_csect_aux_entry_t; + + static bool ParseXCOFFHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset_ptr, + xcoff_header_t &xcoff_header); + bool ParseXCOFFOptionalHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset_ptr); + bool ParseSectionHeaders(uint32_t offset); + + std::vector + GetLoadableData(lldb_private::Target &target) override; + + static lldb::WritableDataBufferSP + MapFileDataWritable(const lldb_private::FileSpec &file, uint64_t Size, + uint64_t Offset); + llvm::StringRef GetSectionName(const section_header_t §); + static lldb::SectionType GetSectionType(llvm::StringRef sect_name, + const section_header_t §); + + uint32_t ParseDependentModules(); + typedef std::vector SectionHeaderColl; + +private: + bool CreateBinary(); + + xcoff_header_t m_xcoff_header; + xcoff_aux_header_t m_xcoff_aux_header; + SectionHeaderColl m_sect_headers; + std::unique_ptr m_binary; + lldb_private::Address m_entry_point_address; + std::optional m_deps_filespec; + std::map> m_deps_base_members; +}; + +#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_ELF_OBJECTFILEELF_H diff --git a/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp b/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp index e026ffefd645e..106e38b6e25ae 100644 --- a/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp +++ b/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp @@ -227,7 +227,7 @@ ThreadSP OperatingSystemPython::CreateThreadFromThreadInfo( ThreadList &old_thread_list, std::vector &core_used_map, bool *did_create_ptr) { ThreadSP thread_sp; - tid_t tid = LLDB_INVALID_THREAD_ID; + lldb::tid_t tid = LLDB_INVALID_THREAD_ID; if (!thread_dict.GetValueForKeyAsInteger("tid", tid)) return ThreadSP(); diff --git a/lldb/source/Plugins/Platform/AIX/CMakeLists.txt b/lldb/source/Plugins/Platform/AIX/CMakeLists.txt new file mode 100644 index 0000000000000..85ff0a315eabd --- /dev/null +++ b/lldb/source/Plugins/Platform/AIX/CMakeLists.txt @@ -0,0 +1,13 @@ +add_definitions("-D_ALL_SOURCE") + +add_lldb_library(lldbPluginPlatformAIX PLUGIN + PlatformAIX.cpp + + LINK_LIBS + lldbBreakpoint + lldbCore + lldbHost + lldbInterpreter + lldbTarget + lldbPluginPlatformPOSIX + ) diff --git a/lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp b/lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp new file mode 100644 index 0000000000000..b6b08b73bec41 --- /dev/null +++ b/lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp @@ -0,0 +1,471 @@ +//===-- PlatformAIX.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 "PlatformAIX.h" +#include "lldb/Host/Config.h" + +#include +#if LLDB_ENABLE_POSIX +#include +#endif + +#include "Utility/ARM64_DWARF_Registers.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/StreamString.h" + +// Define these constants from AIX mman.h for use when targeting remote aix +// systems even when host has different values. + +#if defined(__AIX__) +#include +#endif + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::platform_aix; + +LLDB_PLUGIN_DEFINE(PlatformAIX) + +static uint32_t g_initialize_count = 0; + + +PlatformSP PlatformAIX::CreateInstance(bool force, const ArchSpec *arch) { + Log *log = GetLog(LLDBLog::Platform); + LLDB_LOG(log, "force = {0}, arch=({1}, {2})", force, + arch ? arch->GetArchitectureName() : "", + arch ? arch->GetTriple().getTriple() : ""); + + bool create = force; + if (!create && arch && arch->IsValid()) { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getOS()) { + case llvm::Triple::AIX: + create = true; + break; + + default: + break; + } + } + + LLDB_LOG(log, "create = {0}", create); + if (create) { + return PlatformSP(new PlatformAIX(false)); + } + return PlatformSP(); +} + +llvm::StringRef PlatformAIX::GetPluginDescriptionStatic(bool is_host) { + if (is_host) + return "Local AIX user platform plug-in."; + return "Remote AIX user platform plug-in."; +} + +void PlatformAIX::Initialize() { + PlatformPOSIX::Initialize(); + + if (g_initialize_count++ == 0) { +#if defined(__AIX__) + PlatformSP default_platform_sp(new PlatformAIX(true)); + default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); + Platform::SetHostPlatform(default_platform_sp); +#endif + PluginManager::RegisterPlugin( + PlatformAIX::GetPluginNameStatic(false), + PlatformAIX::GetPluginDescriptionStatic(false), + PlatformAIX::CreateInstance, nullptr); + } +} + +void PlatformAIX::Terminate() { + if (g_initialize_count > 0) { + if (--g_initialize_count == 0) { + PluginManager::UnregisterPlugin(PlatformAIX::CreateInstance); + } + } + + PlatformPOSIX::Terminate(); +} + +/// Default Constructor +PlatformAIX::PlatformAIX(bool is_host) + : PlatformPOSIX(is_host) // This is the local host platform +{ + if (is_host) { + ArchSpec hostArch = HostInfo::GetArchitecture(HostInfo::eArchKindDefault); + m_supported_architectures.push_back(hostArch); + if (hostArch.GetTriple().isArch64Bit()) { + m_supported_architectures.push_back( + HostInfo::GetArchitecture(HostInfo::eArchKind32)); + } + } else { + m_supported_architectures = CreateArchList( + {llvm::Triple::x86_64, llvm::Triple::x86, llvm::Triple::arm, + llvm::Triple::aarch64, llvm::Triple::mips64, llvm::Triple::mips64, + llvm::Triple::hexagon, llvm::Triple::mips, llvm::Triple::mips64el, + llvm::Triple::mipsel, llvm::Triple::systemz}, + llvm::Triple::AIX); + } +} + +std::vector +PlatformAIX::GetSupportedArchitectures(const ArchSpec &process_host_arch) { + if (m_remote_platform_sp) + return m_remote_platform_sp->GetSupportedArchitectures(process_host_arch); + return m_supported_architectures; +} + +void PlatformAIX::GetStatus(Stream &strm) { + Platform::GetStatus(strm); + +#if LLDB_ENABLE_POSIX + // Display local kernel information only when we are running in host mode. + // Otherwise, we would end up printing non-AIX information (when running on + // Mac OS for example). + if (IsHost()) { + struct utsname un; + + if (uname(&un)) + return; + + strm.Printf(" Kernel: %s\n", un.sysname); + strm.Printf(" Release: %s\n", un.release); + strm.Printf(" Version: %s\n", un.version); + } +#endif +} + +uint32_t +PlatformAIX::GetResumeCountForLaunchInfo(ProcessLaunchInfo &launch_info) { + uint32_t resume_count = 0; + + // Always resume past the initial stop when we use eLaunchFlagDebug + if (launch_info.GetFlags().Test(eLaunchFlagDebug)) { + // Resume past the stop for the final exec into the true inferior. + ++resume_count; + } + + // If we're not launching a shell, we're done. + const FileSpec &shell = launch_info.GetShell(); + if (!shell) + return resume_count; + + std::string shell_string = shell.GetPath(); + // We're in a shell, so for sure we have to resume past the shell exec. + ++resume_count; + + // Figure out what shell we're planning on using. + const char *shell_name = strrchr(shell_string.c_str(), '/'); + if (shell_name == nullptr) + shell_name = shell_string.c_str(); + else + shell_name++; + + if (strcmp(shell_name, "csh") == 0 || strcmp(shell_name, "tcsh") == 0 || + strcmp(shell_name, "zsh") == 0 || strcmp(shell_name, "sh") == 0) { + // These shells seem to re-exec themselves. Add another resume. + ++resume_count; + } + + return resume_count; +} + +bool PlatformAIX::CanDebugProcess() { + if (IsHost()) { + return true; + } else { + // If we're connected, we can debug. + return IsConnected(); + } +} + +void PlatformAIX::CalculateTrapHandlerSymbolNames() { + m_trap_handlers.push_back(ConstString("_sigtramp")); + m_trap_handlers.push_back(ConstString("__kernel_rt_sigreturn")); + m_trap_handlers.push_back(ConstString("__restore_rt")); +} + +static lldb::UnwindPlanSP GetAArch64TrapHanlderUnwindPlan(ConstString name) { + UnwindPlanSP unwind_plan_sp; + if (name != "__kernel_rt_sigreturn") + return unwind_plan_sp; + + UnwindPlan::RowSP row = std::make_shared(); + row->SetOffset(0); + + // In the signal trampoline frame, sp points to an rt_sigframe[1], which is: + // - 128-byte siginfo struct + // - ucontext struct: + // - 8-byte long (uc_flags) + // - 8-byte pointer (uc_link) + // - 24-byte stack_t + // - 128-byte signal set + // - 8 bytes of padding because sigcontext has 16-byte alignment + // - sigcontext/mcontext_t + // [1] + // https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/signal.c + int32_t offset = 128 + 8 + 8 + 24 + 128 + 8; + // Then sigcontext[2] is: + // - 8 byte fault address + // - 31 8 byte registers + // - 8 byte sp + // - 8 byte pc + // [2] + // https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/sigcontext.h + + // Skip fault address + offset += 8; + row->GetCFAValue().SetIsRegisterPlusOffset(arm64_dwarf::sp, offset); + + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x0, 0 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x1, 1 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x2, 2 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x3, 3 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x4, 4 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x5, 5 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x6, 6 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x7, 7 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x8, 8 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x9, 9 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x10, 10 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x11, 11 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x12, 12 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x13, 13 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x14, 14 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x15, 15 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x16, 16 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x17, 17 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x18, 18 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x19, 19 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x20, 20 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x21, 21 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x22, 22 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x23, 23 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x24, 24 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x25, 25 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x26, 26 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x27, 27 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x28, 28 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::fp, 29 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x30, 30 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::sp, 31 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::pc, 32 * 8, false); + + // The sigcontext may also contain floating point and SVE registers. + // However this would require a dynamic unwind plan so they are not included + // here. + + unwind_plan_sp = std::make_shared(eRegisterKindDWARF); + unwind_plan_sp->AppendRow(row); + unwind_plan_sp->SetSourceName("AArch64 AIX sigcontext"); + unwind_plan_sp->SetSourcedFromCompiler(eLazyBoolYes); + // Because sp is the same throughout the function + unwind_plan_sp->SetUnwindPlanValidAtAllInstructions(eLazyBoolYes); + unwind_plan_sp->SetUnwindPlanForSignalTrap(eLazyBoolYes); + + return unwind_plan_sp; +} + +lldb::UnwindPlanSP +PlatformAIX::GetTrapHandlerUnwindPlan(const llvm::Triple &triple, + ConstString name) { + if (triple.isAArch64()) + return GetAArch64TrapHanlderUnwindPlan(name); + + return {}; +} + +MmapArgList PlatformAIX::GetMmapArgumentList(const ArchSpec &arch, + addr_t addr, addr_t length, + unsigned prot, unsigned flags, + addr_t fd, addr_t offset) { +#if defined(__AIX__) + unsigned flags_platform = MAP_VARIABLE | MAP_PRIVATE | MAP_ANONYMOUS; +#else + unsigned flags_platform = 0; +#endif + MmapArgList args({addr, length, prot, flags_platform, fd, offset}); + return args; +} + +CompilerType PlatformAIX::GetSiginfoType(const llvm::Triple &triple) { + if (!m_type_system_up) + m_type_system_up.reset(new TypeSystemClang("siginfo", triple)); + TypeSystemClang *ast = m_type_system_up.get(); + + bool si_errno_then_code = true; + + switch (triple.getArch()) { + case llvm::Triple::mips: + case llvm::Triple::mipsel: + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + // mips has si_code and si_errno swapped + si_errno_then_code = false; + break; + default: + break; + } + + // generic types + CompilerType int_type = ast->GetBasicType(eBasicTypeInt); + CompilerType uint_type = ast->GetBasicType(eBasicTypeUnsignedInt); + CompilerType short_type = ast->GetBasicType(eBasicTypeShort); + CompilerType long_type = ast->GetBasicType(eBasicTypeLong); + CompilerType voidp_type = ast->GetBasicType(eBasicTypeVoid).GetPointerType(); + + // platform-specific types + CompilerType &pid_type = int_type; + CompilerType &uid_type = uint_type; + CompilerType &clock_type = long_type; + CompilerType &band_type = long_type; + + CompilerType sigval_type = ast->CreateRecordType( + nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "__lldb_sigval_t", + llvm::to_underlying(clang::TagTypeKind::Union), lldb::eLanguageTypeC); + ast->StartTagDeclarationDefinition(sigval_type); + ast->AddFieldToRecordType(sigval_type, "sival_int", int_type, + lldb::eAccessPublic, 0); + ast->AddFieldToRecordType(sigval_type, "sival_ptr", voidp_type, + lldb::eAccessPublic, 0); + ast->CompleteTagDeclarationDefinition(sigval_type); + + CompilerType sigfault_bounds_type = ast->CreateRecordType( + nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "", + llvm::to_underlying(clang::TagTypeKind::Union), lldb::eLanguageTypeC); + ast->StartTagDeclarationDefinition(sigfault_bounds_type); + ast->AddFieldToRecordType(sigfault_bounds_type, "_addr_bnd", + ast->CreateStructForIdentifier(ConstString(), + { + {"_lower", voidp_type}, + {"_upper", voidp_type}, + }), + lldb::eAccessPublic, 0); + ast->AddFieldToRecordType(sigfault_bounds_type, "_pkey", uint_type, + lldb::eAccessPublic, 0); + ast->CompleteTagDeclarationDefinition(sigfault_bounds_type); + + // siginfo_t + CompilerType siginfo_type = ast->CreateRecordType( + nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "__lldb_siginfo_t", + llvm::to_underlying(clang::TagTypeKind::Struct), lldb::eLanguageTypeC); + ast->StartTagDeclarationDefinition(siginfo_type); + ast->AddFieldToRecordType(siginfo_type, "si_signo", int_type, + lldb::eAccessPublic, 0); + + if (si_errno_then_code) { + ast->AddFieldToRecordType(siginfo_type, "si_errno", int_type, + lldb::eAccessPublic, 0); + ast->AddFieldToRecordType(siginfo_type, "si_code", int_type, + lldb::eAccessPublic, 0); + } else { + ast->AddFieldToRecordType(siginfo_type, "si_code", int_type, + lldb::eAccessPublic, 0); + ast->AddFieldToRecordType(siginfo_type, "si_errno", int_type, + lldb::eAccessPublic, 0); + } + + // the structure is padded on 64-bit arches to fix alignment + if (triple.isArch64Bit()) + ast->AddFieldToRecordType(siginfo_type, "__pad0", int_type, + lldb::eAccessPublic, 0); + + // union used to hold the signal data + CompilerType union_type = ast->CreateRecordType( + nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "", + llvm::to_underlying(clang::TagTypeKind::Union), lldb::eLanguageTypeC); + ast->StartTagDeclarationDefinition(union_type); + + ast->AddFieldToRecordType( + union_type, "_kill", + ast->CreateStructForIdentifier(ConstString(), + { + {"si_pid", pid_type}, + {"si_uid", uid_type}, + }), + lldb::eAccessPublic, 0); + + ast->AddFieldToRecordType( + union_type, "_timer", + ast->CreateStructForIdentifier(ConstString(), + { + {"si_tid", int_type}, + {"si_overrun", int_type}, + {"si_sigval", sigval_type}, + }), + lldb::eAccessPublic, 0); + + ast->AddFieldToRecordType( + union_type, "_rt", + ast->CreateStructForIdentifier(ConstString(), + { + {"si_pid", pid_type}, + {"si_uid", uid_type}, + {"si_sigval", sigval_type}, + }), + lldb::eAccessPublic, 0); + + ast->AddFieldToRecordType( + union_type, "_sigchld", + ast->CreateStructForIdentifier(ConstString(), + { + {"si_pid", pid_type}, + {"si_uid", uid_type}, + {"si_status", int_type}, + {"si_utime", clock_type}, + {"si_stime", clock_type}, + }), + lldb::eAccessPublic, 0); + + ast->AddFieldToRecordType( + union_type, "_sigfault", + ast->CreateStructForIdentifier(ConstString(), + { + {"si_addr", voidp_type}, + {"si_addr_lsb", short_type}, + {"_bounds", sigfault_bounds_type}, + }), + lldb::eAccessPublic, 0); + + ast->AddFieldToRecordType( + union_type, "_sigpoll", + ast->CreateStructForIdentifier(ConstString(), + { + {"si_band", band_type}, + {"si_fd", int_type}, + }), + lldb::eAccessPublic, 0); + + // NB: SIGSYS is not present on ia64 but we don't seem to support that + ast->AddFieldToRecordType( + union_type, "_sigsys", + ast->CreateStructForIdentifier(ConstString(), + { + {"_call_addr", voidp_type}, + {"_syscall", int_type}, + {"_arch", uint_type}, + }), + lldb::eAccessPublic, 0); + + ast->CompleteTagDeclarationDefinition(union_type); + ast->AddFieldToRecordType(siginfo_type, "_sifields", union_type, + lldb::eAccessPublic, 0); + + ast->CompleteTagDeclarationDefinition(siginfo_type); + return siginfo_type; +} diff --git a/lldb/source/Plugins/Platform/AIX/PlatformAIX.h b/lldb/source/Plugins/Platform/AIX/PlatformAIX.h new file mode 100644 index 0000000000000..3ae8089a48d71 --- /dev/null +++ b/lldb/source/Plugins/Platform/AIX/PlatformAIX.h @@ -0,0 +1,74 @@ +//===-- PlatformAIX.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PLATFORM_AIX_PLATFORMAIX_H +#define LLDB_SOURCE_PLUGINS_PLATFORM_AIX_PLATFORMAIX_H + +#include "Plugins/Platform/POSIX/PlatformPOSIX.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" + +namespace lldb_private { +namespace platform_aix { + +class PlatformAIX : public PlatformPOSIX { +public: + PlatformAIX(bool is_host); + + static void Initialize(); + + static void Terminate(); + + // lldb_private::PluginInterface functions + static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch); + + static llvm::StringRef GetPluginNameStatic(bool is_host) { + return is_host ? Platform::GetHostPlatformName() : "remote-AIX"; + } + + static llvm::StringRef GetPluginDescriptionStatic(bool is_host); + + llvm::StringRef GetPluginName() override { + return GetPluginNameStatic(IsHost()); + } + + // lldb_private::Platform functions + llvm::StringRef GetDescription() override { + return GetPluginDescriptionStatic(IsHost()); + } + + void GetStatus(Stream &strm) override; + + std::vector + GetSupportedArchitectures(const ArchSpec &process_host_arch) override; + + uint32_t GetResumeCountForLaunchInfo(ProcessLaunchInfo &launch_info) override; + + bool CanDebugProcess() override; + + void CalculateTrapHandlerSymbolNames() override; + + lldb::UnwindPlanSP GetTrapHandlerUnwindPlan(const llvm::Triple &triple, + ConstString name) override; + + MmapArgList GetMmapArgumentList(const ArchSpec &arch, lldb::addr_t addr, + lldb::addr_t length, unsigned prot, + unsigned flags, lldb::addr_t fd, + lldb::addr_t offset) override; + + CompilerType GetSiginfoType(const llvm::Triple &triple) override; + + std::vector m_supported_architectures; + +private: + std::unique_ptr m_type_system_up; +}; + +} // namespace platform_AIX +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PLATFORM_AIX_PLATFORMAIX_H diff --git a/lldb/source/Plugins/Platform/CMakeLists.txt b/lldb/source/Plugins/Platform/CMakeLists.txt index 6869587f917eb..9d0afd97cff85 100644 --- a/lldb/source/Plugins/Platform/CMakeLists.txt +++ b/lldb/source/Plugins/Platform/CMakeLists.txt @@ -8,3 +8,4 @@ add_subdirectory(OpenBSD) add_subdirectory(POSIX) add_subdirectory(QemuUser) add_subdirectory(Windows) +add_subdirectory(AIX) diff --git a/lldb/source/Plugins/Process/AIX/CMakeLists.txt b/lldb/source/Plugins/Process/AIX/CMakeLists.txt new file mode 100644 index 0000000000000..e9d83266f5857 --- /dev/null +++ b/lldb/source/Plugins/Process/AIX/CMakeLists.txt @@ -0,0 +1,19 @@ +add_definitions("-D_ALL_SOURCE") + +add_lldb_library(lldbPluginProcessAIX + NativeProcessAIX.cpp + NativeRegisterContextAIX.cpp + NativeRegisterContextAIX_ppc64.cpp + NativeThreadAIX.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbSymbol + lldbTarget + lldbUtility + lldbPluginProcessPOSIX + lldbPluginProcessUtility + LINK_COMPONENTS + Support + ) diff --git a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp new file mode 100644 index 0000000000000..882f20d30a3bf --- /dev/null +++ b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp @@ -0,0 +1,2048 @@ +//===-- NativeProcessAIX.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 "NativeProcessAIX.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "NativeThreadAIX.h" +#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" +//#include "Plugins/Process/Utility/LinuxProcMaps.h" +//#include "Procfs.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/ProcessLaunchInfo.h" +#include "lldb/Host/PseudoTerminal.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Host/common/NativeRegisterContext.h" +#include "lldb/Host/aix/Ptrace.h" +//#include "lldb/Host/linux/Host.h" +//#include "lldb/Host/linux/Uio.h" +#include "lldb/Host/posix/ProcessLauncherPosixFork.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/StringExtractor.h" +#include "llvm/ADT/ScopeExit.h" +#include "llvm/Support/Errno.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Threading.h" + +#include +#include +#include +#include +//#include +#include +#include +#include +#include + +#ifdef __aarch64__ +#include +#include +#endif + +// Support hardware breakpoints in case it has not been defined +#ifndef TRAP_HWBKPT +#define TRAP_HWBKPT 4 +#endif + +#ifndef HWCAP2_MTE +#define HWCAP2_MTE (1 << 18) +#endif + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_aix; +using namespace llvm; + +// Private bits we only need internally. + +static bool ProcessVmReadvSupported() { + static bool is_supported; + static llvm::once_flag flag; + + llvm::call_once(flag, [] { + Log *log = GetLog(POSIXLog::Process); + + uint32_t source = 0x47424742; + uint32_t dest = 0; + + struct iovec local, remote; + remote.iov_base = &source; + local.iov_base = &dest; + remote.iov_len = local.iov_len = sizeof source; + +#if 0 + // We shall try if cross-process-memory reads work by attempting to read a + // value from our own process. + ssize_t res = process_vm_readv(getpid(), &local, 1, &remote, 1, 0); + is_supported = (res == sizeof(source) && source == dest); + if (is_supported) + LLDB_LOG(log, + "Detected kernel support for process_vm_readv syscall. " + "Fast memory reads enabled."); + else + LLDB_LOG(log, + "syscall process_vm_readv failed (error: {0}). Fast memory " + "reads disabled.", + llvm::sys::StrError()); +#endif + }); + + return is_supported; +} + +static void MaybeLogLaunchInfo(const ProcessLaunchInfo &info) { + Log *log = GetLog(POSIXLog::Process); + if (!log) + return; + + if (const FileAction *action = info.GetFileActionForFD(STDIN_FILENO)) + LLDB_LOG(log, "setting STDIN to '{0}'", action->GetFileSpec()); + else + LLDB_LOG(log, "leaving STDIN as is"); + + if (const FileAction *action = info.GetFileActionForFD(STDOUT_FILENO)) + LLDB_LOG(log, "setting STDOUT to '{0}'", action->GetFileSpec()); + else + LLDB_LOG(log, "leaving STDOUT as is"); + + if (const FileAction *action = info.GetFileActionForFD(STDERR_FILENO)) + LLDB_LOG(log, "setting STDERR to '{0}'", action->GetFileSpec()); + else + LLDB_LOG(log, "leaving STDERR as is"); + + int i = 0; + for (const char **args = info.GetArguments().GetConstArgumentVector(); *args; + ++args, ++i) + LLDB_LOG(log, "arg {0}: '{1}'", i, *args); +} + +static void DisplayBytes(StreamString &s, void *bytes, uint32_t count) { + uint8_t *ptr = (uint8_t *)bytes; + const uint32_t loop_count = std::min(DEBUG_PTRACE_MAXBYTES, count); + for (uint32_t i = 0; i < loop_count; i++) { + s.Printf("[%x]", *ptr); + ptr++; + } +} + +static void PtraceDisplayBytes(int &req, void *data, size_t data_size) { + Log *log = GetLog(POSIXLog::Ptrace); + if (!log) + return; + StreamString buf; + + switch (req) { + case PTRACE_POKETEXT: { + DisplayBytes(buf, &data, 8); + LLDB_LOGV(log, "PTRACE_POKETEXT {0}", buf.GetData()); + break; + } + case PTRACE_POKEDATA: { + DisplayBytes(buf, &data, 8); + LLDB_LOGV(log, "PTRACE_POKEDATA {0}", buf.GetData()); + break; + } + case PTRACE_POKEUSER: { + DisplayBytes(buf, &data, 8); + LLDB_LOGV(log, "PTRACE_POKEUSER {0}", buf.GetData()); + break; + } + case PTRACE_SETREGS: { + DisplayBytes(buf, data, data_size); + LLDB_LOGV(log, "PTRACE_SETREGS {0}", buf.GetData()); + break; + } + case PTRACE_SETFPREGS: { + DisplayBytes(buf, data, data_size); + LLDB_LOGV(log, "PTRACE_SETFPREGS {0}", buf.GetData()); + break; + } +#if 0 + case PTRACE_SETSIGINFO: { + DisplayBytes(buf, data, sizeof(siginfo_t)); + LLDB_LOGV(log, "PTRACE_SETSIGINFO {0}", buf.GetData()); + break; + } +#endif + case PTRACE_SETREGSET: { + // Extract iov_base from data, which is a pointer to the struct iovec + DisplayBytes(buf, *(void **)data, data_size); + LLDB_LOGV(log, "PTRACE_SETREGSET {0}", buf.GetData()); + break; + } + default: {} + } +} + +static constexpr unsigned k_ptrace_word_size = sizeof(void *); +static_assert(sizeof(long) >= k_ptrace_word_size, + "Size of long must be larger than ptrace word size"); + +// Simple helper function to ensure flags are enabled on the given file +// descriptor. +static Status EnsureFDFlags(int fd, int flags) { + Status error; + + int status = fcntl(fd, F_GETFL); + if (status == -1) { + error.SetErrorToErrno(); + return error; + } + + if (fcntl(fd, F_SETFL, status | flags) == -1) { + error.SetErrorToErrno(); + return error; + } + + return error; +} + +#if 0 +static llvm::Error AddPtraceScopeNote(llvm::Error original_error) { + Expected ptrace_scope = GetPtraceScope(); + if (auto E = ptrace_scope.takeError()) { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "error reading value of ptrace_scope: {0}", E); + + // The original error is probably more interesting than not being able to + // read or interpret ptrace_scope. + return original_error; + } + + // We only have suggestions to provide for 1-3. + switch (*ptrace_scope) { + case 1: + case 2: + return llvm::createStringError( + std::error_code(errno, std::generic_category()), + "The current value of ptrace_scope is %d, which can cause ptrace to " + "fail to attach to a running process. To fix this, run:\n" + "\tsudo sysctl -w kernel.yama.ptrace_scope=0\n" + "For more information, see: " + "https://www.kernel.org/doc/Documentation/security/Yama.txt.", + *ptrace_scope); + case 3: + return llvm::createStringError( + std::error_code(errno, std::generic_category()), + "The current value of ptrace_scope is 3, which will cause ptrace to " + "fail to attach to a running process. This value cannot be changed " + "without rebooting.\n" + "For more information, see: " + "https://www.kernel.org/doc/Documentation/security/Yama.txt."); + case 0: + default: + return original_error; + } +} +#endif + +NativeProcessAIX::Manager::Manager(MainLoop &mainloop) + : NativeProcessProtocol::Manager(mainloop) { + Status status; + m_sigchld_handle = mainloop.RegisterSignal( + SIGCHLD, [this](MainLoopBase &) { SigchldHandler(); }, status); + assert(m_sigchld_handle && status.Success()); +} + +// Public Static Methods + +llvm::Expected> +NativeProcessAIX::Manager::Launch(ProcessLaunchInfo &launch_info, + NativeDelegate &native_delegate) { + Log *log = GetLog(POSIXLog::Process); + + MaybeLogLaunchInfo(launch_info); + + Status status; + ::pid_t pid = ProcessLauncherPosixFork() + .LaunchProcess(launch_info, status) + .GetProcessId(); + LLDB_LOG(log, "pid = {0:x}", pid); + if (status.Fail()) { + LLDB_LOG(log, "failed to launch process: {0}", status); + return status.ToError(); + } + + // Wait for the child process to trap on its call to execve. + int wstatus = 0; + ::pid_t wpid = llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, &wstatus, 0); + assert(wpid == pid); + UNUSED_IF_ASSERT_DISABLED(wpid); + if (!WIFSTOPPED(wstatus)) { + LLDB_LOG(log, "Could not sync with inferior process: wstatus={1}", + WaitStatus::Decode(wstatus)); + return llvm::make_error("Could not sync with inferior process", + llvm::inconvertibleErrorCode()); + } + LLDB_LOG(log, "inferior started, now in stopped state"); + + ProcessInstanceInfo Info; + if (!Host::GetProcessInfo(pid, Info)) { + return llvm::make_error("Cannot get process architectrue", + llvm::inconvertibleErrorCode()); + } + /*llvm::Expected arch_or = + NativeRegisterContextAIX::DetermineArchitecture(pid); + if (!arch_or) + return arch_or.takeError();*/ + + // Set the architecture to the exe architecture. + LLDB_LOG(log, "pid = {0}, detected architecture {1}", pid, + Info.GetArchitecture().GetArchitectureName()); + + return std::unique_ptr(new NativeProcessAIX( + pid, launch_info.GetPTY().ReleasePrimaryFileDescriptor(), native_delegate, + Info.GetArchitecture(), *this, {pid})); +} + +llvm::Expected> +NativeProcessAIX::Manager::Attach( + lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate) { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "pid = {0:x}", pid); + + ProcessInstanceInfo Info; + if (!Host::GetProcessInfo(pid, Info)) { + return llvm::make_error("Cannot get process architectrue", + llvm::inconvertibleErrorCode()); + } + auto tids_or = NativeProcessAIX::Attach(pid); + if (!tids_or) + return tids_or.takeError(); +#if 0 + ArrayRef<::pid_t> tids = *tids_or; + llvm::Expected arch_or = + NativeRegisterContextAIX::DetermineArchitecture(tids[0]); + if (!arch_or) + return arch_or.takeError(); +#endif + + return std::unique_ptr( + new NativeProcessAIX(pid, -1, native_delegate, Info.GetArchitecture(), *this, *tids_or)); +} + +lldb::addr_t NativeProcessAIX::GetSharedLibraryInfoAddress() { + // punt on this for now + return LLDB_INVALID_ADDRESS; +} + +NativeProcessAIX::Extension +NativeProcessAIX::Manager::GetSupportedExtensions() const { + NativeProcessAIX::Extension supported = + Extension::multiprocess | Extension::fork | Extension::vfork | + Extension::pass_signals | Extension::auxv | Extension::libraries_svr4 | + Extension::siginfo_read; + +#ifdef __aarch64__ + // At this point we do not have a process so read auxv directly. + if ((getauxval(AT_HWCAP2) & HWCAP2_MTE)) + supported |= Extension::memory_tagging; +#endif + + return supported; +} + +static std::optional> WaitPid() { + Log *log = GetLog(POSIXLog::Process); + + int status; + ::pid_t wait_pid = llvm::sys::RetryAfterSignal( + -1, ::waitpid, -1, &status, /*__WALL | __WNOTHREAD |*/ WNOHANG); + + if (wait_pid == 0) + return std::nullopt; + + if (wait_pid == -1) { + Status error(errno, eErrorTypePOSIX); + LLDB_LOG(log, "waitpid(-1, &status, _) failed: {1}", error); + return std::nullopt; + } + + WaitStatus wait_status = WaitStatus::Decode(status); + + LLDB_LOG(log, "waitpid(-1, &status, _) = {0}, status = {1}", wait_pid, + wait_status); + return std::make_pair(wait_pid, wait_status); +} + +void NativeProcessAIX::Manager::SigchldHandler() { + Log *log = GetLog(POSIXLog::Process); + while (true) { + auto wait_result = WaitPid(); + if (!wait_result) + return; + lldb::pid_t pid = wait_result->first; + WaitStatus status = wait_result->second; + + // Ask each process whether it wants to handle the event. Each event should + // be handled by exactly one process, but thread creation events require + // special handling. + // Thread creation consists of two events (one on the parent and one on the + // child thread) and they can arrive in any order nondeterministically. The + // parent event carries the information about the child thread, but not + // vice-versa. This means that if the child event arrives first, it may not + // be handled by any process (because it doesn't know the thread belongs to + // it). + bool handled = llvm::any_of(m_processes, [&](NativeProcessAIX *process) { + return process->TryHandleWaitStatus(pid, status); + }); + if (!handled) { + if (status.type == WaitStatus::Stop && status.status == SIGSTOP) { + // Store the thread creation event for later collection. + m_unowned_threads.insert(pid); + } else { + LLDB_LOG(log, "Ignoring waitpid event {0} for pid {1}", status, pid); + } + } + } +} + +void NativeProcessAIX::Manager::CollectThread(::pid_t tid) { + Log *log = GetLog(POSIXLog::Process); + + if (m_unowned_threads.erase(tid)) + return; // We've encountered this thread already. + + // The TID is not tracked yet, let's wait for it to appear. + int status = -1; + LLDB_LOG(log, + "received clone event for tid {0}. tid not tracked yet, " + "waiting for it to appear...", + tid); + ::pid_t wait_pid = + llvm::sys::RetryAfterSignal(-1, ::waitpid, tid, &status, P_ALL/*__WALL*/); + + // It's theoretically possible to get other events if the entire process was + // SIGKILLed before we got a chance to check this. In that case, we'll just + // clean everything up when we get the process exit event. + + LLDB_LOG(log, + "waitpid({0}, &status, __WALL) => {1} (errno: {2}, status = {3})", + tid, wait_pid, errno, WaitStatus::Decode(status)); +} + +// Public Instance Methods + +NativeProcessAIX::NativeProcessAIX(::pid_t pid, int terminal_fd, + NativeDelegate &delegate, + const ArchSpec &arch, Manager &manager, + llvm::ArrayRef<::pid_t> tids) + : NativeProcessProtocol(pid, terminal_fd, delegate), m_manager(manager), + m_arch(arch) { + manager.AddProcess(*this); + if (m_terminal_fd != -1) { + Status status = EnsureFDFlags(m_terminal_fd, O_NONBLOCK); + assert(status.Success()); + } + + for (const auto &tid : tids) { + NativeThreadAIX &thread = AddThread(tid, /*resume*/ false); + ThreadWasCreated(thread); + } + + // Let our process instance know the thread has stopped. + SetCurrentThreadID(tids[0]); + SetState(StateType::eStateStopped, false); +} + +llvm::Expected> NativeProcessAIX::Attach(::pid_t pid) { + Log *log = GetLog(POSIXLog::Process); + + Status status; + if ((status = PtraceWrapper(PT_ATTACH, pid)).Fail()) { + return status.ToError(); + } + + int wpid = + llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, nullptr, WNOHANG); + if (wpid <= 0) { + return llvm::errorCodeToError( + std::error_code(errno, std::generic_category())); + } + + LLDB_LOG(log, "adding pid = {0}", pid); + + std::vector<::pid_t> tids; + tids.push_back(pid); + return std::move(tids); +} + +bool NativeProcessAIX::TryHandleWaitStatus(lldb::pid_t pid, + WaitStatus status) { + if (pid == GetID() && + (status.type == WaitStatus::Exit || status.type == WaitStatus::Signal)) { + // The process exited. We're done monitoring. Report to delegate. + SetExitStatus(status, true); + return true; + } + if (NativeThreadAIX *thread = GetThreadByID(pid)) { + MonitorCallback(*thread, status); + return true; + } + return false; +} + +// Handles all waitpid events from the inferior process. +void NativeProcessAIX::MonitorCallback(NativeThreadAIX &thread, + WaitStatus status) { + Log *log = GetLog(LLDBLog::Process); + + // Certain activities differ based on whether the pid is the tid of the main + // thread. + const bool is_main_thread = (thread.GetID() == GetID()); + + // Handle when the thread exits. + if (status.type == WaitStatus::Exit || status.type == WaitStatus::Signal) { + LLDB_LOG(log, + "got exit status({0}) , tid = {1} ({2} main thread), process " + "state = {3}", + status, thread.GetID(), is_main_thread ? "is" : "is not", + GetState()); + + // This is a thread that exited. Ensure we're not tracking it anymore. + StopTrackingThread(thread); + + assert(!is_main_thread && "Main thread exits handled elsewhere"); + return; + } + + int8_t signo = GetSignalInfo(status); + + // Get details on the signal raised. + if (signo) { + // We have retrieved the signal info. Dispatch appropriately. + if (signo == SIGTRAP) + MonitorSIGTRAP(status, thread); + else + MonitorSignal(status, thread); + } else { + assert(0); + } +} + + +void NativeProcessAIX::MonitorSIGTRAP(const WaitStatus status, + NativeThreadAIX &thread) { + Log *log = GetLog(POSIXLog::Process); + const bool is_main_thread = (thread.GetID() == GetID()); + + NativeRegisterContextAIX ®_ctx = thread.GetRegisterContext(); + const RegisterInfo *pc_info = reg_ctx.GetRegisterInfoByName("pc", 0); + RegisterValue pc_value; + + switch (status.status) { + case SIGTRAP: + // Determine the source of SIGTRAP by checking current instruction: + // if that is trap instruction, then this is breakpoint, otherwise + // this is watchpoint. + reg_ctx.ReadRegister(pc_info, pc_value); + + MonitorBreakpoint(thread); + break; + default: + LLDB_LOG(log, "received unknown SIGTRAP stop event ({0}, pid {1} tid {2}", + status.status, GetID(), thread.GetID()); + MonitorSignal(status, thread); + break; + } +} + +void NativeProcessAIX::MonitorTrace(NativeThreadAIX &thread) { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "received trace event, pid = {0}", thread.GetID()); + + // This thread is currently stopped. + thread.SetStoppedByTrace(); + + StopRunningThreads(thread.GetID()); +} + +void NativeProcessAIX::MonitorBreakpoint(NativeThreadAIX &thread) { + Log *log = GetLog(LLDBLog::Process | LLDBLog::Breakpoints); + LLDB_LOG(log, "received breakpoint event, pid = {0}", thread.GetID()); + + // Mark the thread as stopped at breakpoint. + thread.SetStoppedByBreakpoint(); + FixupBreakpointPCAsNeeded(thread); + + if (m_threads_stepping_with_breakpoint.find(thread.GetID()) != + m_threads_stepping_with_breakpoint.end()) + thread.SetStoppedByTrace(); + + StopRunningThreads(thread.GetID()); +} + +void NativeProcessAIX::MonitorWatchpoint(NativeThreadAIX &thread, + uint32_t wp_index) { + Log *log = GetLog(LLDBLog::Process | LLDBLog::Watchpoints); + LLDB_LOG(log, "received watchpoint event, pid = {0}, wp_index = {1}", + thread.GetID(), wp_index); + + // Mark the thread as stopped at watchpoint. The address is at + // (lldb::addr_t)info->si_addr if we need it. + thread.SetStoppedByWatchpoint(wp_index); + + // We need to tell all other running threads before we notify the delegate + // about this stop. + StopRunningThreads(thread.GetID()); +} + +void NativeProcessAIX::MonitorSignal(const WaitStatus status, + NativeThreadAIX &thread) { + int8_t signo = GetSignalInfo(status); +#if 0 + const bool is_from_llgs = info.si_pid == getpid(); +#endif + + Log *log = GetLog(POSIXLog::Process); + + // POSIX says that process behaviour is undefined after it ignores a SIGFPE, + // SIGILL, SIGSEGV, or SIGBUS *unless* that signal was generated by a kill(2) + // or raise(3). Similarly for tgkill(2) on AIX. + // + // IOW, user generated signals never generate what we consider to be a + // "crash". + // + // Similarly, ACK signals generated by this monitor. + + // Handle the signal. + LLDB_LOG(log, + "received signal {0} ({1}) with code NA, (siginfo pid = {2}, " + "waitpid pid = {3})", + Host::GetSignalAsCString(signo), signo, thread.GetID(), GetID()); + +#if 0 + // Check for thread stop notification. + // FIXME + if (is_from_llgs /*&& (info.si_code == SI_TKILL)*/ && (signo == SIGSTOP)) { + // This is a tgkill()-based stop. + LLDB_LOG(log, "pid {0} tid {1}, thread stopped", GetID(), thread.GetID()); + + // Check that we're not already marked with a stop reason. Note this thread + // really shouldn't already be marked as stopped - if we were, that would + // imply that the kernel signaled us with the thread stopping which we + // handled and marked as stopped, and that, without an intervening resume, + // we received another stop. It is more likely that we are missing the + // marking of a run state somewhere if we find that the thread was marked + // as stopped. + const StateType thread_state = thread.GetState(); + if (!StateIsStoppedState(thread_state, false)) { + // An inferior thread has stopped because of a SIGSTOP we have sent it. + // Generally, these are not important stops and we don't want to report + // them as they are just used to stop other threads when one thread (the + // one with the *real* stop reason) hits a breakpoint (watchpoint, + // etc...). However, in the case of an asynchronous Interrupt(), this + // *is* the real stop reason, so we leave the signal intact if this is + // the thread that was chosen as the triggering thread. + if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID) { + if (m_pending_notification_tid == thread.GetID()) + thread.SetStoppedBySignal(SIGSTOP, &info); + else + thread.SetStoppedWithNoReason(); + + SetCurrentThreadID(thread.GetID()); + SignalIfAllThreadsStopped(); + } else { + // We can end up here if stop was initiated by LLGS but by this time a + // thread stop has occurred - maybe initiated by another event. + Status error = ResumeThread(thread, thread.GetState(), 0); + if (error.Fail()) + LLDB_LOG(log, "failed to resume thread {0}: {1}", thread.GetID(), + error); + } + } else { + LLDB_LOG(log, + "pid {0} tid {1}, thread was already marked as a stopped " + "state (state={2}), leaving stop signal as is", + GetID(), thread.GetID(), thread_state); + SignalIfAllThreadsStopped(); + } + + // Done handling. + return; + } +#endif + + // Check if debugger should stop at this signal or just ignore it and resume + // the inferior. + if (m_signals_to_ignore.contains(signo) || signo == SIGCHLD) { + ResumeThread(thread, thread.GetState(), signo); + return; + } + + // This thread is stopped. + LLDB_LOG(log, "received signal {0}", Host::GetSignalAsCString(signo)); + thread.SetStoppedBySignal(signo); + + // Send a stop to the debugger after we get all other threads to stop. + StopRunningThreads(thread.GetID()); +} + +bool NativeProcessAIX::MonitorClone(NativeThreadAIX &parent, + lldb::pid_t child_pid, int event) { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "parent_tid={0}, child_pid={1}, event={2}", parent.GetID(), + child_pid, event); + + // WaitForCloneNotification(child_pid); + + switch (event) { +#if 0 + case PTRACE_EVENT_CLONE: { + // PTRACE_EVENT_CLONE can either mean a new thread or a new process. + // Try to grab the new process' PGID to figure out which one it is. + // If PGID is the same as the PID, then it's a new process. Otherwise, + // it's a thread. + auto tgid_ret = getPIDForTID(child_pid); + if (tgid_ret != child_pid) { + // A new thread should have PGID matching our process' PID. + assert(!tgid_ret || tgid_ret.getValue() == GetID()); + + NativeThreadAIX &child_thread = AddThread(child_pid, /*resume*/ true); + ThreadWasCreated(child_thread); + + // Resume the parent. + ResumeThread(parent, parent.GetState(), LLDB_INVALID_SIGNAL_NUMBER); + break; + } + } + LLVM_FALLTHROUGH; + case PTRACE_EVENT_FORK: + case PTRACE_EVENT_VFORK: { + bool is_vfork = event == PTRACE_EVENT_VFORK; + std::unique_ptr child_process{new NativeProcessAIX( + static_cast<::pid_t>(child_pid), m_terminal_fd, m_delegate, m_arch, + m_main_loop, {static_cast<::pid_t>(child_pid)})}; + if (!is_vfork) + child_process->m_software_breakpoints = m_software_breakpoints; + + Extension expected_ext = is_vfork ? Extension::vfork : Extension::fork; + if (bool(m_enabled_extensions & expected_ext)) { + m_delegate.NewSubprocess(this, std::move(child_process)); + // NB: non-vfork clone() is reported as fork + parent.SetStoppedByFork(is_vfork, child_pid); + StopRunningThreads(parent.GetID()); + } else { + child_process->Detach(); + ResumeThread(parent, parent.GetState(), LLDB_INVALID_SIGNAL_NUMBER); + } + break; + } +#endif + default: + llvm_unreachable("unknown clone_info.event"); + } + + return true; +} + +bool NativeProcessAIX::SupportHardwareSingleStepping() const { + return false; +} + +Status NativeProcessAIX::Resume(const ResumeActionList &resume_actions) { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "pid {0}", GetID()); + + bool software_single_step = !SupportHardwareSingleStepping(); + + if (software_single_step) { + for (const auto &thread : m_threads) { + assert(thread && "thread list should not contain NULL threads"); + + const ResumeAction *const action = + resume_actions.GetActionForThread(thread->GetID(), true); + if (action == nullptr) + continue; + + if (action->state == eStateStepping) { + Status error = SetupSoftwareSingleStepping( + static_cast(*thread)); + if (error.Fail()) + return error; + } + } + } + + for (const auto &thread : m_threads) { + assert(thread && "thread list should not contain NULL threads"); + + const ResumeAction *const action = + resume_actions.GetActionForThread(thread->GetID(), true); + + if (action == nullptr) { + LLDB_LOG(log, "no action specified for pid {0} tid {1}", GetID(), + thread->GetID()); + continue; + } + + LLDB_LOG(log, "processing resume action state {0} for pid {1} tid {2}", + action->state, GetID(), thread->GetID()); + + switch (action->state) { + case eStateRunning: + case eStateStepping: { + // Run the thread, possibly feeding it the signal. + const int signo = action->signal; + Status error = ResumeThread(static_cast(*thread), + action->state, signo); + if (error.Fail()) + return Status("NativeProcessAIX::%s: failed to resume thread " + "for pid %" PRIu64 ", tid %" PRIu64 ", error = %s", + __FUNCTION__, GetID(), thread->GetID(), + error.AsCString()); + + break; + } + + case eStateSuspended: + case eStateStopped: + break; + + default: + return Status("NativeProcessAIX::%s (): unexpected state %s specified " + "for pid %" PRIu64 ", tid %" PRIu64, + __FUNCTION__, StateAsCString(action->state), GetID(), + thread->GetID()); + } + } + + return Status(); +} + +Status NativeProcessAIX::Halt() { + Status error; + + if (kill(GetID(), SIGSTOP) != 0) + error.SetErrorToErrno(); + + return error; +} + +Status NativeProcessAIX::Detach() { + Status error; + + // Tell ptrace to detach from the process. + if (GetID() == LLDB_INVALID_PROCESS_ID) + return error; + + // Cancel out any SIGSTOPs we may have sent while stopping the process. + // Otherwise, the process may stop as soon as we detach from it. + kill(GetID(), SIGCONT); + + for (const auto &thread : m_threads) { + Status e = Detach(thread->GetID()); + if (e.Fail()) + error = + e; // Save the error, but still attempt to detach from other threads. + } + + return error; +} + +Status NativeProcessAIX::Signal(int signo) { + Status error; + + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "sending signal {0} ({1}) to pid {1}", signo, + Host::GetSignalAsCString(signo), GetID()); + + if (kill(GetID(), signo)) + error.SetErrorToErrno(); + + return error; +} + +Status NativeProcessAIX::Interrupt() { + // Pick a running thread (or if none, a not-dead stopped thread) as the + // chosen thread that will be the stop-reason thread. + Log *log = GetLog(POSIXLog::Process); + + NativeThreadProtocol *running_thread = nullptr; + NativeThreadProtocol *stopped_thread = nullptr; + + LLDB_LOG(log, "selecting running thread for interrupt target"); + for (const auto &thread : m_threads) { + // If we have a running or stepping thread, we'll call that the target of + // the interrupt. + const auto thread_state = thread->GetState(); + if (thread_state == eStateRunning || thread_state == eStateStepping) { + running_thread = thread.get(); + break; + } else if (!stopped_thread && StateIsStoppedState(thread_state, true)) { + // Remember the first non-dead stopped thread. We'll use that as a + // backup if there are no running threads. + stopped_thread = thread.get(); + } + } + + if (!running_thread && !stopped_thread) { + Status error("found no running/stepping or live stopped threads as target " + "for interrupt"); + LLDB_LOG(log, "skipping due to error: {0}", error); + + return error; + } + + NativeThreadProtocol *deferred_signal_thread = + running_thread ? running_thread : stopped_thread; + + LLDB_LOG(log, "pid {0} {1} tid {2} chosen for interrupt target", GetID(), + running_thread ? "running" : "stopped", + deferred_signal_thread->GetID()); + + StopRunningThreads(deferred_signal_thread->GetID()); + + return Status(); +} + +Status NativeProcessAIX::Kill() { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "pid {0}", GetID()); + + Status error; + + switch (m_state) { + case StateType::eStateInvalid: + case StateType::eStateExited: + case StateType::eStateCrashed: + case StateType::eStateDetached: + case StateType::eStateUnloaded: + // Nothing to do - the process is already dead. + LLDB_LOG(log, "ignored for PID {0} due to current state: {1}", GetID(), + m_state); + return error; + + case StateType::eStateConnected: + case StateType::eStateAttaching: + case StateType::eStateLaunching: + case StateType::eStateStopped: + case StateType::eStateRunning: + case StateType::eStateStepping: + case StateType::eStateSuspended: + // We can try to kill a process in these states. + break; + } + + if (kill(GetID(), SIGKILL) != 0) { + error.SetErrorToErrno(); + return error; + } + + return error; +} + +Status NativeProcessAIX::GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) { + // FIXME review that the final memory region returned extends to the end of + // the virtual address space, + // with no perms if it is not mapped. + + // Use an approach that reads memory regions from /proc/{pid}/maps. Assume + // proc maps entries are in ascending order. + // FIXME assert if we find differently. + + if (m_supports_mem_region == LazyBool::eLazyBoolNo) { + // We're done. + return Status("unsupported"); + } + + Status error = PopulateMemoryRegionCache(); + if (error.Fail()) { + return error; + } + + lldb::addr_t prev_base_address = 0; + + // FIXME start by finding the last region that is <= target address using + // binary search. Data is sorted. + // There can be a ton of regions on pthreads apps with lots of threads. + for (auto it = m_mem_region_cache.begin(); it != m_mem_region_cache.end(); + ++it) { + MemoryRegionInfo &proc_entry_info = it->first; + + // Sanity check assumption that /proc/{pid}/maps entries are ascending. + assert((proc_entry_info.GetRange().GetRangeBase() >= prev_base_address) && + "descending /proc/pid/maps entries detected, unexpected"); + prev_base_address = proc_entry_info.GetRange().GetRangeBase(); + UNUSED_IF_ASSERT_DISABLED(prev_base_address); + + // If the target address comes before this entry, indicate distance to next + // region. + if (load_addr < proc_entry_info.GetRange().GetRangeBase()) { + range_info.GetRange().SetRangeBase(load_addr); + range_info.GetRange().SetByteSize( + proc_entry_info.GetRange().GetRangeBase() - load_addr); + range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); + + return error; + } else if (proc_entry_info.GetRange().Contains(load_addr)) { + // The target address is within the memory region we're processing here. + range_info = proc_entry_info; + return error; + } + + // The target memory address comes somewhere after the region we just + // parsed. + } + + // If we made it here, we didn't find an entry that contained the given + // address. Return the load_addr as start and the amount of bytes betwwen + // load address and the end of the memory as size. + range_info.GetRange().SetRangeBase(load_addr); + range_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); + range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); + return error; +} + +Status NativeProcessAIX::PopulateMemoryRegionCache() { + Log *log = GetLog(POSIXLog::Process); + + // If our cache is empty, pull the latest. There should always be at least + // one memory region if memory region handling is supported. + if (!m_mem_region_cache.empty()) { + LLDB_LOG(log, "reusing {0} cached memory region entries", + m_mem_region_cache.size()); + return Status(); + } + + Status Result; +#if 0 + AIXMapCallback callback = [&](llvm::Expected Info) { + if (Info) { + FileSpec file_spec(Info->GetName().GetCString()); + FileSystem::Instance().Resolve(file_spec); + m_mem_region_cache.emplace_back(*Info, file_spec); + return true; + } + + Result = Info.takeError(); + m_supports_mem_region = LazyBool::eLazyBoolNo; + LLDB_LOG(log, "failed to parse proc maps: {0}", Result); + return false; + }; + + // AIX kernel since 2.6.14 has /proc/{pid}/smaps + // if CONFIG_PROC_PAGE_MONITOR is enabled + auto BufferOrError = getProcFile(GetID(), GetCurrentThreadID(), "smaps"); + if (BufferOrError) + ParseAIXSMapRegions(BufferOrError.get()->getBuffer(), callback); + else { + BufferOrError = getProcFile(GetID(), GetCurrentThreadID(), "maps"); + if (!BufferOrError) { + m_supports_mem_region = LazyBool::eLazyBoolNo; + return BufferOrError.getError(); + } + + ParseAIXMapRegions(BufferOrError.get()->getBuffer(), callback); + } + + if (Result.Fail()) + return Result; + + if (m_mem_region_cache.empty()) { + // No entries after attempting to read them. This shouldn't happen if + // /proc/{pid}/maps is supported. Assume we don't support map entries via + // procfs. + m_supports_mem_region = LazyBool::eLazyBoolNo; + LLDB_LOG(log, + "failed to find any procfs maps entries, assuming no support " + "for memory region metadata retrieval"); + return Status("not supported"); + } + + LLDB_LOG(log, "read {0} memory region entries from /proc/{1}/maps", + m_mem_region_cache.size(), GetID()); + + // We support memory retrieval, remember that. + m_supports_mem_region = LazyBool::eLazyBoolYes; +#endif + return Status(); +} + +void NativeProcessAIX::DoStopIDBumped(uint32_t newBumpId) { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "newBumpId={0}", newBumpId); + LLDB_LOG(log, "clearing {0} entries from memory region cache", + m_mem_region_cache.size()); + m_mem_region_cache.clear(); +} + +llvm::Expected +NativeProcessAIX::Syscall(llvm::ArrayRef args) { + PopulateMemoryRegionCache(); + auto region_it = llvm::find_if(m_mem_region_cache, [](const auto &pair) { + return pair.first.GetExecutable() == MemoryRegionInfo::eYes && + pair.first.GetShared() != MemoryRegionInfo::eYes; + }); + if (region_it == m_mem_region_cache.end()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "No executable memory region found!"); + + addr_t exe_addr = region_it->first.GetRange().GetRangeBase(); + + NativeThreadAIX &thread = *GetCurrentThread(); + assert(thread.GetState() == eStateStopped); + NativeRegisterContextAIX ®_ctx = thread.GetRegisterContext(); + + NativeRegisterContextAIX::SyscallData syscall_data = + *reg_ctx.GetSyscallData(); + + WritableDataBufferSP registers_sp; + if (llvm::Error Err = reg_ctx.ReadAllRegisterValues(registers_sp).ToError()) + return std::move(Err); + auto restore_regs = llvm::make_scope_exit( + [&] { reg_ctx.WriteAllRegisterValues(registers_sp); }); + + llvm::SmallVector memory(syscall_data.Insn.size()); + size_t bytes_read; + if (llvm::Error Err = + ReadMemory(exe_addr, memory.data(), memory.size(), bytes_read) + .ToError()) { + return std::move(Err); + } + + auto restore_mem = llvm::make_scope_exit( + [&] { WriteMemory(exe_addr, memory.data(), memory.size(), bytes_read); }); + + if (llvm::Error Err = reg_ctx.SetPC(exe_addr).ToError()) + return std::move(Err); + + for (const auto &zip : llvm::zip_first(args, syscall_data.Args)) { + if (llvm::Error Err = + reg_ctx + .WriteRegisterFromUnsigned(std::get<1>(zip), std::get<0>(zip)) + .ToError()) { + return std::move(Err); + } + } + if (llvm::Error Err = WriteMemory(exe_addr, syscall_data.Insn.data(), + syscall_data.Insn.size(), bytes_read) + .ToError()) + return std::move(Err); + + m_mem_region_cache.clear(); + + // With software single stepping the syscall insn buffer must also include a + // trap instruction to stop the process. + int req = SupportHardwareSingleStepping() ? PTRACE_SINGLESTEP : PTRACE_CONT; + if (llvm::Error Err = + PtraceWrapper(req, thread.GetID(), nullptr, nullptr).ToError()) + return std::move(Err); + + //FIXME + int status; + ::pid_t wait_pid = llvm::sys::RetryAfterSignal(-1, ::waitpid, thread.GetID(), + &status, P_ALL/*__WALL*/); + if (wait_pid == -1) { + return llvm::errorCodeToError( + std::error_code(errno, std::generic_category())); + } + assert((unsigned)wait_pid == thread.GetID()); + + uint64_t result = reg_ctx.ReadRegisterAsUnsigned(syscall_data.Result, -ESRCH); + + // Values larger than this are actually negative errno numbers. + uint64_t errno_threshold = + (uint64_t(-1) >> (64 - 8 * m_arch.GetAddressByteSize())) - 0x1000; + if (result > errno_threshold) { + return llvm::errorCodeToError( + std::error_code(-result & 0xfff, std::generic_category())); + } + + return result; +} + +llvm::Expected +NativeProcessAIX::AllocateMemory(size_t size, uint32_t permissions) { + + std::optional mmap_data = + GetCurrentThread()->GetRegisterContext().GetMmapData(); + if (!mmap_data) + return llvm::make_error(); + + unsigned prot = PROT_NONE; + assert((permissions & (ePermissionsReadable | ePermissionsWritable | + ePermissionsExecutable)) == permissions && + "Unknown permission!"); + if (permissions & ePermissionsReadable) + prot |= PROT_READ; + if (permissions & ePermissionsWritable) + prot |= PROT_WRITE; + if (permissions & ePermissionsExecutable) + prot |= PROT_EXEC; + + llvm::Expected Result = + Syscall({mmap_data->SysMmap, 0, size, prot, MAP_ANONYMOUS | MAP_PRIVATE, + uint64_t(-1), 0}); + if (Result) + m_allocated_memory.try_emplace(*Result, size); + return Result; +} + +llvm::Error NativeProcessAIX::DeallocateMemory(lldb::addr_t addr) { + std::optional mmap_data = + GetCurrentThread()->GetRegisterContext().GetMmapData(); + if (!mmap_data) + return llvm::make_error(); + + auto it = m_allocated_memory.find(addr); + if (it == m_allocated_memory.end()) + return llvm::createStringError(llvm::errc::invalid_argument, + "Memory not allocated by the debugger."); + + llvm::Expected Result = + Syscall({mmap_data->SysMunmap, addr, it->second}); + if (!Result) + return Result.takeError(); + + m_allocated_memory.erase(it); + return llvm::Error::success(); +} + +Status NativeProcessAIX::ReadMemoryTags(int32_t type, lldb::addr_t addr, + size_t len, + std::vector &tags) { + llvm::Expected details = + GetCurrentThread()->GetRegisterContext().GetMemoryTaggingDetails(type); + if (!details) + return Status(details.takeError()); + + // Ignore 0 length read + if (!len) + return Status(); + + // lldb will align the range it requests but it is not required to by + // the protocol so we'll do it again just in case. + // Remove tag bits too. Ptrace calls may work regardless but that + // is not a guarantee. + MemoryTagManager::TagRange range(details->manager->RemoveTagBits(addr), len); + range = details->manager->ExpandToGranule(range); + + // Allocate enough space for all tags to be read + size_t num_tags = range.GetByteSize() / details->manager->GetGranuleSize(); + tags.resize(num_tags * details->manager->GetTagSizeInBytes()); + + struct iovec tags_iovec; + uint8_t *dest = tags.data(); + lldb::addr_t read_addr = range.GetRangeBase(); + + // This call can return partial data so loop until we error or + // get all tags back. + while (num_tags) { + tags_iovec.iov_base = dest; + tags_iovec.iov_len = num_tags; + + Status error = NativeProcessAIX::PtraceWrapper( + details->ptrace_read_req, GetCurrentThreadID(), + reinterpret_cast(read_addr), static_cast(&tags_iovec), + 0, nullptr); + + if (error.Fail()) { + // Discard partial reads + tags.resize(0); + return error; + } + + size_t tags_read = tags_iovec.iov_len; + assert(tags_read && (tags_read <= num_tags)); + + dest += tags_read * details->manager->GetTagSizeInBytes(); + read_addr += details->manager->GetGranuleSize() * tags_read; + num_tags -= tags_read; + } + + return Status(); +} + +Status NativeProcessAIX::WriteMemoryTags(int32_t type, lldb::addr_t addr, + size_t len, + const std::vector &tags) { + llvm::Expected details = + GetCurrentThread()->GetRegisterContext().GetMemoryTaggingDetails(type); + if (!details) + return Status(details.takeError()); + + // Ignore 0 length write + if (!len) + return Status(); + + // lldb will align the range it requests but it is not required to by + // the protocol so we'll do it again just in case. + // Remove tag bits too. Ptrace calls may work regardless but that + // is not a guarantee. + MemoryTagManager::TagRange range(details->manager->RemoveTagBits(addr), len); + range = details->manager->ExpandToGranule(range); + + // Not checking number of tags here, we may repeat them below + llvm::Expected> unpacked_tags_or_err = + details->manager->UnpackTagsData(tags); + if (!unpacked_tags_or_err) + return Status(unpacked_tags_or_err.takeError()); + + llvm::Expected> repeated_tags_or_err = + details->manager->RepeatTagsForRange(*unpacked_tags_or_err, range); + if (!repeated_tags_or_err) + return Status(repeated_tags_or_err.takeError()); + + // Repack them for ptrace to use + llvm::Expected> final_tag_data = + details->manager->PackTags(*repeated_tags_or_err); + if (!final_tag_data) + return Status(final_tag_data.takeError()); + + struct iovec tags_vec; + uint8_t *src = final_tag_data->data(); + lldb::addr_t write_addr = range.GetRangeBase(); + // unpacked tags size because the number of bytes per tag might not be 1 + size_t num_tags = repeated_tags_or_err->size(); + + // This call can partially write tags, so we loop until we + // error or all tags have been written. + while (num_tags > 0) { + tags_vec.iov_base = src; + tags_vec.iov_len = num_tags; + + Status error = NativeProcessAIX::PtraceWrapper( + details->ptrace_write_req, GetCurrentThreadID(), + reinterpret_cast(write_addr), static_cast(&tags_vec), 0, + nullptr); + + if (error.Fail()) { + // Don't attempt to restore the original values in the case of a partial + // write + return error; + } + + size_t tags_written = tags_vec.iov_len; + assert(tags_written && (tags_written <= num_tags)); + + src += tags_written * details->manager->GetTagSizeInBytes(); + write_addr += details->manager->GetGranuleSize() * tags_written; + num_tags -= tags_written; + } + + return Status(); +} + +size_t NativeProcessAIX::UpdateThreads() { + // The NativeProcessAIX monitoring threads are always up to date with + // respect to thread state and they keep the thread list populated properly. + // All this method needs to do is return the thread count. + return m_threads.size(); +} + +Status NativeProcessAIX::SetBreakpoint(lldb::addr_t addr, uint32_t size, + bool hardware) { + if (hardware) + return SetHardwareBreakpoint(addr, size); + else + return SetSoftwareBreakpoint(addr, size); +} + +Status NativeProcessAIX::RemoveBreakpoint(lldb::addr_t addr, bool hardware) { + if (hardware) + return RemoveHardwareBreakpoint(addr); + else + return NativeProcessProtocol::RemoveBreakpoint(addr); +} + +llvm::Expected> +NativeProcessAIX::GetSoftwareBreakpointTrapOpcode(size_t size_hint) { + // The ARM reference recommends the use of 0xe7fddefe and 0xdefe but the + // linux kernel does otherwise. + static const uint8_t g_arm_opcode[] = {0xf0, 0x01, 0xf0, 0xe7}; + static const uint8_t g_thumb_opcode[] = {0x01, 0xde}; + + switch (GetArchitecture().GetMachine()) { + case llvm::Triple::arm: + switch (size_hint) { + case 2: + return llvm::ArrayRef(g_thumb_opcode); + case 4: + return llvm::ArrayRef(g_arm_opcode); + default: + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Unrecognised trap opcode size hint!"); + } + default: + return NativeProcessProtocol::GetSoftwareBreakpointTrapOpcode(size_hint); + } +} + +Status NativeProcessAIX::ReadMemory(lldb::addr_t addr, void *buf, size_t size, + size_t &bytes_read) { + unsigned char *dst = static_cast(buf); + size_t remainder; + long data; + + Log *log = GetLog(POSIXLog::Memory); + LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size); + + for (bytes_read = 0; bytes_read < size; bytes_read += remainder) { + Status error = NativeProcessAIX::PtraceWrapper( + PT_READ_BLOCK, GetCurrentThreadID(), (void *)addr, nullptr, sizeof(data), &data); + if (error.Fail()) + return error; + + remainder = size - bytes_read; + remainder = remainder > k_ptrace_word_size ? k_ptrace_word_size : remainder; + + // Copy the data into our buffer + memcpy(dst, &data, remainder); + + LLDB_LOG(log, "[{0:x}]:{1:x}", addr, data); + addr += k_ptrace_word_size; + dst += k_ptrace_word_size; + } + return Status(); +} + +Status NativeProcessAIX::WriteMemory(lldb::addr_t addr, const void *buf, + size_t size, size_t &bytes_written) { + const unsigned char *src = static_cast(buf); + size_t remainder; + Status error; + + Log *log = GetLog(POSIXLog::Memory); + LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size); + + error = NativeProcessAIX::PtraceWrapper( + PT_WRITE_BLOCK, GetCurrentThreadID(), (void *)addr, nullptr, (int)size, (long *)buf); + if (error.Fail()) + return error; + + bytes_written = size; + return error; +} + +int8_t NativeProcessAIX::GetSignalInfo(WaitStatus wstatus) const { + return wstatus.status; +} + +Status NativeProcessAIX::GetEventMessage(lldb::tid_t tid, + unsigned long *message) { + //FIXME + return PtraceWrapper(PT_CLEAR/*PTRACE_GETEVENTMSG*/, tid, nullptr, message); +} + +Status NativeProcessAIX::Detach(lldb::tid_t tid) { + if (tid == LLDB_INVALID_THREAD_ID) + return Status(); + + return PtraceWrapper(PT_DETACH, tid); +} + +bool NativeProcessAIX::HasThreadNoLock(lldb::tid_t thread_id) { + for (const auto &thread : m_threads) { + assert(thread && "thread list should not contain NULL threads"); + if (thread->GetID() == thread_id) { + // We have this thread. + return true; + } + } + + // We don't have this thread. + return false; +} + +void NativeProcessAIX::StopTrackingThread(NativeThreadAIX &thread) { + Log *const log = GetLog(POSIXLog::Thread); + lldb::tid_t thread_id = thread.GetID(); + LLDB_LOG(log, "tid: {0}", thread_id); + + auto it = llvm::find_if(m_threads, [&](const auto &thread_up) { + return thread_up.get() == &thread; + }); + assert(it != m_threads.end()); + m_threads.erase(it); + + NotifyTracersOfThreadDestroyed(thread_id); + SignalIfAllThreadsStopped(); +} + +void NativeProcessAIX::NotifyTracersProcessDidStop() { +} + +void NativeProcessAIX::NotifyTracersProcessWillResume() { +} + +Status NativeProcessAIX::NotifyTracersOfNewThread(lldb::tid_t tid) { + Log *log = GetLog(POSIXLog::Thread); + Status error; + return error; +} + +Status NativeProcessAIX::NotifyTracersOfThreadDestroyed(lldb::tid_t tid) { + Log *log = GetLog(POSIXLog::Thread); + Status error; + return error; +} + +NativeThreadAIX &NativeProcessAIX::AddThread(lldb::tid_t thread_id, + bool resume) { + Log *log = GetLog(POSIXLog::Thread); + LLDB_LOG(log, "pid {0} adding thread with tid {1}", GetID(), thread_id); + + assert(!HasThreadNoLock(thread_id) && + "attempted to add a thread by id that already exists"); + + // If this is the first thread, save it as the current thread + if (m_threads.empty()) + SetCurrentThreadID(thread_id); + + m_threads.push_back(std::make_unique(*this, thread_id)); + NativeThreadAIX &thread = + static_cast(*m_threads.back()); + + Status tracing_error = NotifyTracersOfNewThread(thread.GetID()); + if (tracing_error.Fail()) { + thread.SetStoppedByProcessorTrace(tracing_error.AsCString()); + StopRunningThreads(thread.GetID()); + } else if (resume) + ResumeThread(thread, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER); + else + thread.SetStoppedBySignal(SIGSTOP); + + return thread; +} + +Status NativeProcessAIX::GetLoadedModuleFileSpec(const char *module_path, + FileSpec &file_spec) { + Status error = PopulateMemoryRegionCache(); + if (error.Fail()) + return error; + + FileSpec module_file_spec(module_path); + FileSystem::Instance().Resolve(module_file_spec); + + file_spec.Clear(); + for (const auto &it : m_mem_region_cache) { + if (it.second.GetFilename() == module_file_spec.GetFilename()) { + file_spec = it.second; + return Status(); + } + } + return Status("Module file (%s) not found in /proc/%" PRIu64 "/maps file!", + module_file_spec.GetFilename().AsCString(), GetID()); +} + +Status NativeProcessAIX::GetFileLoadAddress(const llvm::StringRef &file_name, + lldb::addr_t &load_addr) { + load_addr = LLDB_INVALID_ADDRESS; + + NativeThreadAIX &thread = *GetCurrentThread(); + NativeRegisterContextAIX ®_ctx = thread.GetRegisterContext(); + + // FIXME: buffer size + struct ld_xinfo info[64]; + if (ptrace64(PT_LDXINFO, reg_ctx.GetThread().GetID(), (long long)&(info[0]), sizeof(info), nullptr) == 0) { + load_addr = (unsigned long)info[0].ldinfo_textorg; + return Status(); + } + return Status("No load address found for specified file."); +} + +NativeThreadAIX *NativeProcessAIX::GetThreadByID(lldb::tid_t tid) { + return static_cast( + NativeProcessProtocol::GetThreadByID(tid)); +} + +NativeThreadAIX *NativeProcessAIX::GetCurrentThread() { + return static_cast( + NativeProcessProtocol::GetCurrentThread()); +} + +Status NativeProcessAIX::ResumeThread(NativeThreadAIX &thread, + lldb::StateType state, int signo) { + Log *const log = GetLog(POSIXLog::Thread); + LLDB_LOG(log, "tid: {0}", thread.GetID()); + + // Before we do the resume below, first check if we have a pending stop + // notification that is currently waiting for all threads to stop. This is + // potentially a buggy situation since we're ostensibly waiting for threads + // to stop before we send out the pending notification, and here we are + // resuming one before we send out the pending stop notification. + if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID) { + LLDB_LOG(log, + "about to resume tid {0} per explicit request but we have a " + "pending stop notification (tid {1}) that is actively " + "waiting for this thread to stop. Valid sequence of events?", + thread.GetID(), m_pending_notification_tid); + } + + // Request a resume. We expect this to be synchronous and the system to + // reflect it is running after this completes. + switch (state) { + case eStateRunning: { + const auto resume_result = thread.Resume(signo); + if (resume_result.Success()) + SetState(eStateRunning, true); + return resume_result; + } + case eStateStepping: { + const auto step_result = thread.SingleStep(signo); + if (step_result.Success()) + SetState(eStateRunning, true); + return step_result; + } + default: + LLDB_LOG(log, "Unhandled state {0}.", state); + llvm_unreachable("Unhandled state for resume"); + } +} + +//===----------------------------------------------------------------------===// + +void NativeProcessAIX::StopRunningThreads(const lldb::tid_t triggering_tid) { + Log *const log = GetLog(POSIXLog::Thread); + LLDB_LOG(log, "about to process event: (triggering_tid: {0})", + triggering_tid); + + m_pending_notification_tid = triggering_tid; + + // Request a stop for all the thread stops that need to be stopped and are + // not already known to be stopped. + for (const auto &thread : m_threads) { + if (StateIsRunningState(thread->GetState())) + static_cast(thread.get())->RequestStop(); + } + + SignalIfAllThreadsStopped(); + LLDB_LOG(log, "event processing done"); +} + +void NativeProcessAIX::SignalIfAllThreadsStopped() { + if (m_pending_notification_tid == LLDB_INVALID_THREAD_ID) + return; // No pending notification. Nothing to do. + + for (const auto &thread_sp : m_threads) { + if (StateIsRunningState(thread_sp->GetState())) + return; // Some threads are still running. Don't signal yet. + } + + // We have a pending notification and all threads have stopped. + Log *log = GetLog(LLDBLog::Process | LLDBLog::Breakpoints); + + // Clear any temporary breakpoints we used to implement software single + // stepping. + for (const auto &thread_info : m_threads_stepping_with_breakpoint) { + Status error = RemoveBreakpoint(thread_info.second); + if (error.Fail()) + LLDB_LOG(log, "pid = {0} remove stepping breakpoint: {1}", + thread_info.first, error); + } + m_threads_stepping_with_breakpoint.clear(); + + // Notify the delegate about the stop + SetCurrentThreadID(m_pending_notification_tid); + SetState(StateType::eStateStopped, true); + m_pending_notification_tid = LLDB_INVALID_THREAD_ID; +} + +void NativeProcessAIX::ThreadWasCreated(NativeThreadAIX &thread) { + Log *const log = GetLog(POSIXLog::Thread); + LLDB_LOG(log, "tid: {0}", thread.GetID()); + + if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID && + StateIsRunningState(thread.GetState())) { + // We will need to wait for this new thread to stop as well before firing + // the notification. + thread.RequestStop(); + } +} + +#define DECLARE_REGISTER_INFOS_PPC64LE_STRUCT +#include "Plugins/Process/Utility/RegisterInfos_ppc64le.h" +#undef DECLARE_REGISTER_INFOS_PPC64LE_STRUCT + +static void GetRegister(lldb::pid_t pid, long long addr, void *buf) { + uint64_t val = 0; + ptrace64(PT_READ_GPR, pid, addr, 0, (int *)&val); + *(uint64_t *)buf = llvm::byteswap(val); +} + +static void SetRegister(lldb::pid_t pid, long long addr, void *buf) { + uint64_t val = llvm::byteswap(*(uint64_t *)buf); + ptrace64(PT_WRITE_GPR, pid, addr, 0, (int *)&val); +} + +static void GetFPRegister(lldb::pid_t pid, long long addr, void *buf) { + uint64_t val = 0; + ptrace64(PT_READ_FPR, pid, addr, 0, (int *)&val); + *(uint64_t *)buf = llvm::byteswap(val); +} + +static void GetVMRegister(lldb::tid_t tid, long long addr, void *buf) { + uint64_t val = 0; + ptrace64(PTT_READ_VEC, tid, addr, 0, (int *)&val); + //*(uint64_t *)buf = llvm::byteswap(val); +} + +static void GetVSRegister(lldb::tid_t tid, long long addr, void *buf) { + uint64_t val = 0; + ptrace64(PTT_READ_VSX, tid, addr, 0, (int *)&val); + //*(uint64_t *)buf = llvm::byteswap(val); +} + +// Wrapper for ptrace to catch errors and log calls. Note that ptrace sets +// errno on error because -1 can be a valid result (i.e. for PTRACE_PEEK*) +Status NativeProcessAIX::PtraceWrapper(int req, lldb::pid_t pid, void *addr, + void *data, size_t data_size, + long *result) { + Status error; + long int ret; + + Log *log = GetLog(POSIXLog::Ptrace); + + PtraceDisplayBytes(req, data, data_size); + + errno = 0; + + // for PTT_* + const char procdir[] = "/proc/"; + const char lwpdir[] = "/lwp/"; + std::string process_task_dir = procdir + std::to_string(pid) + lwpdir; + DIR *dirproc = opendir(process_task_dir.c_str()); + + lldb::tid_t tid = 0; + if (dirproc) { + struct dirent *direntry = nullptr; + while ((direntry = readdir(dirproc)) != nullptr) { + if (strcmp(direntry->d_name, ".") == 0 || strcmp(direntry->d_name, "..") == 0) { + continue; + } + tid = atoi(direntry->d_name); + break; + } + closedir(dirproc); + } + + if (req == PTRACE_GETREGS) { + GetRegister(pid, GPR0, &(((GPR *)data)->r0)); + GetRegister(pid, GPR1, &(((GPR *)data)->r1)); + GetRegister(pid, GPR2, &(((GPR *)data)->r2)); + GetRegister(pid, GPR3, &(((GPR *)data)->r3)); + GetRegister(pid, GPR4, &(((GPR *)data)->r4)); + GetRegister(pid, GPR5, &(((GPR *)data)->r5)); + GetRegister(pid, GPR6, &(((GPR *)data)->r6)); + GetRegister(pid, GPR7, &(((GPR *)data)->r7)); + GetRegister(pid, GPR8, &(((GPR *)data)->r8)); + GetRegister(pid, GPR9, &(((GPR *)data)->r9)); + GetRegister(pid, GPR10, &(((GPR *)data)->r10)); + GetRegister(pid, GPR11, &(((GPR *)data)->r11)); + GetRegister(pid, GPR12, &(((GPR *)data)->r12)); + GetRegister(pid, GPR13, &(((GPR *)data)->r13)); + GetRegister(pid, GPR14, &(((GPR *)data)->r14)); + GetRegister(pid, GPR15, &(((GPR *)data)->r15)); + GetRegister(pid, GPR16, &(((GPR *)data)->r16)); + GetRegister(pid, GPR17, &(((GPR *)data)->r17)); + GetRegister(pid, GPR18, &(((GPR *)data)->r18)); + GetRegister(pid, GPR19, &(((GPR *)data)->r19)); + GetRegister(pid, GPR20, &(((GPR *)data)->r20)); + GetRegister(pid, GPR21, &(((GPR *)data)->r21)); + GetRegister(pid, GPR22, &(((GPR *)data)->r22)); + GetRegister(pid, GPR23, &(((GPR *)data)->r23)); + GetRegister(pid, GPR24, &(((GPR *)data)->r24)); + GetRegister(pid, GPR25, &(((GPR *)data)->r25)); + GetRegister(pid, GPR26, &(((GPR *)data)->r26)); + GetRegister(pid, GPR27, &(((GPR *)data)->r27)); + GetRegister(pid, GPR28, &(((GPR *)data)->r28)); + GetRegister(pid, GPR29, &(((GPR *)data)->r29)); + GetRegister(pid, GPR30, &(((GPR *)data)->r30)); + GetRegister(pid, GPR31, &(((GPR *)data)->r31)); + GetRegister(pid, IAR, &(((GPR *)data)->pc)); + GetRegister(pid, MSR, &(((GPR *)data)->msr)); + //FIXME: origr3/softe/trap on AIX? + GetRegister(pid, CTR, &(((GPR *)data)->ctr)); + GetRegister(pid, LR, &(((GPR *)data)->lr)); + GetRegister(pid, XER, &(((GPR *)data)->xer)); + GetRegister(pid, CR, &(((GPR *)data)->cr)); + } else if (req == PTRACE_SETREGS) { + SetRegister(pid, GPR0, &(((GPR *)data)->r0)); + SetRegister(pid, GPR1, &(((GPR *)data)->r1)); + SetRegister(pid, GPR2, &(((GPR *)data)->r2)); + SetRegister(pid, GPR3, &(((GPR *)data)->r3)); + SetRegister(pid, GPR4, &(((GPR *)data)->r4)); + SetRegister(pid, GPR5, &(((GPR *)data)->r5)); + SetRegister(pid, GPR6, &(((GPR *)data)->r6)); + SetRegister(pid, GPR7, &(((GPR *)data)->r7)); + SetRegister(pid, GPR8, &(((GPR *)data)->r8)); + SetRegister(pid, GPR9, &(((GPR *)data)->r9)); + SetRegister(pid, GPR10, &(((GPR *)data)->r10)); + SetRegister(pid, GPR11, &(((GPR *)data)->r11)); + SetRegister(pid, GPR12, &(((GPR *)data)->r12)); + SetRegister(pid, GPR13, &(((GPR *)data)->r13)); + SetRegister(pid, GPR14, &(((GPR *)data)->r14)); + SetRegister(pid, GPR15, &(((GPR *)data)->r15)); + SetRegister(pid, GPR16, &(((GPR *)data)->r16)); + SetRegister(pid, GPR17, &(((GPR *)data)->r17)); + SetRegister(pid, GPR18, &(((GPR *)data)->r18)); + SetRegister(pid, GPR19, &(((GPR *)data)->r19)); + SetRegister(pid, GPR20, &(((GPR *)data)->r20)); + SetRegister(pid, GPR21, &(((GPR *)data)->r21)); + SetRegister(pid, GPR22, &(((GPR *)data)->r22)); + SetRegister(pid, GPR23, &(((GPR *)data)->r23)); + SetRegister(pid, GPR24, &(((GPR *)data)->r24)); + SetRegister(pid, GPR25, &(((GPR *)data)->r25)); + SetRegister(pid, GPR26, &(((GPR *)data)->r26)); + SetRegister(pid, GPR27, &(((GPR *)data)->r27)); + SetRegister(pid, GPR28, &(((GPR *)data)->r28)); + SetRegister(pid, GPR29, &(((GPR *)data)->r29)); + SetRegister(pid, GPR30, &(((GPR *)data)->r30)); + SetRegister(pid, GPR31, &(((GPR *)data)->r31)); + SetRegister(pid, IAR, &(((GPR *)data)->pc)); + SetRegister(pid, MSR, &(((GPR *)data)->msr)); + //FIXME: origr3/softe/trap on AIX? + SetRegister(pid, CTR, &(((GPR *)data)->ctr)); + SetRegister(pid, LR, &(((GPR *)data)->lr)); + SetRegister(pid, XER, &(((GPR *)data)->xer)); + SetRegister(pid, CR, &(((GPR *)data)->cr)); + } else if (req == PTRACE_GETFPREGS) { + GetFPRegister(pid, FPR0, &(((FPR *)data)->f0)); + GetFPRegister(pid, FPR1, &(((FPR *)data)->f1)); + GetFPRegister(pid, FPR2, &(((FPR *)data)->f2)); + GetFPRegister(pid, FPR3, &(((FPR *)data)->f3)); + GetFPRegister(pid, FPR4, &(((FPR *)data)->f4)); + GetFPRegister(pid, FPR5, &(((FPR *)data)->f5)); + GetFPRegister(pid, FPR6, &(((FPR *)data)->f6)); + GetFPRegister(pid, FPR7, &(((FPR *)data)->f7)); + GetFPRegister(pid, FPR8, &(((FPR *)data)->f8)); + GetFPRegister(pid, FPR9, &(((FPR *)data)->f9)); + GetFPRegister(pid, FPR10, &(((FPR *)data)->f10)); + GetFPRegister(pid, FPR11, &(((FPR *)data)->f11)); + GetFPRegister(pid, FPR12, &(((FPR *)data)->f12)); + GetFPRegister(pid, FPR13, &(((FPR *)data)->f13)); + GetFPRegister(pid, FPR14, &(((FPR *)data)->f14)); + GetFPRegister(pid, FPR15, &(((FPR *)data)->f15)); + GetFPRegister(pid, FPR16, &(((FPR *)data)->f16)); + GetFPRegister(pid, FPR17, &(((FPR *)data)->f17)); + GetFPRegister(pid, FPR18, &(((FPR *)data)->f18)); + GetFPRegister(pid, FPR19, &(((FPR *)data)->f19)); + GetFPRegister(pid, FPR20, &(((FPR *)data)->f20)); + GetFPRegister(pid, FPR21, &(((FPR *)data)->f21)); + GetFPRegister(pid, FPR22, &(((FPR *)data)->f22)); + GetFPRegister(pid, FPR23, &(((FPR *)data)->f23)); + GetFPRegister(pid, FPR24, &(((FPR *)data)->f24)); + GetFPRegister(pid, FPR25, &(((FPR *)data)->f25)); + GetFPRegister(pid, FPR26, &(((FPR *)data)->f26)); + GetFPRegister(pid, FPR27, &(((FPR *)data)->f27)); + GetFPRegister(pid, FPR28, &(((FPR *)data)->f28)); + GetFPRegister(pid, FPR29, &(((FPR *)data)->f29)); + GetFPRegister(pid, FPR30, &(((FPR *)data)->f30)); + GetFPRegister(pid, FPR31, &(((FPR *)data)->f31)); + GetFPRegister(pid, FPSCR, &(((FPR *)data)->fpscr)); + } else if (req == PTRACE_GETVRREGS && tid) { + GetVMRegister(tid, VR0, &(((VMX *)data)->vr0[0])); + GetVMRegister(tid, VR1, &(((VMX *)data)->vr1[0])); + GetVMRegister(tid, VR2, &(((VMX *)data)->vr2[0])); + GetVMRegister(tid, VR3, &(((VMX *)data)->vr3[0])); + GetVMRegister(tid, VR4, &(((VMX *)data)->vr4[0])); + GetVMRegister(tid, VR5, &(((VMX *)data)->vr5[0])); + GetVMRegister(tid, VR6, &(((VMX *)data)->vr6[0])); + GetVMRegister(tid, VR7, &(((VMX *)data)->vr7[0])); + GetVMRegister(tid, VR8, &(((VMX *)data)->vr8[0])); + GetVMRegister(tid, VR9, &(((VMX *)data)->vr9[0])); + GetVMRegister(tid, VR10, &(((VMX *)data)->vr10[0])); + GetVMRegister(tid, VR11, &(((VMX *)data)->vr11[0])); + GetVMRegister(tid, VR12, &(((VMX *)data)->vr12[0])); + GetVMRegister(tid, VR13, &(((VMX *)data)->vr13[0])); + GetVMRegister(tid, VR14, &(((VMX *)data)->vr14[0])); + GetVMRegister(tid, VR15, &(((VMX *)data)->vr15[0])); + GetVMRegister(tid, VR16, &(((VMX *)data)->vr16[0])); + GetVMRegister(tid, VR17, &(((VMX *)data)->vr17[0])); + GetVMRegister(tid, VR18, &(((VMX *)data)->vr18[0])); + GetVMRegister(tid, VR19, &(((VMX *)data)->vr19[0])); + GetVMRegister(tid, VR20, &(((VMX *)data)->vr20[0])); + GetVMRegister(tid, VR21, &(((VMX *)data)->vr21[0])); + GetVMRegister(tid, VR22, &(((VMX *)data)->vr22[0])); + GetVMRegister(tid, VR23, &(((VMX *)data)->vr23[0])); + GetVMRegister(tid, VR24, &(((VMX *)data)->vr24[0])); + GetVMRegister(tid, VR25, &(((VMX *)data)->vr25[0])); + GetVMRegister(tid, VR26, &(((VMX *)data)->vr26[0])); + GetVMRegister(tid, VR27, &(((VMX *)data)->vr27[0])); + GetVMRegister(tid, VR28, &(((VMX *)data)->vr28[0])); + GetVMRegister(tid, VR29, &(((VMX *)data)->vr29[0])); + GetVMRegister(tid, VR30, &(((VMX *)data)->vr30[0])); + GetVMRegister(tid, VR31, &(((VMX *)data)->vr31[0])); + GetVMRegister(tid, VSCR, &(((VMX *)data)->vscr[0])); + GetVMRegister(tid, VRSAVE, &(((VMX *)data)->vrsave)); + } else if (req == PTRACE_GETVSRREGS && tid) { + GetVSRegister(tid, VSR0, &(((VSX *)data)->vs0[0])); + GetVSRegister(tid, VSR1, &(((VSX *)data)->vs1[0])); + GetVSRegister(tid, VSR2, &(((VSX *)data)->vs2[0])); + GetVSRegister(tid, VSR3, &(((VSX *)data)->vs3[0])); + GetVSRegister(tid, VSR4, &(((VSX *)data)->vs4[0])); + GetVSRegister(tid, VSR5, &(((VSX *)data)->vs5[0])); + GetVSRegister(tid, VSR6, &(((VSX *)data)->vs6[0])); + GetVSRegister(tid, VSR7, &(((VSX *)data)->vs7[0])); + GetVSRegister(tid, VSR8, &(((VSX *)data)->vs8[0])); + GetVSRegister(tid, VSR9, &(((VSX *)data)->vs9[0])); + GetVSRegister(tid, VSR10, &(((VSX *)data)->vs10[0])); + GetVSRegister(tid, VSR11, &(((VSX *)data)->vs11[0])); + GetVSRegister(tid, VSR12, &(((VSX *)data)->vs12[0])); + GetVSRegister(tid, VSR13, &(((VSX *)data)->vs13[0])); + GetVSRegister(tid, VSR14, &(((VSX *)data)->vs14[0])); + GetVSRegister(tid, VSR15, &(((VSX *)data)->vs15[0])); + GetVSRegister(tid, VSR16, &(((VSX *)data)->vs16[0])); + GetVSRegister(tid, VSR17, &(((VSX *)data)->vs17[0])); + GetVSRegister(tid, VSR18, &(((VSX *)data)->vs18[0])); + GetVSRegister(tid, VSR19, &(((VSX *)data)->vs19[0])); + GetVSRegister(tid, VSR20, &(((VSX *)data)->vs20[0])); + GetVSRegister(tid, VSR21, &(((VSX *)data)->vs21[0])); + GetVSRegister(tid, VSR22, &(((VSX *)data)->vs22[0])); + GetVSRegister(tid, VSR23, &(((VSX *)data)->vs23[0])); + GetVSRegister(tid, VSR24, &(((VSX *)data)->vs24[0])); + GetVSRegister(tid, VSR25, &(((VSX *)data)->vs25[0])); + GetVSRegister(tid, VSR26, &(((VSX *)data)->vs26[0])); + GetVSRegister(tid, VSR27, &(((VSX *)data)->vs27[0])); + GetVSRegister(tid, VSR28, &(((VSX *)data)->vs28[0])); + GetVSRegister(tid, VSR29, &(((VSX *)data)->vs29[0])); + GetVSRegister(tid, VSR30, &(((VSX *)data)->vs30[0])); + GetVSRegister(tid, VSR31, &(((VSX *)data)->vs31[0])); + GetVSRegister(tid, VSR32, &(((VSX *)data)->vs32[0])); + GetVSRegister(tid, VSR33, &(((VSX *)data)->vs33[0])); + GetVSRegister(tid, VSR34, &(((VSX *)data)->vs34[0])); + GetVSRegister(tid, VSR35, &(((VSX *)data)->vs35[0])); + GetVSRegister(tid, VSR36, &(((VSX *)data)->vs36[0])); + GetVSRegister(tid, VSR37, &(((VSX *)data)->vs37[0])); + GetVSRegister(tid, VSR38, &(((VSX *)data)->vs38[0])); + GetVSRegister(tid, VSR39, &(((VSX *)data)->vs39[0])); + GetVSRegister(tid, VSR40, &(((VSX *)data)->vs40[0])); + GetVSRegister(tid, VSR41, &(((VSX *)data)->vs41[0])); + GetVSRegister(tid, VSR42, &(((VSX *)data)->vs42[0])); + GetVSRegister(tid, VSR43, &(((VSX *)data)->vs43[0])); + GetVSRegister(tid, VSR44, &(((VSX *)data)->vs44[0])); + GetVSRegister(tid, VSR45, &(((VSX *)data)->vs45[0])); + GetVSRegister(tid, VSR46, &(((VSX *)data)->vs46[0])); + GetVSRegister(tid, VSR47, &(((VSX *)data)->vs47[0])); + GetVSRegister(tid, VSR48, &(((VSX *)data)->vs48[0])); + GetVSRegister(tid, VSR49, &(((VSX *)data)->vs49[0])); + GetVSRegister(tid, VSR50, &(((VSX *)data)->vs50[0])); + GetVSRegister(tid, VSR51, &(((VSX *)data)->vs51[0])); + GetVSRegister(tid, VSR52, &(((VSX *)data)->vs52[0])); + GetVSRegister(tid, VSR53, &(((VSX *)data)->vs53[0])); + GetVSRegister(tid, VSR54, &(((VSX *)data)->vs54[0])); + GetVSRegister(tid, VSR55, &(((VSX *)data)->vs55[0])); + GetVSRegister(tid, VSR56, &(((VSX *)data)->vs56[0])); + GetVSRegister(tid, VSR57, &(((VSX *)data)->vs57[0])); + GetVSRegister(tid, VSR58, &(((VSX *)data)->vs58[0])); + GetVSRegister(tid, VSR59, &(((VSX *)data)->vs59[0])); + GetVSRegister(tid, VSR60, &(((VSX *)data)->vs60[0])); + GetVSRegister(tid, VSR61, &(((VSX *)data)->vs61[0])); + GetVSRegister(tid, VSR62, &(((VSX *)data)->vs62[0])); + GetVSRegister(tid, VSR63, &(((VSX *)data)->vs63[0])); + } else if (req < PT_COMMAND_MAX) { + if (req == PT_CONTINUE) { +#if 0 + // Use PTT_CONTINUE + const char procdir[] = "/proc/"; + const char lwpdir[] = "/lwp/"; + std::string process_task_dir = procdir + std::to_string(pid) + lwpdir; + DIR *dirproc = opendir(process_task_dir.c_str()); + + struct ptthreads64 pts; + int idx = 0; + lldb::tid_t tid = 0; + if (dirproc) { + struct dirent *direntry = nullptr; + while ((direntry = readdir(dirproc)) != nullptr) { + if (strcmp(direntry->d_name, ".") == 0 || strcmp(direntry->d_name, "..") == 0) { + continue; + } + tid = atoi(direntry->d_name); + pts.th[idx++] = tid; + } + closedir(dirproc); + } + pts.th[idx] = 0; + ret = ptrace64(PTT_CONTINUE, tid, (long long)1, (int)(size_t)data, (int *)&pts); +#else + int buf; + ptrace64(req, pid, 1, (int)(size_t)data, &buf); +#endif + } else if (req == PT_READ_BLOCK) { + ptrace64(req, pid, (long long)addr, (int)data_size, (int *)result); + } else if (req == PT_WRITE_BLOCK) { + ptrace64(req, pid, (long long)addr, (int)data_size, (int *)result); + } else if (req == PT_ATTACH) { + ptrace64(req, pid, 0, 0, nullptr); + } else if (req == PT_WATCH) { + ptrace64(req, pid, (long long)addr, (int)data_size, nullptr); + } else if (req == PT_DETACH) { + ptrace64(req, pid, 0, 0, nullptr); + } else { + assert(0 && "Not supported yet."); + } + } else { + assert(0 && "Not supported yet."); + } + + if (errno) { + error.SetErrorToErrno(); + ret = -1; + } + + LLDB_LOG(log, "ptrace({0}, {1}, {2}, {3}, {4})={5:x}", req, pid, addr, data, + data_size, ret); + + PtraceDisplayBytes(req, data, data_size); + + if (error.Fail()) + LLDB_LOG(log, "ptrace() failed: {0}", error); + + return error; +} + +llvm::Expected NativeProcessAIX::TraceSupported() { + return NativeProcessProtocol::TraceSupported(); +} + +Error NativeProcessAIX::TraceStart(StringRef json_request, StringRef type) { + return NativeProcessProtocol::TraceStart(json_request, type); +} + +Error NativeProcessAIX::TraceStop(const TraceStopRequest &request) { + return NativeProcessProtocol::TraceStop(request); +} + +Expected NativeProcessAIX::TraceGetState(StringRef type) { + return NativeProcessProtocol::TraceGetState(type); +} + +Expected> NativeProcessAIX::TraceGetBinaryData( + const TraceGetBinaryDataRequest &request) { + return NativeProcessProtocol::TraceGetBinaryData(request); +} diff --git a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.h b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.h new file mode 100644 index 0000000000000..bdb6f7c500885 --- /dev/null +++ b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.h @@ -0,0 +1,283 @@ +//===-- NativeProcessAIX.h ---------------------------------- -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_NativeProcessAIX_H_ +#define liblldb_NativeProcessAIX_H_ + +#include +#include + +#include "lldb/Host/Debug.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/lldb-types.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "lldb/Host/aix/Support.h" + +#include "NativeThreadAIX.h" +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "Plugins/Process/Utility/NativeProcessSoftwareSingleStep.h" + +namespace lldb_private { +class Status; +class Scalar; + +namespace process_aix { +/// \class NativeProcessAIX +/// Manages communication with the inferior (debugee) process. +/// +/// Upon construction, this class prepares and launches an inferior process +/// for debugging. +/// +/// Changes in the inferior process state are broadcasted. +class NativeProcessAIX : public NativeProcessProtocol, + private NativeProcessSoftwareSingleStep { +public: + class Manager : public NativeProcessProtocol::Manager { + public: + Manager(MainLoop &mainloop); + + llvm::Expected> + Launch(ProcessLaunchInfo &launch_info, + NativeDelegate &native_delegate) override; + + llvm::Expected> + Attach(lldb::pid_t pid, NativeDelegate &native_delegate) override; + + Extension GetSupportedExtensions() const override; + + void AddProcess(NativeProcessAIX &process) { + m_processes.insert(&process); + } + + void RemoveProcess(NativeProcessAIX &process) { + m_processes.erase(&process); + } + + // Collect an event for the given tid, waiting for it if necessary. + void CollectThread(::pid_t tid); + + private: + MainLoop::SignalHandleUP m_sigchld_handle; + + llvm::SmallPtrSet m_processes; + + // Threads (events) which haven't been claimed by any process. + llvm::DenseSet<::pid_t> m_unowned_threads; + + void SigchldHandler(); + }; + + // NativeProcessProtocol Interface + + ~NativeProcessAIX() override { m_manager.RemoveProcess(*this); } + + Status Resume(const ResumeActionList &resume_actions) override; + + Status Halt() override; + + Status Detach() override; + + Status Signal(int signo) override; + + Status Interrupt() override; + + Status Kill() override; + + lldb::addr_t GetSharedLibraryInfoAddress() override; + + Status GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) override; + + Status ReadMemory(lldb::addr_t addr, void *buf, size_t size, + size_t &bytes_read) override; + + Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size, + size_t &bytes_written) override; + + llvm::Expected AllocateMemory(size_t size, + uint32_t permissions) override; + + llvm::Error DeallocateMemory(lldb::addr_t addr) override; + + Status ReadMemoryTags(int32_t type, lldb::addr_t addr, size_t len, + std::vector &tags) override; + + Status WriteMemoryTags(int32_t type, lldb::addr_t addr, size_t len, + const std::vector &tags) override; + + size_t UpdateThreads() override; + + const ArchSpec &GetArchitecture() const override { return m_arch; } + + Status SetBreakpoint(lldb::addr_t addr, uint32_t size, + bool hardware) override; + + Status RemoveBreakpoint(lldb::addr_t addr, bool hardware = false) override; + + void DoStopIDBumped(uint32_t newBumpId) override; + + Status GetLoadedModuleFileSpec(const char *module_path, + FileSpec &file_spec) override; + + Status GetFileLoadAddress(const llvm::StringRef &file_name, + lldb::addr_t &load_addr) override; + + NativeThreadAIX *GetThreadByID(lldb::tid_t id); + NativeThreadAIX *GetCurrentThread(); + + llvm::ErrorOr> + GetAuxvData() const override { + // Not available on this target. + return llvm::errc::not_supported; + } + + /// Tracing + /// These methods implement the jLLDBTrace packets + /// \{ + llvm::Error TraceStart(llvm::StringRef json_request, + llvm::StringRef type) override; + + llvm::Error TraceStop(const TraceStopRequest &request) override; + + llvm::Expected + TraceGetState(llvm::StringRef type) override; + + llvm::Expected> + TraceGetBinaryData(const TraceGetBinaryDataRequest &request) override; + + llvm::Expected TraceSupported() override; + /// } + + // Interface used by NativeRegisterContext-derived classes. + static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr = nullptr, + void *data = nullptr, size_t data_size = 0, + long *result = nullptr); + + bool SupportHardwareSingleStepping() const; + + /// Writes a siginfo_t structure corresponding to the given thread ID to the + /// memory region pointed to by \p siginfo. + int8_t GetSignalInfo(WaitStatus wstatus) const; + +protected: + llvm::Expected> + GetSoftwareBreakpointTrapOpcode(size_t size_hint) override; + + llvm::Expected Syscall(llvm::ArrayRef args); + +private: + Manager &m_manager; + /*MainLoop::SignalHandleUP m_sigchld_handle;*/ + ArchSpec m_arch; + /*MainLoop& m_main_loop;*/ + + LazyBool m_supports_mem_region = eLazyBoolCalculate; + std::vector> m_mem_region_cache; + + lldb::tid_t m_pending_notification_tid = LLDB_INVALID_THREAD_ID; + + /// Inferior memory (allocated by us) and its size. + llvm::DenseMap m_allocated_memory; + + // Private Instance Methods + NativeProcessAIX(::pid_t pid, int terminal_fd, NativeDelegate &delegate, + const ArchSpec &arch, Manager &manager, + llvm::ArrayRef<::pid_t> tids); + + // Returns a list of process threads that we have attached to. + static llvm::Expected> Attach(::pid_t pid); + + static Status SetDefaultPtraceOpts(const lldb::pid_t); + + bool TryHandleWaitStatus(lldb::pid_t pid, WaitStatus status); + + void MonitorCallback(NativeThreadAIX &thread, WaitStatus status); + + void MonitorSIGTRAP(const WaitStatus status, NativeThreadAIX &thread); + + void MonitorTrace(NativeThreadAIX &thread); + + void MonitorBreakpoint(NativeThreadAIX &thread); + + void MonitorWatchpoint(NativeThreadAIX &thread, uint32_t wp_index); + + void MonitorSignal(const WaitStatus status, NativeThreadAIX &thread); + + bool HasThreadNoLock(lldb::tid_t thread_id); + + void StopTrackingThread(NativeThreadAIX &thread); + + /// Create a new thread. + /// + /// If process tracing is enabled and the thread can't be traced, then the + /// thread is left stopped with a \a eStopReasonProcessorTrace status, and + /// then the process is stopped. + /// + /// \param[in] resume + /// If a tracing error didn't happen, then resume the thread after + /// creation if \b true, or leave it stopped with SIGSTOP if \b false. + NativeThreadAIX &AddThread(lldb::tid_t thread_id, bool resume); + + /// Start tracing a new thread if process tracing is enabled. + /// + /// Trace mechanisms should modify this method to provide automatic tracing + /// for new threads. + Status NotifyTracersOfNewThread(lldb::tid_t tid); + + /// Stop tracing threads upon a destroy event. + /// + /// Trace mechanisms should modify this method to provide automatic trace + /// stopping for threads being destroyed. + Status NotifyTracersOfThreadDestroyed(lldb::tid_t tid); + + void NotifyTracersProcessWillResume() override; + + void NotifyTracersProcessDidStop() override; + /// Writes the raw event message code (vis-a-vis PTRACE_GETEVENTMSG) + /// corresponding to the given thread ID to the memory pointed to by @p + /// message. + Status GetEventMessage(lldb::tid_t tid, unsigned long *message); + + void NotifyThreadDeath(lldb::tid_t tid); + + Status Detach(lldb::tid_t tid); + + // This method is requests a stop on all threads which are still running. It + // sets up a + // deferred delegate notification, which will fire once threads report as + // stopped. The + // triggerring_tid will be set as the current thread (main stop reason). + void StopRunningThreads(lldb::tid_t triggering_tid); + + // Notify the delegate if all threads have stopped. + void SignalIfAllThreadsStopped(); + + // Resume the given thread, optionally passing it the given signal. The type + // of resume + // operation (continue, single-step) depends on the state parameter. + Status ResumeThread(NativeThreadAIX &thread, lldb::StateType state, + int signo); + + void ThreadWasCreated(NativeThreadAIX &thread); + + void SigchldHandler(); + + Status PopulateMemoryRegionCache(); + + // Handle a clone()-like event. + bool MonitorClone(NativeThreadAIX &parent, lldb::pid_t child_pid, + int event); +}; + +} // namespace process_aix +} // namespace lldb_private + +#endif // #ifndef liblldb_NativeProcessAIX_H_ diff --git a/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.cpp b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.cpp new file mode 100644 index 0000000000000..0859f9501c1b6 --- /dev/null +++ b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.cpp @@ -0,0 +1,157 @@ +//===-- NativeRegisterContextAIX.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 "NativeRegisterContextAIX.h" + +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/Host/common/NativeThreadProtocol.h" +#include "lldb/Utility/RegisterValue.h" + +#include "Plugins/Process/AIX/NativeProcessAIX.h" +#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" +#include "lldb/Host/aix/Ptrace.h" + +using namespace lldb_private; +using namespace lldb_private::process_aix; + +lldb::ByteOrder NativeRegisterContextAIX::GetByteOrder() const { + return m_thread.GetProcess().GetByteOrder(); +} + +Status NativeRegisterContextAIX::ReadRegisterRaw(uint32_t reg_index, + RegisterValue ®_value) { + const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(reg_index); + if (!reg_info) + return Status("register %" PRIu32 " not found", reg_index); + + return DoReadRegisterValue(GetPtraceOffset(reg_index), reg_info->name, + reg_info->byte_size, reg_value); +} + +Status +NativeRegisterContextAIX::WriteRegisterRaw(uint32_t reg_index, + const RegisterValue ®_value) { + uint32_t reg_to_write = reg_index; + RegisterValue value_to_write = reg_value; + + // Check if this is a subregister of a full register. + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_index); + if (reg_info->invalidate_regs && + (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM)) { + Status error; + + RegisterValue full_value; + uint32_t full_reg = reg_info->invalidate_regs[0]; + const RegisterInfo *full_reg_info = GetRegisterInfoAtIndex(full_reg); + + // Read the full register. + error = ReadRegister(full_reg_info, full_value); + if (error.Fail()) + return error; + + lldb::ByteOrder byte_order = GetByteOrder(); + uint8_t dst[RegisterValue::kMaxRegisterByteSize]; + + // Get the bytes for the full register. + const uint32_t dest_size = full_value.GetAsMemoryData( + *full_reg_info, dst, sizeof(dst), byte_order, error); + if (error.Success() && dest_size) { + uint8_t src[RegisterValue::kMaxRegisterByteSize]; + + // Get the bytes for the source data. + const uint32_t src_size = reg_value.GetAsMemoryData( + *reg_info, src, sizeof(src), byte_order, error); + if (error.Success() && src_size && (src_size < dest_size)) { + // Copy the src bytes to the destination. + memcpy(dst + (reg_info->byte_offset & 0x1), src, src_size); + // Set this full register as the value to write. + value_to_write.SetBytes(dst, full_value.GetByteSize(), byte_order); + value_to_write.SetType(*full_reg_info); + reg_to_write = full_reg; + } + } + } + + const RegisterInfo *const register_to_write_info_p = + GetRegisterInfoAtIndex(reg_to_write); + assert(register_to_write_info_p && + "register to write does not have valid RegisterInfo"); + if (!register_to_write_info_p) + return Status("NativeRegisterContextAIX::%s failed to get RegisterInfo " + "for write register index %" PRIu32, + __FUNCTION__, reg_to_write); + + return DoWriteRegisterValue(GetPtraceOffset(reg_index), reg_info->name, + reg_value); +} + +Status NativeRegisterContextAIX::ReadGPR() { + return NativeProcessAIX::PtraceWrapper( + PTRACE_GETREGS, m_thread.GetID(), nullptr, GetGPRBuffer(), GetGPRSize()); +} + +Status NativeRegisterContextAIX::WriteGPR() { + return NativeProcessAIX::PtraceWrapper( + PTRACE_SETREGS, m_thread.GetID(), nullptr, GetGPRBuffer(), GetGPRSize()); +} + +Status NativeRegisterContextAIX::ReadFPR() { + return NativeProcessAIX::PtraceWrapper(PTRACE_GETFPREGS, m_thread.GetID(), + nullptr, GetFPRBuffer(), + GetFPRSize()); +} + +Status NativeRegisterContextAIX::WriteFPR() { + return NativeProcessAIX::PtraceWrapper(PTRACE_SETFPREGS, m_thread.GetID(), + nullptr, GetFPRBuffer(), + GetFPRSize()); +} + +Status NativeRegisterContextAIX::ReadRegisterSet(void *buf, size_t buf_size, + unsigned int regset) { + return NativeProcessAIX::PtraceWrapper(PTRACE_GETREGSET, m_thread.GetID(), + static_cast(®set), buf, + buf_size); +} + +Status NativeRegisterContextAIX::WriteRegisterSet(void *buf, size_t buf_size, + unsigned int regset) { + return NativeProcessAIX::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), + static_cast(®set), buf, + buf_size); +} + +Status NativeRegisterContextAIX::DoReadRegisterValue(uint32_t offset, + const char *reg_name, + uint32_t size, + RegisterValue &value) { + Log *log = GetLog(POSIXLog::Registers); + + long data; + Status error = NativeProcessAIX::PtraceWrapper( + PTRACE_PEEKUSER, m_thread.GetID(), reinterpret_cast(offset), + nullptr, 0, &data); + + if (error.Success()) + // First cast to an unsigned of the same size to avoid sign extension. + value.SetUInt(static_cast(data), size); + + LLDB_LOG(log, "{0}: {1:x}", reg_name, data); + return error; +} + +Status NativeRegisterContextAIX::DoWriteRegisterValue( + uint32_t offset, const char *reg_name, const RegisterValue &value) { + Log *log = GetLog(POSIXLog::Registers); + + void *buf = reinterpret_cast(value.GetAsUInt64()); + LLDB_LOG(log, "{0}: {1}", reg_name, buf); + + return NativeProcessAIX::PtraceWrapper( + PTRACE_POKEUSER, m_thread.GetID(), reinterpret_cast(offset), buf); +} diff --git a/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.h b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.h new file mode 100644 index 0000000000000..9c2a326856c0b --- /dev/null +++ b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.h @@ -0,0 +1,133 @@ +//===-- NativeRegisterContextAIX.h ----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_NativeRegisterContextAIX_h +#define lldb_NativeRegisterContextAIX_h + +#include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h" +#include "lldb/Host/common/NativeThreadProtocol.h" +#include "lldb/Target/MemoryTagManager.h" +#include "llvm/Support/Error.h" + +namespace lldb_private { +namespace process_aix { + +class NativeThreadAIX; + +class NativeRegisterContextAIX + : public virtual NativeRegisterContextRegisterInfo { +public: + // This function is implemented in the NativeRegisterContextAIX_* subclasses + // to create a new instance of the host specific NativeRegisterContextAIX. + // The implementations can't collide as only one NativeRegisterContextAIX_* + // variant should be compiled into the final executable. + static std::unique_ptr + CreateHostNativeRegisterContextAIX(const ArchSpec &target_arch, + NativeThreadAIX &native_thread); + + // Invalidates cached values in register context data structures + virtual void InvalidateAllRegisters(){} + + struct SyscallData { + /// The syscall instruction. If the architecture uses software + /// single-stepping, the instruction should also be followed by a trap to + /// ensure the process is stopped after the syscall. + llvm::ArrayRef Insn; + + /// Registers used for syscall arguments. The first register is used to + /// store the syscall number. + llvm::ArrayRef Args; + + uint32_t Result; ///< Register containing the syscall result. + }; + /// Return architecture-specific data needed to make inferior syscalls, if + /// they are supported. + virtual std::optional GetSyscallData() { return std::nullopt; } + + struct MmapData { + // Syscall numbers can be found (e.g.) in /usr/include/asm/unistd.h for the + // relevant architecture. + unsigned SysMmap; ///< mmap syscall number. + unsigned SysMunmap; ///< munmap syscall number + }; + /// Return the architecture-specific data needed to make mmap syscalls, if + /// they are supported. + virtual std::optional GetMmapData() { return std::nullopt; } + + struct MemoryTaggingDetails { + /// Object with tag handling utilities. If the function below returns + /// a valid structure, you can assume that this pointer is valid. + std::unique_ptr manager; + int ptrace_read_req; /// ptrace operation number for memory tag read + int ptrace_write_req; /// ptrace operation number for memory tag write + }; + /// Return architecture specific data needed to use memory tags, + /// if they are supported. + virtual llvm::Expected + GetMemoryTaggingDetails(int32_t type) { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "Architecture does not support memory tagging"); + } + +protected: + // NB: This constructor is here only because gcc<=6.5 requires a virtual base + // class initializer on abstract class (even though it is never used). It can + // be deleted once we move to gcc>=7.0. + NativeRegisterContextAIX(NativeThreadProtocol &thread) + : NativeRegisterContextRegisterInfo(thread, nullptr) {} + + lldb::ByteOrder GetByteOrder() const; + + virtual Status ReadRegisterRaw(uint32_t reg_index, RegisterValue ®_value); + + virtual Status WriteRegisterRaw(uint32_t reg_index, + const RegisterValue ®_value); + + virtual Status ReadRegisterSet(void *buf, size_t buf_size, + unsigned int regset); + + virtual Status WriteRegisterSet(void *buf, size_t buf_size, + unsigned int regset); + + virtual Status ReadGPR(); + + virtual Status WriteGPR(); + + virtual Status ReadFPR(); + + virtual Status WriteFPR(); + + virtual void *GetGPRBuffer() = 0; + + virtual size_t GetGPRSize() const { + return GetRegisterInfoInterface().GetGPRSize(); + } + + virtual void *GetFPRBuffer() = 0; + + virtual size_t GetFPRSize() = 0; + + virtual uint32_t GetPtraceOffset(uint32_t reg_index) { + return GetRegisterInfoAtIndex(reg_index)->byte_offset; + } + + // The Do*** functions are executed on the privileged thread and can perform + // ptrace + // operations directly. + virtual Status DoReadRegisterValue(uint32_t offset, const char *reg_name, + uint32_t size, RegisterValue &value); + + virtual Status DoWriteRegisterValue(uint32_t offset, const char *reg_name, + const RegisterValue &value); +}; + +} // namespace process_aix +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextAIX_h diff --git a/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.cpp b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.cpp new file mode 100644 index 0000000000000..1996373791748 --- /dev/null +++ b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.cpp @@ -0,0 +1,744 @@ +//===-- NativeRegisterContextAIX_ppc64.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 +// +//===----------------------------------------------------------------------===// + +// This implementation is related to the OpenPOWER ABI for Power Architecture +// 64-bit ELF V2 ABI + +#if defined(__powerpc64__) + +#include "NativeRegisterContextAIX_ppc64.h" + +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Status.h" +#include "lldb/Host/aix/Ptrace.h" + +#include "Plugins/Process/AIX/NativeProcessAIX.h" +#include "Plugins/Process/Linux/Procfs.h" +#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" +#include "Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.h" + +// System includes - They have to be included after framework includes because +// they define some macros which collide with variable names in other modules +#include +#include +#include +#include + +#define REG_CONTEXT_SIZE \ + (GetGPRSize() + GetFPRSize() + sizeof(m_vmx_ppc64le) + sizeof(m_vsx_ppc64le)) +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_aix; + +static const uint32_t g_gpr_regnums_ppc64le[] = { + gpr_r0_ppc64le, gpr_r1_ppc64le, gpr_r2_ppc64le, gpr_r3_ppc64le, + gpr_r4_ppc64le, gpr_r5_ppc64le, gpr_r6_ppc64le, gpr_r7_ppc64le, + gpr_r8_ppc64le, gpr_r9_ppc64le, gpr_r10_ppc64le, gpr_r11_ppc64le, + gpr_r12_ppc64le, gpr_r13_ppc64le, gpr_r14_ppc64le, gpr_r15_ppc64le, + gpr_r16_ppc64le, gpr_r17_ppc64le, gpr_r18_ppc64le, gpr_r19_ppc64le, + gpr_r20_ppc64le, gpr_r21_ppc64le, gpr_r22_ppc64le, gpr_r23_ppc64le, + gpr_r24_ppc64le, gpr_r25_ppc64le, gpr_r26_ppc64le, gpr_r27_ppc64le, + gpr_r28_ppc64le, gpr_r29_ppc64le, gpr_r30_ppc64le, gpr_r31_ppc64le, + gpr_pc_ppc64le, gpr_msr_ppc64le, gpr_origr3_ppc64le, gpr_ctr_ppc64le, + gpr_lr_ppc64le, gpr_xer_ppc64le, gpr_cr_ppc64le, gpr_softe_ppc64le, + gpr_trap_ppc64le, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; + +static const uint32_t g_fpr_regnums_ppc64le[] = { + fpr_f0_ppc64le, fpr_f1_ppc64le, fpr_f2_ppc64le, fpr_f3_ppc64le, + fpr_f4_ppc64le, fpr_f5_ppc64le, fpr_f6_ppc64le, fpr_f7_ppc64le, + fpr_f8_ppc64le, fpr_f9_ppc64le, fpr_f10_ppc64le, fpr_f11_ppc64le, + fpr_f12_ppc64le, fpr_f13_ppc64le, fpr_f14_ppc64le, fpr_f15_ppc64le, + fpr_f16_ppc64le, fpr_f17_ppc64le, fpr_f18_ppc64le, fpr_f19_ppc64le, + fpr_f20_ppc64le, fpr_f21_ppc64le, fpr_f22_ppc64le, fpr_f23_ppc64le, + fpr_f24_ppc64le, fpr_f25_ppc64le, fpr_f26_ppc64le, fpr_f27_ppc64le, + fpr_f28_ppc64le, fpr_f29_ppc64le, fpr_f30_ppc64le, fpr_f31_ppc64le, + fpr_fpscr_ppc64le, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; + +static const uint32_t g_vmx_regnums_ppc64le[] = { + vmx_vr0_ppc64le, vmx_vr1_ppc64le, vmx_vr2_ppc64le, vmx_vr3_ppc64le, + vmx_vr4_ppc64le, vmx_vr5_ppc64le, vmx_vr6_ppc64le, vmx_vr7_ppc64le, + vmx_vr8_ppc64le, vmx_vr9_ppc64le, vmx_vr10_ppc64le, vmx_vr11_ppc64le, + vmx_vr12_ppc64le, vmx_vr13_ppc64le, vmx_vr14_ppc64le, vmx_vr15_ppc64le, + vmx_vr16_ppc64le, vmx_vr17_ppc64le, vmx_vr18_ppc64le, vmx_vr19_ppc64le, + vmx_vr20_ppc64le, vmx_vr21_ppc64le, vmx_vr22_ppc64le, vmx_vr23_ppc64le, + vmx_vr24_ppc64le, vmx_vr25_ppc64le, vmx_vr26_ppc64le, vmx_vr27_ppc64le, + vmx_vr28_ppc64le, vmx_vr29_ppc64le, vmx_vr30_ppc64le, vmx_vr31_ppc64le, + vmx_vscr_ppc64le, vmx_vrsave_ppc64le, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; + +static const uint32_t g_vsx_regnums_ppc64le[] = { + vsx_vs0_ppc64le, vsx_vs1_ppc64le, vsx_vs2_ppc64le, vsx_vs3_ppc64le, + vsx_vs4_ppc64le, vsx_vs5_ppc64le, vsx_vs6_ppc64le, vsx_vs7_ppc64le, + vsx_vs8_ppc64le, vsx_vs9_ppc64le, vsx_vs10_ppc64le, vsx_vs11_ppc64le, + vsx_vs12_ppc64le, vsx_vs13_ppc64le, vsx_vs14_ppc64le, vsx_vs15_ppc64le, + vsx_vs16_ppc64le, vsx_vs17_ppc64le, vsx_vs18_ppc64le, vsx_vs19_ppc64le, + vsx_vs20_ppc64le, vsx_vs21_ppc64le, vsx_vs22_ppc64le, vsx_vs23_ppc64le, + vsx_vs24_ppc64le, vsx_vs25_ppc64le, vsx_vs26_ppc64le, vsx_vs27_ppc64le, + vsx_vs28_ppc64le, vsx_vs29_ppc64le, vsx_vs30_ppc64le, vsx_vs31_ppc64le, + vsx_vs32_ppc64le, vsx_vs33_ppc64le, vsx_vs34_ppc64le, vsx_vs35_ppc64le, + vsx_vs36_ppc64le, vsx_vs37_ppc64le, vsx_vs38_ppc64le, vsx_vs39_ppc64le, + vsx_vs40_ppc64le, vsx_vs41_ppc64le, vsx_vs42_ppc64le, vsx_vs43_ppc64le, + vsx_vs44_ppc64le, vsx_vs45_ppc64le, vsx_vs46_ppc64le, vsx_vs47_ppc64le, + vsx_vs48_ppc64le, vsx_vs49_ppc64le, vsx_vs50_ppc64le, vsx_vs51_ppc64le, + vsx_vs52_ppc64le, vsx_vs53_ppc64le, vsx_vs54_ppc64le, vsx_vs55_ppc64le, + vsx_vs56_ppc64le, vsx_vs57_ppc64le, vsx_vs58_ppc64le, vsx_vs59_ppc64le, + vsx_vs60_ppc64le, vsx_vs61_ppc64le, vsx_vs62_ppc64le, vsx_vs63_ppc64le, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; + +// Number of register sets provided by this context. +static constexpr int k_num_register_sets = 4; + +static const RegisterSet g_reg_sets_ppc64le[k_num_register_sets] = { + {"General Purpose Registers", "gpr", k_num_gpr_registers_ppc64le, + g_gpr_regnums_ppc64le}, + {"Floating Point Registers", "fpr", k_num_fpr_registers_ppc64le, + g_fpr_regnums_ppc64le}, + {"AltiVec/VMX Registers", "vmx", k_num_vmx_registers_ppc64le, + g_vmx_regnums_ppc64le}, + {"VSX Registers", "vsx", k_num_vsx_registers_ppc64le, + g_vsx_regnums_ppc64le}, +}; + +std::unique_ptr +NativeRegisterContextAIX::CreateHostNativeRegisterContextAIX( + const ArchSpec &target_arch, NativeThreadAIX &native_thread) { + switch (target_arch.GetMachine()) { + case llvm::Triple::ppc64: + return std::make_unique(target_arch, + native_thread); + default: + llvm_unreachable("have no register context for architecture"); + } +} + +NativeRegisterContextAIX_ppc64::NativeRegisterContextAIX_ppc64( + const ArchSpec &target_arch, NativeThreadProtocol &native_thread) + : NativeRegisterContextRegisterInfo( + native_thread, new RegisterInfoPOSIX_ppc64le(target_arch)), + NativeRegisterContextAIX(native_thread) { + if (target_arch.GetMachine() != llvm::Triple::ppc64) { + llvm_unreachable("Unhandled target architecture."); + } + + ::memset(&m_gpr_ppc64le, 0, sizeof(m_gpr_ppc64le)); + ::memset(&m_fpr_ppc64le, 0, sizeof(m_fpr_ppc64le)); + ::memset(&m_vmx_ppc64le, 0, sizeof(m_vmx_ppc64le)); + ::memset(&m_vsx_ppc64le, 0, sizeof(m_vsx_ppc64le)); + ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs)); +} + +uint32_t NativeRegisterContextAIX_ppc64::GetRegisterSetCount() const { + return k_num_register_sets; +} + +const RegisterSet * +NativeRegisterContextAIX_ppc64::GetRegisterSet(uint32_t set_index) const { + if (set_index < k_num_register_sets) + return &g_reg_sets_ppc64le[set_index]; + + return nullptr; +} + +uint32_t NativeRegisterContextAIX_ppc64::GetUserRegisterCount() const { + uint32_t count = 0; + for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) + count += g_reg_sets_ppc64le[set_index].num_registers; + return count; +} + +Status NativeRegisterContextAIX_ppc64::ReadRegister( + const RegisterInfo *reg_info, RegisterValue ®_value) { + Status error; + + if (!reg_info) { + error.SetErrorString("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + + if (IsFPR(reg)) { + error = ReadFPR(); + if (error.Fail()) + return error; + + // Get pointer to m_fpr_ppc64le variable and set the data from it. + uint32_t fpr_offset = CalculateFprOffset(reg_info); + assert(fpr_offset < sizeof m_fpr_ppc64le); + uint8_t *src = (uint8_t *)&m_fpr_ppc64le + fpr_offset; + reg_value.SetFromMemoryData(*reg_info, src, reg_info->byte_size, + eByteOrderLittle, error); + } else if (IsVSX(reg)) { + uint32_t vsx_offset = CalculateVsxOffset(reg_info); + assert(vsx_offset < sizeof(m_vsx_ppc64le)); + + if (vsx_offset < sizeof(m_vsx_ppc64le) / 2) { + error = ReadVSX(); + if (error.Fail()) + return error; + + error = ReadFPR(); + if (error.Fail()) + return error; + + uint64_t value[2]; + uint8_t *dst, *src; + dst = (uint8_t *)&value; + src = (uint8_t *)&m_vsx_ppc64le + vsx_offset / 2; + ::memcpy(dst, src, 8); + dst += 8; + src = (uint8_t *)&m_fpr_ppc64le + vsx_offset / 2; + ::memcpy(dst, src, 8); + reg_value.SetFromMemoryData(*reg_info, &value, reg_info->byte_size, + eByteOrderLittle, error); + } else { + error = ReadVMX(); + if (error.Fail()) + return error; + + // Get pointer to m_vmx_ppc64le variable and set the data from it. + uint32_t vmx_offset = vsx_offset - sizeof(m_vsx_ppc64le) / 2; + uint8_t *src = (uint8_t *)&m_vmx_ppc64le + vmx_offset; + reg_value.SetFromMemoryData(*reg_info, src, reg_info->byte_size, + eByteOrderLittle, error); + } + } else if (IsVMX(reg)) { + error = ReadVMX(); + if (error.Fail()) + return error; + + // Get pointer to m_vmx_ppc64le variable and set the data from it. + uint32_t vmx_offset = CalculateVmxOffset(reg_info); + assert(vmx_offset < sizeof m_vmx_ppc64le); + uint8_t *src = (uint8_t *)&m_vmx_ppc64le + vmx_offset; + reg_value.SetFromMemoryData(*reg_info, src, reg_info->byte_size, + eByteOrderLittle, error); + } else if (IsGPR(reg)) { + error = ReadGPR(); + if (error.Fail()) + return error; + + uint8_t *src = (uint8_t *) &m_gpr_ppc64le + reg_info->byte_offset; + reg_value.SetFromMemoryData(*reg_info, src, reg_info->byte_size, + eByteOrderLittle, error); + } else { + return Status("failed - register wasn't recognized to be a GPR, FPR, VSX " + "or VMX, read strategy unknown"); + } + + return error; +} + +Status NativeRegisterContextAIX_ppc64::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue ®_value) { + Status error; + if (!reg_info) + return Status("reg_info NULL"); + + const uint32_t reg_index = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg_index == LLDB_INVALID_REGNUM) + return Status("no lldb regnum for %s", reg_info && reg_info->name + ? reg_info->name + : ""); + + if (IsGPR(reg_index)) { + error = ReadGPR(); + if (error.Fail()) + return error; + + uint8_t *dst = (uint8_t *)&m_gpr_ppc64le + reg_info->byte_offset; + ::memcpy(dst, reg_value.GetBytes(), reg_value.GetByteSize()); + *(uint64_t *)dst = llvm::byteswap(*(uint64_t *)dst); + + error = WriteGPR(); + if (error.Fail()) + return error; + + return Status(); + } + + if (IsFPR(reg_index)) { + error = ReadFPR(); + if (error.Fail()) + return error; + + // Get pointer to m_fpr_ppc64le variable and set the data to it. + uint32_t fpr_offset = CalculateFprOffset(reg_info); + assert(fpr_offset < GetFPRSize()); + uint8_t *dst = (uint8_t *)&m_fpr_ppc64le + fpr_offset; + ::memcpy(dst, reg_value.GetBytes(), reg_value.GetByteSize()); + + error = WriteFPR(); + if (error.Fail()) + return error; + + return Status(); + } + + if (IsVMX(reg_index)) { + error = ReadVMX(); + if (error.Fail()) + return error; + + // Get pointer to m_vmx_ppc64le variable and set the data to it. + uint32_t vmx_offset = CalculateVmxOffset(reg_info); + assert(vmx_offset < sizeof(m_vmx_ppc64le)); + uint8_t *dst = (uint8_t *)&m_vmx_ppc64le + vmx_offset; + ::memcpy(dst, reg_value.GetBytes(), reg_value.GetByteSize()); + + error = WriteVMX(); + if (error.Fail()) + return error; + + return Status(); + } + + if (IsVSX(reg_index)) { + uint32_t vsx_offset = CalculateVsxOffset(reg_info); + assert(vsx_offset < sizeof(m_vsx_ppc64le)); + + if (vsx_offset < sizeof(m_vsx_ppc64le) / 2) { + error = ReadVSX(); + if (error.Fail()) + return error; + + error = ReadFPR(); + if (error.Fail()) + return error; + + uint64_t value[2]; + ::memcpy(value, reg_value.GetBytes(), 16); + uint8_t *dst, *src; + src = (uint8_t *)value; + dst = (uint8_t *)&m_vsx_ppc64le + vsx_offset / 2; + ::memcpy(dst, src, 8); + src += 8; + dst = (uint8_t *)&m_fpr_ppc64le + vsx_offset / 2; + ::memcpy(dst, src, 8); + + WriteVSX(); + WriteFPR(); + } else { + error = ReadVMX(); + if (error.Fail()) + return error; + + // Get pointer to m_vmx_ppc64le variable and set the data from it. + uint32_t vmx_offset = vsx_offset - sizeof(m_vsx_ppc64le) / 2; + uint8_t *dst = (uint8_t *)&m_vmx_ppc64le + vmx_offset; + ::memcpy(dst, reg_value.GetBytes(), reg_value.GetByteSize()); + WriteVMX(); + } + + return Status(); + } + + return Status("failed - register wasn't recognized to be a GPR, FPR, VSX " + "or VMX, write strategy unknown"); +} + +Status NativeRegisterContextAIX_ppc64::ReadAllRegisterValues( + lldb::WritableDataBufferSP &data_sp) { + Status error; + + data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0)); + error = ReadGPR(); + if (error.Fail()) + return error; + + error = ReadFPR(); + if (error.Fail()) + return error; + + error = ReadVMX(); + if (error.Fail()) + return error; + + error = ReadVSX(); + if (error.Fail()) + return error; + + uint8_t *dst = data_sp->GetBytes(); + ::memcpy(dst, &m_gpr_ppc64le, GetGPRSize()); + dst += GetGPRSize(); + ::memcpy(dst, &m_fpr_ppc64le, GetFPRSize()); + dst += GetFPRSize(); + ::memcpy(dst, &m_vmx_ppc64le, sizeof(m_vmx_ppc64le)); + dst += sizeof(m_vmx_ppc64le); + ::memcpy(dst, &m_vsx_ppc64le, sizeof(m_vsx_ppc64le)); + + return error; +} + +Status NativeRegisterContextAIX_ppc64::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + Status error; + + if (!data_sp) { + error.SetErrorStringWithFormat( + "NativeRegisterContextAIX_ppc64::%s invalid data_sp provided", + __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) { + error.SetErrorStringWithFormat( + "NativeRegisterContextAIX_ppc64::%s data_sp contained mismatched " + "data size, expected %" PRIu64 ", actual %" PRIu64, + __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize()); + return error; + } + + const uint8_t *src = data_sp->GetBytes(); + if (src == nullptr) { + error.SetErrorStringWithFormat("NativeRegisterContextAIX_ppc64::%s " + "DataBuffer::GetBytes() returned a null " + "pointer", + __FUNCTION__); + return error; + } + + ::memcpy(&m_gpr_ppc64le, src, GetGPRSize()); + error = WriteGPR(); + + if (error.Fail()) + return error; + + src += GetGPRSize(); + ::memcpy(&m_fpr_ppc64le, src, GetFPRSize()); + + error = WriteFPR(); + if (error.Fail()) + return error; + + src += GetFPRSize(); + ::memcpy(&m_vmx_ppc64le, src, sizeof(m_vmx_ppc64le)); + + error = WriteVMX(); + if (error.Fail()) + return error; + + src += sizeof(m_vmx_ppc64le); + ::memcpy(&m_vsx_ppc64le, src, sizeof(m_vsx_ppc64le)); + error = WriteVSX(); + + return error; +} + +bool NativeRegisterContextAIX_ppc64::IsGPR(unsigned reg) const { + return reg <= k_last_gpr_ppc64le; // GPR's come first. +} + +bool NativeRegisterContextAIX_ppc64::IsFPR(unsigned reg) const { + return (k_first_fpr_ppc64le <= reg && reg <= k_last_fpr_ppc64le); +} + +uint32_t NativeRegisterContextAIX_ppc64::CalculateFprOffset( + const RegisterInfo *reg_info) const { + return reg_info->byte_offset - + GetRegisterInfoAtIndex(k_first_fpr_ppc64le)->byte_offset; +} + +uint32_t NativeRegisterContextAIX_ppc64::CalculateVmxOffset( + const RegisterInfo *reg_info) const { + return reg_info->byte_offset - + GetRegisterInfoAtIndex(k_first_vmx_ppc64le)->byte_offset; +} + +uint32_t NativeRegisterContextAIX_ppc64::CalculateVsxOffset( + const RegisterInfo *reg_info) const { + return reg_info->byte_offset - + GetRegisterInfoAtIndex(k_first_vsx_ppc64le)->byte_offset; +} + +Status NativeRegisterContextAIX_ppc64::ReadVMX() { + return NativeProcessAIX::PtraceWrapper(PTRACE_GETVRREGS, m_thread.GetID(), + nullptr, &m_vmx_ppc64le, + sizeof(m_vmx_ppc64le)); +} + +Status NativeRegisterContextAIX_ppc64::WriteVMX() { + //FIXME + int regset = 0/*NT_PPC_VMX*/; + return NativeProcessAIX::PtraceWrapper(PT_CLEAR/*PTRACE_SETVRREGS*/, m_thread.GetID(), + ®set, &m_vmx_ppc64le, + sizeof(m_vmx_ppc64le)); +} + +Status NativeRegisterContextAIX_ppc64::ReadVSX() { + return NativeProcessAIX::PtraceWrapper(PTRACE_GETVSRREGS, m_thread.GetID(), + nullptr, &m_vsx_ppc64le, + sizeof(m_vsx_ppc64le)); +} + +Status NativeRegisterContextAIX_ppc64::WriteVSX() { + //FIXME + int regset = 0/*NT_PPC_VSX*/; + return NativeProcessAIX::PtraceWrapper(PT_CLEAR/*PTRACE_SETVSRREGS*/, m_thread.GetID(), + ®set, &m_vsx_ppc64le, + sizeof(m_vsx_ppc64le)); +} + +bool NativeRegisterContextAIX_ppc64::IsVMX(unsigned reg) { + return (reg >= k_first_vmx_ppc64le) && (reg <= k_last_vmx_ppc64le); +} + +bool NativeRegisterContextAIX_ppc64::IsVSX(unsigned reg) { + return (reg >= k_first_vsx_ppc64le) && (reg <= k_last_vsx_ppc64le); +} + +uint32_t NativeRegisterContextAIX_ppc64::NumSupportedHardwareWatchpoints() { + Log *log = GetLog(POSIXLog::Watchpoints); + + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + + if (error.Fail()) + return 0; + + LLDB_LOG(log, "{0}", m_max_hwp_supported); + return m_max_hwp_supported; +} + +uint32_t NativeRegisterContextAIX_ppc64::SetHardwareWatchpoint( + lldb::addr_t addr, size_t size, uint32_t watch_flags) { + Log *log = GetLog(POSIXLog::Watchpoints); + LLDB_LOG(log, "addr: {0:x}, size: {1:x} watch_flags: {2:x}", addr, size, + watch_flags); + + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + + if (error.Fail()) + return LLDB_INVALID_INDEX32; + + uint32_t control_value = 0, wp_index = 0; + lldb::addr_t real_addr = addr; + uint32_t rw_mode = 0; + + // Check if we are setting watchpoint other than read/write/access Update + // watchpoint flag to match ppc64le write-read bit configuration. + switch (watch_flags) { + case eWatchpointKindWrite: + //FIXME + //rw_mode = 0/*PPC_BREAKPOINT_TRIGGER_WRITE*/; + watch_flags = 2; + break; + // Watchpoint read not supported + case eWatchpointKindRead: + case (eWatchpointKindRead | eWatchpointKindWrite): + default: + return LLDB_INVALID_INDEX32; + } + + // Check if size has a valid hardware watchpoint length. + if (size != 8) + return LLDB_INVALID_INDEX32; + + // Check 8-byte alignment for hardware watchpoint target address. Below is a + // hack to recalculate address and size in order to make sure we can watch + // non 8-byte aligned addresses as well. + if (addr & 0x07) { + + addr_t begin = llvm::alignDown(addr, 8); + addr_t end = llvm::alignTo(addr + size, 8); + size = llvm::PowerOf2Ceil(end - begin); + + addr = addr & (~0x07); + } + + // Setup control value + control_value = watch_flags << 3; + control_value |= ((1 << size) - 1) << 5; + control_value |= (2 << 1) | 1; + + // Iterate over stored watchpoints and find a free wp_index + wp_index = LLDB_INVALID_INDEX32; + for (uint32_t i = 0; i < m_max_hwp_supported; i++) { + if ((m_hwp_regs[i].control & 1) == 0) { + wp_index = i; // Mark last free slot + } else if (m_hwp_regs[i].address == addr) { + return LLDB_INVALID_INDEX32; // We do not support duplicate watchpoints. + } + } + + if (wp_index == LLDB_INVALID_INDEX32) + return LLDB_INVALID_INDEX32; + + // Update watchpoint in local cache + m_hwp_regs[wp_index].real_addr = real_addr; + m_hwp_regs[wp_index].address = addr; + m_hwp_regs[wp_index].control = control_value; + //m_hwp_regs[wp_index].mode = rw_mode; + + // PTRACE call to set corresponding watchpoint register. + error = WriteHardwareDebugRegs(); + + if (error.Fail()) { + m_hwp_regs[wp_index].address = 0; + m_hwp_regs[wp_index].control &= llvm::maskTrailingZeros(1); + + return LLDB_INVALID_INDEX32; + } + + return wp_index; +} + +bool NativeRegisterContextAIX_ppc64::ClearHardwareWatchpoint( + uint32_t wp_index) { + Log *log = GetLog(POSIXLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + + if (error.Fail()) + return false; + + if (wp_index >= m_max_hwp_supported) + return false; + + // Create a backup we can revert to in case of failure. + lldb::addr_t tempAddr = m_hwp_regs[wp_index].address; + uint32_t tempControl = m_hwp_regs[wp_index].control; + long *tempSlot = reinterpret_cast(m_hwp_regs[wp_index].slot); + + // Update watchpoint in local cache + m_hwp_regs[wp_index].control &= llvm::maskTrailingZeros(1); + m_hwp_regs[wp_index].address = 0; + m_hwp_regs[wp_index].slot = 0; + m_hwp_regs[wp_index].mode = 0; + + // Ptrace call to update hardware debug registers + //FIXME + error = NativeProcessAIX::PtraceWrapper(PT_CLEAR/*PPC_PTRACE_DELHWDEBUG*/, + m_thread.GetID(), 0, tempSlot); + + if (error.Fail()) { + m_hwp_regs[wp_index].control = tempControl; + m_hwp_regs[wp_index].address = tempAddr; + m_hwp_regs[wp_index].slot = reinterpret_cast(tempSlot); + + return false; + } + + return true; +} + +uint32_t +NativeRegisterContextAIX_ppc64::GetWatchpointSize(uint32_t wp_index) { + Log *log = GetLog(POSIXLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + unsigned control = (m_hwp_regs[wp_index].control >> 5) & 0xff; + if (llvm::isPowerOf2_32(control + 1)) { + return llvm::popcount(control); + } + + return 0; +} + +bool NativeRegisterContextAIX_ppc64::WatchpointIsEnabled( + uint32_t wp_index) { + Log *log = GetLog(POSIXLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + return !!((m_hwp_regs[wp_index].control & 0x1) == 0x1); +} + +Status NativeRegisterContextAIX_ppc64::GetWatchpointHitIndex( + uint32_t &wp_index, lldb::addr_t trap_addr) { + Log *log = GetLog(POSIXLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}, trap_addr: {1:x}", wp_index, trap_addr); + + uint32_t watch_size; + lldb::addr_t watch_addr; + + for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) { + watch_size = GetWatchpointSize(wp_index); + watch_addr = m_hwp_regs[wp_index].address; + + if (WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr && + trap_addr <= watch_addr + watch_size) { + m_hwp_regs[wp_index].hit_addr = trap_addr; + return Status(); + } + } + + wp_index = LLDB_INVALID_INDEX32; + return Status(); +} + +lldb::addr_t +NativeRegisterContextAIX_ppc64::GetWatchpointAddress(uint32_t wp_index) { + Log *log = GetLog(POSIXLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + if (wp_index >= m_max_hwp_supported) + return LLDB_INVALID_ADDRESS; + + if (WatchpointIsEnabled(wp_index)) + return m_hwp_regs[wp_index].real_addr; + else + return LLDB_INVALID_ADDRESS; +} + +lldb::addr_t +NativeRegisterContextAIX_ppc64::GetWatchpointHitAddress(uint32_t wp_index) { + Log *log = GetLog(POSIXLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + if (wp_index >= m_max_hwp_supported) + return LLDB_INVALID_ADDRESS; + + if (WatchpointIsEnabled(wp_index)) + return m_hwp_regs[wp_index].hit_addr; + + return LLDB_INVALID_ADDRESS; +} + +Status NativeRegisterContextAIX_ppc64::ReadHardwareDebugInfo() { + if (!m_refresh_hwdebug_info) { + return Status(); + } + + m_max_hwp_supported = 1; + m_max_hbp_supported = 0; + m_refresh_hwdebug_info = false; + + return Status(); +} + +Status NativeRegisterContextAIX_ppc64::WriteHardwareDebugRegs() { + Status error; + long ret; + + for (uint32_t i = 0; i < m_max_hwp_supported; i++) { + if ((m_hwp_regs[i].control & 1) == 0) + continue; + + error = NativeProcessAIX::PtraceWrapper(PT_WATCH, m_thread.GetID(), (void *)m_hwp_regs[i].address, nullptr, 8, &ret); + + if (error.Fail()) + return error; + + m_hwp_regs[i].slot = ret; + } + return error; +} + +#endif // defined(__powerpc64__) diff --git a/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.h b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.h new file mode 100644 index 0000000000000..a29f786f2313a --- /dev/null +++ b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.h @@ -0,0 +1,138 @@ +//===-- NativeRegisterContextAIX_ppc64.h --------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// This implementation is related to the OpenPOWER ABI for Power Architecture +// 64-bit ELF V2 ABI + +#if defined(__powerpc64__) + +#ifndef lldb_NativeRegisterContextAIX_ppc64_h +#define lldb_NativeRegisterContextAIX_ppc64_h + +#include "Plugins/Process/AIX/NativeRegisterContextAIX.h" +#include "Plugins/Process/Utility/lldb-ppc64le-register-enums.h" + +#define DECLARE_REGISTER_INFOS_PPC64LE_STRUCT +#include "Plugins/Process/Utility/RegisterInfos_ppc64le.h" +#undef DECLARE_REGISTER_INFOS_PPC64LE_STRUCT + +namespace lldb_private { +namespace process_aix { + +class NativeProcessAIX; + +class NativeRegisterContextAIX_ppc64 : public NativeRegisterContextAIX { +public: + NativeRegisterContextAIX_ppc64(const ArchSpec &target_arch, + NativeThreadProtocol &native_thread); + + uint32_t GetRegisterSetCount() const override; + + uint32_t GetUserRegisterCount() const override; + + const RegisterSet *GetRegisterSet(uint32_t set_index) const override; + + Status ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) override; + + Status WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) override; + + Status ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override; + + Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + // Hardware watchpoint management functions + + uint32_t NumSupportedHardwareWatchpoints() override; + + uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags) override; + + bool ClearHardwareWatchpoint(uint32_t hw_index) override; + + Status GetWatchpointHitIndex(uint32_t &wp_index, + lldb::addr_t trap_addr) override; + + lldb::addr_t GetWatchpointHitAddress(uint32_t wp_index) override; + + lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override; + + uint32_t GetWatchpointSize(uint32_t wp_index); + + bool WatchpointIsEnabled(uint32_t wp_index); + +protected: + bool IsVMX(unsigned reg); + + bool IsVSX(unsigned reg); + + Status ReadVMX(); + + Status WriteVMX(); + + Status ReadVSX(); + + Status WriteVSX(); + + void *GetGPRBuffer() override { return &m_gpr_ppc64le; } + + void *GetFPRBuffer() override { return &m_fpr_ppc64le; } + + size_t GetFPRSize() override { return sizeof(m_fpr_ppc64le); } + +private: + GPR m_gpr_ppc64le; // 64-bit general purpose registers. + FPR m_fpr_ppc64le; // floating-point registers including extended register. + VMX m_vmx_ppc64le; // VMX registers. + VSX m_vsx_ppc64le; // Last lower bytes from first VSX registers. + + bool IsGPR(unsigned reg) const; + + bool IsFPR(unsigned reg) const; + + bool IsVMX(unsigned reg) const; + + bool IsVSX(unsigned reg) const; + + uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const; + + uint32_t CalculateVmxOffset(const RegisterInfo *reg_info) const; + + uint32_t CalculateVsxOffset(const RegisterInfo *reg_info) const; + + Status ReadHardwareDebugInfo(); + + Status WriteHardwareDebugRegs(); + + // Debug register info for hardware watchpoints management. + struct DREG { + lldb::addr_t address; // Breakpoint/watchpoint address value. + lldb::addr_t hit_addr; // Address at which last watchpoint trigger + // exception occurred. + lldb::addr_t real_addr; // Address value that should cause target to stop. + uint32_t control; // Breakpoint/watchpoint control value. + uint32_t refcount; // Serves as enable/disable and reference counter. + long slot; // Saves the value returned from PTRACE_SETHWDEBUG. + int mode; // Defines if watchpoint is read/write/access. + }; + + std::array m_hwp_regs; + + // 16 is just a maximum value, query hardware for actual watchpoint count + uint32_t m_max_hwp_supported = 16; + uint32_t m_max_hbp_supported = 16; + bool m_refresh_hwdebug_info = true; +}; + +} // namespace process_aix +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextAIX_ppc64_h + +#endif // defined(__powerpc64__) diff --git a/lldb/source/Plugins/Process/AIX/NativeThreadAIX.cpp b/lldb/source/Plugins/Process/AIX/NativeThreadAIX.cpp new file mode 100644 index 0000000000000..e07daccdff550 --- /dev/null +++ b/lldb/source/Plugins/Process/AIX/NativeThreadAIX.cpp @@ -0,0 +1,526 @@ +//===-- NativeThreadAIX.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 "NativeThreadAIX.h" + +#include +#include + +#include "NativeProcessAIX.h" +#include "NativeRegisterContextAIX.h" + +#include "lldb/Host/HostNativeThread.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" +#include "lldb/lldb-enumerations.h" + +#include "llvm/ADT/SmallString.h" + +#include "Plugins/Process/POSIX/CrashReason.h" + +#include +#include +#include + +#if 0 +#include +// Try to define a macro to encapsulate the tgkill syscall +#define tgkill(pid, tid, sig) \ + syscall(__NR_tgkill, static_cast<::pid_t>(pid), static_cast<::pid_t>(tid), \ + sig) +#endif + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_aix; + +namespace { +void LogThreadStopInfo(Log &log, const ThreadStopInfo &stop_info, + const char *const header) { + switch (stop_info.reason) { + case eStopReasonNone: + log.Printf("%s: %s no stop reason", __FUNCTION__, header); + return; + case eStopReasonTrace: + log.Printf("%s: %s trace, stopping signal 0x%" PRIx32, __FUNCTION__, header, + stop_info.signo); + return; + case eStopReasonBreakpoint: + log.Printf("%s: %s breakpoint, stopping signal 0x%" PRIx32, __FUNCTION__, + header, stop_info.signo); + return; + case eStopReasonWatchpoint: + log.Printf("%s: %s watchpoint, stopping signal 0x%" PRIx32, __FUNCTION__, + header, stop_info.signo); + return; + case eStopReasonSignal: + log.Printf("%s: %s signal 0x%02" PRIx32, __FUNCTION__, header, + stop_info.signo); + return; + case eStopReasonException: + log.Printf("%s: %s exception type 0x%02" PRIx64, __FUNCTION__, header, + stop_info.details.exception.type); + return; + case eStopReasonExec: + log.Printf("%s: %s exec, stopping signal 0x%" PRIx32, __FUNCTION__, header, + stop_info.signo); + return; + case eStopReasonPlanComplete: + log.Printf("%s: %s plan complete", __FUNCTION__, header); + return; + case eStopReasonThreadExiting: + log.Printf("%s: %s thread exiting", __FUNCTION__, header); + return; + case eStopReasonInstrumentation: + log.Printf("%s: %s instrumentation", __FUNCTION__, header); + return; + case eStopReasonProcessorTrace: + log.Printf("%s: %s processor trace", __FUNCTION__, header); + return; + default: + log.Printf("%s: %s invalid stop reason %" PRIu32, __FUNCTION__, header, + static_cast(stop_info.reason)); + } +} +} + +NativeThreadAIX::NativeThreadAIX(NativeProcessAIX &process, + lldb::tid_t tid) + : NativeThreadProtocol(process, tid), m_state(StateType::eStateInvalid), + m_stop_info(), + m_reg_context_up( + NativeRegisterContextAIX::CreateHostNativeRegisterContextAIX( + process.GetArchitecture(), *this)), + m_stop_description() {} + +std::string NativeThreadAIX::GetName() { + NativeProcessAIX &process = GetProcess(); + + auto BufferOrError = getProcFile(process.GetID(), GetID(), "comm"); + if (!BufferOrError) + return ""; + return std::string(BufferOrError.get()->getBuffer().rtrim('\n')); +} + +lldb::StateType NativeThreadAIX::GetState() { return m_state; } + +bool NativeThreadAIX::GetStopReason(ThreadStopInfo &stop_info, + std::string &description) { + Log *log = GetLog(LLDBLog::Thread); + + description.clear(); + + switch (m_state) { + case eStateStopped: + case eStateCrashed: + case eStateExited: + case eStateSuspended: + case eStateUnloaded: + if (log) + LogThreadStopInfo(*log, m_stop_info, "m_stop_info in thread:"); + stop_info = m_stop_info; + description = m_stop_description; + if (log) + LogThreadStopInfo(*log, stop_info, "returned stop_info:"); + + return true; + + case eStateInvalid: + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + case eStateDetached: + if (log) { + LLDB_LOGF(log, + "NativeThreadAIX::%s tid %" PRIu64 + " in state %s cannot answer stop reason", + __FUNCTION__, GetID(), StateAsCString(m_state)); + } + return false; + } + llvm_unreachable("unhandled StateType!"); +} + +Status NativeThreadAIX::SetWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags, bool hardware) { + if (!hardware) + return Status("not implemented"); + if (m_state == eStateLaunching) + return Status(); + Status error = RemoveWatchpoint(addr); + if (error.Fail()) + return error; + uint32_t wp_index = + m_reg_context_up->SetHardwareWatchpoint(addr, size, watch_flags); + if (wp_index == LLDB_INVALID_INDEX32) + return Status("Setting hardware watchpoint failed."); + m_watchpoint_index_map.insert({addr, wp_index}); + return Status(); +} + +Status NativeThreadAIX::RemoveWatchpoint(lldb::addr_t addr) { + auto wp = m_watchpoint_index_map.find(addr); + if (wp == m_watchpoint_index_map.end()) + return Status(); + uint32_t wp_index = wp->second; + m_watchpoint_index_map.erase(wp); + if (m_reg_context_up->ClearHardwareWatchpoint(wp_index)) + return Status(); + return Status("Clearing hardware watchpoint failed."); +} + +Status NativeThreadAIX::SetHardwareBreakpoint(lldb::addr_t addr, + size_t size) { + if (m_state == eStateLaunching) + return Status(); + + Status error = RemoveHardwareBreakpoint(addr); + if (error.Fail()) + return error; + + uint32_t bp_index = m_reg_context_up->SetHardwareBreakpoint(addr, size); + + if (bp_index == LLDB_INVALID_INDEX32) + return Status("Setting hardware breakpoint failed."); + + m_hw_break_index_map.insert({addr, bp_index}); + return Status(); +} + +Status NativeThreadAIX::RemoveHardwareBreakpoint(lldb::addr_t addr) { + auto bp = m_hw_break_index_map.find(addr); + if (bp == m_hw_break_index_map.end()) + return Status(); + + uint32_t bp_index = bp->second; + if (m_reg_context_up->ClearHardwareBreakpoint(bp_index)) { + m_hw_break_index_map.erase(bp); + return Status(); + } + + return Status("Clearing hardware breakpoint failed."); +} + +Status NativeThreadAIX::Resume(uint32_t signo) { + const StateType new_state = StateType::eStateRunning; + MaybeLogStateChange(new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonNone; + m_stop_description.clear(); + + // If watchpoints have been set, but none on this thread, then this is a new + // thread. So set all existing watchpoints. + if (m_watchpoint_index_map.empty()) { + NativeProcessAIX &process = GetProcess(); + + const auto &watchpoint_map = process.GetWatchpointMap(); + m_reg_context_up->ClearAllHardwareWatchpoints(); + for (const auto &pair : watchpoint_map) { + const auto &wp = pair.second; + SetWatchpoint(wp.m_addr, wp.m_size, wp.m_watch_flags, wp.m_hardware); + } + } + + // Set all active hardware breakpoint on all threads. + if (m_hw_break_index_map.empty()) { + NativeProcessAIX &process = GetProcess(); + + const auto &hw_breakpoint_map = process.GetHardwareBreakpointMap(); + m_reg_context_up->ClearAllHardwareBreakpoints(); + for (const auto &pair : hw_breakpoint_map) { + const auto &bp = pair.second; + SetHardwareBreakpoint(bp.m_addr, bp.m_size); + } + } + + intptr_t data = 0; + + if (signo != LLDB_INVALID_SIGNAL_NUMBER) + data = signo; + + return NativeProcessAIX::PtraceWrapper(PT_CONTINUE, GetID(), nullptr, + reinterpret_cast(data)); +} + +Status NativeThreadAIX::SingleStep(uint32_t signo) { + const StateType new_state = StateType::eStateStepping; + MaybeLogStateChange(new_state); + m_state = new_state; + m_stop_info.reason = StopReason::eStopReasonNone; + + intptr_t data = 0; + if (signo != LLDB_INVALID_SIGNAL_NUMBER) + data = signo; + + // If hardware single-stepping is not supported, we just do a continue. The + // breakpoint on the next instruction has been setup in + // NativeProcessAIX::Resume. + return NativeProcessAIX::PtraceWrapper( + GetProcess().SupportHardwareSingleStepping() ? PT_STEP : PT_CONTINUE, + m_tid, nullptr, reinterpret_cast(data)); +} + +void NativeThreadAIX::SetStoppedBySignal(uint32_t signo, + const siginfo_t *info) { + Log *log = GetLog(LLDBLog::Thread); + LLDB_LOGF(log, "NativeThreadAIX::%s called with signal 0x%02" PRIx32, + __FUNCTION__, signo); + + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonSignal; + m_stop_info.signo = signo; + + m_stop_description.clear(); + switch (signo) { + case SIGSEGV: + case SIGBUS: + case SIGFPE: + case SIGILL: + break; + } +} + +void NativeThreadAIX::AnnotateSyncTagCheckFault(const siginfo_t *info) { + int32_t allocation_tag_type = 0; + switch (GetProcess().GetArchitecture().GetMachine()) { + default: + return; + } + + auto details = + GetRegisterContext().GetMemoryTaggingDetails(allocation_tag_type); + if (!details) { + llvm::consumeError(details.takeError()); + return; + } + + // We assume that the stop description is currently: + // signal SIGSEGV: sync tag check fault (fault address: ) + // Remove the closing ) + m_stop_description.pop_back(); + + std::stringstream ss; + lldb::addr_t fault_addr = reinterpret_cast(info->si_addr); + std::unique_ptr manager(std::move(details->manager)); + + ss << " logical tag: 0x" << std::hex << manager->GetLogicalTag(fault_addr); + + std::vector allocation_tag_data; + // The fault address may not be granule aligned. ReadMemoryTags will granule + // align any range you give it, potentially making it larger. + // To prevent this set len to 1. This always results in a range that is at + // most 1 granule in size and includes fault_addr. + Status status = GetProcess().ReadMemoryTags(allocation_tag_type, fault_addr, + 1, allocation_tag_data); + + if (status.Success()) { + llvm::Expected> allocation_tag = + manager->UnpackTagsData(allocation_tag_data, 1); + if (allocation_tag) { + ss << " allocation tag: 0x" << std::hex << allocation_tag->front() << ")"; + } else { + llvm::consumeError(allocation_tag.takeError()); + ss << ")"; + } + } else + ss << ")"; + + m_stop_description += ss.str(); +} + +bool NativeThreadAIX::IsStopped(int *signo) { + if (!StateIsStoppedState(m_state, false)) + return false; + + // If we are stopped by a signal, return the signo. + if (signo && m_state == StateType::eStateStopped && + m_stop_info.reason == StopReason::eStopReasonSignal) { + *signo = m_stop_info.signo; + } + + // Regardless, we are stopped. + return true; +} + +void NativeThreadAIX::SetStopped() { + // On every stop, clear any cached register data structures + GetRegisterContext().InvalidateAllRegisters(); + + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange(new_state); + m_state = new_state; + m_stop_description.clear(); +} + +void NativeThreadAIX::SetStoppedByExec() { + Log *log = GetLog(LLDBLog::Thread); + LLDB_LOGF(log, "NativeThreadAIX::%s()", __FUNCTION__); + + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonExec; + m_stop_info.signo = SIGSTOP; +} + +void NativeThreadAIX::SetStoppedByBreakpoint() { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonBreakpoint; + m_stop_info.signo = SIGTRAP; + m_stop_description.clear(); +} + +void NativeThreadAIX::SetStoppedByWatchpoint(uint32_t wp_index) { + SetStopped(); + + lldbassert(wp_index != LLDB_INVALID_INDEX32 && "wp_index cannot be invalid"); + + std::ostringstream ostr; + ostr << m_reg_context_up->GetWatchpointAddress(wp_index) << " "; + ostr << wp_index; + + /* + * MIPS: Last 3bits of the watchpoint address are masked by the kernel. For + * example: + * 'n' is at 0x120010d00 and 'm' is 0x120010d04. When a watchpoint is set at + * 'm', then + * watch exception is generated even when 'n' is read/written. To handle this + * case, + * find the base address of the load/store instruction and append it in the + * stop-info + * packet. + */ + ostr << " " << m_reg_context_up->GetWatchpointHitAddress(wp_index); + + m_stop_description = ostr.str(); + + m_stop_info.reason = StopReason::eStopReasonWatchpoint; + m_stop_info.signo = SIGTRAP; +} + +bool NativeThreadAIX::IsStoppedAtBreakpoint() { + return GetState() == StateType::eStateStopped && + m_stop_info.reason == StopReason::eStopReasonBreakpoint; +} + +bool NativeThreadAIX::IsStoppedAtWatchpoint() { + return GetState() == StateType::eStateStopped && + m_stop_info.reason == StopReason::eStopReasonWatchpoint; +} + +void NativeThreadAIX::SetStoppedByTrace() { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonTrace; + m_stop_info.signo = SIGTRAP; +} + +void NativeThreadAIX::SetStoppedByFork(bool is_vfork, lldb::pid_t child_pid) { + SetStopped(); + + m_stop_info.reason = + is_vfork ? StopReason::eStopReasonVFork : StopReason::eStopReasonFork; + m_stop_info.details.fork.child_pid = child_pid; + m_stop_info.details.fork.child_tid = child_pid; +} + +void NativeThreadAIX::SetStoppedByVForkDone() { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonVForkDone; +} + +void NativeThreadAIX::SetStoppedWithNoReason() { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonNone; + m_stop_info.signo = 0; +} + +void NativeThreadAIX::SetStoppedByProcessorTrace( + llvm::StringRef description) { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonProcessorTrace; + m_stop_info.signo = 0; + m_stop_description = description.str(); +} + +void NativeThreadAIX::SetExited() { + const StateType new_state = StateType::eStateExited; + MaybeLogStateChange(new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonThreadExiting; +} + +Status NativeThreadAIX::RequestStop() { + Log *log = GetLog(LLDBLog::Thread); + + NativeProcessAIX &process = GetProcess(); + + lldb::pid_t pid = process.GetID(); + lldb::tid_t tid = GetID(); + + LLDB_LOGF(log, + "NativeThreadAIX::%s requesting thread stop(pid: %" PRIu64 + ", tid: %" PRIu64 ")", + __FUNCTION__, pid, tid); + + Status err; + errno = 0; + if (::kill(pid, SIGSTOP) != 0) { + err.SetErrorToErrno(); + LLDB_LOGF(log, + "NativeThreadAIX::%s kill(%" PRIu64 ", SIGSTOP) failed: %s", + __FUNCTION__, pid, err.AsCString()); + } + return err; +} + +void NativeThreadAIX::MaybeLogStateChange(lldb::StateType new_state) { + Log *log = GetLog(LLDBLog::Thread); + // If we're not logging, we're done. + if (!log) + return; + + // If this is a state change to the same state, we're done. + lldb::StateType old_state = m_state; + if (new_state == old_state) + return; + + LLDB_LOG(log, "pid={0}, tid={1}: changing from state {2} to {3}", + m_process.GetID(), GetID(), old_state, new_state); +} + +NativeProcessAIX &NativeThreadAIX::GetProcess() { + return static_cast(m_process); +} + +const NativeProcessAIX &NativeThreadAIX::GetProcess() const { + return static_cast(m_process); +} + +llvm::Expected> +NativeThreadAIX::GetSiginfo() const { + auto siginfo_buf = + llvm::WritableMemoryBuffer::getNewUninitMemBuffer(sizeof(siginfo_t)); +#if 0 + Status error = + GetProcess().GetSignalInfo(GetID(), siginfo_buf->getBufferStart()); + if (!error.Success()) + return error.ToError(); +#endif + return std::move(siginfo_buf); +} diff --git a/lldb/source/Plugins/Process/AIX/NativeThreadAIX.h b/lldb/source/Plugins/Process/AIX/NativeThreadAIX.h new file mode 100644 index 0000000000000..706a7ce69da8e --- /dev/null +++ b/lldb/source/Plugins/Process/AIX/NativeThreadAIX.h @@ -0,0 +1,126 @@ +//===-- NativeThreadAIX.h ----------------------------------- -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_NativeThreadAIX_H_ +#define liblldb_NativeThreadAIX_H_ + +#include "Plugins/Process/AIX/NativeRegisterContextAIX.h" +#include "lldb/Host/common/NativeThreadProtocol.h" +#include "lldb/lldb-private-forward.h" + +#include "llvm/ADT/StringRef.h" + +#include +#include +#include +#include + +namespace lldb_private { +namespace process_aix { + +class NativeProcessAIX; + +class NativeThreadAIX : public NativeThreadProtocol { + friend class NativeProcessAIX; + +public: + NativeThreadAIX(NativeProcessAIX &process, lldb::tid_t tid); + + // NativeThreadProtocol Interface + std::string GetName() override; + + lldb::StateType GetState() override; + + bool GetStopReason(ThreadStopInfo &stop_info, + std::string &description) override; + + NativeRegisterContextAIX &GetRegisterContext() override { + return *m_reg_context_up; + } + + Status SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags, + bool hardware) override; + + Status RemoveWatchpoint(lldb::addr_t addr) override; + + Status SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; + + Status RemoveHardwareBreakpoint(lldb::addr_t addr) override; + + NativeProcessAIX &GetProcess(); + + const NativeProcessAIX &GetProcess() const; + + llvm::Expected> + GetSiginfo() const override; + +private: + // Interface for friend classes + + /// Resumes the thread. If \p signo is anything but + /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. + Status Resume(uint32_t signo); + + /// Single steps the thread. If \p signo is anything but + /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. + Status SingleStep(uint32_t signo); + + void SetStoppedBySignal(uint32_t signo, const siginfo_t *info = nullptr); + + /// Return true if the thread is stopped. + /// If stopped by a signal, indicate the signo in the signo argument. + /// Otherwise, return LLDB_INVALID_SIGNAL_NUMBER. + bool IsStopped(int *signo); + + void SetStoppedByExec(); + + void SetStoppedByBreakpoint(); + + void SetStoppedByWatchpoint(uint32_t wp_index); + + bool IsStoppedAtBreakpoint(); + + bool IsStoppedAtWatchpoint(); + + void SetStoppedByTrace(); + + void SetStoppedByFork(bool is_vfork, lldb::pid_t child_pid); + + void SetStoppedByVForkDone(); + + void SetStoppedWithNoReason(); + + void SetStoppedByProcessorTrace(llvm::StringRef description); + + void SetExited(); + + Status RequestStop(); + + // Private interface + void MaybeLogStateChange(lldb::StateType new_state); + + void SetStopped(); + + /// Extend m_stop_description with logical and allocation tag values. + /// If there is an error along the way just add the information we were able + /// to get. + void AnnotateSyncTagCheckFault(const siginfo_t *info); + + // Member Variables + lldb::StateType m_state; + ThreadStopInfo m_stop_info; + std::unique_ptr m_reg_context_up; + std::string m_stop_description; + using WatchpointIndexMap = std::map; + WatchpointIndexMap m_watchpoint_index_map; + WatchpointIndexMap m_hw_break_index_map; +}; +} // namespace process_aix +} // namespace lldb_private + +#endif // #ifndef liblldb_NativeThreadAIX_H_ diff --git a/lldb/source/Plugins/Process/CMakeLists.txt b/lldb/source/Plugins/Process/CMakeLists.txt index a51d0f7afd175..01bb5f462eba4 100644 --- a/lldb/source/Plugins/Process/CMakeLists.txt +++ b/lldb/source/Plugins/Process/CMakeLists.txt @@ -7,6 +7,9 @@ elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") elseif (CMAKE_SYSTEM_NAME MATCHES "NetBSD") add_subdirectory(NetBSD) add_subdirectory(POSIX) +elseif (CMAKE_SYSTEM_NAME MATCHES "AIX") + add_subdirectory(AIX) + add_subdirectory(POSIX) elseif (CMAKE_SYSTEM_NAME MATCHES "Windows") add_subdirectory(Windows/Common) elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin") diff --git a/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp b/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp index 32c71d87c7f58..db271357d792a 100644 --- a/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp +++ b/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp @@ -46,8 +46,34 @@ bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr, function_options.include_inlines = false; SymbolContextList sc_list; +#if !defined(__AIX__) process->GetTarget().GetImages().FindFunctions( ConstString("mmap"), eFunctionNameTypeFull, function_options, sc_list); +#else + process->GetTarget().GetImages().FindFunctions( + ConstString("mmap64"), eFunctionNameTypeFull, function_options, sc_list); + SymbolContextList toc_list; + process->GetTarget().GetImages().FindSymbolsWithNameAndType( + ConstString("TOC"), lldb::eSymbolTypeAny, toc_list); + + AddressRange toc_range; + if (sc_list.GetSize() > 0) { + SymbolContext sc; + if (sc_list.GetContextAtIndex(0, sc)) { + for (int i = 0; i < toc_list.GetSize(); ++i) { + SymbolContext tocSC; + if (toc_list.GetContextAtIndex(i, tocSC)) { + if (tocSC.module_sp == sc.module_sp) { + if (tocSC.GetAddressRange(eSymbolContextSymbol, 0, false, + toc_range)) { + break; + } + } + } + } + } + } +#endif const uint32_t count = sc_list.GetSize(); if (count > 0) { SymbolContext sc; @@ -96,9 +122,16 @@ bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr, MmapArgList args = process->GetTarget().GetPlatform()->GetMmapArgumentList( arch, addr, length, prot_arg, flags, fd, offset); +#if defined(__AIX__) + lldb::ThreadPlanSP call_plan_sp( + new ThreadPlanCallFunction(*thread, mmap_range.GetBaseAddress(), + toc_range.GetBaseAddress(), + void_ptr_type, args, options)); +#else lldb::ThreadPlanSP call_plan_sp( new ThreadPlanCallFunction(*thread, mmap_range.GetBaseAddress(), void_ptr_type, args, options)); +#endif if (call_plan_sp) { DiagnosticManager diagnostics; diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.cpp b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.cpp index 159fd2856443c..d9b41d595147f 100644 --- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.cpp +++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.cpp @@ -23,6 +23,8 @@ static const lldb_private::RegisterInfo * GetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) { switch (target_arch.GetMachine()) { + //HH + case llvm::Triple::ppc64: case llvm::Triple::ppc64le: return g_register_infos_ppc64le; default: @@ -34,6 +36,8 @@ GetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) { static uint32_t GetRegisterInfoCount(const lldb_private::ArchSpec &target_arch) { switch (target_arch.GetMachine()) { + //HitchHike + case llvm::Triple::ppc64: case llvm::Triple::ppc64le: return static_cast(sizeof(g_register_infos_ppc64le) / sizeof(g_register_infos_ppc64le[0])); diff --git a/lldb/source/Plugins/Process/Utility/ThreadMemory.cpp b/lldb/source/Plugins/Process/Utility/ThreadMemory.cpp index 89ecc757a68f5..550b53688fd39 100644 --- a/lldb/source/Plugins/Process/Utility/ThreadMemory.cpp +++ b/lldb/source/Plugins/Process/Utility/ThreadMemory.cpp @@ -20,7 +20,7 @@ using namespace lldb; using namespace lldb_private; -ThreadMemory::ThreadMemory(Process &process, tid_t tid, +ThreadMemory::ThreadMemory(Process &process, lldb::tid_t tid, const ValueObjectSP &thread_info_valobj_sp) : Thread(process, tid), m_backing_thread_sp(), m_thread_info_valobj_sp(thread_info_valobj_sp), m_name(), m_queue(), diff --git a/lldb/source/Plugins/Process/gdb-remote/CMakeLists.txt b/lldb/source/Plugins/Process/gdb-remote/CMakeLists.txt index 6755999b18185..4eddbb5ec4cfd 100644 --- a/lldb/source/Plugins/Process/gdb-remote/CMakeLists.txt +++ b/lldb/source/Plugins/Process/gdb-remote/CMakeLists.txt @@ -6,6 +6,11 @@ lldb_tablegen(ProcessGDBRemotePropertiesEnum.inc -gen-lldb-property-enum-defs SOURCE ProcessGDBRemoteProperties.td TARGET LLDBPluginProcessGDBRemotePropertiesEnumGen) +if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX") + remove_definitions("-D_XOPEN_SOURCE=700") + add_definitions("-D_ALL_SOURCE") +endif() + set(LLDB_PLUGINS lldbPluginProcessUtility ) diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index 74e392249a94e..b7ecf7a5dc328 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -40,6 +40,10 @@ #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/JSON.h" +#if defined(__AIX__) +#include +#endif + #if defined(HAVE_LIBCOMPRESSION) #include #endif @@ -1710,6 +1714,32 @@ Status GDBRemoteCommunicationClient::GetMemoryRegionInfo( return error; } +#if defined(__AIX__) +Status GDBRemoteCommunicationClient::GetLDXINFO(struct ld_xinfo *info_ptr) +{ + Status error; + + char packet[64]; + const int packet_len = ::snprintf(packet, sizeof(packet), "qLDXINFO"); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, response) == + PacketResult::Success && + response.GetResponseType() == StringExtractorGDBRemote::eResponse) { + llvm::MutableArrayRef infoData((uint8_t *)info_ptr, sizeof(struct ld_xinfo)*64); + size_t got_bytes = response.GetHexBytesAvail(infoData); + if (got_bytes != sizeof(struct ld_xinfo)*64) { + error.SetErrorString("qLDXINFO ret bad size"); + return error; + } + } else { + error.SetErrorString("qLDXINFO is not supported"); + } + return error; +} +#endif + Status GDBRemoteCommunicationClient::GetQXferMemoryMapRegionInfo( lldb::addr_t addr, MemoryRegionInfo ®ion) { Status error = LoadQXferMemoryMap(); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h index 898d176abc346..520f37ac56716 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -32,6 +32,10 @@ #include "llvm/Support/VersionTuple.h" +#if defined(__AIX__) +struct ld_xinfo; +#endif + namespace lldb_private { namespace process_gdb_remote { @@ -196,6 +200,9 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase { Status GetMemoryRegionInfo(lldb::addr_t addr, MemoryRegionInfo &range_info); std::optional GetWatchpointSlotCount(); +#if defined(__AIX__) + Status GetLDXINFO(struct ld_xinfo *info_ptr); +#endif std::optional GetWatchpointReportedAfter(); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp index a0b08a219ae14..f019062986925 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -48,6 +48,9 @@ #include "ProcessGDBRemote.h" #include "ProcessGDBRemoteLog.h" #include "lldb/Utility/StringExtractorGDBRemote.h" +#if defined(__AIX__) +#include +#endif using namespace lldb; using namespace lldb_private; @@ -193,6 +196,8 @@ void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() { &GDBRemoteCommunicationServerLLGS::Handle_Z); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_z, &GDBRemoteCommunicationServerLLGS::Handle_z); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qLDXINFO, + &GDBRemoteCommunicationServerLLGS::Handle_qLDXINFO); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_QPassSignals, &GDBRemoteCommunicationServerLLGS::Handle_QPassSignals); @@ -3006,6 +3011,29 @@ GDBRemoteCommunicationServerLLGS::Handle_z(StringExtractorGDBRemote &packet) { } } +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qLDXINFO(StringExtractorGDBRemote &packet) { + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) { + Log *log = GetLog(LLDBLog::Process); + LLDB_LOG(log, "qLDXINFO failed, no process available"); + return SendErrorResponse(0xff); + } + +#if defined(__AIX__) + // FIXME: buffer size + struct ld_xinfo info[64]; + if (ptrace64(PT_LDXINFO, m_current_process->GetID(), (long long)&(info[0]), sizeof(info), nullptr) != 0) { + return SendErrorResponse(0xff); + } + StreamGDBRemote response; + response.PutBytesAsRawHex8(&(info[0]), sizeof(info)); + return SendPacketNoLock(response.GetString()); +#else + return SendErrorResponse(0xff); +#endif +} + GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_s(StringExtractorGDBRemote &packet) { Log *log = GetLog(LLDBLog::Process | LLDBLog::Thread); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h index 646b6a102abf6..a464479e178de 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h @@ -211,6 +211,8 @@ class GDBRemoteCommunicationServerLLGS PacketResult Handle_z(StringExtractorGDBRemote &packet); + PacketResult Handle_qLDXINFO(StringExtractorGDBRemote &packet); + PacketResult Handle_s(StringExtractorGDBRemote &packet); PacketResult Handle_qXfer(StringExtractorGDBRemote &packet); diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index 6f9c2cc1e4b4e..10fbaa2b3c837 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -92,6 +92,10 @@ #include "llvm/Support/Threading.h" #include "llvm/Support/raw_ostream.h" +#if defined(__AIX__) +#include +#endif + #define DEBUGSERVER_BASENAME "debugserver" using namespace lldb; using namespace lldb_private; @@ -1513,7 +1517,7 @@ bool ProcessGDBRemote::DoUpdateThreadList(ThreadList &old_thread_list, ThreadList old_thread_list_copy(old_thread_list); if (num_thread_ids > 0) { for (size_t i = 0; i < num_thread_ids; ++i) { - tid_t tid = m_thread_ids[i]; + lldb::tid_t tid = m_thread_ids[i]; ThreadSP thread_sp( old_thread_list_copy.RemoveThreadByProtocolID(tid, false)); if (!thread_sp) { @@ -2945,6 +2949,13 @@ Status ProcessGDBRemote::DoGetMemoryRegionInfo(addr_t load_addr, return error; } +#if defined(__AIX__) +Status ProcessGDBRemote::DoGetLDXINFO(struct ld_xinfo *info_ptr) { + Status error(m_gdb_comm.GetLDXINFO(info_ptr)); + return error; +} +#endif + std::optional ProcessGDBRemote::GetWatchpointSlotCount() { return m_gdb_comm.GetWatchpointSlotCount(); } diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index 2492795851388..82200fbea21cd 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -37,6 +37,10 @@ #include "GDBRemoteCommunicationClient.h" #include "GDBRemoteRegisterContext.h" +#if defined(__AIX__) +struct ld_xinfo; +#endif + #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringMap.h" @@ -423,6 +427,10 @@ class ProcessGDBRemote : public Process, Status DoGetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo ®ion_info) override; +#if defined(__AIX__) + Status DoGetLDXINFO(struct ld_xinfo *info_ptr) override; +#endif + private: // For ProcessGDBRemote only std::string m_partial_profile_data; diff --git a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp index 1da7696c9a352..930c707604bb3 100644 --- a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp +++ b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp @@ -593,19 +593,19 @@ bool ProcessMachCore::DoUpdateThreadList(ThreadList &old_thread_list, ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); if (core_objfile) { - std::set used_tids; + std::set used_tids; const uint32_t num_threads = core_objfile->GetNumThreadContexts(); - std::vector tids; + std::vector tids; if (core_objfile->GetCorefileThreadExtraInfos(tids)) { assert(tids.size() == num_threads); // Find highest tid value. - tid_t highest_tid = 0; + lldb::tid_t highest_tid = 0; for (uint32_t i = 0; i < num_threads; i++) { if (tids[i] != LLDB_INVALID_THREAD_ID && tids[i] > highest_tid) highest_tid = tids[i]; } - tid_t current_unused_tid = highest_tid + 1; + lldb::tid_t current_unused_tid = highest_tid + 1; for (uint32_t i = 0; i < num_threads; i++) { if (tids[i] == LLDB_INVALID_THREAD_ID) { tids[i] = current_unused_tid++; diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt b/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt index 7523d65abf0f8..1ce60a0b66154 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt +++ b/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt @@ -20,6 +20,11 @@ if (LLDB_ENABLE_LIBEDIT) endif() add_subdirectory(Interfaces) +if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX") + remove_definitions("-D_XOPEN_SOURCE=700") + add_definitions("-D_ALL_SOURCE") +endif() + add_lldb_library(lldbPluginScriptInterpreterPython PLUGIN PythonDataObjects.cpp diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp index e1f73f1997e36..92882cfc3da31 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp @@ -500,6 +500,8 @@ dw_addr_t DWARFFormValue::Address() const { &offset, index_size); } +bool UGLY_FLAG_FOR_AIX __attribute__((weak)) = false; + std::pair DWARFFormValue::ReferencedUnitAndOffset() const { uint64_t value = m_value.value.uval; @@ -512,6 +514,8 @@ DWARFFormValue::ReferencedUnitAndOffset() const { assert(m_unit); // Unit must be valid for DW_FORM_ref forms that are compile // unit relative or we will get this wrong value += m_unit->GetOffset(); + if (UGLY_FLAG_FOR_AIX) + value -= 8; if (!m_unit->ContainsDIEOffset(value)) { m_unit->GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError( "DW_FORM_ref* DIE reference {0:x16} is outside of its CU", value); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp index 66a762bf9b685..6721c1895a576 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp @@ -924,6 +924,12 @@ const DWARFDebugAranges &DWARFUnit::GetFunctionAranges() { return *m_func_aranges_up; } +/* AIX-NOTE - TODO: Removed conflicting code due to merge conflicts + * Refer Patches: 27,28,29,30,35 and 76 + * and modify the code accordingly. */ + +bool UGLY_FLAG_FOR_AIX __attribute__((weak)) = false; + llvm::Expected DWARFUnit::extract(SymbolFileDWARF &dwarf, user_id_t uid, const DWARFDataExtractor &debug_info, @@ -1002,6 +1008,10 @@ const lldb_private::DWARFDataExtractor &DWARFUnit::GetData() const { uint32_t DWARFUnit::GetHeaderByteSize() const { switch (m_header.getUnitType()) { case llvm::dwarf::DW_UT_compile: + if (UGLY_FLAG_FOR_AIX) + return 11 + 4/*GetDWARFSizeOfOffset*/; + else + return GetVersion() < 5 ? 11 : 12; case llvm::dwarf::DW_UT_partial: return GetVersion() < 5 ? 11 : 12; case llvm::dwarf::DW_UT_skeleton: @@ -1016,7 +1026,7 @@ uint32_t DWARFUnit::GetHeaderByteSize() const { std::optional DWARFUnit::GetStringOffsetSectionItem(uint32_t index) const { - offset_t offset = GetStrOffsetsBase() + index * 4; + lldb::offset_t offset = GetStrOffsetsBase() + index * 4; return m_dwarf.GetDWARFContext().getOrLoadStrOffsetsData().GetU32(&offset); } diff --git a/lldb/source/Plugins/SystemRuntime/MacOSX/AppleGetThreadItemInfoHandler.cpp b/lldb/source/Plugins/SystemRuntime/MacOSX/AppleGetThreadItemInfoHandler.cpp index 2064b73dc3ea5..824528fc3acfa 100644 --- a/lldb/source/Plugins/SystemRuntime/MacOSX/AppleGetThreadItemInfoHandler.cpp +++ b/lldb/source/Plugins/SystemRuntime/MacOSX/AppleGetThreadItemInfoHandler.cpp @@ -216,7 +216,7 @@ lldb::addr_t AppleGetThreadItemInfoHandler::SetupGetThreadItemInfoFunction( AppleGetThreadItemInfoHandler::GetThreadItemInfoReturnInfo AppleGetThreadItemInfoHandler::GetThreadItemInfo(Thread &thread, - tid_t thread_id, + lldb::tid_t thread_id, addr_t page_to_free, uint64_t page_to_free_size, Status &error) { diff --git a/lldb/source/Symbol/DWARFCallFrameInfo.cpp b/lldb/source/Symbol/DWARFCallFrameInfo.cpp index f3df8a2c27f5a..de244e372579d 100644 --- a/lldb/source/Symbol/DWARFCallFrameInfo.cpp +++ b/lldb/source/Symbol/DWARFCallFrameInfo.cpp @@ -33,7 +33,7 @@ using namespace lldb_private::dwarf; // Used for calls when the value type is specified by a DWARF EH Frame pointer // encoding. static uint64_t -GetGNUEHPointer(const DataExtractor &DE, offset_t *offset_ptr, +GetGNUEHPointer(const DataExtractor &DE, lldb::offset_t *offset_ptr, uint32_t eh_ptr_enc, addr_t pc_rel_addr, addr_t text_addr, addr_t data_addr) //, BSDRelocs *data_relocs) const { @@ -588,7 +588,7 @@ bool DWARFCallFrameInfo::FDEToUnwindPlan(dw_offset_t dwarf_offset, if (cie->augmentation[0] == 'z') { uint32_t aug_data_len = (uint32_t)m_cfi_data.GetULEB128(&offset); if (aug_data_len != 0 && cie->lsda_addr_encoding != DW_EH_PE_omit) { - offset_t saved_offset = offset; + lldb::offset_t saved_offset = offset; lsda_data_file_address = GetGNUEHPointer(m_cfi_data, &offset, cie->lsda_addr_encoding, pc_rel_addr, text_addr, data_addr); diff --git a/lldb/source/Target/ABI.cpp b/lldb/source/Target/ABI.cpp index 110b5c86fc425..6df03533cda29 100644 --- a/lldb/source/Target/ABI.cpp +++ b/lldb/source/Target/ABI.cpp @@ -208,6 +208,15 @@ bool ABI::PrepareTrivialCall(Thread &thread, lldb::addr_t sp, llvm_unreachable("Should never get here!"); } +bool ABI::PrepareTrivialCall(Thread &thread, lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t tocAddress, + lldb::addr_t returnAddress, + llvm::ArrayRef args) const { + // dummy prepare trivial call + llvm_unreachable("Should never get here!"); +} + bool ABI::GetFallbackRegisterLocation( const RegisterInfo *reg_info, UnwindPlan::Row::RegisterLocation &unwind_regloc) { diff --git a/lldb/source/Target/CMakeLists.txt b/lldb/source/Target/CMakeLists.txt index a42c44b761dc5..833489b16dfd7 100644 --- a/lldb/source/Target/CMakeLists.txt +++ b/lldb/source/Target/CMakeLists.txt @@ -1,3 +1,8 @@ +if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX") + remove_definitions("-D_XOPEN_SOURCE=700") + add_definitions("-D_ALL_SOURCE") +endif() + lldb_tablegen(TargetProperties.inc -gen-lldb-property-defs SOURCE TargetProperties.td TARGET LLDBTargetPropertiesGen) diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index e3c4f2ee398cc..e31245178b2f2 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -75,6 +75,10 @@ #include "lldb/Utility/State.h" #include "lldb/Utility/Timer.h" +#if defined(__AIX__) +#include +#endif + using namespace lldb; using namespace lldb_private; using namespace std::chrono; @@ -6206,6 +6210,12 @@ Status Process::GetMemoryRegionInfo(lldb::addr_t load_addr, return DoGetMemoryRegionInfo(load_addr, range_info); } +#if defined(__AIX__) +Status Process::GetLDXINFO(struct ld_xinfo *info_ptr) { + return DoGetLDXINFO(info_ptr); +} +#endif + Status Process::GetMemoryRegions(lldb_private::MemoryRegionInfos ®ion_list) { Status error; diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index a61228d092d89..57f42ea56cb18 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -40,6 +40,9 @@ #include #include +#ifdef __AIX__ +#include "Plugins/Process/Utility/lldb-ppc64le-register-enums.h" +#endif using namespace lldb; using namespace lldb_private; @@ -1257,6 +1260,10 @@ bool RegisterContextUnwind::IsTrapHandlerSymbol( // Answer the question: Where did THIS frame save the CALLER frame ("previous" // frame)'s register value? +#ifdef __AIX__ +extern bool UGLY_HACK_NULL_TOPFRAME; +#endif + enum UnwindLLDB::RegisterSearchResult RegisterContextUnwind::SavedLocationForRegister( uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation ®loc) { @@ -1517,6 +1524,11 @@ RegisterContextUnwind::SavedLocationForRegister( new_regloc.type = UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext; new_regloc.location.register_number = regnum.GetAsKind(eRegisterKindLLDB); +#ifdef __AIX__ + if (UGLY_HACK_NULL_TOPFRAME && new_regloc.location.register_number == 0x20) { + new_regloc.location.register_number = 0x24; + } +#endif m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = new_regloc; regloc = new_regloc; UnwindLogMsg("supplying caller's register %s (%d) from the live " @@ -2368,6 +2380,40 @@ bool RegisterContextUnwind::ReadPC(addr_t &pc) { } } +#ifdef __AIX__ +bool RegisterContextUnwind::ReadLR(addr_t &lr) { + if (!IsValid()) + return false; + + bool above_trap_handler = false; + if (GetNextFrame().get() && GetNextFrame()->IsValid() && + GetNextFrame()->IsTrapHandlerFrame()) + above_trap_handler = true; + + if (ReadGPRValue(eRegisterKindLLDB, gpr_lr_ppc64le, lr)) { + // A lr value of 0 or 1 is impossible in the middle of the stack -- it + // indicates the end of a stack walk. + // On the currently executing frame (or such a frame interrupted + // asynchronously by sigtramp et al) this may occur if code has jumped + // through a NULL pointer -- we want to be able to unwind past that frame + // to help find the bug. + + ProcessSP process_sp (m_thread.GetProcess()); + if (process_sp) + { + ABI *abi = process_sp->GetABI().get(); + if (abi) + lr = abi->FixCodeAddress(lr); + } + + return !(m_all_registers_available == false && + above_trap_handler == false && (lr == 0 || lr == 1)); + } else { + return false; + } +} +#endif + void RegisterContextUnwind::UnwindLogMsg(const char *fmt, ...) { Log *log = GetLog(LLDBLog::Unwind); if (!log) diff --git a/lldb/source/Target/ThreadPlanCallFunction.cpp b/lldb/source/Target/ThreadPlanCallFunction.cpp index 50dcb66b9719f..0926579ea2930 100644 --- a/lldb/source/Target/ThreadPlanCallFunction.cpp +++ b/lldb/source/Target/ThreadPlanCallFunction.cpp @@ -127,6 +127,40 @@ ThreadPlanCallFunction::ThreadPlanCallFunction( m_valid = true; } +ThreadPlanCallFunction::ThreadPlanCallFunction( + Thread &thread, const Address &function, const Address &toc, + const CompilerType &return_type, + llvm::ArrayRef args, const EvaluateExpressionOptions &options) + : ThreadPlan(ThreadPlan::eKindCallFunction, "Call function plan", thread, + eVoteNoOpinion, eVoteNoOpinion), + m_valid(false), m_stop_other_threads(options.GetStopOthers()), + m_unwind_on_error(options.DoesUnwindOnError()), + m_ignore_breakpoints(options.DoesIgnoreBreakpoints()), + m_debug_execution(options.GetDebug()), + m_trap_exceptions(options.GetTrapExceptions()), m_function_addr(function), + m_function_sp(0), m_takedown_done(false), + m_should_clear_objc_exception_bp(false), + m_should_clear_cxx_exception_bp(false), + m_stop_address(LLDB_INVALID_ADDRESS), m_return_type(return_type) { + lldb::addr_t start_load_addr = LLDB_INVALID_ADDRESS; + lldb::addr_t function_load_addr = LLDB_INVALID_ADDRESS; + lldb::addr_t toc_addr = LLDB_INVALID_ADDRESS; + ABI *abi = nullptr; + + if (!ConstructorSetup(thread, abi, start_load_addr, function_load_addr)) + return; + + toc_addr = toc.GetLoadAddress(&GetTarget()); + + if (!abi->PrepareTrivialCall(thread, m_function_sp, function_load_addr, + toc_addr, start_load_addr, args)) + return; + + ReportRegisterState("Function call was set up. Register state was:"); + + m_valid = true; +} + ThreadPlanCallFunction::ThreadPlanCallFunction( Thread &thread, const Address &function, const EvaluateExpressionOptions &options) diff --git a/lldb/source/Target/UnwindLLDB.cpp b/lldb/source/Target/UnwindLLDB.cpp index f43e940492b09..255b829738ba2 100644 --- a/lldb/source/Target/UnwindLLDB.cpp +++ b/lldb/source/Target/UnwindLLDB.cpp @@ -68,6 +68,10 @@ uint32_t UnwindLLDB::DoGetFrameCount() { return m_frames.size(); } +#ifdef __AIX__ +bool UGLY_HACK_NULL_TOPFRAME = false; +#endif + bool UnwindLLDB::AddFirstFrame() { if (m_frames.size() > 0) return true; @@ -91,6 +95,17 @@ bool UnwindLLDB::AddFirstFrame() { if (!reg_ctx_sp->ReadPC(first_cursor_sp->start_pc)) goto unwind_done; +#ifdef __AIX__ + lldb::addr_t lr; + if (!reg_ctx_sp->ReadLR(lr)) + goto unwind_done; + + if (first_cursor_sp->start_pc == 0) { + first_cursor_sp->start_pc = lr; + UGLY_HACK_NULL_TOPFRAME = true; + } +#endif + // Everything checks out, so release the auto pointer value and let the // cursor own it in its shared pointer first_cursor_sp->reg_ctx_lldb_sp = reg_ctx_sp; diff --git a/lldb/source/Utility/ArchSpec.cpp b/lldb/source/Utility/ArchSpec.cpp index 07ef435ef451d..3868f77169cc6 100644 --- a/lldb/source/Utility/ArchSpec.cpp +++ b/lldb/source/Utility/ArchSpec.cpp @@ -14,6 +14,7 @@ #include "lldb/lldb-defines.h" #include "llvm/ADT/STLExtras.h" #include "llvm/BinaryFormat/COFF.h" +#include "llvm/BinaryFormat/XCOFF.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/Support/Compiler.h" @@ -459,10 +460,22 @@ static const ArchDefinition g_coff_arch_def = { "pe-coff", }; +static const ArchDefinitionEntry g_xcoff_arch_entries[] = { + {ArchSpec::eCore_ppc_generic, llvm::XCOFF::TCPU_COM, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, + {ArchSpec::eCore_ppc64_generic, llvm::XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu} +}; + +static const ArchDefinition g_xcoff_arch_def = { + eArchTypeXCOFF, + std::size(g_xcoff_arch_entries), + g_xcoff_arch_entries, + "xcoff", +}; + //===----------------------------------------------------------------------===// // Table of all ArchDefinitions static const ArchDefinition *g_arch_definitions[] = { - &g_macho_arch_def, &g_elf_arch_def, &g_coff_arch_def}; + &g_macho_arch_def, &g_elf_arch_def, &g_coff_arch_def, &g_xcoff_arch_def}; //===----------------------------------------------------------------------===// // Static helper functions. @@ -903,6 +916,9 @@ bool ArchSpec::SetArchitecture(ArchitectureType arch_type, uint32_t cpu, } else if (arch_type == eArchTypeCOFF && os == llvm::Triple::Win32) { m_triple.setVendor(llvm::Triple::PC); m_triple.setOS(llvm::Triple::Win32); + } else if (arch_type == eArchTypeXCOFF && os == llvm::Triple::AIX) { + m_triple.setVendor(llvm::Triple::IBM); + m_triple.setOS(llvm::Triple::AIX); } else { m_triple.setVendor(llvm::Triple::UnknownVendor); m_triple.setOS(llvm::Triple::UnknownOS); diff --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp b/lldb/source/Utility/StringExtractorGDBRemote.cpp index 9f79d2271b1e6..dbd3236536f8c 100644 --- a/lldb/source/Utility/StringExtractorGDBRemote.cpp +++ b/lldb/source/Utility/StringExtractorGDBRemote.cpp @@ -227,6 +227,8 @@ StringExtractorGDBRemote::GetServerPacketType() const { return eServerPacketType_qLaunchGDBServer; if (PACKET_MATCHES("qLaunchSuccess")) return eServerPacketType_qLaunchSuccess; + if (PACKET_MATCHES("qLDXINFO")) + return eServerPacketType_qLDXINFO; break; case 'M': diff --git a/lldb/test/CMakeLists.txt b/lldb/test/CMakeLists.txt index 5ac474736eb63..413a1e5120288 100644 --- a/lldb/test/CMakeLists.txt +++ b/lldb/test/CMakeLists.txt @@ -155,7 +155,7 @@ if(TARGET clang) add_lldb_test_dependency(clang) # TestFullLtoStepping depends on LTO, and only runs when the compiler is clang. - add_lldb_test_dependency(LTO) + #add_lldb_test_dependency(LTO) if (TARGET libcxx OR ("libcxx" IN_LIST LLVM_ENABLE_RUNTIMES)) set(LLDB_HAS_LIBCXX ON) diff --git a/lldb/test/Shell/Expr/TestIRMemoryMap.test b/lldb/test/Shell/Expr/TestIRMemoryMap.test index 9dd0413be14cf..5ed61ad33ffc4 100644 --- a/lldb/test/Shell/Expr/TestIRMemoryMap.test +++ b/lldb/test/Shell/Expr/TestIRMemoryMap.test @@ -1,6 +1,6 @@ # UNSUPPORTED: system-windows -# RUN: %clangxx_host %p/Inputs/call-function.cpp -g -o %t +# RUN: %clangxx_host -std=c++11 %p/Inputs/env.cpp -o %t # RUN: lldb-test ir-memory-map %t %S/Inputs/ir-memory-map-basic # RUN: lldb-test ir-memory-map -host-only %t %S/Inputs/ir-memory-map-basic diff --git a/lldb/test/Shell/Process/TestEnvironment.test b/lldb/test/Shell/Process/TestEnvironment.test index e6d6e56fc9203..2ead258719f32 100644 --- a/lldb/test/Shell/Process/TestEnvironment.test +++ b/lldb/test/Shell/Process/TestEnvironment.test @@ -3,7 +3,7 @@ UNSUPPORTED: lldb-repro The double quotes around "BAR" ensure we don't match the command. -RUN: %clangxx_host -std=c++11 %p/Inputs/env.cpp -o %t +RUN: %clangxx_host -std=c++11 -I/compgpfs/build/xlcit/rings/openxlC/aix/wyvern_dev/ring0/latest/opt/IBM/openxlC/17.1.2/include/c++/v1/ %p/Inputs/env.cpp -o %t RUN: %lldb %t -o 'process launch --environment FOO="BAR"' | FileCheck %s RUN: %lldb %t -o 'env FOO="BAR"' -o 'process launch' | FileCheck %s diff --git a/lldb/tools/driver/CMakeLists.txt b/lldb/tools/driver/CMakeLists.txt index cd304a047dea6..78617be24f780 100644 --- a/lldb/tools/driver/CMakeLists.txt +++ b/lldb/tools/driver/CMakeLists.txt @@ -11,6 +11,11 @@ if(APPLE) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-sectcreate,__TEXT,__info_plist,${CMAKE_CURRENT_BINARY_DIR}/lldb-Info.plist") endif() +if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX") + remove_definitions("-D_XOPEN_SOURCE=700") + add_definitions("-D_ALL_SOURCE") +endif() + add_lldb_tool(lldb Driver.cpp Platform.cpp diff --git a/lldb/tools/driver/Driver.cpp b/lldb/tools/driver/Driver.cpp index 14371da64f2f2..f7eaf56738d7d 100644 --- a/lldb/tools/driver/Driver.cpp +++ b/lldb/tools/driver/Driver.cpp @@ -639,7 +639,7 @@ void sigwinch_handler(int signo) { } void sigint_handler(int signo) { -#ifdef _WIN32 // Restore handler as it is not persistent on Windows +#if defined(_WIN32) || defined(__AIX__) // Restore handler as it is not persistent on Windows signal(SIGINT, sigint_handler); #endif static std::atomic_flag g_interrupt_sent = ATOMIC_FLAG_INIT; @@ -727,8 +727,11 @@ static void printHelp(LLDBOptTable &table, llvm::StringRef tool_name) { int main(int argc, char const *argv[]) { // Editline uses for example iswprint which is dependent on LC_CTYPE. + // FIXME: this caused unexpected SIGTRAP on AIX +#ifndef __AIX__ std::setlocale(LC_ALL, ""); std::setlocale(LC_CTYPE, ""); +#endif // Setup LLVM signal handlers and make sure we call llvm_shutdown() on // destruction. diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt index f8f0d86453f58..2fa6f6c9a5369 100644 --- a/lldb/tools/lldb-dap/CMakeLists.txt +++ b/lldb/tools/lldb-dap/CMakeLists.txt @@ -6,6 +6,10 @@ if (HAVE_LIBPTHREAD) list(APPEND extra_libs pthread) endif () +if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX") + add_definitions("-D_AIX") + add_definitions("-D_ALL_SOURCE") +endif() if(APPLE) configure_file( diff --git a/lldb/tools/lldb-server/CMakeLists.txt b/lldb/tools/lldb-server/CMakeLists.txt index 9030ed709a647..0d69ae32a008f 100644 --- a/lldb/tools/lldb-server/CMakeLists.txt +++ b/lldb/tools/lldb-server/CMakeLists.txt @@ -8,6 +8,10 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux|Android") list(APPEND LLDB_PLUGINS lldbPluginProcessLinux) endif() +if(CMAKE_SYSTEM_NAME MATCHES "AIX") + list(APPEND LLDB_PLUGINS lldbPluginProcessAIX) +endif() + if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") list(APPEND LLDB_PLUGINS lldbPluginProcessFreeBSD) endif() @@ -20,6 +24,8 @@ if(CMAKE_SYSTEM_NAME MATCHES "Darwin") list(APPEND LLDB_PLUGINS lldbPluginObjectFileMachO) elseif(CMAKE_SYSTEM_NAME MATCHES "Windows") list(APPEND LLDB_PLUGINS lldbPluginObjectFilePECOFF) +elseif(CMAKE_SYSTEM_NAME MATCHES "AIX") + list(APPEND LLDB_PLUGINS lldbPluginObjectFileXCOFF) else() list(APPEND LLDB_PLUGINS lldbPluginObjectFileELF) endif() @@ -54,6 +60,7 @@ add_lldb_tool(lldb-server lldbPluginInstructionMIPS lldbPluginInstructionMIPS64 lldbPluginInstructionRISCV + lldbPluginInstructionPPC64 ${LLDB_SYSTEM_LIBS} LINK_COMPONENTS diff --git a/lldb/tools/lldb-server/SystemInitializerLLGS.cpp b/lldb/tools/lldb-server/SystemInitializerLLGS.cpp index 4233252a84dfc..91bb2083a88b5 100644 --- a/lldb/tools/lldb-server/SystemInitializerLLGS.cpp +++ b/lldb/tools/lldb-server/SystemInitializerLLGS.cpp @@ -14,6 +14,9 @@ using HostObjectFile = ObjectFileMachO; #elif defined(_WIN32) #include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h" using HostObjectFile = ObjectFilePECOFF; +#elif defined(__AIX__) +#include "Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.h" +using HostObjectFile = ObjectFileXCOFF; #else #include "Plugins/ObjectFile/ELF/ObjectFileELF.h" using HostObjectFile = ObjectFileELF; @@ -46,6 +49,10 @@ using HostObjectFile = ObjectFileELF; #include "Plugins/Instruction/MIPS/EmulateInstructionMIPS.h" #endif +#if defined(__AIX__) +#include "Plugins/Instruction/PPC64/EmulateInstructionPPC64.h" +#endif + #if defined(__riscv) #define LLDB_TARGET_RISCV #include "Plugins/Instruction/RISCV/EmulateInstructionRISCV.h" @@ -75,6 +82,10 @@ llvm::Error SystemInitializerLLGS::Initialize() { EmulateInstructionRISCV::Initialize(); #endif +#if defined(__AIX__) + EmulateInstructionPPC64::Initialize(); +#endif + return llvm::Error::success(); } @@ -97,5 +108,9 @@ void SystemInitializerLLGS::Terminate() { EmulateInstructionRISCV::Terminate(); #endif +#if defined(__AIX__) + EmulateInstructionPPC64::Terminate(); +#endif + SystemInitializerCommon::Terminate(); } diff --git a/lldb/tools/lldb-server/lldb-gdbserver.cpp b/lldb/tools/lldb-server/lldb-gdbserver.cpp index 563284730bc70..2a14f4f9c82aa 100644 --- a/lldb/tools/lldb-server/lldb-gdbserver.cpp +++ b/lldb/tools/lldb-server/lldb-gdbserver.cpp @@ -45,6 +45,8 @@ #include "Plugins/Process/NetBSD/NativeProcessNetBSD.h" #elif defined(_WIN32) #include "Plugins/Process/Windows/Common/NativeProcessWindows.h" +#elif defined(__AIX__) +#include "Plugins/Process/AIX/NativeProcessAIX.h" #endif #ifndef LLGS_PROGRAM_NAME @@ -70,6 +72,8 @@ typedef process_freebsd::NativeProcessFreeBSD::Manager NativeProcessManager; typedef process_netbsd::NativeProcessNetBSD::Manager NativeProcessManager; #elif defined(_WIN32) typedef NativeProcessWindows::Manager NativeProcessManager; +#elif defined(__AIX__) +typedef process_aix::NativeProcessAIX::Manager NativeProcessManager; #else // Dummy implementation to make sure the code compiles class NativeProcessManager : public NativeProcessProtocol::Manager { diff --git a/lldb/unittests/Host/FileSystemTest.cpp b/lldb/unittests/Host/FileSystemTest.cpp index 58887f6b2467e..89d0f5b87171a 100644 --- a/lldb/unittests/Host/FileSystemTest.cpp +++ b/lldb/unittests/Host/FileSystemTest.cpp @@ -59,7 +59,7 @@ class DummyFileSystem : public vfs::FileSystem { return I->second; } ErrorOr> - openFileForRead(const Twine &Path) override { + openFileForRead(const Twine &Path, bool IsText) override { auto S = status(Path); if (S) return std::unique_ptr(new DummyFile{*S}); diff --git a/lldb/unittests/Host/posix/TerminalTest.cpp b/lldb/unittests/Host/posix/TerminalTest.cpp index 5187a0c20a68b..f3de92c0852b1 100644 --- a/lldb/unittests/Host/posix/TerminalTest.cpp +++ b/lldb/unittests/Host/posix/TerminalTest.cpp @@ -94,15 +94,19 @@ TEST_F(TerminalTest, SetRaw) { TEST_F(TerminalTest, SetBaudRate) { struct termios terminfo; +#if (defined(__AIX__) && defined(B38400)) || !defined(__AIX__) ASSERT_THAT_ERROR(m_term.SetBaudRate(38400), llvm::Succeeded()); ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0); EXPECT_EQ(cfgetispeed(&terminfo), static_cast(B38400)); EXPECT_EQ(cfgetospeed(&terminfo), static_cast(B38400)); +#endif +#if (defined(__AIX__) && defined(B115200)) || !defined(__AIX__) ASSERT_THAT_ERROR(m_term.SetBaudRate(115200), llvm::Succeeded()); ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0); EXPECT_EQ(cfgetispeed(&terminfo), static_cast(B115200)); EXPECT_EQ(cfgetospeed(&terminfo), static_cast(B115200)); +#endif // uncommon value #if defined(B153600) diff --git a/llvm/include/llvm/Object/XCOFFObjectFile.h b/llvm/include/llvm/Object/XCOFFObjectFile.h index 5a7cd8e38f2b7..fa9c6781e24f5 100644 --- a/llvm/include/llvm/Object/XCOFFObjectFile.h +++ b/llvm/include/llvm/Object/XCOFFObjectFile.h @@ -542,7 +542,6 @@ class XCOFFObjectFile : public ObjectFile { template const T *sectionHeaderTable() const; size_t getFileHeaderSize() const; - size_t getSectionHeaderSize() const; const XCOFFSectionHeader32 *toSection32(DataRefImpl Ref) const; const XCOFFSectionHeader64 *toSection64(DataRefImpl Ref) const; @@ -578,6 +577,9 @@ class XCOFFObjectFile : public ObjectFile { void checkSectionAddress(uintptr_t Addr, uintptr_t TableAddr) const; public: + size_t getSectionHeaderSize() const; + Expected getLoaderSectionAddress() const; + static constexpr uint64_t InvalidRelocOffset = std::numeric_limits::max(); diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp index bdd04b00f557b..9c96df1bbdc54 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp @@ -251,10 +251,16 @@ Expected DWARFUnit::getStringOffsetSectionItem(uint32_t Index) const { return DA.getRelocatedValue(ItemSize, &Offset); } +bool UGLY_FLAG_FOR_AIX __attribute__((weak)) = false; + Error DWARFUnitHeader::extract(DWARFContext &Context, const DWARFDataExtractor &debug_info, uint64_t *offset_ptr, DWARFSectionKind SectionKind) { + if (UGLY_FLAG_FOR_AIX) { + // FIXME: hack to get version + *offset_ptr += 8; + } Offset = *offset_ptr; Error Err = Error::success(); IndexEntry = nullptr; @@ -267,8 +273,13 @@ Error DWARFUnitHeader::extract(DWARFContext &Context, AbbrOffset = debug_info.getRelocatedValue( FormParams.getDwarfOffsetByteSize(), offset_ptr, nullptr, &Err); } else { - AbbrOffset = debug_info.getRelocatedValue( - FormParams.getDwarfOffsetByteSize(), offset_ptr, nullptr, &Err); + if (UGLY_FLAG_FOR_AIX) { + AbbrOffset = debug_info.getRelocatedValue( + 8, offset_ptr, nullptr, &Err); + } else { + AbbrOffset = debug_info.getRelocatedValue( + FormParams.getDwarfOffsetByteSize(), offset_ptr, nullptr, &Err); + } FormParams.AddrSize = debug_info.getU8(offset_ptr, &Err); // Fake a unit type based on the section type. This isn't perfect, // but distinguishing compile and type units is generally enough. >From b1da5b1cf35829fcbf4ad6564c6005c755012e47 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Wed, 7 Aug 2024 12:18:45 -0500 Subject: [PATCH 02/46] Code license notice --- lldb/NOTICE.TXT | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 lldb/NOTICE.TXT diff --git a/lldb/NOTICE.TXT b/lldb/NOTICE.TXT new file mode 100644 index 0000000000000..d814272967476 --- /dev/null +++ b/lldb/NOTICE.TXT @@ -0,0 +1,7 @@ + +This product contains small piece of code to support AIX, taken from netbsd. + + * LICENSE: + * lldb/source/Host/common/LICENSE.aix-netbsd.txt (OpenSSL License) + * HOMEPAGE: + * https://ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/crypto/external/bsd/openssl/dist >From 50ad673a78029fd6c47d90317e2c61ca2b59d5c5 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Wed, 7 Aug 2024 13:27:20 -0500 Subject: [PATCH 03/46] Reverting .tests --- lldb/test/Shell/Expr/TestIRMemoryMap.test | 2 +- lldb/test/Shell/Process/TestEnvironment.test | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lldb/test/Shell/Expr/TestIRMemoryMap.test b/lldb/test/Shell/Expr/TestIRMemoryMap.test index 5ed61ad33ffc4..9dd0413be14cf 100644 --- a/lldb/test/Shell/Expr/TestIRMemoryMap.test +++ b/lldb/test/Shell/Expr/TestIRMemoryMap.test @@ -1,6 +1,6 @@ # UNSUPPORTED: system-windows -# RUN: %clangxx_host -std=c++11 %p/Inputs/env.cpp -o %t +# RUN: %clangxx_host %p/Inputs/call-function.cpp -g -o %t # RUN: lldb-test ir-memory-map %t %S/Inputs/ir-memory-map-basic # RUN: lldb-test ir-memory-map -host-only %t %S/Inputs/ir-memory-map-basic diff --git a/lldb/test/Shell/Process/TestEnvironment.test b/lldb/test/Shell/Process/TestEnvironment.test index 2ead258719f32..e6d6e56fc9203 100644 --- a/lldb/test/Shell/Process/TestEnvironment.test +++ b/lldb/test/Shell/Process/TestEnvironment.test @@ -3,7 +3,7 @@ UNSUPPORTED: lldb-repro The double quotes around "BAR" ensure we don't match the command. -RUN: %clangxx_host -std=c++11 -I/compgpfs/build/xlcit/rings/openxlC/aix/wyvern_dev/ring0/latest/opt/IBM/openxlC/17.1.2/include/c++/v1/ %p/Inputs/env.cpp -o %t +RUN: %clangxx_host -std=c++11 %p/Inputs/env.cpp -o %t RUN: %lldb %t -o 'process launch --environment FOO="BAR"' | FileCheck %s RUN: %lldb %t -o 'env FOO="BAR"' -o 'process launch' | FileCheck %s >From c1967be8fe14d469cb5ae9d41d115a7003ff39b6 Mon Sep 17 00:00:00 2001 From: Lakshmi Surekha Kovvuri Date: Thu, 22 Aug 2024 08:49:50 -0500 Subject: [PATCH 04/46] For TestSuite Run --- lldb/unittests/Host/FileSystemTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/unittests/Host/FileSystemTest.cpp b/lldb/unittests/Host/FileSystemTest.cpp index 89d0f5b87171a..58887f6b2467e 100644 --- a/lldb/unittests/Host/FileSystemTest.cpp +++ b/lldb/unittests/Host/FileSystemTest.cpp @@ -59,7 +59,7 @@ class DummyFileSystem : public vfs::FileSystem { return I->second; } ErrorOr> - openFileForRead(const Twine &Path, bool IsText) override { + openFileForRead(const Twine &Path) override { auto S = status(Path); if (S) return std::unique_ptr(new DummyFile{*S}); >From 758ab642d0974e799ac902d8ad240a3a90aeb24d Mon Sep 17 00:00:00 2001 From: Lakshmi Surekha Kovvuri Date: Fri, 30 Aug 2024 08:33:32 -0500 Subject: [PATCH 05/46] Changes made to AIX-specific files to eliminate errors encountered during CI when updating LLDB. --- .../Plugins/Process/AIX/NativeProcessAIX.cpp | 20 ++++++++++--------- .../Process/AIX/NativeRegisterContextAIX.cpp | 4 ++-- .../AIX/NativeRegisterContextAIX_ppc64.cpp | 10 +++++----- .../Plugins/Process/AIX/NativeThreadAIX.cpp | 2 +- .../GDBRemoteCommunicationClient.cpp | 4 ++-- 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp index 882f20d30a3bf..5b01a66b0453f 100644 --- a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp +++ b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp @@ -211,12 +211,14 @@ static Status EnsureFDFlags(int fd, int flags) { int status = fcntl(fd, F_GETFL); if (status == -1) { - error.SetErrorToErrno(); + error = Status::FromErrno(); + // error.SetErrorToErrno(); return error; } if (fcntl(fd, F_SETFL, status | flags) == -1) { - error.SetErrorToErrno(); + error = Status::FromErrno(); + // error.SetErrorToErrno(); return error; } @@ -813,7 +815,7 @@ Status NativeProcessAIX::Resume(const ResumeActionList &resume_actions) { Status error = ResumeThread(static_cast(*thread), action->state, signo); if (error.Fail()) - return Status("NativeProcessAIX::%s: failed to resume thread " + return Status::FromErrorStringWithFormat("NativeProcessAIX::%s: failed to resume thread " "for pid %" PRIu64 ", tid %" PRIu64 ", error = %s", __FUNCTION__, GetID(), thread->GetID(), error.AsCString()); @@ -826,7 +828,7 @@ Status NativeProcessAIX::Resume(const ResumeActionList &resume_actions) { break; default: - return Status("NativeProcessAIX::%s (): unexpected state %s specified " + return Status::FromErrorStringWithFormat("NativeProcessAIX::%s (): unexpected state %s specified " "for pid %" PRIu64 ", tid %" PRIu64, __FUNCTION__, StateAsCString(action->state), GetID(), thread->GetID()); @@ -840,7 +842,7 @@ Status NativeProcessAIX::Halt() { Status error; if (kill(GetID(), SIGSTOP) != 0) - error.SetErrorToErrno(); + error = Status::FromErrno(); return error; } @@ -874,7 +876,7 @@ Status NativeProcessAIX::Signal(int signo) { Host::GetSignalAsCString(signo), GetID()); if (kill(GetID(), signo)) - error.SetErrorToErrno(); + error = Status::FromErrno(); return error; } @@ -951,7 +953,7 @@ Status NativeProcessAIX::Kill() { } if (kill(GetID(), SIGKILL) != 0) { - error.SetErrorToErrno(); + error = Status::FromErrno(); return error; } @@ -1555,7 +1557,7 @@ Status NativeProcessAIX::GetLoadedModuleFileSpec(const char *module_path, return Status(); } } - return Status("Module file (%s) not found in /proc/%" PRIu64 "/maps file!", + return Status::FromErrorStringWithFormat("Module file (%s) not found in /proc/%" PRIu64 "/maps file!", module_file_spec.GetFilename().AsCString(), GetID()); } @@ -2011,7 +2013,7 @@ Status NativeProcessAIX::PtraceWrapper(int req, lldb::pid_t pid, void *addr, } if (errno) { - error.SetErrorToErrno(); + error = Status::FromErrno(); ret = -1; } diff --git a/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.cpp b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.cpp index 0859f9501c1b6..071e55543cc3c 100644 --- a/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.cpp +++ b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.cpp @@ -27,7 +27,7 @@ Status NativeRegisterContextAIX::ReadRegisterRaw(uint32_t reg_index, RegisterValue ®_value) { const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(reg_index); if (!reg_info) - return Status("register %" PRIu32 " not found", reg_index); + return Status::FromErrorStringWithFormat("register %" PRIu32 " not found", reg_index); return DoReadRegisterValue(GetPtraceOffset(reg_index), reg_info->name, reg_info->byte_size, reg_value); @@ -82,7 +82,7 @@ NativeRegisterContextAIX::WriteRegisterRaw(uint32_t reg_index, assert(register_to_write_info_p && "register to write does not have valid RegisterInfo"); if (!register_to_write_info_p) - return Status("NativeRegisterContextAIX::%s failed to get RegisterInfo " + return Status::FromErrorStringWithFormat("NativeRegisterContextAIX::%s failed to get RegisterInfo " "for write register index %" PRIu32, __FUNCTION__, reg_to_write); diff --git a/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.cpp b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.cpp index 1996373791748..0132b52dec6f2 100644 --- a/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.cpp +++ b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.cpp @@ -165,7 +165,7 @@ Status NativeRegisterContextAIX_ppc64::ReadRegister( Status error; if (!reg_info) { - error.SetErrorString("reg_info NULL"); + error.FromErrorString("reg_info NULL"); return error; } @@ -251,7 +251,7 @@ Status NativeRegisterContextAIX_ppc64::WriteRegister( const uint32_t reg_index = reg_info->kinds[lldb::eRegisterKindLLDB]; if (reg_index == LLDB_INVALID_REGNUM) - return Status("no lldb regnum for %s", reg_info && reg_info->name + return Status::FromErrorStringWithFormat("no lldb regnum for %s", reg_info && reg_info->name ? reg_info->name : ""); @@ -389,14 +389,14 @@ Status NativeRegisterContextAIX_ppc64::WriteAllRegisterValues( Status error; if (!data_sp) { - error.SetErrorStringWithFormat( + error = Status::FromErrorStringWithFormat( "NativeRegisterContextAIX_ppc64::%s invalid data_sp provided", __FUNCTION__); return error; } if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) { - error.SetErrorStringWithFormat( + error = Status::FromErrorStringWithFormat( "NativeRegisterContextAIX_ppc64::%s data_sp contained mismatched " "data size, expected %" PRIu64 ", actual %" PRIu64, __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize()); @@ -405,7 +405,7 @@ Status NativeRegisterContextAIX_ppc64::WriteAllRegisterValues( const uint8_t *src = data_sp->GetBytes(); if (src == nullptr) { - error.SetErrorStringWithFormat("NativeRegisterContextAIX_ppc64::%s " + error = Status::FromErrorStringWithFormat("NativeRegisterContextAIX_ppc64::%s " "DataBuffer::GetBytes() returned a null " "pointer", __FUNCTION__); diff --git a/lldb/source/Plugins/Process/AIX/NativeThreadAIX.cpp b/lldb/source/Plugins/Process/AIX/NativeThreadAIX.cpp index e07daccdff550..bb14b6ab4a05e 100644 --- a/lldb/source/Plugins/Process/AIX/NativeThreadAIX.cpp +++ b/lldb/source/Plugins/Process/AIX/NativeThreadAIX.cpp @@ -481,7 +481,7 @@ Status NativeThreadAIX::RequestStop() { Status err; errno = 0; if (::kill(pid, SIGSTOP) != 0) { - err.SetErrorToErrno(); + err = Status::FromErrno(); LLDB_LOGF(log, "NativeThreadAIX::%s kill(%" PRIu64 ", SIGSTOP) failed: %s", __FUNCTION__, pid, err.AsCString()); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index 17926f8e4ab53..0aa68a4a09cbe 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -1728,11 +1728,11 @@ Status GDBRemoteCommunicationClient::GetLDXINFO(struct ld_xinfo *info_ptr) llvm::MutableArrayRef infoData((uint8_t *)info_ptr, sizeof(struct ld_xinfo)*64); size_t got_bytes = response.GetHexBytesAvail(infoData); if (got_bytes != sizeof(struct ld_xinfo)*64) { - error.SetErrorString("qLDXINFO ret bad size"); + error.FromErrorString("qLDXINFO ret bad size"); return error; } } else { - error.SetErrorString("qLDXINFO is not supported"); + error.FromErrorString("qLDXINFO is not supported"); } return error; } >From 33d561f4bb74a2efd0da163ebde416c9ad1c2925 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Tue, 10 Sep 2024 02:00:09 -0500 Subject: [PATCH 06/46] Removed non-required PTRACE defs --- lldb/include/lldb/Host/aix/Ptrace.h | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/lldb/include/lldb/Host/aix/Ptrace.h b/lldb/include/lldb/Host/aix/Ptrace.h index 88928f18102d7..393928a89add3 100644 --- a/lldb/include/lldb/Host/aix/Ptrace.h +++ b/lldb/include/lldb/Host/aix/Ptrace.h @@ -34,29 +34,11 @@ #ifndef PTRACE_SETREGSET #define PTRACE_SETREGSET 0x4205 #endif -#ifndef PTRACE_GET_THREAD_AREA -#define PTRACE_GET_THREAD_AREA (PT_COMMAND_MAX+5) -#endif -#ifndef PTRACE_ARCH_PRCTL -#define PTRACE_ARCH_PRCTL (PT_COMMAND_MAX+6) -#endif -#ifndef ARCH_GET_FS -#define ARCH_SET_GS 0x1001 -#define ARCH_SET_FS 0x1002 -#define ARCH_GET_FS 0x1003 -#define ARCH_GET_GS 0x1004 -#endif -#ifndef PTRACE_PEEKMTETAGS -#define PTRACE_PEEKMTETAGS (PT_COMMAND_MAX+7) -#endif -#ifndef PTRACE_POKEMTETAGS -#define PTRACE_POKEMTETAGS (PT_COMMAND_MAX+8) -#endif #ifndef PTRACE_GETVRREGS -#define PTRACE_GETVRREGS (PT_COMMAND_MAX+9) +#define PTRACE_GETVRREGS (PT_COMMAND_MAX+5) #endif #ifndef PTRACE_GETVSRREGS -#define PTRACE_GETVSRREGS (PT_COMMAND_MAX+10) +#define PTRACE_GETVSRREGS (PT_COMMAND_MAX+6) #endif #endif // liblldb_Host_aix_Ptrace_h_ >From 450793d7270999ecdd6714c4222663517dab3928 Mon Sep 17 00:00:00 2001 From: Lakshmi Surekha Kovvuri Date: Wed, 11 Sep 2024 02:52:41 -0500 Subject: [PATCH 07/46] Patch for running of unit testcases without hang --- lldb/unittests/Host/MainLoopTest.cpp | 4 +++- lldb/unittests/Host/PipeTest.cpp | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lldb/unittests/Host/MainLoopTest.cpp b/lldb/unittests/Host/MainLoopTest.cpp index 4084e90782fd5..9e92ec1470d4d 100644 --- a/lldb/unittests/Host/MainLoopTest.cpp +++ b/lldb/unittests/Host/MainLoopTest.cpp @@ -183,7 +183,7 @@ TEST_F(MainLoopTest, PendingCallbackAfterLoopExited) { loop.AddPendingCallback([&](MainLoopBase &loop) {}); } -#ifdef LLVM_ON_UNIX +#if defined(LLVM_ON_UNIX) && !defined(__AIX__) TEST_F(MainLoopTest, DetectsEOF) { PseudoTerminal term; @@ -202,7 +202,9 @@ TEST_F(MainLoopTest, DetectsEOF) { ASSERT_TRUE(loop.Run().Success()); ASSERT_EQ(1u, callback_count); } +// #endif +// #ifdef LLVM_ON_UNIX TEST_F(MainLoopTest, Signal) { MainLoop loop; Status error; diff --git a/lldb/unittests/Host/PipeTest.cpp b/lldb/unittests/Host/PipeTest.cpp index 506f3d225a21e..c1013aa7a7e4e 100644 --- a/lldb/unittests/Host/PipeTest.cpp +++ b/lldb/unittests/Host/PipeTest.cpp @@ -55,6 +55,7 @@ TEST_F(PipeTest, OpenAsReader) { } #endif +#if !defined(__AIX__) TEST_F(PipeTest, WriteWithTimeout) { Pipe pipe; ASSERT_THAT_ERROR(pipe.CreateNew(false).ToError(), llvm::Succeeded()); @@ -150,3 +151,4 @@ TEST_F(PipeTest, WriteWithTimeout) { .ToError(), llvm::Succeeded()); } +#endif >From 61e7843b431ff3657e3c4b39d1559401ff3de891 Mon Sep 17 00:00:00 2001 From: Lakshmi Surekha Kovvuri Date: Thu, 12 Sep 2024 13:22:03 -0500 Subject: [PATCH 08/46] changes applied to NativeProcessAIX.cpp file to solve build errors while making LLDB up to date --- .../Plugins/Process/AIX/NativeProcessAIX.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp index 5b01a66b0453f..fc84763857453 100644 --- a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp +++ b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp @@ -862,7 +862,7 @@ Status NativeProcessAIX::Detach() { Status e = Detach(thread->GetID()); if (e.Fail()) error = - e; // Save the error, but still attempt to detach from other threads. + e.Clone(); // Save the error, but still attempt to detach from other threads. } return error; @@ -1240,7 +1240,7 @@ Status NativeProcessAIX::ReadMemoryTags(int32_t type, lldb::addr_t addr, llvm::Expected details = GetCurrentThread()->GetRegisterContext().GetMemoryTaggingDetails(type); if (!details) - return Status(details.takeError()); + return Status::FromError(details.takeError()); // Ignore 0 length read if (!len) @@ -1295,7 +1295,7 @@ Status NativeProcessAIX::WriteMemoryTags(int32_t type, lldb::addr_t addr, llvm::Expected details = GetCurrentThread()->GetRegisterContext().GetMemoryTaggingDetails(type); if (!details) - return Status(details.takeError()); + return Status::FromError(details.takeError()); // Ignore 0 length write if (!len) @@ -1312,18 +1312,18 @@ Status NativeProcessAIX::WriteMemoryTags(int32_t type, lldb::addr_t addr, llvm::Expected> unpacked_tags_or_err = details->manager->UnpackTagsData(tags); if (!unpacked_tags_or_err) - return Status(unpacked_tags_or_err.takeError()); + return Status::FromError(unpacked_tags_or_err.takeError()); llvm::Expected> repeated_tags_or_err = details->manager->RepeatTagsForRange(*unpacked_tags_or_err, range); if (!repeated_tags_or_err) - return Status(repeated_tags_or_err.takeError()); + return Status::FromError(repeated_tags_or_err.takeError()); // Repack them for ptrace to use llvm::Expected> final_tag_data = details->manager->PackTags(*repeated_tags_or_err); if (!final_tag_data) - return Status(final_tag_data.takeError()); + return Status::FromError(final_tag_data.takeError()); struct iovec tags_vec; uint8_t *src = final_tag_data->data(); @@ -1609,13 +1609,13 @@ Status NativeProcessAIX::ResumeThread(NativeThreadAIX &thread, // reflect it is running after this completes. switch (state) { case eStateRunning: { - const auto resume_result = thread.Resume(signo); + Status resume_result = thread.Resume(signo); if (resume_result.Success()) SetState(eStateRunning, true); return resume_result; } case eStateStepping: { - const auto step_result = thread.SingleStep(signo); + Status step_result = thread.SingleStep(signo); if (step_result.Success()) SetState(eStateRunning, true); return step_result; >From 627a5427daba3fc5ea03ae481874f4aa1b4d2ed0 Mon Sep 17 00:00:00 2001 From: Lakshmi-Surekha Date: Fri, 13 Sep 2024 16:25:47 +0530 Subject: [PATCH 09/46] Revert "Removed non-required PTRACE defs" --- lldb/include/lldb/Host/aix/Ptrace.h | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/lldb/include/lldb/Host/aix/Ptrace.h b/lldb/include/lldb/Host/aix/Ptrace.h index 393928a89add3..88928f18102d7 100644 --- a/lldb/include/lldb/Host/aix/Ptrace.h +++ b/lldb/include/lldb/Host/aix/Ptrace.h @@ -34,11 +34,29 @@ #ifndef PTRACE_SETREGSET #define PTRACE_SETREGSET 0x4205 #endif +#ifndef PTRACE_GET_THREAD_AREA +#define PTRACE_GET_THREAD_AREA (PT_COMMAND_MAX+5) +#endif +#ifndef PTRACE_ARCH_PRCTL +#define PTRACE_ARCH_PRCTL (PT_COMMAND_MAX+6) +#endif +#ifndef ARCH_GET_FS +#define ARCH_SET_GS 0x1001 +#define ARCH_SET_FS 0x1002 +#define ARCH_GET_FS 0x1003 +#define ARCH_GET_GS 0x1004 +#endif +#ifndef PTRACE_PEEKMTETAGS +#define PTRACE_PEEKMTETAGS (PT_COMMAND_MAX+7) +#endif +#ifndef PTRACE_POKEMTETAGS +#define PTRACE_POKEMTETAGS (PT_COMMAND_MAX+8) +#endif #ifndef PTRACE_GETVRREGS -#define PTRACE_GETVRREGS (PT_COMMAND_MAX+5) +#define PTRACE_GETVRREGS (PT_COMMAND_MAX+9) #endif #ifndef PTRACE_GETVSRREGS -#define PTRACE_GETVSRREGS (PT_COMMAND_MAX+6) +#define PTRACE_GETVSRREGS (PT_COMMAND_MAX+10) #endif #endif // liblldb_Host_aix_Ptrace_h_ >From ea34b15d8568b4639b4e850ef032e684d82dd971 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Thu, 10 Oct 2024 00:38:18 -0500 Subject: [PATCH 10/46] Replaced __AIX__ with _AIX --- clang/test/SemaCXX/class-layout.cpp | 2 +- lldb/CMakeLists.txt | 2 +- lldb/include/lldb/Host/HostGetOpt.h | 2 +- lldb/include/lldb/Host/HostInfo.h | 2 +- lldb/include/lldb/Host/XML.h | 2 +- lldb/include/lldb/Host/common/GetOptInc.h | 4 ++-- lldb/include/lldb/Target/Process.h | 6 +++--- lldb/include/lldb/Target/RegisterContextUnwind.h | 2 +- lldb/source/Core/Mangled.cpp | 2 +- lldb/source/Core/Section.cpp | 2 +- lldb/source/Host/common/Host.cpp | 4 ++-- lldb/source/Host/common/XML.cpp | 2 +- lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp | 2 +- lldb/source/Host/posix/FileSystemPosix.cpp | 2 +- lldb/source/Host/posix/MainLoopPosix.cpp | 4 ++-- lldb/source/Host/posix/ProcessLauncherPosixFork.cpp | 2 +- lldb/source/Initialization/SystemInitializerCommon.cpp | 4 ++-- lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.cpp | 4 ++-- .../DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp | 6 +++--- .../Plugins/Instruction/PPC64/EmulateInstructionPPC64.h | 2 +- lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp | 2 +- lldb/source/Plugins/Language/ObjC/Cocoa.cpp | 2 +- .../BSD-Archive/ObjectContainerBSDArchive.cpp | 2 +- .../Big-Archive/ObjectContainerBigArchive.cpp | 2 +- .../Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp | 2 +- lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp | 6 +++--- .../source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp | 6 +++--- lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp | 6 +++--- lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp | 4 ++-- .../Process/gdb-remote/GDBRemoteCommunicationClient.cpp | 4 ++-- .../Process/gdb-remote/GDBRemoteCommunicationClient.h | 4 ++-- .../gdb-remote/GDBRemoteCommunicationServerLLGS.cpp | 4 ++-- .../Plugins/Process/gdb-remote/ProcessGDBRemote.cpp | 4 ++-- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h | 4 ++-- lldb/source/Target/Process.cpp | 4 ++-- lldb/source/Target/RegisterContextUnwind.cpp | 8 ++++---- lldb/source/Target/UnwindLLDB.cpp | 4 ++-- lldb/tools/driver/Driver.cpp | 4 ++-- lldb/tools/lldb-server/SystemInitializerLLGS.cpp | 8 ++++---- lldb/tools/lldb-server/lldb-gdbserver.cpp | 4 ++-- lldb/unittests/Host/MainLoopTest.cpp | 2 +- lldb/unittests/Host/PipeTest.cpp | 2 +- lldb/unittests/Host/posix/TerminalTest.cpp | 4 ++-- 43 files changed, 75 insertions(+), 75 deletions(-) diff --git a/clang/test/SemaCXX/class-layout.cpp b/clang/test/SemaCXX/class-layout.cpp index 22fb34b8419c5..0931d905a9749 100644 --- a/clang/test/SemaCXX/class-layout.cpp +++ b/clang/test/SemaCXX/class-layout.cpp @@ -639,7 +639,7 @@ namespace PR37275 { #pragma pack(pop) } -#endif // !defined(__MVS__) && !defined(__AIX__) +#endif // !defined(__MVS__) && !defined(_AIX) namespace non_pod { struct t1 { diff --git a/lldb/CMakeLists.txt b/lldb/CMakeLists.txt index 2e9ae0d0b3221..a4fd8bccf056d 100644 --- a/lldb/CMakeLists.txt +++ b/lldb/CMakeLists.txt @@ -39,7 +39,7 @@ include(LLDBConfig) include(AddLLDB) if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX") - add_definitions("-D__AIX__") + add_definitions("-D_AIX") endif() # Define the LLDB_CONFIGURATION_xxx matching the build type. diff --git a/lldb/include/lldb/Host/HostGetOpt.h b/lldb/include/lldb/Host/HostGetOpt.h index f450e561d6afb..b2b436e64a692 100644 --- a/lldb/include/lldb/Host/HostGetOpt.h +++ b/lldb/include/lldb/Host/HostGetOpt.h @@ -9,7 +9,7 @@ #ifndef LLDB_HOST_HOSTGETOPT_H #define LLDB_HOST_HOSTGETOPT_H -#if !defined(_MSC_VER) && !defined(__NetBSD__) && !defined(__AIX__) +#if !defined(_MSC_VER) && !defined(__NetBSD__) && !defined(_AIX) #include #include diff --git a/lldb/include/lldb/Host/HostInfo.h b/lldb/include/lldb/Host/HostInfo.h index 156df8cf6901d..0f7ec0e0aa0d2 100644 --- a/lldb/include/lldb/Host/HostInfo.h +++ b/lldb/include/lldb/Host/HostInfo.h @@ -55,7 +55,7 @@ #elif defined(__APPLE__) #include "lldb/Host/macosx/HostInfoMacOSX.h" #define HOST_INFO_TYPE HostInfoMacOSX -#elif defined(__AIX__) +#elif defined(_AIX) #include "lldb/Host/aix/HostInfoAIX.h" #define HOST_INFO_TYPE HostInfoAIX #else diff --git a/lldb/include/lldb/Host/XML.h b/lldb/include/lldb/Host/XML.h index cf359f7726d5d..483589f1abc75 100644 --- a/lldb/include/lldb/Host/XML.h +++ b/lldb/include/lldb/Host/XML.h @@ -11,7 +11,7 @@ #include "lldb/Host/Config.h" -#if defined(__AIX__) +#if defined(_AIX) //FIXME for AIX #undef LLDB_ENABLE_LIBXML2 #endif diff --git a/lldb/include/lldb/Host/common/GetOptInc.h b/lldb/include/lldb/Host/common/GetOptInc.h index ebb475bfaf6b8..652e6174ff8b6 100644 --- a/lldb/include/lldb/Host/common/GetOptInc.h +++ b/lldb/include/lldb/Host/common/GetOptInc.h @@ -11,11 +11,11 @@ #include "lldb/lldb-defines.h" -#if defined(_MSC_VER) || defined(__AIX__) +#if defined(_MSC_VER) || defined(_AIX) #define REPLACE_GETOPT #define REPLACE_GETOPT_LONG #endif -#if defined(_MSC_VER) || defined(__NetBSD__) || defined(__AIX__) +#if defined(_MSC_VER) || defined(__NetBSD__) || defined(_AIX) #define REPLACE_GETOPT_LONG_ONLY #endif diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index 4a47ffd8d779d..d1527d316d678 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -65,7 +65,7 @@ #include "llvm/Support/Threading.h" #include "llvm/Support/VersionTuple.h" -#if defined(__AIX__) +#if defined(_AIX) struct ld_xinfo; #endif @@ -1884,7 +1884,7 @@ class Process : public std::enable_shared_from_this, Status GetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo &range_info); -#if defined(__AIX__) +#if defined(_AIX) Status GetLDXINFO(struct ld_xinfo *info_ptr); #endif @@ -2823,7 +2823,7 @@ void PruneThreadPlans(); "Process::DoGetMemoryRegionInfo() not supported"); } -#if defined(__AIX__) +#if defined(_AIX) virtual Status DoGetLDXINFO(struct ld_xinfo *info_ptr) { return Status("Process::DoGetLDXINFO() not supported"); } diff --git a/lldb/include/lldb/Target/RegisterContextUnwind.h b/lldb/include/lldb/Target/RegisterContextUnwind.h index 46c06cb422caf..b6176f8e5727f 100644 --- a/lldb/include/lldb/Target/RegisterContextUnwind.h +++ b/lldb/include/lldb/Target/RegisterContextUnwind.h @@ -67,7 +67,7 @@ class RegisterContextUnwind : public lldb_private::RegisterContext { bool ReadPC(lldb::addr_t &start_pc); -#ifdef __AIX__ +#ifdef _AIX bool ReadLR(lldb::addr_t &lr); #endif diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp index 43c5b043ef7a2..8f2e3562f6577 100644 --- a/lldb/source/Core/Mangled.cpp +++ b/lldb/source/Core/Mangled.cpp @@ -167,7 +167,7 @@ static char *GetItaniumDemangledStr(const char *M) { "Expected demangled_size to return length including trailing null"); } -#if !defined(__AIX__) +#if !defined(_AIX) if (Log *log = GetLog(LLDBLog::Demangle)) { if (demangled_cstr) LLDB_LOGF(log, "demangled itanium: %s -> \"%s\"", M, demangled_cstr); diff --git a/lldb/source/Core/Section.cpp b/lldb/source/Core/Section.cpp index 9ed55853930a6..e0a9f7fcc7135 100644 --- a/lldb/source/Core/Section.cpp +++ b/lldb/source/Core/Section.cpp @@ -263,7 +263,7 @@ bool Section::ResolveContainedAddress(addr_t offset, Address &so_addr, bool Section::ContainsFileAddress(addr_t vm_addr) const { const addr_t file_addr = GetFileAddress(); -#ifdef __AIX__ +#ifdef _AIX if (file_addr == 0) return false; #endif diff --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp index 94b1d0fd57d07..dc48cb87b5ce6 100644 --- a/lldb/source/Host/common/Host.cpp +++ b/lldb/source/Host/common/Host.cpp @@ -358,7 +358,7 @@ bool Host::ResolveExecutableInBundle(FileSpec &file) { return false; } #ifndef _WIN32 -#if defined(__AIX__) +#if defined(_AIX) #include extern char **p_xargv; @@ -525,7 +525,7 @@ static int dladdr(const void *ptr, Dl_info *dl) FileSpec Host::GetModuleFileSpecForHostAddress(const void *host_addr) { FileSpec module_filespec; #if !defined(__ANDROID__) -#ifdef __AIX__ +#ifdef _AIX if (host_addr == reinterpret_cast(HostInfoBase::ComputeSharedLibraryDirectory)) { // FIXME: AIX dladdr return "lldb" for this case if (p_xargv[0]) { diff --git a/lldb/source/Host/common/XML.cpp b/lldb/source/Host/common/XML.cpp index 62cac78aaac23..fbc409105fe60 100644 --- a/lldb/source/Host/common/XML.cpp +++ b/lldb/source/Host/common/XML.cpp @@ -10,7 +10,7 @@ #include "lldb/Host/XML.h" #include "llvm/ADT/StringExtras.h" -#if defined(__AIX__) +#if defined(_AIX) #undef LLDB_ENABLE_LIBXML2 #endif diff --git a/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp b/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp index bd204c812b7e3..09c3fd2af6d3e 100644 --- a/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp +++ b/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp @@ -722,7 +722,7 @@ ConnectionFileDescriptor::ConnectFD(llvm::StringRef s, ConnectionStatus ConnectionFileDescriptor::ConnectFile( llvm::StringRef s, socket_id_callback_type socket_id_callback, Status *error_ptr) { -#if !defined(__AIX__) +#if !defined(_AIX) #if LLDB_ENABLE_POSIX std::string addr_str = s.str(); // file:///PATH diff --git a/lldb/source/Host/posix/FileSystemPosix.cpp b/lldb/source/Host/posix/FileSystemPosix.cpp index 866fd8ac96c7b..21da5612ff6b8 100644 --- a/lldb/source/Host/posix/FileSystemPosix.cpp +++ b/lldb/source/Host/posix/FileSystemPosix.cpp @@ -11,7 +11,7 @@ // C includes #include #include -#if !defined(__AIX__) +#if !defined(_AIX) #include #endif #include diff --git a/lldb/source/Host/posix/MainLoopPosix.cpp b/lldb/source/Host/posix/MainLoopPosix.cpp index d1eba52791a78..015570236b9d3 100644 --- a/lldb/source/Host/posix/MainLoopPosix.cpp +++ b/lldb/source/Host/posix/MainLoopPosix.cpp @@ -179,7 +179,7 @@ Status MainLoopPosix::RunImpl::Poll() { read_fds.push_back(pfd); } -#if defined(__AIX__) +#if defined(_AIX) sigset_t origmask; int timeout; @@ -325,7 +325,7 @@ MainLoopPosix::RegisterSignal(int signo, const Callback &callback, // If we're using kqueue, the signal needs to be unblocked in order to // receive it. If using pselect/ppoll, we need to block it, and later unblock // it as a part of the system call. -#if defined(__AIX__) +#if defined(_AIX) //FIXME: where is signal unblocked? ret = pthread_sigmask(SIG_UNBLOCK, &new_action.sa_mask, &old_set); #else diff --git a/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp b/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp index b8a96fbd19f02..f9f99decd39c2 100644 --- a/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp +++ b/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp @@ -193,7 +193,7 @@ struct ForkLaunchInfo { } // Start tracing this child that is about to exec. -#if !defined(__AIX__) +#if !defined(_AIX) if (ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1) ExitWithError(error_fd, "ptrace"); #else diff --git a/lldb/source/Initialization/SystemInitializerCommon.cpp b/lldb/source/Initialization/SystemInitializerCommon.cpp index 4b01442a94bac..2e2d622d9981c 100644 --- a/lldb/source/Initialization/SystemInitializerCommon.cpp +++ b/lldb/source/Initialization/SystemInitializerCommon.cpp @@ -19,7 +19,7 @@ #include "lldb/Version/Version.h" #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ - defined(__OpenBSD__) || defined(__AIX__) + defined(__OpenBSD__) || defined(_AIX) #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #endif @@ -79,7 +79,7 @@ llvm::Error SystemInitializerCommon::Initialize() { process_gdb_remote::ProcessGDBRemoteLog::Initialize(); #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ - defined(__OpenBSD__) || defined(__AIX__) + defined(__OpenBSD__) || defined(_AIX) ProcessPOSIXLog::Initialize(); #endif #if defined(_WIN32) diff --git a/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.cpp b/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.cpp index 88a82f4a0d20c..a3abb15ee625b 100644 --- a/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.cpp +++ b/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.cpp @@ -156,7 +156,7 @@ bool ABISysV_ppc64::PrepareTrivialCall(Thread &thread, addr_t sp, if (!reg_ctx->WriteRegisterFromUnsigned(r12_reg_info, func_addr)) return false; -#if defined(__AIX__) +#if defined(_AIX) assert(0); #else // Read TOC pointer value. @@ -279,7 +279,7 @@ bool ABISysV_ppc64::PrepareTrivialCall(Thread &thread, addr_t sp, if (!reg_ctx->WriteRegisterFromUnsigned(r12_reg_info, func_addr)) return false; -#if defined(__AIX__) +#if defined(_AIX) LLDB_LOGF(log, "Writing R2: 0x%" PRIx64, (uint64_t)toc_addr); if (!reg_ctx->WriteRegisterFromUnsigned(r2_reg_info, toc_addr)) return false; diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp index 62663974134b0..7f3a638d5b028 100644 --- a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp @@ -18,7 +18,7 @@ #include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" -#if defined(__AIX__) +#if defined(_AIX) #include #endif @@ -160,7 +160,7 @@ void DynamicLoaderAIXDYLD::DidAttach() { auto error = m_process->LoadModules(); LLDB_LOG_ERROR(log, std::move(error), "failed to load modules: {0}"); -#if defined(__AIX__) +#if defined(_AIX) // Get struct ld_xinfo (FIXME) struct ld_xinfo ldinfo[64]; Status status = m_process->GetLDXINFO(&(ldinfo[0])); @@ -221,7 +221,7 @@ void DynamicLoaderAIXDYLD::DidLaunch() { LLDB_LOG_ERROR(log, std::move(error), "failed to load modules: {0}"); } -#if defined(__AIX__) +#if defined(_AIX) // Get struct ld_xinfo (FIXME) struct ld_xinfo ldinfo[64]; Status status = m_process->GetLDXINFO(&(ldinfo[0])); diff --git a/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h b/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h index 1576c9700e557..d98b2880ca3b4 100644 --- a/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h +++ b/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h @@ -39,7 +39,7 @@ class EmulateInstructionPPC64 : public EmulateInstruction { return true; case eInstructionTypePCModifying: -#if defined(__AIX__) +#if defined(_AIX) return true; #else return false; diff --git a/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp b/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp index 690fb0d60a09a..9a52fb2f2adc5 100644 --- a/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp +++ b/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp @@ -194,7 +194,7 @@ void JITLoaderGDB::SetJITBreakpoint(lldb_private::ModuleList &module_list) { if (jit_addr == LLDB_INVALID_ADDRESS) return; -#if defined(__AIX__) +#if defined(_AIX) return; #endif diff --git a/lldb/source/Plugins/Language/ObjC/Cocoa.cpp b/lldb/source/Plugins/Language/ObjC/Cocoa.cpp index fb5bc2c58e6fb..71f2b127afb12 100644 --- a/lldb/source/Plugins/Language/ObjC/Cocoa.cpp +++ b/lldb/source/Plugins/Language/ObjC/Cocoa.cpp @@ -1227,7 +1227,7 @@ bool lldb_private::formatters::ObjCSELSummaryProvider( time_t lldb_private::formatters::GetOSXEpoch() { static time_t epoch = 0; if (!epoch) { -#if !defined(__AIX__) +#if !defined(_AIX) #ifndef _WIN32 tzset(); tm tm_epoch; diff --git a/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp index 5ea55772c3aba..4f747ab20c9ef 100644 --- a/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp +++ b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp @@ -8,7 +8,7 @@ #include "ObjectContainerBSDArchive.h" -#if defined(_WIN32) || defined(__ANDROID__) || defined(__AIX__) +#if defined(_WIN32) || defined(__ANDROID__) || defined(_AIX) // Defines from ar, missing on Windows #define SARMAG 8 #define ARFMAG "`\n" diff --git a/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.cpp b/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.cpp index 050ad73f1d19a..38756a0dd2969 100644 --- a/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.cpp +++ b/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.cpp @@ -8,7 +8,7 @@ #include "ObjectContainerBigArchive.h" -#if defined(_WIN32) || defined(__ANDROID__) || defined(__AIX__) +#if defined(_WIN32) || defined(__ANDROID__) || defined(_AIX) // Defines from ar, missing on Windows #define ARMAG "!\n" #define SARMAG 8 diff --git a/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp b/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp index d8834af2c33ef..9aab76c6c48ba 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp +++ b/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp @@ -51,7 +51,7 @@ size_t ObjectFileMinidump::GetModuleSpecifications( const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp, lldb::offset_t data_offset, lldb::offset_t file_offset, lldb::offset_t length, lldb_private::ModuleSpecList &specs) { -#if !defined(__AIX__) +#if !defined(_AIX) specs.Clear(); #endif return 0; diff --git a/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp b/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp index 75cc54e4f0d48..d76d6adb1be2c 100644 --- a/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp +++ b/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp @@ -117,7 +117,7 @@ size_t ObjectFilePDB::GetModuleSpecifications( llvm::BumpPtrAllocator allocator; std::unique_ptr pdb_file = loadPDBFile(file.GetPath(), allocator); if (!pdb_file){ -#if !defined(__AIX__) +#if !defined(_AIX) return initial_count; #else return specs.GetSize() - initial_count; @@ -127,7 +127,7 @@ size_t ObjectFilePDB::GetModuleSpecifications( auto info_stream = pdb_file->getPDBInfoStream(); if (!info_stream) { llvm::consumeError(info_stream.takeError()); -#if !defined(__AIX__) +#if !defined(_AIX) return initial_count; #else return specs.GetSize() - initial_count; @@ -136,7 +136,7 @@ size_t ObjectFilePDB::GetModuleSpecifications( auto dbi_stream = pdb_file->getPDBDbiStream(); if (!dbi_stream) { llvm::consumeError(dbi_stream.takeError()); -#if !defined(__AIX__) +#if !defined(_AIX) return initial_count; #else return specs.GetSize() - initial_count; diff --git a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp index 519ce2ca4a0b2..02a86234bd363 100644 --- a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp @@ -254,7 +254,7 @@ size_t ObjectFilePECOFF::GetModuleSpecifications( lldb::offset_t length, lldb_private::ModuleSpecList &specs) { const size_t initial_count = specs.GetSize(); if (!data_sp || !ObjectFilePECOFF::MagicBytesMatch(data_sp)){ -#if !defined(__AIX__) +#if !defined(_AIX) return initial_count; #else return specs.GetSize() - initial_count; @@ -272,7 +272,7 @@ size_t ObjectFilePECOFF::GetModuleSpecifications( if (!binary) { LLDB_LOG_ERROR(log, binary.takeError(), "Failed to create binary for file ({1}): {0}", file); -#if !defined(__AIX__) +#if !defined(_AIX) return initial_count; #else return specs.GetSize() - initial_count; @@ -281,7 +281,7 @@ size_t ObjectFilePECOFF::GetModuleSpecifications( auto *COFFObj = llvm::dyn_cast(binary->get()); if (!COFFObj){ -#if !defined(__AIX__) +#if !defined(_AIX) return initial_count; #else return specs.GetSize() - initial_count; diff --git a/lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp b/lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp index b6b08b73bec41..5c94477002978 100644 --- a/lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp +++ b/lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp @@ -31,7 +31,7 @@ // Define these constants from AIX mman.h for use when targeting remote aix // systems even when host has different values. -#if defined(__AIX__) +#if defined(_AIX) #include #endif @@ -80,7 +80,7 @@ void PlatformAIX::Initialize() { PlatformPOSIX::Initialize(); if (g_initialize_count++ == 0) { -#if defined(__AIX__) +#if defined(_AIX) PlatformSP default_platform_sp(new PlatformAIX(true)); default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); Platform::SetHostPlatform(default_platform_sp); @@ -294,7 +294,7 @@ MmapArgList PlatformAIX::GetMmapArgumentList(const ArchSpec &arch, addr_t addr, addr_t length, unsigned prot, unsigned flags, addr_t fd, addr_t offset) { -#if defined(__AIX__) +#if defined(_AIX) unsigned flags_platform = MAP_VARIABLE | MAP_PRIVATE | MAP_ANONYMOUS; #else unsigned flags_platform = 0; diff --git a/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp b/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp index db271357d792a..ea758caa653a1 100644 --- a/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp +++ b/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp @@ -46,7 +46,7 @@ bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr, function_options.include_inlines = false; SymbolContextList sc_list; -#if !defined(__AIX__) +#if !defined(_AIX) process->GetTarget().GetImages().FindFunctions( ConstString("mmap"), eFunctionNameTypeFull, function_options, sc_list); #else @@ -122,7 +122,7 @@ bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr, MmapArgList args = process->GetTarget().GetPlatform()->GetMmapArgumentList( arch, addr, length, prot_arg, flags, fd, offset); -#if defined(__AIX__) +#if defined(_AIX) lldb::ThreadPlanSP call_plan_sp( new ThreadPlanCallFunction(*thread, mmap_range.GetBaseAddress(), toc_range.GetBaseAddress(), diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index 443b7c7b2c7fb..fa0a3b5d4dc38 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -41,7 +41,7 @@ #include "llvm/Config/llvm-config.h" // for LLVM_ENABLE_ZLIB #include "llvm/Support/JSON.h" -#if defined(__AIX__) +#if defined(_AIX) #include #endif @@ -1715,7 +1715,7 @@ Status GDBRemoteCommunicationClient::GetMemoryRegionInfo( return error; } -#if defined(__AIX__) +#if defined(_AIX) Status GDBRemoteCommunicationClient::GetLDXINFO(struct ld_xinfo *info_ptr) { Status error; diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h index 520f37ac56716..1812fc9b7ca65 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -32,7 +32,7 @@ #include "llvm/Support/VersionTuple.h" -#if defined(__AIX__) +#if defined(_AIX) struct ld_xinfo; #endif @@ -200,7 +200,7 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase { Status GetMemoryRegionInfo(lldb::addr_t addr, MemoryRegionInfo &range_info); std::optional GetWatchpointSlotCount(); -#if defined(__AIX__) +#if defined(_AIX) Status GetLDXINFO(struct ld_xinfo *info_ptr); #endif diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp index 4f1ef0898ba08..27be61a474238 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -48,7 +48,7 @@ #include "ProcessGDBRemote.h" #include "ProcessGDBRemoteLog.h" #include "lldb/Utility/StringExtractorGDBRemote.h" -#if defined(__AIX__) +#if defined(_AIX) #include #endif @@ -3011,7 +3011,7 @@ GDBRemoteCommunicationServerLLGS::Handle_qLDXINFO(StringExtractorGDBRemote &pack return SendErrorResponse(0xff); } -#if defined(__AIX__) +#if defined(_AIX) // FIXME: buffer size struct ld_xinfo info[64]; if (ptrace64(PT_LDXINFO, m_current_process->GetID(), (long long)&(info[0]), sizeof(info), nullptr) != 0) { diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index ca381290d0e9f..5b7ce5f1424d9 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -92,7 +92,7 @@ #include "llvm/Support/Threading.h" #include "llvm/Support/raw_ostream.h" -#if defined(__AIX__) +#if defined(_AIX) #include #endif @@ -2963,7 +2963,7 @@ Status ProcessGDBRemote::DoGetMemoryRegionInfo(addr_t load_addr, return error; } -#if defined(__AIX__) +#if defined(_AIX) Status ProcessGDBRemote::DoGetLDXINFO(struct ld_xinfo *info_ptr) { Status error(m_gdb_comm.GetLDXINFO(info_ptr)); return error; diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index 82200fbea21cd..2bf3a04d213d4 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -37,7 +37,7 @@ #include "GDBRemoteCommunicationClient.h" #include "GDBRemoteRegisterContext.h" -#if defined(__AIX__) +#if defined(_AIX) struct ld_xinfo; #endif @@ -427,7 +427,7 @@ class ProcessGDBRemote : public Process, Status DoGetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo ®ion_info) override; -#if defined(__AIX__) +#if defined(_AIX) Status DoGetLDXINFO(struct ld_xinfo *info_ptr) override; #endif diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index a9aef7ef21855..e6ae7fc559ef4 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -75,7 +75,7 @@ #include "lldb/Utility/State.h" #include "lldb/Utility/Timer.h" -#if defined(__AIX__) +#if defined(_AIX) #include #endif @@ -6188,7 +6188,7 @@ Status Process::GetMemoryRegionInfo(lldb::addr_t load_addr, return DoGetMemoryRegionInfo(load_addr, range_info); } -#if defined(__AIX__) +#if defined(_AIX) Status Process::GetLDXINFO(struct ld_xinfo *info_ptr) { return DoGetLDXINFO(info_ptr); } diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index fbdbc8c63a5d0..fdf269a3d3653 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -40,7 +40,7 @@ #include #include -#ifdef __AIX__ +#ifdef _AIX #include "Plugins/Process/Utility/lldb-ppc64le-register-enums.h" #endif @@ -1260,7 +1260,7 @@ bool RegisterContextUnwind::IsTrapHandlerSymbol( // Answer the question: Where did THIS frame save the CALLER frame ("previous" // frame)'s register value? -#ifdef __AIX__ +#ifdef _AIX extern bool UGLY_HACK_NULL_TOPFRAME; #endif @@ -1525,7 +1525,7 @@ RegisterContextUnwind::SavedLocationForRegister( new_regloc.type = UnwindLLDB::ConcreteRegisterLocation::eRegisterInLiveRegisterContext; new_regloc.location.register_number = regnum.GetAsKind(eRegisterKindLLDB); -#ifdef __AIX__ +#ifdef _AIX if (UGLY_HACK_NULL_TOPFRAME && new_regloc.location.register_number == 0x20) { new_regloc.location.register_number = 0x24; } @@ -2390,7 +2390,7 @@ bool RegisterContextUnwind::ReadPC(addr_t &pc) { } } -#ifdef __AIX__ +#ifdef _AIX bool RegisterContextUnwind::ReadLR(addr_t &lr) { if (!IsValid()) return false; diff --git a/lldb/source/Target/UnwindLLDB.cpp b/lldb/source/Target/UnwindLLDB.cpp index 8edf359cac497..764bea5bf86c6 100644 --- a/lldb/source/Target/UnwindLLDB.cpp +++ b/lldb/source/Target/UnwindLLDB.cpp @@ -68,7 +68,7 @@ uint32_t UnwindLLDB::DoGetFrameCount() { return m_frames.size(); } -#ifdef __AIX__ +#ifdef _AIX bool UGLY_HACK_NULL_TOPFRAME = false; #endif @@ -95,7 +95,7 @@ bool UnwindLLDB::AddFirstFrame() { if (!reg_ctx_sp->ReadPC(first_cursor_sp->start_pc)) goto unwind_done; -#ifdef __AIX__ +#ifdef _AIX lldb::addr_t lr; if (!reg_ctx_sp->ReadLR(lr)) goto unwind_done; diff --git a/lldb/tools/driver/Driver.cpp b/lldb/tools/driver/Driver.cpp index 45837503e8b73..d17ed77485d31 100644 --- a/lldb/tools/driver/Driver.cpp +++ b/lldb/tools/driver/Driver.cpp @@ -640,7 +640,7 @@ void sigwinch_handler(int signo) { } void sigint_handler(int signo) { -#if defined(_WIN32) || defined(__AIX__) // Restore handler as it is not persistent on Windows +#if defined(_WIN32) || defined(_AIX) // Restore handler as it is not persistent on Windows signal(SIGINT, sigint_handler); #endif static std::atomic_flag g_interrupt_sent = ATOMIC_FLAG_INIT; @@ -729,7 +729,7 @@ static void printHelp(LLDBOptTable &table, llvm::StringRef tool_name) { int main(int argc, char const *argv[]) { // Editline uses for example iswprint which is dependent on LC_CTYPE. // FIXME: this caused unexpected SIGTRAP on AIX -#ifndef __AIX__ +#ifndef _AIX std::setlocale(LC_ALL, ""); std::setlocale(LC_CTYPE, ""); #endif diff --git a/lldb/tools/lldb-server/SystemInitializerLLGS.cpp b/lldb/tools/lldb-server/SystemInitializerLLGS.cpp index 91bb2083a88b5..52c2eae0c9033 100644 --- a/lldb/tools/lldb-server/SystemInitializerLLGS.cpp +++ b/lldb/tools/lldb-server/SystemInitializerLLGS.cpp @@ -14,7 +14,7 @@ using HostObjectFile = ObjectFileMachO; #elif defined(_WIN32) #include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h" using HostObjectFile = ObjectFilePECOFF; -#elif defined(__AIX__) +#elif defined(_AIX) #include "Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.h" using HostObjectFile = ObjectFileXCOFF; #else @@ -49,7 +49,7 @@ using HostObjectFile = ObjectFileELF; #include "Plugins/Instruction/MIPS/EmulateInstructionMIPS.h" #endif -#if defined(__AIX__) +#if defined(_AIX) #include "Plugins/Instruction/PPC64/EmulateInstructionPPC64.h" #endif @@ -82,7 +82,7 @@ llvm::Error SystemInitializerLLGS::Initialize() { EmulateInstructionRISCV::Initialize(); #endif -#if defined(__AIX__) +#if defined(_AIX) EmulateInstructionPPC64::Initialize(); #endif @@ -108,7 +108,7 @@ void SystemInitializerLLGS::Terminate() { EmulateInstructionRISCV::Terminate(); #endif -#if defined(__AIX__) +#if defined(_AIX) EmulateInstructionPPC64::Terminate(); #endif diff --git a/lldb/tools/lldb-server/lldb-gdbserver.cpp b/lldb/tools/lldb-server/lldb-gdbserver.cpp index 844a6370bdb2e..dcbb421a73e25 100644 --- a/lldb/tools/lldb-server/lldb-gdbserver.cpp +++ b/lldb/tools/lldb-server/lldb-gdbserver.cpp @@ -45,7 +45,7 @@ #include "Plugins/Process/NetBSD/NativeProcessNetBSD.h" #elif defined(_WIN32) #include "Plugins/Process/Windows/Common/NativeProcessWindows.h" -#elif defined(__AIX__) +#elif defined(_AIX) #include "Plugins/Process/AIX/NativeProcessAIX.h" #endif @@ -72,7 +72,7 @@ typedef process_freebsd::NativeProcessFreeBSD::Manager NativeProcessManager; typedef process_netbsd::NativeProcessNetBSD::Manager NativeProcessManager; #elif defined(_WIN32) typedef NativeProcessWindows::Manager NativeProcessManager; -#elif defined(__AIX__) +#elif defined(_AIX) typedef process_aix::NativeProcessAIX::Manager NativeProcessManager; #else // Dummy implementation to make sure the code compiles diff --git a/lldb/unittests/Host/MainLoopTest.cpp b/lldb/unittests/Host/MainLoopTest.cpp index c76476c947054..5c042261b9ef2 100644 --- a/lldb/unittests/Host/MainLoopTest.cpp +++ b/lldb/unittests/Host/MainLoopTest.cpp @@ -223,7 +223,7 @@ TEST_F(MainLoopTest, PendingCallbackAfterLoopExited) { loop.AddPendingCallback([&](MainLoopBase &loop) {}); } -#if defined(LLVM_ON_UNIX) && !defined(__AIX__) +#if defined(LLVM_ON_UNIX) && !defined(_AIX) TEST_F(MainLoopTest, DetectsEOF) { PseudoTerminal term; diff --git a/lldb/unittests/Host/PipeTest.cpp b/lldb/unittests/Host/PipeTest.cpp index c1013aa7a7e4e..00ffd33d68f7a 100644 --- a/lldb/unittests/Host/PipeTest.cpp +++ b/lldb/unittests/Host/PipeTest.cpp @@ -55,7 +55,7 @@ TEST_F(PipeTest, OpenAsReader) { } #endif -#if !defined(__AIX__) +#if !defined(_AIX) TEST_F(PipeTest, WriteWithTimeout) { Pipe pipe; ASSERT_THAT_ERROR(pipe.CreateNew(false).ToError(), llvm::Succeeded()); diff --git a/lldb/unittests/Host/posix/TerminalTest.cpp b/lldb/unittests/Host/posix/TerminalTest.cpp index f3de92c0852b1..64e6be64db80c 100644 --- a/lldb/unittests/Host/posix/TerminalTest.cpp +++ b/lldb/unittests/Host/posix/TerminalTest.cpp @@ -94,14 +94,14 @@ TEST_F(TerminalTest, SetRaw) { TEST_F(TerminalTest, SetBaudRate) { struct termios terminfo; -#if (defined(__AIX__) && defined(B38400)) || !defined(__AIX__) +#if (defined(_AIX) && defined(B38400)) || !defined(_AIX) ASSERT_THAT_ERROR(m_term.SetBaudRate(38400), llvm::Succeeded()); ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0); EXPECT_EQ(cfgetispeed(&terminfo), static_cast(B38400)); EXPECT_EQ(cfgetospeed(&terminfo), static_cast(B38400)); #endif -#if (defined(__AIX__) && defined(B115200)) || !defined(__AIX__) +#if (defined(_AIX) && defined(B115200)) || !defined(_AIX) ASSERT_THAT_ERROR(m_term.SetBaudRate(115200), llvm::Succeeded()); ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0); EXPECT_EQ(cfgetispeed(&terminfo), static_cast(B115200)); >From a8020a6a8692f059679195ae1a0ef5e0eeee94c8 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Thu, 10 Oct 2024 04:52:08 -0500 Subject: [PATCH 11/46] Removed from lldb/CMakeLists --- lldb/CMakeLists.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lldb/CMakeLists.txt b/lldb/CMakeLists.txt index a4fd8bccf056d..59cdc4593463c 100644 --- a/lldb/CMakeLists.txt +++ b/lldb/CMakeLists.txt @@ -38,10 +38,6 @@ endif() include(LLDBConfig) include(AddLLDB) -if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX") - add_definitions("-D_AIX") -endif() - # Define the LLDB_CONFIGURATION_xxx matching the build type. if(uppercase_CMAKE_BUILD_TYPE STREQUAL "DEBUG" ) add_definitions(-DLLDB_CONFIGURATION_DEBUG) >From 7609ad339bfab48412221be54edc2d2d146279c3 Mon Sep 17 00:00:00 2001 From: Lakshmi-Surekha Date: Thu, 14 Nov 2024 13:23:59 -0600 Subject: [PATCH 12/46] Patch for the Merge conflict of xcoff first merge with llvm --- .../ObjectFile/XCOFF/ObjectFileXCOFF.cpp | 253 +++++++++++++++++- 1 file changed, 252 insertions(+), 1 deletion(-) diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp index 395a126a01fce..a4d9ea295b4c3 100644 --- a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp @@ -188,7 +188,258 @@ bool ObjectFileXCOFF::ParseHeader() { if (module_sp) { std::lock_guard guard(module_sp->GetMutex()); m_sect_headers.clear(); - lldb::offs + lldb::offset_t offset = 0; + + if (ParseXCOFFHeader(m_data, &offset, m_xcoff_header)) { + m_data.SetAddressByteSize(GetAddressByteSize()); + if (m_xcoff_header.auxhdrsize > 0) + ParseXCOFFOptionalHeader(m_data, &offset); + ParseSectionHeaders(offset); + } + return true; + } + + return false; +} + +bool ObjectFileXCOFF::ParseXCOFFHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset_ptr, + xcoff_header_t &xcoff_header) { + //FIXME: data.ValidOffsetForDataOfSize + xcoff_header.magic = data.GetU16(offset_ptr); + xcoff_header.nsects = data.GetU16(offset_ptr); + xcoff_header.modtime = data.GetU32(offset_ptr); + xcoff_header.symoff = data.GetU64(offset_ptr); + xcoff_header.auxhdrsize = data.GetU16(offset_ptr); + xcoff_header.flags = data.GetU16(offset_ptr); + xcoff_header.nsyms = data.GetU32(offset_ptr); + return true; +} + +bool ObjectFileXCOFF::ParseXCOFFOptionalHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset_ptr) { + lldb::offset_t init_offset = *offset_ptr; + //FIXME: data.ValidOffsetForDataOfSize + m_xcoff_aux_header.AuxMagic = data.GetU16(offset_ptr); + m_xcoff_aux_header.Version = data.GetU16(offset_ptr); + m_xcoff_aux_header.ReservedForDebugger = data.GetU32(offset_ptr); + m_xcoff_aux_header.TextStartAddr = data.GetU64(offset_ptr); + m_xcoff_aux_header.DataStartAddr = data.GetU64(offset_ptr); + m_xcoff_aux_header.TOCAnchorAddr = data.GetU64(offset_ptr); + m_xcoff_aux_header.SecNumOfEntryPoint = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfText = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfData = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfTOC = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfLoader = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfBSS = data.GetU16(offset_ptr); + m_xcoff_aux_header.MaxAlignOfText = data.GetU16(offset_ptr); + m_xcoff_aux_header.MaxAlignOfData = data.GetU16(offset_ptr); + m_xcoff_aux_header.ModuleType = data.GetU16(offset_ptr); + m_xcoff_aux_header.CpuFlag = data.GetU8(offset_ptr); + m_xcoff_aux_header.CpuType = data.GetU8(offset_ptr); + m_xcoff_aux_header.TextPageSize = data.GetU8(offset_ptr); + m_xcoff_aux_header.DataPageSize = data.GetU8(offset_ptr); + m_xcoff_aux_header.StackPageSize = data.GetU8(offset_ptr); + m_xcoff_aux_header.FlagAndTDataAlignment = data.GetU8(offset_ptr); + m_xcoff_aux_header.TextSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.InitDataSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.BssDataSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.EntryPointAddr = data.GetU64(offset_ptr); + m_xcoff_aux_header.MaxStackSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.MaxDataSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.SecNumOfTData = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfTBSS = data.GetU16(offset_ptr); + m_xcoff_aux_header.XCOFF64Flag = data.GetU16(offset_ptr); + lldb::offset_t last_offset = *offset_ptr; + if ((last_offset - init_offset) < m_xcoff_header.auxhdrsize) + *offset_ptr += (m_xcoff_header.auxhdrsize - (last_offset - init_offset)); + return true; +} + +bool ObjectFileXCOFF::ParseSectionHeaders( + uint32_t section_header_data_offset) { + const uint32_t nsects = m_xcoff_header.nsects; + m_sect_headers.clear(); + + if (nsects > 0) { + const size_t section_header_byte_size = nsects * m_binary->getSectionHeaderSize(); + lldb_private::DataExtractor section_header_data = + ReadImageData(section_header_data_offset, section_header_byte_size); + + lldb::offset_t offset = 0; + //FIXME: section_header_data.ValidOffsetForDataOfSize + m_sect_headers.resize(nsects); + + for (uint32_t idx = 0; idx < nsects; ++idx) { + const void *name_data = section_header_data.GetData(&offset, 8); + if (name_data) { + memcpy(m_sect_headers[idx].name, name_data, 8); + m_sect_headers[idx].phyaddr = section_header_data.GetU64(&offset); + m_sect_headers[idx].vmaddr = section_header_data.GetU64(&offset); + m_sect_headers[idx].size = section_header_data.GetU64(&offset); + m_sect_headers[idx].offset = section_header_data.GetU64(&offset); + m_sect_headers[idx].reloff = section_header_data.GetU64(&offset); + m_sect_headers[idx].lineoff = section_header_data.GetU64(&offset); + m_sect_headers[idx].nreloc = section_header_data.GetU32(&offset); + m_sect_headers[idx].nline = section_header_data.GetU32(&offset); + m_sect_headers[idx].flags = section_header_data.GetU32(&offset); + offset += 4; + } else { + offset += (m_binary->getSectionHeaderSize() - 8); + } + } + } + + return !m_sect_headers.empty(); +} + +lldb_private::DataExtractor ObjectFileXCOFF::ReadImageData(uint32_t offset, size_t size) { + if (!size) + return {}; + + if (m_data.ValidOffsetForDataOfSize(offset, size)) + return lldb_private::DataExtractor(m_data, offset, size); + + assert(0); + ProcessSP process_sp(m_process_wp.lock()); + lldb_private::DataExtractor data; + if (process_sp) { + auto data_up = std::make_unique(size, 0); + Status readmem_error; + size_t bytes_read = + process_sp->ReadMemory(offset, data_up->GetBytes(), + data_up->GetByteSize(), readmem_error); + if (bytes_read == size) { + DataBufferSP buffer_sp(data_up.release()); + data.SetData(buffer_sp, 0, buffer_sp->GetByteSize()); + } + } + return data; +} + +bool ObjectFileXCOFF::SetLoadAddress(Target &target, lldb::addr_t value, + bool value_is_offset) { + bool changed = false; + ModuleSP module_sp = GetModule(); + if (module_sp) { + size_t num_loaded_sections = 0; + SectionList *section_list = GetSectionList(); + if (section_list) { + const size_t num_sections = section_list->GetSize(); + size_t sect_idx = 0; + + for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) { + // Iterate through the object file sections to find all of the sections + // that have SHF_ALLOC in their flag bits. + SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); + if (section_sp && !section_sp->IsThreadSpecific()) { + bool use_offset = false; + if (strcmp(section_sp->GetName().AsCString(), ".text") == 0 || + strcmp(section_sp->GetName().AsCString(), ".data") == 0 || + strcmp(section_sp->GetName().AsCString(), ".bss") == 0) + use_offset = true; + + if (target.GetSectionLoadList().SetSectionLoadAddress( + section_sp, (use_offset ? + (section_sp->GetFileOffset() + value) : (section_sp->GetFileAddress() + value)))) + ++num_loaded_sections; + } + } + changed = num_loaded_sections > 0; + } + } + return changed; +} + +bool ObjectFileXCOFF::SetLoadAddressByType(Target &target, lldb::addr_t value, + bool value_is_offset, int type_id) { + bool changed = false; + ModuleSP module_sp = GetModule(); + if (module_sp) { + size_t num_loaded_sections = 0; + SectionList *section_list = GetSectionList(); + if (section_list) { + const size_t num_sections = section_list->GetSize(); + size_t sect_idx = 0; + + for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) { + // Iterate through the object file sections to find all of the sections + // that have SHF_ALLOC in their flag bits. + SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); + if (type_id == 1 && section_sp && strcmp(section_sp->GetName().AsCString(), ".text") == 0) { + if (!section_sp->IsThreadSpecific()) { + if (target.GetSectionLoadList().SetSectionLoadAddress( + section_sp, section_sp->GetFileOffset() + value)) + ++num_loaded_sections; + } + } else if (type_id == 2 && section_sp && strcmp(section_sp->GetName().AsCString(), ".data") == 0) { + if (!section_sp->IsThreadSpecific()) { + if (target.GetSectionLoadList().SetSectionLoadAddress( + section_sp, section_sp->GetFileAddress() + value)) + ++num_loaded_sections; + } + } + } + changed = num_loaded_sections > 0; + } + } + return changed; +} + +ByteOrder ObjectFileXCOFF::GetByteOrder() const { + return eByteOrderBig; +} + +bool ObjectFileXCOFF::IsExecutable() const { + return true; +} + +uint32_t ObjectFileXCOFF::GetAddressByteSize() const { + if (m_xcoff_header.magic == XCOFF::XCOFF64) + return 8; + else if (m_xcoff_header.magic == XCOFF::XCOFF32) + return 4; + return 4; +} + +AddressClass ObjectFileXCOFF::GetAddressClass(addr_t file_addr) { + return AddressClass::eUnknown; +} + +lldb::SymbolType ObjectFileXCOFF::MapSymbolType(llvm::object::SymbolRef::Type sym_type) { + if (sym_type == llvm::object::SymbolRef::ST_Function) + return lldb::eSymbolTypeCode; + else if (sym_type == llvm::object::SymbolRef::ST_Data) + return lldb::eSymbolTypeData; + return lldb::eSymbolTypeInvalid; +} + +void ObjectFileXCOFF::ParseSymtab(Symtab &lldb_symtab) { + SectionList *sect_list = GetSectionList(); + const uint32_t num_syms = m_xcoff_header.nsyms; + uint32_t sidx = 0; + if (num_syms > 0 && m_xcoff_header.symoff > 0) { + const uint32_t symbol_size = XCOFF::SymbolTableEntrySize; + const size_t symbol_data_size = num_syms * symbol_size; + lldb_private::DataExtractor symtab_data = + ReadImageData(m_xcoff_header.symoff, symbol_data_size); + + lldb::offset_t offset = 0; + std::string symbol_name; + Symbol *symbols = lldb_symtab.Resize(num_syms); + llvm::object::symbol_iterator SI = m_binary->symbol_begin(); + for (uint32_t i = 0; i < num_syms; ++i, ++SI) { + xcoff_symbol_t symbol; + const uint32_t symbol_offset = offset; + symbol.value = symtab_data.GetU64(&offset); + symbol.offset = symtab_data.GetU32(&offset); + Expected symbol_name_or_err = m_binary->getStringTableEntry(symbol.offset); + if (!symbol_name_or_err) { + consumeError(symbol_name_or_err.takeError()); + return; + } + StringRef symbol_name_str = symbol_name_or_err.get(); + symbol_name.assign(symbol_name_str.data()); symbol.sect = symtab_data.GetU16(&offset); symbol.type = symtab_data.GetU16(&offset); symbol.storage = symtab_data.GetU8(&offset); >From dd56fce276b60b40e1997292b3f554a20157661a Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Sun, 17 Nov 2024 00:15:01 -0600 Subject: [PATCH 13/46] Attach fix for AIX --- .../AIX-DYLD/DynamicLoaderAIXDYLD.cpp | 130 ++++++++++++++++++ .../AIX-DYLD/DynamicLoaderAIXDYLD.h | 5 +- 2 files changed, 134 insertions(+), 1 deletion(-) diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp index 7f3a638d5b028..acaa6a72edded 100644 --- a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp @@ -18,8 +18,13 @@ #include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" +#include "llvm/Support/FileSystem.h" #if defined(_AIX) #include +#include +#include +#include +#include #endif /*#include "llvm/ADT/Triple.h" @@ -131,14 +136,139 @@ bool DynamicLoaderAIXDYLD::NotifyBreakpointHit( lldb::user_id_t break_loc_id) { } + +void DynamicLoaderAIXDYLD::ResolveExecutableModule( + lldb::ModuleSP &module_sp) { + Log *log = GetLog(LLDBLog::DynamicLoader); + + if (m_process == nullptr) + return; + + auto &target = m_process->GetTarget(); + const auto platform_sp = target.GetPlatform(); + + ProcessInstanceInfo process_info; + if (!m_process->GetProcessInfo(process_info)) { + LLDB_LOGF(log, + "DynamicLoaderPOSIXDYLD::%s - failed to get process info for " + "pid %" PRIu64, + __FUNCTION__, m_process->GetID()); + return; + } + + char procinfo_path[64], exe_path[PATH_MAX], arg_buffer[8192]; + struct procsinfo64 procs_info; + int32long64_t pid = m_process->GetID(); + std::string proc_file = "/proc/" + std::to_string(pid) + "/psinfo"; + std::string cwd_link = "/proc/" + std::to_string(pid) + "/cwd"; + psinfo_t psinfo; + std::ifstream file(proc_file, std::ios::binary); + if(!file.is_open()) + { + LLDB_LOGF(log, "Error psinfo "); + } + file.read(reinterpret_cast(&psinfo), sizeof(psinfo_t)); + if(!file) + LLDB_LOGF(log, "Error psinfo: Failed to read "); + + std::string relative_path(psinfo.pr_fname); + LLDB_LOGF(log, "relative path %s",relative_path.c_str()); + + char cwd[PATH_MAX]; + char resolved_path[PATH_MAX]; + std::string executable_name; + bool found = 0; + if(readlink(cwd_link.c_str(), cwd, sizeof(cwd)) != -1){ + std::filesystem::path full_path = std::filesystem::path(cwd)/relative_path; + if(realpath(full_path.c_str(), resolved_path)) { + LLDB_LOGF(log, " RESOLVED PATH: %s", resolved_path); + found = 1; + } + else + perror("realpath error");} + + executable_name = resolved_path; + if(found == 0) { + std::string command_line(psinfo.pr_psargs); + LLDB_LOGF(log, "command line %s",command_line.c_str()); + if (!command_line.empty()) { + size_t space1 = command_line.find(' '); + executable_name = command_line.substr(0, space1); + LLDB_LOGF(log, "executable name %s",executable_name.c_str()); + } + } + + LLDB_LOGF(log, "executable name %s",executable_name.c_str()); + /*target.SetExecutableModule(target.GetOrCreateModule(lldb_private::FileSpec(resolved_path), + true),true);*/ + process_info.SetExecutableFile(lldb_private::FileSpec(executable_name), + true); + +/* snprintf(procinfo_path, sizeof(procinfo_path), "/proc/%d/object/a.out", pid); + ssize_t len = readlink(procinfo_path, exe_path, sizeof(exe_path) - 1); + exe_path[len] = '\0'; + int num_procs = getprocs64(&procs_info, sizeof(struct procsinfo64), NULL, 0, + &pid, + 1); + int result = getargs(pid, arg_buffer, sizeof(arg_buffer)); + std::vector args; + char *arg_start = arg_buffer; + while(*arg_start != '\0') { + args.emplace_back(arg_start); + arg_start += strlen(arg_start) + 1; + } + + LLDB_LOGF( + log, "1. DynamicLoaderPOSIXDYLD::%s - got executable by pid %" PRIu64 ": %s" + ", pid: %d, current_path: %s", + __FUNCTION__, m_process->GetID(), + args[0], pid, current_path.c_str()); + LLDB_LOGF( + log, "1. DynamicLoaderPOSIXDYLD::%s - got executable by pid %" PRIu64 ": %s" + "num_procs: %d, pid: %d", + __FUNCTION__, m_process->GetID(), + std::string(procs_info.pi_comm).c_str(), num_procs, pid); + if(num_procs <= 0) + perror("getprocs64 failed"); */ + + LLDB_LOGF( + log, "DynamicLoaderPOSIXDYLD::%s - got executable by pid %" PRIu64 ": %s", + __FUNCTION__, m_process->GetID(), + process_info.GetExecutableFile().GetPath().c_str()); + + ModuleSpec module_spec(process_info.GetExecutableFile(), + process_info.GetArchitecture()); + if (module_sp && module_sp->MatchesModuleSpec(module_spec)) + return; + + const auto executable_search_paths(Target::GetDefaultExecutableSearchPaths()); + auto error = platform_sp->ResolveExecutable( + module_spec, module_sp, + !executable_search_paths.IsEmpty() ? &executable_search_paths : nullptr); + if (error.Fail()) { + StreamString stream; + module_spec.Dump(stream); + + LLDB_LOGF(log, + "DynamicLoaderPOSIXDYLD::%s - failed to resolve executable " + "with module spec \"%s\": %s", + __FUNCTION__, stream.GetData(), error.AsCString()); + return; + } + + target.SetExecutableModule(module_sp, eLoadDependentsNo); +} + void DynamicLoaderAIXDYLD::DidAttach() { Log *log = GetLog(LLDBLog::DynamicLoader); LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); ModuleSP executable = GetTargetExecutable(); + ResolveExecutableModule(executable); if (!executable.get()) return; + LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); // Try to fetch the load address of the file from the process, since there // could be randomization of the load address. diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h index ae4b7aca66dcc..0ffbe688e0069 100644 --- a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h @@ -24,7 +24,7 @@ class DynamicLoaderAIXDYLD : public DynamicLoader { static void Initialize(); static void Terminate(); - static llvm::StringRef GetPluginNameStatic() { return "windows-dyld"; } + static llvm::StringRef GetPluginNameStatic() { return "aix-dyld"; } static llvm::StringRef GetPluginDescriptionStatic(); static DynamicLoader *CreateInstance(Process *process, bool force); @@ -46,6 +46,9 @@ class DynamicLoaderAIXDYLD : public DynamicLoader { protected: lldb::addr_t GetLoadAddress(lldb::ModuleSP executable); + /// Loads Module from inferior process. + void ResolveExecutableModule(lldb::ModuleSP &module_sp); + private: std::map m_loaded_modules; }; >From 48b8b1b6532181acab0ee1710d5f4ab92903ae78 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Mon, 18 Nov 2024 02:56:31 -0600 Subject: [PATCH 14/46] Cleanup --- .../AIX-DYLD/DynamicLoaderAIXDYLD.cpp | 65 +++++-------------- 1 file changed, 17 insertions(+), 48 deletions(-) diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp index acaa6a72edded..1a98bb9334043 100644 --- a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp @@ -156,81 +156,50 @@ void DynamicLoaderAIXDYLD::ResolveExecutableModule( return; } - char procinfo_path[64], exe_path[PATH_MAX], arg_buffer[8192]; - struct procsinfo64 procs_info; int32long64_t pid = m_process->GetID(); + char cwd[PATH_MAX], resolved_path[PATH_MAX]; + std::string executable_name; + bool path_resolved = false; + psinfo_t psinfo; + std::string proc_file = "/proc/" + std::to_string(pid) + "/psinfo"; std::string cwd_link = "/proc/" + std::to_string(pid) + "/cwd"; - psinfo_t psinfo; std::ifstream file(proc_file, std::ios::binary); if(!file.is_open()) - { - LLDB_LOGF(log, "Error psinfo "); - } + LLDB_LOGF(log, "Error: Unable to access process info "); + file.read(reinterpret_cast(&psinfo), sizeof(psinfo_t)); if(!file) - LLDB_LOGF(log, "Error psinfo: Failed to read "); + LLDB_LOGF(log, "Process info error: Failed to read "); std::string relative_path(psinfo.pr_fname); - LLDB_LOGF(log, "relative path %s",relative_path.c_str()); + LLDB_LOGF(log, "Relative path %s",relative_path.c_str()); - char cwd[PATH_MAX]; - char resolved_path[PATH_MAX]; - std::string executable_name; - bool found = 0; if(readlink(cwd_link.c_str(), cwd, sizeof(cwd)) != -1){ std::filesystem::path full_path = std::filesystem::path(cwd)/relative_path; if(realpath(full_path.c_str(), resolved_path)) { - LLDB_LOGF(log, " RESOLVED PATH: %s", resolved_path); - found = 1; + LLDB_LOGF(log, "Resolved Path using process info : %s", resolved_path); + path_resolved = true; } else - perror("realpath error");} + LLDB_LOGF(log, "Realpath error: Unable to resolve. "); + } executable_name = resolved_path; - if(found == 0) { + if(path_resolved == false) { std::string command_line(psinfo.pr_psargs); - LLDB_LOGF(log, "command line %s",command_line.c_str()); + LLDB_LOGF(log, "Command line: %s",command_line.c_str()); if (!command_line.empty()) { size_t space1 = command_line.find(' '); executable_name = command_line.substr(0, space1); - LLDB_LOGF(log, "executable name %s",executable_name.c_str()); + LLDB_LOGF(log, "Resolved path using command line arg %s",executable_name.c_str()); } } - LLDB_LOGF(log, "executable name %s",executable_name.c_str()); - /*target.SetExecutableModule(target.GetOrCreateModule(lldb_private::FileSpec(resolved_path), - true),true);*/ + LLDB_LOGF(log, "Executable Name %s",executable_name.c_str()); process_info.SetExecutableFile(lldb_private::FileSpec(executable_name), true); -/* snprintf(procinfo_path, sizeof(procinfo_path), "/proc/%d/object/a.out", pid); - ssize_t len = readlink(procinfo_path, exe_path, sizeof(exe_path) - 1); - exe_path[len] = '\0'; - int num_procs = getprocs64(&procs_info, sizeof(struct procsinfo64), NULL, 0, - &pid, - 1); - int result = getargs(pid, arg_buffer, sizeof(arg_buffer)); - std::vector args; - char *arg_start = arg_buffer; - while(*arg_start != '\0') { - args.emplace_back(arg_start); - arg_start += strlen(arg_start) + 1; - } - - LLDB_LOGF( - log, "1. DynamicLoaderPOSIXDYLD::%s - got executable by pid %" PRIu64 ": %s" - ", pid: %d, current_path: %s", - __FUNCTION__, m_process->GetID(), - args[0], pid, current_path.c_str()); - LLDB_LOGF( - log, "1. DynamicLoaderPOSIXDYLD::%s - got executable by pid %" PRIu64 ": %s" - "num_procs: %d, pid: %d", - __FUNCTION__, m_process->GetID(), - std::string(procs_info.pi_comm).c_str(), num_procs, pid); - if(num_procs <= 0) - perror("getprocs64 failed"); */ - LLDB_LOGF( log, "DynamicLoaderPOSIXDYLD::%s - got executable by pid %" PRIu64 ": %s", __FUNCTION__, m_process->GetID(), >From d410734184a681b3e95949d3953142995682d7f6 Mon Sep 17 00:00:00 2001 From: Lakshmi-Surekha Date: Tue, 19 Nov 2024 09:44:42 -0600 Subject: [PATCH 15/46] Patch in MainLoopPosix.cpp for runtime issue --- lldb/source/Host/posix/MainLoopPosix.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/source/Host/posix/MainLoopPosix.cpp b/lldb/source/Host/posix/MainLoopPosix.cpp index f68268f114075..4c617cdde67ba 100644 --- a/lldb/source/Host/posix/MainLoopPosix.cpp +++ b/lldb/source/Host/posix/MainLoopPosix.cpp @@ -149,7 +149,7 @@ Status MainLoopPosix::RunImpl::Poll() { int timeout; timeout = -1; - pthread_sigmask(SIG_SETMASK, &sigmask, &origmask); + pthread_sigmask(SIG_SETMASK, nullptr, &origmask); int ready = poll(read_fds.data(), read_fds.size(), timeout); pthread_sigmask(SIG_SETMASK, &origmask, nullptr); if (ready == -1 && errno != EINTR) >From 48f39dadbbdb4874fbd9b6350933dc67e8823339 Mon Sep 17 00:00:00 2001 From: Lakshmi-Surekha Date: Thu, 5 Dec 2024 05:13:14 -0600 Subject: [PATCH 16/46] Patch for compilation failure in DomainSocket.cpp, AbstractSocket.cpp and AbstractSocket.h --- lldb/include/lldb/Host/aix/AbstractSocket.h | 2 +- lldb/source/Host/aix/AbstractSocket.cpp | 3 +-- lldb/source/Host/posix/DomainSocket.cpp | 4 ++++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lldb/include/lldb/Host/aix/AbstractSocket.h b/lldb/include/lldb/Host/aix/AbstractSocket.h index 78a567a6b9095..accfd01457a5e 100644 --- a/lldb/include/lldb/Host/aix/AbstractSocket.h +++ b/lldb/include/lldb/Host/aix/AbstractSocket.h @@ -14,7 +14,7 @@ namespace lldb_private { class AbstractSocket : public DomainSocket { public: - AbstractSocket(bool child_processes_inherit); + AbstractSocket(); protected: size_t GetNameOffset() const override; diff --git a/lldb/source/Host/aix/AbstractSocket.cpp b/lldb/source/Host/aix/AbstractSocket.cpp index bfb67d452f7ec..fddf78f54f46d 100644 --- a/lldb/source/Host/aix/AbstractSocket.cpp +++ b/lldb/source/Host/aix/AbstractSocket.cpp @@ -13,8 +13,7 @@ using namespace lldb; using namespace lldb_private; -AbstractSocket::AbstractSocket(bool child_processes_inherit) - : DomainSocket(ProtocolUnixAbstract, child_processes_inherit) {} +AbstractSocket::AbstractSocket() : DomainSocket(ProtocolUnixAbstract) {} size_t AbstractSocket::GetNameOffset() const { return 1; } diff --git a/lldb/source/Host/posix/DomainSocket.cpp b/lldb/source/Host/posix/DomainSocket.cpp index 9a0b385d998bf..6cbffb2d9c4bd 100644 --- a/lldb/source/Host/posix/DomainSocket.cpp +++ b/lldb/source/Host/posix/DomainSocket.cpp @@ -17,6 +17,10 @@ #include #include +#if defined(_AIX) +#include +#endif + using namespace lldb; using namespace lldb_private; >From 97531f7bf6e385f0f51d860c6eea17aeb32f6594 Mon Sep 17 00:00:00 2001 From: Lakshmi-Surekha Date: Thu, 19 Dec 2024 06:38:36 -0600 Subject: [PATCH 17/46] Patch for merge conflict in ObjectFileXCOFF.cpp & ObjectFileXCOFF.h --- .../ObjectFile/XCOFF/ObjectFileXCOFF.cpp | 253 +++++++++++++++++- 1 file changed, 252 insertions(+), 1 deletion(-) diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp index 395a126a01fce..a4d9ea295b4c3 100644 --- a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp @@ -188,7 +188,258 @@ bool ObjectFileXCOFF::ParseHeader() { if (module_sp) { std::lock_guard guard(module_sp->GetMutex()); m_sect_headers.clear(); - lldb::offs + lldb::offset_t offset = 0; + + if (ParseXCOFFHeader(m_data, &offset, m_xcoff_header)) { + m_data.SetAddressByteSize(GetAddressByteSize()); + if (m_xcoff_header.auxhdrsize > 0) + ParseXCOFFOptionalHeader(m_data, &offset); + ParseSectionHeaders(offset); + } + return true; + } + + return false; +} + +bool ObjectFileXCOFF::ParseXCOFFHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset_ptr, + xcoff_header_t &xcoff_header) { + //FIXME: data.ValidOffsetForDataOfSize + xcoff_header.magic = data.GetU16(offset_ptr); + xcoff_header.nsects = data.GetU16(offset_ptr); + xcoff_header.modtime = data.GetU32(offset_ptr); + xcoff_header.symoff = data.GetU64(offset_ptr); + xcoff_header.auxhdrsize = data.GetU16(offset_ptr); + xcoff_header.flags = data.GetU16(offset_ptr); + xcoff_header.nsyms = data.GetU32(offset_ptr); + return true; +} + +bool ObjectFileXCOFF::ParseXCOFFOptionalHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset_ptr) { + lldb::offset_t init_offset = *offset_ptr; + //FIXME: data.ValidOffsetForDataOfSize + m_xcoff_aux_header.AuxMagic = data.GetU16(offset_ptr); + m_xcoff_aux_header.Version = data.GetU16(offset_ptr); + m_xcoff_aux_header.ReservedForDebugger = data.GetU32(offset_ptr); + m_xcoff_aux_header.TextStartAddr = data.GetU64(offset_ptr); + m_xcoff_aux_header.DataStartAddr = data.GetU64(offset_ptr); + m_xcoff_aux_header.TOCAnchorAddr = data.GetU64(offset_ptr); + m_xcoff_aux_header.SecNumOfEntryPoint = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfText = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfData = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfTOC = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfLoader = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfBSS = data.GetU16(offset_ptr); + m_xcoff_aux_header.MaxAlignOfText = data.GetU16(offset_ptr); + m_xcoff_aux_header.MaxAlignOfData = data.GetU16(offset_ptr); + m_xcoff_aux_header.ModuleType = data.GetU16(offset_ptr); + m_xcoff_aux_header.CpuFlag = data.GetU8(offset_ptr); + m_xcoff_aux_header.CpuType = data.GetU8(offset_ptr); + m_xcoff_aux_header.TextPageSize = data.GetU8(offset_ptr); + m_xcoff_aux_header.DataPageSize = data.GetU8(offset_ptr); + m_xcoff_aux_header.StackPageSize = data.GetU8(offset_ptr); + m_xcoff_aux_header.FlagAndTDataAlignment = data.GetU8(offset_ptr); + m_xcoff_aux_header.TextSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.InitDataSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.BssDataSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.EntryPointAddr = data.GetU64(offset_ptr); + m_xcoff_aux_header.MaxStackSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.MaxDataSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.SecNumOfTData = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfTBSS = data.GetU16(offset_ptr); + m_xcoff_aux_header.XCOFF64Flag = data.GetU16(offset_ptr); + lldb::offset_t last_offset = *offset_ptr; + if ((last_offset - init_offset) < m_xcoff_header.auxhdrsize) + *offset_ptr += (m_xcoff_header.auxhdrsize - (last_offset - init_offset)); + return true; +} + +bool ObjectFileXCOFF::ParseSectionHeaders( + uint32_t section_header_data_offset) { + const uint32_t nsects = m_xcoff_header.nsects; + m_sect_headers.clear(); + + if (nsects > 0) { + const size_t section_header_byte_size = nsects * m_binary->getSectionHeaderSize(); + lldb_private::DataExtractor section_header_data = + ReadImageData(section_header_data_offset, section_header_byte_size); + + lldb::offset_t offset = 0; + //FIXME: section_header_data.ValidOffsetForDataOfSize + m_sect_headers.resize(nsects); + + for (uint32_t idx = 0; idx < nsects; ++idx) { + const void *name_data = section_header_data.GetData(&offset, 8); + if (name_data) { + memcpy(m_sect_headers[idx].name, name_data, 8); + m_sect_headers[idx].phyaddr = section_header_data.GetU64(&offset); + m_sect_headers[idx].vmaddr = section_header_data.GetU64(&offset); + m_sect_headers[idx].size = section_header_data.GetU64(&offset); + m_sect_headers[idx].offset = section_header_data.GetU64(&offset); + m_sect_headers[idx].reloff = section_header_data.GetU64(&offset); + m_sect_headers[idx].lineoff = section_header_data.GetU64(&offset); + m_sect_headers[idx].nreloc = section_header_data.GetU32(&offset); + m_sect_headers[idx].nline = section_header_data.GetU32(&offset); + m_sect_headers[idx].flags = section_header_data.GetU32(&offset); + offset += 4; + } else { + offset += (m_binary->getSectionHeaderSize() - 8); + } + } + } + + return !m_sect_headers.empty(); +} + +lldb_private::DataExtractor ObjectFileXCOFF::ReadImageData(uint32_t offset, size_t size) { + if (!size) + return {}; + + if (m_data.ValidOffsetForDataOfSize(offset, size)) + return lldb_private::DataExtractor(m_data, offset, size); + + assert(0); + ProcessSP process_sp(m_process_wp.lock()); + lldb_private::DataExtractor data; + if (process_sp) { + auto data_up = std::make_unique(size, 0); + Status readmem_error; + size_t bytes_read = + process_sp->ReadMemory(offset, data_up->GetBytes(), + data_up->GetByteSize(), readmem_error); + if (bytes_read == size) { + DataBufferSP buffer_sp(data_up.release()); + data.SetData(buffer_sp, 0, buffer_sp->GetByteSize()); + } + } + return data; +} + +bool ObjectFileXCOFF::SetLoadAddress(Target &target, lldb::addr_t value, + bool value_is_offset) { + bool changed = false; + ModuleSP module_sp = GetModule(); + if (module_sp) { + size_t num_loaded_sections = 0; + SectionList *section_list = GetSectionList(); + if (section_list) { + const size_t num_sections = section_list->GetSize(); + size_t sect_idx = 0; + + for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) { + // Iterate through the object file sections to find all of the sections + // that have SHF_ALLOC in their flag bits. + SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); + if (section_sp && !section_sp->IsThreadSpecific()) { + bool use_offset = false; + if (strcmp(section_sp->GetName().AsCString(), ".text") == 0 || + strcmp(section_sp->GetName().AsCString(), ".data") == 0 || + strcmp(section_sp->GetName().AsCString(), ".bss") == 0) + use_offset = true; + + if (target.GetSectionLoadList().SetSectionLoadAddress( + section_sp, (use_offset ? + (section_sp->GetFileOffset() + value) : (section_sp->GetFileAddress() + value)))) + ++num_loaded_sections; + } + } + changed = num_loaded_sections > 0; + } + } + return changed; +} + +bool ObjectFileXCOFF::SetLoadAddressByType(Target &target, lldb::addr_t value, + bool value_is_offset, int type_id) { + bool changed = false; + ModuleSP module_sp = GetModule(); + if (module_sp) { + size_t num_loaded_sections = 0; + SectionList *section_list = GetSectionList(); + if (section_list) { + const size_t num_sections = section_list->GetSize(); + size_t sect_idx = 0; + + for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) { + // Iterate through the object file sections to find all of the sections + // that have SHF_ALLOC in their flag bits. + SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); + if (type_id == 1 && section_sp && strcmp(section_sp->GetName().AsCString(), ".text") == 0) { + if (!section_sp->IsThreadSpecific()) { + if (target.GetSectionLoadList().SetSectionLoadAddress( + section_sp, section_sp->GetFileOffset() + value)) + ++num_loaded_sections; + } + } else if (type_id == 2 && section_sp && strcmp(section_sp->GetName().AsCString(), ".data") == 0) { + if (!section_sp->IsThreadSpecific()) { + if (target.GetSectionLoadList().SetSectionLoadAddress( + section_sp, section_sp->GetFileAddress() + value)) + ++num_loaded_sections; + } + } + } + changed = num_loaded_sections > 0; + } + } + return changed; +} + +ByteOrder ObjectFileXCOFF::GetByteOrder() const { + return eByteOrderBig; +} + +bool ObjectFileXCOFF::IsExecutable() const { + return true; +} + +uint32_t ObjectFileXCOFF::GetAddressByteSize() const { + if (m_xcoff_header.magic == XCOFF::XCOFF64) + return 8; + else if (m_xcoff_header.magic == XCOFF::XCOFF32) + return 4; + return 4; +} + +AddressClass ObjectFileXCOFF::GetAddressClass(addr_t file_addr) { + return AddressClass::eUnknown; +} + +lldb::SymbolType ObjectFileXCOFF::MapSymbolType(llvm::object::SymbolRef::Type sym_type) { + if (sym_type == llvm::object::SymbolRef::ST_Function) + return lldb::eSymbolTypeCode; + else if (sym_type == llvm::object::SymbolRef::ST_Data) + return lldb::eSymbolTypeData; + return lldb::eSymbolTypeInvalid; +} + +void ObjectFileXCOFF::ParseSymtab(Symtab &lldb_symtab) { + SectionList *sect_list = GetSectionList(); + const uint32_t num_syms = m_xcoff_header.nsyms; + uint32_t sidx = 0; + if (num_syms > 0 && m_xcoff_header.symoff > 0) { + const uint32_t symbol_size = XCOFF::SymbolTableEntrySize; + const size_t symbol_data_size = num_syms * symbol_size; + lldb_private::DataExtractor symtab_data = + ReadImageData(m_xcoff_header.symoff, symbol_data_size); + + lldb::offset_t offset = 0; + std::string symbol_name; + Symbol *symbols = lldb_symtab.Resize(num_syms); + llvm::object::symbol_iterator SI = m_binary->symbol_begin(); + for (uint32_t i = 0; i < num_syms; ++i, ++SI) { + xcoff_symbol_t symbol; + const uint32_t symbol_offset = offset; + symbol.value = symtab_data.GetU64(&offset); + symbol.offset = symtab_data.GetU32(&offset); + Expected symbol_name_or_err = m_binary->getStringTableEntry(symbol.offset); + if (!symbol_name_or_err) { + consumeError(symbol_name_or_err.takeError()); + return; + } + StringRef symbol_name_str = symbol_name_or_err.get(); + symbol_name.assign(symbol_name_str.data()); symbol.sect = symtab_data.GetU16(&offset); symbol.type = symtab_data.GetU16(&offset); symbol.storage = symtab_data.GetU8(&offset); >From 71d2fcff8975831e7f0a657481220749b0a473dc Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Tue, 24 Dec 2024 02:05:35 -0600 Subject: [PATCH 18/46] Added upcoming clang-format and other merge changes --- .../posix/ConnectionFileDescriptorPosix.cpp | 9 ++-- lldb/source/Host/posix/DomainSocket.cpp | 5 ++- lldb/source/Host/posix/FileSystemPosix.cpp | 2 +- lldb/source/Host/posix/MainLoopPosix.cpp | 43 ++++++++----------- .../Host/posix/ProcessLauncherPosixFork.cpp | 2 +- lldb/source/Plugins/Language/ObjC/Cocoa.cpp | 17 +++----- .../BSD-Archive/ObjectContainerBSDArchive.cpp | 29 +++++++------ lldb/source/Utility/ArchSpec.cpp | 1 - 8 files changed, 49 insertions(+), 59 deletions(-) diff --git a/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp b/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp index 32d034e60d26c..e3d1300cf76ed 100644 --- a/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp +++ b/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp @@ -119,8 +119,7 @@ bool ConnectionFileDescriptor::IsConnected() const { ConnectionStatus ConnectionFileDescriptor::Connect(llvm::StringRef path, Status *error_ptr) { - return Connect( - path, [](llvm::StringRef) {}, error_ptr); + return Connect(path, [](llvm::StringRef) {}, error_ptr); } ConnectionStatus @@ -716,8 +715,7 @@ ConnectionFileDescriptor::ConnectFD(llvm::StringRef s, ConnectionStatus ConnectionFileDescriptor::ConnectFile( llvm::StringRef s, socket_id_callback_type socket_id_callback, Status *error_ptr) { -#if !defined(_AIX) -#if LLDB_ENABLE_POSIX +#if LLDB_ENABLE_POSIX && !defined(_AIX) std::string addr_str = s.str(); // file:///PATH int fd = FileSystem::Instance().Open(addr_str.c_str(), O_RDWR); @@ -748,8 +746,7 @@ ConnectionStatus ConnectionFileDescriptor::ConnectFile( m_io_sp = std::make_shared(fd, File::eOpenOptionReadWrite, true); return eConnectionStatusSuccess; -#endif // LLDB_ENABLE_POSIX -#endif +#endif // LLDB_ENABLE_POSIX && !defined(_AIX) llvm_unreachable("this function should be only called w/ LLDB_ENABLE_POSIX"); } diff --git a/lldb/source/Host/posix/DomainSocket.cpp b/lldb/source/Host/posix/DomainSocket.cpp index 6cbffb2d9c4bd..28db5964a5a8a 100644 --- a/lldb/source/Host/posix/DomainSocket.cpp +++ b/lldb/source/Host/posix/DomainSocket.cpp @@ -89,8 +89,9 @@ Status DomainSocket::Connect(llvm::StringRef name) { m_socket = CreateSocket(kDomain, kType, 0, error); if (error.Fail()) return error; - if (llvm::sys::RetryAfterSignal(-1, ::connect, GetNativeSocket(), - (struct sockaddr *)&saddr_un, saddr_un_len) < 0) + if (llvm::sys::RetryAfterSignal(-1, ::connect, GetNativeSocket(), + (struct sockaddr *)&saddr_un, + saddr_un_len) < 0) SetLastError(error); return error; diff --git a/lldb/source/Host/posix/FileSystemPosix.cpp b/lldb/source/Host/posix/FileSystemPosix.cpp index 21da5612ff6b8..1a84f550662d7 100644 --- a/lldb/source/Host/posix/FileSystemPosix.cpp +++ b/lldb/source/Host/posix/FileSystemPosix.cpp @@ -11,7 +11,7 @@ // C includes #include #include -#if !defined(_AIX) +#ifndef _AIX #include #endif #include diff --git a/lldb/source/Host/posix/MainLoopPosix.cpp b/lldb/source/Host/posix/MainLoopPosix.cpp index 125b954023dc0..e4ff928a58962 100644 --- a/lldb/source/Host/posix/MainLoopPosix.cpp +++ b/lldb/source/Host/posix/MainLoopPosix.cpp @@ -99,6 +99,7 @@ class MainLoopPosix::RunImpl { ~RunImpl() = default; Status Poll(); + int StartPoll(std::optional point); void ProcessReadEvents(); private: @@ -159,6 +160,22 @@ MainLoopPosix::RunImpl::RunImpl(MainLoopPosix &loop) : loop(loop) { read_fds.reserve(loop.m_read_fds.size()); } +int MainLoopPosix::RunImpl::StartPoll( + std::optional point) { +#if HAVE_PPOLL + return ppoll(read_fds.data(), read_fds.size(), ToTimeSpec(point), + /*sigmask=*/nullptr); +#else + using namespace std::chrono; + int timeout = -1; + if (point) { + nanoseconds dur = std::max(*point - steady_clock::now(), nanoseconds(0)); + timeout = ceil(dur).count(); + } + return poll(read_fds.data(), read_fds.size(), timeout); +#endif +} + Status MainLoopPosix::RunImpl::Poll() { read_fds.clear(); @@ -169,24 +186,10 @@ Status MainLoopPosix::RunImpl::Poll() { pfd.revents = 0; read_fds.push_back(pfd); } + int ready = StartPoll(loop.GetNextWakeupTime()); -#if defined(_AIX) - sigset_t origmask; - int timeout; - - timeout = -1; - pthread_sigmask(SIG_SETMASK, nullptr, &origmask); - int ready = poll(read_fds.data(), read_fds.size(), timeout); - pthread_sigmask(SIG_SETMASK, &origmask, nullptr); if (ready == -1 && errno != EINTR) return Status(errno, eErrorTypePOSIX); -#else - if (ppoll(read_fds.data(), read_fds.size(), - ToTimeSpec(loop.GetNextWakeupTime()), - /*sigmask=*/nullptr) == -1 && - errno != EINTR) - return Status(errno, eErrorTypePOSIX); -#endif return Status(); } @@ -291,16 +294,6 @@ MainLoopPosix::RegisterSignal(int signo, const Callback &callback, UNUSED_IF_ASSERT_DISABLED(ret); assert(ret == 0 && "sigaction failed"); -#if HAVE_SYS_EVENT_H - struct kevent ev; - EV_SET(&ev, signo, EVFILT_SIGNAL, EV_ADD, 0, 0, 0); - ret = kevent(m_kqueue, &ev, 1, nullptr, 0, nullptr); - assert(ret == 0); -#endif - - // If we're using kqueue, the signal needs to be unblocked in order to - // receive it. If using pselect/ppoll, we need to block it, and later unblock - // it as a part of the system call. ret = pthread_sigmask(SIG_UNBLOCK, &new_action.sa_mask, &old_set); assert(ret == 0 && "pthread_sigmask failed"); info.was_blocked = sigismember(&old_set, signo); diff --git a/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp b/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp index 52fc58aa21bf4..7b8b42a4b7fe0 100644 --- a/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp +++ b/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp @@ -197,7 +197,7 @@ struct ForkLaunchInfo { #else if (ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1) #endif - ExitWithError(error_fd, "ptrace"); + ExitWithError(error_fd, "ptrace"); } // Execute. We should never return... diff --git a/lldb/source/Plugins/Language/ObjC/Cocoa.cpp b/lldb/source/Plugins/Language/ObjC/Cocoa.cpp index 1d841a032aa6e..1d79edbede5d6 100644 --- a/lldb/source/Plugins/Language/ObjC/Cocoa.cpp +++ b/lldb/source/Plugins/Language/ObjC/Cocoa.cpp @@ -31,7 +31,6 @@ #include "llvm/ADT/APInt.h" #include "llvm/ADT/bit.h" - using namespace lldb; using namespace lldb_private; using namespace lldb_private::formatters; @@ -267,21 +266,21 @@ bool lldb_private::formatters::NSIndexSetSummaryProvider( if (class_name == "NSIndexSet" || class_name == "NSMutableIndexSet") { // Foundation version 2000 added a bitmask if the index set fit in 64 bits // and a Tagged Pointer version if the bitmask is small enough to fit in - // the tagged pointer payload. + // the tagged pointer payload. // It also changed the layout (but not the size) of the set descriptor. // First check whether this is a tagged pointer. The bitmask will be in // the payload of the tagged pointer. uint64_t payload; - if (runtime->GetFoundationVersion() >= 2000 - && descriptor->GetTaggedPointerInfo(nullptr, nullptr, &payload)) { + if (runtime->GetFoundationVersion() >= 2000 && + descriptor->GetTaggedPointerInfo(nullptr, nullptr, &payload)) { count = llvm::popcount(payload); break; } // The first 32 bits describe the index set in all cases: Status error; uint32_t mode = process_sp->ReadUnsignedIntegerFromMemory( - valobj_addr + ptr_size, 4, 0, error); + valobj_addr + ptr_size, 4, 0, error); if (error.Fail()) return false; // Now check if the index is held in a bitmask in the object: @@ -292,7 +291,7 @@ bool lldb_private::formatters::NSIndexSetSummaryProvider( if ((mode & 2) == 2) { // The bitfield is a 64 bit uint at the beginning of the data var. uint64_t bitfield = process_sp->ReadUnsignedIntegerFromMemory( - valobj_addr + 2 * ptr_size, 8, 0, error); + valobj_addr + 2 * ptr_size, 8, 0, error); if (error.Fail()) return false; count = llvm::popcount(bitfield); @@ -309,7 +308,7 @@ bool lldb_private::formatters::NSIndexSetSummaryProvider( count = 0; break; } - + if ((mode & 2) == 2) mode = 1; // this means the set only has one range else @@ -1227,8 +1226,7 @@ bool lldb_private::formatters::ObjCSELSummaryProvider( time_t lldb_private::formatters::GetOSXEpoch() { static time_t epoch = 0; if (!epoch) { -#if !defined(_AIX) -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(_AIX) tzset(); tm tm_epoch; tm_epoch.tm_sec = 0; @@ -1241,7 +1239,6 @@ time_t lldb_private::formatters::GetOSXEpoch() { tm_epoch.tm_gmtoff = 0; tm_epoch.tm_zone = nullptr; epoch = timegm(&tm_epoch); -#endif #endif } return epoch; diff --git a/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp index 4f747ab20c9ef..b202898ff438a 100644 --- a/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp +++ b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp @@ -81,10 +81,10 @@ size_t ObjectContainerBSDArchive::Archive::ParseObjects() { std::unique_ptr mem_buffer = llvm::MemoryBuffer::getMemBuffer( - llvm::StringRef((const char *)data.GetDataStart(), - data.GetByteSize()), - llvm::StringRef(), - /*RequiresNullTerminator=*/false); + llvm::StringRef((const char *)data.GetDataStart(), + data.GetByteSize()), + llvm::StringRef(), + /*RequiresNullTerminator=*/false); auto exp_ar = llvm::object::Archive::create(mem_buffer->getMemBufferRef()); if (!exp_ar) { @@ -95,7 +95,7 @@ size_t ObjectContainerBSDArchive::Archive::ParseObjects() { llvm::Error iter_err = llvm::Error::success(); Object obj; - for (const auto &child: llvm_archive->children(iter_err)) { + for (const auto &child : llvm_archive->children(iter_err)) { obj.Clear(); auto exp_name = child.getName(); if (exp_name) { @@ -111,7 +111,9 @@ size_t ObjectContainerBSDArchive::Archive::ParseObjects() { obj.modification_time = std::chrono::duration_cast( std::chrono::time_point_cast( - exp_mtime.get()).time_since_epoch()).count(); + exp_mtime.get()) + .time_since_epoch()) + .count(); } else { LLDB_LOG_ERROR(l, exp_mtime.takeError(), "failed to get archive object time: {0}"); @@ -331,21 +333,21 @@ ObjectContainer *ObjectContainerBSDArchive::CreateInstance( ArchiveType ObjectContainerBSDArchive::MagicBytesMatch(const DataExtractor &data) { uint32_t offset = 0; - const char *armag = (const char *)data.PeekData(offset, - sizeof(ar_hdr) + SARMAG); + const char *armag = + (const char *)data.PeekData(offset, sizeof(ar_hdr) + SARMAG); if (armag == nullptr) return ArchiveType::Invalid; ArchiveType result = ArchiveType::Invalid; if (strncmp(armag, ArchiveMagic, SARMAG) == 0) - result = ArchiveType::Archive; + result = ArchiveType::Archive; else if (strncmp(armag, ThinArchiveMagic, SARMAG) == 0) - result = ArchiveType::ThinArchive; + result = ArchiveType::ThinArchive; else - return ArchiveType::Invalid; + return ArchiveType::Invalid; armag += offsetof(struct ar_hdr, ar_fmag) + SARMAG; if (strncmp(armag, ARFMAG, 2) == 0) - return result; + return result; return ArchiveType::Invalid; } @@ -443,7 +445,8 @@ size_t ObjectContainerBSDArchive::GetModuleSpecifications( return 0; const size_t initial_count = specs.GetSize(); - llvm::sys::TimePoint<> file_mod_time = FileSystem::Instance().GetModificationTime(file); + llvm::sys::TimePoint<> file_mod_time = + FileSystem::Instance().GetModificationTime(file); Archive::shared_ptr archive_sp( Archive::FindCachedArchive(file, ArchSpec(), file_mod_time, file_offset)); bool set_archive_arch = false; diff --git a/lldb/source/Utility/ArchSpec.cpp b/lldb/source/Utility/ArchSpec.cpp index ac91183a271cc..85bb85044ec15 100644 --- a/lldb/source/Utility/ArchSpec.cpp +++ b/lldb/source/Utility/ArchSpec.cpp @@ -14,7 +14,6 @@ #include "lldb/lldb-defines.h" #include "llvm/ADT/STLExtras.h" #include "llvm/BinaryFormat/COFF.h" -#include "llvm/BinaryFormat/XCOFF.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/BinaryFormat/XCOFF.h" >From 8fcf69ed77148f8b339b87f75ed97e5ce719b4ba Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Fri, 27 Dec 2024 06:50:27 -0600 Subject: [PATCH 19/46] Some Updates --- lldb/include/lldb/Host/aix/HostInfoAIX.h | 11 +-- lldb/source/Host/aix/HostInfoAIX.cpp | 65 +------------- .../ObjectFile/XCOFF/ObjectFileXCOFF.cpp | 87 +++++++++---------- .../ObjectFile/XCOFF/ObjectFileXCOFF.h | 11 ++- 4 files changed, 50 insertions(+), 124 deletions(-) diff --git a/lldb/include/lldb/Host/aix/HostInfoAIX.h b/lldb/include/lldb/Host/aix/HostInfoAIX.h index ced4cf34d38a8..ba727e1d5f171 100644 --- a/lldb/include/lldb/Host/aix/HostInfoAIX.h +++ b/lldb/include/lldb/Host/aix/HostInfoAIX.h @@ -6,16 +6,14 @@ // //===----------------------------------------------------------------------===// -#ifndef lldb_Host_aix_HostInfoAIX_h_ -#define lldb_Host_aix_HostInfoAIX_h_ +#ifndef LLDB_HOST_AIX_HOSTINFOAIX_H_ +#define LLDB_HOST_AIX_HOSTINFOAIX_H_ #include "lldb/Host/posix/HostInfoPosix.h" #include "lldb/Utility/FileSpec.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/VersionTuple.h" -#include - namespace lldb_private { class HostInfoAIX : public HostInfoPosix { @@ -25,15 +23,10 @@ class HostInfoAIX : public HostInfoPosix { static void Initialize(SharedLibraryDirectoryHelper *helper = nullptr); static void Terminate(); - static llvm::VersionTuple GetOSVersion(); - static std::optional GetOSBuildString(); static llvm::StringRef GetDistributionId(); static FileSpec GetProgramFileSpec(); protected: - static bool ComputeSupportExeDirectory(FileSpec &file_spec); - static bool ComputeSystemPluginsDirectory(FileSpec &file_spec); - static bool ComputeUserPluginsDirectory(FileSpec &file_spec); static void ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64); }; diff --git a/lldb/source/Host/aix/HostInfoAIX.cpp b/lldb/source/Host/aix/HostInfoAIX.cpp index 8bda09e01741b..ef07b07c8cab2 100644 --- a/lldb/source/Host/aix/HostInfoAIX.cpp +++ b/lldb/source/Host/aix/HostInfoAIX.cpp @@ -29,8 +29,6 @@ namespace { struct HostInfoAIXFields { llvm::once_flag m_distribution_once_flag; std::string m_distribution_id; - llvm::once_flag m_os_version_once_flag; - llvm::VersionTuple m_os_version; }; } // namespace @@ -49,33 +47,6 @@ void HostInfoAIX::Terminate() { HostInfoBase::Terminate(); } -llvm::VersionTuple HostInfoAIX::GetOSVersion() { - assert(g_fields && "Missing call to Initialize?"); - llvm::call_once(g_fields->m_os_version_once_flag, []() { - struct utsname un; - if (uname(&un) != 0) - return; - - llvm::StringRef release = un.release; - // The kernel release string can include a lot of stuff (e.g. - // 4.9.0-6-amd64). We're only interested in the numbered prefix. - release = release.substr(0, release.find_first_not_of("0123456789.")); - g_fields->m_os_version.tryParse(release); - }); - - return g_fields->m_os_version; -} - -std::optional HostInfoAIX::GetOSBuildString() { - struct utsname un; - ::memset(&un, 0, sizeof(utsname)); - - if (uname(&un) < 0) - return std::nullopt; - - return std::string(un.release); -} - llvm::StringRef HostInfoAIX::GetDistributionId() { assert(g_fields && "Missing call to Initialize?"); // Try to run 'lbs_release -i', and use that response for the distribution @@ -122,8 +93,7 @@ llvm::StringRef HostInfoAIX::GetDistributionId() { if (strstr(distribution_id, distributor_id_key)) { // strip newlines std::string id_string(distribution_id + strlen(distributor_id_key)); - id_string.erase(std::remove(id_string.begin(), id_string.end(), '\n'), - id_string.end()); + llvm::erase(id_string, '\n'); // lower case it and convert whitespace to underscores std::transform( @@ -167,42 +137,11 @@ FileSpec HostInfoAIX::GetProgramFileSpec() { return g_program_filespec; } -bool HostInfoAIX::ComputeSupportExeDirectory(FileSpec &file_spec) { - if (HostInfoPosix::ComputeSupportExeDirectory(file_spec) && - file_spec.IsAbsolute() && FileSystem::Instance().Exists(file_spec)) - return true; - file_spec.SetDirectory(GetProgramFileSpec().GetDirectory()); - return !file_spec.GetDirectory().IsEmpty(); -} - -bool HostInfoAIX::ComputeSystemPluginsDirectory(FileSpec &file_spec) { - FileSpec temp_file("/usr/" LLDB_INSTALL_LIBDIR_BASENAME "/lldb/plugins"); - FileSystem::Instance().Resolve(temp_file); - file_spec.SetDirectory(temp_file.GetPath()); - return true; -} - -bool HostInfoAIX::ComputeUserPluginsDirectory(FileSpec &file_spec) { - // XDG Base Directory Specification - // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html If - // XDG_DATA_HOME exists, use that, otherwise use ~/.local/share/lldb. - const char *xdg_data_home = getenv("XDG_DATA_HOME"); - if (xdg_data_home && xdg_data_home[0]) { - std::string user_plugin_dir(xdg_data_home); - user_plugin_dir += "/lldb"; - file_spec.SetDirectory(user_plugin_dir.c_str()); - } else - file_spec.SetDirectory("~/.local/share/lldb"); - return true; -} - void HostInfoAIX::ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64) { HostInfoPosix::ComputeHostArchitectureSupport(arch_32, arch_64); - const char *distribution_id = GetDistributionId().data(); - - // On Linux, "unknown" in the vendor slot isn't what we want for the default + // "unknown" in the vendor slot isn't what we want for the default // triple. It's probably an artifact of config.guess. if (arch_32.IsValid()) { if (arch_32.GetTriple().getVendor() == llvm::Triple::UnknownVendor) diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp index a4d9ea295b4c3..afd8027bab06c 100644 --- a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp @@ -1,4 +1,5 @@ -//===-- ObjectFileXCOFF.cpp -------------------------------------------------===// +//===-- ObjectFileXCOFF.cpp +//-------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,13 +8,6 @@ //===----------------------------------------------------------------------===// #include "ObjectFileXCOFF.h" - -#include -#include -#include -#include - -#include "lldb/Utility/FileSpecList.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" @@ -28,6 +22,7 @@ #include "lldb/Target/Target.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/FileSpecList.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/RangeMap.h" @@ -38,12 +33,16 @@ #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/XCOFF.h" +#include "llvm/Object/XCOFFObjectFile.h" #include "llvm/Object/Decompressor.h" #include "llvm/Support/CRC.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/MemoryBuffer.h" -#include "llvm/Object/XCOFFObjectFile.h" +#include +#include +#include +#include using namespace llvm; using namespace lldb; @@ -69,21 +68,19 @@ void ObjectFileXCOFF::Terminate() { bool UGLY_FLAG_FOR_AIX __attribute__((weak)) = false; ObjectFile *ObjectFileXCOFF::CreateInstance(const lldb::ModuleSP &module_sp, - DataBufferSP data_sp, - lldb::offset_t data_offset, - const lldb_private::FileSpec *file, - lldb::offset_t file_offset, - lldb::offset_t length) { + DataBufferSP data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec *file, + lldb::offset_t file_offset, + lldb::offset_t length) { if (!data_sp) { data_sp = MapFileData(*file, length, file_offset); if (!data_sp) return nullptr; data_offset = 0; } - if (!ObjectFileXCOFF::MagicBytesMatch(data_sp, data_offset, length)) return nullptr; - // Update the data to contain the entire file if it doesn't already if (data_sp->GetByteSize() < length) { data_sp = MapFileData(*file, length, file_offset); @@ -114,15 +111,15 @@ bool ObjectFileXCOFF::CreateBinary() { Log *log = GetLog(LLDBLog::Object); - auto binary = llvm::object::XCOFFObjectFile::createObjectFile(llvm::MemoryBufferRef( - toStringRef(m_data.GetData()), m_file.GetFilename().GetStringRef()), - file_magic::xcoff_object_64); + auto binary = llvm::object::ObjectFile::createObjectFile( + llvm::MemoryBufferRef(toStringRef(m_data.GetData()), + m_file.GetFilename().GetStringRef()), + file_magic::xcoff_object_64); if (!binary) { LLDB_LOG_ERROR(log, binary.takeError(), "Failed to create binary for file ({1}): {0}", m_file); return false; } - // Make sure we only handle COFF format. m_binary = llvm::unique_dyn_cast(std::move(*binary)); @@ -132,6 +129,7 @@ bool ObjectFileXCOFF::CreateBinary() { LLDB_LOG(log, "this = {0}, module = {1} ({2}), file = {3}, binary = {4}", this, GetModule().get(), GetModule()->GetSpecificationDescription(), m_file.GetPath(), m_binary.get()); + return true; } @@ -148,9 +146,12 @@ size_t ObjectFileXCOFF::GetModuleSpecifications( const size_t initial_count = specs.GetSize(); if (ObjectFileXCOFF::MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) { - ArchSpec arch_spec = ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); + ArchSpec arch_spec = + ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); ModuleSpec spec(file, arch_spec); - spec.GetArchitecture().SetArchitecture(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE, llvm::Triple::AIX); + spec.GetArchitecture().SetArchitecture(eArchTypeXCOFF, XCOFF::TCPU_PPC64, + LLDB_INVALID_CPUTYPE, + llvm::Triple::AIX); specs.Append(spec); } return specs.GetSize() - initial_count; @@ -158,11 +159,9 @@ size_t ObjectFileXCOFF::GetModuleSpecifications( static uint32_t XCOFFHeaderSizeFromMagic(uint32_t magic) { switch (magic) { - /* TODO: 32bit not supported yet - case XCOFF::XCOFF32: - return sizeof(struct llvm::object::XCOFFFileHeader32); - */ - + // TODO: 32bit not supported. + // case XCOFF::XCOFF32: + // return sizeof(struct llvm::object::XCOFFFileHeader32); case XCOFF::XCOFF64: return sizeof(struct llvm::object::XCOFFFileHeader64); break; @@ -174,10 +173,12 @@ static uint32_t XCOFFHeaderSizeFromMagic(uint32_t magic) { } bool ObjectFileXCOFF::MagicBytesMatch(DataBufferSP &data_sp, - lldb::addr_t data_offset, - lldb::addr_t data_length) { - lldb_private::DataExtractor data; + lldb::addr_t data_offset, + lldb::addr_t data_length) { + lldb_private::DataExtractor data; data.SetData(data_sp, data_offset, data_length); + // Need to set this as XCOFF is only compatible with Big Endian + data.SetByteOrder(eByteOrderBig); lldb::offset_t offset = 0; uint16_t magic = data.GetU16(&offset); return XCOFFHeaderSizeFromMagic(magic) != 0; @@ -386,13 +387,10 @@ bool ObjectFileXCOFF::SetLoadAddressByType(Target &target, lldb::addr_t value, return changed; } -ByteOrder ObjectFileXCOFF::GetByteOrder() const { - return eByteOrderBig; -} -bool ObjectFileXCOFF::IsExecutable() const { - return true; -} +ByteOrder ObjectFileXCOFF::GetByteOrder() const { return eByteOrderBig; } + +bool ObjectFileXCOFF::IsExecutable() const { return true; } uint32_t ObjectFileXCOFF::GetAddressByteSize() const { if (m_xcoff_header.magic == XCOFF::XCOFF64) @@ -592,13 +590,12 @@ void ObjectFileXCOFF::Dump(Stream *s) { } ArchSpec ObjectFileXCOFF::GetArchitecture() { - ArchSpec arch_spec = ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); + ArchSpec arch_spec = + ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); return arch_spec; } -UUID ObjectFileXCOFF::GetUUID() { - return UUID(); -} +UUID ObjectFileXCOFF::GetUUID() { return UUID(); } std::optional ObjectFileXCOFF::GetDebugLink() { return std::nullopt; @@ -724,16 +721,14 @@ lldb_private::Address ObjectFileXCOFF::GetBaseAddress() { } ObjectFile::Type ObjectFileXCOFF::CalculateType() { - if (m_xcoff_header.flags & XCOFF::F_EXEC) + if (m_binary->fileHeader64()->Flags & XCOFF::F_EXEC) return eTypeExecutable; - else if (m_xcoff_header.flags & XCOFF::F_SHROBJ) + else if (m_binary->fileHeader64()->Flags & XCOFF::F_SHROBJ) return eTypeSharedLibrary; return eTypeUnknown; } -ObjectFile::Strata ObjectFileXCOFF::CalculateStrata() { - return eStrataUnknown; -} +ObjectFile::Strata ObjectFileXCOFF::CalculateStrata() { return eStrataUnknown; } llvm::StringRef ObjectFileXCOFF::StripLinkerSymbolAnnotations(llvm::StringRef symbol_name) const { @@ -752,7 +747,7 @@ ObjectFileXCOFF::GetLoadableData(Target &target) { lldb::WritableDataBufferSP ObjectFileXCOFF::MapFileDataWritable(const FileSpec &file, uint64_t Size, - uint64_t Offset) { + uint64_t Offset) { return FileSystem::Instance().CreateWritableDataBuffer(file.GetPath(), Size, Offset); } diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.h b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.h index 5a12d16886489..f827fca3932f4 100644 --- a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.h +++ b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.h @@ -1,4 +1,5 @@ -//===-- ObjectFileXCOFF.h --------------------------------------- -*- C++ -*-===// +//===-- ObjectFileXCOFF.h --------------------------------------- -*- C++ +//-*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,16 +10,14 @@ #ifndef LLDB_SOURCE_PLUGINS_OBJECTFILE_XCOFF_OBJECTFILEXCOFF_H #define LLDB_SOURCE_PLUGINS_OBJECTFILE_XCOFF_OBJECTFILEXCOFF_H -#include - -#include - #include "lldb/Symbol/ObjectFile.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/UUID.h" #include "lldb/lldb-private.h" #include "llvm/Object/XCOFFObjectFile.h" +#include +#include /// \class ObjectFileXCOFF /// Generic XCOFF object file reader. @@ -240,4 +239,4 @@ class ObjectFileXCOFF : public lldb_private::ObjectFile { std::map> m_deps_base_members; }; -#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_ELF_OBJECTFILEELF_H +#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_XCOFF_OBJECTFILE_H >From f8b05dfc9fc75177a63dfa2d6df4a9af143b09b8 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Sun, 5 Jan 2025 05:01:47 -0600 Subject: [PATCH 20/46] HostInfoAIX Cleanup --- lldb/include/lldb/Host/aix/HostInfoAIX.h | 4 - lldb/source/Host/aix/HostInfoAIX.cpp | 108 ----------------------- 2 files changed, 112 deletions(-) diff --git a/lldb/include/lldb/Host/aix/HostInfoAIX.h b/lldb/include/lldb/Host/aix/HostInfoAIX.h index ba727e1d5f171..5a52c42fa6199 100644 --- a/lldb/include/lldb/Host/aix/HostInfoAIX.h +++ b/lldb/include/lldb/Host/aix/HostInfoAIX.h @@ -23,12 +23,8 @@ class HostInfoAIX : public HostInfoPosix { static void Initialize(SharedLibraryDirectoryHelper *helper = nullptr); static void Terminate(); - static llvm::StringRef GetDistributionId(); static FileSpec GetProgramFileSpec(); -protected: - static void ComputeHostArchitectureSupport(ArchSpec &arch_32, - ArchSpec &arch_64); }; } diff --git a/lldb/source/Host/aix/HostInfoAIX.cpp b/lldb/source/Host/aix/HostInfoAIX.cpp index ef07b07c8cab2..2996fcb55f811 100644 --- a/lldb/source/Host/aix/HostInfoAIX.cpp +++ b/lldb/source/Host/aix/HostInfoAIX.cpp @@ -11,117 +11,25 @@ #include "lldb/Host/FileSystem.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" - #include "llvm/Support/Threading.h" - #include #include #include #include #include - #include #include using namespace lldb_private; -namespace { -struct HostInfoAIXFields { - llvm::once_flag m_distribution_once_flag; - std::string m_distribution_id; -}; -} // namespace - -static HostInfoAIXFields *g_fields = nullptr; - void HostInfoAIX::Initialize(SharedLibraryDirectoryHelper *helper) { HostInfoPosix::Initialize(helper); - - g_fields = new HostInfoAIXFields(); } void HostInfoAIX::Terminate() { - assert(g_fields && "Missing call to Initialize?"); - delete g_fields; - g_fields = nullptr; HostInfoBase::Terminate(); } -llvm::StringRef HostInfoAIX::GetDistributionId() { - assert(g_fields && "Missing call to Initialize?"); - // Try to run 'lbs_release -i', and use that response for the distribution - // id. - llvm::call_once(g_fields->m_distribution_once_flag, []() { - Log *log = GetLog(LLDBLog::Host); - LLDB_LOGF(log, "attempting to determine AIX distribution..."); - - // check if the lsb_release command exists at one of the following paths - const char *const exe_paths[] = {"/bin/lsb_release", - "/usr/bin/lsb_release"}; - - for (size_t exe_index = 0; - exe_index < sizeof(exe_paths) / sizeof(exe_paths[0]); ++exe_index) { - const char *const get_distribution_info_exe = exe_paths[exe_index]; - if (access(get_distribution_info_exe, F_OK)) { - // this exe doesn't exist, move on to next exe - LLDB_LOGF(log, "executable doesn't exist: %s", - get_distribution_info_exe); - continue; - } - - // execute the distribution-retrieval command, read output - std::string get_distribution_id_command(get_distribution_info_exe); - get_distribution_id_command += " -i"; - - FILE *file = popen(get_distribution_id_command.c_str(), "r"); - if (!file) { - LLDB_LOGF(log, - "failed to run command: \"%s\", cannot retrieve " - "platform information", - get_distribution_id_command.c_str()); - break; - } - - // retrieve the distribution id string. - char distribution_id[256] = {'\0'}; - if (fgets(distribution_id, sizeof(distribution_id) - 1, file) != - nullptr) { - LLDB_LOGF(log, "distribution id command returned \"%s\"", - distribution_id); - - const char *const distributor_id_key = "Distributor ID:\t"; - if (strstr(distribution_id, distributor_id_key)) { - // strip newlines - std::string id_string(distribution_id + strlen(distributor_id_key)); - llvm::erase(id_string, '\n'); - - // lower case it and convert whitespace to underscores - std::transform( - id_string.begin(), id_string.end(), id_string.begin(), - [](char ch) { return tolower(isspace(ch) ? '_' : ch); }); - - g_fields->m_distribution_id = id_string; - LLDB_LOGF(log, "distribution id set to \"%s\"", - g_fields->m_distribution_id.c_str()); - } else { - LLDB_LOGF(log, "failed to find \"%s\" field in \"%s\"", - distributor_id_key, distribution_id); - } - } else { - LLDB_LOGF(log, - "failed to retrieve distribution id, \"%s\" returned no" - " lines", - get_distribution_id_command.c_str()); - } - - // clean up the file - pclose(file); - } - }); - - return g_fields->m_distribution_id; -} - FileSpec HostInfoAIX::GetProgramFileSpec() { static FileSpec g_program_filespec; @@ -136,19 +44,3 @@ FileSpec HostInfoAIX::GetProgramFileSpec() { return g_program_filespec; } - -void HostInfoAIX::ComputeHostArchitectureSupport(ArchSpec &arch_32, - ArchSpec &arch_64) { - HostInfoPosix::ComputeHostArchitectureSupport(arch_32, arch_64); - - // "unknown" in the vendor slot isn't what we want for the default - // triple. It's probably an artifact of config.guess. - if (arch_32.IsValid()) { - if (arch_32.GetTriple().getVendor() == llvm::Triple::UnknownVendor) - arch_32.GetTriple().setVendorName(llvm::StringRef()); - } - if (arch_64.IsValid()) { - if (arch_64.GetTriple().getVendor() == llvm::Triple::UnknownVendor) - arch_64.GetTriple().setVendorName(llvm::StringRef()); - } -} >From 57d080e44e80203a6ab848c362469954a7c9f067 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Sun, 5 Jan 2025 05:49:40 -0600 Subject: [PATCH 21/46] Cleanup HostInfoAIX Including the previous commit, Removed: GetDistributionID, ComputeHostArchitectureSupport and Reduced GetProgramFileSpec as it was not needed --- lldb/source/Host/aix/HostInfoAIX.cpp | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/lldb/source/Host/aix/HostInfoAIX.cpp b/lldb/source/Host/aix/HostInfoAIX.cpp index 2996fcb55f811..d09b9052668af 100644 --- a/lldb/source/Host/aix/HostInfoAIX.cpp +++ b/lldb/source/Host/aix/HostInfoAIX.cpp @@ -26,21 +26,9 @@ void HostInfoAIX::Initialize(SharedLibraryDirectoryHelper *helper) { HostInfoPosix::Initialize(helper); } -void HostInfoAIX::Terminate() { - HostInfoBase::Terminate(); -} +void HostInfoAIX::Terminate() { HostInfoBase::Terminate(); } FileSpec HostInfoAIX::GetProgramFileSpec() { static FileSpec g_program_filespec; - - if (!g_program_filespec) { - char exe_path[PATH_MAX]; - ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1); - if (len > 0) { - exe_path[len] = 0; - g_program_filespec.SetFile(exe_path, FileSpec::Style::native); - } - } - return g_program_filespec; } >From 673713a9339de4e4ea395ee2e7f65dc1db43bcf9 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Sun, 5 Jan 2025 15:35:23 -0600 Subject: [PATCH 22/46] Removing headers --- lldb/include/lldb/Host/aix/HostInfoAIX.h | 2 -- lldb/source/Host/aix/HostInfoAIX.cpp | 12 ------------ 2 files changed, 14 deletions(-) diff --git a/lldb/include/lldb/Host/aix/HostInfoAIX.h b/lldb/include/lldb/Host/aix/HostInfoAIX.h index 5a52c42fa6199..331a274630850 100644 --- a/lldb/include/lldb/Host/aix/HostInfoAIX.h +++ b/lldb/include/lldb/Host/aix/HostInfoAIX.h @@ -11,8 +11,6 @@ #include "lldb/Host/posix/HostInfoPosix.h" #include "lldb/Utility/FileSpec.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/VersionTuple.h" namespace lldb_private { diff --git a/lldb/source/Host/aix/HostInfoAIX.cpp b/lldb/source/Host/aix/HostInfoAIX.cpp index d09b9052668af..61b47462dd647 100644 --- a/lldb/source/Host/aix/HostInfoAIX.cpp +++ b/lldb/source/Host/aix/HostInfoAIX.cpp @@ -7,18 +7,6 @@ //===----------------------------------------------------------------------===// #include "lldb/Host/aix/HostInfoAIX.h" -#include "lldb/Host/Config.h" -#include "lldb/Host/FileSystem.h" -#include "lldb/Utility/LLDBLog.h" -#include "lldb/Utility/Log.h" -#include "llvm/Support/Threading.h" -#include -#include -#include -#include -#include -#include -#include using namespace lldb_private; >From cdc31f3963365e4595247ff5a7155662df1ce1af Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Mon, 6 Jan 2025 09:28:56 -0600 Subject: [PATCH 23/46] Reverted merge blunder CMakeLists --- lldb/source/Host/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lldb/source/Host/CMakeLists.txt b/lldb/source/Host/CMakeLists.txt index f326bc07dc1f6..f4fca8acc4d83 100644 --- a/lldb/source/Host/CMakeLists.txt +++ b/lldb/source/Host/CMakeLists.txt @@ -141,7 +141,10 @@ else() elseif (CMAKE_SYSTEM_NAME MATCHES "AIX") add_host_subdirectory(aix + aix/AbstractSocket.cpp + aix/Host.cpp aix/HostInfoAIX.cpp + aix/Support.cpp ) endif() endif() >From 713a6cbbb97b9bc56b039ab050a1690eccc017e3 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Mon, 6 Jan 2025 09:39:36 -0600 Subject: [PATCH 24/46] Removed DomainSocket.cpp FileSystemPosix.cpp includes --- lldb/source/Host/posix/DomainSocket.cpp | 4 ---- lldb/source/Host/posix/FileSystemPosix.cpp | 3 --- 2 files changed, 7 deletions(-) diff --git a/lldb/source/Host/posix/DomainSocket.cpp b/lldb/source/Host/posix/DomainSocket.cpp index f30e84d83efca..be8fcdf2c8f2c 100644 --- a/lldb/source/Host/posix/DomainSocket.cpp +++ b/lldb/source/Host/posix/DomainSocket.cpp @@ -17,10 +17,6 @@ #include #include -#if defined(_AIX) -#include -#endif - using namespace lldb; using namespace lldb_private; diff --git a/lldb/source/Host/posix/FileSystemPosix.cpp b/lldb/source/Host/posix/FileSystemPosix.cpp index 1a84f550662d7..a631bb01209ec 100644 --- a/lldb/source/Host/posix/FileSystemPosix.cpp +++ b/lldb/source/Host/posix/FileSystemPosix.cpp @@ -11,9 +11,6 @@ // C includes #include #include -#ifndef _AIX -#include -#endif #include #include #include >From 0a706d29dabeefa62e354fc9358d650f89032048 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Mon, 6 Jan 2025 09:44:10 -0600 Subject: [PATCH 25/46] sys/mount.h --- lldb/source/Host/posix/FileSystemPosix.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lldb/source/Host/posix/FileSystemPosix.cpp b/lldb/source/Host/posix/FileSystemPosix.cpp index a631bb01209ec..945e2affc8371 100644 --- a/lldb/source/Host/posix/FileSystemPosix.cpp +++ b/lldb/source/Host/posix/FileSystemPosix.cpp @@ -11,6 +11,7 @@ // C includes #include #include +#include #include #include #include >From 84ebb4ec9b542c38fe1b60261a3253383e9fbf5d Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Mon, 6 Jan 2025 09:54:58 -0600 Subject: [PATCH 26/46] sys/mount.h --- lldb/source/Host/posix/FileSystemPosix.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lldb/source/Host/posix/FileSystemPosix.cpp b/lldb/source/Host/posix/FileSystemPosix.cpp index 945e2affc8371..1a84f550662d7 100644 --- a/lldb/source/Host/posix/FileSystemPosix.cpp +++ b/lldb/source/Host/posix/FileSystemPosix.cpp @@ -11,7 +11,9 @@ // C includes #include #include +#ifndef _AIX #include +#endif #include #include #include >From 844f7980040de9e13620e9d65a3fcaef56b6c8d6 Mon Sep 17 00:00:00 2001 From: Dhruv Srivastava Date: Thu, 9 Jan 2025 09:47:43 +0530 Subject: [PATCH 27/46] Removed _AIX from ConnectionFileDescriptorPosix.cpp --- lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp b/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp index 2530c8fa353ba..0ed2016667162 100644 --- a/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp +++ b/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp @@ -715,7 +715,7 @@ ConnectionFileDescriptor::ConnectFD(llvm::StringRef s, ConnectionStatus ConnectionFileDescriptor::ConnectFile( llvm::StringRef s, socket_id_callback_type socket_id_callback, Status *error_ptr) { -#if LLDB_ENABLE_POSIX && !defined(_AIX) +#if LLDB_ENABLE_POSIX std::string addr_str = s.str(); // file:///PATH int fd = FileSystem::Instance().Open(addr_str.c_str(), O_RDWR); @@ -756,7 +756,7 @@ ConnectionStatus ConnectionFileDescriptor::ConnectFile( m_io_sp = std::make_shared(fd, File::eOpenOptionReadWrite, true); return eConnectionStatusSuccess; -#endif // LLDB_ENABLE_POSIX && !defined(_AIX) +#endif // LLDB_ENABLE_POSIX llvm_unreachable("this function should be only called w/ LLDB_ENABLE_POSIX"); } >From 4fe42cda7c2f4990b18a39c1d6563094fb88775f Mon Sep 17 00:00:00 2001 From: ravindra shinde Date: Fri, 17 Jan 2025 13:58:13 +0530 Subject: [PATCH 28/46] [ObjectFileXCOFF] Fix access to protected member 'GetSectionLoadList' in Target - Added a public method to Target for accessing 'GetSectionLoadList' safely. - Updated ObjectFileXCOFF to use the new public method, ensuring compliance with encapsulation rules. This resolves the build error caused by direct access to the protected member. Signed-off-by: ravindra shinde --- lldb/include/lldb/Target/Target.h | 5 +++++ lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h index f31ac381391b4..75f9c9c2e999c 100644 --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -522,6 +522,7 @@ class Target : public std::enable_shared_from_this, eBroadcastBitSymbolsChanged = (1 << 5), }; + // These two functions fill out the Broadcaster interface: static llvm::StringRef GetStaticBroadcasterClass(); @@ -1644,6 +1645,10 @@ class Target : public std::enable_shared_from_this, TargetStats &GetStatistics() { return m_stats; } +public: + SectionLoadList &GetSectionLoadListPublic() { + return GetSectionLoadList(); + } protected: /// Construct with optional file and arch. /// diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp index afd8027bab06c..cf11e5fb8f5a3 100644 --- a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp @@ -340,7 +340,7 @@ bool ObjectFileXCOFF::SetLoadAddress(Target &target, lldb::addr_t value, strcmp(section_sp->GetName().AsCString(), ".bss") == 0) use_offset = true; - if (target.GetSectionLoadList().SetSectionLoadAddress( + if (target.GetSectionLoadListPublic().SetSectionLoadAddress( section_sp, (use_offset ? (section_sp->GetFileOffset() + value) : (section_sp->GetFileAddress() + value)))) ++num_loaded_sections; @@ -369,13 +369,13 @@ bool ObjectFileXCOFF::SetLoadAddressByType(Target &target, lldb::addr_t value, SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); if (type_id == 1 && section_sp && strcmp(section_sp->GetName().AsCString(), ".text") == 0) { if (!section_sp->IsThreadSpecific()) { - if (target.GetSectionLoadList().SetSectionLoadAddress( + if (target.GetSectionLoadListPublic().SetSectionLoadAddress( section_sp, section_sp->GetFileOffset() + value)) ++num_loaded_sections; } } else if (type_id == 2 && section_sp && strcmp(section_sp->GetName().AsCString(), ".data") == 0) { if (!section_sp->IsThreadSpecific()) { - if (target.GetSectionLoadList().SetSectionLoadAddress( + if (target.GetSectionLoadListPublic().SetSectionLoadAddress( section_sp, section_sp->GetFileAddress() + value)) ++num_loaded_sections; } >From e5ed4f21c5bbc709e5e2eff0f83995cc6d89de48 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Sun, 19 Jan 2025 03:43:11 -0600 Subject: [PATCH 29/46] Resolved cmake failure for SBProgress.cpp --- lldb/source/API/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/lldb/source/API/CMakeLists.txt b/lldb/source/API/CMakeLists.txt index eb348e2b97232..0a03e000c0cae 100644 --- a/lldb/source/API/CMakeLists.txt +++ b/lldb/source/API/CMakeLists.txt @@ -85,6 +85,7 @@ add_lldb_library(liblldb STATIC ${option_framework} SBModuleSpec.cpp SBPlatform.cpp SBProcess.cpp + SBProgress.cpp SBProcessInfo.cpp SBProcessInfoList.cpp SBQueue.cpp >From 82dbcb0e776c438e5f40c9f8d8c8e8eb81b7febd Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Mon, 27 Jan 2025 06:35:19 -0600 Subject: [PATCH 30/46] Host.cpp ANDROID --- lldb/source/Host/common/Host.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp index 758e9f49ade2c..adf74df8aa90b 100644 --- a/lldb/source/Host/common/Host.cpp +++ b/lldb/source/Host/common/Host.cpp @@ -1,4 +1,4 @@ -//===-- Host.cpp ----------------------------------------------------------===// + // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -516,7 +516,6 @@ static int dladdr(const void *ptr, Dl_info *dl) FileSpec Host::GetModuleFileSpecForHostAddress(const void *host_addr) { FileSpec module_filespec; -#if !defined(__ANDROID__) #ifdef _AIX if (host_addr == reinterpret_cast(HostInfoBase::ComputeSharedLibraryDirectory)) { // FIXME: AIX dladdr return "lldb" for this case @@ -527,6 +526,7 @@ FileSpec Host::GetModuleFileSpecForHostAddress(const void *host_addr) { } } #endif +#if !defined(__ANDROID__) Dl_info info; if (::dladdr(host_addr, &info)) { if (info.dli_fname) { @@ -534,6 +534,7 @@ FileSpec Host::GetModuleFileSpecForHostAddress(const void *host_addr) { FileSystem::Instance().Resolve(module_filespec); } } +#endif return module_filespec; } >From 60294eaa1611632afc94b9da503752e34ad2703d Mon Sep 17 00:00:00 2001 From: Ravindra Shinde Date: Tue, 4 Feb 2025 03:23:56 -0600 Subject: [PATCH 31/46] Resolving the fatal error while build Build is failing due to the fatal error: 'sys/syscall.h' file not found Signed-off-by: Ravindra Shinde --- lldb/source/Host/common/Host.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp index 40ce76d70d6e8..b5bd68b8539bd 100644 --- a/lldb/source/Host/common/Host.cpp +++ b/lldb/source/Host/common/Host.cpp @@ -18,8 +18,12 @@ #include #include #include + +#ifndef _AIX #include #include +#endif + #include #include #endif >From 2644be59b13a61c69cc635875c94b0b4645fe76c Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Mon, 10 Feb 2025 01:49:12 -0600 Subject: [PATCH 32/46] InferiorCallPOSIX.cpp --- lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp b/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp index d1f9fe851119e..8e74cce097894 100644 --- a/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp +++ b/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp @@ -120,12 +120,13 @@ bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr, arch, addr, length, prot_arg, flags, fd, offset); #if defined(_AIX) lldb::ThreadPlanSP call_plan_sp( - new ThreadPlanCallFunction(*thread, mmap_range.GetBaseAddress(), + new ThreadPlanCallFunction(*thread, mmap_addr, toc_range.GetBaseAddress(), void_ptr_type, args, options)); #else lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallFunction( *thread, mmap_addr, void_ptr_type, args, options)); +#endif if (call_plan_sp) { DiagnosticManager diagnostics; >From 4805b13cba964b58def39a66ad4c4309a9b2c501 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Wed, 12 Feb 2025 07:33:04 -0600 Subject: [PATCH 33/46] Merge branch gh-101657 --- .../AIX-DYLD/DynamicLoaderAIXDYLD.cpp | 97 ------------------- .../AIX-DYLD/DynamicLoaderAIXDYLD.h | 5 +- 2 files changed, 1 insertion(+), 101 deletions(-) diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp index 1a98bb9334043..375d879c7a995 100644 --- a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp @@ -21,10 +21,6 @@ #include "llvm/Support/FileSystem.h" #if defined(_AIX) #include -#include -#include -#include -#include #endif /*#include "llvm/ADT/Triple.h" @@ -137,107 +133,14 @@ bool DynamicLoaderAIXDYLD::NotifyBreakpointHit( } -void DynamicLoaderAIXDYLD::ResolveExecutableModule( - lldb::ModuleSP &module_sp) { - Log *log = GetLog(LLDBLog::DynamicLoader); - - if (m_process == nullptr) - return; - - auto &target = m_process->GetTarget(); - const auto platform_sp = target.GetPlatform(); - - ProcessInstanceInfo process_info; - if (!m_process->GetProcessInfo(process_info)) { - LLDB_LOGF(log, - "DynamicLoaderPOSIXDYLD::%s - failed to get process info for " - "pid %" PRIu64, - __FUNCTION__, m_process->GetID()); - return; - } - - int32long64_t pid = m_process->GetID(); - char cwd[PATH_MAX], resolved_path[PATH_MAX]; - std::string executable_name; - bool path_resolved = false; - psinfo_t psinfo; - - std::string proc_file = "/proc/" + std::to_string(pid) + "/psinfo"; - std::string cwd_link = "/proc/" + std::to_string(pid) + "/cwd"; - std::ifstream file(proc_file, std::ios::binary); - if(!file.is_open()) - LLDB_LOGF(log, "Error: Unable to access process info "); - - file.read(reinterpret_cast(&psinfo), sizeof(psinfo_t)); - if(!file) - LLDB_LOGF(log, "Process info error: Failed to read "); - - std::string relative_path(psinfo.pr_fname); - LLDB_LOGF(log, "Relative path %s",relative_path.c_str()); - - if(readlink(cwd_link.c_str(), cwd, sizeof(cwd)) != -1){ - std::filesystem::path full_path = std::filesystem::path(cwd)/relative_path; - if(realpath(full_path.c_str(), resolved_path)) { - LLDB_LOGF(log, "Resolved Path using process info : %s", resolved_path); - path_resolved = true; - } - else - LLDB_LOGF(log, "Realpath error: Unable to resolve. "); - } - - executable_name = resolved_path; - if(path_resolved == false) { - std::string command_line(psinfo.pr_psargs); - LLDB_LOGF(log, "Command line: %s",command_line.c_str()); - if (!command_line.empty()) { - size_t space1 = command_line.find(' '); - executable_name = command_line.substr(0, space1); - LLDB_LOGF(log, "Resolved path using command line arg %s",executable_name.c_str()); - } - } - - LLDB_LOGF(log, "Executable Name %s",executable_name.c_str()); - process_info.SetExecutableFile(lldb_private::FileSpec(executable_name), - true); - - LLDB_LOGF( - log, "DynamicLoaderPOSIXDYLD::%s - got executable by pid %" PRIu64 ": %s", - __FUNCTION__, m_process->GetID(), - process_info.GetExecutableFile().GetPath().c_str()); - - ModuleSpec module_spec(process_info.GetExecutableFile(), - process_info.GetArchitecture()); - if (module_sp && module_sp->MatchesModuleSpec(module_spec)) - return; - - const auto executable_search_paths(Target::GetDefaultExecutableSearchPaths()); - auto error = platform_sp->ResolveExecutable( - module_spec, module_sp, - !executable_search_paths.IsEmpty() ? &executable_search_paths : nullptr); - if (error.Fail()) { - StreamString stream; - module_spec.Dump(stream); - - LLDB_LOGF(log, - "DynamicLoaderPOSIXDYLD::%s - failed to resolve executable " - "with module spec \"%s\": %s", - __FUNCTION__, stream.GetData(), error.AsCString()); - return; - } - - target.SetExecutableModule(module_sp, eLoadDependentsNo); -} - void DynamicLoaderAIXDYLD::DidAttach() { Log *log = GetLog(LLDBLog::DynamicLoader); LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); ModuleSP executable = GetTargetExecutable(); - ResolveExecutableModule(executable); if (!executable.get()) return; - LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); // Try to fetch the load address of the file from the process, since there // could be randomization of the load address. diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h index 0ffbe688e0069..ae4b7aca66dcc 100644 --- a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h @@ -24,7 +24,7 @@ class DynamicLoaderAIXDYLD : public DynamicLoader { static void Initialize(); static void Terminate(); - static llvm::StringRef GetPluginNameStatic() { return "aix-dyld"; } + static llvm::StringRef GetPluginNameStatic() { return "windows-dyld"; } static llvm::StringRef GetPluginDescriptionStatic(); static DynamicLoader *CreateInstance(Process *process, bool force); @@ -46,9 +46,6 @@ class DynamicLoaderAIXDYLD : public DynamicLoader { protected: lldb::addr_t GetLoadAddress(lldb::ModuleSP executable); - /// Loads Module from inferior process. - void ResolveExecutableModule(lldb::ModuleSP &module_sp); - private: std::map m_loaded_modules; }; >From cff574b36903e12385e5d6cddf4532ba279f47de Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Wed, 12 Feb 2025 07:52:04 -0600 Subject: [PATCH 34/46] Fix for Debugging Attach to AIX Process --- .../AIX-DYLD/DynamicLoaderAIXDYLD.cpp | 97 +++++++++++++++++++ .../AIX-DYLD/DynamicLoaderAIXDYLD.h | 5 +- 2 files changed, 101 insertions(+), 1 deletion(-) diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp index 375d879c7a995..1a98bb9334043 100644 --- a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp @@ -21,6 +21,10 @@ #include "llvm/Support/FileSystem.h" #if defined(_AIX) #include +#include +#include +#include +#include #endif /*#include "llvm/ADT/Triple.h" @@ -133,14 +137,107 @@ bool DynamicLoaderAIXDYLD::NotifyBreakpointHit( } +void DynamicLoaderAIXDYLD::ResolveExecutableModule( + lldb::ModuleSP &module_sp) { + Log *log = GetLog(LLDBLog::DynamicLoader); + + if (m_process == nullptr) + return; + + auto &target = m_process->GetTarget(); + const auto platform_sp = target.GetPlatform(); + + ProcessInstanceInfo process_info; + if (!m_process->GetProcessInfo(process_info)) { + LLDB_LOGF(log, + "DynamicLoaderPOSIXDYLD::%s - failed to get process info for " + "pid %" PRIu64, + __FUNCTION__, m_process->GetID()); + return; + } + + int32long64_t pid = m_process->GetID(); + char cwd[PATH_MAX], resolved_path[PATH_MAX]; + std::string executable_name; + bool path_resolved = false; + psinfo_t psinfo; + + std::string proc_file = "/proc/" + std::to_string(pid) + "/psinfo"; + std::string cwd_link = "/proc/" + std::to_string(pid) + "/cwd"; + std::ifstream file(proc_file, std::ios::binary); + if(!file.is_open()) + LLDB_LOGF(log, "Error: Unable to access process info "); + + file.read(reinterpret_cast(&psinfo), sizeof(psinfo_t)); + if(!file) + LLDB_LOGF(log, "Process info error: Failed to read "); + + std::string relative_path(psinfo.pr_fname); + LLDB_LOGF(log, "Relative path %s",relative_path.c_str()); + + if(readlink(cwd_link.c_str(), cwd, sizeof(cwd)) != -1){ + std::filesystem::path full_path = std::filesystem::path(cwd)/relative_path; + if(realpath(full_path.c_str(), resolved_path)) { + LLDB_LOGF(log, "Resolved Path using process info : %s", resolved_path); + path_resolved = true; + } + else + LLDB_LOGF(log, "Realpath error: Unable to resolve. "); + } + + executable_name = resolved_path; + if(path_resolved == false) { + std::string command_line(psinfo.pr_psargs); + LLDB_LOGF(log, "Command line: %s",command_line.c_str()); + if (!command_line.empty()) { + size_t space1 = command_line.find(' '); + executable_name = command_line.substr(0, space1); + LLDB_LOGF(log, "Resolved path using command line arg %s",executable_name.c_str()); + } + } + + LLDB_LOGF(log, "Executable Name %s",executable_name.c_str()); + process_info.SetExecutableFile(lldb_private::FileSpec(executable_name), + true); + + LLDB_LOGF( + log, "DynamicLoaderPOSIXDYLD::%s - got executable by pid %" PRIu64 ": %s", + __FUNCTION__, m_process->GetID(), + process_info.GetExecutableFile().GetPath().c_str()); + + ModuleSpec module_spec(process_info.GetExecutableFile(), + process_info.GetArchitecture()); + if (module_sp && module_sp->MatchesModuleSpec(module_spec)) + return; + + const auto executable_search_paths(Target::GetDefaultExecutableSearchPaths()); + auto error = platform_sp->ResolveExecutable( + module_spec, module_sp, + !executable_search_paths.IsEmpty() ? &executable_search_paths : nullptr); + if (error.Fail()) { + StreamString stream; + module_spec.Dump(stream); + + LLDB_LOGF(log, + "DynamicLoaderPOSIXDYLD::%s - failed to resolve executable " + "with module spec \"%s\": %s", + __FUNCTION__, stream.GetData(), error.AsCString()); + return; + } + + target.SetExecutableModule(module_sp, eLoadDependentsNo); +} + void DynamicLoaderAIXDYLD::DidAttach() { Log *log = GetLog(LLDBLog::DynamicLoader); LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); ModuleSP executable = GetTargetExecutable(); + ResolveExecutableModule(executable); if (!executable.get()) return; + LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); // Try to fetch the load address of the file from the process, since there // could be randomization of the load address. diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h index ae4b7aca66dcc..0ffbe688e0069 100644 --- a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h @@ -24,7 +24,7 @@ class DynamicLoaderAIXDYLD : public DynamicLoader { static void Initialize(); static void Terminate(); - static llvm::StringRef GetPluginNameStatic() { return "windows-dyld"; } + static llvm::StringRef GetPluginNameStatic() { return "aix-dyld"; } static llvm::StringRef GetPluginDescriptionStatic(); static DynamicLoader *CreateInstance(Process *process, bool force); @@ -46,6 +46,9 @@ class DynamicLoaderAIXDYLD : public DynamicLoader { protected: lldb::addr_t GetLoadAddress(lldb::ModuleSP executable); + /// Loads Module from inferior process. + void ResolveExecutableModule(lldb::ModuleSP &module_sp); + private: std::map m_loaded_modules; }; >From 303fa3bc078d7572702fb302726d4dd1bd13ddb2 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Fri, 21 Feb 2025 06:18:33 -0600 Subject: [PATCH 35/46] Merge branch 'llvm:main' into gh-101657 --- lldb/source/Plugins/Platform/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/lldb/source/Plugins/Platform/CMakeLists.txt b/lldb/source/Plugins/Platform/CMakeLists.txt index f5b0dbdd5e0a9..0220e734b36d1 100644 --- a/lldb/source/Plugins/Platform/CMakeLists.txt +++ b/lldb/source/Plugins/Platform/CMakeLists.txt @@ -9,4 +9,3 @@ add_subdirectory(OpenBSD) add_subdirectory(POSIX) add_subdirectory(QemuUser) add_subdirectory(Windows) -add_subdirectory(AIX) >From 6947dec02bb527f710c1bea3827b2c16d89f308a Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Fri, 21 Feb 2025 07:02:53 -0600 Subject: [PATCH 36/46] Merge branch 'llvm:main' into gh-101657 --- .../Plugins/Platform/AIX/PlatformAIX.cpp | 171 +----------------- 1 file changed, 1 insertion(+), 170 deletions(-) diff --git a/lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp b/lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp index 0d66325d16267..21724d83133e9 100644 --- a/lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp +++ b/lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp @@ -130,12 +130,6 @@ void PlatformAIX::GetStatus(Stream &strm) { #endif } -void PlatformAIX::CalculateTrapHandlerSymbolNames() { - m_trap_handlers.push_back(ConstString("_sigtramp")); - m_trap_handlers.push_back(ConstString("__kernel_rt_sigreturn")); - m_trap_handlers.push_back(ConstString("__restore_rt")); -} - void PlatformAIX::CalculateTrapHandlerSymbolNames() {} lldb::UnwindPlanSP @@ -160,168 +154,5 @@ MmapArgList PlatformAIX::GetMmapArgumentList(const ArchSpec &arch, addr_t addr, } CompilerType PlatformAIX::GetSiginfoType(const llvm::Triple &triple) { - if (!m_type_system_up) - m_type_system_up.reset(new TypeSystemClang("siginfo", triple)); - TypeSystemClang *ast = m_type_system_up.get(); - - bool si_errno_then_code = true; - - switch (triple.getArch()) { - case llvm::Triple::mips: - case llvm::Triple::mipsel: - case llvm::Triple::mips64: - case llvm::Triple::mips64el: - // mips has si_code and si_errno swapped - si_errno_then_code = false; - break; - default: - break; - } - - // generic types - CompilerType int_type = ast->GetBasicType(eBasicTypeInt); - CompilerType uint_type = ast->GetBasicType(eBasicTypeUnsignedInt); - CompilerType short_type = ast->GetBasicType(eBasicTypeShort); - CompilerType long_type = ast->GetBasicType(eBasicTypeLong); - CompilerType voidp_type = ast->GetBasicType(eBasicTypeVoid).GetPointerType(); - - // platform-specific types - CompilerType &pid_type = int_type; - CompilerType &uid_type = uint_type; - CompilerType &clock_type = long_type; - CompilerType &band_type = long_type; - - CompilerType sigval_type = ast->CreateRecordType( - nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "__lldb_sigval_t", - llvm::to_underlying(clang::TagTypeKind::Union), lldb::eLanguageTypeC); - ast->StartTagDeclarationDefinition(sigval_type); - ast->AddFieldToRecordType(sigval_type, "sival_int", int_type, - lldb::eAccessPublic, 0); - ast->AddFieldToRecordType(sigval_type, "sival_ptr", voidp_type, - lldb::eAccessPublic, 0); - ast->CompleteTagDeclarationDefinition(sigval_type); - - CompilerType sigfault_bounds_type = ast->CreateRecordType( - nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "", - llvm::to_underlying(clang::TagTypeKind::Union), lldb::eLanguageTypeC); - ast->StartTagDeclarationDefinition(sigfault_bounds_type); - ast->AddFieldToRecordType(sigfault_bounds_type, "_addr_bnd", - ast->CreateStructForIdentifier(ConstString(), - { - {"_lower", voidp_type}, - {"_upper", voidp_type}, - }), - lldb::eAccessPublic, 0); - ast->AddFieldToRecordType(sigfault_bounds_type, "_pkey", uint_type, - lldb::eAccessPublic, 0); - ast->CompleteTagDeclarationDefinition(sigfault_bounds_type); - - // siginfo_t - CompilerType siginfo_type = ast->CreateRecordType( - nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "__lldb_siginfo_t", - llvm::to_underlying(clang::TagTypeKind::Struct), lldb::eLanguageTypeC); - ast->StartTagDeclarationDefinition(siginfo_type); - ast->AddFieldToRecordType(siginfo_type, "si_signo", int_type, - lldb::eAccessPublic, 0); - - if (si_errno_then_code) { - ast->AddFieldToRecordType(siginfo_type, "si_errno", int_type, - lldb::eAccessPublic, 0); - ast->AddFieldToRecordType(siginfo_type, "si_code", int_type, - lldb::eAccessPublic, 0); - } else { - ast->AddFieldToRecordType(siginfo_type, "si_code", int_type, - lldb::eAccessPublic, 0); - ast->AddFieldToRecordType(siginfo_type, "si_errno", int_type, - lldb::eAccessPublic, 0); - } - - // the structure is padded on 64-bit arches to fix alignment - if (triple.isArch64Bit()) - ast->AddFieldToRecordType(siginfo_type, "__pad0", int_type, - lldb::eAccessPublic, 0); - - // union used to hold the signal data - CompilerType union_type = ast->CreateRecordType( - nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "", - llvm::to_underlying(clang::TagTypeKind::Union), lldb::eLanguageTypeC); - ast->StartTagDeclarationDefinition(union_type); - - ast->AddFieldToRecordType( - union_type, "_kill", - ast->CreateStructForIdentifier(ConstString(), - { - {"si_pid", pid_type}, - {"si_uid", uid_type}, - }), - lldb::eAccessPublic, 0); - - ast->AddFieldToRecordType( - union_type, "_timer", - ast->CreateStructForIdentifier(ConstString(), - { - {"si_tid", int_type}, - {"si_overrun", int_type}, - {"si_sigval", sigval_type}, - }), - lldb::eAccessPublic, 0); - - ast->AddFieldToRecordType( - union_type, "_rt", - ast->CreateStructForIdentifier(ConstString(), - { - {"si_pid", pid_type}, - {"si_uid", uid_type}, - {"si_sigval", sigval_type}, - }), - lldb::eAccessPublic, 0); - - ast->AddFieldToRecordType( - union_type, "_sigchld", - ast->CreateStructForIdentifier(ConstString(), - { - {"si_pid", pid_type}, - {"si_uid", uid_type}, - {"si_status", int_type}, - {"si_utime", clock_type}, - {"si_stime", clock_type}, - }), - lldb::eAccessPublic, 0); - - ast->AddFieldToRecordType( - union_type, "_sigfault", - ast->CreateStructForIdentifier(ConstString(), - { - {"si_addr", voidp_type}, - {"si_addr_lsb", short_type}, - {"_bounds", sigfault_bounds_type}, - }), - lldb::eAccessPublic, 0); - - ast->AddFieldToRecordType( - union_type, "_sigpoll", - ast->CreateStructForIdentifier(ConstString(), - { - {"si_band", band_type}, - {"si_fd", int_type}, - }), - lldb::eAccessPublic, 0); - - // NB: SIGSYS is not present on ia64 but we don't seem to support that - ast->AddFieldToRecordType( - union_type, "_sigsys", - ast->CreateStructForIdentifier(ConstString(), - { - {"_call_addr", voidp_type}, - {"_syscall", int_type}, - {"_arch", uint_type}, - }), - lldb::eAccessPublic, 0); - - ast->CompleteTagDeclarationDefinition(union_type); - ast->AddFieldToRecordType(siginfo_type, "_sifields", union_type, - lldb::eAccessPublic, 0); - - ast->CompleteTagDeclarationDefinition(siginfo_type); - return siginfo_type; + return CompilerType(); } >From 24615119addcdf9fe5a8c5b2ebd0c15d75651279 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Sat, 22 Feb 2025 03:42:26 -0600 Subject: [PATCH 37/46] Removed un-needed changes --- lldb/include/lldb/Host/aix/AbstractSocket.h | 25 --------------------- lldb/include/lldb/Host/aix/Uio.h | 23 ------------------- lldb/source/Host/CMakeLists.txt | 1 - lldb/source/Host/aix/AbstractSocket.cpp | 20 ----------------- 4 files changed, 69 deletions(-) delete mode 100644 lldb/include/lldb/Host/aix/AbstractSocket.h delete mode 100644 lldb/include/lldb/Host/aix/Uio.h delete mode 100644 lldb/source/Host/aix/AbstractSocket.cpp diff --git a/lldb/include/lldb/Host/aix/AbstractSocket.h b/lldb/include/lldb/Host/aix/AbstractSocket.h deleted file mode 100644 index accfd01457a5e..0000000000000 --- a/lldb/include/lldb/Host/aix/AbstractSocket.h +++ /dev/null @@ -1,25 +0,0 @@ -//===-- AbstractSocket.h ----------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef liblldb_AbstractSocket_h_ -#define liblldb_AbstractSocket_h_ - -#include "lldb/Host/posix/DomainSocket.h" - -namespace lldb_private { -class AbstractSocket : public DomainSocket { -public: - AbstractSocket(); - -protected: - size_t GetNameOffset() const override; - void DeleteSocketFile(llvm::StringRef name) override; -}; -} - -#endif // ifndef liblldb_AbstractSocket_h_ diff --git a/lldb/include/lldb/Host/aix/Uio.h b/lldb/include/lldb/Host/aix/Uio.h deleted file mode 100644 index acf79ecc6a1d0..0000000000000 --- a/lldb/include/lldb/Host/aix/Uio.h +++ /dev/null @@ -1,23 +0,0 @@ -//===-- Uio.h ---------------------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef liblldb_Host_aix_Uio_h_ -#define liblldb_Host_aix_Uio_h_ - -#include "lldb/Host/Config.h" -#include - -// We shall provide our own implementation of process_vm_readv if it is not -// present -#if !HAVE_PROCESS_VM_READV -ssize_t process_vm_readv(::pid_t pid, const struct iovec *local_iov, - unsigned long liovcnt, const struct iovec *remote_iov, - unsigned long riovcnt, unsigned long flags); -#endif - -#endif // liblldb_Host_aix_Uio_h_ diff --git a/lldb/source/Host/CMakeLists.txt b/lldb/source/Host/CMakeLists.txt index bb6b5befa16e4..5a14b4c629825 100644 --- a/lldb/source/Host/CMakeLists.txt +++ b/lldb/source/Host/CMakeLists.txt @@ -140,7 +140,6 @@ else() elseif (CMAKE_SYSTEM_NAME MATCHES "AIX") add_host_subdirectory(aix - aix/AbstractSocket.cpp aix/Host.cpp aix/HostInfoAIX.cpp aix/Support.cpp diff --git a/lldb/source/Host/aix/AbstractSocket.cpp b/lldb/source/Host/aix/AbstractSocket.cpp deleted file mode 100644 index fddf78f54f46d..0000000000000 --- a/lldb/source/Host/aix/AbstractSocket.cpp +++ /dev/null @@ -1,20 +0,0 @@ -//===-- AbstractSocket.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 "lldb/Host/aix/AbstractSocket.h" - -#include "llvm/ADT/StringRef.h" - -using namespace lldb; -using namespace lldb_private; - -AbstractSocket::AbstractSocket() : DomainSocket(ProtocolUnixAbstract) {} - -size_t AbstractSocket::GetNameOffset() const { return 1; } - -void AbstractSocket::DeleteSocketFile(llvm::StringRef name) {} >From 25bea9ca48dc458c1dddd72f10a483e76926a04e Mon Sep 17 00:00:00 2001 From: HemangGadhavi Date: Wed, 26 Feb 2025 01:10:15 -0600 Subject: [PATCH 38/46] Resolving coredump issue while attach with library calls --- lldb/include/lldb/Core/ModuleSpec.h | 2 ++ lldb/source/Core/ModuleList.cpp | 12 +++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lldb/include/lldb/Core/ModuleSpec.h b/lldb/include/lldb/Core/ModuleSpec.h index 4fe06412b6b0b..9d79992b48c7e 100644 --- a/lldb/include/lldb/Core/ModuleSpec.h +++ b/lldb/include/lldb/Core/ModuleSpec.h @@ -122,6 +122,8 @@ class ModuleSpec { ConstString &GetObjectName() { return m_object_name; } ConstString GetObjectName() const { return m_object_name; } + + void SetObjectName(ConstString objName) { m_object_name = objName; } uint64_t GetObjectOffset() const { return m_object_offset; } diff --git a/lldb/source/Core/ModuleList.cpp b/lldb/source/Core/ModuleList.cpp index 2b8ccab2406c6..862a2729c1afb 100644 --- a/lldb/source/Core/ModuleList.cpp +++ b/lldb/source/Core/ModuleList.cpp @@ -260,7 +260,17 @@ void ModuleList::ReplaceEquivalent( module_sp->GetArchitecture()); equivalent_module_spec.GetPlatformFileSpec() = module_sp->GetPlatformFileSpec(); - +#ifdef _AIX + // To remove the exact equivalent module, the object name must be + // specified. When the equivalent_module_spec object is created, its + // object name is initially set to NULL. This is because the module_sp's + // GetPath() returns a path in the format (/usr/ccs/libc.a), which does + // not include the object name. As a result, MatchesModuleSpec may return + // true even though the object name is NULL and doesn't match any loaded + // module. To fix this, set the object name of the equivalent_module_spec + // to be the same as the object name of the module_sp. */ + equivalent_module_spec.SetObjectName(module_sp->GetObjectName()); +#endif size_t idx = 0; while (idx < m_modules.size()) { ModuleSP test_module_sp(m_modules[idx]); >From 349ec0064668a0ee1d3af7c2f38fa7427b4b2d36 Mon Sep 17 00:00:00 2001 From: Dhruv Srivastava Date: Fri, 28 Feb 2025 23:44:40 +0530 Subject: [PATCH 39/46] [AIX][Coredump] AIX Coredump debugging Implementation (#25) Creates a general framework to be able to debug an AIX generated coredump file. At this point, we are only supporting 64-bit core files using this general framework. With this implementation, LLDB can recognise and debug any 64-bit AIX coredump file. Most of the generic debugging commands work after this: # bin/lldb --core /home/dhruv/LLDB/tests/core (lldb) target create --core "/home/dhruv/LLDB/tests/core" Core file '/home/dhruv/LLDB/tests/core' (powerpc64) was loaded. (lldb) bt * thread #1, stop reason = SIGSEGV * frame #0: 0x0000000100000940 coretest64`main at core.c:5 frame #1: 0x00000001000004ac coretest64`__start + 116 (lldb) process status Process 18446744071562067991 stopped * thread #1, stop reason = SIGSEGV frame #0: 0x0000000100000940 coretest64`main at core.c:5 2 char *str; 3 int a = 10; 4 str = "GfG"; -> 5 *(str+1) = 'n'; 6 return a; 7 } And others like memory read, image list, image dump sections, disassembly etc --- .../AIX-DYLD/DynamicLoaderAIXDYLD.cpp | 62 ++++- .../AIX-DYLD/DynamicLoaderAIXDYLD.h | 6 + .../Plugins/ObjectFile/AIXCore/CMakeLists.txt | 13 + .../ObjectFile/AIXCore/ObjectFileAIXCore.cpp | 254 ++++++++++++++++++ .../ObjectFile/AIXCore/ObjectFileAIXCore.h | 121 +++++++++ lldb/source/Plugins/ObjectFile/CMakeLists.txt | 1 + lldb/source/Plugins/Process/CMakeLists.txt | 1 + .../Plugins/Process/aix-core/AIXCore.cpp | 116 ++++++++ .../source/Plugins/Process/aix-core/AIXCore.h | 125 +++++++++ .../Plugins/Process/aix-core/CMakeLists.txt | 16 ++ .../Process/aix-core/ProcessAIXCore.cpp | 251 +++++++++++++++++ .../Plugins/Process/aix-core/ProcessAIXCore.h | 100 +++++++ .../aix-core/RegisterContextCoreAIX_ppc64.cpp | 136 ++++++++++ .../aix-core/RegisterContextCoreAIX_ppc64.h | 46 ++++ .../Process/aix-core/ThreadAIXCore.cpp | 127 +++++++++ .../Plugins/Process/aix-core/ThreadAIXCore.h | 110 ++++++++ 16 files changed, 1484 insertions(+), 1 deletion(-) create mode 100644 lldb/source/Plugins/ObjectFile/AIXCore/CMakeLists.txt create mode 100644 lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.cpp create mode 100644 lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.h create mode 100644 lldb/source/Plugins/Process/aix-core/AIXCore.cpp create mode 100644 lldb/source/Plugins/Process/aix-core/AIXCore.h create mode 100644 lldb/source/Plugins/Process/aix-core/CMakeLists.txt create mode 100644 lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp create mode 100644 lldb/source/Plugins/Process/aix-core/ProcessAIXCore.h create mode 100644 lldb/source/Plugins/Process/aix-core/RegisterContextCoreAIX_ppc64.cpp create mode 100644 lldb/source/Plugins/Process/aix-core/RegisterContextCoreAIX_ppc64.h create mode 100644 lldb/source/Plugins/Process/aix-core/ThreadAIXCore.cpp create mode 100644 lldb/source/Plugins/Process/aix-core/ThreadAIXCore.h diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp index 1a98bb9334043..7e44ffbb1c051 100644 --- a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp @@ -228,6 +228,65 @@ void DynamicLoaderAIXDYLD::ResolveExecutableModule( target.SetExecutableModule(module_sp, eLoadDependentsNo); } +bool DynamicLoaderAIXDYLD::IsCoreFile() const { + return !m_process->IsLiveDebugSession(); +} + +void DynamicLoaderAIXDYLD::FillCoreLoaderData(lldb_private::DataExtractor &data, + uint64_t loader_offset, uint64_t loader_size ) { + + static char *buffer = (char *)malloc(loader_size); + struct ld_info ldinfo[64]; + char *buffer_complete; + struct ld_info *ptr; + int i = 0; + + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); + ByteOrder byteorder = data.GetByteOrder(); + data.ExtractBytes(loader_offset, loader_size, eByteOrderBig, buffer); + buffer_complete = buffer + loader_size; + ldinfo[0].ldinfo_next = 1; + + while (ldinfo[i++].ldinfo_next != 0) { + + ptr = (struct ld_info *)buffer; + ldinfo[i].ldinfo_next = ptr->ldinfo_next; + ldinfo[i].ldinfo_flags = ptr->ldinfo_flags; + ldinfo[i].ldinfo_core = ptr->ldinfo_core; + ldinfo[i].ldinfo_textorg = ptr->ldinfo_textorg; + ldinfo[i].ldinfo_textsize = ptr->ldinfo_textsize; + ldinfo[i].ldinfo_dataorg = ptr->ldinfo_dataorg; + ldinfo[i].ldinfo_datasize = ptr->ldinfo_datasize; + + char *filename = &ptr->ldinfo_filename[0]; + char *membername = filename + (strlen(filename) + 1); + strcpy(ldinfo[i].ldinfo_filename, filename); + + buffer += ptr->ldinfo_next; + struct ld_info *ptr2 = &(ldinfo[i]); + char *pathName = ptr2->ldinfo_filename; + char pathWithMember[PATH_MAX] = {0}; + if (strlen(membername) > 0) { + sprintf(pathWithMember, "%s(%s)", pathName, membername); + } else { + sprintf(pathWithMember, "%s", pathName); + } + + FileSpec file(pathWithMember); + ModuleSpec module_spec(file, m_process->GetTarget().GetArchitecture()); + LLDB_LOGF(log, "Module :%s", pathWithMember); + if (ModuleSP module_sp = m_process->GetTarget().GetOrCreateModule(module_spec, true /* notify */)) { + UpdateLoadedSectionsByType(module_sp, LLDB_INVALID_ADDRESS, (lldb::addr_t)ptr2->ldinfo_textorg, false, 1); + UpdateLoadedSectionsByType(module_sp, LLDB_INVALID_ADDRESS, (lldb::addr_t)ptr2->ldinfo_dataorg, false, 2); + // FIXME: .tdata, .bss + } + if (ptr2->ldinfo_next == 0) { + ptr2 = nullptr; + } + } +} + void DynamicLoaderAIXDYLD::DidAttach() { Log *log = GetLog(LLDBLog::DynamicLoader); LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); @@ -361,7 +420,8 @@ void DynamicLoaderAIXDYLD::DidLaunch() { #endif } -Status DynamicLoaderAIXDYLD::CanLoadImage() { return Status(); } +Status DynamicLoaderAIXDYLD::CanLoadImage() { + return Status(); } ThreadPlanSP DynamicLoaderAIXDYLD::GetStepThroughTrampolinePlan(Thread &thread, diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h index 0ffbe688e0069..097f8d048b77f 100644 --- a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h @@ -33,6 +33,9 @@ class DynamicLoaderAIXDYLD : public DynamicLoader { lldb::addr_t module_addr); void OnUnloadModule(lldb::addr_t module_addr); + void FillCoreLoaderData(lldb_private::DataExtractor &data, + uint64_t loader_offset, uint64_t loader_size); + void DidAttach() override; void DidLaunch() override; Status CanLoadImage() override; @@ -49,6 +52,9 @@ class DynamicLoaderAIXDYLD : public DynamicLoader { /// Loads Module from inferior process. void ResolveExecutableModule(lldb::ModuleSP &module_sp); + /// Returns true if the process is for a core file. + bool IsCoreFile() const; + private: std::map m_loaded_modules; }; diff --git a/lldb/source/Plugins/ObjectFile/AIXCore/CMakeLists.txt b/lldb/source/Plugins/ObjectFile/AIXCore/CMakeLists.txt new file mode 100644 index 0000000000000..5656b33a61726 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/AIXCore/CMakeLists.txt @@ -0,0 +1,13 @@ +add_lldb_library(lldbPluginObjectFileAIXCore PLUGIN + ObjectFileAIXCore.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbSymbol + lldbTarget + lldbUtility + lldbPluginProcessUtility + LINK_COMPONENTS + Support + ) diff --git a/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.cpp b/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.cpp new file mode 100644 index 0000000000000..5158fa4e25077 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.cpp @@ -0,0 +1,254 @@ +//===-- ObjectFileAIXCore.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 "ObjectFileAIXCore.h" + +#include +#include +#include +#include + +#include "lldb/Utility/FileSpecList.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Stream.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/XCOFF.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Object/XCOFFObjectFile.h" + +using namespace llvm; +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(ObjectFileAIXCore) + +enum CoreVersion : uint64_t {AIXCORE32 = 0xFEEDDB1, AIXCORE64 = 0xFEEDDB2}; + +bool m_is_core = false; + +// Static methods. +void ObjectFileAIXCore::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + CreateMemoryInstance, GetModuleSpecifications); +} + +void ObjectFileAIXCore::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +ObjectFile *ObjectFileAIXCore::CreateInstance(const lldb::ModuleSP &module_sp, + DataBufferSP data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec *file, + lldb::offset_t file_offset, + lldb::offset_t length) { + + if(m_is_core) + { + + bool mapped_writable = false; + if (!data_sp) { + data_sp = MapFileDataWritable(*file, length, file_offset); + if (!data_sp) + return nullptr; + data_offset = 0; + mapped_writable = true; + } + + assert(data_sp); + + const uint8_t *magic = data_sp->GetBytes() + data_offset; + + // Update the data to contain the entire file if it doesn't already + if (data_sp->GetByteSize() < length) { + data_sp = MapFileDataWritable(*file, length, file_offset); + if (!data_sp) + return nullptr; + data_offset = 0; + mapped_writable = true; + magic = data_sp->GetBytes(); + } + + // If we didn't map the data as writable take ownership of the buffer. + if (!mapped_writable) { + data_sp = std::make_shared(data_sp->GetBytes(), + data_sp->GetByteSize()); + data_offset = 0; + magic = data_sp->GetBytes(); + } + + std::unique_ptr objfile_up(new ObjectFileAIXCore( + module_sp, data_sp, data_offset, file, file_offset, length)); + ArchSpec spec = objfile_up->GetArchitecture(); + objfile_up->SetModulesArchitecture(spec); + return objfile_up.release(); + + } +} + +ObjectFile *ObjectFileAIXCore::CreateMemoryInstance( + const lldb::ModuleSP &module_sp, WritableDataBufferSP data_sp, + const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) { + return nullptr; +} + +size_t ObjectFileAIXCore::GetModuleSpecifications( + const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, lldb::offset_t file_offset, + lldb::offset_t length, lldb_private::ModuleSpecList &specs) { + const size_t initial_count = specs.GetSize(); + + if (ObjectFileAIXCore::MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) { + // Need new ArchType??? + ArchSpec arch_spec = ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); + ModuleSpec spec(file, arch_spec); + spec.GetArchitecture().SetArchitecture(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE, llvm::Triple::AIX); + specs.Append(spec); + } + return specs.GetSize() - initial_count; +} + +static uint32_t AIXCoreHeaderCheckFromMagic(uint32_t magic) { + + Log *log = GetLog(LLDBLog::Modules); + switch (magic) { + case AIXCORE32: + LLDB_LOGF(log, "ObjectFileAIXCore: 32-bit not supported"); + break; + case AIXCORE64: + m_is_core = true; + return 1; + break; + } + return 0; +} + +bool ObjectFileAIXCore::MagicBytesMatch(DataBufferSP &data_sp, + lldb::addr_t data_offset, + lldb::addr_t data_length) { + lldb_private::DataExtractor data; + data.SetData(data_sp, data_offset, data_length); + lldb::offset_t offset = 0; + offset += 4; // Skipping to the coredump version + uint32_t magic = data.GetU32(&offset); + return AIXCoreHeaderCheckFromMagic(magic) != 0; +} + +bool ObjectFileAIXCore::ParseHeader() { + + return false; +} + +ByteOrder ObjectFileAIXCore::GetByteOrder() const { + return eByteOrderBig; +} + +bool ObjectFileAIXCore::IsExecutable() const { + return false; +} + +uint32_t ObjectFileAIXCore::GetAddressByteSize() const { + return 8; +} + +AddressClass ObjectFileAIXCore::GetAddressClass(addr_t file_addr) { + return AddressClass::eUnknown; +} + +lldb::SymbolType ObjectFileAIXCore::MapSymbolType(llvm::object::SymbolRef::Type sym_type) { + if (sym_type == llvm::object::SymbolRef::ST_Function) + return lldb::eSymbolTypeCode; + else if (sym_type == llvm::object::SymbolRef::ST_Data) + return lldb::eSymbolTypeData; + return lldb::eSymbolTypeInvalid; +} + +void ObjectFileAIXCore::ParseSymtab(Symtab &lldb_symtab) { +} + +bool ObjectFileAIXCore::IsStripped() { + return false; +} + +void ObjectFileAIXCore::CreateSections(SectionList &unified_section_list) { +} + +void ObjectFileAIXCore::Dump(Stream *s) { +} + +ArchSpec ObjectFileAIXCore::GetArchitecture() { + ArchSpec arch_spec = ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); + return arch_spec; +} + +UUID ObjectFileAIXCore::GetUUID() { + return UUID(); +} + +uint32_t ObjectFileAIXCore::GetDependentModules(FileSpecList &files) { + + auto original_size = files.GetSize(); + return files.GetSize() - original_size; +} + +Address ObjectFileAIXCore::GetImageInfoAddress(Target *target) { + return Address(); +} + +lldb_private::Address ObjectFileAIXCore::GetBaseAddress() { + return lldb_private::Address(); +} +ObjectFile::Type ObjectFileAIXCore::CalculateType() { + return eTypeCoreFile; +} + +ObjectFile::Strata ObjectFileAIXCore::CalculateStrata() { + return eStrataUnknown; +} + +std::vector +ObjectFileAIXCore::GetLoadableData(Target &target) { + std::vector loadables; + return loadables; +} + +lldb::WritableDataBufferSP +ObjectFileAIXCore::MapFileDataWritable(const FileSpec &file, uint64_t Size, + uint64_t Offset) { + return FileSystem::Instance().CreateWritableDataBuffer(file.GetPath(), Size, + Offset); +} + +ObjectFileAIXCore::ObjectFileAIXCore(const lldb::ModuleSP &module_sp, + DataBufferSP data_sp, lldb::offset_t data_offset, + const FileSpec *file, lldb::offset_t file_offset, + lldb::offset_t length) + : ObjectFile(module_sp, file, file_offset, length, data_sp, data_offset) + { + if (file) + m_file = *file; +} + +ObjectFileAIXCore::ObjectFileAIXCore(const lldb::ModuleSP &module_sp, + DataBufferSP header_data_sp, + const lldb::ProcessSP &process_sp, + addr_t header_addr) + : ObjectFile(module_sp, process_sp, header_addr, header_data_sp) + { +} diff --git a/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.h b/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.h new file mode 100644 index 0000000000000..5dbd78d919bb6 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.h @@ -0,0 +1,121 @@ +//===-- ObjectFileAIXCore.h --------------------------------------- -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_OBJECTFILE_AIXCORE_OBJECTFILEAIXCORE_H +#define LLDB_SOURCE_PLUGINS_OBJECTFILE_AIXCORE_OBJECTFILEAIXCORE_H + +#include + +#include + +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/UUID.h" +#include "lldb/lldb-private.h" +#include "llvm/Object/XCOFFObjectFile.h" + +/// \class ObjectFileAIXCore +/// Generic AIX CORE object file reader. +/// +/// This class provides a generic AIX Core (32/64 bit) reader plugin implementing +/// the ObjectFile protocol. +class ObjectFileAIXCore : public lldb_private::ObjectFile { +public: + // Static Functions + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "aix-core-obj"; } + + static llvm::StringRef GetPluginDescriptionStatic() { + return "AIX core object file reader."; + } + + static lldb_private::ObjectFile * + CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP data_sp, + lldb::offset_t data_offset, const lldb_private::FileSpec *file, + lldb::offset_t file_offset, lldb::offset_t length); + + static lldb_private::ObjectFile *CreateMemoryInstance( + const lldb::ModuleSP &module_sp, lldb::WritableDataBufferSP data_sp, + const lldb::ProcessSP &process_sp, lldb::addr_t header_addr); + + static size_t GetModuleSpecifications(const lldb_private::FileSpec &file, + lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb_private::ModuleSpecList &specs); + + static bool MagicBytesMatch(lldb::DataBufferSP &data_sp, lldb::addr_t offset, + lldb::addr_t length); + + static lldb::SymbolType MapSymbolType(llvm::object::SymbolRef::Type sym_type); + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + // ObjectFile Protocol. + bool ParseHeader() override; + + lldb::ByteOrder GetByteOrder() const override; + + bool IsExecutable() const override; + + uint32_t GetAddressByteSize() const override; + + lldb_private::AddressClass GetAddressClass(lldb::addr_t file_addr) override; + + void ParseSymtab(lldb_private::Symtab &symtab) override; + + bool IsStripped() override; + + void CreateSections(lldb_private::SectionList &unified_section_list) override; + + void Dump(lldb_private::Stream *s) override; + + lldb_private::ArchSpec GetArchitecture() override; + + lldb_private::UUID GetUUID() override; + + uint32_t GetDependentModules(lldb_private::FileSpecList &files) override; + + lldb_private::Address + GetImageInfoAddress(lldb_private::Target *target) override; + lldb_private::Address GetBaseAddress() override; + + ObjectFile::Type CalculateType() override; + + ObjectFile::Strata CalculateStrata() override; + + ObjectFileAIXCore(const lldb::ModuleSP &module_sp, lldb::DataBufferSP data_sp, + lldb::offset_t data_offset, const lldb_private::FileSpec *file, + lldb::offset_t offset, lldb::offset_t length); + + ObjectFileAIXCore(const lldb::ModuleSP &module_sp, + lldb::DataBufferSP header_data_sp, + const lldb::ProcessSP &process_sp, lldb::addr_t header_addr); + +protected: + + static bool ParseAIXCoreHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset_ptr + ); + + std::vector + GetLoadableData(lldb_private::Target &target) override; + + static lldb::WritableDataBufferSP + MapFileDataWritable(const lldb_private::FileSpec &file, uint64_t Size, + uint64_t Offset); + +}; + +#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_AIXCORE_OBJECTFILEAIXCORE_H diff --git a/lldb/source/Plugins/ObjectFile/CMakeLists.txt b/lldb/source/Plugins/ObjectFile/CMakeLists.txt index 7abd0c96f4fd7..1605356fdb7f1 100644 --- a/lldb/source/Plugins/ObjectFile/CMakeLists.txt +++ b/lldb/source/Plugins/ObjectFile/CMakeLists.txt @@ -9,3 +9,4 @@ add_subdirectory(PECOFF) add_subdirectory(XCOFF) add_subdirectory(Placeholder) add_subdirectory(wasm) +add_subdirectory(AIXCore) diff --git a/lldb/source/Plugins/Process/CMakeLists.txt b/lldb/source/Plugins/Process/CMakeLists.txt index 058b4b9ad2157..0b66ea18c82ce 100644 --- a/lldb/source/Plugins/Process/CMakeLists.txt +++ b/lldb/source/Plugins/Process/CMakeLists.txt @@ -24,3 +24,4 @@ add_subdirectory(elf-core) add_subdirectory(mach-core) add_subdirectory(minidump) add_subdirectory(FreeBSDKernel) +add_subdirectory(aix-core) diff --git a/lldb/source/Plugins/Process/aix-core/AIXCore.cpp b/lldb/source/Plugins/Process/aix-core/AIXCore.cpp new file mode 100644 index 0000000000000..95e47b4d8be53 --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/AIXCore.cpp @@ -0,0 +1,116 @@ +//===-- AIXCore.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 + +#include "lldb/Core/Section.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" + +#include "AIXCore.h" + +using namespace AIXCORE; +using namespace lldb; +using namespace lldb_private; + +AIXCore64Header::AIXCore64Header() { memset(this, 0, sizeof(AIXCore64Header)); } + + +bool AIXCore64Header::ParseRegisterContext(lldb_private::DataExtractor &data, + lldb::offset_t *offset) { + // The data is arranged in this order in this coredump file + // so we have to fetch in this exact order. But need to change + // the context structure order according to Infos_ppc64 + for(int i = 0; i < 32; i++) + Fault.context.gpr[i] = data.GetU64(offset); + Fault.context.msr = data.GetU64(offset); + Fault.context.pc = data.GetU64(offset); + Fault.context.lr = data.GetU64(offset); + Fault.context.ctr = data.GetU64(offset); + Fault.context.cr = data.GetU32(offset); + Fault.context.xer = data.GetU32(offset); + Fault.context.fpscr = data.GetU32(offset); + Fault.context.fpscrx = data.GetU32(offset); + Fault.context.except[0] = data.GetU64(offset); + for(int i = 0; i < 32; i++) + Fault.context.fpr[i] = data.GetU64(offset); + Fault.context.fpeu = data.GetU8(offset); + Fault.context.fpinfo = data.GetU8(offset); + Fault.context.fpscr24_31 = data.GetU8(offset); + Fault.context.pad[0] = data.GetU8(offset); + Fault.context.excp_type = data.GetU32(offset); + + return true; +} +bool AIXCore64Header::ParseThreadContext(lldb_private::DataExtractor &data, + lldb::offset_t *offset) { + + lldb::offset_t offset_to_regctx = *offset; + offset_to_regctx += sizeof(thrdentry64); + Fault.thread.ti_tid = data.GetU64(offset); + Fault.thread.ti_pid = data.GetU32(offset); + int ret = ParseRegisterContext(data, &offset_to_regctx); + return true; +} + +bool AIXCore64Header::ParseUserData(lldb_private::DataExtractor &data, + lldb::offset_t *offset) { + User.process.pi_pid = data.GetU32(offset); + User.process.pi_ppid = data.GetU32(offset); + User.process.pi_sid = data.GetU32(offset); + User.process.pi_pgrp = data.GetU32(offset); + User.process.pi_uid = data.GetU32(offset); + User.process.pi_suid = data.GetU32(offset); + + *offset += 76; + + ByteOrder byteorder = data.GetByteOrder(); + size_t size = 33; + data.ExtractBytes(*offset, size, byteorder, User.process.pi_comm); + offset += size; + + return true; +} + +bool AIXCore64Header::ParseCoreHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset) { + + SignalNum = data.GetU8(offset); + Flag = data.GetU8(offset); + Entries = data.GetU16(offset); + Version = data.GetU32(offset); + FDInfo = data.GetU64(offset); + + LoaderOffset = data.GetU64(offset); + LoaderSize = data.GetU64(offset); + NumberOfThreads = data.GetU32(offset); + Reserved0 = data.GetU32(offset); + ThreadContextOffset = data.GetU64(offset); + NumSegRegion = data.GetU64(offset); + SegRegionOffset = data.GetU64(offset); + StackOffset = data.GetU64(offset); + StackBaseAddr = data.GetU64(offset); + StackSize = data.GetU64(offset); + DataRegionOffset = data.GetU64(offset); + DataBaseAddr = data.GetU64(offset); + DataSize = data.GetU64(offset); + + *offset += 104; + lldb::offset_t offset_to_user = (*offset + sizeof(ThreadContext64)); + int ret = 0; + ret = ParseThreadContext(data, offset); + ret = ParseUserData(data, &offset_to_user); + + return true; + +} + diff --git a/lldb/source/Plugins/Process/aix-core/AIXCore.h b/lldb/source/Plugins/Process/aix-core/AIXCore.h new file mode 100644 index 0000000000000..3d78d5e92c7ab --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/AIXCore.h @@ -0,0 +1,125 @@ +//===-- AIXCore.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Notes about AIX Process core dumps: +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_AIXCORE_H +#define LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_AIXCORE_H + +#include "llvm/ADT/StringRef.h" +#include +#include +#include + +#include +#include + +namespace AIXCORE { + +struct RegContext { + // The data is arranged in order as filled by AIXCore.cpp in this coredump file + // so we have to fetch in that exact order, refer there. + // But need to change + // the context structure in order according to Infos_ppc64 + uint64_t gpr[32]; /* 64-bit gprs */ + unsigned long pc; /* msr */ + unsigned long msr; /* iar */ + unsigned long origr3; /* iar */ + unsigned long ctr; /* CTR */ + unsigned long lr; /* LR */ + unsigned long xer; /* XER */ + unsigned long cr; /* CR */ + unsigned long softe; /* CR */ + unsigned long trap; /* CR */ + unsigned int fpscr; /* floating pt status reg */ + unsigned int fpscrx; /* software ext to fpscr */ + unsigned long except[1]; /* exception address */ + double fpr[32]; /* floating pt regs */ + char fpeu; /* floating pt ever used */ + char fpinfo; /* floating pt info */ + char fpscr24_31; /* bits 24-31 of 64-bit FPSCR */ + char pad[1]; + int excp_type; /* exception type */ +}; + + struct ThreadContext64 { + struct thrdentry64 thread; + struct RegContext context; + }; + + struct UserData { + + struct procentry64 process; + unsigned long long reserved[16]; + }; + + struct AIXCore64Header { + + int8_t SignalNum; /* signal number (cause of error) */ + int8_t Flag; /* flag to describe core dump type */ + uint16_t Entries; /* number of core dump modules */ + uint32_t Version; /* core file format number */ + uint64_t FDInfo; /* offset to fd region in file */ + + uint64_t LoaderOffset; /* offset to loader region in file */ + uint64_t LoaderSize; /* size of loader region */ + + uint32_t NumberOfThreads ; /* number of elements in thread table */ + uint32_t Reserved0; /* Padding */ + uint64_t ThreadContextOffset; /* offset to thread context table */ + + uint64_t NumSegRegion; /* n of elements in segregion */ + uint64_t SegRegionOffset; /* offset to start of segregion table */ + + uint64_t StackOffset; /* offset of user stack in file */ + uint64_t StackBaseAddr; /* base address of user stack region */ + uint64_t StackSize; /* size of user stack region */ + + uint64_t DataRegionOffset; /* offset to user data region */ + uint64_t DataBaseAddr; /* base address of user data region */ + uint64_t DataSize; /* size of user data region */ + uint64_t SDataBase; /* base address of sdata region */ + uint64_t SDataSize; /* size of sdata region */ + + uint64_t NumVMRegions; /* number of anonymously mapped areas */ + uint64_t VMOffset; /* offset to start of vm_infox table */ + + int32_t ProcessorImplementation; /* processor implementation */ + uint32_t NumElementsCTX; /* n of elements in extended ctx table*/ + uint64_t CPRSOffset; /* Checkpoint/Restart offset */ + uint64_t ExtendedContextOffset; /* extended context offset */ + uint64_t OffsetUserKey; /* Offset to user-key exception data */ + uint64_t OffsetLoaderTLS; /* offset to the loader region in file + when a process uses TLS data */ + uint64_t TLSLoaderSize; /* size of the above loader region */ + uint64_t ExtendedProcEntry; /* Extended procentry64 information */ + uint64_t Reserved[2]; + + struct ThreadContext64 Fault; + + struct UserData User; + + AIXCore64Header(); + + bool ParseCoreHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset); + bool ParseThreadContext(lldb_private::DataExtractor &data, + lldb::offset_t *offset); + bool ParseUserData(lldb_private::DataExtractor &data, + lldb::offset_t *offset); + bool ParseRegisterContext(lldb_private::DataExtractor &data, + lldb::offset_t *offset); + bool ParseLoaderData(lldb_private::DataExtractor &data, + lldb::offset_t *offset); + + }; + + +} + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_AIXCORE_H diff --git a/lldb/source/Plugins/Process/aix-core/CMakeLists.txt b/lldb/source/Plugins/Process/aix-core/CMakeLists.txt new file mode 100644 index 0000000000000..347717a362491 --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/CMakeLists.txt @@ -0,0 +1,16 @@ +add_definitions("-D_ALL_SOURCE") + +add_lldb_library(lldbPluginProcessAIXCore PLUGIN + ProcessAIXCore.cpp + AIXCore.cpp + ThreadAIXCore.cpp + RegisterContextCoreAIX_ppc64.cpp + + LINK_LIBS + lldbCore + lldbTarget + lldbPluginProcessUtility + LINK_COMPONENTS + BinaryFormat + Support + ) diff --git a/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp b/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp new file mode 100644 index 0000000000000..9300aa14ac4db --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp @@ -0,0 +1,251 @@ +//===-- ProcessAIXCore.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 + +#include +#include + +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" + +#include "llvm/Support/Threading.h" +#include "Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h" + +#include "ProcessAIXCore.h" +#include "AIXCore.h" +#include "ThreadAIXCore.h" + +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(ProcessAIXCore) + +llvm::StringRef ProcessAIXCore::GetPluginDescriptionStatic() { + return "AIX core dump plug-in."; +} + +void ProcessAIXCore::Initialize() { + static llvm::once_flag g_once_flag; + + llvm::call_once(g_once_flag, []() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); + }); +} + +void ProcessAIXCore::Terminate() { + PluginManager::UnregisterPlugin(ProcessAIXCore::CreateInstance); +} + +lldb::ProcessSP ProcessAIXCore::CreateInstance(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec *crash_file, + bool can_connect) { + lldb::ProcessSP process_sp; + if (crash_file && !can_connect) { + const size_t header_size = sizeof(AIXCORE::AIXCore64Header); + + auto data_sp = FileSystem::Instance().CreateDataBuffer( + crash_file->GetPath(), header_size, 0); + + if (data_sp && data_sp->GetByteSize() == header_size) { + AIXCORE::AIXCore64Header aixcore_header; + DataExtractor data(data_sp, lldb::eByteOrderBig, 4); + lldb::offset_t data_offset = 0; + if(aixcore_header.ParseCoreHeader(data, &data_offset)) { + process_sp = std::make_shared(target_sp, listener_sp, + *crash_file); + } + } + + } + return process_sp; +} + +// ProcessAIXCore constructor +ProcessAIXCore::ProcessAIXCore(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec &core_file) + : PostMortemProcess(target_sp, listener_sp, core_file) {} + +// Destructor +ProcessAIXCore::~ProcessAIXCore() { + Clear(); + // We need to call finalize on the process before destroying ourselves to + // make sure all of the broadcaster cleanup goes as planned. If we destruct + // this class, then Process::~Process() might have problems trying to fully + // destroy the broadcaster. + Finalize(true /* destructing */); +} + +bool ProcessAIXCore::CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) { + + if (!m_core_module_sp && FileSystem::Instance().Exists(m_core_file)) { + ModuleSpec core_module_spec(m_core_file, target_sp->GetArchitecture()); + Status error(ModuleList::GetSharedModule(core_module_spec, m_core_module_sp, + nullptr, nullptr, nullptr)); + if (m_core_module_sp) { + ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); + if (core_objfile && core_objfile->GetType() == ObjectFile::eTypeCoreFile){ + return true; + } + } + } + return false; + +} + +ArchSpec ProcessAIXCore::GetArchitecture() { + + ArchSpec arch = m_core_module_sp->GetObjectFile()->GetArchitecture(); + + ArchSpec target_arch = GetTarget().GetArchitecture(); + arch.MergeFrom(target_arch); + + return arch; +} + +lldb_private::DynamicLoader *ProcessAIXCore::GetDynamicLoader() { + if (m_dyld_up.get() == nullptr) { + m_dyld_up.reset(DynamicLoader::FindPlugin( + this, DynamicLoaderAIXDYLD::GetPluginNameStatic())); + } + return m_dyld_up.get(); +} + +void ProcessAIXCore::ParseAIXCoreFile() { + + Log *log = GetLog(LLDBLog::Process); + AIXSigInfo siginfo; + ThreadData thread_data; + + const lldb_private::UnixSignals &unix_signals = *GetUnixSignals(); + const ArchSpec &arch = GetArchitecture(); + + siginfo.Parse(m_aixcore_header, arch, unix_signals); + thread_data.siginfo = siginfo; + SetID(m_aixcore_header.User.process.pi_pid); + + thread_data.name.assign (m_aixcore_header.User.process.pi_comm, + strnlen (m_aixcore_header.User.process.pi_comm, + sizeof (m_aixcore_header.User.process.pi_comm))); + + lldb::DataBufferSP data_buffer_sp(new lldb_private::DataBufferHeap(sizeof(m_aixcore_header.Fault.context), 0)); + + memcpy(static_cast(const_cast(data_buffer_sp->GetBytes())), + &m_aixcore_header.Fault.context, sizeof(m_aixcore_header.Fault.context)); + + lldb_private::DataExtractor data(data_buffer_sp, lldb::eByteOrderBig, 8); + + thread_data.gpregset = DataExtractor(data, 0, sizeof(m_aixcore_header.Fault.context)); + m_thread_data.push_back(thread_data); + LLDB_LOGF(log, "ProcessAIXCore: Parsing Complete!"); + +} + +// Process Control +Status ProcessAIXCore::DoLoadCore() { + + Status error; + if (!m_core_module_sp) { + error = Status::FromErrorString("invalid core module"); + return error; + } + + FileSpec file = m_core_module_sp->GetObjectFile()->GetFileSpec(); + + if (file) { + const size_t header_size = sizeof(AIXCORE::AIXCore64Header); + auto data_sp = FileSystem::Instance().CreateDataBuffer( + file.GetPath(), -1, 0); + if (data_sp && data_sp->GetByteSize() != 0) { + + DataExtractor data(data_sp, lldb::eByteOrderBig, 4); + lldb::offset_t data_offset = 0; + m_aixcore_header.ParseCoreHeader(data, &data_offset); + auto dyld = static_cast(GetDynamicLoader()); + dyld->FillCoreLoaderData(data, m_aixcore_header.LoaderOffset, + m_aixcore_header.LoaderSize); + + } else { + error = Status::FromErrorString("invalid data"); + return error; + } + } else { + error = Status::FromErrorString("invalid file"); + return error; + } + + m_thread_data_valid = true; + ParseAIXCoreFile(); + ArchSpec arch(m_core_module_sp->GetArchitecture()); + + ArchSpec target_arch = GetTarget().GetArchitecture(); + ArchSpec core_arch(m_core_module_sp->GetArchitecture()); + target_arch.MergeFrom(core_arch); + GetTarget().SetArchitecture(target_arch); + + lldb::ModuleSP exe_module_sp = GetTarget().GetExecutableModule(); + if (!exe_module_sp) { + ModuleSpec exe_module_spec; + exe_module_spec.GetArchitecture() = arch; + exe_module_spec.GetFileSpec().SetFile(m_aixcore_header.User.process.pi_comm, + FileSpec::Style::native); + exe_module_sp = GetTarget().GetOrCreateModule(exe_module_spec, true); + GetTarget().SetExecutableModule(exe_module_sp, eLoadDependentsNo); + } + + return error; +} + +bool ProcessAIXCore::DoUpdateThreadList(ThreadList &old_thread_list, + ThreadList &new_thread_list) +{ + const ThreadData &td = m_thread_data[0]; + + lldb::ThreadSP thread_sp = + std::make_shared(*this, td); + new_thread_list.AddThread(thread_sp); + + return true; +} + +void ProcessAIXCore::RefreshStateAfterStop() {} + +// Process Memory +size_t ProcessAIXCore::ReadMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) { + if (lldb::ABISP abi_sp = GetABI()) + addr = abi_sp->FixAnyAddress(addr); + + // Don't allow the caching that lldb_private::Process::ReadMemory does since + // in core files we have it all cached our our core file anyway. + return DoReadMemory(addr, buf, size, error); +} + +size_t ProcessAIXCore::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) { return 0; } + +Status ProcessAIXCore::DoGetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo ®ion_info) { + return Status(); +} + +Status ProcessAIXCore::DoDestroy() { return Status(); } diff --git a/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.h b/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.h new file mode 100644 index 0000000000000..9880c491689ca --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.h @@ -0,0 +1,100 @@ +//===-- ProcessAIXCore.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Notes about AIX Process core dumps: +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_PROCESSAIXCORE_H +#define LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_PROCESSAIXCORE_H + +#include +#include + +#include "lldb/Target/PostMortemProcess.h" +#include "lldb/Utility/Status.h" +#include "lldb/Target/Process.h" +#include "AIXCore.h" +#include "ThreadAIXCore.h" + +struct ThreadData; + +class ProcessAIXCore : public lldb_private::PostMortemProcess { +public: + // Constructors and Destructors + static lldb::ProcessSP + CreateInstance(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, + const lldb_private::FileSpec *crash_file_path, + bool can_connect); + + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "aix-core"; } + + static llvm::StringRef GetPluginDescriptionStatic(); + + // Constructors and Destructors + ProcessAIXCore(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, + const lldb_private::FileSpec &core_file); + + ~ProcessAIXCore() override; + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + // Process Control + lldb_private::Status DoDestroy() override; + + lldb_private::Status WillResume() override { + return lldb_private::Status::FromErrorStringWithFormatv( + "error: {0} does not support resuming processes", GetPluginName()); + } + + bool WarnBeforeDetach() const override { return false; } + + lldb_private::ArchSpec GetArchitecture(); + + bool CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) override; + + // Creating a new process, or attaching to an existing one + lldb_private::Status DoLoadCore() override; + + bool DoUpdateThreadList(lldb_private::ThreadList &old_thread_list, + lldb_private::ThreadList &new_thread_list) override; + + lldb_private::Status + DoGetMemoryRegionInfo(lldb::addr_t load_addr, + lldb_private::MemoryRegionInfo ®ion_info) override; + + void RefreshStateAfterStop() override; + + lldb_private::DynamicLoader *GetDynamicLoader() override; + + // Process Memory + size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size, + lldb_private::Status &error) override; + + size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + lldb_private::Status &error) override; + + void ParseAIXCoreFile(); + + +private: + lldb::ModuleSP m_core_module_sp; + std::string m_dyld_plugin_name; + + // True if m_thread_contexts contains valid entries + bool m_thread_data_valid = false; + AIXCORE::AIXCore64Header m_aixcore_header; + + std::vector m_thread_data; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_PROCESSAIXCORE_H diff --git a/lldb/source/Plugins/Process/aix-core/RegisterContextCoreAIX_ppc64.cpp b/lldb/source/Plugins/Process/aix-core/RegisterContextCoreAIX_ppc64.cpp new file mode 100644 index 0000000000000..b243017bf9a2a --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/RegisterContextCoreAIX_ppc64.cpp @@ -0,0 +1,136 @@ +//===-- RegisterContextCoreAIX_ppc64.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 "RegisterContextCoreAIX_ppc64.h" + +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/RegisterValue.h" + +#include "Plugins/Process/Utility/lldb-ppc64le-register-enums.h" +#include "Plugins/Process/elf-core/RegisterUtilities.h" + +#include + +using namespace lldb_private; + +RegisterContextCoreAIX_ppc64::RegisterContextCoreAIX_ppc64( + Thread &thread, RegisterInfoInterface *register_info, + const DataExtractor &gpregset) + : RegisterContextPOSIX_ppc64le(thread, 0, register_info) { + m_gpr_buffer = std::make_shared(gpregset.GetDataStart(), + gpregset.GetByteSize()); + m_gpr.SetData(m_gpr_buffer); + m_gpr.SetByteOrder(gpregset.GetByteOrder()); + + // This Code is for Registers like FPR, VSR, VMX and is disabled right now. + // It will be implemented as per need. + + /* ArchSpec arch = register_info->GetTargetArchitecture(); + DataExtractor fpregset;// = getRegset(notes, arch.GetTriple(), FPR_Desc); + m_fpr_buffer = std::make_shared(fpregset.GetDataStart(), + fpregset.GetByteSize()); + m_fpr.SetData(m_fpr_buffer); + m_fpr.SetByteOrder(fpregset.GetByteOrder()); + + DataExtractor vmxregset;// = getRegset(notes, arch.GetTriple(), PPC_VMX_Desc); + m_vmx_buffer = std::make_shared(vmxregset.GetDataStart(), + vmxregset.GetByteSize()); + m_vmx.SetData(m_vmx_buffer); + m_vmx.SetByteOrder(vmxregset.GetByteOrder()); + + DataExtractor vsxregset;// = getRegset(notes, arch.GetTriple(), PPC_VSX_Desc); + m_vsx_buffer = std::make_shared(vsxregset.GetDataStart(), + vsxregset.GetByteSize()); + m_vsx.SetData(m_vsx_buffer); + m_vsx.SetByteOrder(vsxregset.GetByteOrder());*/ +} + +size_t RegisterContextCoreAIX_ppc64::GetFPRSize() const { + return k_num_fpr_registers_ppc64le * sizeof(uint64_t); +} + +size_t RegisterContextCoreAIX_ppc64::GetVMXSize() const { + return (k_num_vmx_registers_ppc64le - 1) * sizeof(uint64_t) * 2 + + sizeof(uint32_t); +} + +size_t RegisterContextCoreAIX_ppc64::GetVSXSize() const { + return k_num_vsx_registers_ppc64le * sizeof(uint64_t) * 2; +} + +bool RegisterContextCoreAIX_ppc64::ReadRegister( + const RegisterInfo *reg_info, RegisterValue &value) { + lldb::offset_t offset = reg_info->byte_offset; + + if (IsFPR(reg_info->kinds[lldb::eRegisterKindLLDB])) { + uint64_t v; + offset -= GetGPRSize(); + offset = m_fpr.CopyData(offset, reg_info->byte_size, &v); + + if (offset == reg_info->byte_size) { + value.SetBytes(&v, reg_info->byte_size, m_fpr.GetByteOrder()); + return true; + } + } else if (IsVMX(reg_info->kinds[lldb::eRegisterKindLLDB])) { + uint32_t v[4]; + offset -= GetGPRSize() + GetFPRSize(); + offset = m_vmx.CopyData(offset, reg_info->byte_size, &v); + + if (offset == reg_info->byte_size) { + value.SetBytes(v, reg_info->byte_size, m_vmx.GetByteOrder()); + return true; + } + } else if (IsVSX(reg_info->kinds[lldb::eRegisterKindLLDB])) { + uint32_t v[4]; + lldb::offset_t tmp_offset; + offset -= GetGPRSize() + GetFPRSize() + GetVMXSize(); + + if (offset < GetVSXSize() / 2) { + tmp_offset = m_vsx.CopyData(offset / 2, reg_info->byte_size / 2, &v); + + if (tmp_offset != reg_info->byte_size / 2) { + return false; + } + + uint8_t *dst = (uint8_t *)&v + sizeof(uint64_t); + tmp_offset = m_fpr.CopyData(offset / 2, reg_info->byte_size / 2, dst); + + if (tmp_offset != reg_info->byte_size / 2) { + return false; + } + + value.SetBytes(&v, reg_info->byte_size, m_vsx.GetByteOrder()); + return true; + } else { + offset = + m_vmx.CopyData(offset - GetVSXSize() / 2, reg_info->byte_size, &v); + if (offset == reg_info->byte_size) { + value.SetBytes(v, reg_info->byte_size, m_vmx.GetByteOrder()); + return true; + } + } + } else { + uint64_t v = m_gpr.GetMaxU64(&offset, reg_info->byte_size); + + if (offset == reg_info->byte_offset + reg_info->byte_size) { + if (reg_info->byte_size < sizeof(v)) + value = (uint32_t)v; + else + value = v; + return true; + } + } + + return false; +} + +bool RegisterContextCoreAIX_ppc64::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue &value) { + return false; +} diff --git a/lldb/source/Plugins/Process/aix-core/RegisterContextCoreAIX_ppc64.h b/lldb/source/Plugins/Process/aix-core/RegisterContextCoreAIX_ppc64.h new file mode 100644 index 0000000000000..8f1f71ce8d884 --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/RegisterContextCoreAIX_ppc64.h @@ -0,0 +1,46 @@ +//===-- RegisterContextCoreAIX_ppc64.h ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_REGISTERCONTEXTAIXCORE_PPC64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_REGISTERCONTEXTAIXCORE_PPC64_H + +#include "Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.h" +#include "lldb/Utility/DataExtractor.h" + +class RegisterContextCoreAIX_ppc64 : public RegisterContextPOSIX_ppc64le { +public: + RegisterContextCoreAIX_ppc64( + lldb_private::Thread &thread, + lldb_private::RegisterInfoInterface *register_info, + const lldb_private::DataExtractor &gpregset); + + bool ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value) override; + + bool WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value) override; + +protected: + size_t GetFPRSize() const; + + size_t GetVMXSize() const; + + size_t GetVSXSize() const; + +private: + lldb::DataBufferSP m_gpr_buffer; + lldb::DataBufferSP m_fpr_buffer; + lldb::DataBufferSP m_vmx_buffer; + lldb::DataBufferSP m_vsx_buffer; + lldb_private::DataExtractor m_gpr; + lldb_private::DataExtractor m_fpr; + lldb_private::DataExtractor m_vmx; + lldb_private::DataExtractor m_vsx; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_REGISTERCONTEXTAIXCORE_PPC64_H diff --git a/lldb/source/Plugins/Process/aix-core/ThreadAIXCore.cpp b/lldb/source/Plugins/Process/aix-core/ThreadAIXCore.cpp new file mode 100644 index 0000000000000..979e5199fe24d --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/ThreadAIXCore.cpp @@ -0,0 +1,127 @@ +//===-- ThreadAIXCore.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 "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Target/Unwind.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/ProcessInfo.h" + +#include "Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h" +#include "Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.h" +#include "Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.h" +#include "Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.h" +#include "RegisterContextCoreAIX_ppc64.h" + +#include "ProcessAIXCore.h" +#include "AIXCore.h" +#include "ThreadAIXCore.h" + +#include +#include + +using namespace lldb; +using namespace lldb_private; + +// Construct a Thread object with given data +ThreadAIXCore::ThreadAIXCore(Process &process, const ThreadData &td) + : Thread(process, td.tid), m_thread_name(td.name), m_thread_reg_ctx_sp(), + m_gpregset_data(td.gpregset), + m_siginfo(std::move(td.siginfo)) {} + +ThreadAIXCore::~ThreadAIXCore() { DestroyThread(); } + +void ThreadAIXCore::RefreshStateAfterStop() { + GetRegisterContext()->InvalidateIfNeeded(false); +} + +RegisterContextSP ThreadAIXCore::GetRegisterContext() { + if (!m_reg_context_sp) { + m_reg_context_sp = CreateRegisterContextForFrame(nullptr); + } + return m_reg_context_sp; +} + +RegisterContextSP +ThreadAIXCore::CreateRegisterContextForFrame(StackFrame *frame) { + RegisterContextSP reg_ctx_sp; + uint32_t concrete_frame_idx = 0; + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex(); + + bool is_linux = false; + if (concrete_frame_idx == 0) { + if (m_thread_reg_ctx_sp) + return m_thread_reg_ctx_sp; + + ProcessAIXCore *process = static_cast(GetProcess().get()); + ArchSpec arch = process->GetArchitecture(); + RegisterInfoInterface *reg_interface = nullptr; + + switch (arch.GetMachine()) { + case llvm::Triple::ppc64: + reg_interface = new RegisterInfoPOSIX_ppc64le(arch); + m_thread_reg_ctx_sp = std::make_shared( + *this, reg_interface, m_gpregset_data); + break; + default: + break; + } + reg_ctx_sp = m_thread_reg_ctx_sp; + } else { + reg_ctx_sp = GetUnwinder().CreateRegisterContextForFrame(frame); + } + return reg_ctx_sp; +} + +bool ThreadAIXCore::CalculateStopInfo() { + ProcessSP process_sp(GetProcess()); + if (!process_sp) + return false; + + lldb::UnixSignalsSP unix_signals_sp(process_sp->GetUnixSignals()); + if (!unix_signals_sp) + return false; + + const char *sig_description; + std::string description = m_siginfo.GetDescription(*unix_signals_sp); + if (description.empty()) + sig_description = nullptr; + else + sig_description = description.c_str(); + + SetStopInfo(StopInfo::CreateStopReasonWithSignal( + *this, m_siginfo.si_signo, sig_description, m_siginfo.si_code)); + + SetStopInfo(m_stop_info_sp); + return true; +} + +void AIXSigInfo::Parse(const AIXCORE::AIXCore64Header data, const ArchSpec &arch, + const lldb_private::UnixSignals &unix_signals) { + si_signo = data.SignalNum; + sigfault.si_addr = data.Fault.context.pc; +} + +AIXSigInfo::AIXSigInfo() { memset(this, 0, sizeof(AIXSigInfo)); } + +size_t AIXSigInfo::GetSize(const lldb_private::ArchSpec &arch) { + return sizeof(AIXSigInfo); +} + +std::string AIXSigInfo::GetDescription( + const lldb_private::UnixSignals &unix_signals) const { + return unix_signals.GetSignalDescription(si_signo, 0, + sigfault.si_addr); + +} diff --git a/lldb/source/Plugins/Process/aix-core/ThreadAIXCore.h b/lldb/source/Plugins/Process/aix-core/ThreadAIXCore.h new file mode 100644 index 0000000000000..9ee157e9b2b9b --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/ThreadAIXCore.h @@ -0,0 +1,110 @@ +//===-- ThreadAIXCore.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_THREADAIXCORE_H +#define LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_THREADAIXCORE_H + +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataExtractor.h" +#include "llvm/ADT/DenseMap.h" +#include +#include +#include "ProcessAIXCore.h" +#include "AIXCore.h" +#include "ThreadAIXCore.h" + +namespace lldb_private { +class ProcessInstanceInfo; +} + +struct AIXSigInfo { + + //COPY siginfo_t correctly for AIX version + int32_t si_signo; // Order matters for the first 3. + int32_t si_errno; + int32_t si_code; + struct alignas(8) { + lldb::addr_t si_addr; + int16_t si_addr_lsb; + union { + struct { + lldb::addr_t _lower; + lldb::addr_t _upper; + } _addr_bnd; + uint32_t _pkey; + } bounds; + } sigfault; + + enum SigInfoNoteType : uint8_t { eUnspecified, eNT_SIGINFO }; + SigInfoNoteType note_type; + + AIXSigInfo(); + + void Parse(const AIXCORE::AIXCore64Header data, + const lldb_private::ArchSpec &arch, + const lldb_private::UnixSignals &unix_signals); + + std::string + GetDescription(const lldb_private::UnixSignals &unix_signals) const; + + static size_t GetSize(const lldb_private::ArchSpec &arch); +}; + +struct ThreadData { + lldb_private::DataExtractor gpregset; + std::vector notes; + lldb::tid_t tid; + std::string name; + AIXSigInfo siginfo; + int prstatus_sig = 0; +}; + +class ThreadAIXCore : public lldb_private::Thread { +public: + ThreadAIXCore(lldb_private::Process &process, const ThreadData &td); + + ~ThreadAIXCore() override; + + void RefreshStateAfterStop() override; + + lldb::RegisterContextSP GetRegisterContext() override; + + lldb::RegisterContextSP + CreateRegisterContextForFrame(lldb_private::StackFrame *frame) override; + + static bool ThreadIDIsValid(lldb::tid_t thread) { return thread != 0; } + + const char *GetName() override { + if (m_thread_name.empty()) + return nullptr; + return m_thread_name.c_str(); + } + + void SetName(const char *name) override { + if (name && name[0]) + m_thread_name.assign(name); + else + m_thread_name.clear(); + } + + void CreateStopFromSigInfo(const AIXSigInfo &siginfo, + const lldb_private::UnixSignals &unix_signals); + +protected: + // Member variables. + std::string m_thread_name; + lldb::RegisterContextSP m_thread_reg_ctx_sp; + + lldb_private::DataExtractor m_gpregset_data; + std::vector m_notes; + AIXSigInfo m_siginfo; + + bool CalculateStopInfo() override; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_THREADAIXCORE_H >From 57cb8058646103eeada1f92e039d9c54ccd4788f Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Mon, 17 Mar 2025 07:31:26 -0500 Subject: [PATCH 40/46] Merge branch 'llvm:main' into llvmgh-101657 --- lldb/cmake/modules/LLDBConfig.cmake | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lldb/cmake/modules/LLDBConfig.cmake b/lldb/cmake/modules/LLDBConfig.cmake index 312791ce36fc7..9df71edd8b359 100644 --- a/lldb/cmake/modules/LLDBConfig.cmake +++ b/lldb/cmake/modules/LLDBConfig.cmake @@ -292,11 +292,7 @@ endif() # Figure out if lldb could use lldb-server. If so, then we'll # ensure we build lldb-server when an lldb target is being built. -<<<<<<< HEAD -if (CMAKE_SYSTEM_NAME MATCHES "Android|Darwin|FreeBSD|Linux|NetBSD|OpenBSD|Windows|AIX") -======= if (CMAKE_SYSTEM_NAME MATCHES "AIX|Android|Darwin|FreeBSD|Linux|NetBSD|OpenBSD|Windows") ->>>>>>> upstream/main set(LLDB_CAN_USE_LLDB_SERVER ON) else() set(LLDB_CAN_USE_LLDB_SERVER OFF) >From 0767ef036d3f82d1429e04a319feb6627ea08158 Mon Sep 17 00:00:00 2001 From: Dhruv Srivastava Date: Tue, 18 Mar 2025 15:03:17 +0530 Subject: [PATCH 41/46] Error Handling (#32) * Error Handling for aix core module --- .../AIX-DYLD/DynamicLoaderAIXDYLD.cpp | 12 +++++++----- .../ObjectFile/AIXCore/ObjectFileAIXCore.cpp | 15 +++++++-------- lldb/source/Plugins/Process/aix-core/AIXCore.cpp | 2 +- .../Plugins/Process/aix-core/ProcessAIXCore.cpp | 11 ++++++++--- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp index 7e44ffbb1c051..12f24c049f373 100644 --- a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp @@ -235,17 +235,19 @@ bool DynamicLoaderAIXDYLD::IsCoreFile() const { void DynamicLoaderAIXDYLD::FillCoreLoaderData(lldb_private::DataExtractor &data, uint64_t loader_offset, uint64_t loader_size ) { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); static char *buffer = (char *)malloc(loader_size); - struct ld_info ldinfo[64]; - char *buffer_complete; + if (buffer == NULL) { + LLDB_LOG(log, "Buffer allocation failed error: {0}", std::strerror(errno)); + return; + } + struct ld_info ldinfo[64] = {}; struct ld_info *ptr; int i = 0; - Log *log = GetLog(LLDBLog::DynamicLoader); - LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); ByteOrder byteorder = data.GetByteOrder(); data.ExtractBytes(loader_offset, loader_size, eByteOrderBig, buffer); - buffer_complete = buffer + loader_size; ldinfo[0].ldinfo_next = 1; while (ldinfo[i++].ldinfo_next != 0) { diff --git a/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.cpp b/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.cpp index 5158fa4e25077..0ba1056866937 100644 --- a/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.cpp +++ b/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.cpp @@ -73,8 +73,6 @@ ObjectFile *ObjectFileAIXCore::CreateInstance(const lldb::ModuleSP &module_sp, assert(data_sp); - const uint8_t *magic = data_sp->GetBytes() + data_offset; - // Update the data to contain the entire file if it doesn't already if (data_sp->GetByteSize() < length) { data_sp = MapFileDataWritable(*file, length, file_offset); @@ -82,7 +80,6 @@ ObjectFile *ObjectFileAIXCore::CreateInstance(const lldb::ModuleSP &module_sp, return nullptr; data_offset = 0; mapped_writable = true; - magic = data_sp->GetBytes(); } // If we didn't map the data as writable take ownership of the buffer. @@ -90,7 +87,6 @@ ObjectFile *ObjectFileAIXCore::CreateInstance(const lldb::ModuleSP &module_sp, data_sp = std::make_shared(data_sp->GetBytes(), data_sp->GetByteSize()); data_offset = 0; - magic = data_sp->GetBytes(); } std::unique_ptr objfile_up(new ObjectFileAIXCore( @@ -124,19 +120,22 @@ size_t ObjectFileAIXCore::GetModuleSpecifications( return specs.GetSize() - initial_count; } -static uint32_t AIXCoreHeaderCheckFromMagic(uint32_t magic) { +static bool AIXCoreHeaderCheckFromMagic(uint32_t magic) { Log *log = GetLog(LLDBLog::Modules); + bool ret = false; switch (magic) { case AIXCORE32: LLDB_LOGF(log, "ObjectFileAIXCore: 32-bit not supported"); break; case AIXCORE64: m_is_core = true; - return 1; + ret = true; + break; + default: break; } - return 0; + return ret; } bool ObjectFileAIXCore::MagicBytesMatch(DataBufferSP &data_sp, @@ -147,7 +146,7 @@ bool ObjectFileAIXCore::MagicBytesMatch(DataBufferSP &data_sp, lldb::offset_t offset = 0; offset += 4; // Skipping to the coredump version uint32_t magic = data.GetU32(&offset); - return AIXCoreHeaderCheckFromMagic(magic) != 0; + return AIXCoreHeaderCheckFromMagic(magic); } bool ObjectFileAIXCore::ParseHeader() { diff --git a/lldb/source/Plugins/Process/aix-core/AIXCore.cpp b/lldb/source/Plugins/Process/aix-core/AIXCore.cpp index 95e47b4d8be53..bc496b5af273f 100644 --- a/lldb/source/Plugins/Process/aix-core/AIXCore.cpp +++ b/lldb/source/Plugins/Process/aix-core/AIXCore.cpp @@ -110,7 +110,7 @@ bool AIXCore64Header::ParseCoreHeader(lldb_private::DataExtractor &data, ret = ParseThreadContext(data, offset); ret = ParseUserData(data, &offset_to_user); - return true; + return ret; } diff --git a/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp b/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp index 9300aa14ac4db..cfcbe1a216116 100644 --- a/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp +++ b/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp @@ -208,8 +208,10 @@ Status ProcessAIXCore::DoLoadCore() { exe_module_spec.GetArchitecture() = arch; exe_module_spec.GetFileSpec().SetFile(m_aixcore_header.User.process.pi_comm, FileSpec::Style::native); - exe_module_sp = GetTarget().GetOrCreateModule(exe_module_spec, true); - GetTarget().SetExecutableModule(exe_module_sp, eLoadDependentsNo); + exe_module_sp = + GetTarget().GetOrCreateModule(exe_module_spec, true /* notify */); + if (exe_module_sp) + GetTarget().SetExecutableModule(exe_module_sp, eLoadDependentsNo); } return error; @@ -232,8 +234,11 @@ void ProcessAIXCore::RefreshStateAfterStop() {} // Process Memory size_t ProcessAIXCore::ReadMemory(lldb::addr_t addr, void *buf, size_t size, Status &error) { + if(addr == LLDB_INVALID_ADDRESS) + return 0; + if (lldb::ABISP abi_sp = GetABI()) - addr = abi_sp->FixAnyAddress(addr); + addr = abi_sp->FixAnyAddress(addr); // Don't allow the caching that lldb_private::Process::ReadMemory does since // in core files we have it all cached our our core file anyway. >From 8214e5d8cc52b486d3c3f5f4615848b1782cfcf8 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Wed, 26 Mar 2025 01:34:36 -0500 Subject: [PATCH 42/46] Merge branch 'llvm:main' into gh-101657 --- lldb/source/Host/common/Host.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp index 575bb46216d19..6cf1112511c3f 100644 --- a/lldb/source/Host/common/Host.cpp +++ b/lldb/source/Host/common/Host.cpp @@ -20,7 +20,6 @@ #include #include #include -#endif #include #endif >From d00d28ae68c731efe6f9392000be9822ee74ff0a Mon Sep 17 00:00:00 2001 From: HemangGadhavi Date: Wed, 26 Mar 2025 04:08:23 -0500 Subject: [PATCH 43/46] First time attach resolution --- .../Plugins/Process/AIX/NativeProcessAIX.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp index fc84763857453..83b8ae5eb3258 100644 --- a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp +++ b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp @@ -2000,7 +2000,21 @@ Status NativeProcessAIX::PtraceWrapper(int req, lldb::pid_t pid, void *addr, } else if (req == PT_WRITE_BLOCK) { ptrace64(req, pid, (long long)addr, (int)data_size, (int *)result); } else if (req == PT_ATTACH) { + // Block SIGCHLD signal during attach to the process, + // to prevent interruptions. + // The ptrace operation may send SIGCHLD signals in certain cases + // during the attach, which can interfere. + static sigset_t signal_set; + sigemptyset (&signal_set); + sigaddset (&signal_set, SIGCHLD); + if(!pthread_sigmask( SIG_BLOCK, &signal_set, NULL)) + LLDB_LOG(log,"NativeProcessAIX::pthread_sigmask(SIG_BLOCK) Failed"); + ptrace64(req, pid, 0, 0, nullptr); + + //Unblocking the SIGCHLD after attach work. + if(!pthread_sigmask( SIG_UNBLOCK, &signal_set, NULL )) + LLDB_LOG(log,"NativeProcessAIX::pthread_sigmask(SIG_UNBLOCK) Failed"); } else if (req == PT_WATCH) { ptrace64(req, pid, (long long)addr, (int)data_size, nullptr); } else if (req == PT_DETACH) { >From 9ff945e62a906e9711022bf8f77b86a0aa05c64e Mon Sep 17 00:00:00 2001 From: HemangGadhavi Date: Tue, 1 Apr 2025 04:54:51 -0500 Subject: [PATCH 44/46] Invalid DWARF rangelist --- lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp index cf11e5fb8f5a3..65bd1ee9781d8 100644 --- a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp @@ -578,6 +578,7 @@ SectionType ObjectFileXCOFF::GetSectionType(llvm::StringRef sect_name, .Case(".dwinfo", eSectionTypeDWARFDebugInfo) .Case(".dwline", eSectionTypeDWARFDebugLine) .Case(".dwabrev", eSectionTypeDWARFDebugAbbrev) + .Case(".dwrnges", eSectionTypeDWARFDebugRanges) .Default(eSectionTypeInvalid); if (section_type != eSectionTypeInvalid) >From b443dd5b26354da73cd4d785a093d9d1582b25a8 Mon Sep 17 00:00:00 2001 From: Dhruv Srivastava Date: Wed, 2 Apr 2025 11:57:28 +0530 Subject: [PATCH 45/46] Fix for stack memory access from core file (#40) * Fix for stack memory access in core file --- .../Process/aix-core/ProcessAIXCore.cpp | 74 ++++++++++++++++++- .../Plugins/Process/aix-core/ProcessAIXCore.h | 11 +++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp b/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp index cfcbe1a216116..bb2db66e2980e 100644 --- a/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp +++ b/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp @@ -94,6 +94,33 @@ ProcessAIXCore::~ProcessAIXCore() { Finalize(true /* destructing */); } +lldb::addr_t ProcessAIXCore::AddAddressRanges(AIXCORE::AIXCore64Header header) { + const lldb::addr_t addr = header.StackBaseAddr; + FileRange file_range(header.StackOffset, header.StackSize); + VMRangeToFileOffset::Entry range_entry(addr, header.StackSize, file_range); + + if (header.StackSize > 0) { + VMRangeToFileOffset::Entry *last_entry = m_core_aranges.Back(); + if (last_entry && + last_entry->GetRangeEnd() == range_entry.GetRangeBase() && + last_entry->data.GetRangeEnd() == range_entry.data.GetRangeBase() && + last_entry->GetByteSize() == last_entry->data.GetByteSize()) { + last_entry->SetRangeEnd(range_entry.GetRangeEnd()); + last_entry->data.SetRangeEnd(range_entry.data.GetRangeEnd()); + } else { + m_core_aranges.Append(range_entry); + } + } + + const uint32_t permissions = lldb::ePermissionsReadable | + lldb::ePermissionsWritable; + + m_core_range_infos.Append( + VMRangeToPermissions::Entry(addr, header.StackSize, permissions)); + + return addr; +} + bool ProcessAIXCore::CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) { @@ -170,6 +197,7 @@ Status ProcessAIXCore::DoLoadCore() { } FileSpec file = m_core_module_sp->GetObjectFile()->GetFileSpec(); + Log *log = GetLog(LLDBLog::Process); if (file) { const size_t header_size = sizeof(AIXCORE::AIXCore64Header); @@ -180,6 +208,9 @@ Status ProcessAIXCore::DoLoadCore() { DataExtractor data(data_sp, lldb::eByteOrderBig, 4); lldb::offset_t data_offset = 0; m_aixcore_header.ParseCoreHeader(data, &data_offset); + lldb::addr_t addr = AddAddressRanges(m_aixcore_header); + if (addr == LLDB_INVALID_ADDRESS) + LLDB_LOGF(log, "ProcessAIXCore: Invalid base address. Stack information will be limited"); auto dyld = static_cast(GetDynamicLoader()); dyld->FillCoreLoaderData(data, m_aixcore_header.LoaderOffset, m_aixcore_header.LoaderSize); @@ -246,7 +277,48 @@ size_t ProcessAIXCore::ReadMemory(lldb::addr_t addr, void *buf, size_t size, } size_t ProcessAIXCore::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, - Status &error) { return 0; } + Status &error) { + ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); + if (core_objfile == nullptr) + return 0; + // Get the address range + const VMRangeToFileOffset::Entry *address_range = + m_core_aranges.FindEntryThatContains(addr); + if (address_range == nullptr || address_range->GetRangeEnd() < addr) { + error = Status::FromErrorStringWithFormat( + "core file does not contain 0x%" PRIx64, addr); + return 0; + } + + // Convert the address into core file offset + const lldb::addr_t offset = addr - address_range->GetRangeBase(); + const lldb::addr_t file_start = address_range->data.GetRangeBase(); + const lldb::addr_t file_end = address_range->data.GetRangeEnd(); + size_t bytes_to_read = size; // Number of bytes to read from the core file + size_t bytes_copied = 0; // Number of bytes actually read from the core file + // Number of bytes available in the core file from the given address + lldb::addr_t bytes_left = 0; + + // Don't proceed if core file doesn't contain the actual data for this + // address range. + if (file_start == file_end) + return 0; + + // Figure out how many on-disk bytes remain in this segment starting at the + // given offset + if (file_end > file_start + offset) + bytes_left = file_end - (file_start + offset); + + if (bytes_to_read > bytes_left) + bytes_to_read = bytes_left; + + // If there is data available on the core file read it + if (bytes_to_read) + bytes_copied = + core_objfile->CopyData(offset + file_start, bytes_to_read, buf); + + return bytes_copied; +} Status ProcessAIXCore::DoGetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo ®ion_info) { diff --git a/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.h b/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.h index 9880c491689ca..ffd9e401ee192 100644 --- a/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.h +++ b/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.h @@ -85,11 +85,22 @@ class ProcessAIXCore : public lldb_private::PostMortemProcess { void ParseAIXCoreFile(); + lldb::addr_t AddAddressRanges(AIXCORE::AIXCore64Header header); private: lldb::ModuleSP m_core_module_sp; std::string m_dyld_plugin_name; + typedef lldb_private::Range FileRange; + typedef lldb_private::RangeDataVector + VMRangeToFileOffset; + typedef lldb_private::RangeDataVector + VMRangeToPermissions; + + // Address ranges found in the core + VMRangeToFileOffset m_core_aranges; + VMRangeToPermissions m_core_range_infos; + // True if m_thread_contexts contains valid entries bool m_thread_data_valid = false; AIXCORE::AIXCore64Header m_aixcore_header; >From 96db5e3257436a222ee38049528d682166421fa1 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Wed, 2 Apr 2025 01:46:46 -0500 Subject: [PATCH 46/46] Build fail: SBMutex --- lldb/source/API/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/lldb/source/API/CMakeLists.txt b/lldb/source/API/CMakeLists.txt index 0045edf6743d1..2c8f2d583c054 100644 --- a/lldb/source/API/CMakeLists.txt +++ b/lldb/source/API/CMakeLists.txt @@ -83,6 +83,7 @@ add_lldb_library(liblldb STATIC ${option_framework} SBMemoryRegionInfoList.cpp SBModule.cpp SBModuleSpec.cpp + SBMutex.cpp SBPlatform.cpp SBProcess.cpp SBProgress.cpp From lldb-commits at lists.llvm.org Mon May 5 23:56:30 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Mon, 05 May 2025 23:56:30 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Fix GetIndexOfChildMemberWithName to handle anonymous structs. (PR #138487) In-Reply-To: Message-ID: <6819b29e.170a0220.115b29.c731@mx.google.com> https://github.com/labath edited https://github.com/llvm/llvm-project/pull/138487 From lldb-commits at lists.llvm.org Mon May 5 23:56:30 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Mon, 05 May 2025 23:56:30 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Fix GetIndexOfChildMemberWithName to handle anonymous structs. (PR #138487) In-Reply-To: Message-ID: <6819b29e.170a0220.2be1.21fd@mx.google.com> https://github.com/labath approved this pull request. This looks good now. Thanks. I'm surprised that something like `derb.x` doesn't work -- both for "frame var" and in C++. You're not changing that, so it bears no relation to this patch, but overall, but overall, I think it wouldn't be *un*reasonable to make these kinds of expressions work in "frame var". https://github.com/llvm/llvm-project/pull/138487 From lldb-commits at lists.llvm.org Mon May 5 23:56:30 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Mon, 05 May 2025 23:56:30 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Fix GetIndexOfChildMemberWithName to handle anonymous structs. (PR #138487) In-Reply-To: Message-ID: <6819b29e.170a0220.31a28f.c3f8@mx.google.com> ================ @@ -0,0 +1,33 @@ +int main(int argc, char** argv) { + struct A { + struct { + int x = 1; + }; + int y = 2; + } a; + + struct B { + // Anonymous struct inherits another struct. + struct : public A { + int z = 3; + }; + int w = 4; + A a; + } b; + + struct : public A { + struct { + int z = 13; + }; + } unnamed_derived; + + struct DerivedB : public B { + struct { + // `w` in anonymous struct overrides `w` from `B`. ---------------- labath wrote: I think the precise term is "shadows" https://github.com/llvm/llvm-project/pull/138487 From lldb-commits at lists.llvm.org Mon May 5 23:58:35 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Mon, 05 May 2025 23:58:35 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add IsCoreDumping to ProcessInstanceInfo (PR #138580) In-Reply-To: Message-ID: <6819b31b.170a0220.36c02e.b7a8@mx.google.com> https://github.com/labath approved this pull request. https://github.com/llvm/llvm-project/pull/138580 From lldb-commits at lists.llvm.org Tue May 6 01:03:53 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Tue, 06 May 2025 01:03:53 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <6819c269.170a0220.301ec8.e73e@mx.google.com> https://github.com/labath commented: Apart from the pointer indexing question, this PR also opens the question of "how should the numbers be represented". Here you represent them as ValueObjects, which means you have to give them types, which means you have to find a type system for them, ... I'm not sure I agree with how are those steps implemented (in fact, I'm pretty sure I don't agree with at least some of those steps). In order to keep this PR focused, and because a first-class number representation is not necessary for replacing the current "frame var", my suggestion would be to dumb down the implementation of `postfix_expression`: Instead of `postfix_expression "[" expression "]"`, you could implement something like `postfix_expression "[" integer "]"`. Then we can store the RHS of the `[]` expression directly as a (u)int64 (or AP(S)Int, or whatever), and we can completely sidestep the question its type. My reason for that is that I believe that after this is implemented (and @cmtice finishes member access), we will be in a position to flip the flag on this and make it the default -- which I think is the real proof of the pudding, and I sort of expect we will need to tweak the implementation until everything settles into place. I think it's better to not introduce additional complexity while we're doing that. https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Tue May 6 01:03:53 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Tue, 06 May 2025 01:03:53 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <6819c269.170a0220.20e047.f50a@mx.google.com> ================ @@ -18,6 +18,22 @@ namespace lldb_private::dil { +static lldb::ValueObjectSP +ArrayToPointerConversion(lldb::ValueObjectSP valobj, + std::shared_ptr ctx) { + assert(valobj->IsArrayType() && + "an argument to array-to-pointer conversion must be an array"); + + uint64_t addr = valobj->GetLoadAddress(); + llvm::StringRef name = "result"; + ExecutionContext exe_ctx; + ctx->CalculateExecutionContext(exe_ctx); + return ValueObject::CreateValueObjectFromAddress( + name, addr, exe_ctx, + valobj->GetCompilerType().GetArrayElementType(ctx.get()).GetPointerType(), + /* do_deref */ false); ---------------- labath wrote: ```suggestion /*do_deref=*/false); ``` https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Tue May 6 01:03:55 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Tue, 06 May 2025 01:03:55 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <6819c26b.170a0220.2f6e16.1b69@mx.google.com> ================ @@ -24,6 +28,8 @@ qualified_id = ["::"] [nested_name_specifier] unqualified_id identifier = ? C99 Identifier ? ; +numeric_literal = ? C99 Integer constant ? ; ---------------- labath wrote: I think the C99 part isn't really true. Maybe we should have a separate section to explain the number format we accept? https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Tue May 6 01:03:55 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Tue, 06 May 2025 01:03:55 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <6819c26b.050a0220.926e0.56c6@mx.google.com> ================ @@ -111,7 +111,36 @@ ASTNodeUP DILParser::ParseUnaryExpression() { llvm_unreachable("invalid token kind"); } } - return ParsePrimaryExpression(); + return ParsePostfixExpression(); +} + +// Parse a postfix_expression. +// +// postfix_expression: +// primary_expression +// postfix_expression "[" expression "]" +// +ASTNodeUP DILParser::ParsePostfixExpression() { + ASTNodeUP lhs = ParsePrimaryExpression(); + while (CurToken().Is(Token::l_square)) { + uint32_t loc = CurToken().GetLocation(); + Token token = CurToken(); + switch (token.GetKind()) { + case Token::l_square: { ---------------- labath wrote: What's the point? You've already checked the type three lines above https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Tue May 6 01:03:55 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Tue, 06 May 2025 01:03:55 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <6819c26b.170a0220.289717.e26e@mx.google.com> https://github.com/labath edited https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Tue May 6 01:03:55 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Tue, 06 May 2025 01:03:55 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <6819c26b.050a0220.3db73c.7ba1@mx.google.com> ================ @@ -108,6 +130,26 @@ class UnaryOpNode : public ASTNode { ASTNodeUP m_operand; }; +class ArraySubscriptNode : public ASTNode { +public: + ArraySubscriptNode(uint32_t location, ASTNodeUP lhs, ASTNodeUP rhs) + : ASTNode(location, NodeKind::eArraySubscriptNode), m_lhs(std::move(lhs)), + m_rhs(std::move(rhs)) {} + + llvm::Expected Accept(Visitor *v) const override; + + ASTNode *lhs() const { return m_lhs.get(); } + ASTNode *rhs() const { return m_rhs.get(); } ---------------- labath wrote: Let's use the GetFoo convention for this (and also in UnaryOpNode, which I didn't catch at the time) https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Tue May 6 01:03:55 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Tue, 06 May 2025 01:03:55 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <6819c26b.170a0220.4411f.b5c1@mx.google.com> ================ @@ -57,6 +64,29 @@ static std::optional IsWord(llvm::StringRef expr, return candidate; } +static void ConsumeNumberBody(char &prev_ch, llvm::StringRef::iterator &cur_pos, + llvm::StringRef expr) { + while (cur_pos != expr.end() && + (IsDigit(*cur_pos) || IsLetter(*cur_pos) || *cur_pos == '_')) { + prev_ch = *cur_pos; + cur_pos++; + } +} + +static std::optional IsNumber(llvm::StringRef expr, + llvm::StringRef &remainder) { + llvm::StringRef::iterator cur_pos = remainder.begin(); + llvm::StringRef::iterator start = cur_pos; + char prev_ch = 0; + if (IsDigit(*start)) { + ConsumeNumberBody(prev_ch, cur_pos, expr); + llvm::StringRef number = expr.substr(start - expr.begin(), cur_pos - start); + if (remainder.consume_front(number)) + return number; + } + return std::nullopt; +} ---------------- labath wrote: I think this should be equivalent to what you're doing here. ```suggestion static bool IsNumberBodyChar(char ch) { // Or make it a lambda return IsDigit(ch) || IsLetter(ch) || ch == '_'; } static std::optional IsNumber(llvm::StringRef expr, llvm::StringRef &remainder) { llvm::StringRef::iterator cur_pos = remainder.begin(); llvm::StringRef::iterator start = cur_pos; if (remainder[0]) { llvm::StringRef number = remainder.take_while(IsNumberBodyChar); remainder = remainder.drop_front(number.size()); return number; } return std::nullopt; } ``` https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Tue May 6 01:03:55 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Tue, 06 May 2025 01:03:55 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <6819c26b.050a0220.60ca4.6233@mx.google.com> ================ @@ -280,6 +311,52 @@ void DILParser::BailOut(const std::string &error, uint32_t loc, m_dil_lexer.ResetTokenIdx(m_dil_lexer.NumLexedTokens() - 1); } +// Parse a numeric_literal. +// +// numeric_literal: +// ? Token::numeric_constant ? +// +ASTNodeUP DILParser::ParseNumericLiteral() { + Expect(Token::numeric_constant); + ASTNodeUP numeric_constant = ParseNumericConstant(); + if (numeric_constant->GetKind() == NodeKind::eErrorNode) { + BailOut(llvm::formatv("Failed to parse token as numeric-constant: {0}", + CurToken()), + CurToken().GetLocation(), CurToken().GetSpelling().length()); + return std::make_unique(); + } + m_dil_lexer.Advance(); + return numeric_constant; +} + +static constexpr std::pair type_suffixes[] = { + {"ull", lldb::eBasicTypeUnsignedLongLong}, + {"ul", lldb::eBasicTypeUnsignedLong}, + {"u", lldb::eBasicTypeUnsignedInt}, + {"ll", lldb::eBasicTypeLongLong}, + {"l", lldb::eBasicTypeLong}, +}; + +ASTNodeUP DILParser::ParseNumericConstant() { + Token token = CurToken(); + auto spelling = token.GetSpelling(); + llvm::StringRef spelling_ref = spelling; + lldb::BasicType type = lldb::eBasicTypeInt; + for (auto [suffix, t] : type_suffixes) { + if (spelling_ref.consume_back_insensitive(suffix)) { + type = t; + break; + } + } + llvm::APInt raw_value; + if (!spelling_ref.getAsInteger(0, raw_value)) { ---------------- labath wrote: In the lexer, you accept `_` as a number character. Does this function recognise those? https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Tue May 6 01:07:53 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Tue, 06 May 2025 01:07:53 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][TypeSystemClang] Allow arrays to be dereferenced in C/C++. (PR #135843) In-Reply-To: Message-ID: <6819c359.050a0220.342936.58af@mx.google.com> https://github.com/labath approved this pull request. Thanks. I think this is fine now. @jimingham, @adrian-prantl, do you have anything to add. Heads-up: I believe you will need to implement this method in the Swift type system in order to keep dereferencing working. https://github.com/llvm/llvm-project/pull/135843 From lldb-commits at lists.llvm.org Tue May 6 01:10:35 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Tue, 06 May 2025 01:10:35 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Parse DWARF CFI for discontinuous functions (PR #137006) In-Reply-To: Message-ID: <6819c3fb.170a0220.2d9b35.f537@mx.google.com> labath wrote: @felipepiovezan, after yesterdays explanation, maybe you feel brave enough to review this? :P https://github.com/llvm/llvm-project/pull/137006 From lldb-commits at lists.llvm.org Tue May 6 01:31:32 2025 From: lldb-commits at lists.llvm.org (Ely Ronnen via lldb-commits) Date: Tue, 06 May 2025 01:31:32 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Change synthetic symbol names to have file address (PR #138416) In-Reply-To: Message-ID: <6819c8e4.170a0220.2d2bcf.0375@mx.google.com> https://github.com/eronnen updated https://github.com/llvm/llvm-project/pull/138416 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Tue May 6 02:22:09 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 02:22:09 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] fix crash on FreeBSD/powerpc64le (PR #138331) In-Reply-To: Message-ID: <6819d4c1.170a0220.35d8ef.cf6b@mx.google.com> https://github.com/DavidSpickett edited https://github.com/llvm/llvm-project/pull/138331 From lldb-commits at lists.llvm.org Tue May 6 02:23:37 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Tue, 06 May 2025 02:23:37 -0700 (PDT) Subject: [Lldb-commits] [lldb] 488cb24 - [lldb] fix crash on FreeBSD/powerpc64le (#138331) Message-ID: <6819d519.170a0220.1bba29.4d3a@mx.google.com> Author: Piotr Kubaj Date: 2025-05-06T10:23:33+01:00 New Revision: 488cb24c2eddb8d0ec3419a4117691022e9e679b URL: https://github.com/llvm/llvm-project/commit/488cb24c2eddb8d0ec3419a4117691022e9e679b DIFF: https://github.com/llvm/llvm-project/commit/488cb24c2eddb8d0ec3419a4117691022e9e679b.diff LOG: [lldb] fix crash on FreeBSD/powerpc64le (#138331) Fix for: `Assertion failed: (false && "Architecture or OS not supported"), function CreateRegisterContextForFrame, file /usr/src/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp, line 182. PLEASE submit a bug report to https://bugs.freebsd.org/submit/ and include the crash backtrace. #0 0x000000080cd857c8 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) /usr/src/contrib/llvm-project/llvm/lib/Support/Unix/Signals.inc:723:13 #1 0x000000080cd85ed4 /usr/src/contrib/llvm-project/llvm/lib/Support/Unix/Signals.inc:797:3 #2 0x000000080cd82ae8 llvm::sys::RunSignalHandlers() /usr/src/contrib/llvm-project/llvm/lib/Support/Signals.cpp:104:5 #3 0x000000080cd861f0 SignalHandler /usr/src/contrib/llvm-project/llvm/lib/Support/Unix/Signals.inc:403:3 #4 0x000000080f159644 handle_signal /usr/src/lib/libthr/thread/thr_sig.c:298:3 ` Added: Modified: lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp Removed: ################################################################################ diff --git a/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp b/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp index 91552dd976925..a0cd0ee5025bd 100644 --- a/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp +++ b/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp @@ -95,6 +95,7 @@ ThreadElfCore::CreateRegisterContextForFrame(StackFrame *frame) { reg_interface = new RegisterContextFreeBSD_powerpc32(arch); break; case llvm::Triple::ppc64: + case llvm::Triple::ppc64le: reg_interface = new RegisterContextFreeBSD_powerpc64(arch); break; case llvm::Triple::mips64: From lldb-commits at lists.llvm.org Tue May 6 02:23:39 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 02:23:39 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] fix crash on FreeBSD/powerpc64le (PR #138331) In-Reply-To: Message-ID: <6819d51b.050a0220.3ab004.5e75@mx.google.com> https://github.com/DavidSpickett closed https://github.com/llvm/llvm-project/pull/138331 From lldb-commits at lists.llvm.org Tue May 6 02:28:01 2025 From: lldb-commits at lists.llvm.org (Michael Buch via lldb-commits) Date: Tue, 06 May 2025 02:28:01 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb/docs] Fix/improve the gdb command map for dynamic types (PR #138538) In-Reply-To: Message-ID: <6819d621.170a0220.2b1ea4.113a@mx.google.com> https://github.com/Michael137 approved this pull request. Makes sense to me https://github.com/llvm/llvm-project/pull/138538 From lldb-commits at lists.llvm.org Tue May 6 02:42:57 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 02:42:57 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb/Host] Enable inheriting "non-inheritable" FDs (PR #126935) In-Reply-To: Message-ID: <6819d9a1.630a0220.1eda9c.f810@mx.google.com> https://github.com/DavidSpickett approved this pull request. LGTM. https://github.com/llvm/llvm-project/pull/126935 From lldb-commits at lists.llvm.org Tue May 6 02:43:50 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 02:43:50 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fix block address resolution for functions in multiple sections (PR #137955) In-Reply-To: Message-ID: <6819d9d6.050a0220.346761.8aa3@mx.google.com> https://github.com/DavidSpickett approved this pull request. LGTM https://github.com/llvm/llvm-project/pull/137955 From lldb-commits at lists.llvm.org Tue May 6 02:46:25 2025 From: lldb-commits at lists.llvm.org (Hu Jialun via lldb-commits) Date: Tue, 06 May 2025 02:46:25 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Implement `runInTerminal` for Windows (PR #121269) In-Reply-To: Message-ID: <6819da71.a70a0220.96a1a.6b09@mx.google.com> SuibianP wrote: Pinging again for review @ashgti @omjavaid @walter-erquinigo Also want to highlight https://github.com/SuibianP/llvm-project/commit/f9675fbe23abe59c3cf56a6263b7a78ec20e42d4#diff-ed319b24adfa654beeff8b32f9d8f975c62299fbe5cb6fbe1028aa6bbaa369e7. Not sure how it is passing CI though; I might as well be understanding it wrongly. https://github.com/llvm/llvm-project/pull/121269 From lldb-commits at lists.llvm.org Tue May 6 02:50:32 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 02:50:32 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add IsCoreDumping to ProcessInstanceInfo (PR #138580) In-Reply-To: Message-ID: <6819db68.050a0220.fda0d.664e@mx.google.com> ================ @@ -213,6 +213,11 @@ static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo, } else if (Line.consume_front("Tgid:")) { Line = Line.ltrim(); Line.consumeInteger(10, Tgid); + } else if (Line.consume_front("CoreDumping:")) { + uint32_t coredumping; + Line = Line.ltrim(); + Line.consumeInteger(1, coredumping); ---------------- DavidSpickett wrote: This is calling: ``` template bool consumeInteger(unsigned Radix, T &Result) { ``` * Radix of 1 means binary? * Parsing can fail, though now I look above and nothing else checks the return value, sigh... This is new though, so can you make it check the return value and not set if it's false? The type is optional, so anyone making use of this is handling the "I don't know" state already. https://github.com/llvm/llvm-project/pull/138580 From lldb-commits at lists.llvm.org Tue May 6 02:50:33 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 02:50:33 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add IsCoreDumping to ProcessInstanceInfo (PR #138580) In-Reply-To: Message-ID: <6819db69.630a0220.576ff.05d0@mx.google.com> ================ @@ -115,5 +115,8 @@ TEST_F(HostTest, GetProcessInfoSetsPriority) { } ASSERT_TRUE(Info.IsZombie().has_value()); ASSERT_FALSE(Info.IsZombie().value()); + + ASSERT_TRUE(Info.IsCoreDumping().has_value()); + ASSERT_FALSE(Info.IsCoreDumping().value()); ---------------- DavidSpickett wrote: I presume this field has been around for a very long time, we don't expect any system to not have it? https://github.com/llvm/llvm-project/pull/138580 From lldb-commits at lists.llvm.org Tue May 6 03:01:02 2025 From: lldb-commits at lists.llvm.org (Michael Buch via lldb-commits) Date: Tue, 06 May 2025 03:01:02 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Fix GetIndexOfChildMemberWithName to handle anonymous structs. (PR #138487) In-Reply-To: Message-ID: <6819ddde.170a0220.1edde3.dc7a@mx.google.com> ================ @@ -6743,7 +6743,11 @@ size_t TypeSystemClang::GetIndexOfChildMemberWithName( if (field_name.empty()) { CompilerType field_type = GetType(field->getType()); std::vector save_indices = child_indexes; - child_indexes.push_back(child_idx); + // We have to add on the number of non-empty base classes to this + // index! ---------------- Michael137 wrote: This comment is redundant. The `non-empty base classes` is no longer true now that we're forwarding `omit_empty_base_classes` https://github.com/llvm/llvm-project/pull/138487 From lldb-commits at lists.llvm.org Tue May 6 03:02:07 2025 From: lldb-commits at lists.llvm.org (Michael Buch via lldb-commits) Date: Tue, 06 May 2025 03:02:07 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Fix GetIndexOfChildMemberWithName to handle anonymous structs. (PR #138487) In-Reply-To: Message-ID: <6819de1f.170a0220.1c01a9.3888@mx.google.com> https://github.com/Michael137 approved this pull request. Some day if we could somehow move all of this into Clang, that'd make me happy :) https://github.com/llvm/llvm-project/pull/138487 From lldb-commits at lists.llvm.org Tue May 6 03:04:44 2025 From: lldb-commits at lists.llvm.org (Michael Buch via lldb-commits) Date: Tue, 06 May 2025 03:04:44 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Fix GetIndexOfChildMemberWithName to handle anonymous structs. (PR #138487) In-Reply-To: Message-ID: <6819debc.050a0220.221863.9724@mx.google.com> ================ @@ -0,0 +1,33 @@ +int main(int argc, char** argv) { ---------------- Michael137 wrote: Can we add a test that involves empty base classes? https://github.com/llvm/llvm-project/pull/138487 From lldb-commits at lists.llvm.org Tue May 6 04:36:30 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Tue, 06 May 2025 04:36:30 -0700 (PDT) Subject: [Lldb-commits] [lldb] Extending LLDB to work on AIX (PR #102601) In-Reply-To: Message-ID: <6819f43e.630a0220.3d316a.0f8b@mx.google.com> https://github.com/ravindra-shinde2 updated https://github.com/llvm/llvm-project/pull/102601 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Tue May 6 04:52:58 2025 From: lldb-commits at lists.llvm.org (Charles Zablit via lldb-commits) Date: Tue, 06 May 2025 04:52:58 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Upgrade ExtractIndexFromString to use llvm::Expected (PR #138297) In-Reply-To: Message-ID: <6819f81a.170a0220.1c4194.05c2@mx.google.com> https://github.com/charles-zablit updated https://github.com/llvm/llvm-project/pull/138297 >From 8f62523c8ccece4a1c11af51ccf320b19b2ed013 Mon Sep 17 00:00:00 2001 From: Charles Zablit Date: Fri, 2 May 2025 16:37:09 +0100 Subject: [PATCH 1/2] [lldb] Upgrade ExtractIndexFromString to use llvm::Expected --- .../lldb/DataFormatters/FormattersHelpers.h | 2 +- .../DataFormatters/FormattersHelpers.cpp | 11 ++-- lldb/source/DataFormatters/VectorType.cpp | 12 ++-- .../Language/CPlusPlus/GenericBitset.cpp | 8 ++- .../Language/CPlusPlus/GenericOptional.cpp | 8 ++- .../CPlusPlus/LibCxxInitializerList.cpp | 7 ++- .../Plugins/Language/CPlusPlus/LibCxxList.cpp | 8 ++- .../Plugins/Language/CPlusPlus/LibCxxMap.cpp | 7 ++- .../Language/CPlusPlus/LibCxxProxyArray.cpp | 7 ++- .../Language/CPlusPlus/LibCxxSliceArray.cpp | 8 ++- .../Plugins/Language/CPlusPlus/LibCxxSpan.cpp | 7 ++- .../Language/CPlusPlus/LibCxxTuple.cpp | 8 ++- .../Language/CPlusPlus/LibCxxUnorderedMap.cpp | 7 ++- .../Language/CPlusPlus/LibCxxValarray.cpp | 7 ++- .../Language/CPlusPlus/LibCxxVariant.cpp | 8 ++- .../Language/CPlusPlus/LibCxxVector.cpp | 19 +++--- .../Language/CPlusPlus/LibStdcppTuple.cpp | 7 ++- lldb/source/Plugins/Language/ObjC/NSArray.cpp | 24 ++++--- .../Plugins/Language/ObjC/NSDictionary.cpp | 62 ++++++++++++------- .../Plugins/Language/ObjC/NSIndexPath.cpp | 12 ++-- lldb/source/Plugins/Language/ObjC/NSSet.cpp | 36 +++++++---- 21 files changed, 174 insertions(+), 101 deletions(-) diff --git a/lldb/include/lldb/DataFormatters/FormattersHelpers.h b/lldb/include/lldb/DataFormatters/FormattersHelpers.h index a98042fd40f93..82aae705e2a59 100644 --- a/lldb/include/lldb/DataFormatters/FormattersHelpers.h +++ b/lldb/include/lldb/DataFormatters/FormattersHelpers.h @@ -53,7 +53,7 @@ void AddFilter(TypeCategoryImpl::SharedPointer category_sp, llvm::StringRef type_name, ScriptedSyntheticChildren::Flags flags, bool regex = false); -size_t ExtractIndexFromString(const char *item_name); +llvm::Expected ExtractIndexFromString(const char *item_name); Address GetArrayAddressOrPointerValue(ValueObject &valobj); diff --git a/lldb/source/DataFormatters/FormattersHelpers.cpp b/lldb/source/DataFormatters/FormattersHelpers.cpp index 085ed3d0a2f29..5e29794c291be 100644 --- a/lldb/source/DataFormatters/FormattersHelpers.cpp +++ b/lldb/source/DataFormatters/FormattersHelpers.cpp @@ -97,18 +97,19 @@ void lldb_private::formatters::AddFilter( category_sp->AddTypeFilter(type_name, match_type, filter_sp); } -size_t lldb_private::formatters::ExtractIndexFromString(const char *item_name) { +llvm::Expected +lldb_private::formatters::ExtractIndexFromString(const char *item_name) { if (!item_name || !*item_name) - return UINT32_MAX; + return llvm::createStringError("String has no item named '%s'", item_name); if (*item_name != '[') - return UINT32_MAX; + return llvm::createStringError("String has no item named '%s'", item_name); item_name++; char *endptr = nullptr; unsigned long int idx = ::strtoul(item_name, &endptr, 0); if (idx == 0 && endptr == item_name) - return UINT32_MAX; + return llvm::createStringError("String has no item named '%s'", item_name); if (idx == ULONG_MAX) - return UINT32_MAX; + return llvm::createStringError("String has no item named '%s'", item_name); return idx; } diff --git a/lldb/source/DataFormatters/VectorType.cpp b/lldb/source/DataFormatters/VectorType.cpp index eab2612d1e941..3fa616b9d9b14 100644 --- a/lldb/source/DataFormatters/VectorType.cpp +++ b/lldb/source/DataFormatters/VectorType.cpp @@ -270,10 +270,14 @@ class VectorTypeSyntheticFrontEnd : public SyntheticChildrenFrontEnd { } llvm::Expected GetIndexOfChildWithName(ConstString name) override { - const char *item_name = name.GetCString(); - uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto idx_or_err = ExtractIndexFromString(name.AsCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *idx_or_err; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); return idx; diff --git a/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp b/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp index 234471d5ba518..ebcac0a48bd30 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp @@ -29,11 +29,13 @@ class GenericBitsetFrontEnd : public SyntheticChildrenFrontEnd { GenericBitsetFrontEnd(ValueObject &valobj, StdLib stdlib); llvm::Expected GetIndexOfChildWithName(ConstString name) override { - size_t idx = formatters::ExtractIndexFromString(name.GetCString()); - if (idx == UINT32_MAX) + auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - return idx; + } + return *idx_or_err; } lldb::ChildCacheState Update() override; diff --git a/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp b/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp index 6f1b2ee3fd9e3..bfcc15ae805e7 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp @@ -39,11 +39,13 @@ class GenericOptionalFrontend : public SyntheticChildrenFrontEnd { llvm::Expected GetIndexOfChildWithName(ConstString name) override { if (name == "$$dereference$$") return 0; - size_t idx = formatters::ExtractIndexFromString(name.GetCString()); - if (idx == UINT32_MAX) + auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - return idx; + } + return *idx_or_err; } llvm::Expected CalculateNumChildren() override { diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp index e8a886dd71821..04891d7d094f3 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp @@ -108,12 +108,13 @@ lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd:: return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - size_t idx = ExtractIndexFromString(name.GetCString()); - if (idx == UINT32_MAX) { + auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return idx; + return *idx_or_err; } lldb_private::SyntheticChildrenFrontEnd * diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp index ea18c02ee6591..9d4773c56b3e3 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp @@ -107,11 +107,13 @@ class ListIterator { class AbstractListFrontEnd : public SyntheticChildrenFrontEnd { public: llvm::Expected GetIndexOfChildWithName(ConstString name) override { - size_t idx = ExtractIndexFromString(name.GetCString()); - if (idx == UINT32_MAX) + auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - return idx; + } + return *idx_or_err; } lldb::ChildCacheState Update() override; diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp index fec2d6f29ca54..9c3694e2448fc 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp @@ -395,12 +395,13 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() { llvm::Expected lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: GetIndexOfChildWithName(ConstString name) { - size_t idx = ExtractIndexFromString(name.GetCString()); - if (idx == UINT32_MAX) { + auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return idx; + return *idx_or_err; } SyntheticChildrenFrontEnd * diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxProxyArray.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxProxyArray.cpp index 41fc704d5886f..a4ded6e0d7d85 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxProxyArray.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxProxyArray.cpp @@ -179,12 +179,13 @@ lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEnd:: if (!m_base) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - size_t idx = ExtractIndexFromString(name.GetCString()); - if (idx == UINT32_MAX) { + auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return idx; + return *idx_or_err; } lldb_private::SyntheticChildrenFrontEnd * diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxSliceArray.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxSliceArray.cpp index 1c4d8509374f1..309d91ae02ecb 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxSliceArray.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxSliceArray.cpp @@ -150,11 +150,13 @@ lldb_private::formatters::LibcxxStdSliceArraySyntheticFrontEnd:: if (!m_start) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - size_t idx = ExtractIndexFromString(name.GetCString()); - if (idx == UINT32_MAX) + auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - return idx; + } + return *idx_or_err; } lldb_private::SyntheticChildrenFrontEnd * diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp index 43721e4b41fb8..26e8c48b4f8eb 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp @@ -133,12 +133,13 @@ llvm::Expected lldb_private::formatters:: if (!m_start) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - size_t idx = ExtractIndexFromString(name.GetCString()); - if (idx == UINT32_MAX) { + auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return idx; + return *idx_or_err; } lldb_private::SyntheticChildrenFrontEnd * diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp index 270aad5ea9310..235a26da50a5f 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp @@ -21,11 +21,13 @@ class TupleFrontEnd: public SyntheticChildrenFrontEnd { } llvm::Expected GetIndexOfChildWithName(ConstString name) override { - size_t idx = formatters::ExtractIndexFromString(name.GetCString()); - if (idx == UINT32_MAX) + auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - return idx; + } + return *idx_or_err; } lldb::ChildCacheState Update() override; diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp index 313664e691c9f..de37aeda25168 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp @@ -294,12 +294,13 @@ lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::Update() { llvm::Expected lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: GetIndexOfChildWithName(ConstString name) { - size_t idx = ExtractIndexFromString(name.GetCString()); - if (idx == UINT32_MAX) { + auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return idx; + return *idx_or_err; } SyntheticChildrenFrontEnd * diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxValarray.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxValarray.cpp index 172df6358789e..4c87e167589da 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxValarray.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxValarray.cpp @@ -129,12 +129,13 @@ lldb_private::formatters::LibcxxStdValarraySyntheticFrontEnd:: if (!m_start || !m_finish) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - size_t idx = ExtractIndexFromString(name.GetCString()); - if (idx == UINT32_MAX) { + auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return idx; + return *idx_or_err; } lldb_private::SyntheticChildrenFrontEnd * diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp index 2419683a4d713..12071321189b9 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp @@ -203,11 +203,13 @@ class VariantFrontEnd : public SyntheticChildrenFrontEnd { } llvm::Expected GetIndexOfChildWithName(ConstString name) override { - size_t index = formatters::ExtractIndexFromString(name.GetCString()); - if (index == UINT32_MAX) + auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - return index; + } + return *idx_or_err; } lldb::ChildCacheState Update() override; diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp index abbf1ed935b6b..228647b8961b1 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp @@ -170,12 +170,13 @@ lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd:: if (!m_start || !m_finish) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - size_t index = formatters::ExtractIndexFromString(name.GetCString()); - if (index == UINT32_MAX) { + auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return index; + return *idx_or_err; } lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd:: @@ -273,10 +274,14 @@ lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd:: if (!m_count || !m_base_data_address) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - const char *item_name = name.GetCString(); - uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto idx_or_err = ExtractIndexFromString(name.AsCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *idx_or_err; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); return idx; diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp index cae5aa379b245..4f05dc1934958 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp @@ -98,12 +98,13 @@ LibStdcppTupleSyntheticFrontEnd::CalculateNumChildren() { llvm::Expected LibStdcppTupleSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) { - size_t index = formatters::ExtractIndexFromString(name.GetCString()); - if (index == UINT32_MAX) { + auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return index; + return *idx_or_err; } SyntheticChildrenFrontEnd * diff --git a/lldb/source/Plugins/Language/ObjC/NSArray.cpp b/lldb/source/Plugins/Language/ObjC/NSArray.cpp index 5e72bd5a6bab6..465639a21d943 100644 --- a/lldb/source/Plugins/Language/ObjC/NSArray.cpp +++ b/lldb/source/Plugins/Language/ObjC/NSArray.cpp @@ -528,10 +528,14 @@ lldb_private::formatters::GenericNSArrayMSyntheticFrontEnd::Update() { llvm::Expected lldb_private::formatters::NSArrayMSyntheticFrontEndBase:: GetIndexOfChildWithName(ConstString name) { - const char *item_name = name.GetCString(); - size_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto idx_or_err = ExtractIndexFromString(name.AsCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *idx_or_err; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); return idx; @@ -616,10 +620,14 @@ template llvm::Expected lldb_private::formatters::GenericNSArrayISyntheticFrontEnd< D32, D64, Inline>::GetIndexOfChildWithName(ConstString name) { - const char *item_name = name.GetCString(); - uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto idx_or_err = ExtractIndexFromString(name.AsCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *idx_or_err; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); return idx; diff --git a/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp b/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp index b04b4610a0bf4..babf638a54ee6 100644 --- a/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp +++ b/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp @@ -587,10 +587,14 @@ lldb_private::formatters::NSDictionaryISyntheticFrontEnd:: llvm::Expected lldb_private::formatters:: NSDictionaryISyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) { - const char *item_name = name.GetCString(); - uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto idx_or_err = ExtractIndexFromString(name.AsCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *idx_or_err; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); return idx; @@ -722,12 +726,16 @@ lldb_private::formatters::NSCFDictionarySyntheticFrontEnd:: llvm::Expected lldb_private::formatters:: NSCFDictionarySyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) { - const char *item_name = name.GetCString(); - const uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto idx_or_err = ExtractIndexFromString(name.AsCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *idx_or_err; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", - name.AsCString(), idx); + name.AsCString()); return idx; } @@ -856,10 +864,14 @@ lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd:: llvm::Expected lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd:: GetIndexOfChildWithName(ConstString name) { - const char *item_name = name.GetCString(); - uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto idx_or_err = ExtractIndexFromString(name.AsCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *idx_or_err; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); return idx; @@ -1058,10 +1070,14 @@ template llvm::Expected lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd< D32, D64>::GetIndexOfChildWithName(ConstString name) { - const char *item_name = name.GetCString(); - uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto idx_or_err = ExtractIndexFromString(name.AsCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *idx_or_err; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); return idx; @@ -1217,10 +1233,14 @@ lldb_private::formatters::Foundation1100:: llvm::Expected lldb_private::formatters::Foundation1100:: NSDictionaryMSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) { - const char *item_name = name.GetCString(); - uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto idx_or_err = ExtractIndexFromString(name.AsCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *idx_or_err; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); return idx; diff --git a/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp b/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp index 93d852b7e748c..11a7446bd6269 100644 --- a/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp +++ b/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp @@ -127,10 +127,14 @@ class NSIndexPathSyntheticFrontEnd : public SyntheticChildrenFrontEnd { bool MightHaveChildren() override { return m_impl.m_mode != Mode::Invalid; } llvm::Expected GetIndexOfChildWithName(ConstString name) override { - const char *item_name = name.GetCString(); - uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto idx_or_err = ExtractIndexFromString(name.AsCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *idx_or_err; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); return idx; diff --git a/lldb/source/Plugins/Language/ObjC/NSSet.cpp b/lldb/source/Plugins/Language/ObjC/NSSet.cpp index 71674ea63f4ca..2856e36c11c8b 100644 --- a/lldb/source/Plugins/Language/ObjC/NSSet.cpp +++ b/lldb/source/Plugins/Language/ObjC/NSSet.cpp @@ -389,10 +389,14 @@ lldb_private::formatters::NSSetISyntheticFrontEnd::~NSSetISyntheticFrontEnd() { llvm::Expected lldb_private::formatters::NSSetISyntheticFrontEnd::GetIndexOfChildWithName( ConstString name) { - const char *item_name = name.GetCString(); - uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto idx_or_err = ExtractIndexFromString(name.AsCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *idx_or_err; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); return idx; @@ -524,10 +528,14 @@ lldb_private::formatters::NSCFSetSyntheticFrontEnd::NSCFSetSyntheticFrontEnd( llvm::Expected lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetIndexOfChildWithName( ConstString name) { - const char *item_name = name.GetCString(); - const uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto idx_or_err = ExtractIndexFromString(name.AsCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *idx_or_err; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); return idx; @@ -660,10 +668,14 @@ lldb_private::formatters::GenericNSSetMSyntheticFrontEnd:: template llvm::Expected lldb_private::formatters::GenericNSSetMSyntheticFrontEnd< D32, D64>::GetIndexOfChildWithName(ConstString name) { - const char *item_name = name.GetCString(); - uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto idx_or_err = ExtractIndexFromString(name.AsCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *idx_or_err; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); return idx; >From 2a69d25f1c88cad059fa7cab4ad260726e1dde0a Mon Sep 17 00:00:00 2001 From: Charles Zablit Date: Tue, 6 May 2025 12:52:46 +0100 Subject: [PATCH 2/2] switch to using std::optional --- .../lldb/DataFormatters/FormattersHelpers.h | 2 +- .../DataFormatters/FormattersHelpers.cpp | 10 +++--- lldb/source/DataFormatters/VectorType.cpp | 7 ++-- .../Language/CPlusPlus/GenericBitset.cpp | 7 ++-- .../Language/CPlusPlus/GenericOptional.cpp | 7 ++-- .../CPlusPlus/LibCxxInitializerList.cpp | 7 ++-- .../Plugins/Language/CPlusPlus/LibCxxList.cpp | 7 ++-- .../Plugins/Language/CPlusPlus/LibCxxMap.cpp | 7 ++-- .../Language/CPlusPlus/LibCxxProxyArray.cpp | 7 ++-- .../Language/CPlusPlus/LibCxxSliceArray.cpp | 7 ++-- .../Plugins/Language/CPlusPlus/LibCxxSpan.cpp | 7 ++-- .../Language/CPlusPlus/LibCxxTuple.cpp | 7 ++-- .../Language/CPlusPlus/LibCxxUnorderedMap.cpp | 7 ++-- .../Language/CPlusPlus/LibCxxValarray.cpp | 7 ++-- .../Language/CPlusPlus/LibCxxVariant.cpp | 7 ++-- .../Language/CPlusPlus/LibCxxVector.cpp | 14 ++++---- .../Language/CPlusPlus/LibStdcppTuple.cpp | 7 ++-- lldb/source/Plugins/Language/ObjC/NSArray.cpp | 14 ++++---- .../Plugins/Language/ObjC/NSDictionary.cpp | 35 ++++++++----------- .../Plugins/Language/ObjC/NSIndexPath.cpp | 7 ++-- lldb/source/Plugins/Language/ObjC/NSSet.cpp | 21 +++++------ 21 files changed, 87 insertions(+), 114 deletions(-) diff --git a/lldb/include/lldb/DataFormatters/FormattersHelpers.h b/lldb/include/lldb/DataFormatters/FormattersHelpers.h index 82aae705e2a59..42699d0a0b1b1 100644 --- a/lldb/include/lldb/DataFormatters/FormattersHelpers.h +++ b/lldb/include/lldb/DataFormatters/FormattersHelpers.h @@ -53,7 +53,7 @@ void AddFilter(TypeCategoryImpl::SharedPointer category_sp, llvm::StringRef type_name, ScriptedSyntheticChildren::Flags flags, bool regex = false); -llvm::Expected ExtractIndexFromString(const char *item_name); +std::optional ExtractIndexFromString(const char *item_name); Address GetArrayAddressOrPointerValue(ValueObject &valobj); diff --git a/lldb/source/DataFormatters/FormattersHelpers.cpp b/lldb/source/DataFormatters/FormattersHelpers.cpp index 5e29794c291be..3250ac35ef822 100644 --- a/lldb/source/DataFormatters/FormattersHelpers.cpp +++ b/lldb/source/DataFormatters/FormattersHelpers.cpp @@ -97,19 +97,19 @@ void lldb_private::formatters::AddFilter( category_sp->AddTypeFilter(type_name, match_type, filter_sp); } -llvm::Expected +std::optional lldb_private::formatters::ExtractIndexFromString(const char *item_name) { if (!item_name || !*item_name) - return llvm::createStringError("String has no item named '%s'", item_name); + return std::nullopt; if (*item_name != '[') - return llvm::createStringError("String has no item named '%s'", item_name); + return std::nullopt; item_name++; char *endptr = nullptr; unsigned long int idx = ::strtoul(item_name, &endptr, 0); if (idx == 0 && endptr == item_name) - return llvm::createStringError("String has no item named '%s'", item_name); + return std::nullopt; if (idx == ULONG_MAX) - return llvm::createStringError("String has no item named '%s'", item_name); + return std::nullopt; return idx; } diff --git a/lldb/source/DataFormatters/VectorType.cpp b/lldb/source/DataFormatters/VectorType.cpp index 3fa616b9d9b14..8a842b8675f57 100644 --- a/lldb/source/DataFormatters/VectorType.cpp +++ b/lldb/source/DataFormatters/VectorType.cpp @@ -270,13 +270,12 @@ class VectorTypeSyntheticFrontEnd : public SyntheticChildrenFrontEnd { } llvm::Expected GetIndexOfChildWithName(ConstString name) override { - auto idx_or_err = ExtractIndexFromString(name.AsCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - uint32_t idx = *idx_or_err; + uint32_t idx = *optional_idx; if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); diff --git a/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp b/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp index ebcac0a48bd30..f2521ec750875 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp @@ -29,13 +29,12 @@ class GenericBitsetFrontEnd : public SyntheticChildrenFrontEnd { GenericBitsetFrontEnd(ValueObject &valobj, StdLib stdlib); llvm::Expected GetIndexOfChildWithName(ConstString name) override { - auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return *idx_or_err; + return *optional_idx; } lldb::ChildCacheState Update() override; diff --git a/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp b/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp index bfcc15ae805e7..b1fdc0fe37763 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp @@ -39,13 +39,12 @@ class GenericOptionalFrontend : public SyntheticChildrenFrontEnd { llvm::Expected GetIndexOfChildWithName(ConstString name) override { if (name == "$$dereference$$") return 0; - auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return *idx_or_err; + return *optional_idx; } llvm::Expected CalculateNumChildren() override { diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp index 04891d7d094f3..d952688f381f5 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp @@ -108,13 +108,12 @@ lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd:: return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return *idx_or_err; + return *optional_idx; } lldb_private::SyntheticChildrenFrontEnd * diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp index 9d4773c56b3e3..30db5f15c388f 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp @@ -107,13 +107,12 @@ class ListIterator { class AbstractListFrontEnd : public SyntheticChildrenFrontEnd { public: llvm::Expected GetIndexOfChildWithName(ConstString name) override { - auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return *idx_or_err; + return *optional_idx; } lldb::ChildCacheState Update() override; diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp index 9c3694e2448fc..41441dfbc7180 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp @@ -395,13 +395,12 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() { llvm::Expected lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: GetIndexOfChildWithName(ConstString name) { - auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return *idx_or_err; + return *optional_idx; } SyntheticChildrenFrontEnd * diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxProxyArray.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxProxyArray.cpp index a4ded6e0d7d85..a3d34c6e76d1e 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxProxyArray.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxProxyArray.cpp @@ -179,13 +179,12 @@ lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEnd:: if (!m_base) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return *idx_or_err; + return *optional_idx; } lldb_private::SyntheticChildrenFrontEnd * diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxSliceArray.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxSliceArray.cpp index 309d91ae02ecb..3bdb099d21012 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxSliceArray.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxSliceArray.cpp @@ -150,13 +150,12 @@ lldb_private::formatters::LibcxxStdSliceArraySyntheticFrontEnd:: if (!m_start) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return *idx_or_err; + return *optional_idx; } lldb_private::SyntheticChildrenFrontEnd * diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp index 26e8c48b4f8eb..496bbd07304d6 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp @@ -133,13 +133,12 @@ llvm::Expected lldb_private::formatters:: if (!m_start) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return *idx_or_err; + return *optional_idx; } lldb_private::SyntheticChildrenFrontEnd * diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp index 235a26da50a5f..ebc6d92aabe05 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp @@ -21,13 +21,12 @@ class TupleFrontEnd: public SyntheticChildrenFrontEnd { } llvm::Expected GetIndexOfChildWithName(ConstString name) override { - auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return *idx_or_err; + return *optional_idx; } lldb::ChildCacheState Update() override; diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp index de37aeda25168..aad387137ea50 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp @@ -294,13 +294,12 @@ lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::Update() { llvm::Expected lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: GetIndexOfChildWithName(ConstString name) { - auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return *idx_or_err; + return *optional_idx; } SyntheticChildrenFrontEnd * diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxValarray.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxValarray.cpp index 4c87e167589da..3a8bf3a45ce54 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxValarray.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxValarray.cpp @@ -129,13 +129,12 @@ lldb_private::formatters::LibcxxStdValarraySyntheticFrontEnd:: if (!m_start || !m_finish) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return *idx_or_err; + return *optional_idx; } lldb_private::SyntheticChildrenFrontEnd * diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp index 12071321189b9..30fec4e2dde0f 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp @@ -203,13 +203,12 @@ class VariantFrontEnd : public SyntheticChildrenFrontEnd { } llvm::Expected GetIndexOfChildWithName(ConstString name) override { - auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return *idx_or_err; + return *optional_idx; } lldb::ChildCacheState Update() override; diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp index 228647b8961b1..4bcdf01c221a3 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp @@ -170,13 +170,12 @@ lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd:: if (!m_start || !m_finish) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return *idx_or_err; + return *optional_idx; } lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd:: @@ -274,13 +273,12 @@ lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd:: if (!m_count || !m_base_data_address) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - auto idx_or_err = ExtractIndexFromString(name.AsCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - uint32_t idx = *idx_or_err; + uint32_t idx = *optional_idx; if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp index 4f05dc1934958..cf72265bfbad3 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp @@ -98,13 +98,12 @@ LibStdcppTupleSyntheticFrontEnd::CalculateNumChildren() { llvm::Expected LibStdcppTupleSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) { - auto idx_or_err = formatters::ExtractIndexFromString(name.GetCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return *idx_or_err; + return *optional_idx; } SyntheticChildrenFrontEnd * diff --git a/lldb/source/Plugins/Language/ObjC/NSArray.cpp b/lldb/source/Plugins/Language/ObjC/NSArray.cpp index 465639a21d943..25376e064879d 100644 --- a/lldb/source/Plugins/Language/ObjC/NSArray.cpp +++ b/lldb/source/Plugins/Language/ObjC/NSArray.cpp @@ -528,13 +528,12 @@ lldb_private::formatters::GenericNSArrayMSyntheticFrontEnd::Update() { llvm::Expected lldb_private::formatters::NSArrayMSyntheticFrontEndBase:: GetIndexOfChildWithName(ConstString name) { - auto idx_or_err = ExtractIndexFromString(name.AsCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - uint32_t idx = *idx_or_err; + uint32_t idx = *optional_idx; if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); @@ -620,13 +619,12 @@ template llvm::Expected lldb_private::formatters::GenericNSArrayISyntheticFrontEnd< D32, D64, Inline>::GetIndexOfChildWithName(ConstString name) { - auto idx_or_err = ExtractIndexFromString(name.AsCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - uint32_t idx = *idx_or_err; + uint32_t idx = *optional_idx; if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); diff --git a/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp b/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp index babf638a54ee6..ef1c2c89fe125 100644 --- a/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp +++ b/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp @@ -587,13 +587,12 @@ lldb_private::formatters::NSDictionaryISyntheticFrontEnd:: llvm::Expected lldb_private::formatters:: NSDictionaryISyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) { - auto idx_or_err = ExtractIndexFromString(name.AsCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - uint32_t idx = *idx_or_err; + uint32_t idx = *optional_idx; if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); @@ -726,13 +725,12 @@ lldb_private::formatters::NSCFDictionarySyntheticFrontEnd:: llvm::Expected lldb_private::formatters:: NSCFDictionarySyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) { - auto idx_or_err = ExtractIndexFromString(name.AsCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - uint32_t idx = *idx_or_err; + uint32_t idx = *optional_idx; if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); @@ -864,13 +862,12 @@ lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd:: llvm::Expected lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd:: GetIndexOfChildWithName(ConstString name) { - auto idx_or_err = ExtractIndexFromString(name.AsCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - uint32_t idx = *idx_or_err; + uint32_t idx = *optional_idx; if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); @@ -1070,13 +1067,12 @@ template llvm::Expected lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd< D32, D64>::GetIndexOfChildWithName(ConstString name) { - auto idx_or_err = ExtractIndexFromString(name.AsCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - uint32_t idx = *idx_or_err; + uint32_t idx = *optional_idx; if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); @@ -1233,13 +1229,12 @@ lldb_private::formatters::Foundation1100:: llvm::Expected lldb_private::formatters::Foundation1100:: NSDictionaryMSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) { - auto idx_or_err = ExtractIndexFromString(name.AsCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - uint32_t idx = *idx_or_err; + uint32_t idx = *optional_idx; if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); diff --git a/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp b/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp index 11a7446bd6269..b5360195e91d2 100644 --- a/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp +++ b/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp @@ -127,13 +127,12 @@ class NSIndexPathSyntheticFrontEnd : public SyntheticChildrenFrontEnd { bool MightHaveChildren() override { return m_impl.m_mode != Mode::Invalid; } llvm::Expected GetIndexOfChildWithName(ConstString name) override { - auto idx_or_err = ExtractIndexFromString(name.AsCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - uint32_t idx = *idx_or_err; + uint32_t idx = *optional_idx; if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); diff --git a/lldb/source/Plugins/Language/ObjC/NSSet.cpp b/lldb/source/Plugins/Language/ObjC/NSSet.cpp index 2856e36c11c8b..7d814e656dc5f 100644 --- a/lldb/source/Plugins/Language/ObjC/NSSet.cpp +++ b/lldb/source/Plugins/Language/ObjC/NSSet.cpp @@ -389,13 +389,12 @@ lldb_private::formatters::NSSetISyntheticFrontEnd::~NSSetISyntheticFrontEnd() { llvm::Expected lldb_private::formatters::NSSetISyntheticFrontEnd::GetIndexOfChildWithName( ConstString name) { - auto idx_or_err = ExtractIndexFromString(name.AsCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - uint32_t idx = *idx_or_err; + uint32_t idx = *optional_idx; if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); @@ -528,13 +527,12 @@ lldb_private::formatters::NSCFSetSyntheticFrontEnd::NSCFSetSyntheticFrontEnd( llvm::Expected lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetIndexOfChildWithName( ConstString name) { - auto idx_or_err = ExtractIndexFromString(name.AsCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - uint32_t idx = *idx_or_err; + uint32_t idx = *optional_idx; if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); @@ -668,13 +666,12 @@ lldb_private::formatters::GenericNSSetMSyntheticFrontEnd:: template llvm::Expected lldb_private::formatters::GenericNSSetMSyntheticFrontEnd< D32, D64>::GetIndexOfChildWithName(ConstString name) { - auto idx_or_err = ExtractIndexFromString(name.AsCString()); - if (!idx_or_err) { - llvm::consumeError(idx_or_err.takeError()); + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - uint32_t idx = *idx_or_err; + uint32_t idx = *optional_idx; if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); From lldb-commits at lists.llvm.org Tue May 6 04:58:34 2025 From: lldb-commits at lists.llvm.org (Charles Zablit via lldb-commits) Date: Tue, 06 May 2025 04:58:34 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Upgrade ExtractIndexFromString to use llvm::Expected (PR #138297) In-Reply-To: Message-ID: <6819f96a.a70a0220.24d78a.769d@mx.google.com> https://github.com/charles-zablit updated https://github.com/llvm/llvm-project/pull/138297 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Tue May 6 05:00:04 2025 From: lldb-commits at lists.llvm.org (Charles Zablit via lldb-commits) Date: Tue, 06 May 2025 05:00:04 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Upgrade ExtractIndexFromString to use llvm::Expected (PR #138297) In-Reply-To: Message-ID: <6819f9c4.170a0220.1f29fc.29d3@mx.google.com> ================ @@ -270,10 +270,14 @@ class VectorTypeSyntheticFrontEnd : public SyntheticChildrenFrontEnd { } llvm::Expected GetIndexOfChildWithName(ConstString name) override { - const char *item_name = name.GetCString(); - uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto idx_or_err = ExtractIndexFromString(name.AsCString()); + if (!idx_or_err) { + llvm::consumeError(idx_or_err.takeError()); + return llvm::createStringError("Type has no child named '%s'", ---------------- charles-zablit wrote: I ended up switching to `std::optional` following the different discussions. The error message are not very descriptive, and not finding an index did not mean there was an error, but rather that the name was not found in the string. https://github.com/llvm/llvm-project/pull/138297 From lldb-commits at lists.llvm.org Tue May 6 05:51:44 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 05:51:44 -0700 (PDT) Subject: [Lldb-commits] [lldb] [DRAFT][lldb][RPC] Design doc for upstreaming PR (PR #138612) In-Reply-To: Message-ID: <681a05e0.170a0220.249b01.444a@mx.google.com> https://github.com/DavidSpickett edited https://github.com/llvm/llvm-project/pull/138612 From lldb-commits at lists.llvm.org Tue May 6 05:51:44 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 05:51:44 -0700 (PDT) Subject: [Lldb-commits] [lldb] [DRAFT][lldb][RPC] Design doc for upstreaming PR (PR #138612) In-Reply-To: Message-ID: <681a05e0.170a0220.171368.2fac@mx.google.com> https://github.com/DavidSpickett commented: Thanks for writing this up. When we get to a point where PRs have landed you can just change "will be" / "will do" language to "is" / "does" and it should be a good reference for the future. One thing that is missing right now is how to test it. Basic things just like what format of test does it use (shell I think) and is it added to a ninja check target. Also, is there a way for a developer to set up an lldb-rpc based session? If we have an issue that only happens over lldb-rpc. Which in theory we won't, but we might think we do and need to check that. In other words - if the only way to do that is to use XCode - that would not be good. https://github.com/llvm/llvm-project/pull/138612 From lldb-commits at lists.llvm.org Tue May 6 05:51:45 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 05:51:45 -0700 (PDT) Subject: [Lldb-commits] [lldb] [DRAFT][lldb][RPC] Design doc for upstreaming PR (PR #138612) In-Reply-To: Message-ID: <681a05e1.630a0220.3ab385.09f6@mx.google.com> ================ @@ -0,0 +1,94 @@ +LLDB RPC Upstreaming Design Doc +=============================== + +This document aims to explain the general structure of the upstreaming patches for adding LLDB RPC. The 2 primary concepts explained here will be: + +* How LLDB RPC is used +* How the ``lldb-rpc-gen`` works and what it outputs + +LLDB RPC +********* + +LLDB RPC is a framework by which processes can communicate with LLDB out of process while maintaining compatibility with the SB API. More details are explained in the `RFC`_ for upstreaming LLDB RPC, but the main focus in this doc for this section will be how exactly the code is structured for the PRs that will upstream this code. + +The ``lldb-rpc-gen`` tool +************************* + +``lldb-rpc-gen`` is the tool that generates the main client and server interfaces for LLDB RPC. It is a ``ClangTool`` that reads all SB API header files and their functions and outputs the client/server interfaces and certain other pieces of code, such as RPC-specfic versions of Python bindings used for the test suite. There's 3 main components behind ``lldb-rpc-gen``: + +1. The ``lldb-rpc-gen`` tool itself, which contains the main driver that uses the ``ClangTool``. +2. The code that generates all interfaces, which we call "emitters". All generated code for the interfaces are in C++, so the server side has one emitter for its generated source code and another for its generated header code. The client side has the same. +3. All common code shared between all emitters, such as helper methods and information about exceptions to take when emitting. + +The `current PR`_ up for upstreaming LLDB RPC upstreams a subset of the code used for the tool. It upstreams the ``lldb-rpc-gen`` tool and all code needed for the server side emitters. Here's an example of what ``lldb-rpc-gen`` will output for the server side interface: + +Input +----- + +We'll use ``SBDebugger::CreateTarget(const char *filename)`` as an example. ``lldb-rpc-gen`` will read this method from ``SBDebugger.h``. The output is as follows. + +Source Code Output +------------------ + +:: + + bool rpc_server::_ZN4lldb10SBDebugger12CreateTargetEPKc::HandleRPCCall(rpc_common::Connection &connection, RPCStream &send, RPCStream &response) { + // 1) Make local storage for incoming function arguments + lldb::SBDebugger *this_ptr = nullptr; + rpc_common::ConstCharPointer filename; + // 2) Decode all function arguments + this_ptr = RPCServerObjectDecoder(send, rpc_common::RPCPacket::ValueType::Argument); + if (!this_ptr) + return false; + if (!RPCValueDecoder(send, rpc_common::RPCPacket::ValueType::Argument, filename)) + return false; + // 3) Call the method and encode the return value + lldb::SBTarget && __result = this_ptr->CreateTarget(filename.c_str()); + RPCServerObjectEncoder(response, rpc_common::RPCPacket::ValueType::ReturnValue, std::move(__result)); + return true; + } + +Function signature +~~~~~~~~~~~~~~~~~~ + +All server-side source code functions have a function signature that take the format ``bool rpc_server::::HandleRPCCall(rpc_common::Connection &connection, RPCStream &send, RPCStream &response)``. Here the ``connection`` is what's maintained between the client and server. The ``send`` variable is a byte stream that carries information sent from the client. ``response`` is also a byte stream that will be populated with the return value obtained from the call into the SB API function that will be sent back to the client. ---------------- DavidSpickett wrote: Why mangled function name not just function name? It's in `rpc_server::` so at first glance, it doesn't seem like it needs to be mangled. https://github.com/llvm/llvm-project/pull/138612 From lldb-commits at lists.llvm.org Tue May 6 06:04:21 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Tue, 06 May 2025 06:04:21 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb/Host] Enable inheriting "non-inheritable" FDs (PR #126935) In-Reply-To: Message-ID: <681a08d5.170a0220.31dbab.16f4@mx.google.com> labath wrote: Thank you. https://github.com/llvm/llvm-project/pull/126935 From lldb-commits at lists.llvm.org Tue May 6 06:04:35 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Tue, 06 May 2025 06:04:35 -0700 (PDT) Subject: [Lldb-commits] [lldb] 1eaa289 - [lldb/Host] Enable inheriting "non-inheritable" FDs (#126935) Message-ID: <681a08e3.170a0220.d647e.2225@mx.google.com> Author: Pavel Labath Date: 2025-05-06T15:04:30+02:00 New Revision: 1eaa289472aaddbeabcde10f89cffb161c2dca55 URL: https://github.com/llvm/llvm-project/commit/1eaa289472aaddbeabcde10f89cffb161c2dca55 DIFF: https://github.com/llvm/llvm-project/commit/1eaa289472aaddbeabcde10f89cffb161c2dca55.diff LOG: [lldb/Host] Enable inheriting "non-inheritable" FDs (#126935) Currently we're creating inheritable (`~FD_CLOEXEC`) file descriptors in the (few) cases where we need to pass an FD to a subprocess. The problem with these is that, in a multithreaded application such as lldb, there's essentially no way to prevent them from being leaked into processes other than the intended one. A safer (though still not completely safe) approach is to mark the descriptors as FD_CLOEXEC and only clear this flag in the subprocess. We currently have something that almost does that, which is the ability to add a `DuplicateFileAction` to our `ProcessLaunchInfo` struct (the duplicated file descriptor will be created with the flag cleared). The problem with *that* is that this approach is completely incompatible with Windows. Windows equivalents of file descriptors are `HANDLE`s, but these do not have user controlled values -- applications are expected to work with whatever HANDLE values are assigned by the OS. In unix terms, there is no equivalent to the `dup2` syscall (only `dup`). To find a way out of this conundrum, and create a miniscule API surface that works uniformly across platforms, this PR proposes to extend the `DuplicateFileAction` API to support duplicating a file descriptor onto itself. Currently, this operation does nothing (it leaves the FD_CLOEXEC flag set), because that's how `dup2(fd, fd)` behaves, but I think it's not completely unreasonable to say that this operation should clear the FD_CLOEXEC flag, just like it would do if one was using different fd values. This would enable us to pass a windows HANDLE as itself through the ProcessLaunchInfo API. This PR implements the unix portion of this idea. Macos and non-macos launchers are updated to clear FD_CLOEXEC flag when duplicating a file descriptor onto itself, and I've created a test which enables passing a FD_CLOEXEC file descritor to the subprocess. For the windows portion, please see the follow-up PR. Added: Modified: lldb/source/Host/macosx/objcxx/Host.mm lldb/source/Host/posix/ProcessLauncherPosixFork.cpp lldb/unittests/Host/HostTest.cpp Removed: ################################################################################ diff --git a/lldb/source/Host/macosx/objcxx/Host.mm b/lldb/source/Host/macosx/objcxx/Host.mm index bb270f6a44e43..e187bf98188ae 100644 --- a/lldb/source/Host/macosx/objcxx/Host.mm +++ b/lldb/source/Host/macosx/objcxx/Host.mm @@ -1100,7 +1100,7 @@ static bool AddPosixSpawnFileAction(void *_file_actions, const FileAction *info, else if (info->GetActionArgument() == -1) error = Status::FromErrorString( "invalid duplicate fd for posix_spawn_file_actions_adddup2(...)"); - else { + else if (info->GetFD() != info->GetActionArgument()) { error = Status(::posix_spawn_file_actions_adddup2(file_actions, info->GetFD(), info->GetActionArgument()), @@ -1110,6 +1110,15 @@ static bool AddPosixSpawnFileAction(void *_file_actions, const FileAction *info, "error: {0}, posix_spawn_file_actions_adddup2 " "(action={1}, fd={2}, dup_fd={3})", error, file_actions, info->GetFD(), info->GetActionArgument()); + } else { + error = + Status(::posix_spawn_file_actions_addinherit_np(file_actions, info->GetFD()), + eErrorTypePOSIX); + if (error.Fail()) + LLDB_LOG(log, + "error: {0}, posix_spawn_file_actions_addinherit_np " + "(action={1}, fd={2})", + error, file_actions, info->GetFD()); } break; diff --git a/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp b/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp index 8c6d503fc7fe2..698524349e16a 100644 --- a/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp +++ b/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp @@ -17,6 +17,7 @@ #include "llvm/Support/Errno.h" #include +#include #include #include #include @@ -122,8 +123,14 @@ struct ForkLaunchInfo { ExitWithError(error_fd, "close"); break; case FileAction::eFileActionDuplicate: - if (dup2(action.fd, action.arg) == -1) - ExitWithError(error_fd, "dup2"); + if (action.fd != action.arg) { + if (dup2(action.fd, action.arg) == -1) + ExitWithError(error_fd, "dup2"); + } else { + if (fcntl(action.fd, F_SETFD, + fcntl(action.fd, F_GETFD) & ~FD_CLOEXEC) == -1) + ExitWithError(error_fd, "fcntl"); + } break; case FileAction::eFileActionOpen: DupDescriptor(error_fd, action.path.c_str(), action.fd, action.arg); diff --git a/lldb/unittests/Host/HostTest.cpp b/lldb/unittests/Host/HostTest.cpp index ed1df6de001ea..222de62ab6697 100644 --- a/lldb/unittests/Host/HostTest.cpp +++ b/lldb/unittests/Host/HostTest.cpp @@ -9,8 +9,10 @@ #include "lldb/Host/Host.h" #include "TestingSupport/SubsystemRAII.h" #include "lldb/Host/FileSystem.h" +#include "lldb/Host/Pipe.h" #include "lldb/Host/ProcessLaunchInfo.h" #include "lldb/Utility/ProcessInfo.h" +#include "llvm/ADT/Twine.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Testing/Support/Error.h" @@ -87,3 +89,41 @@ TEST(Host, LaunchProcessSetsArgv0) { ASSERT_THAT_ERROR(Host::LaunchProcess(info).takeError(), Succeeded()); ASSERT_THAT(exit_status.get_future().get(), 0); } + +#ifdef LLVM_ON_UNIX +TEST(Host, LaunchProcessDuplicatesHandle) { + static constexpr llvm::StringLiteral test_msg("Hello subprocess!"); + + SubsystemRAII subsystems; + + if (test_arg) { + Pipe pipe(LLDB_INVALID_PIPE, (lldb::pipe_t)test_arg.getValue()); + llvm::Expected bytes_written = + pipe.Write(test_msg.data(), test_msg.size()); + if (bytes_written && *bytes_written == test_msg.size()) + exit(0); + exit(1); + } + Pipe pipe; + ASSERT_THAT_ERROR(pipe.CreateNew(/*child_process_inherit=*/false).takeError(), + llvm::Succeeded()); + ProcessLaunchInfo info; + info.SetExecutableFile(FileSpec(TestMainArgv0), + /*add_exe_file_as_first_arg=*/true); + info.GetArguments().AppendArgument( + "--gtest_filter=Host.LaunchProcessDuplicatesHandle"); + info.GetArguments().AppendArgument( + ("--test-arg=" + llvm::Twine((uint64_t)pipe.GetWritePipe())).str()); + info.AppendDuplicateFileAction((uint64_t)pipe.GetWritePipe(), + (uint64_t)pipe.GetWritePipe()); + info.SetMonitorProcessCallback(&ProcessLaunchInfo::NoOpMonitorCallback); + ASSERT_THAT_ERROR(Host::LaunchProcess(info).takeError(), llvm::Succeeded()); + pipe.CloseWriteFileDescriptor(); + + char msg[100]; + llvm::Expected bytes_read = + pipe.Read(msg, sizeof(msg), std::chrono::seconds(10)); + ASSERT_THAT_EXPECTED(bytes_read, llvm::Succeeded()); + ASSERT_EQ(llvm::StringRef(msg, *bytes_read), test_msg); +} +#endif From lldb-commits at lists.llvm.org Tue May 6 06:04:36 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Tue, 06 May 2025 06:04:36 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb/Host] Enable inheriting "non-inheritable" FDs (PR #126935) In-Reply-To: Message-ID: <681a08e4.050a0220.2166c1.b772@mx.google.com> https://github.com/labath closed https://github.com/llvm/llvm-project/pull/126935 From lldb-commits at lists.llvm.org Tue May 6 06:06:28 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Tue, 06 May 2025 06:06:28 -0700 (PDT) Subject: [Lldb-commits] [lldb] 6a99d81 - [lldb/docs] Fix/improve the gdb command map for dynamic types (#138538) Message-ID: <681a0954.170a0220.260e28.132b@mx.google.com> Author: Pavel Labath Date: 2025-05-06T15:06:24+02:00 New Revision: 6a99d817204dfa39afc42f1f6a810d82f6a8794f URL: https://github.com/llvm/llvm-project/commit/6a99d817204dfa39afc42f1f6a810d82f6a8794f DIFF: https://github.com/llvm/llvm-project/commit/6a99d817204dfa39afc42f1f6a810d82f6a8794f.diff LOG: [lldb/docs] Fix/improve the gdb command map for dynamic types (#138538) The setting and option value names were wrong. I'm assuming this changed over time, but I haven't tried to figure out when. Added: Modified: lldb/docs/use/map.rst Removed: ################################################################################ diff --git a/lldb/docs/use/map.rst b/lldb/docs/use/map.rst index ed285b2d1f6e9..c648b212006e0 100644 --- a/lldb/docs/use/map.rst +++ b/lldb/docs/use/map.rst @@ -800,16 +800,24 @@ Print the dynamic type of the result of an expression (gdb) p someCPPObjectPtrOrReference (Only works for C++ objects) +LLDB does this automatically if determining the dynamic type does not require +running the target (in C++, running the target is never needed). This default is +controlled by the `target.prefer-dynamic-value` setting. If that is disabled, it +can be re-enabled on a per-command basis: + .. code-block:: shell - (lldb) expr -d 1 -- [SomeClass returnAnObject] - (lldb) expr -d 1 -- someCPPObjectPtrOrReference + (lldb) settings set target.prefer-dynamic-value no-dynamic-values + (lldb) frame variable -d no-run-target someCPPObjectPtrOrReference + (lldb) expr -d no-run-target -- someCPPObjectPtr -or set dynamic type printing to be the default: +Note that printing of the dynamic type of references is not possible with the +`expr` command. The workaround is to take the address of the reference and +instruct lldb to print the children of the resulting pointer. .. code-block:: shell - (lldb) settings set target.prefer-dynamic run-target + (lldb) expr -P1 -d no-run-target -- &someCPPObjectReference Call a function so you can stop at a breakpoint in it ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From lldb-commits at lists.llvm.org Tue May 6 06:06:30 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Tue, 06 May 2025 06:06:30 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb/docs] Fix/improve the gdb command map for dynamic types (PR #138538) In-Reply-To: Message-ID: <681a0956.170a0220.30d8cc.fd7c@mx.google.com> https://github.com/labath closed https://github.com/llvm/llvm-project/pull/138538 From lldb-commits at lists.llvm.org Tue May 6 06:15:56 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 06:15:56 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb[RPC] Upstream RPC server interface emitters (PR #138032) In-Reply-To: Message-ID: <681a0b8c.170a0220.126dfe.2702@mx.google.com> https://github.com/DavidSpickett edited https://github.com/llvm/llvm-project/pull/138032 From lldb-commits at lists.llvm.org Tue May 6 06:15:57 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 06:15:57 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb[RPC] Upstream RPC server interface emitters (PR #138032) In-Reply-To: Message-ID: <681a0b8d.050a0220.64430.b6c1@mx.google.com> ================ @@ -0,0 +1,73 @@ +//===-- RPCServerHeaderEmitter.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 "RPCServerHeaderEmitter.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 RPCServerHeaderEmitter::EmitMethod(const Method &method) { + const std::string &MangledName = method.MangledName; ---------------- DavidSpickett wrote: Why the mangled name? Add a comment explaining why it cannot be the unmangled name. https://github.com/llvm/llvm-project/pull/138032 From lldb-commits at lists.llvm.org Tue May 6 06:15:57 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 06:15:57 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb[RPC] Upstream RPC server interface emitters (PR #138032) In-Reply-To: Message-ID: <681a0b8d.050a0220.151ca4.bdc7@mx.google.com> https://github.com/DavidSpickett commented: Looks OK in isolation, a bit like looking at a TableGen backend, easier to judge the output than the emitters. Please look over the FIXMEs and see if anything can be done right now, I highlighted one that is potentially a problem but the rest don't seem important. See if you agree. Also please remove any that are no longer relevant. https://github.com/llvm/llvm-project/pull/138032 From lldb-commits at lists.llvm.org Tue May 6 06:15:58 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 06:15:58 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb[RPC] Upstream RPC server interface emitters (PR #138032) In-Reply-To: Message-ID: <681a0b8e.050a0220.309e8a.9560@mx.google.com> ================ @@ -0,0 +1,592 @@ +//===-- RPCServerSourceEmitter.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 "RPCServerSourceEmitter.h" +#include "RPCCommon.h" + +#include "clang/AST/AST.h" +#include "clang/Frontend/CompilerInstance.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace clang; +using namespace lldb_rpc_gen; + +// For methods with pointer return types, it's important that we know how big +// the type of the pointee is. We must correctly size a buffer (in the form of a +// Bytes object) before we can actually use it. +static const std::map MethodsWithPointerReturnTypes = { ---------------- DavidSpickett wrote: Is the size of the pointee type not something we can get from clang? Maybe it does know the type but sizeof the return type is as much as we can do? Which would be sizeof(void*) anyway. https://github.com/llvm/llvm-project/pull/138032 From lldb-commits at lists.llvm.org Tue May 6 06:15:57 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 06:15:57 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb[RPC] Upstream RPC server interface emitters (PR #138032) In-Reply-To: Message-ID: <681a0b8d.170a0220.2cabd8.297c@mx.google.com> ================ @@ -0,0 +1,592 @@ +//===-- RPCServerSourceEmitter.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 "RPCServerSourceEmitter.h" +#include "RPCCommon.h" + +#include "clang/AST/AST.h" +#include "clang/Frontend/CompilerInstance.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace clang; +using namespace lldb_rpc_gen; + +// For methods with pointer return types, it's important that we know how big +// the type of the pointee is. We must correctly size a buffer (in the form of a +// Bytes object) before we can actually use it. +static const std::map MethodsWithPointerReturnTypes = { + {"_ZN4lldb12SBModuleSpec12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 + {"_ZNK4lldb8SBModule12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 +}; + +void RPCServerSourceEmitter::EmitMethod(const Method &method) { + if (method.ContainsFunctionPointerParameter) + EmitCallbackFunction(method); + + EmitCommentHeader(method); + EmitFunctionHeader(method); + EmitFunctionBody(method); + EmitFunctionFooter(); +} + +void RPCServerSourceEmitter::EmitCommentHeader(const Method &method) { + std::string CommentLine; + llvm::raw_string_ostream CommentStream(CommentLine); + + CommentStream << "// " << method.QualifiedName << "(" + << method.CreateParamListAsString(eServer) << ")"; + if (method.IsConst) + CommentStream << " const"; + + EmitLine("//------------------------------------------------------------"); + EmitLine(CommentLine); + EmitLine("//------------------------------------------------------------"); +} + +void RPCServerSourceEmitter::EmitFunctionHeader(const Method &method) { + std::string FunctionHeader; + llvm::raw_string_ostream FunctionHeaderStream(FunctionHeader); + FunctionHeaderStream + << "bool rpc_server::" << method.MangledName + << "::HandleRPCCall(rpc_common::Connection &connection, RPCStream " + "&send, RPCStream &response) {"; + EmitLine(FunctionHeader); + IndentLevel++; +} + +void RPCServerSourceEmitter::EmitFunctionBody(const Method &method) { + EmitLine("// 1) Make local storage for incoming function arguments"); + EmitStorageForParameters(method); + EmitLine("// 2) Decode all function arguments"); + EmitDecodeForParameters(method); + EmitLine("// 3) Call the method and encode the return value"); + EmitMethodCallAndEncode(method); +} + +void RPCServerSourceEmitter::EmitFunctionFooter() { + EmitLine("return true;"); + IndentLevel--; + EmitLine("}"); +} + +void RPCServerSourceEmitter::EmitStorageForParameters(const Method &method) { + // If we have an instance method and it isn't a constructor, we'll need to + // emit a "this" pointer. + if (method.IsInstance && !method.IsCtor) + EmitStorageForOneParameter(method.ThisType, "this_ptr", method.Policy, + /* IsFollowedByLen = */ false); + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + EmitStorageForOneParameter(Iter->Type, Iter->Name, method.Policy, + Iter->IsFollowedByLen); + // Skip over the length parameter, we don't emit it. + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) + Iter++; + } +} + +void RPCServerSourceEmitter::EmitStorageForOneParameter( + QualType ParamType, const std::string &ParamName, + const PrintingPolicy &Policy, bool IsFollowedByLen) { + // First, we consider `const char *`, `const char **`. They have special + // server-side types. + if (TypeIsConstCharPtr(ParamType)) { + EmitLine("rpc_common::ConstCharPointer " + ParamName + ";"); + return; + } else if (TypeIsConstCharPtrPtr(ParamType)) { + EmitLine("rpc_common::StringList " + ParamName + ";"); + return; + } + + QualType UnderlyingType = + lldb_rpc_gen::GetUnqualifiedUnderlyingType(ParamType); + const bool IsSBClass = lldb_rpc_gen::TypeIsSBClass(UnderlyingType); + + if (ParamType->isPointerType() && !IsSBClass) { + // Void pointer with no length is usually a baton for a callback. We're + // going to hold onto the pointer value so we can send it back to the + // client-side when we implement callbacks. + if (ParamType->isVoidPointerType() && !IsFollowedByLen) { + EmitLine("void * " + ParamName + " = nullptr;"); + return; + } + + if (!ParamType->isFunctionPointerType()) { + EmitLine("Bytes " + ParamName + ";"); + return; + } + + assert(ParamType->isFunctionPointerType() && "Unhandled pointer type"); + EmitLine("rpc_common::function_ptr_t " + ParamName + " = nullptr;"); + return; + } + + std::string StorageDeclaration; + llvm::raw_string_ostream StorageDeclarationStream(StorageDeclaration); + + UnderlyingType.print(StorageDeclarationStream, Policy); + StorageDeclarationStream << " "; + if (IsSBClass) + StorageDeclarationStream << "*"; + StorageDeclarationStream << ParamName; + if (IsSBClass) + StorageDeclarationStream << " = nullptr"; + else + StorageDeclarationStream << " = {}"; + StorageDeclarationStream << ";"; + EmitLine(StorageDeclaration); +} + +void RPCServerSourceEmitter::EmitDecodeForParameters(const Method &method) { + if (method.IsInstance && !method.IsCtor) + EmitDecodeForOneParameter(method.ThisType, "this_ptr", method.Policy); + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + EmitDecodeForOneParameter(Iter->Type, Iter->Name, method.Policy); + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) + Iter++; + } +} + +void RPCServerSourceEmitter::EmitDecodeForOneParameter( + QualType ParamType, const std::string &ParamName, + const PrintingPolicy &Policy) { + QualType UnderlyingType = + lldb_rpc_gen::GetUnqualifiedUnderlyingType(ParamType); + + if (TypeIsSBClass(UnderlyingType)) { + std::string DecodeLine; + llvm::raw_string_ostream DecodeLineStream(DecodeLine); + DecodeLineStream << ParamName << " = " + << "RPCServerObjectDecoder<"; + UnderlyingType.print(DecodeLineStream, Policy); + DecodeLineStream << ">(send, rpc_common::RPCPacket::ValueType::Argument);"; + EmitLine(DecodeLine); + EmitLine("if (!" + ParamName + ")"); + IndentLevel++; + EmitLine("return false;"); + IndentLevel--; + } else { + EmitLine("if (!RPCValueDecoder(send, " + "rpc_common::RPCPacket::ValueType::Argument, " + + ParamName + "))"); + IndentLevel++; + EmitLine("return false;"); + IndentLevel--; + } +} + +std::string RPCServerSourceEmitter::CreateMethodCall(const Method &method) { + std::string MethodCall; + llvm::raw_string_ostream MethodCallStream(MethodCall); + if (method.IsInstance) { + if (!method.IsCtor) + MethodCallStream << "this_ptr->"; + MethodCallStream << method.BaseName; + } else + MethodCallStream << method.QualifiedName; + + std::vector Args; + std::string FunctionPointerName; + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + std::string Arg; + // We must check for `const char *` and `const char **` first. + if (TypeIsConstCharPtr(Iter->Type)) { + // `const char *` is stored server-side as rpc_common::ConstCharPointer + Arg = Iter->Name + ".c_str()"; + } else if (TypeIsConstCharPtrPtr(Iter->Type)) { + // `const char **` is stored server-side as rpc_common::StringList + Arg = Iter->Name + ".argv()"; + } else if (lldb_rpc_gen::TypeIsSBClass(Iter->Type)) { + Arg = Iter->Name; + if (!Iter->Type->isPointerType()) + Arg = "*" + Iter->Name; + } else if (Iter->Type->isPointerType() && + !Iter->Type->isFunctionPointerType() && + (!Iter->Type->isVoidPointerType() || Iter->IsFollowedByLen)) { + // We move pointers between the server and client as 'Bytes' objects. + // Pointers with length arguments will have their length filled in below. + // Pointers with no length arguments are assumed to behave like an array + // with length of 1, except for void pointers which are handled + // differently. + Arg = "(" + Iter->Type.getAsString(method.Policy) + ")" + Iter->Name + + ".GetData()"; + } else if (Iter->Type->isFunctionPointerType()) { + // If we have a function pointer, we only want to pass something along if + // we got a real pointer. + Arg = Iter->Name + " ? " + method.MangledName + "_callback : nullptr"; + FunctionPointerName = Iter->Name; + } else if (Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen && + method.ContainsFunctionPointerParameter) { + // Assumptions: + // - This is assumed to be the baton for the function pointer. + // - This is assumed to come after the function pointer parameter. + // We always produce this regardless of the value of the baton argument. + Arg = "new CallbackInfo(" + FunctionPointerName + ", " + Iter->Name + + ", connection.GetConnectionID())"; + } else + Arg = Iter->Name; + + if (Iter->Type->isRValueReferenceType()) + Arg = "std::move(" + Arg + ")"; + Args.push_back(Arg); + + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) { + std::string LengthArg = Iter->Name + ".GetSize()"; + if (!Iter->Type->isVoidPointerType()) { + QualType UUT = lldb_rpc_gen::GetUnqualifiedUnderlyingType(Iter->Type); + LengthArg += " / sizeof(" + UUT.getAsString(method.Policy) + ")"; + } + Args.push_back(LengthArg); + Iter++; + } + } + MethodCallStream << "(" << llvm::join(Args, ", ") << ")"; + + return MethodCall; +} + +std::string RPCServerSourceEmitter::CreateEncodeLine(const std::string &Value, + bool IsEncodingSBClass) { + std::string EncodeLine; + llvm::raw_string_ostream EncodeLineStream(EncodeLine); + + if (IsEncodingSBClass) + EncodeLineStream << "RPCServerObjectEncoder("; + else + EncodeLineStream << "RPCValueEncoder("; + + EncodeLineStream + << "response, rpc_common::RPCPacket::ValueType::ReturnValue, "; + EncodeLineStream << Value; + EncodeLineStream << ");"; + return EncodeLine; +} + +// There are 4 cases to consider: +// - const SBClass &: No need to do anything. +// - const foo &: No need to do anything. +// - SBClass &: The server and the client hold on to IDs to refer to specific +// instances, so there's no need to send any information back to the client. +// - foo &: The client is sending us a value over the wire, but because the type +// is mutable, we must send the changed value back in case the method call +// mutated it. +// +// Updating a mutable reference is done as a return value from the RPC +// perspective. These return values need to be emitted after the method's return +// value, and they are emitted in the order in which they occur in the +// declaration. +void RPCServerSourceEmitter::EmitEncodesForMutableParameters( + const std::vector &Params) { + for (auto Iter = Params.begin(); Iter != Params.end(); Iter++) { + // No need to manually update an SBClass + if (lldb_rpc_gen::TypeIsSBClass(Iter->Type)) + continue; + + if (!Iter->Type->isReferenceType() && !Iter->Type->isPointerType()) + continue; + + // If we have a void pointer with no length, there's nothing to update. This + // is likely a baton for a callback. The same goes for function pointers. + if (Iter->Type->isFunctionPointerType() || + (Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen)) + continue; + + // No need to update pointers and references to const-qualified data. + QualType UnderlyingType = lldb_rpc_gen::GetUnderlyingType(Iter->Type); + if (UnderlyingType.isConstQualified()) + continue; + + const std::string EncodeLine = + CreateEncodeLine(Iter->Name, /* IsEncodingSBClass = */ false); + EmitLine(EncodeLine); + } +} + +// There are 3 possible scenarios that this method can encounter: +// 1. The method has no return value and is not a constructor. +// Only the method call itself is emitted. +// 2. The method is a constructor. +// The call to the constructor is emitted in the encode line. +// 3. The method has a return value. +// The method call is emitted and the return value is captured in a variable. +// After that, an encode call is emitted with the variable that captured the +// return value. +void RPCServerSourceEmitter::EmitMethodCallAndEncode(const Method &method) { + // FIXME: The hand-written lldb-rpc-server currently doesn't emit destructors + // for LocalObjectRefs... even if the type is an ObjectRef. What should we do + // here? ---------------- DavidSpickett wrote: What's the impact of this and, to repeat the comment, what should we do here? Is this a nice to have or a memory leak waiting to happen if someone uses a LocalObjectRef in the right/wrong way? (whatever a LocalObjectRef is :) ) https://github.com/llvm/llvm-project/pull/138032 From lldb-commits at lists.llvm.org Tue May 6 06:15:58 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 06:15:58 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb[RPC] Upstream RPC server interface emitters (PR #138032) In-Reply-To: Message-ID: <681a0b8e.170a0220.2c734d.3417@mx.google.com> ================ @@ -0,0 +1,80 @@ +//===-- RPCServerSourceEmitter.h ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +#ifndef LLDB_RPC_GEN_RPCSERVERMETHODEMITTER_H +#define LLDB_RPC_GEN_RPCSERVERMETHODEMITTER_H + +#include "RPCCommon.h" + +#include "clang/AST/AST.h" + +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + +namespace lldb_rpc_gen { +class RPCServerSourceEmitter : public FileEmitter { ---------------- DavidSpickett wrote: Please add a top level description to each of these new classes. Even if it is just "Emits the .cpp files for bla", at least then I know that it's doing headers or code or both maybe. https://github.com/llvm/llvm-project/pull/138032 From lldb-commits at lists.llvm.org Tue May 6 06:15:58 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 06:15:58 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb[RPC] Upstream RPC server interface emitters (PR #138032) In-Reply-To: Message-ID: <681a0b8e.170a0220.20c4dc.2af4@mx.google.com> ================ @@ -0,0 +1,592 @@ +//===-- RPCServerSourceEmitter.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 "RPCServerSourceEmitter.h" +#include "RPCCommon.h" + +#include "clang/AST/AST.h" +#include "clang/Frontend/CompilerInstance.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace clang; +using namespace lldb_rpc_gen; + +// For methods with pointer return types, it's important that we know how big +// the type of the pointee is. We must correctly size a buffer (in the form of a +// Bytes object) before we can actually use it. +static const std::map MethodsWithPointerReturnTypes = { + {"_ZN4lldb12SBModuleSpec12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 + {"_ZNK4lldb8SBModule12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 +}; + +void RPCServerSourceEmitter::EmitMethod(const Method &method) { + if (method.ContainsFunctionPointerParameter) + EmitCallbackFunction(method); + + EmitCommentHeader(method); + EmitFunctionHeader(method); + EmitFunctionBody(method); + EmitFunctionFooter(); +} + +void RPCServerSourceEmitter::EmitCommentHeader(const Method &method) { + std::string CommentLine; + llvm::raw_string_ostream CommentStream(CommentLine); + + CommentStream << "// " << method.QualifiedName << "(" + << method.CreateParamListAsString(eServer) << ")"; + if (method.IsConst) + CommentStream << " const"; + + EmitLine("//------------------------------------------------------------"); + EmitLine(CommentLine); + EmitLine("//------------------------------------------------------------"); +} + +void RPCServerSourceEmitter::EmitFunctionHeader(const Method &method) { + std::string FunctionHeader; + llvm::raw_string_ostream FunctionHeaderStream(FunctionHeader); + FunctionHeaderStream + << "bool rpc_server::" << method.MangledName + << "::HandleRPCCall(rpc_common::Connection &connection, RPCStream " + "&send, RPCStream &response) {"; + EmitLine(FunctionHeader); + IndentLevel++; +} + +void RPCServerSourceEmitter::EmitFunctionBody(const Method &method) { + EmitLine("// 1) Make local storage for incoming function arguments"); + EmitStorageForParameters(method); + EmitLine("// 2) Decode all function arguments"); + EmitDecodeForParameters(method); + EmitLine("// 3) Call the method and encode the return value"); + EmitMethodCallAndEncode(method); +} + +void RPCServerSourceEmitter::EmitFunctionFooter() { + EmitLine("return true;"); + IndentLevel--; + EmitLine("}"); +} + +void RPCServerSourceEmitter::EmitStorageForParameters(const Method &method) { + // If we have an instance method and it isn't a constructor, we'll need to + // emit a "this" pointer. + if (method.IsInstance && !method.IsCtor) + EmitStorageForOneParameter(method.ThisType, "this_ptr", method.Policy, + /* IsFollowedByLen = */ false); + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + EmitStorageForOneParameter(Iter->Type, Iter->Name, method.Policy, + Iter->IsFollowedByLen); + // Skip over the length parameter, we don't emit it. + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) + Iter++; + } +} + +void RPCServerSourceEmitter::EmitStorageForOneParameter( + QualType ParamType, const std::string &ParamName, + const PrintingPolicy &Policy, bool IsFollowedByLen) { + // First, we consider `const char *`, `const char **`. They have special + // server-side types. + if (TypeIsConstCharPtr(ParamType)) { + EmitLine("rpc_common::ConstCharPointer " + ParamName + ";"); + return; + } else if (TypeIsConstCharPtrPtr(ParamType)) { + EmitLine("rpc_common::StringList " + ParamName + ";"); + return; + } + + QualType UnderlyingType = + lldb_rpc_gen::GetUnqualifiedUnderlyingType(ParamType); + const bool IsSBClass = lldb_rpc_gen::TypeIsSBClass(UnderlyingType); + + if (ParamType->isPointerType() && !IsSBClass) { + // Void pointer with no length is usually a baton for a callback. We're + // going to hold onto the pointer value so we can send it back to the + // client-side when we implement callbacks. + if (ParamType->isVoidPointerType() && !IsFollowedByLen) { + EmitLine("void * " + ParamName + " = nullptr;"); + return; + } + + if (!ParamType->isFunctionPointerType()) { + EmitLine("Bytes " + ParamName + ";"); + return; + } + + assert(ParamType->isFunctionPointerType() && "Unhandled pointer type"); + EmitLine("rpc_common::function_ptr_t " + ParamName + " = nullptr;"); + return; + } + + std::string StorageDeclaration; + llvm::raw_string_ostream StorageDeclarationStream(StorageDeclaration); + + UnderlyingType.print(StorageDeclarationStream, Policy); + StorageDeclarationStream << " "; + if (IsSBClass) + StorageDeclarationStream << "*"; + StorageDeclarationStream << ParamName; + if (IsSBClass) + StorageDeclarationStream << " = nullptr"; + else + StorageDeclarationStream << " = {}"; + StorageDeclarationStream << ";"; + EmitLine(StorageDeclaration); +} + +void RPCServerSourceEmitter::EmitDecodeForParameters(const Method &method) { + if (method.IsInstance && !method.IsCtor) + EmitDecodeForOneParameter(method.ThisType, "this_ptr", method.Policy); + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + EmitDecodeForOneParameter(Iter->Type, Iter->Name, method.Policy); + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) + Iter++; + } +} + +void RPCServerSourceEmitter::EmitDecodeForOneParameter( + QualType ParamType, const std::string &ParamName, + const PrintingPolicy &Policy) { + QualType UnderlyingType = + lldb_rpc_gen::GetUnqualifiedUnderlyingType(ParamType); + + if (TypeIsSBClass(UnderlyingType)) { + std::string DecodeLine; + llvm::raw_string_ostream DecodeLineStream(DecodeLine); + DecodeLineStream << ParamName << " = " + << "RPCServerObjectDecoder<"; + UnderlyingType.print(DecodeLineStream, Policy); + DecodeLineStream << ">(send, rpc_common::RPCPacket::ValueType::Argument);"; + EmitLine(DecodeLine); + EmitLine("if (!" + ParamName + ")"); + IndentLevel++; + EmitLine("return false;"); + IndentLevel--; + } else { + EmitLine("if (!RPCValueDecoder(send, " + "rpc_common::RPCPacket::ValueType::Argument, " + + ParamName + "))"); + IndentLevel++; + EmitLine("return false;"); + IndentLevel--; + } +} + +std::string RPCServerSourceEmitter::CreateMethodCall(const Method &method) { + std::string MethodCall; + llvm::raw_string_ostream MethodCallStream(MethodCall); + if (method.IsInstance) { + if (!method.IsCtor) + MethodCallStream << "this_ptr->"; + MethodCallStream << method.BaseName; + } else + MethodCallStream << method.QualifiedName; + + std::vector Args; + std::string FunctionPointerName; + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + std::string Arg; + // We must check for `const char *` and `const char **` first. + if (TypeIsConstCharPtr(Iter->Type)) { + // `const char *` is stored server-side as rpc_common::ConstCharPointer + Arg = Iter->Name + ".c_str()"; + } else if (TypeIsConstCharPtrPtr(Iter->Type)) { + // `const char **` is stored server-side as rpc_common::StringList + Arg = Iter->Name + ".argv()"; + } else if (lldb_rpc_gen::TypeIsSBClass(Iter->Type)) { + Arg = Iter->Name; + if (!Iter->Type->isPointerType()) + Arg = "*" + Iter->Name; + } else if (Iter->Type->isPointerType() && + !Iter->Type->isFunctionPointerType() && + (!Iter->Type->isVoidPointerType() || Iter->IsFollowedByLen)) { + // We move pointers between the server and client as 'Bytes' objects. + // Pointers with length arguments will have their length filled in below. + // Pointers with no length arguments are assumed to behave like an array + // with length of 1, except for void pointers which are handled + // differently. + Arg = "(" + Iter->Type.getAsString(method.Policy) + ")" + Iter->Name + + ".GetData()"; + } else if (Iter->Type->isFunctionPointerType()) { + // If we have a function pointer, we only want to pass something along if + // we got a real pointer. + Arg = Iter->Name + " ? " + method.MangledName + "_callback : nullptr"; + FunctionPointerName = Iter->Name; + } else if (Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen && + method.ContainsFunctionPointerParameter) { + // Assumptions: + // - This is assumed to be the baton for the function pointer. + // - This is assumed to come after the function pointer parameter. + // We always produce this regardless of the value of the baton argument. + Arg = "new CallbackInfo(" + FunctionPointerName + ", " + Iter->Name + + ", connection.GetConnectionID())"; + } else + Arg = Iter->Name; + + if (Iter->Type->isRValueReferenceType()) + Arg = "std::move(" + Arg + ")"; + Args.push_back(Arg); + + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) { + std::string LengthArg = Iter->Name + ".GetSize()"; + if (!Iter->Type->isVoidPointerType()) { + QualType UUT = lldb_rpc_gen::GetUnqualifiedUnderlyingType(Iter->Type); + LengthArg += " / sizeof(" + UUT.getAsString(method.Policy) + ")"; + } + Args.push_back(LengthArg); + Iter++; + } + } + MethodCallStream << "(" << llvm::join(Args, ", ") << ")"; + + return MethodCall; +} + +std::string RPCServerSourceEmitter::CreateEncodeLine(const std::string &Value, + bool IsEncodingSBClass) { + std::string EncodeLine; + llvm::raw_string_ostream EncodeLineStream(EncodeLine); + + if (IsEncodingSBClass) + EncodeLineStream << "RPCServerObjectEncoder("; + else + EncodeLineStream << "RPCValueEncoder("; + + EncodeLineStream + << "response, rpc_common::RPCPacket::ValueType::ReturnValue, "; + EncodeLineStream << Value; + EncodeLineStream << ");"; + return EncodeLine; +} + +// There are 4 cases to consider: +// - const SBClass &: No need to do anything. +// - const foo &: No need to do anything. +// - SBClass &: The server and the client hold on to IDs to refer to specific +// instances, so there's no need to send any information back to the client. +// - foo &: The client is sending us a value over the wire, but because the type +// is mutable, we must send the changed value back in case the method call +// mutated it. +// +// Updating a mutable reference is done as a return value from the RPC +// perspective. These return values need to be emitted after the method's return +// value, and they are emitted in the order in which they occur in the +// declaration. +void RPCServerSourceEmitter::EmitEncodesForMutableParameters( + const std::vector &Params) { + for (auto Iter = Params.begin(); Iter != Params.end(); Iter++) { + // No need to manually update an SBClass + if (lldb_rpc_gen::TypeIsSBClass(Iter->Type)) + continue; + + if (!Iter->Type->isReferenceType() && !Iter->Type->isPointerType()) + continue; + + // If we have a void pointer with no length, there's nothing to update. This + // is likely a baton for a callback. The same goes for function pointers. + if (Iter->Type->isFunctionPointerType() || + (Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen)) + continue; + + // No need to update pointers and references to const-qualified data. + QualType UnderlyingType = lldb_rpc_gen::GetUnderlyingType(Iter->Type); + if (UnderlyingType.isConstQualified()) + continue; + + const std::string EncodeLine = + CreateEncodeLine(Iter->Name, /* IsEncodingSBClass = */ false); + EmitLine(EncodeLine); + } +} + +// There are 3 possible scenarios that this method can encounter: +// 1. The method has no return value and is not a constructor. +// Only the method call itself is emitted. +// 2. The method is a constructor. +// The call to the constructor is emitted in the encode line. +// 3. The method has a return value. +// The method call is emitted and the return value is captured in a variable. +// After that, an encode call is emitted with the variable that captured the +// return value. +void RPCServerSourceEmitter::EmitMethodCallAndEncode(const Method &method) { + // FIXME: The hand-written lldb-rpc-server currently doesn't emit destructors + // for LocalObjectRefs... even if the type is an ObjectRef. What should we do + // here? + + const std::string MethodCall = CreateMethodCall(method); + + // If this function returns nothing, we just emit the call and update any + // mutable references. Note that constructors have return type `void` so we + // must explicitly check for that here. + if (!method.IsCtor && method.ReturnType->isVoidType()) { + EmitLine(MethodCall + ";"); + EmitEncodesForMutableParameters(method.Params); + return; + } + + static constexpr llvm::StringLiteral ReturnVariableName("__result"); + + // If this isn't a constructor, we'll need to store the result of the method + // call in a result variable. + if (!method.IsCtor) { + // We need to determine what the appropriate return type is. Here is the + // strategy: + // 1.) `SBFoo` -> `SBFoo &&` + // 2.) If the type is a pointer other than `const char *` or `const char **` + // or `void *`, the return type will be `Bytes` (e.g. `const uint8_t *` + // -> `Bytes`). + // 3.) Otherwise, emit the exact same return type. + std::string ReturnTypeName; + std::string AssignLine; + llvm::raw_string_ostream AssignLineStream(AssignLine); + if (method.ReturnType->isPointerType() && + !lldb_rpc_gen::TypeIsConstCharPtr(method.ReturnType) && + !lldb_rpc_gen::TypeIsConstCharPtrPtr(method.ReturnType) && + !method.ReturnType->isVoidPointerType()) { + llvm::StringRef MangledNameRef(method.MangledName); + auto Pos = MethodsWithPointerReturnTypes.find(MangledNameRef); + assert(Pos != MethodsWithPointerReturnTypes.end() && + "Unable to determine the size of the return buffer"); + if (Pos == MethodsWithPointerReturnTypes.end()) { + EmitLine( + "// Intentionally inserting a compiler error. lldb-rpc-gen " + "was unable to determine how large the return buffer should be."); + EmitLine("ThisShouldNotCompile"); ---------------- DavidSpickett wrote: `#error ` would be a bit more standard and provide some help to debug this. https://github.com/llvm/llvm-project/pull/138032 From lldb-commits at lists.llvm.org Tue May 6 06:15:58 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 06:15:58 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb[RPC] Upstream RPC server interface emitters (PR #138032) In-Reply-To: Message-ID: <681a0b8e.a70a0220.39d81b.b242@mx.google.com> ================ @@ -0,0 +1,592 @@ +//===-- RPCServerSourceEmitter.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 "RPCServerSourceEmitter.h" +#include "RPCCommon.h" + +#include "clang/AST/AST.h" +#include "clang/Frontend/CompilerInstance.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace clang; +using namespace lldb_rpc_gen; + +// For methods with pointer return types, it's important that we know how big +// the type of the pointee is. We must correctly size a buffer (in the form of a +// Bytes object) before we can actually use it. +static const std::map MethodsWithPointerReturnTypes = { + {"_ZN4lldb12SBModuleSpec12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 + {"_ZNK4lldb8SBModule12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 +}; + +void RPCServerSourceEmitter::EmitMethod(const Method &method) { + if (method.ContainsFunctionPointerParameter) + EmitCallbackFunction(method); + + EmitCommentHeader(method); + EmitFunctionHeader(method); + EmitFunctionBody(method); + EmitFunctionFooter(); +} + +void RPCServerSourceEmitter::EmitCommentHeader(const Method &method) { + std::string CommentLine; + llvm::raw_string_ostream CommentStream(CommentLine); + + CommentStream << "// " << method.QualifiedName << "(" + << method.CreateParamListAsString(eServer) << ")"; + if (method.IsConst) + CommentStream << " const"; + + EmitLine("//------------------------------------------------------------"); + EmitLine(CommentLine); + EmitLine("//------------------------------------------------------------"); +} + +void RPCServerSourceEmitter::EmitFunctionHeader(const Method &method) { + std::string FunctionHeader; + llvm::raw_string_ostream FunctionHeaderStream(FunctionHeader); + FunctionHeaderStream + << "bool rpc_server::" << method.MangledName + << "::HandleRPCCall(rpc_common::Connection &connection, RPCStream " + "&send, RPCStream &response) {"; + EmitLine(FunctionHeader); + IndentLevel++; +} + +void RPCServerSourceEmitter::EmitFunctionBody(const Method &method) { + EmitLine("// 1) Make local storage for incoming function arguments"); + EmitStorageForParameters(method); + EmitLine("// 2) Decode all function arguments"); + EmitDecodeForParameters(method); + EmitLine("// 3) Call the method and encode the return value"); + EmitMethodCallAndEncode(method); +} + +void RPCServerSourceEmitter::EmitFunctionFooter() { + EmitLine("return true;"); + IndentLevel--; + EmitLine("}"); +} + +void RPCServerSourceEmitter::EmitStorageForParameters(const Method &method) { + // If we have an instance method and it isn't a constructor, we'll need to + // emit a "this" pointer. + if (method.IsInstance && !method.IsCtor) + EmitStorageForOneParameter(method.ThisType, "this_ptr", method.Policy, + /* IsFollowedByLen = */ false); + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + EmitStorageForOneParameter(Iter->Type, Iter->Name, method.Policy, + Iter->IsFollowedByLen); + // Skip over the length parameter, we don't emit it. + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) + Iter++; + } +} + +void RPCServerSourceEmitter::EmitStorageForOneParameter( + QualType ParamType, const std::string &ParamName, + const PrintingPolicy &Policy, bool IsFollowedByLen) { + // First, we consider `const char *`, `const char **`. They have special + // server-side types. + if (TypeIsConstCharPtr(ParamType)) { + EmitLine("rpc_common::ConstCharPointer " + ParamName + ";"); + return; + } else if (TypeIsConstCharPtrPtr(ParamType)) { + EmitLine("rpc_common::StringList " + ParamName + ";"); + return; + } + + QualType UnderlyingType = + lldb_rpc_gen::GetUnqualifiedUnderlyingType(ParamType); + const bool IsSBClass = lldb_rpc_gen::TypeIsSBClass(UnderlyingType); + + if (ParamType->isPointerType() && !IsSBClass) { + // Void pointer with no length is usually a baton for a callback. We're + // going to hold onto the pointer value so we can send it back to the + // client-side when we implement callbacks. + if (ParamType->isVoidPointerType() && !IsFollowedByLen) { + EmitLine("void * " + ParamName + " = nullptr;"); + return; + } + + if (!ParamType->isFunctionPointerType()) { + EmitLine("Bytes " + ParamName + ";"); + return; + } + + assert(ParamType->isFunctionPointerType() && "Unhandled pointer type"); + EmitLine("rpc_common::function_ptr_t " + ParamName + " = nullptr;"); + return; + } + + std::string StorageDeclaration; + llvm::raw_string_ostream StorageDeclarationStream(StorageDeclaration); + + UnderlyingType.print(StorageDeclarationStream, Policy); + StorageDeclarationStream << " "; + if (IsSBClass) + StorageDeclarationStream << "*"; + StorageDeclarationStream << ParamName; + if (IsSBClass) + StorageDeclarationStream << " = nullptr"; + else + StorageDeclarationStream << " = {}"; + StorageDeclarationStream << ";"; + EmitLine(StorageDeclaration); +} + +void RPCServerSourceEmitter::EmitDecodeForParameters(const Method &method) { + if (method.IsInstance && !method.IsCtor) + EmitDecodeForOneParameter(method.ThisType, "this_ptr", method.Policy); + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + EmitDecodeForOneParameter(Iter->Type, Iter->Name, method.Policy); + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) + Iter++; + } +} + +void RPCServerSourceEmitter::EmitDecodeForOneParameter( + QualType ParamType, const std::string &ParamName, + const PrintingPolicy &Policy) { + QualType UnderlyingType = + lldb_rpc_gen::GetUnqualifiedUnderlyingType(ParamType); + + if (TypeIsSBClass(UnderlyingType)) { + std::string DecodeLine; + llvm::raw_string_ostream DecodeLineStream(DecodeLine); + DecodeLineStream << ParamName << " = " + << "RPCServerObjectDecoder<"; + UnderlyingType.print(DecodeLineStream, Policy); + DecodeLineStream << ">(send, rpc_common::RPCPacket::ValueType::Argument);"; + EmitLine(DecodeLine); + EmitLine("if (!" + ParamName + ")"); + IndentLevel++; + EmitLine("return false;"); + IndentLevel--; + } else { + EmitLine("if (!RPCValueDecoder(send, " + "rpc_common::RPCPacket::ValueType::Argument, " + + ParamName + "))"); + IndentLevel++; + EmitLine("return false;"); + IndentLevel--; + } +} + +std::string RPCServerSourceEmitter::CreateMethodCall(const Method &method) { + std::string MethodCall; + llvm::raw_string_ostream MethodCallStream(MethodCall); + if (method.IsInstance) { + if (!method.IsCtor) + MethodCallStream << "this_ptr->"; + MethodCallStream << method.BaseName; + } else + MethodCallStream << method.QualifiedName; + + std::vector Args; + std::string FunctionPointerName; + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + std::string Arg; + // We must check for `const char *` and `const char **` first. + if (TypeIsConstCharPtr(Iter->Type)) { + // `const char *` is stored server-side as rpc_common::ConstCharPointer + Arg = Iter->Name + ".c_str()"; + } else if (TypeIsConstCharPtrPtr(Iter->Type)) { + // `const char **` is stored server-side as rpc_common::StringList + Arg = Iter->Name + ".argv()"; + } else if (lldb_rpc_gen::TypeIsSBClass(Iter->Type)) { + Arg = Iter->Name; + if (!Iter->Type->isPointerType()) + Arg = "*" + Iter->Name; + } else if (Iter->Type->isPointerType() && + !Iter->Type->isFunctionPointerType() && + (!Iter->Type->isVoidPointerType() || Iter->IsFollowedByLen)) { + // We move pointers between the server and client as 'Bytes' objects. + // Pointers with length arguments will have their length filled in below. + // Pointers with no length arguments are assumed to behave like an array + // with length of 1, except for void pointers which are handled + // differently. + Arg = "(" + Iter->Type.getAsString(method.Policy) + ")" + Iter->Name + + ".GetData()"; + } else if (Iter->Type->isFunctionPointerType()) { + // If we have a function pointer, we only want to pass something along if + // we got a real pointer. + Arg = Iter->Name + " ? " + method.MangledName + "_callback : nullptr"; + FunctionPointerName = Iter->Name; + } else if (Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen && + method.ContainsFunctionPointerParameter) { + // Assumptions: + // - This is assumed to be the baton for the function pointer. + // - This is assumed to come after the function pointer parameter. + // We always produce this regardless of the value of the baton argument. + Arg = "new CallbackInfo(" + FunctionPointerName + ", " + Iter->Name + + ", connection.GetConnectionID())"; + } else + Arg = Iter->Name; + + if (Iter->Type->isRValueReferenceType()) + Arg = "std::move(" + Arg + ")"; + Args.push_back(Arg); + + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) { + std::string LengthArg = Iter->Name + ".GetSize()"; + if (!Iter->Type->isVoidPointerType()) { + QualType UUT = lldb_rpc_gen::GetUnqualifiedUnderlyingType(Iter->Type); + LengthArg += " / sizeof(" + UUT.getAsString(method.Policy) + ")"; + } + Args.push_back(LengthArg); + Iter++; + } + } + MethodCallStream << "(" << llvm::join(Args, ", ") << ")"; + + return MethodCall; +} + +std::string RPCServerSourceEmitter::CreateEncodeLine(const std::string &Value, + bool IsEncodingSBClass) { + std::string EncodeLine; + llvm::raw_string_ostream EncodeLineStream(EncodeLine); + + if (IsEncodingSBClass) + EncodeLineStream << "RPCServerObjectEncoder("; + else + EncodeLineStream << "RPCValueEncoder("; + + EncodeLineStream + << "response, rpc_common::RPCPacket::ValueType::ReturnValue, "; + EncodeLineStream << Value; + EncodeLineStream << ");"; + return EncodeLine; +} + +// There are 4 cases to consider: +// - const SBClass &: No need to do anything. +// - const foo &: No need to do anything. +// - SBClass &: The server and the client hold on to IDs to refer to specific +// instances, so there's no need to send any information back to the client. +// - foo &: The client is sending us a value over the wire, but because the type +// is mutable, we must send the changed value back in case the method call +// mutated it. +// +// Updating a mutable reference is done as a return value from the RPC +// perspective. These return values need to be emitted after the method's return +// value, and they are emitted in the order in which they occur in the +// declaration. +void RPCServerSourceEmitter::EmitEncodesForMutableParameters( + const std::vector &Params) { + for (auto Iter = Params.begin(); Iter != Params.end(); Iter++) { + // No need to manually update an SBClass + if (lldb_rpc_gen::TypeIsSBClass(Iter->Type)) + continue; + + if (!Iter->Type->isReferenceType() && !Iter->Type->isPointerType()) + continue; + + // If we have a void pointer with no length, there's nothing to update. This + // is likely a baton for a callback. The same goes for function pointers. + if (Iter->Type->isFunctionPointerType() || + (Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen)) + continue; + + // No need to update pointers and references to const-qualified data. + QualType UnderlyingType = lldb_rpc_gen::GetUnderlyingType(Iter->Type); + if (UnderlyingType.isConstQualified()) + continue; + + const std::string EncodeLine = + CreateEncodeLine(Iter->Name, /* IsEncodingSBClass = */ false); + EmitLine(EncodeLine); + } +} + +// There are 3 possible scenarios that this method can encounter: +// 1. The method has no return value and is not a constructor. +// Only the method call itself is emitted. +// 2. The method is a constructor. +// The call to the constructor is emitted in the encode line. +// 3. The method has a return value. +// The method call is emitted and the return value is captured in a variable. +// After that, an encode call is emitted with the variable that captured the +// return value. +void RPCServerSourceEmitter::EmitMethodCallAndEncode(const Method &method) { + // FIXME: The hand-written lldb-rpc-server currently doesn't emit destructors + // for LocalObjectRefs... even if the type is an ObjectRef. What should we do + // here? + + const std::string MethodCall = CreateMethodCall(method); + + // If this function returns nothing, we just emit the call and update any + // mutable references. Note that constructors have return type `void` so we + // must explicitly check for that here. + if (!method.IsCtor && method.ReturnType->isVoidType()) { + EmitLine(MethodCall + ";"); + EmitEncodesForMutableParameters(method.Params); + return; + } + + static constexpr llvm::StringLiteral ReturnVariableName("__result"); + + // If this isn't a constructor, we'll need to store the result of the method + // call in a result variable. + if (!method.IsCtor) { + // We need to determine what the appropriate return type is. Here is the + // strategy: + // 1.) `SBFoo` -> `SBFoo &&` + // 2.) If the type is a pointer other than `const char *` or `const char **` + // or `void *`, the return type will be `Bytes` (e.g. `const uint8_t *` + // -> `Bytes`). + // 3.) Otherwise, emit the exact same return type. + std::string ReturnTypeName; + std::string AssignLine; + llvm::raw_string_ostream AssignLineStream(AssignLine); + if (method.ReturnType->isPointerType() && + !lldb_rpc_gen::TypeIsConstCharPtr(method.ReturnType) && + !lldb_rpc_gen::TypeIsConstCharPtrPtr(method.ReturnType) && + !method.ReturnType->isVoidPointerType()) { + llvm::StringRef MangledNameRef(method.MangledName); + auto Pos = MethodsWithPointerReturnTypes.find(MangledNameRef); + assert(Pos != MethodsWithPointerReturnTypes.end() && + "Unable to determine the size of the return buffer"); + if (Pos == MethodsWithPointerReturnTypes.end()) { + EmitLine( + "// Intentionally inserting a compiler error. lldb-rpc-gen " + "was unable to determine how large the return buffer should be."); + EmitLine("ThisShouldNotCompile"); + return; + } + AssignLineStream << "Bytes " << ReturnVariableName << "(" << MethodCall + << ", " << Pos->second << ");"; + } else { + if (lldb_rpc_gen::TypeIsSBClass(method.ReturnType)) { + // We want to preserve constness, so we don't strip qualifications from + // the underlying type + QualType UnderlyingReturnType = + lldb_rpc_gen::GetUnderlyingType(method.ReturnType); + ReturnTypeName = + UnderlyingReturnType.getAsString(method.Policy) + " &&"; + } else + ReturnTypeName = method.ReturnType.getAsString(method.Policy); + + AssignLineStream << ReturnTypeName << " " << ReturnVariableName << " = " + << MethodCall << ";"; + } + EmitLine(AssignLine); + } + + const bool IsEncodingSBClass = + lldb_rpc_gen::TypeIsSBClass(method.ReturnType) || method.IsCtor; + + std::string ValueToEncode; + if (IsEncodingSBClass) { + if (method.IsCtor) + ValueToEncode = MethodCall; + else + ValueToEncode = "std::move(" + ReturnVariableName.str() + ")"; + } else + ValueToEncode = ReturnVariableName.str(); + + const std::string ReturnValueEncodeLine = + CreateEncodeLine(ValueToEncode, IsEncodingSBClass); + EmitLine(ReturnValueEncodeLine); + EmitEncodesForMutableParameters(method.Params); +} + +// NOTE: This contains most of the same knowledge as RPCLibrarySourceEmitter. I +// have chosen not to re-use code here because the needs are different enough +// that it would be more work to re-use than just reimplement portions of it. +// Specifically: +// - Callbacks do not neatly fit into a `Method` object, which currently +// assumes that you have a CXXMethodDecl (We have a FunctionDecl at most). +// - We only generate callbacks that have a `void *` baton parameter. We hijack +// those baton parameters and treat them differently. +// - Callbacks need to do something special for moving SB class references back +// to the client-side. +// FIXME: Figure out what we can actually re-use in a meaningful way between +// this method and RPCLibrarySourceEmitter. +void RPCServerSourceEmitter::EmitCallbackFunction(const Method &method) { + // Check invariants and locate necessary resources + Param FuncPointerParam; + Param BatonParam; + for (const auto &Param : method.Params) + if (Param.Type->isFunctionPointerType()) + FuncPointerParam = Param; + else if (Param.Type->isVoidPointerType()) + BatonParam = Param; + + assert(FuncPointerParam.Type->isFunctionPointerType() && + "Emitting callback function with no function pointer"); + assert(BatonParam.Type->isVoidPointerType() && + "Emitting callback function with no baton"); + + QualType FuncType = FuncPointerParam.Type->getPointeeType(); + const auto *FuncProtoType = FuncType->getAs(); + assert(FuncProtoType && "Emitting callback with no parameter information"); + if (!FuncProtoType) + return; // If asserts are off, we'll just fail to compile. + + std::vector CallbackParams; + std::vector CallbackParamsAsStrings; + uint8_t ArgIdx = 0; + for (QualType ParamType : FuncProtoType->param_types()) { + Param CallbackParam; + CallbackParam.IsFollowedByLen = false; + CallbackParam.Type = ParamType; + if (ParamType->isVoidPointerType()) + CallbackParam.Name = "baton"; + else + CallbackParam.Name = "arg" + std::to_string(ArgIdx++); + + CallbackParams.push_back(CallbackParam); + CallbackParamsAsStrings.push_back(ParamType.getAsString(method.Policy) + + " " + CallbackParam.Name); + } + const std::string CallbackReturnTypeName = + FuncProtoType->getReturnType().getAsString(method.Policy); + const std::string CallbackName = method.MangledName + "_callback"; + + // Emit Function Header + std::string Header; + llvm::raw_string_ostream HeaderStream(Header); + HeaderStream << "static " << CallbackReturnTypeName << " " << CallbackName + << "(" << llvm::join(CallbackParamsAsStrings, ", ") << ") {"; + EmitLine(Header); + IndentLevel++; + + // Emit Function Body + EmitLine("// RPC connection setup and sanity checking"); + EmitLine("CallbackInfo *callback_info = (CallbackInfo *)baton;"); + EmitLine("rpc_common::ConnectionSP connection_sp = " + "rpc_common::Connection::GetConnectionFromID(callback_info->" + "connection_id);"); + EmitLine("if (!connection_sp)"); + IndentLevel++; + if (FuncProtoType->getReturnType()->isVoidType()) + EmitLine("return;"); + else + EmitLine("return {};"); + IndentLevel--; + + EmitLine("// Preparing to make the call"); + EmitLine("static RPCFunctionInfo g_func(\"" + CallbackName + "\");"); + EmitLine("RPCStream send;"); + EmitLine("RPCStream response;"); + EmitLine("g_func.Encode(send);"); + + EmitLine("// The first thing we encode is the callback address so that the " + "client-side can know where the callback is"); + EmitLine("RPCValueEncoder(send, rpc_common::RPCPacket::ValueType::Argument, " + "callback_info->callback);"); + EmitLine("// Encode all the arguments"); + for (const Param &CallbackParam : CallbackParams) { + if (lldb_rpc_gen::TypeIsSBClass(CallbackParam.Type)) { + + // FIXME: SB class server references are stored as non-const references so + // that we can actually change them as needed. If a parameter is marked + // const, we will fail to compile because we cannot make an + // SBFooServerReference from a `const SBFoo &`. + // To work around this issue, we'll apply a `const_cast` if needed so we + // can continue to generate callbacks for now, but we really should + // rethink the way we store object IDs server-side to support + // const-qualified parameters. + QualType UnderlyingSBClass = + lldb_rpc_gen::GetUnderlyingType(CallbackParam.Type); + QualType UnqualifiedUnderlyingSBClass = + UnderlyingSBClass.getUnqualifiedType(); + + // FIXME: Replace this bespoke logic with a nice function in RPCCommon. ---------------- DavidSpickett wrote: Does RPCCommon actually exist or is that also a future thing? Either way doesn't sound critical, just wondering. https://github.com/llvm/llvm-project/pull/138032 From lldb-commits at lists.llvm.org Tue May 6 06:16:00 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 06:16:00 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb[RPC] Upstream RPC server interface emitters (PR #138032) In-Reply-To: Message-ID: <681a0b90.170a0220.afc2d.860b@mx.google.com> ================ @@ -0,0 +1,592 @@ +//===-- RPCServerSourceEmitter.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 "RPCServerSourceEmitter.h" +#include "RPCCommon.h" + +#include "clang/AST/AST.h" +#include "clang/Frontend/CompilerInstance.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace clang; +using namespace lldb_rpc_gen; + +// For methods with pointer return types, it's important that we know how big +// the type of the pointee is. We must correctly size a buffer (in the form of a +// Bytes object) before we can actually use it. +static const std::map MethodsWithPointerReturnTypes = { + {"_ZN4lldb12SBModuleSpec12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 + {"_ZNK4lldb8SBModule12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 +}; + +void RPCServerSourceEmitter::EmitMethod(const Method &method) { + if (method.ContainsFunctionPointerParameter) + EmitCallbackFunction(method); + + EmitCommentHeader(method); + EmitFunctionHeader(method); + EmitFunctionBody(method); + EmitFunctionFooter(); +} + +void RPCServerSourceEmitter::EmitCommentHeader(const Method &method) { + std::string CommentLine; + llvm::raw_string_ostream CommentStream(CommentLine); + + CommentStream << "// " << method.QualifiedName << "(" + << method.CreateParamListAsString(eServer) << ")"; + if (method.IsConst) + CommentStream << " const"; + + EmitLine("//------------------------------------------------------------"); + EmitLine(CommentLine); + EmitLine("//------------------------------------------------------------"); +} + +void RPCServerSourceEmitter::EmitFunctionHeader(const Method &method) { + std::string FunctionHeader; + llvm::raw_string_ostream FunctionHeaderStream(FunctionHeader); + FunctionHeaderStream + << "bool rpc_server::" << method.MangledName + << "::HandleRPCCall(rpc_common::Connection &connection, RPCStream " + "&send, RPCStream &response) {"; + EmitLine(FunctionHeader); + IndentLevel++; +} + +void RPCServerSourceEmitter::EmitFunctionBody(const Method &method) { + EmitLine("// 1) Make local storage for incoming function arguments"); + EmitStorageForParameters(method); + EmitLine("// 2) Decode all function arguments"); + EmitDecodeForParameters(method); + EmitLine("// 3) Call the method and encode the return value"); + EmitMethodCallAndEncode(method); +} + +void RPCServerSourceEmitter::EmitFunctionFooter() { + EmitLine("return true;"); + IndentLevel--; + EmitLine("}"); +} + +void RPCServerSourceEmitter::EmitStorageForParameters(const Method &method) { + // If we have an instance method and it isn't a constructor, we'll need to + // emit a "this" pointer. + if (method.IsInstance && !method.IsCtor) + EmitStorageForOneParameter(method.ThisType, "this_ptr", method.Policy, + /* IsFollowedByLen = */ false); + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + EmitStorageForOneParameter(Iter->Type, Iter->Name, method.Policy, + Iter->IsFollowedByLen); + // Skip over the length parameter, we don't emit it. + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) + Iter++; + } +} + +void RPCServerSourceEmitter::EmitStorageForOneParameter( + QualType ParamType, const std::string &ParamName, + const PrintingPolicy &Policy, bool IsFollowedByLen) { + // First, we consider `const char *`, `const char **`. They have special + // server-side types. + if (TypeIsConstCharPtr(ParamType)) { + EmitLine("rpc_common::ConstCharPointer " + ParamName + ";"); ---------------- DavidSpickett wrote: Should this have `= nullptr;`? Just to be neater and maybe prevent some compiler warnings. https://github.com/llvm/llvm-project/pull/138032 From lldb-commits at lists.llvm.org Tue May 6 06:16:00 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 06:16:00 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb[RPC] Upstream RPC server interface emitters (PR #138032) In-Reply-To: Message-ID: <681a0b90.170a0220.30406e.2484@mx.google.com> ================ @@ -0,0 +1,592 @@ +//===-- RPCServerSourceEmitter.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 "RPCServerSourceEmitter.h" +#include "RPCCommon.h" + +#include "clang/AST/AST.h" +#include "clang/Frontend/CompilerInstance.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace clang; +using namespace lldb_rpc_gen; + +// For methods with pointer return types, it's important that we know how big +// the type of the pointee is. We must correctly size a buffer (in the form of a +// Bytes object) before we can actually use it. +static const std::map MethodsWithPointerReturnTypes = { + {"_ZN4lldb12SBModuleSpec12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 + {"_ZNK4lldb8SBModule12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 +}; + +void RPCServerSourceEmitter::EmitMethod(const Method &method) { + if (method.ContainsFunctionPointerParameter) + EmitCallbackFunction(method); + + EmitCommentHeader(method); + EmitFunctionHeader(method); + EmitFunctionBody(method); + EmitFunctionFooter(); +} + +void RPCServerSourceEmitter::EmitCommentHeader(const Method &method) { + std::string CommentLine; + llvm::raw_string_ostream CommentStream(CommentLine); + + CommentStream << "// " << method.QualifiedName << "(" + << method.CreateParamListAsString(eServer) << ")"; + if (method.IsConst) + CommentStream << " const"; + + EmitLine("//------------------------------------------------------------"); + EmitLine(CommentLine); + EmitLine("//------------------------------------------------------------"); +} + +void RPCServerSourceEmitter::EmitFunctionHeader(const Method &method) { + std::string FunctionHeader; + llvm::raw_string_ostream FunctionHeaderStream(FunctionHeader); + FunctionHeaderStream + << "bool rpc_server::" << method.MangledName + << "::HandleRPCCall(rpc_common::Connection &connection, RPCStream " + "&send, RPCStream &response) {"; + EmitLine(FunctionHeader); + IndentLevel++; +} + +void RPCServerSourceEmitter::EmitFunctionBody(const Method &method) { + EmitLine("// 1) Make local storage for incoming function arguments"); + EmitStorageForParameters(method); + EmitLine("// 2) Decode all function arguments"); + EmitDecodeForParameters(method); + EmitLine("// 3) Call the method and encode the return value"); + EmitMethodCallAndEncode(method); +} + +void RPCServerSourceEmitter::EmitFunctionFooter() { + EmitLine("return true;"); + IndentLevel--; + EmitLine("}"); +} + +void RPCServerSourceEmitter::EmitStorageForParameters(const Method &method) { + // If we have an instance method and it isn't a constructor, we'll need to + // emit a "this" pointer. + if (method.IsInstance && !method.IsCtor) + EmitStorageForOneParameter(method.ThisType, "this_ptr", method.Policy, + /* IsFollowedByLen = */ false); + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + EmitStorageForOneParameter(Iter->Type, Iter->Name, method.Policy, + Iter->IsFollowedByLen); + // Skip over the length parameter, we don't emit it. + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) + Iter++; + } +} + +void RPCServerSourceEmitter::EmitStorageForOneParameter( + QualType ParamType, const std::string &ParamName, + const PrintingPolicy &Policy, bool IsFollowedByLen) { + // First, we consider `const char *`, `const char **`. They have special + // server-side types. + if (TypeIsConstCharPtr(ParamType)) { + EmitLine("rpc_common::ConstCharPointer " + ParamName + ";"); + return; + } else if (TypeIsConstCharPtrPtr(ParamType)) { + EmitLine("rpc_common::StringList " + ParamName + ";"); + return; + } + + QualType UnderlyingType = + lldb_rpc_gen::GetUnqualifiedUnderlyingType(ParamType); + const bool IsSBClass = lldb_rpc_gen::TypeIsSBClass(UnderlyingType); + + if (ParamType->isPointerType() && !IsSBClass) { + // Void pointer with no length is usually a baton for a callback. We're + // going to hold onto the pointer value so we can send it back to the + // client-side when we implement callbacks. + if (ParamType->isVoidPointerType() && !IsFollowedByLen) { + EmitLine("void * " + ParamName + " = nullptr;"); + return; + } + + if (!ParamType->isFunctionPointerType()) { + EmitLine("Bytes " + ParamName + ";"); + return; + } + + assert(ParamType->isFunctionPointerType() && "Unhandled pointer type"); + EmitLine("rpc_common::function_ptr_t " + ParamName + " = nullptr;"); + return; + } + + std::string StorageDeclaration; + llvm::raw_string_ostream StorageDeclarationStream(StorageDeclaration); + + UnderlyingType.print(StorageDeclarationStream, Policy); + StorageDeclarationStream << " "; + if (IsSBClass) + StorageDeclarationStream << "*"; + StorageDeclarationStream << ParamName; + if (IsSBClass) + StorageDeclarationStream << " = nullptr"; + else + StorageDeclarationStream << " = {}"; + StorageDeclarationStream << ";"; + EmitLine(StorageDeclaration); +} + +void RPCServerSourceEmitter::EmitDecodeForParameters(const Method &method) { + if (method.IsInstance && !method.IsCtor) + EmitDecodeForOneParameter(method.ThisType, "this_ptr", method.Policy); + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + EmitDecodeForOneParameter(Iter->Type, Iter->Name, method.Policy); + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) + Iter++; + } +} + +void RPCServerSourceEmitter::EmitDecodeForOneParameter( + QualType ParamType, const std::string &ParamName, + const PrintingPolicy &Policy) { + QualType UnderlyingType = + lldb_rpc_gen::GetUnqualifiedUnderlyingType(ParamType); + + if (TypeIsSBClass(UnderlyingType)) { + std::string DecodeLine; + llvm::raw_string_ostream DecodeLineStream(DecodeLine); + DecodeLineStream << ParamName << " = " + << "RPCServerObjectDecoder<"; + UnderlyingType.print(DecodeLineStream, Policy); + DecodeLineStream << ">(send, rpc_common::RPCPacket::ValueType::Argument);"; + EmitLine(DecodeLine); + EmitLine("if (!" + ParamName + ")"); + IndentLevel++; + EmitLine("return false;"); + IndentLevel--; + } else { + EmitLine("if (!RPCValueDecoder(send, " + "rpc_common::RPCPacket::ValueType::Argument, " + + ParamName + "))"); + IndentLevel++; + EmitLine("return false;"); + IndentLevel--; + } +} + +std::string RPCServerSourceEmitter::CreateMethodCall(const Method &method) { + std::string MethodCall; + llvm::raw_string_ostream MethodCallStream(MethodCall); + if (method.IsInstance) { + if (!method.IsCtor) + MethodCallStream << "this_ptr->"; + MethodCallStream << method.BaseName; + } else + MethodCallStream << method.QualifiedName; + + std::vector Args; + std::string FunctionPointerName; + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + std::string Arg; + // We must check for `const char *` and `const char **` first. + if (TypeIsConstCharPtr(Iter->Type)) { + // `const char *` is stored server-side as rpc_common::ConstCharPointer + Arg = Iter->Name + ".c_str()"; + } else if (TypeIsConstCharPtrPtr(Iter->Type)) { + // `const char **` is stored server-side as rpc_common::StringList + Arg = Iter->Name + ".argv()"; + } else if (lldb_rpc_gen::TypeIsSBClass(Iter->Type)) { + Arg = Iter->Name; + if (!Iter->Type->isPointerType()) + Arg = "*" + Iter->Name; + } else if (Iter->Type->isPointerType() && + !Iter->Type->isFunctionPointerType() && + (!Iter->Type->isVoidPointerType() || Iter->IsFollowedByLen)) { + // We move pointers between the server and client as 'Bytes' objects. + // Pointers with length arguments will have their length filled in below. + // Pointers with no length arguments are assumed to behave like an array + // with length of 1, except for void pointers which are handled + // differently. + Arg = "(" + Iter->Type.getAsString(method.Policy) + ")" + Iter->Name + + ".GetData()"; + } else if (Iter->Type->isFunctionPointerType()) { + // If we have a function pointer, we only want to pass something along if + // we got a real pointer. + Arg = Iter->Name + " ? " + method.MangledName + "_callback : nullptr"; + FunctionPointerName = Iter->Name; + } else if (Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen && + method.ContainsFunctionPointerParameter) { + // Assumptions: + // - This is assumed to be the baton for the function pointer. + // - This is assumed to come after the function pointer parameter. + // We always produce this regardless of the value of the baton argument. + Arg = "new CallbackInfo(" + FunctionPointerName + ", " + Iter->Name + + ", connection.GetConnectionID())"; + } else + Arg = Iter->Name; + + if (Iter->Type->isRValueReferenceType()) + Arg = "std::move(" + Arg + ")"; + Args.push_back(Arg); + + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) { + std::string LengthArg = Iter->Name + ".GetSize()"; + if (!Iter->Type->isVoidPointerType()) { + QualType UUT = lldb_rpc_gen::GetUnqualifiedUnderlyingType(Iter->Type); + LengthArg += " / sizeof(" + UUT.getAsString(method.Policy) + ")"; + } + Args.push_back(LengthArg); + Iter++; + } + } + MethodCallStream << "(" << llvm::join(Args, ", ") << ")"; + + return MethodCall; +} + +std::string RPCServerSourceEmitter::CreateEncodeLine(const std::string &Value, + bool IsEncodingSBClass) { + std::string EncodeLine; + llvm::raw_string_ostream EncodeLineStream(EncodeLine); + + if (IsEncodingSBClass) + EncodeLineStream << "RPCServerObjectEncoder("; + else + EncodeLineStream << "RPCValueEncoder("; + + EncodeLineStream + << "response, rpc_common::RPCPacket::ValueType::ReturnValue, "; + EncodeLineStream << Value; + EncodeLineStream << ");"; + return EncodeLine; +} + +// There are 4 cases to consider: +// - const SBClass &: No need to do anything. +// - const foo &: No need to do anything. +// - SBClass &: The server and the client hold on to IDs to refer to specific +// instances, so there's no need to send any information back to the client. +// - foo &: The client is sending us a value over the wire, but because the type +// is mutable, we must send the changed value back in case the method call +// mutated it. +// +// Updating a mutable reference is done as a return value from the RPC +// perspective. These return values need to be emitted after the method's return +// value, and they are emitted in the order in which they occur in the +// declaration. +void RPCServerSourceEmitter::EmitEncodesForMutableParameters( + const std::vector &Params) { + for (auto Iter = Params.begin(); Iter != Params.end(); Iter++) { + // No need to manually update an SBClass + if (lldb_rpc_gen::TypeIsSBClass(Iter->Type)) + continue; + + if (!Iter->Type->isReferenceType() && !Iter->Type->isPointerType()) + continue; + + // If we have a void pointer with no length, there's nothing to update. This + // is likely a baton for a callback. The same goes for function pointers. + if (Iter->Type->isFunctionPointerType() || + (Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen)) + continue; + + // No need to update pointers and references to const-qualified data. + QualType UnderlyingType = lldb_rpc_gen::GetUnderlyingType(Iter->Type); + if (UnderlyingType.isConstQualified()) + continue; + + const std::string EncodeLine = + CreateEncodeLine(Iter->Name, /* IsEncodingSBClass = */ false); + EmitLine(EncodeLine); + } +} + +// There are 3 possible scenarios that this method can encounter: +// 1. The method has no return value and is not a constructor. +// Only the method call itself is emitted. +// 2. The method is a constructor. +// The call to the constructor is emitted in the encode line. +// 3. The method has a return value. +// The method call is emitted and the return value is captured in a variable. +// After that, an encode call is emitted with the variable that captured the +// return value. +void RPCServerSourceEmitter::EmitMethodCallAndEncode(const Method &method) { + // FIXME: The hand-written lldb-rpc-server currently doesn't emit destructors + // for LocalObjectRefs... even if the type is an ObjectRef. What should we do + // here? + + const std::string MethodCall = CreateMethodCall(method); + + // If this function returns nothing, we just emit the call and update any + // mutable references. Note that constructors have return type `void` so we + // must explicitly check for that here. + if (!method.IsCtor && method.ReturnType->isVoidType()) { + EmitLine(MethodCall + ";"); + EmitEncodesForMutableParameters(method.Params); + return; + } + + static constexpr llvm::StringLiteral ReturnVariableName("__result"); + + // If this isn't a constructor, we'll need to store the result of the method + // call in a result variable. + if (!method.IsCtor) { + // We need to determine what the appropriate return type is. Here is the + // strategy: + // 1.) `SBFoo` -> `SBFoo &&` ---------------- DavidSpickett wrote: So if have SBFoo you return rvalue reference to SBFoo? I don't claim to understand `&&` in normal circumstances either, so maybe I am just confused. https://github.com/llvm/llvm-project/pull/138032 From lldb-commits at lists.llvm.org Tue May 6 06:16:07 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 06:16:07 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream RPC server interface emitters (PR #138032) In-Reply-To: Message-ID: <681a0b97.170a0220.2c13b.79fa@mx.google.com> https://github.com/DavidSpickett edited https://github.com/llvm/llvm-project/pull/138032 From lldb-commits at lists.llvm.org Tue May 6 06:26:57 2025 From: lldb-commits at lists.llvm.org (Hemang Gadhavi via lldb-commits) Date: Tue, 06 May 2025 06:26:57 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][AIX] get host info for AIX (cont..) (PR #138687) Message-ID: https://github.com/HemangGadhavi created https://github.com/llvm/llvm-project/pull/138687 This PR is in reference to porting LLDB on AIX. Link to discussions on llvm discourse and github: 1. https://discourse.llvm.org/t/port-lldb-to-ibm-aix/80640 2. https://github.com/llvm/llvm-project/issues/101657 The complete changes for porting are present in this draft PR: https://github.com/llvm/llvm-project/pull/102601 - Added changes to get the host information for AIX (info like FindProcessesImpl() GetProgramFileSpec()), continue from the PR https://github.com/llvm/llvm-project/pull/134354 [@DavidSpickett](https://github.com/DavidSpickett) [@labath](https://github.com/labath) [@DhruvSrivastavaX](https://github.com/DhruvSrivastavaX) Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Tue May 6 06:27:44 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Tue, 06 May 2025 06:27:44 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][AIX] get host info for AIX (cont..) (PR #138687) In-Reply-To: Message-ID: <681a0e50.170a0220.3683b7.47b0@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Hemang Gadhavi (HemangGadhavi)
Changes This PR is in reference to porting LLDB on AIX. Link to discussions on llvm discourse and github: 1. https://discourse.llvm.org/t/port-lldb-to-ibm-aix/80640 2. https://github.com/llvm/llvm-project/issues/101657 The complete changes for porting are present in this draft PR: https://github.com/llvm/llvm-project/pull/102601 - Added changes to get the host information for AIX (info like FindProcessesImpl() GetProgramFileSpec()), continue from the PR https://github.com/llvm/llvm-project/pull/134354 [@DavidSpickett](https://github.com/DavidSpickett) [@labath](https://github.com/labath) [@DhruvSrivastavaX](https://github.com/DhruvSrivastavaX) --- Full diff: https://github.com/llvm/llvm-project/pull/138687.diff 2 Files Affected: - (modified) lldb/source/Host/aix/Host.cpp (+48-1) - (modified) lldb/source/Host/aix/HostInfoAIX.cpp (+15) ``````````diff diff --git a/lldb/source/Host/aix/Host.cpp b/lldb/source/Host/aix/Host.cpp index a812e061ccae2..ead8202cbbdef 100644 --- a/lldb/source/Host/aix/Host.cpp +++ b/lldb/source/Host/aix/Host.cpp @@ -13,6 +13,7 @@ #include "lldb/Utility/ProcessInfo.h" #include "lldb/Utility/Status.h" #include "llvm/BinaryFormat/XCOFF.h" +#include #include #include @@ -41,6 +42,14 @@ static ProcessInstanceInfo::timespec convert(pr_timestruc64_t t) { return ts; } +static bool IsDirNumeric(const char *dname) { + for (; *dname; dname++) { + if (!isdigit(*dname)) + return false; + } + return true; +} + static bool GetStatusInfo(::pid_t pid, ProcessInstanceInfo &processInfo, ProcessState &State) { struct pstatus pstatusData; @@ -133,7 +142,45 @@ static bool GetProcessAndStatInfo(::pid_t pid, uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) { - return 0; + static const char procdir[] = "/proc/"; + + DIR *dirproc = opendir(procdir); + if (dirproc) { + struct dirent *direntry = nullptr; + const uid_t our_uid = getuid(); + const lldb::pid_t our_pid = getpid(); + bool all_users = match_info.GetMatchAllUsers(); + + while ((direntry = readdir(dirproc)) != nullptr) { + if (!IsDirNumeric(direntry->d_name)) + continue; + + lldb::pid_t pid = atoi(direntry->d_name); + // Skip this process. + if (pid == our_pid) + continue; + + ProcessState State; + ProcessInstanceInfo process_info; + if (!GetProcessAndStatInfo(pid, process_info, State)) + continue; + + if (State == ProcessState::Zombie || + State == ProcessState::TracedOrStopped) + continue; + + // Check for user match if we're not matching all users and not running + // as root. + if (!all_users && (our_uid != 0) && (process_info.GetUserID() != our_uid)) + continue; + + if (match_info.Matches(process_info)) { + process_infos.push_back(process_info); + } + } + closedir(dirproc); + } + return process_infos.size(); } bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) { diff --git a/lldb/source/Host/aix/HostInfoAIX.cpp b/lldb/source/Host/aix/HostInfoAIX.cpp index 61b47462dd647..d720f5c52d3b1 100644 --- a/lldb/source/Host/aix/HostInfoAIX.cpp +++ b/lldb/source/Host/aix/HostInfoAIX.cpp @@ -7,6 +7,8 @@ //===----------------------------------------------------------------------===// #include "lldb/Host/aix/HostInfoAIX.h" +#include "lldb/Host/posix/Support.h" +#include using namespace lldb_private; @@ -18,5 +20,18 @@ void HostInfoAIX::Terminate() { HostInfoBase::Terminate(); } FileSpec HostInfoAIX::GetProgramFileSpec() { static FileSpec g_program_filespec; + struct psinfo psinfoData; + auto BufferOrError = getProcFile(getpid(), "psinfo"); + if (BufferOrError) { + std::unique_ptr PsinfoBuffer = + std::move(*BufferOrError); + memcpy(&psinfoData, PsinfoBuffer->getBufferStart(), sizeof(psinfoData)); + llvm::StringRef exe_path( + psinfoData.pr_psargs, + strnlen(psinfoData.pr_psargs, sizeof(psinfoData.pr_psargs))); + if (!exe_path.empty()) { + g_program_filespec.SetFile(exe_path, FileSpec::Style::native); + } + } return g_program_filespec; } ``````````
https://github.com/llvm/llvm-project/pull/138687 From lldb-commits at lists.llvm.org Tue May 6 06:30:07 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 06:30:07 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream Python scripts (PR #138028) In-Reply-To: Message-ID: <681a0edf.630a0220.16a024.5de5@mx.google.com> ================ @@ -0,0 +1,13 @@ +// Generate a dummy SB API file using lldb-rpc-gen. +# RUN: mkdir -p %t/server +# RUN: mkdir -p %t/lib ---------------- DavidSpickett wrote: CI failing with: ``` /var/lib/buildkite-agent/builds/linux-56-59b8f5d88-spx4x-1/llvm-project/github-pull-requests/build/tools/lldb/test/Shell/RPC/Scripts/TestFrameworkIncludeFixScript/Output/CheckSBClass.test.script: line 3: fg: no job control ``` Is it passing locally on Linux for you? I don't see anything that would need job control, I'm wondering if a stray character is causing the shell to think that. https://github.com/llvm/llvm-project/pull/138028 From lldb-commits at lists.llvm.org Tue May 6 08:46:49 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Tue, 06 May 2025 08:46:49 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add IsCoreDumping to ProcessInstanceInfo (PR #138580) In-Reply-To: Message-ID: <681a2ee9.170a0220.77795.a4d9@mx.google.com> ================ @@ -115,5 +115,8 @@ TEST_F(HostTest, GetProcessInfoSetsPriority) { } ASSERT_TRUE(Info.IsZombie().has_value()); ASSERT_FALSE(Info.IsZombie().value()); + + ASSERT_TRUE(Info.IsCoreDumping().has_value()); + ASSERT_FALSE(Info.IsCoreDumping().value()); ---------------- Jlalond wrote: Since 4.15, so around 2018 was the end of LTO for that version. I think we're safe to assume it's going to be ubiquitous on our test hosts. As for customers, we would just fail to read it https://github.com/llvm/llvm-project/pull/138580 From lldb-commits at lists.llvm.org Tue May 6 08:47:15 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Tue, 06 May 2025 08:47:15 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add IsCoreDumping to ProcessInstanceInfo (PR #138580) In-Reply-To: Message-ID: <681a2f03.170a0220.3a5d6f.df07@mx.google.com> ================ @@ -213,6 +213,11 @@ static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo, } else if (Line.consume_front("Tgid:")) { Line = Line.ltrim(); Line.consumeInteger(10, Tgid); + } else if (Line.consume_front("CoreDumping:")) { + uint32_t coredumping; + Line = Line.ltrim(); + Line.consumeInteger(1, coredumping); ---------------- Jlalond wrote: Good suggestion, I'll validate the return. https://github.com/llvm/llvm-project/pull/138580 From lldb-commits at lists.llvm.org Tue May 6 09:34:25 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 09:34:25 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add IsCoreDumping to ProcessInstanceInfo (PR #138580) In-Reply-To: Message-ID: <681a3a11.170a0220.af078.0ef0@mx.google.com> https://github.com/DavidSpickett approved this pull request. Once you've checked the return value, this LGTM. https://github.com/llvm/llvm-project/pull/138580 From lldb-commits at lists.llvm.org Tue May 6 09:41:59 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Tue, 06 May 2025 09:41:59 -0700 (PDT) Subject: [Lldb-commits] [lldb] [DRAFT][lldb][RPC] Design doc for upstreaming PR (PR #138612) In-Reply-To: Message-ID: <681a3bd7.170a0220.ffaf7.535a@mx.google.com> chelcassanova wrote: Thanks for responding! I'll add a section on testing the tool itself (which we do with shell tests) and testing the interfaces (which we do by running the main LLDB API test suite against liblldbrpc). > Also, is there a way for a developer to set up an lldb-rpc based session? If we have an issue that only happens over lldb-rpc. Which in theory we won't, but we might think we do and need to check that. Both the RPC client and server are built as binaries that each have a main. The RPC core code contains the functionality for a client binary to connect to RPC and then interface with the `lldb-rpc-server` binary, so Xcode isn't the only way to create a session. This is how we set up the API test suite for RPC, as Python is used as the client binary for the RPC session. https://github.com/llvm/llvm-project/pull/138612 From lldb-commits at lists.llvm.org Tue May 6 09:48:35 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 09:48:35 -0700 (PDT) Subject: [Lldb-commits] [lldb] [DRAFT][lldb][RPC] Design doc for upstreaming PR (PR #138612) In-Reply-To: Message-ID: <681a3d63.620a0220.198627.67b1@mx.google.com> DavidSpickett wrote: Ok so if had a very specific situation I wanted to run via lldb-rpc, I could write an API test for it? That's cool, if I do find a bug I'd need to write a test case anyway. Please add that as a suggestion in the document where you describe / will describe the testing process. https://github.com/llvm/llvm-project/pull/138612 From lldb-commits at lists.llvm.org Tue May 6 09:49:24 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 09:49:24 -0700 (PDT) Subject: [Lldb-commits] [lldb] [DRAFT][lldb][RPC] Design doc for upstreaming PR (PR #138612) In-Reply-To: Message-ID: <681a3d94.170a0220.2dfcd0.9f19@mx.google.com> DavidSpickett wrote: But there's no lldb-rpc client built into `lldb`, right? Because that would be my first instinct to try. https://github.com/llvm/llvm-project/pull/138612 From lldb-commits at lists.llvm.org Tue May 6 09:59:46 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Tue, 06 May 2025 09:59:46 -0700 (PDT) Subject: [Lldb-commits] [lldb] [DRAFT][lldb][RPC] Design doc for upstreaming PR (PR #138612) In-Reply-To: Message-ID: <681a4002.630a0220.ac817.0e3c@mx.google.com> https://github.com/chelcassanova updated https://github.com/llvm/llvm-project/pull/138612 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Tue May 6 10:04:46 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Tue, 06 May 2025 10:04:46 -0700 (PDT) Subject: [Lldb-commits] [lldb] [DRAFT][lldb][RPC] Design doc for upstreaming PR (PR #138612) In-Reply-To: Message-ID: <681a412e.170a0220.123fa8.0759@mx.google.com> ================ @@ -0,0 +1,94 @@ +LLDB RPC Upstreaming Design Doc +=============================== + +This document aims to explain the general structure of the upstreaming patches for adding LLDB RPC. The 2 primary concepts explained here will be: + +* How LLDB RPC is used +* How the ``lldb-rpc-gen`` works and what it outputs + +LLDB RPC +********* + +LLDB RPC is a framework by which processes can communicate with LLDB out of process while maintaining compatibility with the SB API. More details are explained in the `RFC`_ for upstreaming LLDB RPC, but the main focus in this doc for this section will be how exactly the code is structured for the PRs that will upstream this code. + +The ``lldb-rpc-gen`` tool +************************* + +``lldb-rpc-gen`` is the tool that generates the main client and server interfaces for LLDB RPC. It is a ``ClangTool`` that reads all SB API header files and their functions and outputs the client/server interfaces and certain other pieces of code, such as RPC-specfic versions of Python bindings used for the test suite. There's 3 main components behind ``lldb-rpc-gen``: + +1. The ``lldb-rpc-gen`` tool itself, which contains the main driver that uses the ``ClangTool``. +2. The code that generates all interfaces, which we call "emitters". All generated code for the interfaces are in C++, so the server side has one emitter for its generated source code and another for its generated header code. The client side has the same. +3. All common code shared between all emitters, such as helper methods and information about exceptions to take when emitting. + +The `current PR`_ up for upstreaming LLDB RPC upstreams a subset of the code used for the tool. It upstreams the ``lldb-rpc-gen`` tool and all code needed for the server side emitters. Here's an example of what ``lldb-rpc-gen`` will output for the server side interface: + +Input +----- + +We'll use ``SBDebugger::CreateTarget(const char *filename)`` as an example. ``lldb-rpc-gen`` will read this method from ``SBDebugger.h``. The output is as follows. + +Source Code Output +------------------ + +:: + + bool rpc_server::_ZN4lldb10SBDebugger12CreateTargetEPKc::HandleRPCCall(rpc_common::Connection &connection, RPCStream &send, RPCStream &response) { + // 1) Make local storage for incoming function arguments + lldb::SBDebugger *this_ptr = nullptr; + rpc_common::ConstCharPointer filename; + // 2) Decode all function arguments + this_ptr = RPCServerObjectDecoder(send, rpc_common::RPCPacket::ValueType::Argument); + if (!this_ptr) + return false; + if (!RPCValueDecoder(send, rpc_common::RPCPacket::ValueType::Argument, filename)) + return false; + // 3) Call the method and encode the return value + lldb::SBTarget && __result = this_ptr->CreateTarget(filename.c_str()); + RPCServerObjectEncoder(response, rpc_common::RPCPacket::ValueType::ReturnValue, std::move(__result)); + return true; + } + +Function signature +~~~~~~~~~~~~~~~~~~ + +All server-side source code functions have a function signature that take the format ``bool rpc_server::::HandleRPCCall(rpc_common::Connection &connection, RPCStream &send, RPCStream &response)``. Here the ``connection`` is what's maintained between the client and server. The ``send`` variable is a byte stream that carries information sent from the client. ``response`` is also a byte stream that will be populated with the return value obtained from the call into the SB API function that will be sent back to the client. ---------------- chelcassanova wrote: Since we have interfaces for all functions, we want to differentiate them in the event of overloaded functions, e.g. `SBDebugger::CreateTarget` has 2 signatures, so each signature gets registered on the server side as: - `bool rpc_server::_ZN4lldb10SBDebugger12CreateTargetEPKc::HandleRPCCall` - `bool rpc_server::_ZN4lldb10SBDebugger12CreateTargetEPKcS2_S2_bRNS_7SBErrorE::HandleRPCCall` https://github.com/llvm/llvm-project/pull/138612 From lldb-commits at lists.llvm.org Tue May 6 10:06:51 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Tue, 06 May 2025 10:06:51 -0700 (PDT) Subject: [Lldb-commits] [lldb] [DRAFT][lldb][RPC] Design doc for upstreaming PR (PR #138612) In-Reply-To: Message-ID: <681a41ab.170a0220.25a0ec.12ac@mx.google.com> chelcassanova wrote: > Ok so if had a very specific situation I wanted to run via lldb-rpc, I could write an API test for it? That's cool, if I do find a bug I'd need to write a test case anyway. Yes, you could have an API test that could then get run against liblldbrpc. We also have decorators to exclude/include tests that run against liblldbrpc. > But there's no lldb-rpc client built into lldb, right? Because that would be my first instinct to try. Not directly in lldb itself as the `lldb-rpc-client` binary is a separate build object. https://github.com/llvm/llvm-project/pull/138612 From lldb-commits at lists.llvm.org Tue May 6 10:12:16 2025 From: lldb-commits at lists.llvm.org (Mikhail Zakharov via lldb-commits) Date: Tue, 06 May 2025 10:12:16 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Do not bump memory modificator ID when "internal" debugger memory is updated (PR #129092) In-Reply-To: Message-ID: <681a42f0.170a0220.27a584.0495@mx.google.com> real-mikhail wrote: With the current test fix (`TestProcessModificationIdOnExpr`), it is locked to `target-x86` but it seems that it should require `target-x86_64` instead. Currently it is not run on Windows-x64 and it fails on Windows-x86 (since the test is checking low level internal data, and it is different on different architectures). Ideally, I should not check the exact values of `m_stop_id` and `m_memory_id`. We are only interested in the fact whether those values are changed between several calls `process status -d`. But as far as I know there is no way in testing framework to "remember" specific value and refer to it (compare it) later. https://github.com/llvm/llvm-project/pull/129092 From lldb-commits at lists.llvm.org Tue May 6 10:20:02 2025 From: lldb-commits at lists.llvm.org (Kazu Hirata via lldb-commits) Date: Tue, 06 May 2025 10:20:02 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Use llvm::bit_ceil (NFC) (PR #138723) Message-ID: https://github.com/kazutakahirata created https://github.com/llvm/llvm-project/pull/138723 This patch replaces a local implementation of bit_ceil with llvm::bit_ceil. Technically, the local implementation evaluates to 0 on input 0, whereas llvm::bit_ceil evaluates to 1, but that doesn't matter because we have: // Can't watch zero bytes. if (user_size == 0) return {}; >From ddf9dd335a5d63ff414370b4c1162fa2bac4195a Mon Sep 17 00:00:00 2001 From: Kazu Hirata Date: Tue, 6 May 2025 10:02:55 -0700 Subject: [PATCH] [lldb] Use llvm::bit_ceil (NFC) This patch replaces a local implementation of bit_ceil with llvm::bit_ceil. Technically, the local implementation evaluates to 0 on input 0, whereas llvm::bit_ceil evaluates to 1, but that doesn't matter because we have: // Can't watch zero bytes. if (user_size == 0) return {}; --- lldb/source/Breakpoint/WatchpointAlgorithms.cpp | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/lldb/source/Breakpoint/WatchpointAlgorithms.cpp b/lldb/source/Breakpoint/WatchpointAlgorithms.cpp index 3caf29b04317f..d65de13db1dad 100644 --- a/lldb/source/Breakpoint/WatchpointAlgorithms.cpp +++ b/lldb/source/Breakpoint/WatchpointAlgorithms.cpp @@ -58,16 +58,6 @@ WatchpointAlgorithms::AtomizeWatchpointRequest( return resources; } -// This should be `std::bit_ceil(aligned_size)` but -// that requires C++20. -// Calculates the smallest integral power of two that is not smaller than x. -static uint64_t bit_ceil(uint64_t input) { - if (input <= 1 || llvm::popcount(input) == 1) - return input; - - return 1ULL << (64 - llvm::countl_zero(input)); -} - /// Convert a user's watchpoint request (\a user_addr and \a user_size) /// into hardware watchpoints, for a target that can watch a power-of-2 /// region of memory (1, 2, 4, 8, etc), aligned to that same power-of-2 @@ -102,7 +92,7 @@ WatchpointAlgorithms::PowerOf2Watchpoints(addr_t user_addr, size_t user_size, /// Round up \a user_size to the next power-of-2 size /// user_size == 8 -> aligned_size == 8 /// user_size == 9 -> aligned_size == 16 - aligned_size = bit_ceil(aligned_size); + aligned_size = llvm::bit_ceil(aligned_size); addr_t aligned_start = user_addr & ~(aligned_size - 1); From lldb-commits at lists.llvm.org Tue May 6 10:20:41 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Tue, 06 May 2025 10:20:41 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Use llvm::bit_ceil (NFC) (PR #138723) In-Reply-To: Message-ID: <681a44e9.050a0220.2a5eb4.b523@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Kazu Hirata (kazutakahirata)
Changes This patch replaces a local implementation of bit_ceil with llvm::bit_ceil. Technically, the local implementation evaluates to 0 on input 0, whereas llvm::bit_ceil evaluates to 1, but that doesn't matter because we have: // Can't watch zero bytes. if (user_size == 0) return {}; --- Full diff: https://github.com/llvm/llvm-project/pull/138723.diff 1 Files Affected: - (modified) lldb/source/Breakpoint/WatchpointAlgorithms.cpp (+1-11) ``````````diff diff --git a/lldb/source/Breakpoint/WatchpointAlgorithms.cpp b/lldb/source/Breakpoint/WatchpointAlgorithms.cpp index 3caf29b04317f..d65de13db1dad 100644 --- a/lldb/source/Breakpoint/WatchpointAlgorithms.cpp +++ b/lldb/source/Breakpoint/WatchpointAlgorithms.cpp @@ -58,16 +58,6 @@ WatchpointAlgorithms::AtomizeWatchpointRequest( return resources; } -// This should be `std::bit_ceil(aligned_size)` but -// that requires C++20. -// Calculates the smallest integral power of two that is not smaller than x. -static uint64_t bit_ceil(uint64_t input) { - if (input <= 1 || llvm::popcount(input) == 1) - return input; - - return 1ULL << (64 - llvm::countl_zero(input)); -} - /// Convert a user's watchpoint request (\a user_addr and \a user_size) /// into hardware watchpoints, for a target that can watch a power-of-2 /// region of memory (1, 2, 4, 8, etc), aligned to that same power-of-2 @@ -102,7 +92,7 @@ WatchpointAlgorithms::PowerOf2Watchpoints(addr_t user_addr, size_t user_size, /// Round up \a user_size to the next power-of-2 size /// user_size == 8 -> aligned_size == 8 /// user_size == 9 -> aligned_size == 16 - aligned_size = bit_ceil(aligned_size); + aligned_size = llvm::bit_ceil(aligned_size); addr_t aligned_start = user_addr & ~(aligned_size - 1); ``````````
https://github.com/llvm/llvm-project/pull/138723 From lldb-commits at lists.llvm.org Tue May 6 10:22:43 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Tue, 06 May 2025 10:22:43 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Migrate attach to typed RequestHandler. (PR #137911) In-Reply-To: Message-ID: <681a4563.170a0220.ca6eb.20a3@mx.google.com> https://github.com/ashgti updated https://github.com/llvm/llvm-project/pull/137911 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Tue May 6 10:34:50 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Tue, 06 May 2025 10:34:50 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream Python scripts (PR #138028) In-Reply-To: Message-ID: <681a483a.630a0220.3af8c.2c2c@mx.google.com> ================ @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +# Usage: convert-lldb-header-to-rpc-header.py +# This scripts takes common LLDB headers (such as lldb-defines.h) and replaces references to LLDB +# with those for RPC. This happens for: +# - namespace definitions +# - namespace usage +# - version string macros +# - ifdef/ifndef lines + +import argparse +import os +import re + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("input") + parser.add_argument("output") + args = parser.parse_args() + input_path = str(args.input) + output_path = str(args.output) + with open(input_path, "r") as input_file: + lines = input_file.readlines() + + with open(output_path, "w") as output_file: + for line in lines: + # NOTE: We do not use lldb-forward.h or lldb-versioning.h in RPC, so remove + # all includes that are found for these files. + if re.match( + r'#include "lldb/lldb-forward|#include "lldb/lldb-versioning', line + ): + continue + # For lldb-rpc-defines.h, replace the ifndef LLDB_LLDB_ portion with LLDB_RPC_ as we're not + # using LLDB private definitions in RPC. + elif re.match(r".+LLDB_LLDB_", line): + output_file.write(re.sub(r"LLDB_LLDB_", r"LLDB_RPC_", line)) + # Similarly to lldb-rpc-defines.h, replace the ifndef for LLDB_API in SBDefines.h to LLDB_RPC_API_ for the same reason. + elif re.match(r".+LLDB_API_", line): + output_file.write(re.sub(r"LLDB_API_", r"LLDB_RPC_API_", line)) ---------------- chelcassanova wrote: > if matches this then sub with this I can try this instances where the pattern that gets checked with both `if` and then `sub` are the same thing. https://github.com/llvm/llvm-project/pull/138028 From lldb-commits at lists.llvm.org Tue May 6 10:35:32 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Tue, 06 May 2025 10:35:32 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream Python scripts (PR #138028) In-Reply-To: Message-ID: <681a4864.650a0220.143407.8d31@mx.google.com> ================ @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# Usage: framework-header-include-fix.py +# This script modifies all #include lines in all lldb-rpc headers +# from either filesystem or local includes to liblldbrpc includes. + +import argparse +import os +import re + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("input") + parser.add_argument("output") + args = parser.parse_args() + input_path = str(args.input) + output_path = str(args.output) + with open(input_path, "r+") as input_file: ---------------- chelcassanova wrote: This was a mistake from an earlier implementation, I shoud've had all of these replaced with just `r` or `w` from your original set of comments on the larger PR. https://github.com/llvm/llvm-project/pull/138028 From lldb-commits at lists.llvm.org Tue May 6 10:41:04 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Tue, 06 May 2025 10:41:04 -0700 (PDT) Subject: [Lldb-commits] [lldb] [DRAFT][lldb][RPC] Design doc for upstreaming PR (PR #138612) In-Reply-To: Message-ID: <681a49b0.630a0220.31ff65.2deb@mx.google.com> bulbazord wrote: > > But there's no lldb-rpc client built into lldb, right? Because that would be my first instinct to try. > > Not directly in lldb itself as the `lldb-rpc-client` binary is a separate build object. To add to this, LLDBRPC's API should be *almost* the same as LLDB's. The major difference is that some methods and constructors will need to take an additional `connection` parameter. It would be possible to write some `lldb-rpc` driver, but we haven't done. https://github.com/llvm/llvm-project/pull/138612 From lldb-commits at lists.llvm.org Tue May 6 10:41:12 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Tue, 06 May 2025 10:41:12 -0700 (PDT) Subject: [Lldb-commits] [lldb] [DRAFT][lldb][RPC] Design doc for upstreaming PR (PR #138612) In-Reply-To: Message-ID: <681a49b8.170a0220.293329.15fe@mx.google.com> ================ @@ -0,0 +1,107 @@ +LLDB RPC Upstreaming Design Doc +=============================== + +This document aims to explain the general structure of the upstreaming patches for adding LLDB RPC. The 2 primary concepts explained here will be: + +* How LLDB RPC is used +* How the ``lldb-rpc-gen`` works and what it outputs + +LLDB RPC +********* + +LLDB RPC is a framework by which processes can communicate with LLDB out of process while maintaining compatibility with the SB API. More details are explained in the `RFC`_ for upstreaming LLDB RPC, but the main focus in this doc for this section will be how exactly the code is structured for the PRs that will upstream this code. + +The ``lldb-rpc-gen`` tool +************************* + +``lldb-rpc-gen`` is the tool that generates the main client and server interfaces for LLDB RPC. It is a ``ClangTool`` that reads all SB API header files and their functions and outputs the client/server interfaces and certain other pieces of code, such as RPC-specfic versions of Python bindings used for the test suite. There's 3 main components behind ``lldb-rpc-gen``: + +1. The ``lldb-rpc-gen`` tool itself, which contains the main driver that uses the ``ClangTool``. +2. The code that generates all interfaces, which we call "emitters". All generated code for the interfaces are in C++, so the server side has one emitter for its generated source code and another for its generated header code. The client side has the same. +3. All common code shared between all emitters, such as helper methods and information about exceptions to take when emitting. + +There are currently 3 PRs up for upstreaming RPC: +- One that adds the ``lldb-rpc-gen`` tool and its common code: https://github.com/llvm/llvm-project/pull/138031 +- One that adds the RPC server-side interface code emitters: https://github.com/llvm/llvm-project/pull/138032 +- One that adds Python scripts to modify the LLDB private headers for use with RPC: https://github.com/llvm/llvm-project/pull/138028 + +The `current PR`_ up for upstreaming LLDB RPC upstreams a subset of the code used for the tool. It upstreams the ``lldb-rpc-gen`` tool and all code needed for the server side emitters. Here's an example of what ``lldb-rpc-gen`` will output for the server side interface: + +Input +----- + +We'll use ``SBDebugger::CreateTarget(const char *filename)`` as an example. ``lldb-rpc-gen`` will read this method from ``SBDebugger.h``. The output is as follows. + +Source Code Output +------------------ + +:: + + bool rpc_server::_ZN4lldb10SBDebugger12CreateTargetEPKc::HandleRPCCall(rpc_common::Connection &connection, RPCStream &send, RPCStream &response) { + // 1) Make local storage for incoming function arguments + lldb::SBDebugger *this_ptr = nullptr; + rpc_common::ConstCharPointer filename; + // 2) Decode all function arguments + this_ptr = RPCServerObjectDecoder(send, rpc_common::RPCPacket::ValueType::Argument); + if (!this_ptr) + return false; + if (!RPCValueDecoder(send, rpc_common::RPCPacket::ValueType::Argument, filename)) + return false; + // 3) Call the method and encode the return value + lldb::SBTarget && __result = this_ptr->CreateTarget(filename.c_str()); + RPCServerObjectEncoder(response, rpc_common::RPCPacket::ValueType::ReturnValue, std::move(__result)); + return true; + } + +Function signature +~~~~~~~~~~~~~~~~~~ + +All server-side source code functions have a function signature that take the format ``bool rpc_server::::HandleRPCCall(rpc_common::Connection &connection, RPCStream &send, RPCStream &response)``. Here the ``connection`` is what's maintained between the client and server. The ``send`` variable is a byte stream that carries information sent from the client. ``response`` is also a byte stream that will be populated with the return value obtained from the call into the SB API function that will be sent back to the client. + +Local variable storage +~~~~~~~~~~~~~~~~~~~~~~ + +First, variables are created to hold all arguments coming in from the client side. These variables will be a pointer for the SB API class in question, and corresponding variables for all parameters that the function has. Since this signature for ``SBDebugger::CreateTarget()`` only has one parameter, a ``const char *``, 2 local variables get created. A pointer for an ``SBDebugger`` object, and an ``RPCCommon::ConstCharPointer`` for the ``const char * filename`` parameter. The ``ConstCharPointer`` is a typedef over ``const char *`` in the main RPC core code. ---------------- bulbazord wrote: Note: `ConstCharPointer` is not a typedef but a full-fledged class. It's backed by a `std::string` but has lots of convenience methods for operating at the RPC layer. https://github.com/llvm/llvm-project/pull/138612 From lldb-commits at lists.llvm.org Tue May 6 10:41:13 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Tue, 06 May 2025 10:41:13 -0700 (PDT) Subject: [Lldb-commits] [lldb] [DRAFT][lldb][RPC] Design doc for upstreaming PR (PR #138612) In-Reply-To: Message-ID: <681a49b9.a70a0220.3751bc.7be9@mx.google.com> ================ @@ -0,0 +1,94 @@ +LLDB RPC Upstreaming Design Doc +=============================== + +This document aims to explain the general structure of the upstreaming patches for adding LLDB RPC. The 2 primary concepts explained here will be: + +* How LLDB RPC is used +* How the ``lldb-rpc-gen`` works and what it outputs + +LLDB RPC +********* + +LLDB RPC is a framework by which processes can communicate with LLDB out of process while maintaining compatibility with the SB API. More details are explained in the `RFC`_ for upstreaming LLDB RPC, but the main focus in this doc for this section will be how exactly the code is structured for the PRs that will upstream this code. + +The ``lldb-rpc-gen`` tool +************************* + +``lldb-rpc-gen`` is the tool that generates the main client and server interfaces for LLDB RPC. It is a ``ClangTool`` that reads all SB API header files and their functions and outputs the client/server interfaces and certain other pieces of code, such as RPC-specfic versions of Python bindings used for the test suite. There's 3 main components behind ``lldb-rpc-gen``: + +1. The ``lldb-rpc-gen`` tool itself, which contains the main driver that uses the ``ClangTool``. +2. The code that generates all interfaces, which we call "emitters". All generated code for the interfaces are in C++, so the server side has one emitter for its generated source code and another for its generated header code. The client side has the same. +3. All common code shared between all emitters, such as helper methods and information about exceptions to take when emitting. + +The `current PR`_ up for upstreaming LLDB RPC upstreams a subset of the code used for the tool. It upstreams the ``lldb-rpc-gen`` tool and all code needed for the server side emitters. Here's an example of what ``lldb-rpc-gen`` will output for the server side interface: + +Input +----- + +We'll use ``SBDebugger::CreateTarget(const char *filename)`` as an example. ``lldb-rpc-gen`` will read this method from ``SBDebugger.h``. The output is as follows. + +Source Code Output +------------------ + +:: + + bool rpc_server::_ZN4lldb10SBDebugger12CreateTargetEPKc::HandleRPCCall(rpc_common::Connection &connection, RPCStream &send, RPCStream &response) { + // 1) Make local storage for incoming function arguments + lldb::SBDebugger *this_ptr = nullptr; + rpc_common::ConstCharPointer filename; + // 2) Decode all function arguments + this_ptr = RPCServerObjectDecoder(send, rpc_common::RPCPacket::ValueType::Argument); + if (!this_ptr) + return false; + if (!RPCValueDecoder(send, rpc_common::RPCPacket::ValueType::Argument, filename)) + return false; + // 3) Call the method and encode the return value + lldb::SBTarget && __result = this_ptr->CreateTarget(filename.c_str()); + RPCServerObjectEncoder(response, rpc_common::RPCPacket::ValueType::ReturnValue, std::move(__result)); + return true; + } + +Function signature +~~~~~~~~~~~~~~~~~~ + +All server-side source code functions have a function signature that take the format ``bool rpc_server::::HandleRPCCall(rpc_common::Connection &connection, RPCStream &send, RPCStream &response)``. Here the ``connection`` is what's maintained between the client and server. The ``send`` variable is a byte stream that carries information sent from the client. ``response`` is also a byte stream that will be populated with the return value obtained from the call into the SB API function that will be sent back to the client. ---------------- bulbazord wrote: For context, this choice was originally made by @clayborg when he was at Apple. I worked on the earliest incarnation of lldb-rpc-gen and decided to keep the mangled name for the reason Chelsea mentioned, but the choice is indeed arbitrary. https://github.com/llvm/llvm-project/pull/138612 From lldb-commits at lists.llvm.org Tue May 6 10:41:13 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Tue, 06 May 2025 10:41:13 -0700 (PDT) Subject: [Lldb-commits] [lldb] [DRAFT][lldb][RPC] Design doc for upstreaming PR (PR #138612) In-Reply-To: Message-ID: <681a49b9.170a0220.3cda32.18ec@mx.google.com> ================ @@ -0,0 +1,107 @@ +LLDB RPC Upstreaming Design Doc +=============================== + +This document aims to explain the general structure of the upstreaming patches for adding LLDB RPC. The 2 primary concepts explained here will be: + +* How LLDB RPC is used +* How the ``lldb-rpc-gen`` works and what it outputs + +LLDB RPC +********* + +LLDB RPC is a framework by which processes can communicate with LLDB out of process while maintaining compatibility with the SB API. More details are explained in the `RFC`_ for upstreaming LLDB RPC, but the main focus in this doc for this section will be how exactly the code is structured for the PRs that will upstream this code. + +The ``lldb-rpc-gen`` tool +************************* + +``lldb-rpc-gen`` is the tool that generates the main client and server interfaces for LLDB RPC. It is a ``ClangTool`` that reads all SB API header files and their functions and outputs the client/server interfaces and certain other pieces of code, such as RPC-specfic versions of Python bindings used for the test suite. There's 3 main components behind ``lldb-rpc-gen``: + +1. The ``lldb-rpc-gen`` tool itself, which contains the main driver that uses the ``ClangTool``. +2. The code that generates all interfaces, which we call "emitters". All generated code for the interfaces are in C++, so the server side has one emitter for its generated source code and another for its generated header code. The client side has the same. +3. All common code shared between all emitters, such as helper methods and information about exceptions to take when emitting. + +There are currently 3 PRs up for upstreaming RPC: +- One that adds the ``lldb-rpc-gen`` tool and its common code: https://github.com/llvm/llvm-project/pull/138031 +- One that adds the RPC server-side interface code emitters: https://github.com/llvm/llvm-project/pull/138032 +- One that adds Python scripts to modify the LLDB private headers for use with RPC: https://github.com/llvm/llvm-project/pull/138028 + +The `current PR`_ up for upstreaming LLDB RPC upstreams a subset of the code used for the tool. It upstreams the ``lldb-rpc-gen`` tool and all code needed for the server side emitters. Here's an example of what ``lldb-rpc-gen`` will output for the server side interface: + +Input +----- + +We'll use ``SBDebugger::CreateTarget(const char *filename)`` as an example. ``lldb-rpc-gen`` will read this method from ``SBDebugger.h``. The output is as follows. + +Source Code Output +------------------ + +:: + + bool rpc_server::_ZN4lldb10SBDebugger12CreateTargetEPKc::HandleRPCCall(rpc_common::Connection &connection, RPCStream &send, RPCStream &response) { + // 1) Make local storage for incoming function arguments + lldb::SBDebugger *this_ptr = nullptr; + rpc_common::ConstCharPointer filename; + // 2) Decode all function arguments + this_ptr = RPCServerObjectDecoder(send, rpc_common::RPCPacket::ValueType::Argument); + if (!this_ptr) + return false; + if (!RPCValueDecoder(send, rpc_common::RPCPacket::ValueType::Argument, filename)) + return false; + // 3) Call the method and encode the return value + lldb::SBTarget && __result = this_ptr->CreateTarget(filename.c_str()); + RPCServerObjectEncoder(response, rpc_common::RPCPacket::ValueType::ReturnValue, std::move(__result)); + return true; + } + +Function signature +~~~~~~~~~~~~~~~~~~ + +All server-side source code functions have a function signature that take the format ``bool rpc_server::::HandleRPCCall(rpc_common::Connection &connection, RPCStream &send, RPCStream &response)``. Here the ``connection`` is what's maintained between the client and server. The ``send`` variable is a byte stream that carries information sent from the client. ``response`` is also a byte stream that will be populated with the return value obtained from the call into the SB API function that will be sent back to the client. + +Local variable storage +~~~~~~~~~~~~~~~~~~~~~~ + +First, variables are created to hold all arguments coming in from the client side. These variables will be a pointer for the SB API class in question, and corresponding variables for all parameters that the function has. Since this signature for ``SBDebugger::CreateTarget()`` only has one parameter, a ``const char *``, 2 local variables get created. A pointer for an ``SBDebugger`` object, and an ``RPCCommon::ConstCharPointer`` for the ``const char * filename`` parameter. The ``ConstCharPointer`` is a typedef over ``const char *`` in the main RPC core code. + +Incoming stream decoding +~~~~~~~~~~~~~~~~~~~~~~~~ + +Following this, ``RPCServerObjectDecoder`` is used to decode the ``send`` byte stream. In this case, we're decoding this stream into the ``SBDebugger`` pointer we created earlier. We then decode the ``send`` stream again to obtain the ``const char * filename`` sent by the client. Each decoded argument from the client is checked for validity and the function will exit early if any are invalid. + +SB API function call +~~~~~~~~~~~~~~~~~~~~ + +Once all arguments have been decoded, the underlying SB API function called with the decoded arguments. ``RPCServerObjectEncoder`` is then used to encode the return value from the SB API call into the ``response`` stream, and this is then sent back to the client. + +Header Code Output +------------------ +:: + + class _ZN4lldb10SBDebugger12CreateTargetEPKc : public rpc_common::RPCFunctionInstance { + public: + _ZN4lldb10SBDebugger12CreateTargetEPKc() : RPCFunctionInstance("_ZN4lldb10SBDebugger12CreateTargetEPKc") {} + ~_ZN4lldb10SBDebugger12CreateTargetEPKc() override {} + bool HandleRPCCall(rpc_common::Connection &connection, rpc_common::RPCStream &send, rpc_common::RPCStream &response) override; + }; + +Class definition and ``HandleRPCCall`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +ALL RPC server side functions are subclasses of ``RPCFunctionInstance``. Each class will then define their ``HandleRPCCall`` function that is seen in the source code above. This subclassing and ``HandleRPCCall`` definition is what is emitted in the header code for server. ---------------- bulbazord wrote: nit: `ALL` -> `All` https://github.com/llvm/llvm-project/pull/138612 From lldb-commits at lists.llvm.org Tue May 6 10:46:03 2025 From: lldb-commits at lists.llvm.org (Michael Buch via lldb-commits) Date: Tue, 06 May 2025 10:46:03 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fix dynamic type resolutions for core files (PR #138698) In-Reply-To: Message-ID: <681a4adb.170a0220.1e1a05.1dbe@mx.google.com> https://github.com/Michael137 approved this pull request. https://github.com/llvm/llvm-project/pull/138698 From lldb-commits at lists.llvm.org Tue May 6 10:53:19 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Tue, 06 May 2025 10:53:19 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream lldb-rpc-gen tool (PR #138031) In-Reply-To: Message-ID: <681a4c8f.a70a0220.3aac56.c538@mx.google.com> https://github.com/bulbazord edited https://github.com/llvm/llvm-project/pull/138031 From lldb-commits at lists.llvm.org Tue May 6 10:53:19 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Tue, 06 May 2025 10:53:19 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream lldb-rpc-gen tool (PR #138031) In-Reply-To: Message-ID: <681a4c8f.050a0220.27c5b.c038@mx.google.com> ================ @@ -0,0 +1,535 @@ +//===-- 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/Mangle.h" +#include "clang/Lex/Lexer.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +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", // What is this used for? + "SBInputReader", // What is this used for? + "SBCommandPluginInterface", // This is hard to support, we can do it if + // really needed though. + "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. +}; + +// We intentionally avoid generating certain methods either because they are +// difficult to support correctly or they aren't really used much from C++. +// FIXME: We should be able to annotate these methods instead of maintaining a +// list in the generator itself. +static constexpr llvm::StringRef DisallowedMethods[] = { + // The threading functionality in SBHostOS is deprecated and thus we do not + // generate them. It would be ideal to add the annotations to the methods + // and then support not generating deprecated methods. However, without + // annotations the generator generates most things correctly. This one is + // problematic because it returns a pointer to an "opaque" structure + // (thread_t) that is not `void *`, so special casing it is more effort than + // it's worth. + "_ZN4lldb8SBHostOS10ThreadJoinEP17_opaque_pthread_tPPvPNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadCancelEP17_opaque_pthread_tPNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadCreateEPKcPFPvS3_ES3_PNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadDetachEP17_opaque_pthread_tPNS_7SBErrorE", + "_ZN4lldb8SBHostOS13ThreadCreatedEPKc", +}; + +static constexpr llvm::StringRef ClassesWithoutDefaultCtor[] = { + "SBHostOS", + "SBReproducer", +}; + +static constexpr llvm::StringRef ClassesWithoutCopyOperations[] = { + "SBHostOS", + "SBReproducer", + "SBStream", + "SBProgress", +}; + +static constexpr llvm::StringRef MethodsWithPointerPlusLen[] = { + "_ZN4lldb6SBData11ReadRawDataERNS_7SBErrorEyPvm", + "_ZN4lldb6SBData7SetDataERNS_7SBErrorEPKvmNS_9ByteOrderEh", + "_ZN4lldb6SBData20SetDataWithOwnershipERNS_7SBErrorEPKvmNS_9ByteOrderEh", + "_ZN4lldb6SBData25CreateDataFromUInt64ArrayENS_9ByteOrderEjPym", + "_ZN4lldb6SBData25CreateDataFromUInt32ArrayENS_9ByteOrderEjPjm", + "_ZN4lldb6SBData25CreateDataFromSInt64ArrayENS_9ByteOrderEjPxm", + "_ZN4lldb6SBData25CreateDataFromSInt32ArrayENS_9ByteOrderEjPim", + "_ZN4lldb6SBData25CreateDataFromDoubleArrayENS_9ByteOrderEjPdm", + "_ZN4lldb6SBData22SetDataFromUInt64ArrayEPym", + "_ZN4lldb6SBData22SetDataFromUInt32ArrayEPjm", + "_ZN4lldb6SBData22SetDataFromSInt64ArrayEPxm", + "_ZN4lldb6SBData22SetDataFromSInt32ArrayEPim", + "_ZN4lldb6SBData22SetDataFromDoubleArrayEPdm", + "_ZN4lldb10SBDebugger22GetDefaultArchitectureEPcm", + "_ZN4lldb10SBDebugger13DispatchInputEPvPKvm", + "_ZN4lldb10SBDebugger13DispatchInputEPKvm", + "_ZN4lldb6SBFile4ReadEPhmPm", + "_ZN4lldb6SBFile5WriteEPKhmPm", + "_ZNK4lldb10SBFileSpec7GetPathEPcm", + "_ZN4lldb10SBFileSpec11ResolvePathEPKcPcm", + "_ZN4lldb8SBModule10GetVersionEPjj", + "_ZN4lldb12SBModuleSpec12SetUUIDBytesEPKhm", + "_ZNK4lldb9SBProcess9GetSTDOUTEPcm", + "_ZNK4lldb9SBProcess9GetSTDERREPcm", + "_ZNK4lldb9SBProcess19GetAsyncProfileDataEPcm", + "_ZN4lldb9SBProcess10ReadMemoryEyPvmRNS_7SBErrorE", + "_ZN4lldb9SBProcess11WriteMemoryEyPKvmRNS_7SBErrorE", + "_ZN4lldb9SBProcess21ReadCStringFromMemoryEyPvmRNS_7SBErrorE", + "_ZNK4lldb16SBStructuredData14GetStringValueEPcm", + "_ZN4lldb8SBTarget23BreakpointCreateByNamesEPPKcjjRKNS_" + "14SBFileSpecListES6_", + "_ZN4lldb8SBTarget10ReadMemoryENS_9SBAddressEPvmRNS_7SBErrorE", + "_ZN4lldb8SBTarget15GetInstructionsENS_9SBAddressEPKvm", + "_ZN4lldb8SBTarget25GetInstructionsWithFlavorENS_9SBAddressEPKcPKvm", + "_ZN4lldb8SBTarget15GetInstructionsEyPKvm", + "_ZN4lldb8SBTarget25GetInstructionsWithFlavorEyPKcPKvm", + "_ZN4lldb8SBThread18GetStopDescriptionEPcm", + // The below mangled names are used for dummy methods in shell tests + // that test the emitters' output. If you're adding any new mangled names + // from the actual SB API to this list please add them above. + "_ZN4lldb33SBRPC_" + "CHECKCONSTCHARPTRPTRWITHLEN27CheckConstCharPtrPtrWithLenEPPKcm", + "_ZN4lldb19SBRPC_CHECKARRAYPTR13CheckArrayPtrEPPKcm", + "_ZN4lldb18SBRPC_CHECKVOIDPTR12CheckVoidPtrEPvm", +}; + +// These methods should take a connection parameter according to our logic in +// RequiresConnectionParameter() but in the handwritten version they +// don't take a connection. These methods need to have their implementation +// changed but for now, we just have an exception list of functions that will +// never be a given connection parameter. +// +// FIXME: Change the implementation of these methods so that they can be given a +// connection parameter. +static constexpr llvm::StringRef + MethodsThatUnconditionallyDoNotNeedConnection[] = { ---------------- bulbazord wrote: It'd also be great if we could fix these up? These exceptions only exist for our internal clients. https://github.com/llvm/llvm-project/pull/138031 From lldb-commits at lists.llvm.org Tue May 6 10:53:19 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Tue, 06 May 2025 10:53:19 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream lldb-rpc-gen tool (PR #138031) In-Reply-To: Message-ID: <681a4c8f.a70a0220.39d81b.ba12@mx.google.com> https://github.com/bulbazord commented: Given that I wrote some of this code, it looks mostly good to me 😄 Note that this has a dependency on the emitters so this probably shouldn't land quite yet. I commented on some of the FIXMEs because I think now is the time to really address them. It would also be great to get a fresh pair of eyes on this... https://github.com/llvm/llvm-project/pull/138031 From lldb-commits at lists.llvm.org Tue May 6 10:53:19 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Tue, 06 May 2025 10:53:19 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream lldb-rpc-gen tool (PR #138031) In-Reply-To: Message-ID: <681a4c8f.050a0220.2166c1.d809@mx.google.com> ================ @@ -0,0 +1,535 @@ +//===-- 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/Mangle.h" +#include "clang/Lex/Lexer.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +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", // What is this used for? + "SBInputReader", // What is this used for? + "SBCommandPluginInterface", // This is hard to support, we can do it if + // really needed though. + "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. +}; + +// We intentionally avoid generating certain methods either because they are +// difficult to support correctly or they aren't really used much from C++. +// FIXME: We should be able to annotate these methods instead of maintaining a +// list in the generator itself. +static constexpr llvm::StringRef DisallowedMethods[] = { + // The threading functionality in SBHostOS is deprecated and thus we do not + // generate them. It would be ideal to add the annotations to the methods + // and then support not generating deprecated methods. However, without + // annotations the generator generates most things correctly. This one is + // problematic because it returns a pointer to an "opaque" structure + // (thread_t) that is not `void *`, so special casing it is more effort than + // it's worth. + "_ZN4lldb8SBHostOS10ThreadJoinEP17_opaque_pthread_tPPvPNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadCancelEP17_opaque_pthread_tPNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadCreateEPKcPFPvS3_ES3_PNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadDetachEP17_opaque_pthread_tPNS_7SBErrorE", + "_ZN4lldb8SBHostOS13ThreadCreatedEPKc", +}; + +static constexpr llvm::StringRef ClassesWithoutDefaultCtor[] = { + "SBHostOS", + "SBReproducer", +}; + +static constexpr llvm::StringRef ClassesWithoutCopyOperations[] = { + "SBHostOS", + "SBReproducer", + "SBStream", + "SBProgress", +}; + +static constexpr llvm::StringRef MethodsWithPointerPlusLen[] = { + "_ZN4lldb6SBData11ReadRawDataERNS_7SBErrorEyPvm", + "_ZN4lldb6SBData7SetDataERNS_7SBErrorEPKvmNS_9ByteOrderEh", + "_ZN4lldb6SBData20SetDataWithOwnershipERNS_7SBErrorEPKvmNS_9ByteOrderEh", + "_ZN4lldb6SBData25CreateDataFromUInt64ArrayENS_9ByteOrderEjPym", + "_ZN4lldb6SBData25CreateDataFromUInt32ArrayENS_9ByteOrderEjPjm", + "_ZN4lldb6SBData25CreateDataFromSInt64ArrayENS_9ByteOrderEjPxm", + "_ZN4lldb6SBData25CreateDataFromSInt32ArrayENS_9ByteOrderEjPim", + "_ZN4lldb6SBData25CreateDataFromDoubleArrayENS_9ByteOrderEjPdm", + "_ZN4lldb6SBData22SetDataFromUInt64ArrayEPym", + "_ZN4lldb6SBData22SetDataFromUInt32ArrayEPjm", + "_ZN4lldb6SBData22SetDataFromSInt64ArrayEPxm", + "_ZN4lldb6SBData22SetDataFromSInt32ArrayEPim", + "_ZN4lldb6SBData22SetDataFromDoubleArrayEPdm", + "_ZN4lldb10SBDebugger22GetDefaultArchitectureEPcm", + "_ZN4lldb10SBDebugger13DispatchInputEPvPKvm", + "_ZN4lldb10SBDebugger13DispatchInputEPKvm", + "_ZN4lldb6SBFile4ReadEPhmPm", + "_ZN4lldb6SBFile5WriteEPKhmPm", + "_ZNK4lldb10SBFileSpec7GetPathEPcm", + "_ZN4lldb10SBFileSpec11ResolvePathEPKcPcm", + "_ZN4lldb8SBModule10GetVersionEPjj", + "_ZN4lldb12SBModuleSpec12SetUUIDBytesEPKhm", + "_ZNK4lldb9SBProcess9GetSTDOUTEPcm", + "_ZNK4lldb9SBProcess9GetSTDERREPcm", + "_ZNK4lldb9SBProcess19GetAsyncProfileDataEPcm", + "_ZN4lldb9SBProcess10ReadMemoryEyPvmRNS_7SBErrorE", + "_ZN4lldb9SBProcess11WriteMemoryEyPKvmRNS_7SBErrorE", + "_ZN4lldb9SBProcess21ReadCStringFromMemoryEyPvmRNS_7SBErrorE", + "_ZNK4lldb16SBStructuredData14GetStringValueEPcm", + "_ZN4lldb8SBTarget23BreakpointCreateByNamesEPPKcjjRKNS_" + "14SBFileSpecListES6_", + "_ZN4lldb8SBTarget10ReadMemoryENS_9SBAddressEPvmRNS_7SBErrorE", + "_ZN4lldb8SBTarget15GetInstructionsENS_9SBAddressEPKvm", + "_ZN4lldb8SBTarget25GetInstructionsWithFlavorENS_9SBAddressEPKcPKvm", + "_ZN4lldb8SBTarget15GetInstructionsEyPKvm", + "_ZN4lldb8SBTarget25GetInstructionsWithFlavorEyPKcPKvm", + "_ZN4lldb8SBThread18GetStopDescriptionEPcm", + // The below mangled names are used for dummy methods in shell tests + // that test the emitters' output. If you're adding any new mangled names + // from the actual SB API to this list please add them above. + "_ZN4lldb33SBRPC_" + "CHECKCONSTCHARPTRPTRWITHLEN27CheckConstCharPtrPtrWithLenEPPKcm", + "_ZN4lldb19SBRPC_CHECKARRAYPTR13CheckArrayPtrEPPKcm", + "_ZN4lldb18SBRPC_CHECKVOIDPTR12CheckVoidPtrEPvm", +}; + +// These methods should take a connection parameter according to our logic in +// RequiresConnectionParameter() but in the handwritten version they +// don't take a connection. These methods need to have their implementation +// changed but for now, we just have an exception list of functions that will +// never be a given connection parameter. +// +// FIXME: Change the implementation of these methods so that they can be given a +// connection parameter. +static constexpr llvm::StringRef + MethodsThatUnconditionallyDoNotNeedConnection[] = { + "_ZN4lldb16SBBreakpointNameC1ERNS_8SBTargetEPKc", + "_ZN4lldb10SBDebugger7DestroyERS0_", + "_ZN4lldb18SBExecutionContextC1ENS_8SBThreadE", +}; + +// These classes inherit from rpc::ObjectRef directly (as opposed to +// rpc::LocalObjectRef). Changing them from ObjectRef to LocalObjectRef is ABI +// breaking, so we preserve that compatibility here. +// +// lldb-rpc-gen emits classes as LocalObjectRefs by default. +// +// FIXME: Does it matter which one it emits by default? +static constexpr llvm::StringRef ClassesThatInheritFromObjectRef[] = { ---------------- bulbazord wrote: I think something we should do while upstreaming is figure out what the differences between ObjectRef and LocalObjectRef really are and if they actually have any advantages for us. https://github.com/llvm/llvm-project/pull/138031 From lldb-commits at lists.llvm.org Tue May 6 10:53:19 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Tue, 06 May 2025 10:53:19 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream lldb-rpc-gen tool (PR #138031) In-Reply-To: Message-ID: <681a4c8f.170a0220.2f50af.0d84@mx.google.com> ================ @@ -0,0 +1,535 @@ +//===-- 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/Mangle.h" +#include "clang/Lex/Lexer.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +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", // What is this used for? + "SBInputReader", // What is this used for? + "SBCommandPluginInterface", // This is hard to support, we can do it if + // really needed though. + "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. +}; + +// We intentionally avoid generating certain methods either because they are +// difficult to support correctly or they aren't really used much from C++. +// FIXME: We should be able to annotate these methods instead of maintaining a +// list in the generator itself. +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", ---------------- bulbazord wrote: I wonder if we could take advantage of the `deprecated` annotation on these methods and remove this... 🤔 https://github.com/llvm/llvm-project/pull/138031 From lldb-commits at lists.llvm.org Tue May 6 10:53:19 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Tue, 06 May 2025 10:53:19 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream lldb-rpc-gen tool (PR #138031) In-Reply-To: Message-ID: <681a4c8f.050a0220.226b98.8d4c@mx.google.com> ================ @@ -0,0 +1,540 @@ +#include "RPCBindingsHarnessEmitter.h" ---------------- bulbazord wrote: This file will need a license header. https://github.com/llvm/llvm-project/pull/138031 From lldb-commits at lists.llvm.org Tue May 6 10:53:21 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Tue, 06 May 2025 10:53:21 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream lldb-rpc-gen tool (PR #138031) In-Reply-To: Message-ID: <681a4c91.050a0220.3520dc.9c77@mx.google.com> ================ @@ -0,0 +1,535 @@ +//===-- 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/Mangle.h" +#include "clang/Lex/Lexer.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +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", // What is this used for? + "SBInputReader", // What is this used for? + "SBCommandPluginInterface", // This is hard to support, we can do it if + // really needed though. + "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. +}; + +// We intentionally avoid generating certain methods either because they are +// difficult to support correctly or they aren't really used much from C++. +// FIXME: We should be able to annotate these methods instead of maintaining a +// list in the generator itself. +static constexpr llvm::StringRef DisallowedMethods[] = { + // The threading functionality in SBHostOS is deprecated and thus we do not + // generate them. It would be ideal to add the annotations to the methods + // and then support not generating deprecated methods. However, without + // annotations the generator generates most things correctly. This one is + // problematic because it returns a pointer to an "opaque" structure + // (thread_t) that is not `void *`, so special casing it is more effort than + // it's worth. + "_ZN4lldb8SBHostOS10ThreadJoinEP17_opaque_pthread_tPPvPNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadCancelEP17_opaque_pthread_tPNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadCreateEPKcPFPvS3_ES3_PNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadDetachEP17_opaque_pthread_tPNS_7SBErrorE", + "_ZN4lldb8SBHostOS13ThreadCreatedEPKc", +}; + +static constexpr llvm::StringRef ClassesWithoutDefaultCtor[] = { + "SBHostOS", + "SBReproducer", +}; + +static constexpr llvm::StringRef ClassesWithoutCopyOperations[] = { + "SBHostOS", + "SBReproducer", + "SBStream", + "SBProgress", +}; + +static constexpr llvm::StringRef MethodsWithPointerPlusLen[] = { ---------------- bulbazord wrote: I'd love if we could remove this list... https://github.com/llvm/llvm-project/pull/138031 From lldb-commits at lists.llvm.org Tue May 6 10:53:21 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Tue, 06 May 2025 10:53:21 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream lldb-rpc-gen tool (PR #138031) In-Reply-To: Message-ID: <681a4c91.050a0220.4a423.ce15@mx.google.com> ================ @@ -0,0 +1,535 @@ +//===-- 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/Mangle.h" +#include "clang/Lex/Lexer.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +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", // What is this used for? + "SBInputReader", // What is this used for? + "SBCommandPluginInterface", // This is hard to support, we can do it if + // really needed though. + "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. +}; + +// We intentionally avoid generating certain methods either because they are +// difficult to support correctly or they aren't really used much from C++. +// FIXME: We should be able to annotate these methods instead of maintaining a +// list in the generator itself. +static constexpr llvm::StringRef DisallowedMethods[] = { + // The threading functionality in SBHostOS is deprecated and thus we do not + // generate them. It would be ideal to add the annotations to the methods + // and then support not generating deprecated methods. However, without + // annotations the generator generates most things correctly. This one is + // problematic because it returns a pointer to an "opaque" structure + // (thread_t) that is not `void *`, so special casing it is more effort than + // it's worth. + "_ZN4lldb8SBHostOS10ThreadJoinEP17_opaque_pthread_tPPvPNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadCancelEP17_opaque_pthread_tPNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadCreateEPKcPFPvS3_ES3_PNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadDetachEP17_opaque_pthread_tPNS_7SBErrorE", + "_ZN4lldb8SBHostOS13ThreadCreatedEPKc", +}; + +static constexpr llvm::StringRef ClassesWithoutDefaultCtor[] = { + "SBHostOS", + "SBReproducer", +}; + +static constexpr llvm::StringRef ClassesWithoutCopyOperations[] = { + "SBHostOS", + "SBReproducer", + "SBStream", + "SBProgress", +}; + +static constexpr llvm::StringRef MethodsWithPointerPlusLen[] = { + "_ZN4lldb6SBData11ReadRawDataERNS_7SBErrorEyPvm", + "_ZN4lldb6SBData7SetDataERNS_7SBErrorEPKvmNS_9ByteOrderEh", + "_ZN4lldb6SBData20SetDataWithOwnershipERNS_7SBErrorEPKvmNS_9ByteOrderEh", + "_ZN4lldb6SBData25CreateDataFromUInt64ArrayENS_9ByteOrderEjPym", + "_ZN4lldb6SBData25CreateDataFromUInt32ArrayENS_9ByteOrderEjPjm", + "_ZN4lldb6SBData25CreateDataFromSInt64ArrayENS_9ByteOrderEjPxm", + "_ZN4lldb6SBData25CreateDataFromSInt32ArrayENS_9ByteOrderEjPim", + "_ZN4lldb6SBData25CreateDataFromDoubleArrayENS_9ByteOrderEjPdm", + "_ZN4lldb6SBData22SetDataFromUInt64ArrayEPym", + "_ZN4lldb6SBData22SetDataFromUInt32ArrayEPjm", + "_ZN4lldb6SBData22SetDataFromSInt64ArrayEPxm", + "_ZN4lldb6SBData22SetDataFromSInt32ArrayEPim", + "_ZN4lldb6SBData22SetDataFromDoubleArrayEPdm", + "_ZN4lldb10SBDebugger22GetDefaultArchitectureEPcm", + "_ZN4lldb10SBDebugger13DispatchInputEPvPKvm", + "_ZN4lldb10SBDebugger13DispatchInputEPKvm", + "_ZN4lldb6SBFile4ReadEPhmPm", + "_ZN4lldb6SBFile5WriteEPKhmPm", + "_ZNK4lldb10SBFileSpec7GetPathEPcm", + "_ZN4lldb10SBFileSpec11ResolvePathEPKcPcm", + "_ZN4lldb8SBModule10GetVersionEPjj", + "_ZN4lldb12SBModuleSpec12SetUUIDBytesEPKhm", + "_ZNK4lldb9SBProcess9GetSTDOUTEPcm", + "_ZNK4lldb9SBProcess9GetSTDERREPcm", + "_ZNK4lldb9SBProcess19GetAsyncProfileDataEPcm", + "_ZN4lldb9SBProcess10ReadMemoryEyPvmRNS_7SBErrorE", + "_ZN4lldb9SBProcess11WriteMemoryEyPKvmRNS_7SBErrorE", + "_ZN4lldb9SBProcess21ReadCStringFromMemoryEyPvmRNS_7SBErrorE", + "_ZNK4lldb16SBStructuredData14GetStringValueEPcm", + "_ZN4lldb8SBTarget23BreakpointCreateByNamesEPPKcjjRKNS_" + "14SBFileSpecListES6_", + "_ZN4lldb8SBTarget10ReadMemoryENS_9SBAddressEPvmRNS_7SBErrorE", + "_ZN4lldb8SBTarget15GetInstructionsENS_9SBAddressEPKvm", + "_ZN4lldb8SBTarget25GetInstructionsWithFlavorENS_9SBAddressEPKcPKvm", + "_ZN4lldb8SBTarget15GetInstructionsEyPKvm", + "_ZN4lldb8SBTarget25GetInstructionsWithFlavorEyPKcPKvm", + "_ZN4lldb8SBThread18GetStopDescriptionEPcm", + // The below mangled names are used for dummy methods in shell tests + // that test the emitters' output. If you're adding any new mangled names + // from the actual SB API to this list please add them above. + "_ZN4lldb33SBRPC_" + "CHECKCONSTCHARPTRPTRWITHLEN27CheckConstCharPtrPtrWithLenEPPKcm", + "_ZN4lldb19SBRPC_CHECKARRAYPTR13CheckArrayPtrEPPKcm", + "_ZN4lldb18SBRPC_CHECKVOIDPTR12CheckVoidPtrEPvm", +}; + +// These methods should take a connection parameter according to our logic in +// RequiresConnectionParameter() but in the handwritten version they +// don't take a connection. These methods need to have their implementation +// changed but for now, we just have an exception list of functions that will +// never be a given connection parameter. +// +// FIXME: Change the implementation of these methods so that they can be given a +// connection parameter. +static constexpr llvm::StringRef + MethodsThatUnconditionallyDoNotNeedConnection[] = { + "_ZN4lldb16SBBreakpointNameC1ERNS_8SBTargetEPKc", + "_ZN4lldb10SBDebugger7DestroyERS0_", + "_ZN4lldb18SBExecutionContextC1ENS_8SBThreadE", +}; + +// These classes inherit from rpc::ObjectRef directly (as opposed to +// rpc::LocalObjectRef). Changing them from ObjectRef to LocalObjectRef is ABI +// breaking, so we preserve that compatibility here. +// +// lldb-rpc-gen emits classes as LocalObjectRefs by default. +// +// FIXME: Does it matter which one it emits by default? +static constexpr llvm::StringRef ClassesThatInheritFromObjectRef[] = { + "SBAddress", + "SBBreakpointName", + "SBCommandInterpreter", + "SBCommandReturnObject", + "SBError", + "SBExecutionContext", + "SBExpressionOptions", + "SBFileSpec", + "SBFileSpecList", + "SBFormat", + "SBFunction", + "SBHistoricalFrame", + "SBHistoricalLineEntry", + "SBHistoricalLineEntryList", + "SBLineEntry", + "SBStream", + "SBStringList", + "SBStructuredData", + "SBSymbolContext", + "SBSymbolContextList", + "SBTypeMember", + "SBTypeSummaryOptions", + "SBValueList", +}; + +static llvm::StringMap> + ClassName_to_ParameterTypes = { + {"SBLaunchInfo", {"const char *"}}, + {"SBPlatformConnectOptions", {"const char *"}}, + {"SBPlatformShellCommand", {"const char *", "const char *"}}, + {"SBBreakpointList", {"SBTarget"}}, +}; + +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) { + QualType UnderlyingType = GetUnderlyingType(T); + return UnderlyingType.getUnqualifiedType(); +} + +std::string lldb_rpc_gen::GetMangledName(ASTContext &Context, + CXXMethodDecl *MDecl) { + std::string Mangled; + llvm::raw_string_ostream MangledStream(Mangled); + + GlobalDecl GDecl; + if (const auto *CtorDecl = dyn_cast(MDecl)) + GDecl = GlobalDecl(CtorDecl, Ctor_Complete); + else if (const auto *DtorDecl = dyn_cast(MDecl)) + GDecl = GlobalDecl(DtorDecl, Dtor_Deleting); + else + GDecl = GlobalDecl(MDecl); + + MangleContext *MC = Context.createMangleContext(); + MC->mangleName(GDecl, MangledStream); + return Mangled; +} + +bool lldb_rpc_gen::TypeIsFromLLDBPrivate(QualType T) { + + auto CheckTypeForLLDBPrivate = [](const Type *Ty) { + if (!Ty) + return false; + const auto *CXXRDecl = Ty->getAsCXXRecordDecl(); + if (!CXXRDecl) + return false; + const auto *NSDecl = + llvm::dyn_cast(CXXRDecl->getDeclContext()); + if (!NSDecl) + return false; + return NSDecl->getName() == "lldb_private"; + }; + + // First, get the underlying type (remove qualifications and strip off any + // pointers/references). Then we'll need to desugar this type. This will + // remove things like typedefs, so instead of seeing "lldb::DebuggerSP" we'll + // actually see something like "std::shared_ptr". + QualType UnqualifiedUnderlyingType = GetUnqualifiedUnderlyingType(T); + const Type *DesugaredType = + UnqualifiedUnderlyingType->getUnqualifiedDesugaredType(); + assert(DesugaredType && "DesugaredType from a valid Type is nullptr!"); + + // Check the type itself. + if (CheckTypeForLLDBPrivate(DesugaredType)) + return true; + + // If that didn't work, it's possible that the type has a template argument + // that is an lldb_private type. + if (const auto *TemplateSDecl = + llvm::dyn_cast_or_null( + DesugaredType->getAsCXXRecordDecl())) { + for (const TemplateArgument &TA : + TemplateSDecl->getTemplateArgs().asArray()) { + if (TA.getKind() != TemplateArgument::Type) + continue; + if (CheckTypeForLLDBPrivate(TA.getAsType().getTypePtr())) + return true; + } + } + return false; +} + +bool lldb_rpc_gen::TypeIsSBClass(QualType T) { + QualType UnqualifiedUnderlyingType = GetUnqualifiedUnderlyingType(T); + const auto *CXXRDecl = UnqualifiedUnderlyingType->getAsCXXRecordDecl(); + if (!CXXRDecl) + return false; // SB Classes are always C++ classes + + return CXXRDecl->getName().starts_with("SB"); +} + +bool lldb_rpc_gen::TypeIsConstCharPtr(QualType T) { + if (!T->isPointerType()) + return false; + + QualType UnderlyingType = T->getPointeeType(); + if (!UnderlyingType.isConstQualified()) + return false; + + // FIXME: We should be able to do `UnderlyingType->isCharType` but that will + // return true for `const uint8_t *` since that is effectively an unsigned + // char pointer. We currently do not support pointers other than `const char + // *` and `const char **`. + return UnderlyingType->isSpecificBuiltinType(BuiltinType::Char_S) || + UnderlyingType->isSpecificBuiltinType(BuiltinType::SChar); +} + +bool lldb_rpc_gen::TypeIsConstCharPtrPtr(QualType T) { + if (!T->isPointerType()) + return false; + + return TypeIsConstCharPtr(T->getPointeeType()); +} + +bool lldb_rpc_gen::TypeIsDisallowedClass(QualType T) { + QualType UUT = GetUnqualifiedUnderlyingType(T); + const auto *CXXRDecl = UUT->getAsCXXRecordDecl(); + if (!CXXRDecl) + return false; + + llvm::StringRef DeclName = CXXRDecl->getName(); + for (const llvm::StringRef DisallowedClass : DisallowedClasses) + if (DeclName == DisallowedClass) + return true; + return false; +} + +bool lldb_rpc_gen::TypeIsCallbackFunctionPointer(QualType T) { + return T->isFunctionPointerType(); +} + +bool lldb_rpc_gen::MethodIsDisallowed(const std::string &MangledName) { + llvm::StringRef MangledNameRef(MangledName); + return llvm::is_contained(DisallowedMethods, MangledNameRef); +} + +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; + continue; + } + + if ((*Iter)->getType()->isVoidPointerType()) + HasBatonParameter = true; + } + + return HasCallbackParameter && HasBatonParameter; +} + +// FIXME: Find a better way to do this. Here is why it is written this way: ---------------- bulbazord wrote: We should look into this FIXME now that we're upstreaming this. Maybe somebody more familiar with clang tooling can help us. https://github.com/llvm/llvm-project/pull/138031 From lldb-commits at lists.llvm.org Tue May 6 10:53:21 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Tue, 06 May 2025 10:53:21 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream lldb-rpc-gen tool (PR #138031) In-Reply-To: Message-ID: <681a4c91.170a0220.26a001.7b0d@mx.google.com> ================ @@ -0,0 +1,535 @@ +//===-- 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/Mangle.h" +#include "clang/Lex/Lexer.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +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", // What is this used for? + "SBInputReader", // What is this used for? + "SBCommandPluginInterface", // This is hard to support, we can do it if + // really needed though. + "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. +}; + +// We intentionally avoid generating certain methods either because they are +// difficult to support correctly or they aren't really used much from C++. +// FIXME: We should be able to annotate these methods instead of maintaining a +// list in the generator itself. +static constexpr llvm::StringRef DisallowedMethods[] = { + // The threading functionality in SBHostOS is deprecated and thus we do not + // generate them. It would be ideal to add the annotations to the methods + // and then support not generating deprecated methods. However, without + // annotations the generator generates most things correctly. This one is + // problematic because it returns a pointer to an "opaque" structure + // (thread_t) that is not `void *`, so special casing it is more effort than + // it's worth. + "_ZN4lldb8SBHostOS10ThreadJoinEP17_opaque_pthread_tPPvPNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadCancelEP17_opaque_pthread_tPNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadCreateEPKcPFPvS3_ES3_PNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadDetachEP17_opaque_pthread_tPNS_7SBErrorE", + "_ZN4lldb8SBHostOS13ThreadCreatedEPKc", +}; + +static constexpr llvm::StringRef ClassesWithoutDefaultCtor[] = { + "SBHostOS", + "SBReproducer", +}; + +static constexpr llvm::StringRef ClassesWithoutCopyOperations[] = { + "SBHostOS", + "SBReproducer", + "SBStream", + "SBProgress", +}; + +static constexpr llvm::StringRef MethodsWithPointerPlusLen[] = { + "_ZN4lldb6SBData11ReadRawDataERNS_7SBErrorEyPvm", + "_ZN4lldb6SBData7SetDataERNS_7SBErrorEPKvmNS_9ByteOrderEh", + "_ZN4lldb6SBData20SetDataWithOwnershipERNS_7SBErrorEPKvmNS_9ByteOrderEh", + "_ZN4lldb6SBData25CreateDataFromUInt64ArrayENS_9ByteOrderEjPym", + "_ZN4lldb6SBData25CreateDataFromUInt32ArrayENS_9ByteOrderEjPjm", + "_ZN4lldb6SBData25CreateDataFromSInt64ArrayENS_9ByteOrderEjPxm", + "_ZN4lldb6SBData25CreateDataFromSInt32ArrayENS_9ByteOrderEjPim", + "_ZN4lldb6SBData25CreateDataFromDoubleArrayENS_9ByteOrderEjPdm", + "_ZN4lldb6SBData22SetDataFromUInt64ArrayEPym", + "_ZN4lldb6SBData22SetDataFromUInt32ArrayEPjm", + "_ZN4lldb6SBData22SetDataFromSInt64ArrayEPxm", + "_ZN4lldb6SBData22SetDataFromSInt32ArrayEPim", + "_ZN4lldb6SBData22SetDataFromDoubleArrayEPdm", + "_ZN4lldb10SBDebugger22GetDefaultArchitectureEPcm", + "_ZN4lldb10SBDebugger13DispatchInputEPvPKvm", + "_ZN4lldb10SBDebugger13DispatchInputEPKvm", + "_ZN4lldb6SBFile4ReadEPhmPm", + "_ZN4lldb6SBFile5WriteEPKhmPm", + "_ZNK4lldb10SBFileSpec7GetPathEPcm", + "_ZN4lldb10SBFileSpec11ResolvePathEPKcPcm", + "_ZN4lldb8SBModule10GetVersionEPjj", + "_ZN4lldb12SBModuleSpec12SetUUIDBytesEPKhm", + "_ZNK4lldb9SBProcess9GetSTDOUTEPcm", + "_ZNK4lldb9SBProcess9GetSTDERREPcm", + "_ZNK4lldb9SBProcess19GetAsyncProfileDataEPcm", + "_ZN4lldb9SBProcess10ReadMemoryEyPvmRNS_7SBErrorE", + "_ZN4lldb9SBProcess11WriteMemoryEyPKvmRNS_7SBErrorE", + "_ZN4lldb9SBProcess21ReadCStringFromMemoryEyPvmRNS_7SBErrorE", + "_ZNK4lldb16SBStructuredData14GetStringValueEPcm", + "_ZN4lldb8SBTarget23BreakpointCreateByNamesEPPKcjjRKNS_" + "14SBFileSpecListES6_", + "_ZN4lldb8SBTarget10ReadMemoryENS_9SBAddressEPvmRNS_7SBErrorE", + "_ZN4lldb8SBTarget15GetInstructionsENS_9SBAddressEPKvm", + "_ZN4lldb8SBTarget25GetInstructionsWithFlavorENS_9SBAddressEPKcPKvm", + "_ZN4lldb8SBTarget15GetInstructionsEyPKvm", + "_ZN4lldb8SBTarget25GetInstructionsWithFlavorEyPKcPKvm", + "_ZN4lldb8SBThread18GetStopDescriptionEPcm", + // The below mangled names are used for dummy methods in shell tests + // that test the emitters' output. If you're adding any new mangled names + // from the actual SB API to this list please add them above. + "_ZN4lldb33SBRPC_" + "CHECKCONSTCHARPTRPTRWITHLEN27CheckConstCharPtrPtrWithLenEPPKcm", + "_ZN4lldb19SBRPC_CHECKARRAYPTR13CheckArrayPtrEPPKcm", + "_ZN4lldb18SBRPC_CHECKVOIDPTR12CheckVoidPtrEPvm", +}; + +// These methods should take a connection parameter according to our logic in +// RequiresConnectionParameter() but in the handwritten version they +// don't take a connection. These methods need to have their implementation +// changed but for now, we just have an exception list of functions that will +// never be a given connection parameter. +// +// FIXME: Change the implementation of these methods so that they can be given a +// connection parameter. +static constexpr llvm::StringRef + MethodsThatUnconditionallyDoNotNeedConnection[] = { + "_ZN4lldb16SBBreakpointNameC1ERNS_8SBTargetEPKc", + "_ZN4lldb10SBDebugger7DestroyERS0_", + "_ZN4lldb18SBExecutionContextC1ENS_8SBThreadE", +}; + +// These classes inherit from rpc::ObjectRef directly (as opposed to +// rpc::LocalObjectRef). Changing them from ObjectRef to LocalObjectRef is ABI +// breaking, so we preserve that compatibility here. +// +// lldb-rpc-gen emits classes as LocalObjectRefs by default. +// +// FIXME: Does it matter which one it emits by default? +static constexpr llvm::StringRef ClassesThatInheritFromObjectRef[] = { + "SBAddress", + "SBBreakpointName", + "SBCommandInterpreter", + "SBCommandReturnObject", + "SBError", + "SBExecutionContext", + "SBExpressionOptions", + "SBFileSpec", + "SBFileSpecList", + "SBFormat", + "SBFunction", + "SBHistoricalFrame", + "SBHistoricalLineEntry", + "SBHistoricalLineEntryList", + "SBLineEntry", + "SBStream", + "SBStringList", + "SBStructuredData", + "SBSymbolContext", + "SBSymbolContextList", + "SBTypeMember", + "SBTypeSummaryOptions", + "SBValueList", +}; + +static llvm::StringMap> + ClassName_to_ParameterTypes = { ---------------- bulbazord wrote: I don't actually remember what this is for anymore and the name isn't super descriptive... Could we get this a better name? https://github.com/llvm/llvm-project/pull/138031 From lldb-commits at lists.llvm.org Tue May 6 10:54:23 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Tue, 06 May 2025 10:54:23 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][docs] Update instructions to build standalone (PR #137383) In-Reply-To: Message-ID: <681a4ccf.050a0220.2bc3a6.c572@mx.google.com> https://github.com/bulbazord approved this pull request. https://github.com/llvm/llvm-project/pull/137383 From lldb-commits at lists.llvm.org Tue May 6 11:06:30 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Tue, 06 May 2025 11:06:30 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream Python scripts (PR #138028) In-Reply-To: Message-ID: <681a4fa6.170a0220.3717f5.252d@mx.google.com> ================ @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +# Usage: framework-header-version-fix.py MAJOR MINOR PATCH +# This script modifies lldb-rpc-defines.h to uncomment the macro defines used for the LLDB +# major, minor and patch values as well as populating their definitions. + +import argparse +import os +import re + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("input") + parser.add_argument("output") + parser.add_argument("lldb_version_major") + parser.add_argument("lldb_version_minor") + parser.add_argument("lldb_version_patch") + args = parser.parse_args() + input_path = str(args.input) + output_path = str(args.output) + lldb_version_major = args.lldb_version_major + lldb_version_minor = args.lldb_version_minor + lldb_version_patch = args.lldb_version_patch + + with open(input_path, "r") as input_file: + lines = input_file.readlines() + + with open(output_path, "w") as output_file: + for line in lines: + # Uncomment the line that defines the LLDB major version and populate its value. + if re.match(r"//#define LLDB_RPC_VERSION$", line): ---------------- chelcassanova wrote: I used `$` to make sure that the match here for `LLDB_RPC_VERSION` can't have something past the `VERSION` so I think it's possible to try using `//#define LLDB_RPC_VERSION$` as the sub match here. Either that or my regex knowledge is out of whack. https://github.com/llvm/llvm-project/pull/138028 From lldb-commits at lists.llvm.org Tue May 6 11:06:40 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Tue, 06 May 2025 11:06:40 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream Python scripts (PR #138028) In-Reply-To: Message-ID: <681a4fb0.050a0220.221863.dcb3@mx.google.com> ================ @@ -0,0 +1,18 @@ +// Copy lldb-rpc-defines.h from source. +# RUN: mkdir -p %t/input +# RUN: mkdir -p %t/output +# RUN: cp %p/../../../../../include/lldb/lldb-defines.h %t/input + +// Run the convert script on it, then run the framework include fix on it. The framework version fix script +// expects that all lldb references have been renamed to lldb-rpc in order for it to modify the includes +// to go into the framework. +# RUN: %python %p/../../../../../scripts/convert-lldb-header-to-rpc-header.py %t/input/lldb-defines.h %t/output/lldb-rpc-defines.h +# RUN: %python %p/../../../../../scripts/framework-header-version-fix.py %t/output/lldb-rpc-defines.h %t/output/lldb-rpc-defines.h 17 0 0 + +// Check the output +# RUN: cat %t/output/lldb-rpc-defines.h | FileCheck %s + +// The LLDB version defines must be uncommented and filled in with the values passed into the script. +# CHECK: #define LLDB_RPC_VERSION 17 +# CHECK: #define LLDB_RPC_REVISION 0 +# CHECK: #define LLDB_RPC_VERSION_STRING "17.0.0" ---------------- chelcassanova wrote: I'll try this 👍🏾 https://github.com/llvm/llvm-project/pull/138028 From lldb-commits at lists.llvm.org Tue May 6 11:20:47 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Tue, 06 May 2025 11:20:47 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream RPC server interface emitters (PR #138032) In-Reply-To: Message-ID: <681a52ff.050a0220.abbe0.a662@mx.google.com> ================ @@ -0,0 +1,592 @@ +//===-- RPCServerSourceEmitter.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 "RPCServerSourceEmitter.h" +#include "RPCCommon.h" + +#include "clang/AST/AST.h" +#include "clang/Frontend/CompilerInstance.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace clang; +using namespace lldb_rpc_gen; + +// For methods with pointer return types, it's important that we know how big +// the type of the pointee is. We must correctly size a buffer (in the form of a +// Bytes object) before we can actually use it. +static const std::map MethodsWithPointerReturnTypes = { + {"_ZN4lldb12SBModuleSpec12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 + {"_ZNK4lldb8SBModule12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 +}; + +void RPCServerSourceEmitter::EmitMethod(const Method &method) { + if (method.ContainsFunctionPointerParameter) + EmitCallbackFunction(method); + + EmitCommentHeader(method); + EmitFunctionHeader(method); + EmitFunctionBody(method); + EmitFunctionFooter(); +} + +void RPCServerSourceEmitter::EmitCommentHeader(const Method &method) { + std::string CommentLine; + llvm::raw_string_ostream CommentStream(CommentLine); + + CommentStream << "// " << method.QualifiedName << "(" + << method.CreateParamListAsString(eServer) << ")"; + if (method.IsConst) + CommentStream << " const"; + + EmitLine("//------------------------------------------------------------"); + EmitLine(CommentLine); + EmitLine("//------------------------------------------------------------"); +} + +void RPCServerSourceEmitter::EmitFunctionHeader(const Method &method) { + std::string FunctionHeader; + llvm::raw_string_ostream FunctionHeaderStream(FunctionHeader); + FunctionHeaderStream + << "bool rpc_server::" << method.MangledName + << "::HandleRPCCall(rpc_common::Connection &connection, RPCStream " + "&send, RPCStream &response) {"; + EmitLine(FunctionHeader); + IndentLevel++; +} + +void RPCServerSourceEmitter::EmitFunctionBody(const Method &method) { + EmitLine("// 1) Make local storage for incoming function arguments"); + EmitStorageForParameters(method); + EmitLine("// 2) Decode all function arguments"); + EmitDecodeForParameters(method); + EmitLine("// 3) Call the method and encode the return value"); + EmitMethodCallAndEncode(method); +} + +void RPCServerSourceEmitter::EmitFunctionFooter() { + EmitLine("return true;"); + IndentLevel--; + EmitLine("}"); +} + +void RPCServerSourceEmitter::EmitStorageForParameters(const Method &method) { + // If we have an instance method and it isn't a constructor, we'll need to + // emit a "this" pointer. + if (method.IsInstance && !method.IsCtor) + EmitStorageForOneParameter(method.ThisType, "this_ptr", method.Policy, + /* IsFollowedByLen = */ false); + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + EmitStorageForOneParameter(Iter->Type, Iter->Name, method.Policy, + Iter->IsFollowedByLen); + // Skip over the length parameter, we don't emit it. + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) + Iter++; + } +} + +void RPCServerSourceEmitter::EmitStorageForOneParameter( + QualType ParamType, const std::string &ParamName, + const PrintingPolicy &Policy, bool IsFollowedByLen) { + // First, we consider `const char *`, `const char **`. They have special + // server-side types. + if (TypeIsConstCharPtr(ParamType)) { + EmitLine("rpc_common::ConstCharPointer " + ParamName + ";"); ---------------- bulbazord wrote: `rpc_common::ConstCharPointer` is a class with a default constructor. The generated code would look like: ``` rpc_common::ConstCharPointer foo; ``` https://github.com/llvm/llvm-project/pull/138032 From lldb-commits at lists.llvm.org Tue May 6 11:20:47 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Tue, 06 May 2025 11:20:47 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream RPC server interface emitters (PR #138032) In-Reply-To: Message-ID: <681a52ff.630a0220.3d316a.3fb2@mx.google.com> ================ @@ -0,0 +1,592 @@ +//===-- RPCServerSourceEmitter.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 "RPCServerSourceEmitter.h" +#include "RPCCommon.h" + +#include "clang/AST/AST.h" +#include "clang/Frontend/CompilerInstance.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace clang; +using namespace lldb_rpc_gen; + +// For methods with pointer return types, it's important that we know how big +// the type of the pointee is. We must correctly size a buffer (in the form of a +// Bytes object) before we can actually use it. +static const std::map MethodsWithPointerReturnTypes = { ---------------- bulbazord wrote: These methods are difficult for clang to reason about because their return type is `const uint8_t *`. https://github.com/llvm/llvm-project/pull/138032 From lldb-commits at lists.llvm.org Tue May 6 11:20:47 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Tue, 06 May 2025 11:20:47 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream RPC server interface emitters (PR #138032) In-Reply-To: Message-ID: <681a52ff.170a0220.73a0b.2de7@mx.google.com> ================ @@ -0,0 +1,592 @@ +//===-- RPCServerSourceEmitter.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 "RPCServerSourceEmitter.h" +#include "RPCCommon.h" + +#include "clang/AST/AST.h" +#include "clang/Frontend/CompilerInstance.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace clang; +using namespace lldb_rpc_gen; + +// For methods with pointer return types, it's important that we know how big +// the type of the pointee is. We must correctly size a buffer (in the form of a +// Bytes object) before we can actually use it. +static const std::map MethodsWithPointerReturnTypes = { + {"_ZN4lldb12SBModuleSpec12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 + {"_ZNK4lldb8SBModule12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 +}; + +void RPCServerSourceEmitter::EmitMethod(const Method &method) { + if (method.ContainsFunctionPointerParameter) + EmitCallbackFunction(method); + + EmitCommentHeader(method); + EmitFunctionHeader(method); + EmitFunctionBody(method); + EmitFunctionFooter(); +} + +void RPCServerSourceEmitter::EmitCommentHeader(const Method &method) { + std::string CommentLine; + llvm::raw_string_ostream CommentStream(CommentLine); + + CommentStream << "// " << method.QualifiedName << "(" + << method.CreateParamListAsString(eServer) << ")"; + if (method.IsConst) + CommentStream << " const"; + + EmitLine("//------------------------------------------------------------"); + EmitLine(CommentLine); + EmitLine("//------------------------------------------------------------"); +} + +void RPCServerSourceEmitter::EmitFunctionHeader(const Method &method) { + std::string FunctionHeader; + llvm::raw_string_ostream FunctionHeaderStream(FunctionHeader); + FunctionHeaderStream + << "bool rpc_server::" << method.MangledName + << "::HandleRPCCall(rpc_common::Connection &connection, RPCStream " + "&send, RPCStream &response) {"; + EmitLine(FunctionHeader); + IndentLevel++; +} + +void RPCServerSourceEmitter::EmitFunctionBody(const Method &method) { + EmitLine("// 1) Make local storage for incoming function arguments"); + EmitStorageForParameters(method); + EmitLine("// 2) Decode all function arguments"); + EmitDecodeForParameters(method); + EmitLine("// 3) Call the method and encode the return value"); + EmitMethodCallAndEncode(method); +} + +void RPCServerSourceEmitter::EmitFunctionFooter() { + EmitLine("return true;"); + IndentLevel--; + EmitLine("}"); +} + +void RPCServerSourceEmitter::EmitStorageForParameters(const Method &method) { + // If we have an instance method and it isn't a constructor, we'll need to + // emit a "this" pointer. + if (method.IsInstance && !method.IsCtor) + EmitStorageForOneParameter(method.ThisType, "this_ptr", method.Policy, + /* IsFollowedByLen = */ false); + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + EmitStorageForOneParameter(Iter->Type, Iter->Name, method.Policy, + Iter->IsFollowedByLen); + // Skip over the length parameter, we don't emit it. + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) + Iter++; + } +} + +void RPCServerSourceEmitter::EmitStorageForOneParameter( + QualType ParamType, const std::string &ParamName, + const PrintingPolicy &Policy, bool IsFollowedByLen) { + // First, we consider `const char *`, `const char **`. They have special + // server-side types. + if (TypeIsConstCharPtr(ParamType)) { + EmitLine("rpc_common::ConstCharPointer " + ParamName + ";"); + return; + } else if (TypeIsConstCharPtrPtr(ParamType)) { + EmitLine("rpc_common::StringList " + ParamName + ";"); + return; + } + + QualType UnderlyingType = + lldb_rpc_gen::GetUnqualifiedUnderlyingType(ParamType); + const bool IsSBClass = lldb_rpc_gen::TypeIsSBClass(UnderlyingType); + + if (ParamType->isPointerType() && !IsSBClass) { + // Void pointer with no length is usually a baton for a callback. We're + // going to hold onto the pointer value so we can send it back to the + // client-side when we implement callbacks. + if (ParamType->isVoidPointerType() && !IsFollowedByLen) { + EmitLine("void * " + ParamName + " = nullptr;"); + return; + } + + if (!ParamType->isFunctionPointerType()) { + EmitLine("Bytes " + ParamName + ";"); + return; + } + + assert(ParamType->isFunctionPointerType() && "Unhandled pointer type"); + EmitLine("rpc_common::function_ptr_t " + ParamName + " = nullptr;"); + return; + } + + std::string StorageDeclaration; + llvm::raw_string_ostream StorageDeclarationStream(StorageDeclaration); + + UnderlyingType.print(StorageDeclarationStream, Policy); + StorageDeclarationStream << " "; + if (IsSBClass) + StorageDeclarationStream << "*"; + StorageDeclarationStream << ParamName; + if (IsSBClass) + StorageDeclarationStream << " = nullptr"; + else + StorageDeclarationStream << " = {}"; + StorageDeclarationStream << ";"; + EmitLine(StorageDeclaration); +} + +void RPCServerSourceEmitter::EmitDecodeForParameters(const Method &method) { + if (method.IsInstance && !method.IsCtor) + EmitDecodeForOneParameter(method.ThisType, "this_ptr", method.Policy); + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + EmitDecodeForOneParameter(Iter->Type, Iter->Name, method.Policy); + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) + Iter++; + } +} + +void RPCServerSourceEmitter::EmitDecodeForOneParameter( + QualType ParamType, const std::string &ParamName, + const PrintingPolicy &Policy) { + QualType UnderlyingType = + lldb_rpc_gen::GetUnqualifiedUnderlyingType(ParamType); + + if (TypeIsSBClass(UnderlyingType)) { + std::string DecodeLine; + llvm::raw_string_ostream DecodeLineStream(DecodeLine); + DecodeLineStream << ParamName << " = " + << "RPCServerObjectDecoder<"; + UnderlyingType.print(DecodeLineStream, Policy); + DecodeLineStream << ">(send, rpc_common::RPCPacket::ValueType::Argument);"; + EmitLine(DecodeLine); + EmitLine("if (!" + ParamName + ")"); + IndentLevel++; + EmitLine("return false;"); + IndentLevel--; + } else { + EmitLine("if (!RPCValueDecoder(send, " + "rpc_common::RPCPacket::ValueType::Argument, " + + ParamName + "))"); + IndentLevel++; + EmitLine("return false;"); + IndentLevel--; + } +} + +std::string RPCServerSourceEmitter::CreateMethodCall(const Method &method) { + std::string MethodCall; + llvm::raw_string_ostream MethodCallStream(MethodCall); + if (method.IsInstance) { + if (!method.IsCtor) + MethodCallStream << "this_ptr->"; + MethodCallStream << method.BaseName; + } else + MethodCallStream << method.QualifiedName; + + std::vector Args; + std::string FunctionPointerName; + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + std::string Arg; + // We must check for `const char *` and `const char **` first. + if (TypeIsConstCharPtr(Iter->Type)) { + // `const char *` is stored server-side as rpc_common::ConstCharPointer + Arg = Iter->Name + ".c_str()"; + } else if (TypeIsConstCharPtrPtr(Iter->Type)) { + // `const char **` is stored server-side as rpc_common::StringList + Arg = Iter->Name + ".argv()"; + } else if (lldb_rpc_gen::TypeIsSBClass(Iter->Type)) { + Arg = Iter->Name; + if (!Iter->Type->isPointerType()) + Arg = "*" + Iter->Name; + } else if (Iter->Type->isPointerType() && + !Iter->Type->isFunctionPointerType() && + (!Iter->Type->isVoidPointerType() || Iter->IsFollowedByLen)) { + // We move pointers between the server and client as 'Bytes' objects. + // Pointers with length arguments will have their length filled in below. + // Pointers with no length arguments are assumed to behave like an array + // with length of 1, except for void pointers which are handled + // differently. + Arg = "(" + Iter->Type.getAsString(method.Policy) + ")" + Iter->Name + + ".GetData()"; + } else if (Iter->Type->isFunctionPointerType()) { + // If we have a function pointer, we only want to pass something along if + // we got a real pointer. + Arg = Iter->Name + " ? " + method.MangledName + "_callback : nullptr"; + FunctionPointerName = Iter->Name; + } else if (Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen && + method.ContainsFunctionPointerParameter) { + // Assumptions: + // - This is assumed to be the baton for the function pointer. + // - This is assumed to come after the function pointer parameter. + // We always produce this regardless of the value of the baton argument. + Arg = "new CallbackInfo(" + FunctionPointerName + ", " + Iter->Name + + ", connection.GetConnectionID())"; + } else + Arg = Iter->Name; + + if (Iter->Type->isRValueReferenceType()) + Arg = "std::move(" + Arg + ")"; + Args.push_back(Arg); + + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) { + std::string LengthArg = Iter->Name + ".GetSize()"; + if (!Iter->Type->isVoidPointerType()) { + QualType UUT = lldb_rpc_gen::GetUnqualifiedUnderlyingType(Iter->Type); + LengthArg += " / sizeof(" + UUT.getAsString(method.Policy) + ")"; + } + Args.push_back(LengthArg); + Iter++; + } + } + MethodCallStream << "(" << llvm::join(Args, ", ") << ")"; + + return MethodCall; +} + +std::string RPCServerSourceEmitter::CreateEncodeLine(const std::string &Value, + bool IsEncodingSBClass) { + std::string EncodeLine; + llvm::raw_string_ostream EncodeLineStream(EncodeLine); + + if (IsEncodingSBClass) + EncodeLineStream << "RPCServerObjectEncoder("; + else + EncodeLineStream << "RPCValueEncoder("; + + EncodeLineStream + << "response, rpc_common::RPCPacket::ValueType::ReturnValue, "; + EncodeLineStream << Value; + EncodeLineStream << ");"; + return EncodeLine; +} + +// There are 4 cases to consider: +// - const SBClass &: No need to do anything. +// - const foo &: No need to do anything. +// - SBClass &: The server and the client hold on to IDs to refer to specific +// instances, so there's no need to send any information back to the client. +// - foo &: The client is sending us a value over the wire, but because the type +// is mutable, we must send the changed value back in case the method call +// mutated it. +// +// Updating a mutable reference is done as a return value from the RPC +// perspective. These return values need to be emitted after the method's return +// value, and they are emitted in the order in which they occur in the +// declaration. +void RPCServerSourceEmitter::EmitEncodesForMutableParameters( + const std::vector &Params) { + for (auto Iter = Params.begin(); Iter != Params.end(); Iter++) { + // No need to manually update an SBClass + if (lldb_rpc_gen::TypeIsSBClass(Iter->Type)) + continue; + + if (!Iter->Type->isReferenceType() && !Iter->Type->isPointerType()) + continue; + + // If we have a void pointer with no length, there's nothing to update. This + // is likely a baton for a callback. The same goes for function pointers. + if (Iter->Type->isFunctionPointerType() || + (Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen)) + continue; + + // No need to update pointers and references to const-qualified data. + QualType UnderlyingType = lldb_rpc_gen::GetUnderlyingType(Iter->Type); + if (UnderlyingType.isConstQualified()) + continue; + + const std::string EncodeLine = + CreateEncodeLine(Iter->Name, /* IsEncodingSBClass = */ false); + EmitLine(EncodeLine); + } +} + +// There are 3 possible scenarios that this method can encounter: +// 1. The method has no return value and is not a constructor. +// Only the method call itself is emitted. +// 2. The method is a constructor. +// The call to the constructor is emitted in the encode line. +// 3. The method has a return value. +// The method call is emitted and the return value is captured in a variable. +// After that, an encode call is emitted with the variable that captured the +// return value. +void RPCServerSourceEmitter::EmitMethodCallAndEncode(const Method &method) { + // FIXME: The hand-written lldb-rpc-server currently doesn't emit destructors + // for LocalObjectRefs... even if the type is an ObjectRef. What should we do + // here? + + const std::string MethodCall = CreateMethodCall(method); + + // If this function returns nothing, we just emit the call and update any + // mutable references. Note that constructors have return type `void` so we + // must explicitly check for that here. + if (!method.IsCtor && method.ReturnType->isVoidType()) { + EmitLine(MethodCall + ";"); + EmitEncodesForMutableParameters(method.Params); + return; + } + + static constexpr llvm::StringLiteral ReturnVariableName("__result"); + + // If this isn't a constructor, we'll need to store the result of the method + // call in a result variable. + if (!method.IsCtor) { + // We need to determine what the appropriate return type is. Here is the + // strategy: + // 1.) `SBFoo` -> `SBFoo &&` + // 2.) If the type is a pointer other than `const char *` or `const char **` + // or `void *`, the return type will be `Bytes` (e.g. `const uint8_t *` + // -> `Bytes`). + // 3.) Otherwise, emit the exact same return type. + std::string ReturnTypeName; + std::string AssignLine; + llvm::raw_string_ostream AssignLineStream(AssignLine); + if (method.ReturnType->isPointerType() && + !lldb_rpc_gen::TypeIsConstCharPtr(method.ReturnType) && + !lldb_rpc_gen::TypeIsConstCharPtrPtr(method.ReturnType) && + !method.ReturnType->isVoidPointerType()) { + llvm::StringRef MangledNameRef(method.MangledName); + auto Pos = MethodsWithPointerReturnTypes.find(MangledNameRef); + assert(Pos != MethodsWithPointerReturnTypes.end() && + "Unable to determine the size of the return buffer"); + if (Pos == MethodsWithPointerReturnTypes.end()) { + EmitLine( + "// Intentionally inserting a compiler error. lldb-rpc-gen " + "was unable to determine how large the return buffer should be."); + EmitLine("ThisShouldNotCompile"); + return; + } + AssignLineStream << "Bytes " << ReturnVariableName << "(" << MethodCall + << ", " << Pos->second << ");"; + } else { + if (lldb_rpc_gen::TypeIsSBClass(method.ReturnType)) { + // We want to preserve constness, so we don't strip qualifications from + // the underlying type + QualType UnderlyingReturnType = + lldb_rpc_gen::GetUnderlyingType(method.ReturnType); + ReturnTypeName = + UnderlyingReturnType.getAsString(method.Policy) + " &&"; + } else + ReturnTypeName = method.ReturnType.getAsString(method.Policy); + + AssignLineStream << ReturnTypeName << " " << ReturnVariableName << " = " + << MethodCall << ";"; + } + EmitLine(AssignLine); + } + + const bool IsEncodingSBClass = + lldb_rpc_gen::TypeIsSBClass(method.ReturnType) || method.IsCtor; + + std::string ValueToEncode; + if (IsEncodingSBClass) { + if (method.IsCtor) + ValueToEncode = MethodCall; + else + ValueToEncode = "std::move(" + ReturnVariableName.str() + ")"; + } else + ValueToEncode = ReturnVariableName.str(); + + const std::string ReturnValueEncodeLine = + CreateEncodeLine(ValueToEncode, IsEncodingSBClass); + EmitLine(ReturnValueEncodeLine); + EmitEncodesForMutableParameters(method.Params); +} + +// NOTE: This contains most of the same knowledge as RPCLibrarySourceEmitter. I +// have chosen not to re-use code here because the needs are different enough +// that it would be more work to re-use than just reimplement portions of it. +// Specifically: +// - Callbacks do not neatly fit into a `Method` object, which currently +// assumes that you have a CXXMethodDecl (We have a FunctionDecl at most). +// - We only generate callbacks that have a `void *` baton parameter. We hijack +// those baton parameters and treat them differently. +// - Callbacks need to do something special for moving SB class references back +// to the client-side. +// FIXME: Figure out what we can actually re-use in a meaningful way between +// this method and RPCLibrarySourceEmitter. +void RPCServerSourceEmitter::EmitCallbackFunction(const Method &method) { + // Check invariants and locate necessary resources + Param FuncPointerParam; + Param BatonParam; + for (const auto &Param : method.Params) + if (Param.Type->isFunctionPointerType()) + FuncPointerParam = Param; + else if (Param.Type->isVoidPointerType()) + BatonParam = Param; + + assert(FuncPointerParam.Type->isFunctionPointerType() && + "Emitting callback function with no function pointer"); + assert(BatonParam.Type->isVoidPointerType() && + "Emitting callback function with no baton"); + + QualType FuncType = FuncPointerParam.Type->getPointeeType(); + const auto *FuncProtoType = FuncType->getAs(); + assert(FuncProtoType && "Emitting callback with no parameter information"); + if (!FuncProtoType) + return; // If asserts are off, we'll just fail to compile. + + std::vector CallbackParams; + std::vector CallbackParamsAsStrings; + uint8_t ArgIdx = 0; + for (QualType ParamType : FuncProtoType->param_types()) { + Param CallbackParam; + CallbackParam.IsFollowedByLen = false; + CallbackParam.Type = ParamType; + if (ParamType->isVoidPointerType()) + CallbackParam.Name = "baton"; + else + CallbackParam.Name = "arg" + std::to_string(ArgIdx++); + + CallbackParams.push_back(CallbackParam); + CallbackParamsAsStrings.push_back(ParamType.getAsString(method.Policy) + + " " + CallbackParam.Name); + } + const std::string CallbackReturnTypeName = + FuncProtoType->getReturnType().getAsString(method.Policy); + const std::string CallbackName = method.MangledName + "_callback"; + + // Emit Function Header + std::string Header; + llvm::raw_string_ostream HeaderStream(Header); + HeaderStream << "static " << CallbackReturnTypeName << " " << CallbackName + << "(" << llvm::join(CallbackParamsAsStrings, ", ") << ") {"; + EmitLine(Header); + IndentLevel++; + + // Emit Function Body + EmitLine("// RPC connection setup and sanity checking"); + EmitLine("CallbackInfo *callback_info = (CallbackInfo *)baton;"); + EmitLine("rpc_common::ConnectionSP connection_sp = " + "rpc_common::Connection::GetConnectionFromID(callback_info->" + "connection_id);"); + EmitLine("if (!connection_sp)"); + IndentLevel++; + if (FuncProtoType->getReturnType()->isVoidType()) + EmitLine("return;"); + else + EmitLine("return {};"); + IndentLevel--; + + EmitLine("// Preparing to make the call"); + EmitLine("static RPCFunctionInfo g_func(\"" + CallbackName + "\");"); + EmitLine("RPCStream send;"); + EmitLine("RPCStream response;"); + EmitLine("g_func.Encode(send);"); + + EmitLine("// The first thing we encode is the callback address so that the " + "client-side can know where the callback is"); + EmitLine("RPCValueEncoder(send, rpc_common::RPCPacket::ValueType::Argument, " + "callback_info->callback);"); + EmitLine("// Encode all the arguments"); + for (const Param &CallbackParam : CallbackParams) { + if (lldb_rpc_gen::TypeIsSBClass(CallbackParam.Type)) { + + // FIXME: SB class server references are stored as non-const references so + // that we can actually change them as needed. If a parameter is marked + // const, we will fail to compile because we cannot make an + // SBFooServerReference from a `const SBFoo &`. + // To work around this issue, we'll apply a `const_cast` if needed so we + // can continue to generate callbacks for now, but we really should + // rethink the way we store object IDs server-side to support + // const-qualified parameters. + QualType UnderlyingSBClass = + lldb_rpc_gen::GetUnderlyingType(CallbackParam.Type); + QualType UnqualifiedUnderlyingSBClass = + UnderlyingSBClass.getUnqualifiedType(); + + // FIXME: Replace this bespoke logic with a nice function in RPCCommon. ---------------- bulbazord wrote: It's in a separate PR that Chelsea created. There's a circular dependency between this PR and that PR unfortunately. https://github.com/llvm/llvm-project/pull/138032 From lldb-commits at lists.llvm.org Tue May 6 11:20:47 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Tue, 06 May 2025 11:20:47 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream RPC server interface emitters (PR #138032) In-Reply-To: Message-ID: <681a52ff.170a0220.227fd7.55f2@mx.google.com> ================ @@ -0,0 +1,592 @@ +//===-- RPCServerSourceEmitter.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 "RPCServerSourceEmitter.h" +#include "RPCCommon.h" + +#include "clang/AST/AST.h" +#include "clang/Frontend/CompilerInstance.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace clang; +using namespace lldb_rpc_gen; + +// For methods with pointer return types, it's important that we know how big +// the type of the pointee is. We must correctly size a buffer (in the form of a +// Bytes object) before we can actually use it. +static const std::map MethodsWithPointerReturnTypes = { + {"_ZN4lldb12SBModuleSpec12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 + {"_ZNK4lldb8SBModule12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 +}; + +void RPCServerSourceEmitter::EmitMethod(const Method &method) { + if (method.ContainsFunctionPointerParameter) + EmitCallbackFunction(method); + + EmitCommentHeader(method); + EmitFunctionHeader(method); + EmitFunctionBody(method); + EmitFunctionFooter(); +} + +void RPCServerSourceEmitter::EmitCommentHeader(const Method &method) { + std::string CommentLine; + llvm::raw_string_ostream CommentStream(CommentLine); + + CommentStream << "// " << method.QualifiedName << "(" + << method.CreateParamListAsString(eServer) << ")"; + if (method.IsConst) + CommentStream << " const"; + + EmitLine("//------------------------------------------------------------"); + EmitLine(CommentLine); + EmitLine("//------------------------------------------------------------"); +} + +void RPCServerSourceEmitter::EmitFunctionHeader(const Method &method) { + std::string FunctionHeader; + llvm::raw_string_ostream FunctionHeaderStream(FunctionHeader); + FunctionHeaderStream + << "bool rpc_server::" << method.MangledName + << "::HandleRPCCall(rpc_common::Connection &connection, RPCStream " + "&send, RPCStream &response) {"; + EmitLine(FunctionHeader); + IndentLevel++; +} + +void RPCServerSourceEmitter::EmitFunctionBody(const Method &method) { + EmitLine("// 1) Make local storage for incoming function arguments"); + EmitStorageForParameters(method); + EmitLine("// 2) Decode all function arguments"); + EmitDecodeForParameters(method); + EmitLine("// 3) Call the method and encode the return value"); + EmitMethodCallAndEncode(method); +} + +void RPCServerSourceEmitter::EmitFunctionFooter() { + EmitLine("return true;"); + IndentLevel--; + EmitLine("}"); +} + +void RPCServerSourceEmitter::EmitStorageForParameters(const Method &method) { + // If we have an instance method and it isn't a constructor, we'll need to + // emit a "this" pointer. + if (method.IsInstance && !method.IsCtor) + EmitStorageForOneParameter(method.ThisType, "this_ptr", method.Policy, + /* IsFollowedByLen = */ false); + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + EmitStorageForOneParameter(Iter->Type, Iter->Name, method.Policy, + Iter->IsFollowedByLen); + // Skip over the length parameter, we don't emit it. + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) + Iter++; + } +} + +void RPCServerSourceEmitter::EmitStorageForOneParameter( + QualType ParamType, const std::string &ParamName, + const PrintingPolicy &Policy, bool IsFollowedByLen) { + // First, we consider `const char *`, `const char **`. They have special + // server-side types. + if (TypeIsConstCharPtr(ParamType)) { + EmitLine("rpc_common::ConstCharPointer " + ParamName + ";"); + return; + } else if (TypeIsConstCharPtrPtr(ParamType)) { + EmitLine("rpc_common::StringList " + ParamName + ";"); + return; + } + + QualType UnderlyingType = + lldb_rpc_gen::GetUnqualifiedUnderlyingType(ParamType); + const bool IsSBClass = lldb_rpc_gen::TypeIsSBClass(UnderlyingType); + + if (ParamType->isPointerType() && !IsSBClass) { + // Void pointer with no length is usually a baton for a callback. We're + // going to hold onto the pointer value so we can send it back to the + // client-side when we implement callbacks. + if (ParamType->isVoidPointerType() && !IsFollowedByLen) { + EmitLine("void * " + ParamName + " = nullptr;"); + return; + } + + if (!ParamType->isFunctionPointerType()) { + EmitLine("Bytes " + ParamName + ";"); + return; + } + + assert(ParamType->isFunctionPointerType() && "Unhandled pointer type"); + EmitLine("rpc_common::function_ptr_t " + ParamName + " = nullptr;"); + return; + } + + std::string StorageDeclaration; + llvm::raw_string_ostream StorageDeclarationStream(StorageDeclaration); + + UnderlyingType.print(StorageDeclarationStream, Policy); + StorageDeclarationStream << " "; + if (IsSBClass) + StorageDeclarationStream << "*"; + StorageDeclarationStream << ParamName; + if (IsSBClass) + StorageDeclarationStream << " = nullptr"; + else + StorageDeclarationStream << " = {}"; + StorageDeclarationStream << ";"; + EmitLine(StorageDeclaration); +} + +void RPCServerSourceEmitter::EmitDecodeForParameters(const Method &method) { + if (method.IsInstance && !method.IsCtor) + EmitDecodeForOneParameter(method.ThisType, "this_ptr", method.Policy); + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + EmitDecodeForOneParameter(Iter->Type, Iter->Name, method.Policy); + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) + Iter++; + } +} + +void RPCServerSourceEmitter::EmitDecodeForOneParameter( + QualType ParamType, const std::string &ParamName, + const PrintingPolicy &Policy) { + QualType UnderlyingType = + lldb_rpc_gen::GetUnqualifiedUnderlyingType(ParamType); + + if (TypeIsSBClass(UnderlyingType)) { + std::string DecodeLine; + llvm::raw_string_ostream DecodeLineStream(DecodeLine); + DecodeLineStream << ParamName << " = " + << "RPCServerObjectDecoder<"; + UnderlyingType.print(DecodeLineStream, Policy); + DecodeLineStream << ">(send, rpc_common::RPCPacket::ValueType::Argument);"; + EmitLine(DecodeLine); + EmitLine("if (!" + ParamName + ")"); + IndentLevel++; + EmitLine("return false;"); + IndentLevel--; + } else { + EmitLine("if (!RPCValueDecoder(send, " + "rpc_common::RPCPacket::ValueType::Argument, " + + ParamName + "))"); + IndentLevel++; + EmitLine("return false;"); + IndentLevel--; + } +} + +std::string RPCServerSourceEmitter::CreateMethodCall(const Method &method) { + std::string MethodCall; + llvm::raw_string_ostream MethodCallStream(MethodCall); + if (method.IsInstance) { + if (!method.IsCtor) + MethodCallStream << "this_ptr->"; + MethodCallStream << method.BaseName; + } else + MethodCallStream << method.QualifiedName; + + std::vector Args; + std::string FunctionPointerName; + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + std::string Arg; + // We must check for `const char *` and `const char **` first. + if (TypeIsConstCharPtr(Iter->Type)) { + // `const char *` is stored server-side as rpc_common::ConstCharPointer + Arg = Iter->Name + ".c_str()"; + } else if (TypeIsConstCharPtrPtr(Iter->Type)) { + // `const char **` is stored server-side as rpc_common::StringList + Arg = Iter->Name + ".argv()"; + } else if (lldb_rpc_gen::TypeIsSBClass(Iter->Type)) { + Arg = Iter->Name; + if (!Iter->Type->isPointerType()) + Arg = "*" + Iter->Name; + } else if (Iter->Type->isPointerType() && + !Iter->Type->isFunctionPointerType() && + (!Iter->Type->isVoidPointerType() || Iter->IsFollowedByLen)) { + // We move pointers between the server and client as 'Bytes' objects. + // Pointers with length arguments will have their length filled in below. + // Pointers with no length arguments are assumed to behave like an array + // with length of 1, except for void pointers which are handled + // differently. + Arg = "(" + Iter->Type.getAsString(method.Policy) + ")" + Iter->Name + + ".GetData()"; + } else if (Iter->Type->isFunctionPointerType()) { + // If we have a function pointer, we only want to pass something along if + // we got a real pointer. + Arg = Iter->Name + " ? " + method.MangledName + "_callback : nullptr"; + FunctionPointerName = Iter->Name; + } else if (Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen && + method.ContainsFunctionPointerParameter) { + // Assumptions: + // - This is assumed to be the baton for the function pointer. + // - This is assumed to come after the function pointer parameter. + // We always produce this regardless of the value of the baton argument. + Arg = "new CallbackInfo(" + FunctionPointerName + ", " + Iter->Name + + ", connection.GetConnectionID())"; + } else + Arg = Iter->Name; + + if (Iter->Type->isRValueReferenceType()) + Arg = "std::move(" + Arg + ")"; + Args.push_back(Arg); + + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) { + std::string LengthArg = Iter->Name + ".GetSize()"; + if (!Iter->Type->isVoidPointerType()) { + QualType UUT = lldb_rpc_gen::GetUnqualifiedUnderlyingType(Iter->Type); + LengthArg += " / sizeof(" + UUT.getAsString(method.Policy) + ")"; + } + Args.push_back(LengthArg); + Iter++; + } + } + MethodCallStream << "(" << llvm::join(Args, ", ") << ")"; + + return MethodCall; +} + +std::string RPCServerSourceEmitter::CreateEncodeLine(const std::string &Value, + bool IsEncodingSBClass) { + std::string EncodeLine; + llvm::raw_string_ostream EncodeLineStream(EncodeLine); + + if (IsEncodingSBClass) + EncodeLineStream << "RPCServerObjectEncoder("; + else + EncodeLineStream << "RPCValueEncoder("; + + EncodeLineStream + << "response, rpc_common::RPCPacket::ValueType::ReturnValue, "; + EncodeLineStream << Value; + EncodeLineStream << ");"; + return EncodeLine; +} + +// There are 4 cases to consider: +// - const SBClass &: No need to do anything. +// - const foo &: No need to do anything. +// - SBClass &: The server and the client hold on to IDs to refer to specific +// instances, so there's no need to send any information back to the client. +// - foo &: The client is sending us a value over the wire, but because the type +// is mutable, we must send the changed value back in case the method call +// mutated it. +// +// Updating a mutable reference is done as a return value from the RPC +// perspective. These return values need to be emitted after the method's return +// value, and they are emitted in the order in which they occur in the +// declaration. +void RPCServerSourceEmitter::EmitEncodesForMutableParameters( + const std::vector &Params) { + for (auto Iter = Params.begin(); Iter != Params.end(); Iter++) { + // No need to manually update an SBClass + if (lldb_rpc_gen::TypeIsSBClass(Iter->Type)) + continue; + + if (!Iter->Type->isReferenceType() && !Iter->Type->isPointerType()) + continue; + + // If we have a void pointer with no length, there's nothing to update. This + // is likely a baton for a callback. The same goes for function pointers. + if (Iter->Type->isFunctionPointerType() || + (Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen)) + continue; + + // No need to update pointers and references to const-qualified data. + QualType UnderlyingType = lldb_rpc_gen::GetUnderlyingType(Iter->Type); + if (UnderlyingType.isConstQualified()) + continue; + + const std::string EncodeLine = + CreateEncodeLine(Iter->Name, /* IsEncodingSBClass = */ false); + EmitLine(EncodeLine); + } +} + +// There are 3 possible scenarios that this method can encounter: +// 1. The method has no return value and is not a constructor. +// Only the method call itself is emitted. +// 2. The method is a constructor. +// The call to the constructor is emitted in the encode line. +// 3. The method has a return value. +// The method call is emitted and the return value is captured in a variable. +// After that, an encode call is emitted with the variable that captured the +// return value. +void RPCServerSourceEmitter::EmitMethodCallAndEncode(const Method &method) { + // FIXME: The hand-written lldb-rpc-server currently doesn't emit destructors + // for LocalObjectRefs... even if the type is an ObjectRef. What should we do + // here? ---------------- bulbazord wrote: I think this `FIXME` can be removed. There _should_ be no impact because a LocalObjectRef's only member is a shared pointer to an ObjectRef (which does have a proper destructor). Future PRs should have the proper definitions for ObjectRef and LocalObjectRef, but effectively every SB class inherits from one of them. ObjectRef holds onto a connection, a class ID (so you know what kind of object it is), and an object ID (so you know which object it refers to). https://github.com/llvm/llvm-project/pull/138032 From lldb-commits at lists.llvm.org Tue May 6 11:20:47 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Tue, 06 May 2025 11:20:47 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream RPC server interface emitters (PR #138032) In-Reply-To: Message-ID: <681a52ff.050a0220.2bc3a6.ce61@mx.google.com> ================ @@ -0,0 +1,592 @@ +//===-- RPCServerSourceEmitter.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 "RPCServerSourceEmitter.h" +#include "RPCCommon.h" + +#include "clang/AST/AST.h" +#include "clang/Frontend/CompilerInstance.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace clang; +using namespace lldb_rpc_gen; + +// For methods with pointer return types, it's important that we know how big +// the type of the pointee is. We must correctly size a buffer (in the form of a +// Bytes object) before we can actually use it. +static const std::map MethodsWithPointerReturnTypes = { + {"_ZN4lldb12SBModuleSpec12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 + {"_ZNK4lldb8SBModule12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 +}; + +void RPCServerSourceEmitter::EmitMethod(const Method &method) { + if (method.ContainsFunctionPointerParameter) + EmitCallbackFunction(method); + + EmitCommentHeader(method); + EmitFunctionHeader(method); + EmitFunctionBody(method); + EmitFunctionFooter(); +} + +void RPCServerSourceEmitter::EmitCommentHeader(const Method &method) { + std::string CommentLine; + llvm::raw_string_ostream CommentStream(CommentLine); + + CommentStream << "// " << method.QualifiedName << "(" + << method.CreateParamListAsString(eServer) << ")"; + if (method.IsConst) + CommentStream << " const"; + + EmitLine("//------------------------------------------------------------"); + EmitLine(CommentLine); + EmitLine("//------------------------------------------------------------"); +} + +void RPCServerSourceEmitter::EmitFunctionHeader(const Method &method) { + std::string FunctionHeader; + llvm::raw_string_ostream FunctionHeaderStream(FunctionHeader); + FunctionHeaderStream + << "bool rpc_server::" << method.MangledName + << "::HandleRPCCall(rpc_common::Connection &connection, RPCStream " + "&send, RPCStream &response) {"; + EmitLine(FunctionHeader); + IndentLevel++; +} + +void RPCServerSourceEmitter::EmitFunctionBody(const Method &method) { + EmitLine("// 1) Make local storage for incoming function arguments"); + EmitStorageForParameters(method); + EmitLine("// 2) Decode all function arguments"); + EmitDecodeForParameters(method); + EmitLine("// 3) Call the method and encode the return value"); + EmitMethodCallAndEncode(method); +} + +void RPCServerSourceEmitter::EmitFunctionFooter() { + EmitLine("return true;"); + IndentLevel--; + EmitLine("}"); +} + +void RPCServerSourceEmitter::EmitStorageForParameters(const Method &method) { + // If we have an instance method and it isn't a constructor, we'll need to + // emit a "this" pointer. + if (method.IsInstance && !method.IsCtor) + EmitStorageForOneParameter(method.ThisType, "this_ptr", method.Policy, + /* IsFollowedByLen = */ false); + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + EmitStorageForOneParameter(Iter->Type, Iter->Name, method.Policy, + Iter->IsFollowedByLen); + // Skip over the length parameter, we don't emit it. + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) + Iter++; + } +} + +void RPCServerSourceEmitter::EmitStorageForOneParameter( + QualType ParamType, const std::string &ParamName, + const PrintingPolicy &Policy, bool IsFollowedByLen) { + // First, we consider `const char *`, `const char **`. They have special + // server-side types. + if (TypeIsConstCharPtr(ParamType)) { + EmitLine("rpc_common::ConstCharPointer " + ParamName + ";"); + return; + } else if (TypeIsConstCharPtrPtr(ParamType)) { + EmitLine("rpc_common::StringList " + ParamName + ";"); + return; + } + + QualType UnderlyingType = + lldb_rpc_gen::GetUnqualifiedUnderlyingType(ParamType); + const bool IsSBClass = lldb_rpc_gen::TypeIsSBClass(UnderlyingType); + + if (ParamType->isPointerType() && !IsSBClass) { + // Void pointer with no length is usually a baton for a callback. We're + // going to hold onto the pointer value so we can send it back to the + // client-side when we implement callbacks. + if (ParamType->isVoidPointerType() && !IsFollowedByLen) { + EmitLine("void * " + ParamName + " = nullptr;"); + return; + } + + if (!ParamType->isFunctionPointerType()) { + EmitLine("Bytes " + ParamName + ";"); + return; + } + + assert(ParamType->isFunctionPointerType() && "Unhandled pointer type"); + EmitLine("rpc_common::function_ptr_t " + ParamName + " = nullptr;"); + return; + } + + std::string StorageDeclaration; + llvm::raw_string_ostream StorageDeclarationStream(StorageDeclaration); + + UnderlyingType.print(StorageDeclarationStream, Policy); + StorageDeclarationStream << " "; + if (IsSBClass) + StorageDeclarationStream << "*"; + StorageDeclarationStream << ParamName; + if (IsSBClass) + StorageDeclarationStream << " = nullptr"; + else + StorageDeclarationStream << " = {}"; + StorageDeclarationStream << ";"; + EmitLine(StorageDeclaration); +} + +void RPCServerSourceEmitter::EmitDecodeForParameters(const Method &method) { + if (method.IsInstance && !method.IsCtor) + EmitDecodeForOneParameter(method.ThisType, "this_ptr", method.Policy); + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + EmitDecodeForOneParameter(Iter->Type, Iter->Name, method.Policy); + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) + Iter++; + } +} + +void RPCServerSourceEmitter::EmitDecodeForOneParameter( + QualType ParamType, const std::string &ParamName, + const PrintingPolicy &Policy) { + QualType UnderlyingType = + lldb_rpc_gen::GetUnqualifiedUnderlyingType(ParamType); + + if (TypeIsSBClass(UnderlyingType)) { + std::string DecodeLine; + llvm::raw_string_ostream DecodeLineStream(DecodeLine); + DecodeLineStream << ParamName << " = " + << "RPCServerObjectDecoder<"; + UnderlyingType.print(DecodeLineStream, Policy); + DecodeLineStream << ">(send, rpc_common::RPCPacket::ValueType::Argument);"; + EmitLine(DecodeLine); + EmitLine("if (!" + ParamName + ")"); + IndentLevel++; + EmitLine("return false;"); + IndentLevel--; + } else { + EmitLine("if (!RPCValueDecoder(send, " + "rpc_common::RPCPacket::ValueType::Argument, " + + ParamName + "))"); + IndentLevel++; + EmitLine("return false;"); + IndentLevel--; + } +} + +std::string RPCServerSourceEmitter::CreateMethodCall(const Method &method) { + std::string MethodCall; + llvm::raw_string_ostream MethodCallStream(MethodCall); + if (method.IsInstance) { + if (!method.IsCtor) + MethodCallStream << "this_ptr->"; + MethodCallStream << method.BaseName; + } else + MethodCallStream << method.QualifiedName; + + std::vector Args; + std::string FunctionPointerName; + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + std::string Arg; + // We must check for `const char *` and `const char **` first. + if (TypeIsConstCharPtr(Iter->Type)) { + // `const char *` is stored server-side as rpc_common::ConstCharPointer + Arg = Iter->Name + ".c_str()"; + } else if (TypeIsConstCharPtrPtr(Iter->Type)) { + // `const char **` is stored server-side as rpc_common::StringList + Arg = Iter->Name + ".argv()"; + } else if (lldb_rpc_gen::TypeIsSBClass(Iter->Type)) { + Arg = Iter->Name; + if (!Iter->Type->isPointerType()) + Arg = "*" + Iter->Name; + } else if (Iter->Type->isPointerType() && + !Iter->Type->isFunctionPointerType() && + (!Iter->Type->isVoidPointerType() || Iter->IsFollowedByLen)) { + // We move pointers between the server and client as 'Bytes' objects. + // Pointers with length arguments will have their length filled in below. + // Pointers with no length arguments are assumed to behave like an array + // with length of 1, except for void pointers which are handled + // differently. + Arg = "(" + Iter->Type.getAsString(method.Policy) + ")" + Iter->Name + + ".GetData()"; + } else if (Iter->Type->isFunctionPointerType()) { + // If we have a function pointer, we only want to pass something along if + // we got a real pointer. + Arg = Iter->Name + " ? " + method.MangledName + "_callback : nullptr"; + FunctionPointerName = Iter->Name; + } else if (Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen && + method.ContainsFunctionPointerParameter) { + // Assumptions: + // - This is assumed to be the baton for the function pointer. + // - This is assumed to come after the function pointer parameter. + // We always produce this regardless of the value of the baton argument. + Arg = "new CallbackInfo(" + FunctionPointerName + ", " + Iter->Name + + ", connection.GetConnectionID())"; + } else + Arg = Iter->Name; + + if (Iter->Type->isRValueReferenceType()) + Arg = "std::move(" + Arg + ")"; + Args.push_back(Arg); + + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) { + std::string LengthArg = Iter->Name + ".GetSize()"; + if (!Iter->Type->isVoidPointerType()) { + QualType UUT = lldb_rpc_gen::GetUnqualifiedUnderlyingType(Iter->Type); + LengthArg += " / sizeof(" + UUT.getAsString(method.Policy) + ")"; + } + Args.push_back(LengthArg); + Iter++; + } + } + MethodCallStream << "(" << llvm::join(Args, ", ") << ")"; + + return MethodCall; +} + +std::string RPCServerSourceEmitter::CreateEncodeLine(const std::string &Value, + bool IsEncodingSBClass) { + std::string EncodeLine; + llvm::raw_string_ostream EncodeLineStream(EncodeLine); + + if (IsEncodingSBClass) + EncodeLineStream << "RPCServerObjectEncoder("; + else + EncodeLineStream << "RPCValueEncoder("; + + EncodeLineStream + << "response, rpc_common::RPCPacket::ValueType::ReturnValue, "; + EncodeLineStream << Value; + EncodeLineStream << ");"; + return EncodeLine; +} + +// There are 4 cases to consider: +// - const SBClass &: No need to do anything. +// - const foo &: No need to do anything. +// - SBClass &: The server and the client hold on to IDs to refer to specific +// instances, so there's no need to send any information back to the client. +// - foo &: The client is sending us a value over the wire, but because the type +// is mutable, we must send the changed value back in case the method call +// mutated it. +// +// Updating a mutable reference is done as a return value from the RPC +// perspective. These return values need to be emitted after the method's return +// value, and they are emitted in the order in which they occur in the +// declaration. +void RPCServerSourceEmitter::EmitEncodesForMutableParameters( + const std::vector &Params) { + for (auto Iter = Params.begin(); Iter != Params.end(); Iter++) { + // No need to manually update an SBClass + if (lldb_rpc_gen::TypeIsSBClass(Iter->Type)) + continue; + + if (!Iter->Type->isReferenceType() && !Iter->Type->isPointerType()) + continue; + + // If we have a void pointer with no length, there's nothing to update. This + // is likely a baton for a callback. The same goes for function pointers. + if (Iter->Type->isFunctionPointerType() || + (Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen)) + continue; + + // No need to update pointers and references to const-qualified data. + QualType UnderlyingType = lldb_rpc_gen::GetUnderlyingType(Iter->Type); + if (UnderlyingType.isConstQualified()) + continue; + + const std::string EncodeLine = + CreateEncodeLine(Iter->Name, /* IsEncodingSBClass = */ false); + EmitLine(EncodeLine); + } +} + +// There are 3 possible scenarios that this method can encounter: +// 1. The method has no return value and is not a constructor. +// Only the method call itself is emitted. +// 2. The method is a constructor. +// The call to the constructor is emitted in the encode line. +// 3. The method has a return value. +// The method call is emitted and the return value is captured in a variable. +// After that, an encode call is emitted with the variable that captured the +// return value. +void RPCServerSourceEmitter::EmitMethodCallAndEncode(const Method &method) { + // FIXME: The hand-written lldb-rpc-server currently doesn't emit destructors + // for LocalObjectRefs... even if the type is an ObjectRef. What should we do + // here? + + const std::string MethodCall = CreateMethodCall(method); + + // If this function returns nothing, we just emit the call and update any + // mutable references. Note that constructors have return type `void` so we + // must explicitly check for that here. + if (!method.IsCtor && method.ReturnType->isVoidType()) { + EmitLine(MethodCall + ";"); + EmitEncodesForMutableParameters(method.Params); + return; + } + + static constexpr llvm::StringLiteral ReturnVariableName("__result"); + + // If this isn't a constructor, we'll need to store the result of the method + // call in a result variable. + if (!method.IsCtor) { + // We need to determine what the appropriate return type is. Here is the + // strategy: + // 1.) `SBFoo` -> `SBFoo &&` ---------------- bulbazord wrote: This comment is a bit misleading (and I wrote it, so you can blame me :p). The code we generated here would look like this: ``` SBFoo &&__result = this_ptr->GetTheFoo(); RPCServerObjectEncoder(response, rpc_common::RPCPacket::ValueType::ReturnValue, std::move(__result)); return true; ``` `this_ptr->GetTheFoo` would create a temporary `SBFoo` value and `__result` is a reference (rvalue ref) to that temporary value. This extends the lifetime of that temporary `SBFoo`. On the next line, we'll "encode" that result value into the `response` stream (which is going to get turned into a packet). This line does a lot of work that you don't have access to yet, but effectively it will: 1. Put the SBFoo into an "ObjectInstance" map. The client and server speak in terms of "object IDs" instead of moving information about an object in their protocol packets. The server maintains a mapping of (Object ID -> Object Instance), and when we encode the SBFoo return value, the instance gets assigned an Object ID and stuck in the map. 2. Encode the return value's object ID into the "response" packet and indicate that it is a return value from this method. 3. Returning "true" indicates that the RPC call succeeded on the server-side. https://github.com/llvm/llvm-project/pull/138032 From lldb-commits at lists.llvm.org Tue May 6 11:21:40 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Tue, 06 May 2025 11:21:40 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream Python scripts (PR #138028) In-Reply-To: Message-ID: <681a5334.170a0220.3cda38.2ca5@mx.google.com> chelcassanova wrote: For what it's worth, while these scripts do work for intended purpose for now, an approach I'd love is to have all this header modification be a subtool of the `lldb-rpc-gen` tool itself. Subtooling a ClangTool is something I'm not too sure on how to do so maybe a better Clang expert can help guide the way on this? :) https://github.com/llvm/llvm-project/pull/138028 From lldb-commits at lists.llvm.org Tue May 6 11:23:05 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Tue, 06 May 2025 11:23:05 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Do not bump memory modificator ID when "internal" debugger memory is updated (PR #129092) In-Reply-To: Message-ID: <681a5389.630a0220.367ecd.32f7@mx.google.com> DavidSpickett wrote: Please correct the REQUIRES if you have access to such machines to confirm it. Maybe the test is so specific that it is fine to be on only one architecture/os combination. I recall a way in codegen tests to capture a line number in one pattern and re-use that in a check pattern, I'll have a look for it tomorrow. https://github.com/llvm/llvm-project/pull/129092 From lldb-commits at lists.llvm.org Tue May 6 11:23:58 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Tue, 06 May 2025 11:23:58 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream RPC server interface emitters (PR #138032) In-Reply-To: Message-ID: <681a53be.170a0220.c7b91.2e8e@mx.google.com> ================ @@ -0,0 +1,592 @@ +//===-- RPCServerSourceEmitter.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 "RPCServerSourceEmitter.h" +#include "RPCCommon.h" + +#include "clang/AST/AST.h" +#include "clang/Frontend/CompilerInstance.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace clang; +using namespace lldb_rpc_gen; + +// For methods with pointer return types, it's important that we know how big +// the type of the pointee is. We must correctly size a buffer (in the form of a +// Bytes object) before we can actually use it. +static const std::map MethodsWithPointerReturnTypes = { + {"_ZN4lldb12SBModuleSpec12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 + {"_ZNK4lldb8SBModule12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 +}; + +void RPCServerSourceEmitter::EmitMethod(const Method &method) { + if (method.ContainsFunctionPointerParameter) + EmitCallbackFunction(method); + + EmitCommentHeader(method); + EmitFunctionHeader(method); + EmitFunctionBody(method); + EmitFunctionFooter(); +} + +void RPCServerSourceEmitter::EmitCommentHeader(const Method &method) { + std::string CommentLine; + llvm::raw_string_ostream CommentStream(CommentLine); + + CommentStream << "// " << method.QualifiedName << "(" + << method.CreateParamListAsString(eServer) << ")"; + if (method.IsConst) + CommentStream << " const"; + + EmitLine("//------------------------------------------------------------"); + EmitLine(CommentLine); + EmitLine("//------------------------------------------------------------"); +} + +void RPCServerSourceEmitter::EmitFunctionHeader(const Method &method) { + std::string FunctionHeader; + llvm::raw_string_ostream FunctionHeaderStream(FunctionHeader); + FunctionHeaderStream + << "bool rpc_server::" << method.MangledName + << "::HandleRPCCall(rpc_common::Connection &connection, RPCStream " + "&send, RPCStream &response) {"; + EmitLine(FunctionHeader); + IndentLevel++; +} + +void RPCServerSourceEmitter::EmitFunctionBody(const Method &method) { + EmitLine("// 1) Make local storage for incoming function arguments"); + EmitStorageForParameters(method); + EmitLine("// 2) Decode all function arguments"); + EmitDecodeForParameters(method); + EmitLine("// 3) Call the method and encode the return value"); + EmitMethodCallAndEncode(method); +} + +void RPCServerSourceEmitter::EmitFunctionFooter() { + EmitLine("return true;"); + IndentLevel--; + EmitLine("}"); +} + +void RPCServerSourceEmitter::EmitStorageForParameters(const Method &method) { + // If we have an instance method and it isn't a constructor, we'll need to + // emit a "this" pointer. + if (method.IsInstance && !method.IsCtor) + EmitStorageForOneParameter(method.ThisType, "this_ptr", method.Policy, + /* IsFollowedByLen = */ false); + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + EmitStorageForOneParameter(Iter->Type, Iter->Name, method.Policy, + Iter->IsFollowedByLen); + // Skip over the length parameter, we don't emit it. + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) + Iter++; + } +} + +void RPCServerSourceEmitter::EmitStorageForOneParameter( + QualType ParamType, const std::string &ParamName, + const PrintingPolicy &Policy, bool IsFollowedByLen) { + // First, we consider `const char *`, `const char **`. They have special + // server-side types. + if (TypeIsConstCharPtr(ParamType)) { + EmitLine("rpc_common::ConstCharPointer " + ParamName + ";"); + return; + } else if (TypeIsConstCharPtrPtr(ParamType)) { + EmitLine("rpc_common::StringList " + ParamName + ";"); + return; + } + + QualType UnderlyingType = + lldb_rpc_gen::GetUnqualifiedUnderlyingType(ParamType); + const bool IsSBClass = lldb_rpc_gen::TypeIsSBClass(UnderlyingType); + + if (ParamType->isPointerType() && !IsSBClass) { + // Void pointer with no length is usually a baton for a callback. We're + // going to hold onto the pointer value so we can send it back to the + // client-side when we implement callbacks. + if (ParamType->isVoidPointerType() && !IsFollowedByLen) { + EmitLine("void * " + ParamName + " = nullptr;"); + return; + } + + if (!ParamType->isFunctionPointerType()) { + EmitLine("Bytes " + ParamName + ";"); + return; + } + + assert(ParamType->isFunctionPointerType() && "Unhandled pointer type"); + EmitLine("rpc_common::function_ptr_t " + ParamName + " = nullptr;"); + return; + } + + std::string StorageDeclaration; + llvm::raw_string_ostream StorageDeclarationStream(StorageDeclaration); + + UnderlyingType.print(StorageDeclarationStream, Policy); + StorageDeclarationStream << " "; + if (IsSBClass) + StorageDeclarationStream << "*"; + StorageDeclarationStream << ParamName; + if (IsSBClass) + StorageDeclarationStream << " = nullptr"; + else + StorageDeclarationStream << " = {}"; + StorageDeclarationStream << ";"; + EmitLine(StorageDeclaration); +} + +void RPCServerSourceEmitter::EmitDecodeForParameters(const Method &method) { + if (method.IsInstance && !method.IsCtor) + EmitDecodeForOneParameter(method.ThisType, "this_ptr", method.Policy); + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + EmitDecodeForOneParameter(Iter->Type, Iter->Name, method.Policy); + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) + Iter++; + } +} + +void RPCServerSourceEmitter::EmitDecodeForOneParameter( + QualType ParamType, const std::string &ParamName, + const PrintingPolicy &Policy) { + QualType UnderlyingType = + lldb_rpc_gen::GetUnqualifiedUnderlyingType(ParamType); + + if (TypeIsSBClass(UnderlyingType)) { + std::string DecodeLine; + llvm::raw_string_ostream DecodeLineStream(DecodeLine); + DecodeLineStream << ParamName << " = " + << "RPCServerObjectDecoder<"; + UnderlyingType.print(DecodeLineStream, Policy); + DecodeLineStream << ">(send, rpc_common::RPCPacket::ValueType::Argument);"; + EmitLine(DecodeLine); + EmitLine("if (!" + ParamName + ")"); + IndentLevel++; + EmitLine("return false;"); + IndentLevel--; + } else { + EmitLine("if (!RPCValueDecoder(send, " + "rpc_common::RPCPacket::ValueType::Argument, " + + ParamName + "))"); + IndentLevel++; + EmitLine("return false;"); + IndentLevel--; + } +} + +std::string RPCServerSourceEmitter::CreateMethodCall(const Method &method) { + std::string MethodCall; + llvm::raw_string_ostream MethodCallStream(MethodCall); + if (method.IsInstance) { + if (!method.IsCtor) + MethodCallStream << "this_ptr->"; + MethodCallStream << method.BaseName; + } else + MethodCallStream << method.QualifiedName; + + std::vector Args; + std::string FunctionPointerName; + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + std::string Arg; + // We must check for `const char *` and `const char **` first. + if (TypeIsConstCharPtr(Iter->Type)) { + // `const char *` is stored server-side as rpc_common::ConstCharPointer + Arg = Iter->Name + ".c_str()"; + } else if (TypeIsConstCharPtrPtr(Iter->Type)) { + // `const char **` is stored server-side as rpc_common::StringList + Arg = Iter->Name + ".argv()"; + } else if (lldb_rpc_gen::TypeIsSBClass(Iter->Type)) { + Arg = Iter->Name; + if (!Iter->Type->isPointerType()) + Arg = "*" + Iter->Name; + } else if (Iter->Type->isPointerType() && + !Iter->Type->isFunctionPointerType() && + (!Iter->Type->isVoidPointerType() || Iter->IsFollowedByLen)) { + // We move pointers between the server and client as 'Bytes' objects. + // Pointers with length arguments will have their length filled in below. + // Pointers with no length arguments are assumed to behave like an array + // with length of 1, except for void pointers which are handled + // differently. + Arg = "(" + Iter->Type.getAsString(method.Policy) + ")" + Iter->Name + + ".GetData()"; + } else if (Iter->Type->isFunctionPointerType()) { + // If we have a function pointer, we only want to pass something along if + // we got a real pointer. + Arg = Iter->Name + " ? " + method.MangledName + "_callback : nullptr"; + FunctionPointerName = Iter->Name; + } else if (Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen && + method.ContainsFunctionPointerParameter) { + // Assumptions: + // - This is assumed to be the baton for the function pointer. + // - This is assumed to come after the function pointer parameter. + // We always produce this regardless of the value of the baton argument. + Arg = "new CallbackInfo(" + FunctionPointerName + ", " + Iter->Name + + ", connection.GetConnectionID())"; + } else + Arg = Iter->Name; + + if (Iter->Type->isRValueReferenceType()) + Arg = "std::move(" + Arg + ")"; + Args.push_back(Arg); + + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) { + std::string LengthArg = Iter->Name + ".GetSize()"; + if (!Iter->Type->isVoidPointerType()) { + QualType UUT = lldb_rpc_gen::GetUnqualifiedUnderlyingType(Iter->Type); + LengthArg += " / sizeof(" + UUT.getAsString(method.Policy) + ")"; + } + Args.push_back(LengthArg); + Iter++; + } + } + MethodCallStream << "(" << llvm::join(Args, ", ") << ")"; + + return MethodCall; +} + +std::string RPCServerSourceEmitter::CreateEncodeLine(const std::string &Value, + bool IsEncodingSBClass) { + std::string EncodeLine; + llvm::raw_string_ostream EncodeLineStream(EncodeLine); + + if (IsEncodingSBClass) + EncodeLineStream << "RPCServerObjectEncoder("; + else + EncodeLineStream << "RPCValueEncoder("; + + EncodeLineStream + << "response, rpc_common::RPCPacket::ValueType::ReturnValue, "; + EncodeLineStream << Value; + EncodeLineStream << ");"; + return EncodeLine; +} + +// There are 4 cases to consider: +// - const SBClass &: No need to do anything. +// - const foo &: No need to do anything. +// - SBClass &: The server and the client hold on to IDs to refer to specific +// instances, so there's no need to send any information back to the client. +// - foo &: The client is sending us a value over the wire, but because the type +// is mutable, we must send the changed value back in case the method call +// mutated it. +// +// Updating a mutable reference is done as a return value from the RPC +// perspective. These return values need to be emitted after the method's return +// value, and they are emitted in the order in which they occur in the +// declaration. +void RPCServerSourceEmitter::EmitEncodesForMutableParameters( + const std::vector &Params) { + for (auto Iter = Params.begin(); Iter != Params.end(); Iter++) { + // No need to manually update an SBClass + if (lldb_rpc_gen::TypeIsSBClass(Iter->Type)) + continue; + + if (!Iter->Type->isReferenceType() && !Iter->Type->isPointerType()) + continue; + + // If we have a void pointer with no length, there's nothing to update. This + // is likely a baton for a callback. The same goes for function pointers. + if (Iter->Type->isFunctionPointerType() || + (Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen)) + continue; + + // No need to update pointers and references to const-qualified data. + QualType UnderlyingType = lldb_rpc_gen::GetUnderlyingType(Iter->Type); + if (UnderlyingType.isConstQualified()) + continue; + + const std::string EncodeLine = + CreateEncodeLine(Iter->Name, /* IsEncodingSBClass = */ false); + EmitLine(EncodeLine); + } +} + +// There are 3 possible scenarios that this method can encounter: +// 1. The method has no return value and is not a constructor. +// Only the method call itself is emitted. +// 2. The method is a constructor. +// The call to the constructor is emitted in the encode line. +// 3. The method has a return value. +// The method call is emitted and the return value is captured in a variable. +// After that, an encode call is emitted with the variable that captured the +// return value. +void RPCServerSourceEmitter::EmitMethodCallAndEncode(const Method &method) { + // FIXME: The hand-written lldb-rpc-server currently doesn't emit destructors + // for LocalObjectRefs... even if the type is an ObjectRef. What should we do + // here? ---------------- chelcassanova wrote: And to add on to this, `ObjectRefs` are held on to by the server (they're usually used to store larger amounts of information about an RPC object and its connection`) while `LocalObjectRef`s are used locally for smaller classes (like Lists and Specs if I'm not mistaken). https://github.com/llvm/llvm-project/pull/138032 From lldb-commits at lists.llvm.org Tue May 6 11:24:12 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Tue, 06 May 2025 11:24:12 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream RPC server interface emitters (PR #138032) In-Reply-To: Message-ID: <681a53cc.170a0220.39adf2.28dc@mx.google.com> https://github.com/chelcassanova edited https://github.com/llvm/llvm-project/pull/138032 From lldb-commits at lists.llvm.org Tue May 6 11:46:08 2025 From: lldb-commits at lists.llvm.org (Ilia Kuklin via lldb-commits) Date: Tue, 06 May 2025 11:46:08 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <681a58f0.170a0220.edd8a.3381@mx.google.com> ================ @@ -111,7 +111,36 @@ ASTNodeUP DILParser::ParseUnaryExpression() { llvm_unreachable("invalid token kind"); } } - return ParsePrimaryExpression(); + return ParsePostfixExpression(); +} + +// Parse a postfix_expression. +// +// postfix_expression: +// primary_expression +// postfix_expression "[" expression "]" +// +ASTNodeUP DILParser::ParsePostfixExpression() { + ASTNodeUP lhs = ParsePrimaryExpression(); + while (CurToken().Is(Token::l_square)) { + uint32_t loc = CurToken().GetLocation(); + Token token = CurToken(); + switch (token.GetKind()) { + case Token::l_square: { ---------------- kuilpd wrote: This is for when other postfix expressions get added here, `.` and `->` from the other PR. https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Tue May 6 11:46:17 2025 From: lldb-commits at lists.llvm.org (Ilia Kuklin via lldb-commits) Date: Tue, 06 May 2025 11:46:17 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <681a58f9.170a0220.27bc97.3b46@mx.google.com> ================ @@ -280,6 +311,52 @@ void DILParser::BailOut(const std::string &error, uint32_t loc, m_dil_lexer.ResetTokenIdx(m_dil_lexer.NumLexedTokens() - 1); } +// Parse a numeric_literal. +// +// numeric_literal: +// ? Token::numeric_constant ? +// +ASTNodeUP DILParser::ParseNumericLiteral() { + Expect(Token::numeric_constant); + ASTNodeUP numeric_constant = ParseNumericConstant(); + if (numeric_constant->GetKind() == NodeKind::eErrorNode) { + BailOut(llvm::formatv("Failed to parse token as numeric-constant: {0}", + CurToken()), + CurToken().GetLocation(), CurToken().GetSpelling().length()); + return std::make_unique(); + } + m_dil_lexer.Advance(); + return numeric_constant; +} + +static constexpr std::pair type_suffixes[] = { + {"ull", lldb::eBasicTypeUnsignedLongLong}, + {"ul", lldb::eBasicTypeUnsignedLong}, + {"u", lldb::eBasicTypeUnsignedInt}, + {"ll", lldb::eBasicTypeLongLong}, + {"l", lldb::eBasicTypeLong}, +}; + +ASTNodeUP DILParser::ParseNumericConstant() { + Token token = CurToken(); + auto spelling = token.GetSpelling(); + llvm::StringRef spelling_ref = spelling; + lldb::BasicType type = lldb::eBasicTypeInt; + for (auto [suffix, t] : type_suffixes) { + if (spelling_ref.consume_back_insensitive(suffix)) { + type = t; + break; + } + } + llvm::APInt raw_value; + if (!spelling_ref.getAsInteger(0, raw_value)) { ---------------- kuilpd wrote: It doesn't, I guess we don't really need this character in numbers. https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Tue May 6 11:46:22 2025 From: lldb-commits at lists.llvm.org (Ilia Kuklin via lldb-commits) Date: Tue, 06 May 2025 11:46:22 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <681a58fe.170a0220.c7e4f.36a1@mx.google.com> kuilpd wrote: > Apart from the pointer indexing question, this PR also opens the question of "how should the numbers be represented". Here you represent them as ValueObjects, which means you have to give them types, which means you have to find a type system for them, ... Why is that a bad thing? Can we do math operations later without types? Plus it's a unified interface, node evaluation returns a `ValueObject`. > I'm not sure I agree with how are those steps implemented (in fact, I'm pretty sure I don't agree with at least some of those steps). In order to keep this PR focused, and because a first-class number representation is not necessary for replacing the current "frame var", my suggestion would be to dumb down the implementation of `postfix_expression`: Instead of `postfix_expression "[" expression "]"`, you could implement something like `postfix_expression "[" integer "]"`. Then we can store the RHS of the `[]` expression directly as a (u)int64 (or AP(S)Int, or whatever), and we can completely sidestep the question its type. I mean... this works and is a basis for future patches, why remove something that we will have to bring back shortly afterwards? After replacing frame var, DIL will just have a little bit of extra capabilities, like using another variable as an index. > My reason for that is that I believe that after this is implemented (and @cmtice finishes member access), we will be in a position to flip the flag on this and make it the default -- which I think is the real proof of the pudding, and I sort of expect we will need to tweak the implementation until everything settles into place. I think it's better to not introduce additional complexity while we're doing that. We still need to implement bit extraction that current `frame var` allows, which looks like this: `integer[4-8]`, another node we will have to re-implement later if we redo how numbers are stored now. https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Tue May 6 11:46:12 2025 From: lldb-commits at lists.llvm.org (Ilia Kuklin via lldb-commits) Date: Tue, 06 May 2025 11:46:12 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <681a58f4.630a0220.382d9.5999@mx.google.com> ================ @@ -24,6 +28,8 @@ qualified_id = ["::"] [nested_name_specifier] unqualified_id identifier = ? C99 Identifier ? ; +numeric_literal = ? C99 Integer constant ? ; ---------------- kuilpd wrote: Yeah, `0b...` and `0o...` formats supported here are missing from C99. How should I explain the format without writing its full grammar as well? https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Tue May 6 11:47:56 2025 From: lldb-commits at lists.llvm.org (Ilia Kuklin via lldb-commits) Date: Tue, 06 May 2025 11:47:56 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <681a595c.050a0220.142dbb.e10d@mx.google.com> https://github.com/kuilpd updated https://github.com/llvm/llvm-project/pull/138551 >From cfe7359bd16c1e87932e2ebb8bcdfc88130e9729 Mon Sep 17 00:00:00 2001 From: Ilia Kuklin Date: Wed, 30 Apr 2025 22:03:50 +0500 Subject: [PATCH 1/3] [LLDB] Add array subscription and integer parsing to DIL --- lldb/docs/dil-expr-lang.ebnf | 12 +- lldb/include/lldb/ValueObject/DILAST.h | 46 +++++ lldb/include/lldb/ValueObject/DILEval.h | 6 + lldb/include/lldb/ValueObject/DILLexer.h | 3 + lldb/include/lldb/ValueObject/DILParser.h | 3 + lldb/source/ValueObject/DILAST.cpp | 10 ++ lldb/source/ValueObject/DILEval.cpp | 159 ++++++++++++++++++ lldb/source/ValueObject/DILLexer.cpp | 43 ++++- lldb/source/ValueObject/DILParser.cpp | 79 ++++++++- .../var-dil/basics/ArraySubscript/Makefile | 3 + .../TestFrameVarDILArraySubscript.py | 88 ++++++++++ .../var-dil/basics/ArraySubscript/main.cpp | 31 ++++ lldb/unittests/ValueObject/DILLexerTests.cpp | 34 +++- 13 files changed, 506 insertions(+), 11 deletions(-) create mode 100644 lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/Makefile create mode 100644 lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py create mode 100644 lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf index c8bf4231b3e4a..0cbb5403785db 100644 --- a/lldb/docs/dil-expr-lang.ebnf +++ b/lldb/docs/dil-expr-lang.ebnf @@ -6,16 +6,20 @@ expression = unary_expression ; unary_expression = unary_operator expression - | primary_expression ; + | postfix_expression ; unary_operator = "*" | "&" ; -primary_expression = id_expression +postfix_expression = primary_expression + | postfix_expression "[" expression "]"; + +primary_expression = numeric_literal + | id_expression | "(" expression ")"; id_expression = unqualified_id | qualified_id - | register ; + | register ; unqualified_id = identifier ; @@ -24,6 +28,8 @@ qualified_id = ["::"] [nested_name_specifier] unqualified_id identifier = ? C99 Identifier ? ; +numeric_literal = ? C99 Integer constant ? ; + register = "$" ? Register name ? ; nested_name_specifier = type_name "::" diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h index fe3827ef0516a..6908deed7aee3 100644 --- a/lldb/include/lldb/ValueObject/DILAST.h +++ b/lldb/include/lldb/ValueObject/DILAST.h @@ -18,8 +18,10 @@ namespace lldb_private::dil { /// The various types DIL AST nodes (used by the DIL parser). enum class NodeKind { + eArraySubscriptNode, eErrorNode, eIdentifierNode, + eScalarLiteralNode, eUnaryOpNode, }; @@ -71,6 +73,26 @@ class ErrorNode : public ASTNode { } }; +class ScalarLiteralNode : public ASTNode { +public: + ScalarLiteralNode(uint32_t location, lldb::BasicType type, Scalar value) + : ASTNode(location, NodeKind::eScalarLiteralNode), m_type(type), + m_value(value) {} + + llvm::Expected Accept(Visitor *v) const override; + + lldb::BasicType GetType() const { return m_type; } + Scalar GetValue() const & { return m_value; } + + static bool classof(const ASTNode *node) { + return node->GetKind() == NodeKind::eScalarLiteralNode; + } + +private: + lldb::BasicType m_type; + Scalar m_value; +}; + class IdentifierNode : public ASTNode { public: IdentifierNode(uint32_t location, std::string name) @@ -108,6 +130,26 @@ class UnaryOpNode : public ASTNode { ASTNodeUP m_operand; }; +class ArraySubscriptNode : public ASTNode { +public: + ArraySubscriptNode(uint32_t location, ASTNodeUP lhs, ASTNodeUP rhs) + : ASTNode(location, NodeKind::eArraySubscriptNode), m_lhs(std::move(lhs)), + m_rhs(std::move(rhs)) {} + + llvm::Expected Accept(Visitor *v) const override; + + ASTNode *lhs() const { return m_lhs.get(); } + ASTNode *rhs() const { return m_rhs.get(); } + + static bool classof(const ASTNode *node) { + return node->GetKind() == NodeKind::eArraySubscriptNode; + } + +private: + ASTNodeUP m_lhs; + ASTNodeUP m_rhs; +}; + /// 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 @@ -116,9 +158,13 @@ class Visitor { public: virtual ~Visitor() = default; virtual llvm::Expected + Visit(const ScalarLiteralNode *node) = 0; + virtual llvm::Expected Visit(const IdentifierNode *node) = 0; virtual llvm::Expected Visit(const UnaryOpNode *node) = 0; + virtual llvm::Expected + Visit(const ArraySubscriptNode *node) = 0; }; } // namespace lldb_private::dil diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h index b1dd3fdb49739..e3df80862b082 100644 --- a/lldb/include/lldb/ValueObject/DILEval.h +++ b/lldb/include/lldb/ValueObject/DILEval.h @@ -47,9 +47,15 @@ class Interpreter : Visitor { llvm::Expected Evaluate(const ASTNode *node); private: + llvm::Expected + Visit(const ScalarLiteralNode *node) override; llvm::Expected Visit(const IdentifierNode *node) override; llvm::Expected Visit(const UnaryOpNode *node) override; + llvm::Expected + Visit(const ArraySubscriptNode *node) override; + + lldb::ValueObjectSP PointerAdd(lldb::ValueObjectSP lhs, int64_t offset); // 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 3508b8b7a85c6..0176db73835e9 100644 --- a/lldb/include/lldb/ValueObject/DILLexer.h +++ b/lldb/include/lldb/ValueObject/DILLexer.h @@ -29,7 +29,10 @@ class Token { eof, identifier, l_paren, + l_square, + numeric_constant, r_paren, + r_square, star, }; diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h index f5c00b1040ef4..af237ece0228d 100644 --- a/lldb/include/lldb/ValueObject/DILParser.h +++ b/lldb/include/lldb/ValueObject/DILParser.h @@ -84,12 +84,15 @@ class DILParser { ASTNodeUP ParseExpression(); ASTNodeUP ParseUnaryExpression(); + ASTNodeUP ParsePostfixExpression(); ASTNodeUP ParsePrimaryExpression(); std::string ParseNestedNameSpecifier(); std::string ParseIdExpression(); std::string ParseUnqualifiedId(); + ASTNodeUP ParseNumericLiteral(); + ASTNodeUP ParseNumericConstant(); void BailOut(const std::string &error, uint32_t loc, uint16_t err_len); diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp index ea847587501ee..ceb4a4aa99c4f 100644 --- a/lldb/source/ValueObject/DILAST.cpp +++ b/lldb/source/ValueObject/DILAST.cpp @@ -15,6 +15,11 @@ llvm::Expected ErrorNode::Accept(Visitor *v) const { llvm_unreachable("Attempting to Visit a DIL ErrorNode."); } +llvm::Expected +ScalarLiteralNode::Accept(Visitor *v) const { + return v->Visit(this); +} + llvm::Expected IdentifierNode::Accept(Visitor *v) const { return v->Visit(this); } @@ -23,4 +28,9 @@ llvm::Expected UnaryOpNode::Accept(Visitor *v) const { return v->Visit(this); } +llvm::Expected +ArraySubscriptNode::Accept(Visitor *v) const { + return v->Visit(this); +} + } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index 15a66d4866305..527017da7c019 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -18,6 +18,22 @@ namespace lldb_private::dil { +static lldb::ValueObjectSP +ArrayToPointerConversion(lldb::ValueObjectSP valobj, + std::shared_ptr ctx) { + assert(valobj->IsArrayType() && + "an argument to array-to-pointer conversion must be an array"); + + uint64_t addr = valobj->GetLoadAddress(); + llvm::StringRef name = "result"; + ExecutionContext exe_ctx; + ctx->CalculateExecutionContext(exe_ctx); + return ValueObject::CreateValueObjectFromAddress( + name, addr, exe_ctx, + valobj->GetCompilerType().GetArrayElementType(ctx.get()).GetPointerType(), + /* do_deref */ false); +} + static lldb::ValueObjectSP LookupStaticIdentifier( VariableList &variable_list, std::shared_ptr exe_scope, llvm::StringRef name_ref, llvm::StringRef unqualified_name) { @@ -214,6 +230,45 @@ llvm::Expected Interpreter::Evaluate(const ASTNode *node) { return value_or_error; } +static CompilerType GetBasicType(std::shared_ptr ctx, + lldb::BasicType basic_type) { + static std::unordered_map basic_types; + auto type = basic_types.find(basic_type); + if (type != basic_types.end()) { + std::string type_name((type->second).GetTypeName().AsCString()); + // Only return the found type if it's valid. + if (type_name != "") + return type->second; + } + + lldb::TargetSP target_sp = ctx->CalculateTarget(); + if (target_sp) { + for (auto type_system_sp : target_sp->GetScratchTypeSystems()) + if (auto compiler_type = + type_system_sp->GetBasicTypeFromAST(basic_type)) { + basic_types.insert({basic_type, compiler_type}); + return compiler_type; + } + } + CompilerType empty_type; + return empty_type; +} + +llvm::Expected +Interpreter::Visit(const ScalarLiteralNode *node) { + CompilerType result_type = GetBasicType(m_exe_ctx_scope, node->GetType()); + Scalar value = node->GetValue(); + + if (result_type.IsInteger() || result_type.IsNullPtrType() || + result_type.IsPointerType()) { + llvm::APInt val = value.GetAPSInt(); + return ValueObject::CreateValueObjectFromAPInt(m_target, val, result_type, + "result"); + } + + return lldb::ValueObjectSP(); +} + llvm::Expected Interpreter::Visit(const IdentifierNode *node) { lldb::DynamicValueType use_dynamic = m_default_dynamic; @@ -272,4 +327,108 @@ Interpreter::Visit(const UnaryOpNode *node) { m_expr, "invalid ast: unexpected binary operator", node->GetLocation()); } +lldb::ValueObjectSP Interpreter::PointerAdd(lldb::ValueObjectSP lhs, + int64_t offset) { + uint64_t byte_size = 0; + if (auto temp = lhs->GetCompilerType().GetPointeeType().GetByteSize( + lhs->GetTargetSP().get())) + byte_size = *temp; + uintptr_t addr = lhs->GetValueAsUnsigned(0) + offset * byte_size; + + llvm::StringRef name = "result"; + ExecutionContext exe_ctx(m_target.get(), false); + return ValueObject::CreateValueObjectFromAddress(name, addr, exe_ctx, + lhs->GetCompilerType(), + /* do_deref */ false); +} + +llvm::Expected +Interpreter::Visit(const ArraySubscriptNode *node) { + auto lhs_or_err = Evaluate(node->lhs()); + if (!lhs_or_err) { + return lhs_or_err; + } + lldb::ValueObjectSP base = *lhs_or_err; + auto rhs_or_err = Evaluate(node->rhs()); + if (!rhs_or_err) { + return rhs_or_err; + } + lldb::ValueObjectSP index = *rhs_or_err; + + Status error; + if (base->GetCompilerType().IsReferenceType()) { + base = base->Dereference(error); + if (error.Fail()) + return error.ToError(); + } + if (index->GetCompilerType().IsReferenceType()) { + index = index->Dereference(error); + if (error.Fail()) + return error.ToError(); + } + + auto index_type = index->GetCompilerType(); + if (!index_type.IsIntegerOrUnscopedEnumerationType()) + return llvm::make_error( + m_expr, "array subscript is not an integer", node->GetLocation()); + + // Check to see if 'base' has a synthetic value; if so, try using that. + if (base->HasSyntheticValue()) { + lldb::ValueObjectSP synthetic = base->GetSyntheticValue(); + if (synthetic && synthetic != base) { + uint32_t num_children = synthetic->GetNumChildrenIgnoringErrors(); + // Verify that the 'index' is not out-of-range for the declared type. + if (index->GetValueAsSigned(0) >= num_children) { + auto message = + llvm::formatv("array index {0} is not valid for \"({1}) {2}\"", + index->GetValueAsSigned(0), + base->GetTypeName().AsCString(""), + base->GetName().AsCString()); + return llvm::make_error(m_expr, message, + node->GetLocation()); + } + + uint64_t child_idx = index->GetValueAsUnsigned(0); + if (static_cast(child_idx) < + synthetic->GetNumChildrenIgnoringErrors()) { + lldb::ValueObjectSP child_valobj_sp = + synthetic->GetChildAtIndex(child_idx); + if (child_valobj_sp) { + return child_valobj_sp; + } + } + } + } + + auto base_type = base->GetCompilerType(); + if (!base_type.IsPointerType() && !base_type.IsArrayType()) + return llvm::make_error( + m_expr, "subscripted value is not an array or pointer", + node->GetLocation()); + if (base_type.IsPointerToVoid()) + return llvm::make_error( + m_expr, "subscript of pointer to incomplete type 'void'", + node->GetLocation()); + + if (base_type.IsArrayType()) + base = ArrayToPointerConversion(base, m_exe_ctx_scope); + + CompilerType item_type = base->GetCompilerType().GetPointeeType(); + lldb::addr_t base_addr = base->GetValueAsUnsigned(0); + + llvm::StringRef name = "result"; + ExecutionContext exe_ctx(m_target.get(), false); + // Create a pointer and add the index, i.e. "base + index". + lldb::ValueObjectSP value = + PointerAdd(ValueObject::CreateValueObjectFromAddress( + name, base_addr, exe_ctx, item_type.GetPointerType(), + /*do_deref=*/false), + index->GetValueAsSigned(0)); + + lldb::ValueObjectSP val2 = value->Dereference(error); + if (error.Fail()) + return error.ToError(); + return val2; +} + } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp index b9c2e7971e3b4..3222032feef19 100644 --- a/lldb/source/ValueObject/DILLexer.cpp +++ b/lldb/source/ValueObject/DILLexer.cpp @@ -13,6 +13,7 @@ #include "lldb/ValueObject/DILLexer.h" #include "lldb/Utility/Status.h" +#include "lldb/ValueObject/DILParser.h" #include "llvm/ADT/StringSwitch.h" namespace lldb_private::dil { @@ -29,8 +30,14 @@ llvm::StringRef Token::GetTokenName(Kind kind) { return "identifier"; case Kind::l_paren: return "l_paren"; + case Kind::l_square: + return "l_square"; + case Kind::numeric_constant: + return "numeric_constant"; case Kind::r_paren: return "r_paren"; + case Kind::r_square: + return "r_square"; case Token::star: return "star"; } @@ -57,6 +64,29 @@ static std::optional IsWord(llvm::StringRef expr, return candidate; } +static void ConsumeNumberBody(char &prev_ch, llvm::StringRef::iterator &cur_pos, + llvm::StringRef expr) { + while (cur_pos != expr.end() && + (IsDigit(*cur_pos) || IsLetter(*cur_pos) || *cur_pos == '_')) { + prev_ch = *cur_pos; + cur_pos++; + } +} + +static std::optional IsNumber(llvm::StringRef expr, + llvm::StringRef &remainder) { + llvm::StringRef::iterator cur_pos = remainder.begin(); + llvm::StringRef::iterator start = cur_pos; + char prev_ch = 0; + if (IsDigit(*start)) { + ConsumeNumberBody(prev_ch, cur_pos, expr); + llvm::StringRef number = expr.substr(start - expr.begin(), cur_pos - start); + if (remainder.consume_front(number)) + return number; + } + return std::nullopt; +} + llvm::Expected DILLexer::Create(llvm::StringRef expr) { std::vector tokens; llvm::StringRef remainder = expr; @@ -81,13 +111,19 @@ llvm::Expected DILLexer::Lex(llvm::StringRef expr, return Token(Token::eof, "", (uint32_t)expr.size()); uint32_t position = cur_pos - expr.begin(); + std::optional maybe_number = IsNumber(expr, remainder); + if (maybe_number) { + std::string number = (*maybe_number).str(); + return Token(Token::numeric_constant, number, position); + } std::optional maybe_word = IsWord(expr, remainder); if (maybe_word) return Token(Token::identifier, maybe_word->str(), position); constexpr std::pair operators[] = { - {Token::amp, "&"}, {Token::coloncolon, "::"}, {Token::l_paren, "("}, - {Token::r_paren, ")"}, {Token::star, "*"}, + {Token::amp, "&"}, {Token::coloncolon, "::"}, {Token::l_paren, "("}, + {Token::l_square, "["}, {Token::r_paren, ")"}, {Token::r_square, "]"}, + {Token::star, "*"}, }; for (auto [kind, str] : operators) { if (remainder.consume_front(str)) @@ -95,7 +131,8 @@ llvm::Expected DILLexer::Lex(llvm::StringRef expr, } // Unrecognized character(s) in string; unable to lex it. - return llvm::createStringError("Unable to lex input string"); + return llvm::make_error(expr, "unrecognized token", + position); } } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp index 2c78eae8cf6bf..24a6f0c909a0a 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -111,7 +111,36 @@ ASTNodeUP DILParser::ParseUnaryExpression() { llvm_unreachable("invalid token kind"); } } - return ParsePrimaryExpression(); + return ParsePostfixExpression(); +} + +// Parse a postfix_expression. +// +// postfix_expression: +// primary_expression +// postfix_expression "[" expression "]" +// +ASTNodeUP DILParser::ParsePostfixExpression() { + ASTNodeUP lhs = ParsePrimaryExpression(); + while (CurToken().Is(Token::l_square)) { + uint32_t loc = CurToken().GetLocation(); + Token token = CurToken(); + switch (token.GetKind()) { + case Token::l_square: { + m_dil_lexer.Advance(); + auto rhs = ParseExpression(); + Expect(Token::r_square); + m_dil_lexer.Advance(); + lhs = std::make_unique(loc, std::move(lhs), + std::move(rhs)); + break; + } + default: + llvm_unreachable("invalid token"); + } + } + + return lhs; } // Parse a primary_expression. @@ -121,6 +150,8 @@ ASTNodeUP DILParser::ParseUnaryExpression() { // "(" expression ")" // ASTNodeUP DILParser::ParsePrimaryExpression() { + if (CurToken().Is(Token::numeric_constant)) + return ParseNumericLiteral(); if (CurToken().IsOneOf({Token::coloncolon, Token::identifier})) { // Save the source location for the diagnostics message. uint32_t loc = CurToken().GetLocation(); @@ -280,6 +311,52 @@ void DILParser::BailOut(const std::string &error, uint32_t loc, m_dil_lexer.ResetTokenIdx(m_dil_lexer.NumLexedTokens() - 1); } +// Parse a numeric_literal. +// +// numeric_literal: +// ? Token::numeric_constant ? +// +ASTNodeUP DILParser::ParseNumericLiteral() { + Expect(Token::numeric_constant); + ASTNodeUP numeric_constant = ParseNumericConstant(); + if (numeric_constant->GetKind() == NodeKind::eErrorNode) { + BailOut(llvm::formatv("Failed to parse token as numeric-constant: {0}", + CurToken()), + CurToken().GetLocation(), CurToken().GetSpelling().length()); + return std::make_unique(); + } + m_dil_lexer.Advance(); + return numeric_constant; +} + +static constexpr std::pair type_suffixes[] = { + {"ull", lldb::eBasicTypeUnsignedLongLong}, + {"ul", lldb::eBasicTypeUnsignedLong}, + {"u", lldb::eBasicTypeUnsignedInt}, + {"ll", lldb::eBasicTypeLongLong}, + {"l", lldb::eBasicTypeLong}, +}; + +ASTNodeUP DILParser::ParseNumericConstant() { + Token token = CurToken(); + auto spelling = token.GetSpelling(); + llvm::StringRef spelling_ref = spelling; + lldb::BasicType type = lldb::eBasicTypeInt; + for (auto [suffix, t] : type_suffixes) { + if (spelling_ref.consume_back_insensitive(suffix)) { + type = t; + break; + } + } + llvm::APInt raw_value; + if (!spelling_ref.getAsInteger(0, raw_value)) { + Scalar scalar_value(raw_value); + return std::make_unique(token.GetLocation(), type, + scalar_value); + } + return std::make_unique(); +} + void DILParser::Expect(Token::Kind kind) { if (CurToken().IsNot(kind)) { BailOut(llvm::formatv("expected {0}, got: {1}", kind, CurToken()), diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/Makefile b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py new file mode 100644 index 0000000000000..e142889124613 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py @@ -0,0 +1,88 @@ +""" +Test DIL array subscript. +""" + +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +from lldbsuite.test import lldbutil + + +class TestFrameVarDILGlobalVariableLookup(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def expect_var_path(self, expr, compare_to_framevar=False, value=None, type=None): + value_dil = super().expect_var_path(expr, value=value, type=type) + if compare_to_framevar: + self.runCmd("settings set target.experimental.use-DIL false") + value_frv = super().expect_var_path(expr, value=value, type=type) + self.runCmd("settings set target.experimental.use-DIL true") + self.assertEqual(value_dil.GetValue(), value_frv.GetValue()) + + def test_dereference(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp") + ) + + self.runCmd("settings set target.experimental.use-DIL true") + + # Test int[] and int* + self.expect_var_path("int_arr[0]", True, value="1") + self.expect_var_path("int_ptr[1]", True, value="2") + self.expect_var_path("int_arr[enum_one]", value="2") + + # Test when base and index are references. + self.expect_var_path("int_arr[0]", True, value="1") + self.expect_var_path("int_arr[idx_1_ref]", value="2") + self.expect_var_path("int_arr[enum_ref]", value="2") + self.expect_var_path("int_arr_ref[0]", value="1") + self.expect_var_path("int_arr_ref[idx_1_ref]", value="2") + self.expect_var_path("int_arr_ref[enum_ref]", value="2") + + # Test when base and index are typedefs. + self.expect_var_path("td_int_arr[0]", True, value="1") + self.expect_var_path("td_int_arr[td_int_idx_1]", value="2") + self.expect_var_path("td_int_arr[td_td_int_idx_2]", value="3") + self.expect_var_path("td_int_ptr[0]", True, value="1") + self.expect_var_path("td_int_ptr[td_int_idx_1]", value="2") + self.expect_var_path("td_int_ptr[td_td_int_idx_2]", value="3") + + # Both typedefs and refs + self.expect_var_path("td_int_arr_ref[td_int_idx_1_ref]", value="2") + + # Test for index out of bounds. + self.expect_var_path("int_arr[42]", True, type="int") + self.expect_var_path("int_arr[100]", True, type="int") + + # Test address-of of the subscripted value. + self.expect_var_path("*(&int_arr[1])", value="2") + + # Test synthetic value subscription + self.expect_var_path("vector[1]", value="2") + + # Test for negative index. + self.expect( + "frame var 'int_arr[-1]'", + error=True, + substrs=["unrecognized token"], + ) + + # Test for floating point index + self.expect( + "frame var 'int_arr[1.0]'", + error=True, + substrs=["unrecognized token"], + ) + + # Base should be a "pointer to T" and index should be of an integral type. + self.expect( + "frame var 'int_arr[int_ptr]'", + error=True, + substrs=["array subscript is not an integer"], + ) + self.expect( + "frame var '1[2]'", + error=True, + substrs=["subscripted value is not an array or pointer"], + ) diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp new file mode 100644 index 0000000000000..b34e4670b9db6 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp @@ -0,0 +1,31 @@ +#include + +int main(int argc, char **argv) { + int int_arr[] = {1, 2, 3}; + int *int_ptr = int_arr; + int(&int_arr_ref)[3] = int_arr; + + int idx_1 = 1; + const int &idx_1_ref = idx_1; + + typedef int td_int_t; + typedef td_int_t td_td_int_t; + typedef int *td_int_ptr_t; + typedef int &td_int_ref_t; + + td_int_t td_int_idx_1 = 1; + td_td_int_t td_td_int_idx_2 = 2; + + td_int_t td_int_arr[3] = {1, 2, 3}; + td_int_ptr_t td_int_ptr = td_int_arr; + + td_int_ref_t td_int_idx_1_ref = td_int_idx_1; + td_int_t(&td_int_arr_ref)[3] = td_int_arr; + + enum Enum { kZero, kOne } enum_one = kOne; + Enum &enum_ref = enum_one; + + std::vector vector = {1, 2, 3}; + + return 0; // Set a breakpoint here +} diff --git a/lldb/unittests/ValueObject/DILLexerTests.cpp b/lldb/unittests/ValueObject/DILLexerTests.cpp index 9afa957901ae7..203763b91afc4 100644 --- a/lldb/unittests/ValueObject/DILLexerTests.cpp +++ b/lldb/unittests/ValueObject/DILLexerTests.cpp @@ -121,11 +121,11 @@ TEST(DILLexerTests, IdentifiersTest) { "a_b", "this", "self", "a", "MyName", "namespace"}; // The lexer can lex these strings, but they should not be identifiers. - std::vector invalid_identifiers = {"", "::", "(", ")"}; + std::vector invalid_identifiers = {"", "::", "(", ")", "0abc"}; // The lexer is expected to fail attempting to lex these strings (it cannot // create valid tokens out of them). - std::vector invalid_tok_strings = {"234", "2a", "2", "1MyName"}; + std::vector invalid_tok_strings = {"#include", "a at a"}; // Verify that all of the valid identifiers come out as identifier tokens. for (auto &str : valid_identifiers) { @@ -150,7 +150,33 @@ TEST(DILLexerTests, IdentifiersTest) { DILLexer lexer(*maybe_lexer); Token token = lexer.GetCurrentToken(); EXPECT_TRUE(token.IsNot(Token::identifier)); - EXPECT_TRUE(token.IsOneOf( - {Token::eof, Token::coloncolon, Token::l_paren, Token::r_paren})); + EXPECT_TRUE(token.IsOneOf({Token::eof, Token::coloncolon, Token::l_paren, + Token::r_paren, Token::numeric_constant})); + } +} + +TEST(DILLexerTests, NumbersTest) { + // These strings should lex into number tokens. + std::vector valid_numbers = {"123", "0x123", "0123", "0b101", + "1_000"}; + + // The lexer can lex these strings, but they should not be numbers. + std::vector invalid_numbers = {"", "x123", "b123"}; + + for (auto &str : valid_numbers) { + SCOPED_TRACE(str); + EXPECT_THAT_EXPECTED(ExtractTokenData(str), + llvm::HasValue(testing::ElementsAre( + testing::Pair(Token::numeric_constant, str)))); + } + // Verify that none of the invalid numbers come out as numeric tokens. + for (auto &str : invalid_numbers) { + SCOPED_TRACE(str); + llvm::Expected maybe_lexer = DILLexer::Create(str); + EXPECT_THAT_EXPECTED(maybe_lexer, llvm::Succeeded()); + DILLexer lexer(*maybe_lexer); + Token token = lexer.GetCurrentToken(); + EXPECT_TRUE(token.IsNot(Token::numeric_constant)); + EXPECT_TRUE(token.IsOneOf({Token::eof, Token::identifier})); } } >From df1ef931315375ba9e8c15c092f8548f24afc1ce Mon Sep 17 00:00:00 2001 From: Ilia Kuklin Date: Tue, 6 May 2025 23:07:12 +0500 Subject: [PATCH 2/3] Use `GetSyntheticArrayMember` to subscript pointers and out of bounds index from arrays --- lldb/include/lldb/ValueObject/DILEval.h | 2 - lldb/source/ValueObject/DILEval.cpp | 71 +++++-------------------- 2 files changed, 14 insertions(+), 59 deletions(-) diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h index e3df80862b082..a03b1a138798f 100644 --- a/lldb/include/lldb/ValueObject/DILEval.h +++ b/lldb/include/lldb/ValueObject/DILEval.h @@ -55,8 +55,6 @@ class Interpreter : Visitor { llvm::Expected Visit(const ArraySubscriptNode *node) override; - lldb::ValueObjectSP PointerAdd(lldb::ValueObjectSP lhs, int64_t offset); - // Used by the interpreter to create objects, perform casts, etc. lldb::TargetSP m_target; llvm::StringRef m_expr; diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index 527017da7c019..a3e1380ea1de4 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -18,22 +18,6 @@ namespace lldb_private::dil { -static lldb::ValueObjectSP -ArrayToPointerConversion(lldb::ValueObjectSP valobj, - std::shared_ptr ctx) { - assert(valobj->IsArrayType() && - "an argument to array-to-pointer conversion must be an array"); - - uint64_t addr = valobj->GetLoadAddress(); - llvm::StringRef name = "result"; - ExecutionContext exe_ctx; - ctx->CalculateExecutionContext(exe_ctx); - return ValueObject::CreateValueObjectFromAddress( - name, addr, exe_ctx, - valobj->GetCompilerType().GetArrayElementType(ctx.get()).GetPointerType(), - /* do_deref */ false); -} - static lldb::ValueObjectSP LookupStaticIdentifier( VariableList &variable_list, std::shared_ptr exe_scope, llvm::StringRef name_ref, llvm::StringRef unqualified_name) { @@ -327,21 +311,6 @@ Interpreter::Visit(const UnaryOpNode *node) { m_expr, "invalid ast: unexpected binary operator", node->GetLocation()); } -lldb::ValueObjectSP Interpreter::PointerAdd(lldb::ValueObjectSP lhs, - int64_t offset) { - uint64_t byte_size = 0; - if (auto temp = lhs->GetCompilerType().GetPointeeType().GetByteSize( - lhs->GetTargetSP().get())) - byte_size = *temp; - uintptr_t addr = lhs->GetValueAsUnsigned(0) + offset * byte_size; - - llvm::StringRef name = "result"; - ExecutionContext exe_ctx(m_target.get(), false); - return ValueObject::CreateValueObjectFromAddress(name, addr, exe_ctx, - lhs->GetCompilerType(), - /* do_deref */ false); -} - llvm::Expected Interpreter::Visit(const ArraySubscriptNode *node) { auto lhs_or_err = Evaluate(node->lhs()); @@ -373,22 +342,21 @@ Interpreter::Visit(const ArraySubscriptNode *node) { m_expr, "array subscript is not an integer", node->GetLocation()); // Check to see if 'base' has a synthetic value; if so, try using that. + uint64_t child_idx = index->GetValueAsUnsigned(0); if (base->HasSyntheticValue()) { lldb::ValueObjectSP synthetic = base->GetSyntheticValue(); if (synthetic && synthetic != base) { uint32_t num_children = synthetic->GetNumChildrenIgnoringErrors(); // Verify that the 'index' is not out-of-range for the declared type. - if (index->GetValueAsSigned(0) >= num_children) { - auto message = - llvm::formatv("array index {0} is not valid for \"({1}) {2}\"", - index->GetValueAsSigned(0), - base->GetTypeName().AsCString(""), - base->GetName().AsCString()); + if (child_idx >= num_children) { + auto message = llvm::formatv( + "array index {0} is not valid for \"({1}) {2}\"", child_idx, + base->GetTypeName().AsCString(""), + base->GetName().AsCString()); return llvm::make_error(m_expr, message, node->GetLocation()); } - uint64_t child_idx = index->GetValueAsUnsigned(0); if (static_cast(child_idx) < synthetic->GetNumChildrenIgnoringErrors()) { lldb::ValueObjectSP child_valobj_sp = @@ -410,25 +378,14 @@ Interpreter::Visit(const ArraySubscriptNode *node) { m_expr, "subscript of pointer to incomplete type 'void'", node->GetLocation()); - if (base_type.IsArrayType()) - base = ArrayToPointerConversion(base, m_exe_ctx_scope); - - CompilerType item_type = base->GetCompilerType().GetPointeeType(); - lldb::addr_t base_addr = base->GetValueAsUnsigned(0); - - llvm::StringRef name = "result"; - ExecutionContext exe_ctx(m_target.get(), false); - // Create a pointer and add the index, i.e. "base + index". - lldb::ValueObjectSP value = - PointerAdd(ValueObject::CreateValueObjectFromAddress( - name, base_addr, exe_ctx, item_type.GetPointerType(), - /*do_deref=*/false), - index->GetValueAsSigned(0)); - - lldb::ValueObjectSP val2 = value->Dereference(error); - if (error.Fail()) - return error.ToError(); - return val2; + if (base_type.IsArrayType()) { + uint32_t num_children = base->GetNumChildrenIgnoringErrors(); + if (child_idx < num_children) + return base->GetChildAtIndex(child_idx); + } + + int64_t signed_child_idx = index->GetValueAsSigned(0); + return base->GetSyntheticArrayMember(signed_child_idx, true); } } // namespace lldb_private::dil >From 05da91b5a9fa77570c5157068bc858f4359e6d3a Mon Sep 17 00:00:00 2001 From: Ilia Kuklin Date: Tue, 6 May 2025 23:22:47 +0500 Subject: [PATCH 3/3] Rename getters, refactor number lexing and remove '_' --- lldb/include/lldb/ValueObject/DILAST.h | 8 ++++---- lldb/source/ValueObject/DILEval.cpp | 8 ++++---- lldb/source/ValueObject/DILLexer.cpp | 21 +++++--------------- lldb/unittests/ValueObject/DILLexerTests.cpp | 3 +-- 4 files changed, 14 insertions(+), 26 deletions(-) diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h index 6908deed7aee3..b6deff19a2f8b 100644 --- a/lldb/include/lldb/ValueObject/DILAST.h +++ b/lldb/include/lldb/ValueObject/DILAST.h @@ -118,8 +118,8 @@ class UnaryOpNode : public ASTNode { llvm::Expected Accept(Visitor *v) const override; - UnaryOpKind kind() const { return m_kind; } - ASTNode *operand() const { return m_operand.get(); } + UnaryOpKind GetKind() const { return m_kind; } + ASTNode *GetOperand() const { return m_operand.get(); } static bool classof(const ASTNode *node) { return node->GetKind() == NodeKind::eUnaryOpNode; @@ -138,8 +138,8 @@ class ArraySubscriptNode : public ASTNode { llvm::Expected Accept(Visitor *v) const override; - ASTNode *lhs() const { return m_lhs.get(); } - ASTNode *rhs() const { return m_rhs.get(); } + ASTNode *GetLHS() const { return m_lhs.get(); } + ASTNode *GetRHS() const { return m_rhs.get(); } static bool classof(const ASTNode *node) { return node->GetKind() == NodeKind::eArraySubscriptNode; diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index a3e1380ea1de4..604f9da777223 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -276,13 +276,13 @@ Interpreter::Visit(const IdentifierNode *node) { llvm::Expected Interpreter::Visit(const UnaryOpNode *node) { Status error; - auto rhs_or_err = Evaluate(node->operand()); + auto rhs_or_err = Evaluate(node->GetOperand()); if (!rhs_or_err) return rhs_or_err; lldb::ValueObjectSP rhs = *rhs_or_err; - switch (node->kind()) { + switch (node->GetKind()) { case UnaryOpKind::Deref: { lldb::ValueObjectSP dynamic_rhs = rhs->GetDynamicValue(m_default_dynamic); if (dynamic_rhs) @@ -313,12 +313,12 @@ Interpreter::Visit(const UnaryOpNode *node) { llvm::Expected Interpreter::Visit(const ArraySubscriptNode *node) { - auto lhs_or_err = Evaluate(node->lhs()); + auto lhs_or_err = Evaluate(node->GetLHS()); if (!lhs_or_err) { return lhs_or_err; } lldb::ValueObjectSP base = *lhs_or_err; - auto rhs_or_err = Evaluate(node->rhs()); + auto rhs_or_err = Evaluate(node->GetRHS()); if (!rhs_or_err) { return rhs_or_err; } diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp index 3222032feef19..6e41ff50bd571 100644 --- a/lldb/source/ValueObject/DILLexer.cpp +++ b/lldb/source/ValueObject/DILLexer.cpp @@ -64,25 +64,14 @@ static std::optional IsWord(llvm::StringRef expr, return candidate; } -static void ConsumeNumberBody(char &prev_ch, llvm::StringRef::iterator &cur_pos, - llvm::StringRef expr) { - while (cur_pos != expr.end() && - (IsDigit(*cur_pos) || IsLetter(*cur_pos) || *cur_pos == '_')) { - prev_ch = *cur_pos; - cur_pos++; - } -} +static bool IsNumberBodyChar(char ch) { return IsDigit(ch) || IsLetter(ch); } static std::optional IsNumber(llvm::StringRef expr, llvm::StringRef &remainder) { - llvm::StringRef::iterator cur_pos = remainder.begin(); - llvm::StringRef::iterator start = cur_pos; - char prev_ch = 0; - if (IsDigit(*start)) { - ConsumeNumberBody(prev_ch, cur_pos, expr); - llvm::StringRef number = expr.substr(start - expr.begin(), cur_pos - start); - if (remainder.consume_front(number)) - return number; + if (IsDigit(remainder[0])) { + llvm::StringRef number = remainder.take_while(IsNumberBodyChar); + remainder = remainder.drop_front(number.size()); + return number; } return std::nullopt; } diff --git a/lldb/unittests/ValueObject/DILLexerTests.cpp b/lldb/unittests/ValueObject/DILLexerTests.cpp index 203763b91afc4..f65034c1dbea3 100644 --- a/lldb/unittests/ValueObject/DILLexerTests.cpp +++ b/lldb/unittests/ValueObject/DILLexerTests.cpp @@ -157,8 +157,7 @@ TEST(DILLexerTests, IdentifiersTest) { TEST(DILLexerTests, NumbersTest) { // These strings should lex into number tokens. - std::vector valid_numbers = {"123", "0x123", "0123", "0b101", - "1_000"}; + std::vector valid_numbers = {"123", "0x123", "0123", "0b101"}; // The lexer can lex these strings, but they should not be numbers. std::vector invalid_numbers = {"", "x123", "b123"}; From lldb-commits at lists.llvm.org Tue May 6 11:49:32 2025 From: lldb-commits at lists.llvm.org (Ilia Kuklin via lldb-commits) Date: Tue, 06 May 2025 11:49:32 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <681a59bc.630a0220.20324f.53c1@mx.google.com> kuilpd wrote: > I don't have an answer to that, but I do know that it's possible to index pointers in the current implementation. I suggest checking out how it achieves that and seeing if it can be translated to here. I found `GetSyntheticArrayMember`, hopefully this is the one you're referring to. https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Tue May 6 11:55:40 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Tue, 06 May 2025 11:55:40 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Change the launch sequence (PR #138219) In-Reply-To: Message-ID: <681a5b2c.630a0220.216826.49c7@mx.google.com> kusmour wrote: Unblocked! > @kusmour Please take another look. In the current state all the test pass (reliably) locally and the linux bot seems happy too. Not sure if we want to keep the test disabled for now or turn them back on and see what the reliability is like on the bots. I don't know much about the build bot infra but looks like the only way to get the tests running is to land an enablement patch? Looks like a bunch of tests got disabled today: https://github.com/llvm/llvm-project/issues/137660 I don't think we should keep ignoring tests if we want to land this tho. I am all for re-enable them with the risk of reverting. But I will let others to chime in. cc. @IanWood1 @felipepiovezan https://github.com/llvm/llvm-project/pull/138219 From lldb-commits at lists.llvm.org Tue May 6 13:03:17 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Tue, 06 May 2025 13:03:17 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Change the launch sequence (PR #138219) In-Reply-To: Message-ID: <681a6b05.050a0220.17264b.dc83@mx.google.com> JDevlieghere wrote: > Unblocked! Thanks! > > @kusmour Please take another look. In the current state all the test pass (reliably) locally and the linux bot seems happy too. Not sure if we want to keep the test disabled for now or turn them back on and see what the reliability is like on the bots. > > I don't know much about the build bot infra but looks like the only way to get the tests running is to land an enablement patch? We have pre-commit testing (which passed) but that doesn't say much about the reliability. We'll need to enable them to see what the impact is on the post-commit CI. I think I'll split off the test-reenablement into a separate PR so we don't have to revert the whole thing here. > Looks like a bunch of tests got disabled today: #137660 I don't think we should keep ignoring tests if we want to land this tho. I am all for re-enable them with the risk of reverting. But I will let others to chime in. > > cc. @IanWood1 @felipepiovezan I don't t think those got disabled today, they're cherrypicks on forks from the upstream commits (the ones I mentioned on Discouse): - [[lldb][test] Disable a bunch of flakey lldb-dap tests · llvm/llvm-project at 2dbab4c · GitHub](https://github.com/llvm/llvm-project/commit/2dbab4ca8ddb218af555d8d1fd86b72612387582) - [[lldb] Disable TestDAP_attachByPortNum · llvm/llvm-project at b73169a · GitHub](https://github.com/llvm/llvm-project/commit/b73169ad3edc56fe2b4f5bd3d5259310168da50c) - [[lldb] Disable TestDAP_attach · llvm/llvm-project at 55b4e5e · GitHub](https://github.com/llvm/llvm-project/commit/55b4e5e5154550ba981af08ca9bd1e3da00e6fea) https://github.com/llvm/llvm-project/pull/138219 From lldb-commits at lists.llvm.org Tue May 6 14:01:56 2025 From: lldb-commits at lists.llvm.org (Ely Ronnen via lldb-commits) Date: Tue, 06 May 2025 14:01:56 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Change synthetic symbol names to have file address (PR #138416) In-Reply-To: Message-ID: <681a78c4.170a0220.c7e4f.4229@mx.google.com> https://github.com/eronnen updated https://github.com/llvm/llvm-project/pull/138416 >From 3ec9e1962940799ab291aaa8455e6f76da02af0f Mon Sep 17 00:00:00 2001 From: Ely Ronnen Date: Sun, 27 Apr 2025 13:48:45 +0200 Subject: [PATCH 1/6] Change ___lldb_unnamed_symbol generated names to have the file address --- lldb/source/Symbol/Symbol.cpp | 4 +++- lldb/test/Shell/ObjectFile/ELF/eh_frame-symbols.yaml | 4 ++-- .../test/Shell/SymbolFile/Breakpad/symtab-sorted-by-size.test | 2 +- lldb/test/Shell/SymbolFile/Breakpad/symtab.test | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lldb/source/Symbol/Symbol.cpp b/lldb/source/Symbol/Symbol.cpp index 4828de4fdfa37..da74707c75e13 100644 --- a/lldb/source/Symbol/Symbol.cpp +++ b/lldb/source/Symbol/Symbol.cpp @@ -639,7 +639,9 @@ void Symbol::SynthesizeNameIfNeeded() const { // breakpoints on them. llvm::SmallString<256> name; llvm::raw_svector_ostream os(name); - os << GetSyntheticSymbolPrefix() << GetID(); + os << GetSyntheticSymbolPrefix() << "_" + << llvm::format_hex_no_prefix( + m_addr_range.GetBaseAddress().GetFileAddress(), 0); m_mangled.SetDemangledName(ConstString(os.str())); } } diff --git a/lldb/test/Shell/ObjectFile/ELF/eh_frame-symbols.yaml b/lldb/test/Shell/ObjectFile/ELF/eh_frame-symbols.yaml index 0dcc9fb76bd4f..709c37e79d878 100644 --- a/lldb/test/Shell/ObjectFile/ELF/eh_frame-symbols.yaml +++ b/lldb/test/Shell/ObjectFile/ELF/eh_frame-symbols.yaml @@ -3,8 +3,8 @@ # CHECK: Index UserID DSX Type File Address/Value Load Address Size Flags Name # CHECK: [ 0] 1 SourceFile 0x0000000000000000 0x0000000000000000 0x00000004 - -# CHECK: [ 1] 2 SX Code 0x0000000000201180 0x0000000000000010 0x00000000 ___lldb_unnamed_symbol{{[0-9]*}} -# CHECK: [ 2] 3 SX Code 0x0000000000201190 0x0000000000000006 0x00000000 ___lldb_unnamed_symbol{{[0-9]*}} +# CHECK: [ 1] 2 SX Code 0x0000000000201180 0x0000000000000010 0x00000000 ___lldb_unnamed_symbol_{{[0-9a-f]*}} +# CHECK: [ 2] 3 SX Code 0x0000000000201190 0x0000000000000006 0x00000000 ___lldb_unnamed_symbol_{{[0-9a-f]*}} --- !ELF FileHeader: diff --git a/lldb/test/Shell/SymbolFile/Breakpad/symtab-sorted-by-size.test b/lldb/test/Shell/SymbolFile/Breakpad/symtab-sorted-by-size.test index 98052ea20bedd..00e04eb39a98e 100644 --- a/lldb/test/Shell/SymbolFile/Breakpad/symtab-sorted-by-size.test +++ b/lldb/test/Shell/SymbolFile/Breakpad/symtab-sorted-by-size.test @@ -3,7 +3,7 @@ # RUN: -s %s | FileCheck %s # CHECK: num_symbols = 4 (sorted by size): -# CHECK: [ 0] 0 SX Code 0x0000000000400000 0x00000000000000b0 0x00000000 ___lldb_unnamed_symbol0 +# CHECK: [ 0] 0 SX Code 0x0000000000400000 0x00000000000000b0 0x00000000 ___lldb_unnamed_symbol_400000 # CHECK: [ 1] 0 X Code 0x00000000004000d0 0x0000000000000022 0x00000000 _start # CHECK: [ 2] 0 X Code 0x00000000004000b0 0x0000000000000010 0x00000000 f1 # CHECK: [ 3] 0 X Code 0x00000000004000c0 0x0000000000000010 0x00000000 f2 diff --git a/lldb/test/Shell/SymbolFile/Breakpad/symtab.test b/lldb/test/Shell/SymbolFile/Breakpad/symtab.test index ef41bb3bea955..a32eb5808426f 100644 --- a/lldb/test/Shell/SymbolFile/Breakpad/symtab.test +++ b/lldb/test/Shell/SymbolFile/Breakpad/symtab.test @@ -5,7 +5,7 @@ # CHECK-LABEL: (lldb) image dump symtab symtab.out # CHECK: Symtab, file = {{.*}}symtab.out, num_symbols = 4: # CHECK: Index UserID DSX Type File Address/Value Load Address Size Flags Name -# CHECK: [ 0] 0 SX Code 0x0000000000400000 0x00000000000000b0 0x00000000 ___lldb_unnamed_symbol{{[0-9]*}} +# CHECK: [ 0] 0 SX Code 0x0000000000400000 0x00000000000000b0 0x00000000 ___lldb_unnamed_symbol_{{[0-9a-f]*}} # CHECK: [ 1] 0 X Code 0x00000000004000b0 0x0000000000000010 0x00000000 f1 # CHECK: [ 2] 0 X Code 0x00000000004000c0 0x0000000000000010 0x00000000 f2 # CHECK: [ 3] 0 X Code 0x00000000004000d0 0x0000000000000022 0x00000000 _start >From 3798aff067f8facbb386a3b3282975b23680447f Mon Sep 17 00:00:00 2001 From: Ely Ronnen Date: Sat, 3 May 2025 18:55:50 +0200 Subject: [PATCH 2/6] add test for looking up unnamed symbols --- .../python_api/unnamed_symbol_lookup/Makefile | 12 ++++++ .../TestUnnamedSymbolLookup.py | 40 +++++++++++++++++++ .../python_api/unnamed_symbol_lookup/main.c | 10 +++++ 3 files changed, 62 insertions(+) create mode 100644 lldb/test/API/python_api/unnamed_symbol_lookup/Makefile create mode 100644 lldb/test/API/python_api/unnamed_symbol_lookup/TestUnnamedSymbolLookup.py create mode 100644 lldb/test/API/python_api/unnamed_symbol_lookup/main.c diff --git a/lldb/test/API/python_api/unnamed_symbol_lookup/Makefile b/lldb/test/API/python_api/unnamed_symbol_lookup/Makefile new file mode 100644 index 0000000000000..9ba76898b61ba --- /dev/null +++ b/lldb/test/API/python_api/unnamed_symbol_lookup/Makefile @@ -0,0 +1,12 @@ +C_SOURCES := main.c + +include Makefile.rules + +all: a.out.stripped + +a.out.stripped: + $(STRIP) --keep-symbol=main -o a.out.stripped a.out + +ifneq "$(CODESIGN)" "" + $(CODESIGN) -fs - a.out.stripped +endif diff --git a/lldb/test/API/python_api/unnamed_symbol_lookup/TestUnnamedSymbolLookup.py b/lldb/test/API/python_api/unnamed_symbol_lookup/TestUnnamedSymbolLookup.py new file mode 100644 index 0000000000000..09d43a34c7e30 --- /dev/null +++ b/lldb/test/API/python_api/unnamed_symbol_lookup/TestUnnamedSymbolLookup.py @@ -0,0 +1,40 @@ +""" +Test lookup unnamed symbols. +""" + + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestUnnamedSymbolLookup(TestBase): + def test_unnamed_symbol_lookup(self): + """Test looking up unnamed symbol synthetic name""" + self.build() + (target, process, thread, bkpt) = lldbutil.run_to_name_breakpoint( + self, "main", exe_name="a.out.stripped" + ) + + main_frame = thread.GetFrameAtIndex(0) + + # Step until reaching the unnamed symbol called from main + for _ in range(100): + thread.StepInto() + if thread.GetFrameAtIndex(0) != main_frame: + break + + thread.StepInto() + + self.assertEqual( + main_frame, thread.GetFrameAtIndex(1), "Expected to be called from main" + ) + symbol = thread.GetFrameAtIndex(0).GetSymbol() + self.assertIsNotNone(symbol, "unnamed symbol called from main not reached") + self.assertTrue(symbol.name.startswith("___lldb_unnamed_symbol")) + + exe_module = symbol.GetStartAddress().GetModule() + found_symbols = exe_module.FindSymbols(symbol.name) + self.assertIsNotNone(found_symbols) + self.assertEqual(found_symbols.GetSize(), 1) diff --git a/lldb/test/API/python_api/unnamed_symbol_lookup/main.c b/lldb/test/API/python_api/unnamed_symbol_lookup/main.c new file mode 100644 index 0000000000000..1c9ce8e0e6697 --- /dev/null +++ b/lldb/test/API/python_api/unnamed_symbol_lookup/main.c @@ -0,0 +1,10 @@ +__attribute__((nodebug)) int stripped_function(int val) +{ + return val * val; +} + +int main (void) +{ + stripped_function(10); + return 0; +} >From 1ef05ed18e225388019d6008934f8d51fb8012bc Mon Sep 17 00:00:00 2001 From: Ely Ronnen Date: Sat, 3 May 2025 19:09:46 +0200 Subject: [PATCH 3/6] correctly finding unnamed symbols in symbol table with file address --- lldb/include/lldb/Symbol/Symbol.h | 2 +- lldb/source/Symbol/Symbol.cpp | 2 +- lldb/source/Symbol/Symtab.cpp | 11 ++++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lldb/include/lldb/Symbol/Symbol.h b/lldb/include/lldb/Symbol/Symbol.h index e05c845a69f3e..688c8a5931feb 100644 --- a/lldb/include/lldb/Symbol/Symbol.h +++ b/lldb/include/lldb/Symbol/Symbol.h @@ -258,7 +258,7 @@ class Symbol : public SymbolContextScope { bool ContainsFileAddress(lldb::addr_t file_addr) const; static llvm::StringRef GetSyntheticSymbolPrefix() { - return "___lldb_unnamed_symbol"; + return "___lldb_unnamed_symbol_"; } /// Decode a serialized version of this object from data. diff --git a/lldb/source/Symbol/Symbol.cpp b/lldb/source/Symbol/Symbol.cpp index da74707c75e13..d6689a647062a 100644 --- a/lldb/source/Symbol/Symbol.cpp +++ b/lldb/source/Symbol/Symbol.cpp @@ -639,7 +639,7 @@ void Symbol::SynthesizeNameIfNeeded() const { // breakpoints on them. llvm::SmallString<256> name; llvm::raw_svector_ostream os(name); - os << GetSyntheticSymbolPrefix() << "_" + os << GetSyntheticSymbolPrefix() << llvm::format_hex_no_prefix( m_addr_range.GetBaseAddress().GetFileAddress(), 0); m_mangled.SetDemangledName(ConstString(os.str())); diff --git a/lldb/source/Symbol/Symtab.cpp b/lldb/source/Symbol/Symtab.cpp index 9aee5d3e813d8..52545f22322d2 100644 --- a/lldb/source/Symbol/Symtab.cpp +++ b/lldb/source/Symbol/Symtab.cpp @@ -654,7 +654,7 @@ uint32_t Symtab::GetNameIndexes(ConstString symbol_name, if (count) return count; // Synthetic symbol names are not added to the name indexes, but they start - // with a prefix and end with a the symbol UserID. This allows users to find + // with a prefix and end with the symbol file address. This allows users to find // these symbols without having to add them to the name indexes. These // queries will not happen very often since the names don't mean anything, so // performance is not paramount in this case. @@ -663,11 +663,12 @@ uint32_t Symtab::GetNameIndexes(ConstString symbol_name, if (!name.consume_front(Symbol::GetSyntheticSymbolPrefix())) return 0; // Not a synthetic symbol name - // Extract the user ID from the symbol name - unsigned long long uid = 0; - if (getAsUnsignedInteger(name, /*Radix=*/10, uid)) + // Extract the file address from the symbol name + unsigned long long file_address = 0; + if (getAsUnsignedInteger(name, /*Radix=*/16, file_address)) return 0; // Failed to extract the user ID as an integer - Symbol *symbol = FindSymbolByID(uid); + + Symbol *symbol = FindSymbolAtFileAddress(static_cast(file_address)); if (symbol == nullptr) return 0; const uint32_t symbol_idx = GetIndexForSymbol(symbol); >From 2b233f3bc71253b9adb3329243a7a0575cf11083 Mon Sep 17 00:00:00 2001 From: Ely Ronnen Date: Sat, 3 May 2025 19:16:52 +0200 Subject: [PATCH 4/6] format --- lldb/source/Symbol/Symtab.cpp | 4 ++-- lldb/test/API/python_api/unnamed_symbol_lookup/main.c | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/lldb/source/Symbol/Symtab.cpp b/lldb/source/Symbol/Symtab.cpp index 52545f22322d2..9a3e8476fa356 100644 --- a/lldb/source/Symbol/Symtab.cpp +++ b/lldb/source/Symbol/Symtab.cpp @@ -654,8 +654,8 @@ uint32_t Symtab::GetNameIndexes(ConstString symbol_name, if (count) return count; // Synthetic symbol names are not added to the name indexes, but they start - // with a prefix and end with the symbol file address. This allows users to find - // these symbols without having to add them to the name indexes. These + // with a prefix and end with the symbol file address. This allows users to + // find these symbols without having to add them to the name indexes. These // queries will not happen very often since the names don't mean anything, so // performance is not paramount in this case. llvm::StringRef name = symbol_name.GetStringRef(); diff --git a/lldb/test/API/python_api/unnamed_symbol_lookup/main.c b/lldb/test/API/python_api/unnamed_symbol_lookup/main.c index 1c9ce8e0e6697..ec8350706b3c9 100644 --- a/lldb/test/API/python_api/unnamed_symbol_lookup/main.c +++ b/lldb/test/API/python_api/unnamed_symbol_lookup/main.c @@ -1,10 +1,6 @@ -__attribute__((nodebug)) int stripped_function(int val) -{ - return val * val; -} +__attribute__((nodebug)) int stripped_function(int val) { return val * val; } -int main (void) -{ +int main(void) { stripped_function(10); return 0; } >From bdfa13a20145f94516dc1ebafcf811448541e87e Mon Sep 17 00:00:00 2001 From: Ely Ronnen Date: Tue, 6 May 2025 10:31:08 +0200 Subject: [PATCH 5/6] try to fix unnamed_symbol_lookup test for mac --- lldb/test/API/python_api/unnamed_symbol_lookup/Makefile | 8 +++++++- .../API/python_api/unnamed_symbol_lookup/keep_symbols.txt | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 lldb/test/API/python_api/unnamed_symbol_lookup/keep_symbols.txt diff --git a/lldb/test/API/python_api/unnamed_symbol_lookup/Makefile b/lldb/test/API/python_api/unnamed_symbol_lookup/Makefile index 9ba76898b61ba..18d7caa9bc14d 100644 --- a/lldb/test/API/python_api/unnamed_symbol_lookup/Makefile +++ b/lldb/test/API/python_api/unnamed_symbol_lookup/Makefile @@ -4,8 +4,14 @@ include Makefile.rules all: a.out.stripped +ifeq "$(OS)" "Darwin" +STRIP_COMMAND = $(STRIP) -s keep_symbols.txt +else +STRIP_COMMAND = $(STRIP) --keep-symbol=main +endif + a.out.stripped: - $(STRIP) --keep-symbol=main -o a.out.stripped a.out + $(STRIP_COMMAND) -o a.out.stripped a.out ifneq "$(CODESIGN)" "" $(CODESIGN) -fs - a.out.stripped diff --git a/lldb/test/API/python_api/unnamed_symbol_lookup/keep_symbols.txt b/lldb/test/API/python_api/unnamed_symbol_lookup/keep_symbols.txt new file mode 100644 index 0000000000000..91c2d67a9329b --- /dev/null +++ b/lldb/test/API/python_api/unnamed_symbol_lookup/keep_symbols.txt @@ -0,0 +1 @@ +_main \ No newline at end of file >From 75ba84b5d6b6ced40095f2c2876089851e93b4c8 Mon Sep 17 00:00:00 2001 From: Ely Ronnen Date: Tue, 6 May 2025 23:01:40 +0200 Subject: [PATCH 6/6] try to fix unnamed_symbol_lookup test for mac --- lldb/test/API/python_api/unnamed_symbol_lookup/Makefile | 9 +++++++-- .../python_api/unnamed_symbol_lookup/keep_symbols.txt | 1 - 2 files changed, 7 insertions(+), 3 deletions(-) delete mode 100644 lldb/test/API/python_api/unnamed_symbol_lookup/keep_symbols.txt diff --git a/lldb/test/API/python_api/unnamed_symbol_lookup/Makefile b/lldb/test/API/python_api/unnamed_symbol_lookup/Makefile index 18d7caa9bc14d..4e29185e9dd59 100644 --- a/lldb/test/API/python_api/unnamed_symbol_lookup/Makefile +++ b/lldb/test/API/python_api/unnamed_symbol_lookup/Makefile @@ -10,8 +10,13 @@ else STRIP_COMMAND = $(STRIP) --keep-symbol=main endif -a.out.stripped: - $(STRIP_COMMAND) -o a.out.stripped a.out +a.out.stripped: a.out +ifeq "$(OS)" "Darwin" + echo "_main" > keep_symbols.txt + $(STRIP) -s keep_symbols.txt -o a.out.stripped a.out +else + $(STRIP) --keep-symbol=main -o a.out.stripped a.out +endif ifneq "$(CODESIGN)" "" $(CODESIGN) -fs - a.out.stripped diff --git a/lldb/test/API/python_api/unnamed_symbol_lookup/keep_symbols.txt b/lldb/test/API/python_api/unnamed_symbol_lookup/keep_symbols.txt deleted file mode 100644 index 91c2d67a9329b..0000000000000 --- a/lldb/test/API/python_api/unnamed_symbol_lookup/keep_symbols.txt +++ /dev/null @@ -1 +0,0 @@ -_main \ No newline at end of file From lldb-commits at lists.llvm.org Tue May 6 14:05:05 2025 From: lldb-commits at lists.llvm.org (Ely Ronnen via lldb-commits) Date: Tue, 06 May 2025 14:05:05 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Change synthetic symbol names to have file address (PR #138416) In-Reply-To: Message-ID: <681a7981.170a0220.18b641.d795@mx.google.com> eronnen wrote: @JDevlieghere thanks for taking the time to test it! I attempted to fix it but still not sure it's good :| https://github.com/llvm/llvm-project/pull/138416 From lldb-commits at lists.llvm.org Tue May 6 14:09:23 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Tue, 06 May 2025 14:09:23 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream Python scripts (PR #138028) In-Reply-To: Message-ID: <681a7a83.170a0220.369cb0.469e@mx.google.com> ================ @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +# Usage: convert-lldb-header-to-rpc-header.py +# This scripts takes common LLDB headers (such as lldb-defines.h) and replaces references to LLDB +# with those for RPC. This happens for: +# - namespace definitions +# - namespace usage +# - version string macros +# - ifdef/ifndef lines + +import argparse +import os +import re + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("input") + parser.add_argument("output") + args = parser.parse_args() + input_path = str(args.input) + output_path = str(args.output) + with open(input_path, "r") as input_file: + lines = input_file.readlines() + + with open(output_path, "w") as output_file: + for line in lines: + # NOTE: We do not use lldb-forward.h or lldb-versioning.h in RPC, so remove + # all includes that are found for these files. + if re.match( + r'#include "lldb/lldb-forward|#include "lldb/lldb-versioning', line + ): + continue + # For lldb-rpc-defines.h, replace the ifndef LLDB_LLDB_ portion with LLDB_RPC_ as we're not + # using LLDB private definitions in RPC. + elif re.match(r".+LLDB_LLDB_", line): + output_file.write(re.sub(r"LLDB_LLDB_", r"LLDB_RPC_", line)) + # Similarly to lldb-rpc-defines.h, replace the ifndef for LLDB_API in SBDefines.h to LLDB_RPC_API_ for the same reason. + elif re.match(r".+LLDB_API_", line): + output_file.write(re.sub(r"LLDB_API_", r"LLDB_RPC_API_", line)) ---------------- chelcassanova wrote: I looked at running only the `sub` and I think I'll actually keep it as is here. The reason for that is because the general pattern here if "if we need to change the line then change it, otherwise just write whatever was in the original input file". Running only the `sub` without if clauses makes it harder to know when we need to just use the original input file line. I'd have preferred for this to be a `switch` statement (or `case` in Python's case) but IIUC the current version of Python that we support hasn't caught up to using that statement yet. https://github.com/llvm/llvm-project/pull/138028 From lldb-commits at lists.llvm.org Tue May 6 14:11:52 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Tue, 06 May 2025 14:11:52 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Use llvm::bit_ceil (NFC) (PR #138723) In-Reply-To: Message-ID: <681a7b18.170a0220.2888b3.2eb0@mx.google.com> https://github.com/jasonmolenda approved this pull request. Thanks! https://github.com/llvm/llvm-project/pull/138723 From lldb-commits at lists.llvm.org Tue May 6 14:14:06 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Tue, 06 May 2025 14:14:06 -0700 (PDT) Subject: [Lldb-commits] [lldb] 5c3ef62 - [lldb] Use llvm::bit_ceil (NFC) (#138723) Message-ID: <681a7b9e.170a0220.2dddaf.3cf9@mx.google.com> Author: Kazu Hirata Date: 2025-05-06T14:14:03-07:00 New Revision: 5c3ef62f64cc379b8c5a4cf23bcaf019d398fcc4 URL: https://github.com/llvm/llvm-project/commit/5c3ef62f64cc379b8c5a4cf23bcaf019d398fcc4 DIFF: https://github.com/llvm/llvm-project/commit/5c3ef62f64cc379b8c5a4cf23bcaf019d398fcc4.diff LOG: [lldb] Use llvm::bit_ceil (NFC) (#138723) This patch replaces a local implementation of bit_ceil with llvm::bit_ceil. Technically, the local implementation evaluates to 0 on input 0, whereas llvm::bit_ceil evaluates to 1, but that doesn't matter because we have: // Can't watch zero bytes. if (user_size == 0) return {}; Added: Modified: lldb/source/Breakpoint/WatchpointAlgorithms.cpp Removed: ################################################################################ diff --git a/lldb/source/Breakpoint/WatchpointAlgorithms.cpp b/lldb/source/Breakpoint/WatchpointAlgorithms.cpp index 3caf29b04317f..d65de13db1dad 100644 --- a/lldb/source/Breakpoint/WatchpointAlgorithms.cpp +++ b/lldb/source/Breakpoint/WatchpointAlgorithms.cpp @@ -58,16 +58,6 @@ WatchpointAlgorithms::AtomizeWatchpointRequest( return resources; } -// This should be `std::bit_ceil(aligned_size)` but -// that requires C++20. -// Calculates the smallest integral power of two that is not smaller than x. -static uint64_t bit_ceil(uint64_t input) { - if (input <= 1 || llvm::popcount(input) == 1) - return input; - - return 1ULL << (64 - llvm::countl_zero(input)); -} - /// Convert a user's watchpoint request (\a user_addr and \a user_size) /// into hardware watchpoints, for a target that can watch a power-of-2 /// region of memory (1, 2, 4, 8, etc), aligned to that same power-of-2 @@ -102,7 +92,7 @@ WatchpointAlgorithms::PowerOf2Watchpoints(addr_t user_addr, size_t user_size, /// Round up \a user_size to the next power-of-2 size /// user_size == 8 -> aligned_size == 8 /// user_size == 9 -> aligned_size == 16 - aligned_size = bit_ceil(aligned_size); + aligned_size = llvm::bit_ceil(aligned_size); addr_t aligned_start = user_addr & ~(aligned_size - 1); From lldb-commits at lists.llvm.org Tue May 6 14:14:09 2025 From: lldb-commits at lists.llvm.org (Kazu Hirata via lldb-commits) Date: Tue, 06 May 2025 14:14:09 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Use llvm::bit_ceil (NFC) (PR #138723) In-Reply-To: Message-ID: <681a7ba1.170a0220.120fce.4271@mx.google.com> https://github.com/kazutakahirata closed https://github.com/llvm/llvm-project/pull/138723 From lldb-commits at lists.llvm.org Tue May 6 14:26:12 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Tue, 06 May 2025 14:26:12 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream lldb-rpc-gen tool (PR #138031) In-Reply-To: Message-ID: <681a7e74.170a0220.328866.4bde@mx.google.com> ================ @@ -0,0 +1,535 @@ +//===-- 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/Mangle.h" +#include "clang/Lex/Lexer.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +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", // What is this used for? + "SBInputReader", // What is this used for? + "SBCommandPluginInterface", // This is hard to support, we can do it if + // really needed though. + "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. +}; + +// We intentionally avoid generating certain methods either because they are +// difficult to support correctly or they aren't really used much from C++. +// FIXME: We should be able to annotate these methods instead of maintaining a +// list in the generator itself. +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", ---------------- chelcassanova wrote: We should be able to, it would change `MethodIsDisallowed` to just check for some underlying `DeprecatedAttr` instead of reading from this list. https://github.com/llvm/llvm-project/pull/138031 From lldb-commits at lists.llvm.org Tue May 6 14:29:52 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Tue, 06 May 2025 14:29:52 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream lldb-rpc-gen tool (PR #138031) In-Reply-To: Message-ID: <681a7f50.630a0220.393f84.4811@mx.google.com> ================ @@ -0,0 +1,535 @@ +//===-- 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/Mangle.h" +#include "clang/Lex/Lexer.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +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", // What is this used for? + "SBInputReader", // What is this used for? + "SBCommandPluginInterface", // This is hard to support, we can do it if + // really needed though. + "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. +}; + +// We intentionally avoid generating certain methods either because they are +// difficult to support correctly or they aren't really used much from C++. +// FIXME: We should be able to annotate these methods instead of maintaining a +// list in the generator itself. +static constexpr llvm::StringRef DisallowedMethods[] = { + // The threading functionality in SBHostOS is deprecated and thus we do not + // generate them. It would be ideal to add the annotations to the methods + // and then support not generating deprecated methods. However, without + // annotations the generator generates most things correctly. This one is + // problematic because it returns a pointer to an "opaque" structure + // (thread_t) that is not `void *`, so special casing it is more effort than + // it's worth. + "_ZN4lldb8SBHostOS10ThreadJoinEP17_opaque_pthread_tPPvPNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadCancelEP17_opaque_pthread_tPNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadCreateEPKcPFPvS3_ES3_PNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadDetachEP17_opaque_pthread_tPNS_7SBErrorE", + "_ZN4lldb8SBHostOS13ThreadCreatedEPKc", +}; + +static constexpr llvm::StringRef ClassesWithoutDefaultCtor[] = { + "SBHostOS", + "SBReproducer", +}; + +static constexpr llvm::StringRef ClassesWithoutCopyOperations[] = { + "SBHostOS", + "SBReproducer", + "SBStream", + "SBProgress", +}; + +static constexpr llvm::StringRef MethodsWithPointerPlusLen[] = { ---------------- chelcassanova wrote: Ismail had an idea here to use a typedef over methods that used pointers and lens so that the tool could pick it up and I liked that idea. Otherwise, if we have a function that has a pointer and a length then we have no way of knowing whether or not they're related to each other. https://github.com/llvm/llvm-project/pull/138031 From lldb-commits at lists.llvm.org Tue May 6 15:18:43 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Tue, 06 May 2025 15:18:43 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][TypeSystemClang] Allow arrays to be dereferenced in C/C++. (PR #135843) In-Reply-To: Message-ID: <681a8ac3.170a0220.1806a1.461c@mx.google.com> ================ @@ -2794,47 +2794,31 @@ ValueObjectSP ValueObject::Dereference(Status &error) { if (m_deref_valobj) return m_deref_valobj->GetSP(); - const bool is_pointer_or_reference_type = IsPointerOrReferenceType(); - if (is_pointer_or_reference_type) { - bool omit_empty_base_classes = true; - bool ignore_array_bounds = false; - - std::string child_name_str; - uint32_t child_byte_size = 0; - int32_t child_byte_offset = 0; - uint32_t child_bitfield_bit_size = 0; - uint32_t child_bitfield_bit_offset = 0; - bool child_is_base_class = false; - bool child_is_deref_of_parent = false; - const bool transparent_pointers = false; - CompilerType compiler_type = GetCompilerType(); - uint64_t language_flags = 0; + std::string child_name_str; + uint32_t child_byte_size = 0; ---------------- jimingham wrote: This is a tiny nit, but I find this flow harder to read because I don't understand why you use both "child" and "deref" as prefixes related to the defererenced object. I had to think a bit to realize that this distinction was without difference... It is true that in strict lldb terms, the deference valobj is a child of the valobj it is the dereference of, but that's the least interesting aspect of it here. For instance, it's odd that `child_name_str` is the name that you are going to give to `m_deref_valobj`, or when you assign `deref_error` from `child_type_or_err`. https://github.com/llvm/llvm-project/pull/135843 From lldb-commits at lists.llvm.org Tue May 6 15:19:34 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Tue, 06 May 2025 15:19:34 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][TypeSystemClang] Allow arrays to be dereferenced in C/C++. (PR #135843) In-Reply-To: Message-ID: <681a8af6.050a0220.31513.bbf5@mx.google.com> jimingham wrote: This looks fine to me as well. I had a small typographical nit which you can act on or not as you feel moved to. https://github.com/llvm/llvm-project/pull/135843 From lldb-commits at lists.llvm.org Tue May 6 15:25:03 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Tue, 06 May 2025 15:25:03 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Ensure we acquire the SB API lock while handling requests. (PR #137026) In-Reply-To: Message-ID: <681a8c3f.050a0220.2ad8ca.b8f2@mx.google.com> jeffreytan81 wrote: @ashgti, this change is causing deadlock in our lldb. We have internal changes that main thread's disconnect request handler tried to reset debugger states and call StopEventHandlers() to ensure all event threads to stop. However, this deadlocks now because event thread can call `SBProcess::GetExitStatus()` to acquire the API mutex which will wait forever: Main Thread ``` __futex_abstimed_wait_common64 (/home/engshare/third-party2/glibc/2.34/src/glibc-2.34/nptl/futex-internal.c:57) __futex_abstimed_wait_common (/home/engshare/third-party2/glibc/2.34/src/glibc-2.34/nptl/futex-internal.c:87) __GI___futex_abstimed_wait_cancelable64 (/home/engshare/third-party2/glibc/2.34/src/glibc-2.34/nptl/futex-internal.c:139) __pthread_clockjoin_ex (/home/engshare/third-party2/glibc/2.34/src/glibc-2.34/nptl/pthread_join_common.c:105) __gthread_join (/home/engshare/third-party2/libgcc/11.x/src/gcc-11.x/x86_64-facebook-linux/libstdc++-v3/include/x86_64-facebook-linux/bits/gthr-default.h:669) std::thread::join() (/home/engshare/third-party2/libgcc/11.x/src/gcc-11.x/libstdc++-v3/src/c++11/thread.cc:112) lldb_dap::DAP::StopEventHandlers() (/data/users/jeffreytan/llvm-sand/external/llvm-project/lldb/tools/lldb-dap/DAP.cpp:234) lldb_dap::DAP::ResetDebuggerState() (/data/users/jeffreytan/llvm-sand/external/llvm-project/lldb/tools/lldb-dap/DAP.cpp:1411) lldb_dap::DAP::Disconnect(bool) (/data/users/jeffreytan/llvm-sand/external/llvm-project/lldb/tools/lldb-dap/DAP.cpp:842) lldb_dap::DisconnectRequestHandler::Run(std::optional const&) const (/data/users/jeffreytan/llvm-sand/external/llvm-project/lldb/tools/lldb-dap/Handler/DisconnectRequestHandler.cpp:32) lldb_dap::RequestHandler, llvm::Error>::operator()(lldb_dap::protocol::Request const&) const (/data/users/jeffreytan/llvm-sand/external/llvm-project/lldb/tools/lldb-dap/Handler/RequestHandler.h:140) lldb_dap::BaseRequestHandler::Run(lldb_dap::protocol::Request const&) (/data/users/jeffreytan/llvm-sand/external/llvm-project/lldb/tools/lldb-dap/Handler/RequestHandler.cpp:197) lldb_dap::DAP::HandleObject(std::variant const&) (/data/users/jeffreytan/llvm-sand/external/llvm-project/lldb/tools/lldb-dap/DAP.cpp:734) lldb_dap::DAP::Loop() (/data/users/jeffreytan/llvm-sand/external/llvm-project/lldb/tools/lldb-dap/DAP.cpp:979) main (/data/users/jeffreytan/llvm-sand/external/llvm-project/lldb/tools/lldb-dap/lldb-dap.cpp:620) __libc_start_call_main (/home/engshare/third-party2/glibc/2.34/src/glibc-2.34/sysdeps/nptl/libc_start_call_main.h:58) __libc_start_main_impl (/home/engshare/third-party2/glibc/2.34/src/glibc-2.34/csu/libc-start.c:409) _start (/home/engshare/third-party2/glibc/2.34/src/glibc-2.34/sysdeps/x86_64/start.S:116) ``` Event thread: ``` futex_wait (/home/engshare/third-party2/glibc/2.34/src/glibc-2.34/sysdeps/nptl/futex-internal.h:146) __GI___lll_lock_wait (/home/engshare/third-party2/glibc/2.34/src/glibc-2.34/nptl/lowlevellock.c:50) lll_mutex_lock_optimized (/home/engshare/third-party2/glibc/2.34/src/glibc-2.34/nptl/pthread_mutex_lock.c:49) ___pthread_mutex_lock (/home/engshare/third-party2/glibc/2.34/src/glibc-2.34/nptl/pthread_mutex_lock.c:124) __gthread_mutex_lock(pthread_mutex_t*) (/mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a/include/c++/11.x/x86_64-facebook-linux/bits/gthr-default.h:749) __gthread_recursive_mutex_lock(pthread_mutex_t*) (/mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a/include/c++/11.x/x86_64-facebook-linux/bits/gthr-default.h:811) std::recursive_mutex::lock() (/mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a/include/c++/11.x/mutex:108) std::lock_guard::lock_guard(std::recursive_mutex&) (/mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a/include/c++/11.x/bits/std_mutex.h:229) lldb::SBProcess::GetExitStatus() (/data/users/jeffreytan/llvm-sand/external/llvm-project/lldb/source/API/SBProcess.cpp:505) lldb_dap::SendProcessExitedEvent(lldb_dap::DAP&, lldb::SBProcess&) (/data/users/jeffreytan/llvm-sand/external/llvm-project/lldb/tools/lldb-dap/EventHelper.cpp:269) lldb_dap::EventThreadFunction(lldb_dap::DAP&) (/data/users/jeffreytan/llvm-sand/external/llvm-project/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp:198) void std::__invoke_impl>(std::__invoke_other, void (*&&)(lldb_dap::DAP&), std::reference_wrapper&&) (/mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a/include/c++/11.x/bits/invoke.h:61) std::__invoke_result>::type std::__invoke>(void (*&&)(lldb_dap::DAP&), std::reference_wrapper&&) (/mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a/include/c++/11.x/bits/invoke.h:96) void std::thread::_Invoker>>::_M_invoke<0ul, 1ul>(std::_Index_tuple<0ul, 1ul>) (/mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a/include/c++/11.x/bits/std_thread.h:253) std::thread::_Invoker>>::operator()() (/mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a/include/c++/11.x/bits/std_thread.h:260) std::thread::_State_impl>>>::_M_run() (/mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a/include/c++/11.x/bits/std_thread.h:211) std::execute_native_thread_routine(void *) (/home/engshare/third-party2/libgcc/11.x/src/gcc-11.x/libstdc++-v3/src/c++11/thread.cc:82) start_thread (/home/engshare/third-party2/glibc/2.34/src/glibc-2.34/nptl/pthread_create.c:434) __clone3 (/home/engshare/third-party2/glibc/2.34/src/glibc-2.34/sysdeps/unix/sysv/linux/x86_64/clone3.S:81) ``` High level, I do not think it is a good idea to hold the top level API mutex which is way too large locking scope. We should leave the decision to each request handler for smaller scope locks if they want to ensure a critical section. Can we revert the PR? cc @clayborg https://github.com/llvm/llvm-project/pull/137026 From lldb-commits at lists.llvm.org Tue May 6 15:46:43 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Tue, 06 May 2025 15:46:43 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream lldb-rpc-gen tool (PR #138031) In-Reply-To: Message-ID: <681a9153.170a0220.88e13.4a7a@mx.google.com> ================ @@ -0,0 +1,535 @@ +//===-- 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/Mangle.h" +#include "clang/Lex/Lexer.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +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", // What is this used for? + "SBInputReader", // What is this used for? + "SBCommandPluginInterface", // This is hard to support, we can do it if + // really needed though. + "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. +}; + +// We intentionally avoid generating certain methods either because they are +// difficult to support correctly or they aren't really used much from C++. +// FIXME: We should be able to annotate these methods instead of maintaining a +// list in the generator itself. +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", ---------------- chelcassanova wrote: Trying that idea out, it seems that just checking the attributes on a given method to see if it has a deprecated attribute isn't working? I'm wondering if it's because these methods are marked as deprecated using a preprocessor macro that wraps around the `[[deprecated]]` attribute itself... 🤔 https://github.com/llvm/llvm-project/pull/138031 From lldb-commits at lists.llvm.org Tue May 6 15:53:45 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Tue, 06 May 2025 15:53:45 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Change the launch sequence (PR #138219) In-Reply-To: Message-ID: <681a92f9.630a0220.3b3535.5a41@mx.google.com> https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/138219 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Tue May 6 15:53:51 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Tue, 06 May 2025 15:53:51 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream lldb-rpc-gen tool (PR #138031) In-Reply-To: Message-ID: <681a92ff.170a0220.26a001.9c1b@mx.google.com> ================ @@ -0,0 +1,535 @@ +//===-- 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/Mangle.h" +#include "clang/Lex/Lexer.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +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", // What is this used for? + "SBInputReader", // What is this used for? + "SBCommandPluginInterface", // This is hard to support, we can do it if + // really needed though. + "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. +}; + +// We intentionally avoid generating certain methods either because they are +// difficult to support correctly or they aren't really used much from C++. +// FIXME: We should be able to annotate these methods instead of maintaining a +// list in the generator itself. +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", ---------------- chelcassanova wrote: Adding the `[[deprecated]]` attribute does allow the approach of just checking the attributes with Clang to work as intended so we'll probably have to do some preprocessor reading to make sure that we're capturing that `LLDB_DEPRECATED` macro. https://github.com/llvm/llvm-project/pull/138031 From lldb-commits at lists.llvm.org Tue May 6 15:58:47 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Tue, 06 May 2025 15:58:47 -0700 (PDT) Subject: [Lldb-commits] [lldb] ba29e60 - [lldb-dap] Change the launch sequence (#138219) Message-ID: <681a9427.170a0220.2de5a7.3a07@mx.google.com> Author: Jonas Devlieghere Date: 2025-05-06T15:58:44-07:00 New Revision: ba29e60f9a2222bd5e883579bb78db13fc5a7588 URL: https://github.com/llvm/llvm-project/commit/ba29e60f9a2222bd5e883579bb78db13fc5a7588 DIFF: https://github.com/llvm/llvm-project/commit/ba29e60f9a2222bd5e883579bb78db13fc5a7588.diff LOG: [lldb-dap] Change the launch sequence (#138219) This PR changes how we treat the launch sequence in lldb-dap. - Send the initialized event after we finish handling the initialize request, rather than after we finish attaching or launching. - Delay handling the launch and attach request until we have handled the configurationDone request. The latter is now largely a NO-OP and only exists to signal lldb-dap that it can handle the launch and attach requests. - Delay handling the initial threads requests until we have handled the launch or attach request. - Make all attaching and launching synchronous, including when we have attach or launch commands. This removes the need to synchronize between the request and event thread. Background: https://discourse.llvm.org/t/reliability-of-the-lldb-dap-tests/86125 Added: Modified: lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py lldb/test/API/tools/lldb-dap/console/TestDAP_console.py lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py lldb/tools/lldb-dap/DAP.cpp lldb/tools/lldb-dap/DAP.h lldb/tools/lldb-dap/EventHelper.cpp lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp lldb/tools/lldb-dap/Handler/RequestHandler.cpp lldb/tools/lldb-dap/Handler/RequestHandler.h Removed: ################################################################################ diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py index 6d9ab770684f1..e10342b72f4f0 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py @@ -132,7 +132,6 @@ def __init__(self, recv, send, init_commands, log_file=None): self.exit_status = None self.initialize_body = None self.thread_stop_reasons = {} - self.breakpoint_events = [] self.progress_events = [] self.reverse_requests = [] self.module_events = [] @@ -244,13 +243,6 @@ def handle_recv_packet(self, packet): self._process_stopped() tid = body["threadId"] self.thread_stop_reasons[tid] = body - elif event == "breakpoint": - # Breakpoint events come in when a breakpoint has locations - # added or removed. Keep track of them so we can look for them - # in tests. - self.breakpoint_events.append(packet) - # no need to add 'breakpoint' event packets to our packets list - return keepGoing elif event.startswith("progress"): # Progress events come in as 'progressStart', 'progressUpdate', # and 'progressEnd' events. Keep these around in case test @@ -412,6 +404,15 @@ def wait_for_stopped(self, timeout=None): self.threads = [] return stopped_events + def wait_for_breakpoint_events(self, timeout=None): + breakpoint_events = [] + while True: + event = self.wait_for_event("breakpoint", timeout=timeout) + if not event: + break + breakpoint_events.append(event) + return breakpoint_events + def wait_for_exited(self): event_dict = self.wait_for_event("exited") if event_dict is None: @@ -591,6 +592,7 @@ def request_attach( attachCommands=None, terminateCommands=None, coreFile=None, + stopOnAttach=True, postRunCommands=None, sourceMap=None, gdbRemotePort=None, @@ -620,6 +622,8 @@ def request_attach( args_dict["attachCommands"] = attachCommands if coreFile: args_dict["coreFile"] = coreFile + if stopOnAttach: + args_dict["stopOnEntry"] = stopOnAttach if postRunCommands: args_dict["postRunCommands"] = postRunCommands if sourceMap: @@ -632,7 +636,7 @@ def request_attach( response = self.send_recv(command_dict) if response["success"]: - self.wait_for_events(["process", "initialized"]) + self.wait_for_event("process") return response def request_breakpointLocations( @@ -666,10 +670,6 @@ def request_configurationDone(self): response = self.send_recv(command_dict) if response: self.configuration_done_sent = True - # Client requests the baseline of currently existing threads after - # a successful launch or attach. - # Kick off the threads request that follows - self.request_threads() return response def _process_stopped(self): @@ -887,7 +887,7 @@ def request_launch( response = self.send_recv(command_dict) if response["success"]: - self.wait_for_events(["process", "initialized"]) + self.wait_for_event("process") return response def request_next(self, threadId, granularity="statement"): @@ -1325,6 +1325,26 @@ def attach_options_specified(options): def run_vscode(dbg, args, options): dbg.request_initialize(options.sourceInitFile) + + if options.sourceBreakpoints: + source_to_lines = {} + for file_line in options.sourceBreakpoints: + (path, line) = file_line.split(":") + if len(path) == 0 or len(line) == 0: + print('error: invalid source with line "%s"' % (file_line)) + + else: + if path in source_to_lines: + source_to_lines[path].append(int(line)) + else: + source_to_lines[path] = [int(line)] + for source in source_to_lines: + dbg.request_setBreakpoints(source, source_to_lines[source]) + if options.funcBreakpoints: + dbg.request_setFunctionBreakpoints(options.funcBreakpoints) + + dbg.request_configurationDone() + if attach_options_specified(options): response = dbg.request_attach( program=options.program, @@ -1353,23 +1373,6 @@ def run_vscode(dbg, args, options): ) if response["success"]: - if options.sourceBreakpoints: - source_to_lines = {} - for file_line in options.sourceBreakpoints: - (path, line) = file_line.split(":") - if len(path) == 0 or len(line) == 0: - print('error: invalid source with line "%s"' % (file_line)) - - else: - if path in source_to_lines: - source_to_lines[path].append(int(line)) - else: - source_to_lines[path] = [int(line)] - for source in source_to_lines: - dbg.request_setBreakpoints(source, source_to_lines[source]) - if options.funcBreakpoints: - dbg.request_setFunctionBreakpoints(options.funcBreakpoints) - dbg.request_configurationDone() dbg.wait_for_stopped() else: if "message" in response: diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py index 2c14bb35162b5..958c7268c0c72 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py @@ -340,6 +340,7 @@ def attach( exitCommands=None, attachCommands=None, coreFile=None, + stopOnAttach=True, disconnectAutomatically=True, terminateCommands=None, postRunCommands=None, @@ -364,6 +365,8 @@ def cleanup(): self.addTearDownHook(cleanup) # Initialize and launch the program self.dap_server.request_initialize(sourceInitFile) + self.dap_server.wait_for_event("initialized") + self.dap_server.request_configurationDone() response = self.dap_server.request_attach( program=program, pid=pid, @@ -376,6 +379,7 @@ def cleanup(): attachCommands=attachCommands, terminateCommands=terminateCommands, coreFile=coreFile, + stopOnAttach=stopOnAttach, postRunCommands=postRunCommands, sourceMap=sourceMap, gdbRemotePort=gdbRemotePort, @@ -434,6 +438,9 @@ def cleanup(): # Initialize and launch the program self.dap_server.request_initialize(sourceInitFile) + self.dap_server.wait_for_event("initialized") + self.dap_server.request_configurationDone() + response = self.dap_server.request_launch( program, args=args, diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py index f48d5a7db3c50..741c011a3d692 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py @@ -27,6 +27,8 @@ def spawn_and_wait(program, delay): @skip class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase): def set_and_hit_breakpoint(self, continueToExit=True): + self.dap_server.wait_for_stopped() + source = "main.c" breakpoint1_line = line_number(source, "// breakpoint 1") lines = [breakpoint1_line] diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py index 7f93b9f2a3a22..7250e67ebcd8c 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py @@ -18,17 +18,17 @@ import socket - at skip class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase): default_timeout = 20 def set_and_hit_breakpoint(self, continueToExit=True): + self.dap_server.wait_for_stopped() + source = "main.c" - main_source_path = os.path.join(os.getcwd(), source) - breakpoint1_line = line_number(main_source_path, "// breakpoint 1") + breakpoint1_line = line_number(source, "// breakpoint 1") lines = [breakpoint1_line] # Set breakpoint in the thread function so we can step the threads - breakpoint_ids = self.set_source_breakpoints(main_source_path, lines) + breakpoint_ids = self.set_source_breakpoints(source, lines) self.assertEqual( len(breakpoint_ids), len(lines), "expect correct number of breakpoints" ) diff --git a/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py b/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py index e5590e1b332a0..8581f10cef22a 100644 --- a/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py +++ b/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py @@ -81,52 +81,27 @@ def test_breakpoint_events(self): breakpoint["verified"], "expect foo breakpoint to not be verified" ) - # Get the stop at the entry point - self.continue_to_next_stop() + # Make sure we're stopped. + self.dap_server.wait_for_stopped() - # We are now stopped at the entry point to the program. Shared - # libraries are not loaded yet (at least on macOS they aren't) and only - # the breakpoint in the main executable should be resolved. - self.assertEqual(len(self.dap_server.breakpoint_events), 1) - event = self.dap_server.breakpoint_events[0] - body = event["body"] - self.assertEqual( - body["reason"], "changed", "breakpoint event should say changed" - ) - breakpoint = body["breakpoint"] - self.assertEqual(breakpoint["id"], main_bp_id) - self.assertTrue(breakpoint["verified"], "main breakpoint should be resolved") - - # Clear the list of breakpoint events so we don't see this one again. - self.dap_server.breakpoint_events.clear() + # Flush the breakpoint events. + self.dap_server.wait_for_breakpoint_events(timeout=5) # Continue to the breakpoint self.continue_to_breakpoints(dap_breakpoint_ids) - # When the process launches, we first expect to see both the main and - # foo breakpoint as unresolved. - for event in self.dap_server.breakpoint_events[:2]: - body = event["body"] - self.assertEqual( - body["reason"], "changed", "breakpoint event should say changed" - ) - breakpoint = body["breakpoint"] - self.assertIn(str(breakpoint["id"]), dap_breakpoint_ids) - self.assertFalse(breakpoint["verified"], "breakpoint should be unresolved") + verified_breakpoint_ids = [] + unverified_breakpoint_ids = [] + for breakpoint_event in self.dap_server.wait_for_breakpoint_events(timeout=5): + breakpoint = breakpoint_event["body"]["breakpoint"] + id = breakpoint["id"] + if breakpoint["verified"]: + verified_breakpoint_ids.append(id) + else: + unverified_breakpoint_ids.append(id) - # Then, once the dynamic loader has given us a load address, they - # should show up as resolved again. - for event in self.dap_server.breakpoint_events[3:]: - body = event["body"] - self.assertEqual( - body["reason"], "changed", "breakpoint event should say changed" - ) - breakpoint = body["breakpoint"] - self.assertIn(str(breakpoint["id"]), dap_breakpoint_ids) - self.assertTrue(breakpoint["verified"], "breakpoint should be resolved") - self.assertNotIn( - "source", - breakpoint, - "breakpoint event should not return a source object", - ) - self.assertIn("line", breakpoint, "breakpoint event should have line") + self.assertIn(main_bp_id, unverified_breakpoint_ids) + self.assertIn(foo_bp_id, unverified_breakpoint_ids) + + self.assertIn(main_bp_id, verified_breakpoint_ids) + self.assertIn(foo_bp_id, verified_breakpoint_ids) diff --git a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py index 210e591bff426..455ac84168baf 100644 --- a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py +++ b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py @@ -44,9 +44,9 @@ def verify_completions(self, actual_list, expected_list, not_expected_list=[]): self.assertNotIn(not_expected_item, actual_list) - def setup_debugee(self): + def setup_debugee(self, stopOnEntry=False): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=stopOnEntry) source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") @@ -235,7 +235,7 @@ def test_auto_completions(self): """ Tests completion requests in "repl-mode=auto" """ - self.setup_debugee() + self.setup_debugee(stopOnEntry=True) res = self.dap_server.request_evaluate( "`lldb-dap repl-mode auto", context="repl" diff --git a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py index b07c4f871d73b..65a1bc04c7cd7 100644 --- a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py +++ b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py @@ -167,7 +167,7 @@ def test_exit_status_message_ok(self): def test_diagnositcs(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) core = self.getBuildArtifact("minidump.core") self.yaml2obj("minidump.yaml", core) diff --git a/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py b/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py index 0cb792d662a80..09e3f62f0eead 100644 --- a/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py +++ b/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py @@ -31,7 +31,7 @@ def test_launch(self): created. """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program, disconnectAutomatically=False) + self.build_and_launch(program, stopOnEntry=True, disconnectAutomatically=False) # We set a breakpoint right before the side effect file is created self.set_source_breakpoints( @@ -39,7 +39,11 @@ def test_launch(self): ) self.continue_to_next_stop() + # verify we haven't produced the side effect file yet + self.assertFalse(os.path.exists(program + ".side_effect")) + self.dap_server.request_disconnect() + # verify we didn't produce the side effect file time.sleep(1) self.assertFalse(os.path.exists(program + ".side_effect")) diff --git a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py index d97fda730c46a..19b682dfcd22d 100644 --- a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py +++ b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py @@ -10,6 +10,7 @@ from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * + # DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. @skip class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase): @@ -42,7 +43,9 @@ def run_test_evaluate_expressions( self.context = context program = self.getBuildArtifact("a.out") self.build_and_launch( - program, enableAutoVariableSummaries=enableAutoVariableSummaries + program, + enableAutoVariableSummaries=enableAutoVariableSummaries, + stopOnEntry=True, ) source = "main.cpp" self.set_source_breakpoints( diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py index 931456299e03e..604a41678500c 100644 --- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py +++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py @@ -88,8 +88,8 @@ def test_stopOnEntry(self): """ program = self.getBuildArtifact("a.out") self.build_and_launch(program, stopOnEntry=True) - self.set_function_breakpoints(["main"]) - stopped_events = self.continue_to_next_stop() + + stopped_events = self.dap_server.wait_for_stopped() for stopped_event in stopped_events: if "body" in stopped_event: body = stopped_event["body"] diff --git a/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py b/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py index fee63655de0da..0f94b50c31fba 100755 --- a/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py +++ b/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py @@ -50,7 +50,7 @@ def verify_progress_events( @skipIfWindows def test(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) progress_emitter = os.path.join(os.getcwd(), "Progress_emitter.py") self.dap_server.request_evaluate( f"`command script import {progress_emitter}", context="repl" diff --git a/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py b/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py index c6f59949d668e..81edcdf4bd0f9 100644 --- a/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py +++ b/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py @@ -20,7 +20,7 @@ def assertEvaluate(self, expression, regex): def test_completions(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") diff --git a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py index 36fa0bd40183f..5f95c7bfb1556 100644 --- a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py +++ b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py @@ -22,7 +22,6 @@ def test_basic_functionality(self): [bp_A, bp_B] = self.set_source_breakpoints("main.c", [line_A, line_B]) # Verify we hit A, then B. - self.dap_server.request_configurationDone() self.verify_breakpoint_hit([bp_A]) self.dap_server.request_continue() self.verify_breakpoint_hit([bp_B]) diff --git a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py index a94c9860c1508..eed769a5a0cc6 100644 --- a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py +++ b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py @@ -74,7 +74,6 @@ def test_stopOnEntry(self): program = self.getBuildArtifact("a.out") self.build_and_launch(program, runInTerminal=True, stopOnEntry=True) [bp_main] = self.set_function_breakpoints(["main"]) - self.dap_server.request_configurationDone() # When using stopOnEntry, configurationDone doesn't result in a running # process, we should immediately get a stopped event instead. diff --git a/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py b/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py index 70c11a63a79f7..7e28a5af4331c 100644 --- a/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py +++ b/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py @@ -19,7 +19,7 @@ def test_stop_hooks_before_run(self): self.build_and_launch(program, stopOnEntry=True, preRunCommands=preRunCommands) # The first stop is on entry. - self.continue_to_next_stop() + self.dap_server.wait_for_stopped() breakpoint_ids = self.set_function_breakpoints(["main"]) # This request hangs if the race happens, because, in that case, the diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 4b631484c9fab..62c60cc3a9b3b 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -84,8 +84,8 @@ DAP::DAP(Log *log, const ReplMode default_repl_mode, : log(log), transport(transport), broadcaster("lldb-dap"), exception_breakpoints(), focus_tid(LLDB_INVALID_THREAD_ID), stop_at_entry(false), is_attach(false), - restarting_process_id(LLDB_INVALID_PROCESS_ID), - configuration_done_sent(false), waiting_for_run_in_terminal(false), + restarting_process_id(LLDB_INVALID_PROCESS_ID), configuration_done(false), + waiting_for_run_in_terminal(false), progress_event_reporter( [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }), reverse_request_seq(0), repl_mode(default_repl_mode) { @@ -893,10 +893,19 @@ llvm::Error DAP::Loop() { return errWrapper; } + // The launch sequence is special and we need to carefully handle + // packets in the right order. Until we've handled configurationDone, + bool add_to_pending_queue = false; + if (const protocol::Request *req = - std::get_if(&*next); - req && req->command == "disconnect") { - disconnecting = true; + std::get_if(&*next)) { + llvm::StringRef command = req->command; + if (command == "disconnect") + disconnecting = true; + if (!configuration_done) + add_to_pending_queue = + command != "initialize" && command != "configurationDone" && + command != "disconnect" && !command.ends_with("Breakpoints"); } const std::optional cancel_args = @@ -924,7 +933,8 @@ llvm::Error DAP::Loop() { { std::lock_guard guard(m_queue_mutex); - m_queue.push_back(std::move(*next)); + auto &queue = add_to_pending_queue ? m_pending_queue : m_queue; + queue.push_back(std::move(*next)); } m_queue_cv.notify_one(); } @@ -938,16 +948,19 @@ llvm::Error DAP::Loop() { StopEventHandlers(); }); - while (!disconnecting || !m_queue.empty()) { + while (true) { std::unique_lock lock(m_queue_mutex); m_queue_cv.wait(lock, [&] { return disconnecting || !m_queue.empty(); }); - if (m_queue.empty()) + if (disconnecting && m_queue.empty()) break; Message next = m_queue.front(); m_queue.pop_front(); + // Unlock while we're processing the event. + lock.unlock(); + if (!HandleObject(next)) return llvm::createStringError(llvm::inconvertibleErrorCode(), "unhandled packet"); @@ -1219,6 +1232,16 @@ void DAP::SetConfiguration(const protocol::Configuration &config, SetThreadFormat(*configuration.customThreadFormat); } +void DAP::SetConfigurationDone() { + { + std::lock_guard guard(m_queue_mutex); + std::copy(m_pending_queue.begin(), m_pending_queue.end(), + std::front_inserter(m_queue)); + configuration_done = true; + } + m_queue_cv.notify_all(); +} + void DAP::SetFrameFormat(llvm::StringRef format) { if (format.empty()) return; diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index 88eedb0860cf1..b581ae759b1bc 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -188,7 +188,7 @@ struct DAP { // shutting down the entire adapter. When we're restarting, we keep the id of // the old process here so we can detect this case and keep running. lldb::pid_t restarting_process_id; - bool configuration_done_sent; + bool configuration_done; llvm::StringMap> request_handlers; bool waiting_for_run_in_terminal; ProgressEventReporter progress_event_reporter; @@ -251,6 +251,8 @@ struct DAP { /// Configures the debug adapter for launching/attaching. void SetConfiguration(const protocol::Configuration &confing, bool is_attach); + void SetConfigurationDone(); + /// Configure source maps based on the current `DAPConfiguration`. void ConfigureSourceMaps(); @@ -417,8 +419,10 @@ struct DAP { lldb::SBMutex GetAPIMutex() const { return target.GetAPIMutex(); } private: - std::mutex m_queue_mutex; + /// Queue for all incoming messages. std::deque m_queue; + std::deque m_pending_queue; + std::mutex m_queue_mutex; std::condition_variable m_queue_cv; std::mutex m_cancelled_requests_mutex; diff --git a/lldb/tools/lldb-dap/EventHelper.cpp b/lldb/tools/lldb-dap/EventHelper.cpp index 2c659f39f4b66..ed2d8700c26b0 100644 --- a/lldb/tools/lldb-dap/EventHelper.cpp +++ b/lldb/tools/lldb-dap/EventHelper.cpp @@ -222,7 +222,7 @@ void SendContinuedEvent(DAP &dap) { // If the focus thread is not set then we haven't reported any thread status // to the client, so nothing to report. - if (!dap.configuration_done_sent || dap.focus_tid == LLDB_INVALID_THREAD_ID) { + if (!dap.configuration_done || dap.focus_tid == LLDB_INVALID_THREAD_ID) { return; } diff --git a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp index 7a0f091128e4a..5dc9c3f9772e3 100644 --- a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp @@ -133,61 +133,70 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { dap.SendOutput(OutputType::Console, llvm::StringRef(attach_msg, attach_msg_len)); } - if (attachCommands.empty()) { - // No "attachCommands", just attach normally. - // Disable async events so the attach will be successful when we return from - // the launch call and the launch will happen synchronously + { + // Perform the launch in synchronous mode so that we don't have to worry + // about process state changes during the launch. ScopeSyncMode scope_sync_mode(dap.debugger); - - if (core_file.empty()) { - if ((pid != LLDB_INVALID_PROCESS_ID) && - (gdb_remote_port != invalid_port)) { - // If both pid and port numbers are specified. - error.SetErrorString("The user can't specify both pid and port"); - } else if (gdb_remote_port != invalid_port) { - // If port is specified and pid is not. - lldb::SBListener listener = dap.debugger.GetListener(); - - // If the user hasn't provided the hostname property, default localhost - // being used. - std::string connect_url = - llvm::formatv("connect://{0}:", gdb_remote_hostname); - connect_url += std::to_string(gdb_remote_port); - dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", - error); + if (attachCommands.empty()) { + // No "attachCommands", just attach normally. + if (core_file.empty()) { + if ((pid != LLDB_INVALID_PROCESS_ID) && + (gdb_remote_port != invalid_port)) { + // If both pid and port numbers are specified. + error.SetErrorString("The user can't specify both pid and port"); + } else if (gdb_remote_port != invalid_port) { + // If port is specified and pid is not. + lldb::SBListener listener = dap.debugger.GetListener(); + + // If the user hasn't provided the hostname property, default + // localhost being used. + std::string connect_url = + llvm::formatv("connect://{0}:", gdb_remote_hostname); + connect_url += std::to_string(gdb_remote_port); + dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", + error); + } else { + // Attach by pid or process name. + lldb::SBAttachInfo attach_info; + if (pid != LLDB_INVALID_PROCESS_ID) + attach_info.SetProcessID(pid); + else if (dap.configuration.program.has_value()) + attach_info.SetExecutable(dap.configuration.program->data()); + attach_info.SetWaitForLaunch(wait_for, false /*async*/); + dap.target.Attach(attach_info, error); + } } else { - // Attach by pid or process name. - lldb::SBAttachInfo attach_info; - if (pid != LLDB_INVALID_PROCESS_ID) - attach_info.SetProcessID(pid); - else if (dap.configuration.program.has_value()) - attach_info.SetExecutable(dap.configuration.program->data()); - attach_info.SetWaitForLaunch(wait_for, false /*async*/); - dap.target.Attach(attach_info, error); + dap.target.LoadCore(core_file.data(), error); } } else { - dap.target.LoadCore(core_file.data(), error); - } - } else { - // We have "attachCommands" that are a set of commands that are expected - // to execute the commands after which a process should be created. If there - // is no valid process after running these commands, we have failed. - if (llvm::Error err = dap.RunAttachCommands(attachCommands)) { - response["success"] = false; - EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; + // We have "attachCommands" that are a set of commands that are expected + // to execute the commands after which a process should be created. If + // there is no valid process after running these commands, we have failed. + if (llvm::Error err = dap.RunAttachCommands(attachCommands)) { + response["success"] = false; + EmplaceSafeString(response, "message", llvm::toString(std::move(err))); + dap.SendJSON(llvm::json::Value(std::move(response))); + return; + } + // The custom commands might have created a new target so we should use + // the selected target after these commands are run. + dap.target = dap.debugger.GetSelectedTarget(); } - // The custom commands might have created a new target so we should use the - // selected target after these commands are run. - dap.target = dap.debugger.GetSelectedTarget(); - - // Make sure the process is attached and stopped before proceeding as the - // the launch commands are not run using the synchronous mode. - error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds)); } + // Make sure the process is attached and stopped. + error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds)); + + // Clients can request a baseline of currently existing threads after + // we acknowledge the configurationDone request. + // Client requests the baseline of currently existing threads after + // a successful or attach by sending a 'threads' request + // right after receiving the configurationDone response. + // Obtain the list of threads before we resume the process + dap.initial_thread_list = + GetThreads(dap.target.GetProcess(), dap.thread_format); + if (error.Success() && core_file.empty()) { auto attached_pid = dap.target.GetProcess().GetProcessID(); if (attached_pid == LLDB_INVALID_PROCESS_ID) { @@ -206,9 +215,17 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { } dap.SendJSON(llvm::json::Value(std::move(response))); + + // FIXME: Move this into PostRun. if (error.Success()) { - SendProcessEvent(dap, Attach); - dap.SendJSON(CreateEventObject("initialized")); + if (dap.target.GetProcess().IsValid()) { + SendProcessEvent(dap, Attach); + + if (dap.stop_at_entry) + SendThreadStoppedEvent(dap); + else + dap.target.GetProcess().Continue(); + } } } diff --git a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp index f39bbdefdbb95..802c28d7b8904 100644 --- a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp @@ -47,21 +47,11 @@ namespace lldb_dap { void ConfigurationDoneRequestHandler::operator()( const llvm::json::Object &request) const { + dap.SetConfigurationDone(); + llvm::json::Object response; FillResponse(request, response); dap.SendJSON(llvm::json::Value(std::move(response))); - dap.configuration_done_sent = true; - if (dap.stop_at_entry) - SendThreadStoppedEvent(dap); - else { - // Client requests the baseline of currently existing threads after - // a successful launch or attach by sending a 'threads' request - // right after receiving the configurationDone response. - // Obtain the list of threads before we resume the process - dap.initial_thread_list = - GetThreads(dap.target.GetProcess(), dap.thread_format); - dap.target.GetProcess().Continue(); - } } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp index ce34c52bcc334..aa947d3cb5ab9 100644 --- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp @@ -140,43 +140,28 @@ static void EventThreadFunction(DAP &dap) { lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event); if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) { auto state = lldb::SBProcess::GetStateFromEvent(event); + + DAP_LOG(dap.log, "State = {0}", state); switch (state) { + case lldb::eStateConnected: + case lldb::eStateDetached: case lldb::eStateInvalid: - // Not a state event - break; case lldb::eStateUnloaded: break; - case lldb::eStateConnected: - break; case lldb::eStateAttaching: - break; - case lldb::eStateLaunching: - break; - case lldb::eStateStepping: - break; case lldb::eStateCrashed: - break; - case lldb::eStateDetached: - break; - case lldb::eStateSuspended: - break; + case lldb::eStateLaunching: case lldb::eStateStopped: - // We launch and attach in synchronous mode then the first stop - // event will not be delivered. If we use "launchCommands" during a - // launch or "attachCommands" during an attach we might some process - // stop events which we do not want to send an event for. We will - // manually send a stopped event in request_configurationDone(...) - // so don't send any before then. - if (dap.configuration_done_sent) { - // Only report a stopped event if the process was not - // automatically restarted. - if (!lldb::SBProcess::GetRestartedFromEvent(event)) { - SendStdOutStdErr(dap, process); - SendThreadStoppedEvent(dap); - } + case lldb::eStateSuspended: + // Only report a stopped event if the process was not + // automatically restarted. + if (!lldb::SBProcess::GetRestartedFromEvent(event)) { + SendStdOutStdErr(dap, process); + SendThreadStoppedEvent(dap); } break; case lldb::eStateRunning: + case lldb::eStateStepping: dap.WillContinue(); SendContinuedEvent(dap); break; @@ -284,6 +269,7 @@ llvm::Expected InitializeRequestHandler::Run( // Do not source init files until in/out/err are configured. dap.debugger = lldb::SBDebugger::Create(false); dap.debugger.SetInputFile(dap.in); + dap.target = dap.debugger.GetDummyTarget(); llvm::Expected out_fd = dap.out.GetWriteFileDescriptor(); if (!out_fd) @@ -338,4 +324,8 @@ llvm::Expected InitializeRequestHandler::Run( return dap.GetCapabilities(); } +void InitializeRequestHandler::PostRun() const { + dap.SendJSON(CreateEventObject("initialized")); +} + } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp index 3e4532e754ec6..7e0e76935dd02 100644 --- a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp @@ -71,9 +71,12 @@ void LaunchRequestHandler::PostRun() const { if (dap.target.GetProcess().IsValid()) { // Attach happens when launching with runInTerminal. SendProcessEvent(dap, dap.is_attach ? Attach : Launch); - } - dap.SendJSON(CreateEventObject("initialized")); + if (dap.stop_at_entry) + SendThreadStoppedEvent(dap); + else + dap.target.GetProcess().Continue(); + } } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp index 7a75cd93abc19..282c5f4ab15a5 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp @@ -8,6 +8,7 @@ #include "Handler/RequestHandler.h" #include "DAP.h" +#include "EventHelper.h" #include "Handler/ResponseHandler.h" #include "JSONUtils.h" #include "LLDBUtils.h" @@ -162,7 +163,7 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) { dap.target.GetProcess().Continue(); // Now that the actual target is just starting (i.e. exec was just invoked), - // we return the debugger to its async state. + // we return the debugger to its sync state. scope_sync_mode.reset(); // If sending the notification failed, the launcher should be dead by now and @@ -238,35 +239,47 @@ llvm::Error BaseRequestHandler::LaunchProcess( launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug | lldb::eLaunchFlagStopAtEntry); - if (arguments.runInTerminal) { - if (llvm::Error err = RunInTerminal(dap, arguments)) - return err; - } else if (launchCommands.empty()) { - lldb::SBError error; - // Disable async events so the launch will be successful when we return from - // the launch call and the launch will happen synchronously + { + // Perform the launch in synchronous mode so that we don't have to worry + // about process state changes during the launch. ScopeSyncMode scope_sync_mode(dap.debugger); - dap.target.Launch(launch_info, error); - if (error.Fail()) - return llvm::make_error(error.GetCString()); - } else { - // Set the launch info so that run commands can access the configured - // launch details. - dap.target.SetLaunchInfo(launch_info); - if (llvm::Error err = dap.RunLaunchCommands(launchCommands)) - return err; - - // The custom commands might have created a new target so we should use the - // selected target after these commands are run. - dap.target = dap.debugger.GetSelectedTarget(); - // Make sure the process is launched and stopped at the entry point before - // proceeding as the launch commands are not run using the synchronous - // mode. - lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout); - if (error.Fail()) - return llvm::make_error(error.GetCString()); + + if (arguments.runInTerminal) { + if (llvm::Error err = RunInTerminal(dap, arguments)) + return err; + } else if (launchCommands.empty()) { + lldb::SBError error; + dap.target.Launch(launch_info, error); + if (error.Fail()) + return llvm::make_error(error.GetCString()); + } else { + // Set the launch info so that run commands can access the configured + // launch details. + dap.target.SetLaunchInfo(launch_info); + if (llvm::Error err = dap.RunLaunchCommands(launchCommands)) + return err; + + // The custom commands might have created a new target so we should use + // the selected target after these commands are run. + dap.target = dap.debugger.GetSelectedTarget(); + } } + // Make sure the process is launched and stopped at the entry point before + // proceeding. + lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout); + if (error.Fail()) + return llvm::make_error(error.GetCString()); + + // Clients can request a baseline of currently existing threads after + // we acknowledge the configurationDone request. + // Client requests the baseline of currently existing threads after + // a successful or attach by sending a 'threads' request + // right after receiving the configurationDone response. + // Obtain the list of threads before we resume the process + dap.initial_thread_list = + GetThreads(dap.target.GetProcess(), dap.thread_format); + return llvm::Error::success(); } diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index 37cc902e1c98e..9e9cfb13d77b8 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.h +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h @@ -282,6 +282,7 @@ class InitializeRequestHandler static llvm::StringLiteral GetCommand() { return "initialize"; } llvm::Expected Run(const protocol::InitializeRequestArguments &args) const override; + void PostRun() const override; }; class LaunchRequestHandler From lldb-commits at lists.llvm.org Tue May 6 15:58:50 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Tue, 06 May 2025 15:58:50 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Change the launch sequence (PR #138219) In-Reply-To: Message-ID: <681a942a.170a0220.11f495.4887@mx.google.com> https://github.com/JDevlieghere closed https://github.com/llvm/llvm-project/pull/138219 From lldb-commits at lists.llvm.org Tue May 6 16:06:07 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Tue, 06 May 2025 16:06:07 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Change the launch sequence (PR #138219) In-Reply-To: Message-ID: <681a95df.170a0220.227fd7.6e4f@mx.google.com> JDevlieghere wrote: I created #138778 to track extending the launch and attach helpers to take breakpoints. https://github.com/llvm/llvm-project/pull/138219 From lldb-commits at lists.llvm.org Tue May 6 16:06:58 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Tue, 06 May 2025 16:06:58 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Ensure we acquire the SB API lock while handling requests. (PR #137026) In-Reply-To: Message-ID: <681a9611.050a0220.3520dc.c2f4@mx.google.com> ashgti wrote: We could revert this and try to add more specific locks in request handlers instead. https://github.com/llvm/llvm-project/pull/137026 From lldb-commits at lists.llvm.org Tue May 6 16:11:21 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Tue, 06 May 2025 16:11:21 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add IsCoreDumping to ProcessInstanceInfo (PR #138580) In-Reply-To: Message-ID: <681a9719.630a0220.d1886.3e5c@mx.google.com> https://github.com/Jlalond updated https://github.com/llvm/llvm-project/pull/138580 >From 03d5449b30f414e2a3f322f102101dc4b2c05c4f Mon Sep 17 00:00:00 2001 From: Jacob Lalonde Date: Mon, 5 May 2025 11:33:05 -0700 Subject: [PATCH 1/3] Add IsCoredumping to ProcessInfo to use in the future of the ptrace_seize code --- lldb/include/lldb/Utility/ProcessInfo.h | 6 ++++++ lldb/source/Host/linux/Host.cpp | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/lldb/include/lldb/Utility/ProcessInfo.h b/lldb/include/lldb/Utility/ProcessInfo.h index 78ade4bbb1ee6..24041faad80bf 100644 --- a/lldb/include/lldb/Utility/ProcessInfo.h +++ b/lldb/include/lldb/Utility/ProcessInfo.h @@ -247,6 +247,11 @@ class ProcessInstanceInfo : public ProcessInfo { std::optional IsZombie() const { return m_zombie; } + // proc/../status specifies CoreDumping as the field + // so we match the case here. + void SetIsCoreDumping(bool is_coredumping) { m_coredumping = is_coredumping; } + std::optional IsCoreDumping() const { return m_coredumping; } + void Dump(Stream &s, UserIDResolver &resolver) const; static void DumpTableHeader(Stream &s, bool show_args, bool verbose); @@ -266,6 +271,7 @@ class ProcessInstanceInfo : public ProcessInfo { struct timespec m_cumulative_system_time; std::optional m_priority_value = std::nullopt; std::optional m_zombie = std::nullopt; + std::optional m_coredumping = std::nullopt; }; typedef std::vector ProcessInstanceInfoList; diff --git a/lldb/source/Host/linux/Host.cpp b/lldb/source/Host/linux/Host.cpp index 8b475a7ab5003..97e8024da6c07 100644 --- a/lldb/source/Host/linux/Host.cpp +++ b/lldb/source/Host/linux/Host.cpp @@ -213,6 +213,11 @@ static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo, } else if (Line.consume_front("Tgid:")) { Line = Line.ltrim(); Line.consumeInteger(10, Tgid); + } else if (Line.consume_front("CoreDumping:")) { + uint32_t coredumping; + Line = Line.ltrim(); + Line.consumeInteger(1, coredumping); + ProcessInfo.SetIsCoredumping(coredumping); } } return true; >From 7e2b11ff8d63260d34ff63e9eeb2d519d887742c Mon Sep 17 00:00:00 2001 From: Jacob Lalonde Date: Mon, 5 May 2025 13:18:31 -0700 Subject: [PATCH 2/3] Add test --- lldb/source/Host/linux/Host.cpp | 2 +- lldb/unittests/Host/posix/HostTest.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lldb/source/Host/linux/Host.cpp b/lldb/source/Host/linux/Host.cpp index 97e8024da6c07..2e2d4fdd84097 100644 --- a/lldb/source/Host/linux/Host.cpp +++ b/lldb/source/Host/linux/Host.cpp @@ -217,7 +217,7 @@ static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo, uint32_t coredumping; Line = Line.ltrim(); Line.consumeInteger(1, coredumping); - ProcessInfo.SetIsCoredumping(coredumping); + ProcessInfo.SetIsCoreDumping(coredumping); } } return true; diff --git a/lldb/unittests/Host/posix/HostTest.cpp b/lldb/unittests/Host/posix/HostTest.cpp index 5d50de3524d1e..082edccf4e774 100644 --- a/lldb/unittests/Host/posix/HostTest.cpp +++ b/lldb/unittests/Host/posix/HostTest.cpp @@ -115,5 +115,8 @@ TEST_F(HostTest, GetProcessInfoSetsPriority) { } ASSERT_TRUE(Info.IsZombie().has_value()); ASSERT_FALSE(Info.IsZombie().value()); + + ASSERT_TRUE(Info.IsCoreDumping().has_value()); + ASSERT_FALSE(Info.IsCoreDumping().value()); } #endif >From 697912d258822a18a7e38e29125cbb72fceecafa Mon Sep 17 00:00:00 2001 From: Jacob Lalonde Date: Tue, 6 May 2025 16:11:06 -0700 Subject: [PATCH 3/3] Handle error in case we fail to parse CoreDumping --- lldb/source/Host/linux/Host.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lldb/source/Host/linux/Host.cpp b/lldb/source/Host/linux/Host.cpp index 2e2d4fdd84097..b5f050426d88b 100644 --- a/lldb/source/Host/linux/Host.cpp +++ b/lldb/source/Host/linux/Host.cpp @@ -216,8 +216,8 @@ static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo, } else if (Line.consume_front("CoreDumping:")) { uint32_t coredumping; Line = Line.ltrim(); - Line.consumeInteger(1, coredumping); - ProcessInfo.SetIsCoreDumping(coredumping); + if (!Line.consumeInteger(2, coredumping)) + ProcessInfo.SetIsCoreDumping(coredumping); } } return true; From lldb-commits at lists.llvm.org Tue May 6 16:16:01 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Tue, 06 May 2025 16:16:01 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Ensure we acquire the SB API lock while handling requests. (PR #137026) In-Reply-To: Message-ID: <681a9831.630a0220.216826.58fc@mx.google.com> JDevlieghere wrote: > High level, I do not think it is a good idea to hold the top level API mutex which is way too large locking scope. We should leave the decision to each request handler for smaller scope locks if they want to ensure a critical section. Can we revert the PR? FWIW I don't necessarily agree with this observation. While I agree that a smaller critical section is better, conceptually a single request corresponds to a group of SB API calls. Making it the responsibility of the requests to group all SB API calls within the scope of the lock is a lot more error prone. What is the other lock involved in the deadlock, other than the API mutex? https://github.com/llvm/llvm-project/pull/137026 From lldb-commits at lists.llvm.org Tue May 6 16:24:50 2025 From: lldb-commits at lists.llvm.org (Jan Svoboda via lldb-commits) Date: Tue, 06 May 2025 16:24:50 -0700 (PDT) Subject: [Lldb-commits] [clang] [clang-tools-extra] [lldb] [clang][modules] Lazily load by name lookups in module maps (PR #132853) In-Reply-To: Message-ID: <681a9a42.050a0220.4359c.c235@mx.google.com> jansvoboda11 wrote: Test change LGTM, I was probably trying to get to a minimal test-case and ended up with something that was relying on implementation details. https://github.com/llvm/llvm-project/pull/132853 From lldb-commits at lists.llvm.org Tue May 6 16:34:58 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Tue, 06 May 2025 16:34:58 -0700 (PDT) Subject: [Lldb-commits] [lldb] Branch island with numbers (PR #138781) Message-ID: https://github.com/jimingham created https://github.com/llvm/llvm-project/pull/138781 Reapply the support for stepping through branch islands, add support for a branch that takes multiple hops to get to the target. Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Tue May 6 16:35:32 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Tue, 06 May 2025 16:35:32 -0700 (PDT) Subject: [Lldb-commits] [lldb] Branch island with numbers (PR #138781) In-Reply-To: Message-ID: <681a9cc4.170a0220.10c52d.5a98@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: None (jimingham)
Changes Reapply the support for stepping through branch islands, add support for a branch that takes multiple hops to get to the target. --- Full diff: https://github.com/llvm/llvm-project/pull/138781.diff 9 Files Affected: - (modified) lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp (+25-7) - (added) lldb/test/API/macosx/branch-islands/Makefile (+16) - (added) lldb/test/API/macosx/branch-islands/TestBranchIslands.py (+35) - (added) lldb/test/API/macosx/branch-islands/foo.c (+6) - (added) lldb/test/API/macosx/branch-islands/main.c (+6) - (added) lldb/test/API/macosx/branch-islands/padding1.s (+3) - (added) lldb/test/API/macosx/branch-islands/padding2.s (+3) - (added) lldb/test/API/macosx/branch-islands/padding3.s (+3) - (added) lldb/test/API/macosx/branch-islands/padding4.s (+3) ``````````diff diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index e25c4ff55e408..ad3b63cf59135 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -26,6 +26,7 @@ #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Utility/DataBuffer.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/LLDBLog.h" @@ -923,15 +924,15 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, if (current_symbol != nullptr) { std::vector
addresses; + ConstString current_name = + current_symbol->GetMangled().GetName(Mangled::ePreferMangled); if (current_symbol->IsTrampoline()) { - ConstString trampoline_name = - current_symbol->GetMangled().GetName(Mangled::ePreferMangled); - if (trampoline_name) { + if (current_name) { const ModuleList &images = target_sp->GetImages(); SymbolContextList code_symbols; - images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeCode, + images.FindSymbolsWithNameAndType(current_name, eSymbolTypeCode, code_symbols); for (const SymbolContext &context : code_symbols) { Address addr = context.GetFunctionOrSymbolAddress(); @@ -945,8 +946,8 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, } SymbolContextList reexported_symbols; - images.FindSymbolsWithNameAndType( - trampoline_name, eSymbolTypeReExported, reexported_symbols); + images.FindSymbolsWithNameAndType(current_name, eSymbolTypeReExported, + reexported_symbols); for (const SymbolContext &context : reexported_symbols) { if (context.symbol) { Symbol *actual_symbol = @@ -968,7 +969,7 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, } SymbolContextList indirect_symbols; - images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeResolver, + images.FindSymbolsWithNameAndType(current_name, eSymbolTypeResolver, indirect_symbols); for (const SymbolContext &context : indirect_symbols) { @@ -1028,6 +1029,23 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, thread_plan_sp = std::make_shared( thread, load_addrs, stop_others); } + // One more case we have to consider is "branch islands". These are regular + // TEXT symbols but their names end in .island plus maybe a .digit suffix. + // They are to allow arm64 code to branch further than the size of the + // address slot allows. We just need to single-instruction step in that + // case. + static const char *g_branch_island_pattern = "\\.island\\.?[0-9]*$"; + static RegularExpression g_branch_island_regex(g_branch_island_pattern); + + bool is_branch_island = g_branch_island_regex.Execute(current_name); + if (!thread_plan_sp && is_branch_island) { + thread_plan_sp = std::make_shared( + thread, + /* step_over= */ false, /* stop_others */ false, eVoteNoOpinion, + eVoteNoOpinion); + LLDB_LOG(log, "Stepping one instruction over branch island: '{0}'.", + current_name); + } } else { LLDB_LOGF(log, "Could not find symbol for step through."); } diff --git a/lldb/test/API/macosx/branch-islands/Makefile b/lldb/test/API/macosx/branch-islands/Makefile new file mode 100644 index 0000000000000..062e947f6d6ee --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/Makefile @@ -0,0 +1,16 @@ +C_SOURCES := main.c foo.c +CFLAGS_EXTRAS := -std=c99 + +include Makefile.rules + +a.out: main.o padding1.o padding2.o padding3.o padding4.o foo.o + ${CC} ${LDFLAGS} foo.o padding1.o padding2.o padding3.o padding4.o main.o -o a.out + +%.o: $(SRCDIR)/%.s + ${CC} -c $< + +#padding1.o: padding1.s +# ${CC} -c $(SRCDIR)/padding1.s + +#padding2.o: padding2.s +# ${CC} -c $(SRCDIR)/padding2.s diff --git a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py new file mode 100644 index 0000000000000..b397e0c229b08 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py @@ -0,0 +1,35 @@ +""" +Make sure that we can step in across an arm64 branch island +""" + + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * + + +class TestBranchIslandStepping(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + @skipUnlessDarwin + def test_step_in_branch_island(self): + """Make sure we can step in across a branch island""" + self.build() + self.main_source_file = lldb.SBFileSpec("main.c") + self.do_test() + + def do_test(self): + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "Set a breakpoint here", self.main_source_file + ) + + # Make sure that we did manage to generate a branch island for foo: + syms = target.FindSymbols("foo.island", lldb.eSymbolTypeCode) + self.assertEqual(len(syms), 1, "We did generate an island for foo") + + thread.StepInto() + stop_frame = thread.frames[0] + self.assertIn("foo", stop_frame.name, "Stepped into foo") + var = stop_frame.FindVariable("a_variable_in_foo") + self.assertTrue(var.IsValid(), "Found the variable in foo") diff --git a/lldb/test/API/macosx/branch-islands/foo.c b/lldb/test/API/macosx/branch-islands/foo.c new file mode 100644 index 0000000000000..a5dd2e59e1d82 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/foo.c @@ -0,0 +1,6 @@ +#include + +void foo() { + int a_variable_in_foo = 10; + printf("I am foo: %d.\n", a_variable_in_foo); +} diff --git a/lldb/test/API/macosx/branch-islands/main.c b/lldb/test/API/macosx/branch-islands/main.c new file mode 100644 index 0000000000000..b5578bdd715df --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/main.c @@ -0,0 +1,6 @@ +extern void foo(); + +int main() { + foo(); // Set a breakpoint here + return 0; +} diff --git a/lldb/test/API/macosx/branch-islands/padding1.s b/lldb/test/API/macosx/branch-islands/padding1.s new file mode 100644 index 0000000000000..4911e53b0240d --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding1.s @@ -0,0 +1,3 @@ +.text +_padding1: +.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding2.s b/lldb/test/API/macosx/branch-islands/padding2.s new file mode 100644 index 0000000000000..5ad1bad11263b --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding2.s @@ -0,0 +1,3 @@ +.text +_padding2: +.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding3.s b/lldb/test/API/macosx/branch-islands/padding3.s new file mode 100644 index 0000000000000..9f614eecf56d9 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding3.s @@ -0,0 +1,3 @@ +.text +_padding3: +.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding4.s b/lldb/test/API/macosx/branch-islands/padding4.s new file mode 100644 index 0000000000000..12896cf5e5b8e --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding4.s @@ -0,0 +1,3 @@ +.text +_padding4: +.space 120*1024*1024 ``````````
https://github.com/llvm/llvm-project/pull/138781 From lldb-commits at lists.llvm.org Tue May 6 16:37:19 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Tue, 06 May 2025 16:37:19 -0700 (PDT) Subject: [Lldb-commits] [lldb] Branch island with numbers (PR #138781) In-Reply-To: Message-ID: <681a9d2f.170a0220.25a563.40e3@mx.google.com> github-actions[bot] wrote: :warning: C/C++ code formatter, clang-format found issues in your code. :warning:
You can test this locally with the following command: ``````````bash git-clang-format --diff HEAD~1 HEAD --extensions c,cpp -- lldb/test/API/macosx/branch-islands/foo.c lldb/test/API/macosx/branch-islands/main.c lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp ``````````
View the diff from clang-format here. ``````````diff diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index ad3b63cf5..578ab1226 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -1030,9 +1030,9 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, thread, load_addrs, stop_others); } // One more case we have to consider is "branch islands". These are regular - // TEXT symbols but their names end in .island plus maybe a .digit suffix. - // They are to allow arm64 code to branch further than the size of the - // address slot allows. We just need to single-instruction step in that + // TEXT symbols but their names end in .island plus maybe a .digit suffix. + // They are to allow arm64 code to branch further than the size of the + // address slot allows. We just need to single-instruction step in that // case. static const char *g_branch_island_pattern = "\\.island\\.?[0-9]*$"; static RegularExpression g_branch_island_regex(g_branch_island_pattern); ``````````
https://github.com/llvm/llvm-project/pull/138781 From lldb-commits at lists.llvm.org Tue May 6 16:39:36 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Tue, 06 May 2025 16:39:36 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Add support for displaying `__float128` variables (PR #98369) In-Reply-To: Message-ID: <681a9db8.630a0220.3d316a.5c0d@mx.google.com> https://github.com/beetrees updated https://github.com/llvm/llvm-project/pull/98369 >From 8c0d1c67386c5a380ce8603562483a45ac30db72 Mon Sep 17 00:00:00 2001 From: beetrees Date: Wed, 10 Jul 2024 18:49:45 +0100 Subject: [PATCH] [lldb] Add support for displaying `__float128` variables --- lldb/bindings/python/python-extensions.swig | 1 + lldb/docs/python_api_enums.rst | 2 ++ lldb/include/lldb/lldb-enumerations.h | 7 ++++++- lldb/source/Commands/CommandObjectMemory.cpp | 2 ++ lldb/source/Core/DumpDataExtractor.cpp | 5 ++++- lldb/source/DataFormatters/FormatManager.cpp | 1 + lldb/source/DataFormatters/VectorType.cpp | 2 ++ .../TypeSystem/Clang/TypeSystemClang.cpp | 21 +++++++++++++++++++ lldb/source/ValueObject/ValueObject.cpp | 5 +++-- lldb/unittests/Core/DumpDataExtractorTest.cpp | 6 ++++++ lldb/unittests/Symbol/TestTypeSystemClang.cpp | 2 ++ 11 files changed, 50 insertions(+), 4 deletions(-) diff --git a/lldb/bindings/python/python-extensions.swig b/lldb/bindings/python/python-extensions.swig index 4ba1607c70909..40fa76872ee96 100644 --- a/lldb/bindings/python/python-extensions.swig +++ b/lldb/bindings/python/python-extensions.swig @@ -594,6 +594,7 @@ def is_numeric_type(basic_type): if basic_type == eBasicTypeFloat: return (True,True) if basic_type == eBasicTypeDouble: return (True,True) if basic_type == eBasicTypeLongDouble: return (True,True) + if basic_type == eBasicTypeFloat128: return (True,True) if basic_type == eBasicTypeFloatComplex: return (True,True) if basic_type == eBasicTypeDoubleComplex: return (True,True) if basic_type == eBasicTypeLongDoubleComplex: return (True,True) diff --git a/lldb/docs/python_api_enums.rst b/lldb/docs/python_api_enums.rst index b6a2497ea878e..a43a47b8d6985 100644 --- a/lldb/docs/python_api_enums.rst +++ b/lldb/docs/python_api_enums.rst @@ -321,6 +321,7 @@ Format .. py:data:: eFormatInstruction .. py:data:: eFormatVoid .. py:data:: eFormatUnicode8 +.. py:data:: eFormatFloat128 .. _DescriptionLevel: @@ -1045,6 +1046,7 @@ BasicType .. py:data:: eBasicTypeObjCSel .. py:data:: eBasicTypeNullPtr .. py:data:: eBasicTypeOther +.. py:data:: eBasicTypeFloat128 .. _TraceType: diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index 6d10cc8bcffcb..311a18b850e0a 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -203,6 +203,10 @@ enum Format { eFormatInstruction, ///< Disassemble an opcode eFormatVoid, ///< Do not print this eFormatUnicode8, + eFormatFloat128, ///< Disambiguate between 128-bit `long double` (which uses + ///< `eFormatFloat`) and `__float128` (which uses + ///< `eFormatFloat128`). If the value being formatted is not + ///< 128 bits, then this is identical to `eFormatFloat`. kNumFormats }; @@ -837,7 +841,8 @@ enum BasicType { eBasicTypeObjCClass, eBasicTypeObjCSel, eBasicTypeNullPtr, - eBasicTypeOther + eBasicTypeOther, + eBasicTypeFloat128 }; /// Deprecated diff --git a/lldb/source/Commands/CommandObjectMemory.cpp b/lldb/source/Commands/CommandObjectMemory.cpp index 7140333bb3cde..5ccd4be7741e6 100644 --- a/lldb/source/Commands/CommandObjectMemory.cpp +++ b/lldb/source/Commands/CommandObjectMemory.cpp @@ -156,6 +156,7 @@ class OptionGroupReadMemory : public OptionGroup { case eFormatBinary: case eFormatFloat: + case eFormatFloat128: case eFormatOctal: case eFormatDecimal: case eFormatEnum: @@ -1330,6 +1331,7 @@ class CommandObjectMemoryWrite : public CommandObjectParsed { switch (m_format_options.GetFormat()) { case kNumFormats: case eFormatFloat: // TODO: add support for floats soon + case eFormatFloat128: case eFormatCharPrintable: case eFormatBytesWithASCII: case eFormatComplex: diff --git a/lldb/source/Core/DumpDataExtractor.cpp b/lldb/source/Core/DumpDataExtractor.cpp index 72140736d8877..5a6670c96e408 100644 --- a/lldb/source/Core/DumpDataExtractor.cpp +++ b/lldb/source/Core/DumpDataExtractor.cpp @@ -653,6 +653,7 @@ lldb::offset_t lldb_private::DumpDataExtractor( } } break; + case eFormatFloat128: case eFormatFloat: { TargetSP target_sp; if (exe_scope) @@ -666,7 +667,9 @@ lldb::offset_t lldb_private::DumpDataExtractor( const unsigned format_precision = 0; const llvm::fltSemantics &semantics = - GetFloatSemantics(target_sp, item_byte_size); + item_format == eFormatFloat128 && item_byte_size == 16 + ? llvm::APFloat::IEEEquad() + : GetFloatSemantics(target_sp, item_byte_size); // Recalculate the byte size in case of a difference. This is possible // when item_byte_size is 16 (128-bit), because you could get back the diff --git a/lldb/source/DataFormatters/FormatManager.cpp b/lldb/source/DataFormatters/FormatManager.cpp index 3b891cecb1c8b..6ff4948eec34a 100644 --- a/lldb/source/DataFormatters/FormatManager.cpp +++ b/lldb/source/DataFormatters/FormatManager.cpp @@ -72,6 +72,7 @@ static constexpr FormatInfo g_format_infos[] = { {eFormatInstruction, 'i', "instruction"}, {eFormatVoid, 'v', "void"}, {eFormatUnicode8, 'u', "unicode8"}, + {eFormatFloat128, '\0', "float128"}, }; static_assert((sizeof(g_format_infos) / sizeof(g_format_infos[0])) == diff --git a/lldb/source/DataFormatters/VectorType.cpp b/lldb/source/DataFormatters/VectorType.cpp index eab2612d1e941..2cea366c44adf 100644 --- a/lldb/source/DataFormatters/VectorType.cpp +++ b/lldb/source/DataFormatters/VectorType.cpp @@ -55,6 +55,8 @@ static CompilerType GetCompilerTypeForFormat(lldb::Format format, case lldb::eFormatFloat: return type_system->GetBasicTypeFromAST(lldb::eBasicTypeFloat); + case lldb::eFormatFloat128: + return type_system->GetBasicTypeFromAST(lldb::eBasicTypeFloat128); case lldb::eFormatHex: case lldb::eFormatHexUppercase: diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 1a2b3d4133e51..5be2f4487d8d8 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -809,6 +809,8 @@ TypeSystemClang::GetBuiltinTypeForEncodingAndBitSize(Encoding encoding, return GetType(ast.LongDoubleTy); if (QualTypeMatchesBitSize(bit_size, ast, ast.HalfTy)) return GetType(ast.HalfTy); + if (QualTypeMatchesBitSize(bit_size, ast, ast.Float128Ty)) + return GetType(ast.Float128Ty); break; case eEncodingVector: @@ -970,6 +972,13 @@ CompilerType TypeSystemClang::GetBuiltinTypeForDWARFEncodingAndBitSize( if (type_name == "long double" && QualTypeMatchesBitSize(bit_size, ast, ast.LongDoubleTy)) return GetType(ast.LongDoubleTy); + // As Rust currently uses `TypeSystemClang`, match `f128` here as well so it + // doesn't get misinterpreted as `long double` on targets where they are + // the same size but different formats. + if ((type_name == "__float128" || type_name == "_Float128" || + type_name == "f128") && + QualTypeMatchesBitSize(bit_size, ast, ast.Float128Ty)) + return GetType(ast.Float128Ty); // Fall back to not requiring a name match if (QualTypeMatchesBitSize(bit_size, ast, ast.FloatTy)) return GetType(ast.FloatTy); @@ -979,6 +988,8 @@ CompilerType TypeSystemClang::GetBuiltinTypeForDWARFEncodingAndBitSize( return GetType(ast.LongDoubleTy); if (QualTypeMatchesBitSize(bit_size, ast, ast.HalfTy)) return GetType(ast.HalfTy); + if (QualTypeMatchesBitSize(bit_size, ast, ast.Float128Ty)) + return GetType(ast.Float128Ty); break; case DW_ATE_signed: @@ -2068,6 +2079,8 @@ TypeSystemClang::GetOpaqueCompilerType(clang::ASTContext *ast, return ast->DoubleTy.getAsOpaquePtr(); case eBasicTypeLongDouble: return ast->LongDoubleTy.getAsOpaquePtr(); + case eBasicTypeFloat128: + return ast->Float128Ty.getAsOpaquePtr(); case eBasicTypeFloatComplex: return ast->getComplexType(ast->FloatTy).getAsOpaquePtr(); case eBasicTypeDoubleComplex: @@ -4750,6 +4763,8 @@ TypeSystemClang::GetFloatTypeSemantics(size_t byte_size) { return ast.getFloatTypeSemantics(ast.LongDoubleTy); else if (bit_size == ast.getTypeSize(ast.HalfTy)) return ast.getFloatTypeSemantics(ast.HalfTy); + else if (bit_size == ast.getTypeSize(ast.Float128Ty)) + return ast.getFloatTypeSemantics(ast.Float128Ty); return llvm::APFloatBase::Bogus(); } @@ -5222,6 +5237,8 @@ lldb::Format TypeSystemClang::GetFormat(lldb::opaque_compiler_type_t type) { case clang::BuiltinType::Double: case clang::BuiltinType::LongDouble: return lldb::eFormatFloat; + case clang::BuiltinType::Float128: + return lldb::eFormatFloat128; default: return lldb::eFormatHex; } @@ -5545,6 +5562,8 @@ TypeSystemClang::GetBasicTypeEnumeration(lldb::opaque_compiler_type_t type) { return eBasicTypeDouble; case clang::BuiltinType::LongDouble: return eBasicTypeLongDouble; + case clang::BuiltinType::Float128: + return eBasicTypeFloat128; case clang::BuiltinType::NullPtr: return eBasicTypeNullPtr; @@ -6106,6 +6125,7 @@ uint32_t TypeSystemClang::GetNumPointeeChildren(clang::QualType type) { case clang::BuiltinType::Float: case clang::BuiltinType::Double: case clang::BuiltinType::LongDouble: + case clang::BuiltinType::Float128: case clang::BuiltinType::Dependent: case clang::BuiltinType::Overload: case clang::BuiltinType::ObjCId: @@ -8837,6 +8857,7 @@ bool TypeSystemClang::DumpTypeValue( case eFormatHex: case eFormatHexUppercase: case eFormatFloat: + case eFormatFloat128: case eFormatOctal: case eFormatOSType: case eFormatUnsigned: diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp index e1c66763ff0b8..ddd95122aafa3 100644 --- a/lldb/source/ValueObject/ValueObject.cpp +++ b/lldb/source/ValueObject/ValueObject.cpp @@ -1468,8 +1468,9 @@ bool ValueObject::DumpPrintableRepresentation( (custom_format == eFormatComplexFloat) || (custom_format == eFormatDecimal) || (custom_format == eFormatHex) || (custom_format == eFormatHexUppercase) || - (custom_format == eFormatFloat) || (custom_format == eFormatOctal) || - (custom_format == eFormatOSType) || + (custom_format == eFormatFloat) || + (custom_format == eFormatFloat128) || + (custom_format == eFormatOctal) || (custom_format == eFormatOSType) || (custom_format == eFormatUnicode16) || (custom_format == eFormatUnicode32) || (custom_format == eFormatUnsigned) || diff --git a/lldb/unittests/Core/DumpDataExtractorTest.cpp b/lldb/unittests/Core/DumpDataExtractorTest.cpp index 3d1e8bc5e4623..6302f1e1d31a6 100644 --- a/lldb/unittests/Core/DumpDataExtractorTest.cpp +++ b/lldb/unittests/Core/DumpDataExtractorTest.cpp @@ -163,6 +163,9 @@ TEST_F(DumpDataExtractorTest, Formats) { TestDump(0xcafef00d, lldb::Format::eFormatHex, "0xcafef00d"); TestDump(0xcafef00d, lldb::Format::eFormatHexUppercase, "0xCAFEF00D"); TestDump(0.456, lldb::Format::eFormatFloat, "0.45600000000000002"); + TestDump(std::vector{0x47ae147ae147ae14, 0x40011147ae147ae1}, + lldb::Format::eFormatFloat128, + "4.26999999999999999999999999999999963"); TestDump(9, lldb::Format::eFormatOctal, "011"); // Chars packed into an integer. TestDump(0x4C4C4442, lldb::Format::eFormatOSType, "'LLDB'"); @@ -388,6 +391,9 @@ TEST_F(DumpDataExtractorTest, ItemByteSizeErrors) { TestDumpWithItemByteSize( 18, lldb::Format::eFormatFloat, "error: unsupported byte size (18) for float format"); + TestDumpWithItemByteSize( + 17, lldb::Format::eFormatFloat128, + "error: unsupported byte size (17) for float format"); // We want sizes to exactly match one of float/double. TestDumpWithItemByteSize( diff --git a/lldb/unittests/Symbol/TestTypeSystemClang.cpp b/lldb/unittests/Symbol/TestTypeSystemClang.cpp index a9b0c87c4fbce..a3a982ee596a9 100644 --- a/lldb/unittests/Symbol/TestTypeSystemClang.cpp +++ b/lldb/unittests/Symbol/TestTypeSystemClang.cpp @@ -76,6 +76,8 @@ TEST_F(TestTypeSystemClang, TestGetBasicTypeFromEnum) { context.getComplexType(context.FloatTy))); EXPECT_TRUE( context.hasSameType(GetBasicQualType(eBasicTypeHalf), context.HalfTy)); + EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeFloat128), + context.Float128Ty)); EXPECT_TRUE( context.hasSameType(GetBasicQualType(eBasicTypeInt), context.IntTy)); EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeInt128), From lldb-commits at lists.llvm.org Tue May 6 16:40:07 2025 From: lldb-commits at lists.llvm.org (Michael Spencer via lldb-commits) Date: Tue, 06 May 2025 16:40:07 -0700 (PDT) Subject: [Lldb-commits] [clang] [clang-tools-extra] [lldb] [clang][modules] Lazily load by name lookups in module maps (PR #132853) In-Reply-To: Message-ID: <681a9dd7.170a0220.31efc4.b945@mx.google.com> https://github.com/Bigcheese closed https://github.com/llvm/llvm-project/pull/132853 From lldb-commits at lists.llvm.org Tue May 6 16:53:52 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Tue, 06 May 2025 16:53:52 -0700 (PDT) Subject: [Lldb-commits] [lldb] Branch island with numbers (PR #138781) In-Reply-To: Message-ID: <681aa110.170a0220.248942.599f@mx.google.com> https://github.com/jimingham updated https://github.com/llvm/llvm-project/pull/138781 >From 9bcf63344ba475c73029c5c44975bfab57117f80 Mon Sep 17 00:00:00 2001 From: Jim Ingham Date: Mon, 5 May 2025 15:02:22 -0700 Subject: [PATCH 1/3] Revert "Revert "Handle step-in over a Darwin "branch island". (#138330)"" This reverts commit b1b7e6df31a54c4da5b30454ea910d89b0ce924b. --- .../MacOSX-DYLD/DynamicLoaderDarwin.cpp | 27 ++++++++++---- lldb/test/API/macosx/branch-islands/Makefile | 13 +++++++ .../branch-islands/TestBranchIslands.py | 35 +++++++++++++++++++ lldb/test/API/macosx/branch-islands/foo.c | 6 ++++ lldb/test/API/macosx/branch-islands/main.c | 6 ++++ .../test/API/macosx/branch-islands/padding1.s | 3 ++ .../test/API/macosx/branch-islands/padding2.s | 3 ++ 7 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 lldb/test/API/macosx/branch-islands/Makefile create mode 100644 lldb/test/API/macosx/branch-islands/TestBranchIslands.py create mode 100644 lldb/test/API/macosx/branch-islands/foo.c create mode 100644 lldb/test/API/macosx/branch-islands/main.c create mode 100644 lldb/test/API/macosx/branch-islands/padding1.s create mode 100644 lldb/test/API/macosx/branch-islands/padding2.s diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index e25c4ff55e408..52c50cd88902a 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -26,6 +26,7 @@ #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Utility/DataBuffer.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/LLDBLog.h" @@ -923,15 +924,15 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, if (current_symbol != nullptr) { std::vector
addresses; + ConstString current_name = + current_symbol->GetMangled().GetName(Mangled::ePreferMangled); if (current_symbol->IsTrampoline()) { - ConstString trampoline_name = - current_symbol->GetMangled().GetName(Mangled::ePreferMangled); - if (trampoline_name) { + if (current_name) { const ModuleList &images = target_sp->GetImages(); SymbolContextList code_symbols; - images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeCode, + images.FindSymbolsWithNameAndType(current_name, eSymbolTypeCode, code_symbols); for (const SymbolContext &context : code_symbols) { Address addr = context.GetFunctionOrSymbolAddress(); @@ -945,8 +946,8 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, } SymbolContextList reexported_symbols; - images.FindSymbolsWithNameAndType( - trampoline_name, eSymbolTypeReExported, reexported_symbols); + images.FindSymbolsWithNameAndType(current_name, eSymbolTypeReExported, + reexported_symbols); for (const SymbolContext &context : reexported_symbols) { if (context.symbol) { Symbol *actual_symbol = @@ -968,7 +969,7 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, } SymbolContextList indirect_symbols; - images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeResolver, + images.FindSymbolsWithNameAndType(current_name, eSymbolTypeResolver, indirect_symbols); for (const SymbolContext &context : indirect_symbols) { @@ -1028,6 +1029,18 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, thread_plan_sp = std::make_shared( thread, load_addrs, stop_others); } + // One more case we have to consider is "branch islands". These are regular + // TEXT symbols but their names end in .island. They are to allow arm64 + // code to branch further than the size of the address slot allows. We + // just need to single-instruction step in that case. + if (!thread_plan_sp && current_name.GetStringRef().ends_with(".island")) { + thread_plan_sp = std::make_shared( + thread, + /* step_over= */ false, /* stop_others */ false, eVoteNoOpinion, + eVoteNoOpinion); + LLDB_LOG(log, "Stepping one instruction over branch island: '{0}'.", + current_name); + } } else { LLDB_LOGF(log, "Could not find symbol for step through."); } diff --git a/lldb/test/API/macosx/branch-islands/Makefile b/lldb/test/API/macosx/branch-islands/Makefile new file mode 100644 index 0000000000000..8675bbf6f85de --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/Makefile @@ -0,0 +1,13 @@ +C_SOURCES := main.c foo.c +CFLAGS_EXTRAS := -std=c99 + +include Makefile.rules + +a.out: main.o padding1.o padding2.o foo.o + ${CC} ${LDFLAGS} foo.o padding1.o padding2.o main.o -o a.out + +padding1.o: padding1.s + ${CC} -c $(SRCDIR)/padding1.s + +padding2.o: padding2.s + ${CC} -c $(SRCDIR)/padding2.s diff --git a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py new file mode 100644 index 0000000000000..b397e0c229b08 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py @@ -0,0 +1,35 @@ +""" +Make sure that we can step in across an arm64 branch island +""" + + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * + + +class TestBranchIslandStepping(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + @skipUnlessDarwin + def test_step_in_branch_island(self): + """Make sure we can step in across a branch island""" + self.build() + self.main_source_file = lldb.SBFileSpec("main.c") + self.do_test() + + def do_test(self): + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "Set a breakpoint here", self.main_source_file + ) + + # Make sure that we did manage to generate a branch island for foo: + syms = target.FindSymbols("foo.island", lldb.eSymbolTypeCode) + self.assertEqual(len(syms), 1, "We did generate an island for foo") + + thread.StepInto() + stop_frame = thread.frames[0] + self.assertIn("foo", stop_frame.name, "Stepped into foo") + var = stop_frame.FindVariable("a_variable_in_foo") + self.assertTrue(var.IsValid(), "Found the variable in foo") diff --git a/lldb/test/API/macosx/branch-islands/foo.c b/lldb/test/API/macosx/branch-islands/foo.c new file mode 100644 index 0000000000000..a5dd2e59e1d82 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/foo.c @@ -0,0 +1,6 @@ +#include + +void foo() { + int a_variable_in_foo = 10; + printf("I am foo: %d.\n", a_variable_in_foo); +} diff --git a/lldb/test/API/macosx/branch-islands/main.c b/lldb/test/API/macosx/branch-islands/main.c new file mode 100644 index 0000000000000..b5578bdd715df --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/main.c @@ -0,0 +1,6 @@ +extern void foo(); + +int main() { + foo(); // Set a breakpoint here + return 0; +} diff --git a/lldb/test/API/macosx/branch-islands/padding1.s b/lldb/test/API/macosx/branch-islands/padding1.s new file mode 100644 index 0000000000000..e3bdf7007d757 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding1.s @@ -0,0 +1,3 @@ +.text +_junk1: +.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding2.s b/lldb/test/API/macosx/branch-islands/padding2.s new file mode 100644 index 0000000000000..187a2c3ebd117 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding2.s @@ -0,0 +1,3 @@ +.text +_junk2: +.space 120*1024*1024 >From 2d614757a2f1d2f78b1a844cff66451b66f58b75 Mon Sep 17 00:00:00 2001 From: Jim Ingham Date: Tue, 6 May 2025 16:31:21 -0700 Subject: [PATCH 2/3] Fix the branch island handling to account for the other possible forms: '.island2' and '.island.2'. Also add more padding so that we produce an island that takes several jumps. --- .../MacOSX-DYLD/DynamicLoaderDarwin.cpp | 13 +++++++++---- lldb/test/API/macosx/branch-islands/Makefile | 15 +++++++++------ lldb/test/API/macosx/branch-islands/padding1.s | 2 +- lldb/test/API/macosx/branch-islands/padding2.s | 2 +- lldb/test/API/macosx/branch-islands/padding3.s | 3 +++ lldb/test/API/macosx/branch-islands/padding4.s | 3 +++ 6 files changed, 26 insertions(+), 12 deletions(-) create mode 100644 lldb/test/API/macosx/branch-islands/padding3.s create mode 100644 lldb/test/API/macosx/branch-islands/padding4.s diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index 52c50cd88902a..ad3b63cf59135 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -1030,10 +1030,15 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, thread, load_addrs, stop_others); } // One more case we have to consider is "branch islands". These are regular - // TEXT symbols but their names end in .island. They are to allow arm64 - // code to branch further than the size of the address slot allows. We - // just need to single-instruction step in that case. - if (!thread_plan_sp && current_name.GetStringRef().ends_with(".island")) { + // TEXT symbols but their names end in .island plus maybe a .digit suffix. + // They are to allow arm64 code to branch further than the size of the + // address slot allows. We just need to single-instruction step in that + // case. + static const char *g_branch_island_pattern = "\\.island\\.?[0-9]*$"; + static RegularExpression g_branch_island_regex(g_branch_island_pattern); + + bool is_branch_island = g_branch_island_regex.Execute(current_name); + if (!thread_plan_sp && is_branch_island) { thread_plan_sp = std::make_shared( thread, /* step_over= */ false, /* stop_others */ false, eVoteNoOpinion, diff --git a/lldb/test/API/macosx/branch-islands/Makefile b/lldb/test/API/macosx/branch-islands/Makefile index 8675bbf6f85de..062e947f6d6ee 100644 --- a/lldb/test/API/macosx/branch-islands/Makefile +++ b/lldb/test/API/macosx/branch-islands/Makefile @@ -3,11 +3,14 @@ CFLAGS_EXTRAS := -std=c99 include Makefile.rules -a.out: main.o padding1.o padding2.o foo.o - ${CC} ${LDFLAGS} foo.o padding1.o padding2.o main.o -o a.out +a.out: main.o padding1.o padding2.o padding3.o padding4.o foo.o + ${CC} ${LDFLAGS} foo.o padding1.o padding2.o padding3.o padding4.o main.o -o a.out -padding1.o: padding1.s - ${CC} -c $(SRCDIR)/padding1.s +%.o: $(SRCDIR)/%.s + ${CC} -c $< -padding2.o: padding2.s - ${CC} -c $(SRCDIR)/padding2.s +#padding1.o: padding1.s +# ${CC} -c $(SRCDIR)/padding1.s + +#padding2.o: padding2.s +# ${CC} -c $(SRCDIR)/padding2.s diff --git a/lldb/test/API/macosx/branch-islands/padding1.s b/lldb/test/API/macosx/branch-islands/padding1.s index e3bdf7007d757..4911e53b0240d 100644 --- a/lldb/test/API/macosx/branch-islands/padding1.s +++ b/lldb/test/API/macosx/branch-islands/padding1.s @@ -1,3 +1,3 @@ .text -_junk1: +_padding1: .space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding2.s b/lldb/test/API/macosx/branch-islands/padding2.s index 187a2c3ebd117..5ad1bad11263b 100644 --- a/lldb/test/API/macosx/branch-islands/padding2.s +++ b/lldb/test/API/macosx/branch-islands/padding2.s @@ -1,3 +1,3 @@ .text -_junk2: +_padding2: .space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding3.s b/lldb/test/API/macosx/branch-islands/padding3.s new file mode 100644 index 0000000000000..9f614eecf56d9 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding3.s @@ -0,0 +1,3 @@ +.text +_padding3: +.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding4.s b/lldb/test/API/macosx/branch-islands/padding4.s new file mode 100644 index 0000000000000..12896cf5e5b8e --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding4.s @@ -0,0 +1,3 @@ +.text +_padding4: +.space 120*1024*1024 >From a5944b752915ee2f3ccfda63332d33111d59ff15 Mon Sep 17 00:00:00 2001 From: Jim Ingham Date: Tue, 6 May 2025 16:53:34 -0700 Subject: [PATCH 3/3] formatting --- .../DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index ad3b63cf59135..578ab12268ea3 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -1030,9 +1030,9 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, thread, load_addrs, stop_others); } // One more case we have to consider is "branch islands". These are regular - // TEXT symbols but their names end in .island plus maybe a .digit suffix. - // They are to allow arm64 code to branch further than the size of the - // address slot allows. We just need to single-instruction step in that + // TEXT symbols but their names end in .island plus maybe a .digit suffix. + // They are to allow arm64 code to branch further than the size of the + // address slot allows. We just need to single-instruction step in that // case. static const char *g_branch_island_pattern = "\\.island\\.?[0-9]*$"; static RegularExpression g_branch_island_regex(g_branch_island_pattern); From lldb-commits at lists.llvm.org Tue May 6 16:54:47 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Tue, 06 May 2025 16:54:47 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Add support for displaying `__float128` variables (PR #98369) In-Reply-To: Message-ID: <681aa147.170a0220.ec086.3889@mx.google.com> https://github.com/JDevlieghere approved this pull request. LGTM. Do the other reviewers have any other concerns? If not I'm happy to merge this on your behalf. https://github.com/llvm/llvm-project/pull/98369 From lldb-commits at lists.llvm.org Tue May 6 16:56:59 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Tue, 06 May 2025 16:56:59 -0700 (PDT) Subject: [Lldb-commits] [lldb] Branch island with numbers (PR #138781) In-Reply-To: Message-ID: <681aa1cb.630a0220.c82ea.57b5@mx.google.com> https://github.com/JDevlieghere approved this pull request. Ship it https://github.com/llvm/llvm-project/pull/138781 From lldb-commits at lists.llvm.org Tue May 6 16:58:05 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Tue, 06 May 2025 16:58:05 -0700 (PDT) Subject: [Lldb-commits] [lldb] 11f33ab - Branch island with numbers (#138781) Message-ID: <681aa20d.170a0220.3d1a3a.b336@mx.google.com> Author: jimingham Date: 2025-05-06T16:58:01-07:00 New Revision: 11f33ab3850886510a831122078a155be7dc1167 URL: https://github.com/llvm/llvm-project/commit/11f33ab3850886510a831122078a155be7dc1167 DIFF: https://github.com/llvm/llvm-project/commit/11f33ab3850886510a831122078a155be7dc1167.diff LOG: Branch island with numbers (#138781) Reapply the support for stepping through branch islands, add support for a branch that takes multiple hops to get to the target. Added: lldb/test/API/macosx/branch-islands/Makefile lldb/test/API/macosx/branch-islands/TestBranchIslands.py lldb/test/API/macosx/branch-islands/foo.c lldb/test/API/macosx/branch-islands/main.c lldb/test/API/macosx/branch-islands/padding1.s lldb/test/API/macosx/branch-islands/padding2.s lldb/test/API/macosx/branch-islands/padding3.s lldb/test/API/macosx/branch-islands/padding4.s Modified: lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp Removed: ################################################################################ diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index e25c4ff55e408..578ab12268ea3 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -26,6 +26,7 @@ #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Utility/DataBuffer.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/LLDBLog.h" @@ -923,15 +924,15 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, if (current_symbol != nullptr) { std::vector
addresses; + ConstString current_name = + current_symbol->GetMangled().GetName(Mangled::ePreferMangled); if (current_symbol->IsTrampoline()) { - ConstString trampoline_name = - current_symbol->GetMangled().GetName(Mangled::ePreferMangled); - if (trampoline_name) { + if (current_name) { const ModuleList &images = target_sp->GetImages(); SymbolContextList code_symbols; - images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeCode, + images.FindSymbolsWithNameAndType(current_name, eSymbolTypeCode, code_symbols); for (const SymbolContext &context : code_symbols) { Address addr = context.GetFunctionOrSymbolAddress(); @@ -945,8 +946,8 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, } SymbolContextList reexported_symbols; - images.FindSymbolsWithNameAndType( - trampoline_name, eSymbolTypeReExported, reexported_symbols); + images.FindSymbolsWithNameAndType(current_name, eSymbolTypeReExported, + reexported_symbols); for (const SymbolContext &context : reexported_symbols) { if (context.symbol) { Symbol *actual_symbol = @@ -968,7 +969,7 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, } SymbolContextList indirect_symbols; - images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeResolver, + images.FindSymbolsWithNameAndType(current_name, eSymbolTypeResolver, indirect_symbols); for (const SymbolContext &context : indirect_symbols) { @@ -1028,6 +1029,23 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, thread_plan_sp = std::make_shared( thread, load_addrs, stop_others); } + // One more case we have to consider is "branch islands". These are regular + // TEXT symbols but their names end in .island plus maybe a .digit suffix. + // They are to allow arm64 code to branch further than the size of the + // address slot allows. We just need to single-instruction step in that + // case. + static const char *g_branch_island_pattern = "\\.island\\.?[0-9]*$"; + static RegularExpression g_branch_island_regex(g_branch_island_pattern); + + bool is_branch_island = g_branch_island_regex.Execute(current_name); + if (!thread_plan_sp && is_branch_island) { + thread_plan_sp = std::make_shared( + thread, + /* step_over= */ false, /* stop_others */ false, eVoteNoOpinion, + eVoteNoOpinion); + LLDB_LOG(log, "Stepping one instruction over branch island: '{0}'.", + current_name); + } } else { LLDB_LOGF(log, "Could not find symbol for step through."); } diff --git a/lldb/test/API/macosx/branch-islands/Makefile b/lldb/test/API/macosx/branch-islands/Makefile new file mode 100644 index 0000000000000..062e947f6d6ee --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/Makefile @@ -0,0 +1,16 @@ +C_SOURCES := main.c foo.c +CFLAGS_EXTRAS := -std=c99 + +include Makefile.rules + +a.out: main.o padding1.o padding2.o padding3.o padding4.o foo.o + ${CC} ${LDFLAGS} foo.o padding1.o padding2.o padding3.o padding4.o main.o -o a.out + +%.o: $(SRCDIR)/%.s + ${CC} -c $< + +#padding1.o: padding1.s +# ${CC} -c $(SRCDIR)/padding1.s + +#padding2.o: padding2.s +# ${CC} -c $(SRCDIR)/padding2.s diff --git a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py new file mode 100644 index 0000000000000..b397e0c229b08 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py @@ -0,0 +1,35 @@ +""" +Make sure that we can step in across an arm64 branch island +""" + + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * + + +class TestBranchIslandStepping(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + @skipUnlessDarwin + def test_step_in_branch_island(self): + """Make sure we can step in across a branch island""" + self.build() + self.main_source_file = lldb.SBFileSpec("main.c") + self.do_test() + + def do_test(self): + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "Set a breakpoint here", self.main_source_file + ) + + # Make sure that we did manage to generate a branch island for foo: + syms = target.FindSymbols("foo.island", lldb.eSymbolTypeCode) + self.assertEqual(len(syms), 1, "We did generate an island for foo") + + thread.StepInto() + stop_frame = thread.frames[0] + self.assertIn("foo", stop_frame.name, "Stepped into foo") + var = stop_frame.FindVariable("a_variable_in_foo") + self.assertTrue(var.IsValid(), "Found the variable in foo") diff --git a/lldb/test/API/macosx/branch-islands/foo.c b/lldb/test/API/macosx/branch-islands/foo.c new file mode 100644 index 0000000000000..a5dd2e59e1d82 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/foo.c @@ -0,0 +1,6 @@ +#include + +void foo() { + int a_variable_in_foo = 10; + printf("I am foo: %d.\n", a_variable_in_foo); +} diff --git a/lldb/test/API/macosx/branch-islands/main.c b/lldb/test/API/macosx/branch-islands/main.c new file mode 100644 index 0000000000000..b5578bdd715df --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/main.c @@ -0,0 +1,6 @@ +extern void foo(); + +int main() { + foo(); // Set a breakpoint here + return 0; +} diff --git a/lldb/test/API/macosx/branch-islands/padding1.s b/lldb/test/API/macosx/branch-islands/padding1.s new file mode 100644 index 0000000000000..4911e53b0240d --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding1.s @@ -0,0 +1,3 @@ +.text +_padding1: +.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding2.s b/lldb/test/API/macosx/branch-islands/padding2.s new file mode 100644 index 0000000000000..5ad1bad11263b --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding2.s @@ -0,0 +1,3 @@ +.text +_padding2: +.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding3.s b/lldb/test/API/macosx/branch-islands/padding3.s new file mode 100644 index 0000000000000..9f614eecf56d9 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding3.s @@ -0,0 +1,3 @@ +.text +_padding3: +.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding4.s b/lldb/test/API/macosx/branch-islands/padding4.s new file mode 100644 index 0000000000000..12896cf5e5b8e --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding4.s @@ -0,0 +1,3 @@ +.text +_padding4: +.space 120*1024*1024 From lldb-commits at lists.llvm.org Tue May 6 16:58:08 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Tue, 06 May 2025 16:58:08 -0700 (PDT) Subject: [Lldb-commits] [lldb] Branch island with numbers (PR #138781) In-Reply-To: Message-ID: <681aa210.170a0220.148790.4b87@mx.google.com> https://github.com/jimingham closed https://github.com/llvm/llvm-project/pull/138781 From lldb-commits at lists.llvm.org Tue May 6 17:01:32 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Tue, 06 May 2025 17:01:32 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Migrate attach to typed RequestHandler. (PR #137911) In-Reply-To: Message-ID: <681aa2dc.630a0220.382d9.6ddc@mx.google.com> https://github.com/ashgti updated https://github.com/llvm/llvm-project/pull/137911 >From a43b90dba56bd411b09257d47e3a3091faa09efd Mon Sep 17 00:00:00 2001 From: John Harrison Date: Tue, 29 Apr 2025 17:36:46 -0700 Subject: [PATCH 1/7] [lldb-dap] Migrate attach to typed RequestHandler. This updates the `attach` request to the typed `RequestHandler`. Added a few more overlapping configurations to `lldb_dap::protocol::Configuration` that are shared between launching and attaching. There may be some additional code we could clean-up that is no longer referenced now that this has migrated to use well defined types. --- lldb/tools/lldb-dap/DAP.cpp | 26 +-- .../lldb-dap/Handler/AttachRequestHandler.cpp | 214 ++++++------------ .../Handler/InitializeRequestHandler.cpp | 2 +- .../lldb-dap/Handler/LaunchRequestHandler.cpp | 6 +- .../tools/lldb-dap/Handler/RequestHandler.cpp | 64 +----- lldb/tools/lldb-dap/Handler/RequestHandler.h | 21 +- .../Handler/RestartRequestHandler.cpp | 1 - .../lldb-dap/Protocol/ProtocolRequests.cpp | 35 ++- .../lldb-dap/Protocol/ProtocolRequests.h | 87 +++++-- lldb/tools/lldb-dap/package.json | 13 +- 10 files changed, 204 insertions(+), 265 deletions(-) diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index b593353110787..abed9983118f9 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -675,12 +675,11 @@ lldb::SBTarget DAP::CreateTarget(lldb::SBError &error) { // enough information to determine correct arch and platform (or ELF can be // omitted at all), so it is good to leave the user an opportunity to specify // those. Any of those three can be left empty. - auto target = this->debugger.CreateTarget( - configuration.program.value_or("").data(), - configuration.targetTriple.value_or("").data(), - configuration.platformName.value_or("").data(), - true, // Add dependent modules. - error); + auto target = this->debugger.CreateTarget(configuration.program.data(), + configuration.targetTriple.data(), + configuration.platformName.data(), + true, // Add dependent modules. + error); return target; } @@ -1192,7 +1191,7 @@ bool SendEventRequestHandler::DoExecute(lldb::SBDebugger debugger, } void DAP::ConfigureSourceMaps() { - if (configuration.sourceMap.empty() && !configuration.sourcePath) + if (configuration.sourceMap.empty() && configuration.sourcePath.empty()) return; std::string sourceMapCommand; @@ -1203,8 +1202,8 @@ void DAP::ConfigureSourceMaps() { for (const auto &kv : configuration.sourceMap) { strm << "\"" << kv.first << "\" \"" << kv.second << "\" "; } - } else if (configuration.sourcePath) { - strm << "\".\" \"" << *configuration.sourcePath << "\""; + } else if (!configuration.sourcePath.empty()) { + strm << "\".\" \"" << configuration.sourcePath << "\""; } RunLLDBCommands("Setting source map:", {sourceMapCommand}); @@ -1213,12 +1212,13 @@ void DAP::ConfigureSourceMaps() { void DAP::SetConfiguration(const protocol::Configuration &config, bool is_attach) { configuration = config; + stop_at_entry = config.stopOnEntry; this->is_attach = is_attach; - if (configuration.customFrameFormat) - SetFrameFormat(*configuration.customFrameFormat); - if (configuration.customThreadFormat) - SetThreadFormat(*configuration.customThreadFormat); + if (!configuration.customFrameFormat.empty()) + SetFrameFormat(configuration.customFrameFormat); + if (!configuration.customThreadFormat.empty()) + SetThreadFormat(configuration.customThreadFormat); } void DAP::SetFrameFormat(llvm::StringRef format) { diff --git a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp index 3ef87cbef873c..fd19a4f835686 100644 --- a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp @@ -9,203 +9,135 @@ #include "DAP.h" #include "EventHelper.h" #include "JSONUtils.h" +#include "LLDBUtils.h" +#include "Protocol/ProtocolRequests.h" #include "RequestHandler.h" #include "lldb/API/SBListener.h" +#include "lldb/lldb-defines.h" +#include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" +using namespace llvm; +using namespace lldb_dap::protocol; + namespace lldb_dap { -// "AttachRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "Attach request; value of command field is 'attach'.", -// "properties": { -// "command": { -// "type": "string", -// "enum": [ "attach" ] -// }, -// "arguments": { -// "$ref": "#/definitions/AttachRequestArguments" -// } -// }, -// "required": [ "command", "arguments" ] -// }] -// }, -// "AttachRequestArguments": { -// "type": "object", -// "description": "Arguments for 'attach' request.\nThe attach request has no -// standardized attributes." -// }, -// "AttachResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to 'attach' request. This is just an -// acknowledgement, so no body field is required." -// }] -// } -void AttachRequestHandler::operator()(const llvm::json::Object &request) const { - dap.is_attach = true; - llvm::json::Object response; - lldb::SBError error; - FillResponse(request, response); - lldb::SBAttachInfo attach_info; - const int invalid_port = 0; - const auto *arguments = request.getObject("arguments"); - const lldb::pid_t pid = - GetInteger(arguments, "pid").value_or(LLDB_INVALID_PROCESS_ID); - const auto gdb_remote_port = - GetInteger(arguments, "gdb-remote-port").value_or(invalid_port); - const auto gdb_remote_hostname = - GetString(arguments, "gdb-remote-hostname").value_or("localhost"); - if (pid != LLDB_INVALID_PROCESS_ID) - attach_info.SetProcessID(pid); - const auto wait_for = GetBoolean(arguments, "waitFor").value_or(false); - attach_info.SetWaitForLaunch(wait_for, false /*async*/); - dap.configuration.initCommands = GetStrings(arguments, "initCommands"); - dap.configuration.preRunCommands = GetStrings(arguments, "preRunCommands"); - dap.configuration.postRunCommands = GetStrings(arguments, "postRunCommands"); - dap.configuration.stopCommands = GetStrings(arguments, "stopCommands"); - dap.configuration.exitCommands = GetStrings(arguments, "exitCommands"); - dap.configuration.terminateCommands = - GetStrings(arguments, "terminateCommands"); - auto attachCommands = GetStrings(arguments, "attachCommands"); - llvm::StringRef core_file = GetString(arguments, "coreFile").value_or(""); - const uint64_t timeout_seconds = - GetInteger(arguments, "timeout").value_or(30); - dap.stop_at_entry = core_file.empty() - ? GetBoolean(arguments, "stopOnEntry").value_or(false) - : true; - const llvm::StringRef debuggerRoot = - GetString(arguments, "debuggerRoot").value_or(""); - dap.configuration.enableAutoVariableSummaries = - GetBoolean(arguments, "enableAutoVariableSummaries").value_or(false); - dap.configuration.enableSyntheticChildDebugging = - GetBoolean(arguments, "enableSyntheticChildDebugging").value_or(false); - dap.configuration.displayExtendedBacktrace = - GetBoolean(arguments, "displayExtendedBacktrace").value_or(false); - dap.configuration.commandEscapePrefix = - GetString(arguments, "commandEscapePrefix").value_or("`"); - dap.configuration.program = GetString(arguments, "program"); - dap.configuration.targetTriple = GetString(arguments, "targetTriple"); - dap.configuration.platformName = GetString(arguments, "platformName"); - dap.SetFrameFormat(GetString(arguments, "customFrameFormat").value_or("")); - dap.SetThreadFormat(GetString(arguments, "customThreadFormat").value_or("")); +/// The `attach` request is sent from the client to the debug adapter to attach +/// to a debuggee that is already running. +/// +/// Since attaching is debugger/runtime specific, the arguments for this request +/// are not part of this specification. +Error AttachRequestHandler::Run(const AttachRequestArguments &args) const { + dap.SetConfiguration(args.configuration, true); + if (!args.coreFile.empty()) + dap.stop_at_entry = true; + + // If both pid and port numbers are specified. + if ((args.pid != LLDB_INVALID_PROCESS_ID) && + (args.gdbRemotePort != LLDB_DAP_INVALID_PORT)) + return make_error( + "pid and gdb-remote-port are mutually exclusive"); PrintWelcomeMessage(); // This is a hack for loading DWARF in .o files on Mac where the .o files - // in the debug map of the main executable have relative paths which require - // the lldb-dap binary to have its working directory set to that relative - // root for the .o files in order to be able to load debug info. - if (!debuggerRoot.empty()) - llvm::sys::fs::set_current_path(debuggerRoot); + // in the debug map of the main executable have relative paths which + // require the lldb-dap binary to have its working directory set to that + // relative root for the .o files in order to be able to load debug info. + if (!dap.configuration.debuggerRoot.empty()) + sys::fs::set_current_path(dap.configuration.debuggerRoot); // Run any initialize LLDB commands the user specified in the launch.json - if (llvm::Error err = dap.RunInitCommands()) { - response["success"] = false; - EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; - } + if (llvm::Error err = dap.RunInitCommands()) + return err; - SetSourceMapFromArguments(*arguments); + dap.ConfigureSourceMaps(); - lldb::SBError status; - dap.SetTarget(dap.CreateTarget(status)); - if (status.Fail()) { - response["success"] = llvm::json::Value(false); - EmplaceSafeString(response, "message", status.GetCString()); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; - } + lldb::SBError error; + lldb::SBTarget target = dap.CreateTarget(error); + if (error.Fail()) + return ToError(error); + + dap.SetTarget(target); // Run any pre run LLDB commands the user specified in the launch.json - if (llvm::Error err = dap.RunPreRunCommands()) { - response["success"] = false; - EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; - } + if (Error err = dap.RunPreRunCommands()) + return err; - if ((pid == LLDB_INVALID_PROCESS_ID || gdb_remote_port == invalid_port) && - wait_for) { + if ((args.pid == LLDB_INVALID_PROCESS_ID || + args.gdbRemotePort == LLDB_DAP_INVALID_PORT) && + args.waitFor) { char attach_msg[256]; auto attach_msg_len = snprintf(attach_msg, sizeof(attach_msg), "Waiting to attach to \"%s\"...", dap.target.GetExecutable().GetFilename()); - dap.SendOutput(OutputType::Console, - llvm::StringRef(attach_msg, attach_msg_len)); + dap.SendOutput(OutputType::Console, StringRef(attach_msg, attach_msg_len)); } - if (attachCommands.empty()) { + + if (args.attachCommands.empty()) { // No "attachCommands", just attach normally. // Disable async events so the attach will be successful when we return from // the launch call and the launch will happen synchronously dap.debugger.SetAsync(false); - if (core_file.empty()) { - if ((pid != LLDB_INVALID_PROCESS_ID) && - (gdb_remote_port != invalid_port)) { - // If both pid and port numbers are specified. - error.SetErrorString("The user can't specify both pid and port"); - } else if (gdb_remote_port != invalid_port) { + if (args.coreFile.empty()) { + if (args.gdbRemotePort != LLDB_DAP_INVALID_PORT) { // If port is specified and pid is not. lldb::SBListener listener = dap.debugger.GetListener(); // If the user hasn't provided the hostname property, default localhost // being used. std::string connect_url = - llvm::formatv("connect://{0}:", gdb_remote_hostname); - connect_url += std::to_string(gdb_remote_port); - dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", + llvm::formatv("connect://{0}:", args.gdbRemoteHostname); + connect_url += std::to_string(args.gdbRemotePort); + dap.target.ConnectRemote(listener, connect_url.data(), "gdb-remote", error); } else { // Attach by process name or id. + lldb::SBAttachInfo attach_info; + if (!args.configuration.program.empty()) + attach_info.SetExecutable(args.configuration.program.data()); + if (args.pid != LLDB_INVALID_PROCESS_ID) + attach_info.SetProcessID(args.pid); + attach_info.SetWaitForLaunch(args.waitFor, false /*async*/); dap.target.Attach(attach_info, error); } } else - dap.target.LoadCore(core_file.data(), error); + dap.target.LoadCore(args.coreFile.data(), error); // Reenable async events dap.debugger.SetAsync(true); } else { // We have "attachCommands" that are a set of commands that are expected // to execute the commands after which a process should be created. If there // is no valid process after running these commands, we have failed. - if (llvm::Error err = dap.RunAttachCommands(attachCommands)) { - response["success"] = false; - EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; - } + if (llvm::Error err = dap.RunAttachCommands(args.attachCommands)) + return err; + // The custom commands might have created a new target so we should use the // selected target after these commands are run. dap.target = dap.debugger.GetSelectedTarget(); // Make sure the process is attached and stopped before proceeding as the // the launch commands are not run using the synchronous mode. - error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds)); + error = dap.WaitForProcessToStop(dap.configuration.timeout); } - if (error.Success() && core_file.empty()) { - auto attached_pid = dap.target.GetProcess().GetProcessID(); - if (attached_pid == LLDB_INVALID_PROCESS_ID) { - if (attachCommands.empty()) - error.SetErrorString("failed to attach to a process"); - else - error.SetErrorString("attachCommands failed to attach to a process"); - } - } + if (error.Fail()) + return ToError(error); - if (error.Fail()) { - response["success"] = llvm::json::Value(false); - EmplaceSafeString(response, "message", std::string(error.GetCString())); - } else { - dap.RunPostRunCommands(); - } + if (args.coreFile.empty() && !dap.target.GetProcess().IsValid()) + return make_error("failed to attach to process"); - dap.SendJSON(llvm::json::Value(std::move(response))); - if (error.Success()) { + dap.RunPostRunCommands(); + + return Error::success(); +} + +void AttachRequestHandler::PostRun() const { + if (dap.target.GetProcess().IsValid()) { SendProcessEvent(dap, Attach); - dap.SendJSON(CreateEventObject("initialized")); } + + dap.SendJSON(CreateEventObject("initialized")); } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp index ce34c52bcc334..7a2adef4b2faf 100644 --- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp @@ -277,7 +277,7 @@ static void EventThreadFunction(DAP &dap) { } /// Initialize request; value of command field is 'initialize'. -llvm::Expected InitializeRequestHandler::Run( +llvm::Expected InitializeRequestHandler::Run( const InitializeRequestArguments &arguments) const { dap.clientFeatures = arguments.supportedFeatures; diff --git a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp index 3e4532e754ec6..fd0846bd00f45 100644 --- a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp @@ -24,7 +24,6 @@ namespace lldb_dap { Error LaunchRequestHandler::Run(const LaunchRequestArguments &arguments) const { dap.SetConfiguration(arguments.configuration, /*is_attach=*/false); dap.last_launch_request = arguments; - dap.stop_at_entry = arguments.stopOnEntry; if (!arguments.launchCommands.empty() && arguments.runInTerminal) return make_error( @@ -36,9 +35,8 @@ Error LaunchRequestHandler::Run(const LaunchRequestArguments &arguments) const { // in the debug map of the main executable have relative paths which // require the lldb-dap binary to have its working directory set to that // relative root for the .o files in order to be able to load debug info. - const std::string debugger_root = dap.configuration.debuggerRoot.value_or(""); - if (!debugger_root.empty()) - sys::fs::set_current_path(debugger_root); + if (!dap.configuration.debuggerRoot.empty()) + sys::fs::set_current_path(dap.configuration.debuggerRoot); // Run any initialize LLDB commands the user specified in the launch.json. // This is run before target is created, so commands can't do anything with diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp index b7d3c8ced69f1..dbfb1da635cbb 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp @@ -49,56 +49,6 @@ static uint32_t SetLaunchFlag(uint32_t flags, bool flag, return flags; } -// Both attach and launch take either a sourcePath or a sourceMap -// argument (or neither), from which we need to set the target.source-map. -void BaseRequestHandler::SetSourceMapFromArguments( - const llvm::json::Object &arguments) const { - const char *sourceMapHelp = - "source must be be an array of two-element arrays, " - "each containing a source and replacement path string.\n"; - - std::string sourceMapCommand; - llvm::raw_string_ostream strm(sourceMapCommand); - strm << "settings set target.source-map "; - const auto sourcePath = GetString(arguments, "sourcePath").value_or(""); - - // sourceMap is the new, more general form of sourcePath and overrides it. - constexpr llvm::StringRef sourceMapKey = "sourceMap"; - - if (const auto *sourceMapArray = arguments.getArray(sourceMapKey)) { - for (const auto &value : *sourceMapArray) { - const auto *mapping = value.getAsArray(); - if (mapping == nullptr || mapping->size() != 2 || - (*mapping)[0].kind() != llvm::json::Value::String || - (*mapping)[1].kind() != llvm::json::Value::String) { - dap.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp)); - return; - } - const auto mapFrom = GetAsString((*mapping)[0]); - const auto mapTo = GetAsString((*mapping)[1]); - strm << "\"" << mapFrom << "\" \"" << mapTo << "\" "; - } - } else if (const auto *sourceMapObj = arguments.getObject(sourceMapKey)) { - for (const auto &[key, value] : *sourceMapObj) { - if (value.kind() == llvm::json::Value::String) { - strm << "\"" << key.str() << "\" \"" << GetAsString(value) << "\" "; - } - } - } else { - if (ObjectContainsKey(arguments, sourceMapKey)) { - dap.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp)); - return; - } - if (sourcePath.empty()) - return; - // Do any source remapping needed before we create our targets - strm << "\".\" \"" << sourcePath << "\""; - } - if (!sourceMapCommand.empty()) { - dap.RunLLDBCommands("Setting source map:", {sourceMapCommand}); - } -} - static llvm::Error RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) { if (!dap.clientFeatures.contains( @@ -106,8 +56,7 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) { return llvm::make_error("Cannot use runInTerminal, feature is " "not supported by the connected client"); - if (!arguments.configuration.program || - arguments.configuration.program->empty()) + if (arguments.configuration.program.empty()) return llvm::make_error( "program must be set to when using runInTerminal"); @@ -128,8 +77,8 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) { #endif llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest( - *arguments.configuration.program, arguments.args, arguments.env, - arguments.cwd.value_or(""), comm_file.m_path, debugger_pid); + arguments.configuration.program, arguments.args, arguments.env, + arguments.cwd, comm_file.m_path, debugger_pid); dap.SendReverseRequest("runInTerminal", std::move(reverse_request)); @@ -209,9 +158,8 @@ llvm::Error BaseRequestHandler::LaunchProcess( // Grab the current working directory if there is one and set it in the // launch info. - const auto cwd = arguments.cwd.value_or(""); - if (!cwd.empty()) - launch_info.SetWorkingDirectory(cwd.data()); + if (!arguments.cwd.empty()) + launch_info.SetWorkingDirectory(arguments.cwd.data()); // Extract any extra arguments and append them to our program arguments for // when we launch @@ -262,7 +210,7 @@ llvm::Error BaseRequestHandler::LaunchProcess( // Make sure the process is launched and stopped at the entry point before // proceeding as the launch commands are not run using the synchronous // mode. - lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout); + lldb::SBError error = dap.WaitForProcessToStop(dap.configuration.timeout); if (error.Fail()) return llvm::make_error(error.GetCString()); } diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index fa3d76ed4a125..a726254f53640 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.h +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h @@ -59,10 +59,6 @@ class BaseRequestHandler { /// FIXME: Move these into the DAP class? /// @{ - /// Both attach and launch take a either a sourcePath or sourceMap - /// argument (or neither), from which we need to set the target.source-map. - void SetSourceMapFromArguments(const llvm::json::Object &arguments) const; - /// Prints a welcome message on the editor if the preprocessor variable /// LLDB_DAP_WELCOME_MESSAGE is defined. void PrintWelcomeMessage() const; @@ -197,11 +193,14 @@ class RequestHandler : public BaseRequestHandler { } }; -class AttachRequestHandler : public LegacyRequestHandler { +class AttachRequestHandler + : public RequestHandler { public: - using LegacyRequestHandler::LegacyRequestHandler; + using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "attach"; } - void operator()(const llvm::json::Object &request) const override; + llvm::Error Run(const protocol::AttachRequestArguments &args) const override; + void PostRun() const override; }; class BreakpointLocationsRequestHandler : public LegacyRequestHandler { @@ -276,17 +275,17 @@ class ExceptionInfoRequestHandler : public LegacyRequestHandler { class InitializeRequestHandler : public RequestHandler> { + llvm::Expected> { public: using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "initialize"; } - llvm::Expected + llvm::Expected Run(const protocol::InitializeRequestArguments &args) const override; }; class LaunchRequestHandler : public RequestHandler { + protocol::LaunchResponse> { public: using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "launch"; } @@ -503,7 +502,7 @@ class ReadMemoryRequestHandler : public LegacyRequestHandler { class CancelRequestHandler : public RequestHandler { + protocol::CancelResponse> { public: using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "cancel"; } diff --git a/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp index e6f2d9ec669cb..a2d1639c9b91f 100644 --- a/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp @@ -108,7 +108,6 @@ void RestartRequestHandler::operator()( // Update DAP configuration based on the latest copy of the launch // arguments. dap.SetConfiguration(updated_arguments.configuration, false); - dap.stop_at_entry = updated_arguments.stopOnEntry; dap.ConfigureSourceMaps(); } } diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp index 61fea66490c30..d48c11e53d190 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp @@ -223,26 +223,29 @@ bool fromJSON(const json::Value &Params, InitializeRequestArguments &IRA, bool fromJSON(const json::Value &Params, Configuration &C, json::Path P) { json::ObjectMapper O(Params, P); - return O.map("debuggerRoot", C.debuggerRoot) && + return O.mapOptional("debuggerRoot", C.debuggerRoot) && O.mapOptional("enableAutoVariableSummaries", C.enableAutoVariableSummaries) && O.mapOptional("enableSyntheticChildDebugging", C.enableSyntheticChildDebugging) && O.mapOptional("displayExtendedBacktrace", C.displayExtendedBacktrace) && + O.mapOptional("stopOnEntry", C.stopOnEntry) && O.mapOptional("commandEscapePrefix", C.commandEscapePrefix) && - O.map("customFrameFormat", C.customFrameFormat) && - O.map("customThreadFormat", C.customThreadFormat) && - O.map("sourcePath", C.sourcePath) && + O.mapOptional("customFrameFormat", C.customFrameFormat) && + O.mapOptional("customThreadFormat", C.customThreadFormat) && + O.mapOptional("sourcePath", C.sourcePath) && O.mapOptional("initCommands", C.initCommands) && O.mapOptional("preRunCommands", C.preRunCommands) && O.mapOptional("postRunCommands", C.postRunCommands) && O.mapOptional("stopCommands", C.stopCommands) && O.mapOptional("exitCommands", C.exitCommands) && O.mapOptional("terminateCommands", C.terminateCommands) && - O.map("program", C.program) && O.map("targetTriple", C.targetTriple) && - O.map("platformName", C.platformName) && - parseSourceMap(Params, C.sourceMap, P); + O.mapOptional("program", C.program) && + O.mapOptional("targetTriple", C.targetTriple) && + O.mapOptional("platformName", C.platformName) && + parseSourceMap(Params, C.sourceMap, P) && + parseTimeout(Params, C.timeout, P); } bool fromJSON(const json::Value &Params, LaunchRequestArguments &LRA, @@ -251,14 +254,26 @@ bool fromJSON(const json::Value &Params, LaunchRequestArguments &LRA, return O && fromJSON(Params, LRA.configuration, P) && O.mapOptional("noDebug", LRA.noDebug) && O.mapOptional("launchCommands", LRA.launchCommands) && - O.map("cwd", LRA.cwd) && O.mapOptional("args", LRA.args) && + O.mapOptional("cwd", LRA.cwd) && O.mapOptional("args", LRA.args) && O.mapOptional("detachOnError", LRA.detachOnError) && O.mapOptional("disableASLR", LRA.disableASLR) && O.mapOptional("disableSTDIO", LRA.disableSTDIO) && O.mapOptional("shellExpandArguments", LRA.shellExpandArguments) && - O.mapOptional("stopOnEntry", LRA.stopOnEntry) && + O.mapOptional("runInTerminal", LRA.runInTerminal) && - parseEnv(Params, LRA.env, P) && parseTimeout(Params, LRA.timeout, P); + parseEnv(Params, LRA.env, P); +} + +bool fromJSON(const json::Value &Params, AttachRequestArguments &ARA, + json::Path P) { + json::ObjectMapper O(Params, P); + return O && fromJSON(Params, ARA.configuration, P) && + O.mapOptional("attachCommands", ARA.attachCommands) && + O.mapOptional("pid", ARA.pid) && + O.mapOptional("waitFor", ARA.waitFor) && + O.mapOptional("gdb-remote-port", ARA.gdbRemotePort) && + O.mapOptional("gdb-remote-hostname", ARA.gdbRemoteHostname) && + O.mapOptional("coreFile", ARA.coreFile); } bool fromJSON(const json::Value &Params, SourceArguments &SA, json::Path P) { diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h index 33f93cc38799a..8c4c11b9fe7eb 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h @@ -23,6 +23,7 @@ #include "Protocol/ProtocolBase.h" #include "Protocol/ProtocolTypes.h" #include "lldb/lldb-defines.h" +#include "lldb/lldb-types.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/JSON.h" @@ -51,7 +52,7 @@ bool fromJSON(const llvm::json::Value &, CancelArguments &, llvm::json::Path); /// Response to `cancel` request. This is just an acknowledgement, so no body /// field is required. -using CancelResponseBody = VoidResponse; +using CancelResponse = VoidResponse; /// Arguments for `disconnect` request. struct DisconnectArguments { @@ -139,15 +140,18 @@ bool fromJSON(const llvm::json::Value &, InitializeRequestArguments &, llvm::json::Path); /// Response to `initialize` request. The capabilities of this debug adapter. -using InitializeResponseBody = std::optional; +using InitializeResponse = std::optional; /// DAP Launch and Attach common configurations. +/// +/// See package.json debuggers > configurationAttributes > launch or attach > +/// properties for common configurations. struct Configuration { /// Specify a working directory to use when launching `lldb-dap`. If the debug /// information in your executable contains relative paths, this option can be /// used so that `lldb-dap` can find source files and object files that have /// relative paths. - std::optional debuggerRoot; + std::string debuggerRoot = ""; /// Enable auto generated summaries for variables when no summaries exist for /// a given type. This feature can cause performance delays in large projects @@ -163,6 +167,13 @@ struct Configuration { /// Enable language specific extended backtraces. bool displayExtendedBacktrace = false; + /// Stop at the entry point of the program when launching or attaching. + bool stopOnEntry = false; + + /// Optional timeout when waiting for the program to `runInTermianl` or + /// attach. + std::chrono::seconds timeout = std::chrono::seconds(30); + /// The escape prefix to use for executing regular LLDB commands in the Debug /// Console, instead of printing variables. Defaults to a backtick. If it's an /// empty string, then all expression in the Debug Console are treated as @@ -176,14 +187,14 @@ struct Configuration { /// default frame names will be used. This might come with a performance cost /// because debug information might need to be processed to generate the /// description. - std::optional customFrameFormat; + std::string customFrameFormat = ""; /// Same as `customFrameFormat`, but for threads instead of stack frames. - std::optional customThreadFormat; + std::string customThreadFormat = ""; /// Specify a source path to remap "./" to allow full paths to be used when /// setting breakpoints in binaries that have relative source paths. - std::optional sourcePath; + std::string sourcePath = ""; /// Specify an array of path re-mappings. Each element in the array must be a /// two element array containing a source and destination pathname. Overrides @@ -219,15 +230,15 @@ struct Configuration { /// /// *NOTE:* When launching, either `launchCommands` or `program` must be /// configured. If both are configured then `launchCommands` takes priority. - std::optional program; + std::string program = ""; /// Target triple for the program (arch-vendor-os). If not set, inferred from /// the binary. - std::optional targetTriple; + std::string targetTriple = ""; /// Specify name of the platform to use for this target, creating the platform /// if necessary. - std::optional platformName; + std::string platformName = ""; }; /// lldb-dap specific launch arguments. @@ -240,6 +251,9 @@ struct LaunchRequestArguments { bool noDebug = false; /// Launch specific operations. + /// + /// See package.json debuggers > configurationAttributes > launch > + /// properties. /// @{ /// LLDB commands executed to launch the program. @@ -250,7 +264,7 @@ struct LaunchRequestArguments { std::vector launchCommands; /// The program working directory. - std::optional cwd; + std::string cwd = ""; /// An array of command line argument strings to be passed to the program /// being launched. @@ -275,16 +289,10 @@ struct LaunchRequestArguments { /// Set whether to shell expand arguments to the process when launching. bool shellExpandArguments = false; - /// Stop at the entry point of the program when launching a process. - bool stopOnEntry = false; - /// Launch the program inside an integrated terminal in the IDE. Useful for /// debugging interactive command line programs. bool runInTerminal = false; - /// Optional timeout for `runInTerminal` requests. - std::chrono::seconds timeout = std::chrono::seconds(30); - /// @} }; bool fromJSON(const llvm::json::Value &, LaunchRequestArguments &, @@ -292,7 +300,52 @@ bool fromJSON(const llvm::json::Value &, LaunchRequestArguments &, /// Response to `launch` request. This is just an acknowledgement, so no body /// field is required. -using LaunchResponseBody = VoidResponse; +using LaunchResponse = VoidResponse; + +#define LLDB_DAP_INVALID_PORT -1 + +/// lldb-dap specific attach arguments. +struct AttachRequestArguments { + /// Common lldb-dap configuration values for launching/attaching operations. + Configuration configuration; + + /// Attach specific operations. + /// + /// See package.json debuggers > configurationAttributes > attach > + /// properties. + /// @{ + + /// Custom commands that are executed instead of attaching to a process ID or + /// to a process by name. These commands may optionally create a new target + /// and must perform an attach. A valid process must exist after these + /// commands complete or the `"attach"` will fail. + std::vector attachCommands; + + /// System process ID to attach to. + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + + /// Wait for the process to launch. + bool waitFor = false; + + /// TCP/IP port to attach to a remote system. Specifying both pid and port is + /// an error. + int32_t gdbRemotePort = LLDB_DAP_INVALID_PORT; + + /// The hostname to connect to a remote system. The default hostname being + /// used `localhost`. + std::string gdbRemoteHostname = "localhost"; + + /// Path to the core file to debug. + std::string coreFile = ""; + + /// @} +}; +bool fromJSON(const llvm::json::Value &, AttachRequestArguments &, + llvm::json::Path); + +/// Response to `attach` request. This is just an acknowledgement, so no body +/// field is required. +using AttachResponse = VoidResponse; /// Arguments for `source` request. struct SourceArguments { diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json index 5cfd79849b17f..27d97ed43a53f 100644 --- a/lldb/tools/lldb-dap/package.json +++ b/lldb/tools/lldb-dap/package.json @@ -417,10 +417,7 @@ "description": "Path to the program to attach to." }, "pid": { - "type": [ - "number", - "string" - ], + "type": "number", "description": "System process ID to attach to." }, "waitFor": { @@ -536,15 +533,13 @@ "description": "The time in seconds to wait for a program to stop when attaching using \"attachCommands\". Defaults to 30 seconds." }, "gdb-remote-port": { - "type": [ - "number", - "string" - ], + "type": "number", "description": "TCP/IP port to attach to a remote system. Specifying both pid and port is an error." }, "gdb-remote-hostname": { "type": "string", - "description": "The hostname to connect to a remote system. The default hostname being used localhost." + "description": "The hostname to connect to a remote system. The default hostname being used localhost.", + "default": "localhost" }, "enableAutoVariableSummaries": { "type": "boolean", >From 087ab33212f5ca523747208d960e4e5483fb3469 Mon Sep 17 00:00:00 2001 From: John Harrison Date: Wed, 30 Apr 2025 08:51:53 -0700 Subject: [PATCH 2/7] Applying clang-format. --- lldb/tools/lldb-dap/Handler/RequestHandler.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index a726254f53640..0dd9928ce0861 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.h +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h @@ -500,9 +500,8 @@ class ReadMemoryRequestHandler : public LegacyRequestHandler { void operator()(const llvm::json::Object &request) const override; }; -class CancelRequestHandler - : public RequestHandler { +class CancelRequestHandler : public RequestHandler { public: using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "cancel"; } >From b11201f0c7937dab4e901075333b1c4b19b4551e Mon Sep 17 00:00:00 2001 From: John Harrison Date: Thu, 1 May 2025 16:23:51 -0700 Subject: [PATCH 3/7] Using an optional for frame and thread format --- lldb/tools/lldb-dap/DAP.cpp | 8 ++++---- lldb/tools/lldb-dap/Protocol/ProtocolRequests.h | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index abed9983118f9..1dab9e69e827e 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -1215,10 +1215,10 @@ void DAP::SetConfiguration(const protocol::Configuration &config, stop_at_entry = config.stopOnEntry; this->is_attach = is_attach; - if (!configuration.customFrameFormat.empty()) - SetFrameFormat(configuration.customFrameFormat); - if (!configuration.customThreadFormat.empty()) - SetThreadFormat(configuration.customThreadFormat); + if (configuration.customFrameFormat) + SetFrameFormat(*configuration.customFrameFormat); + if (configuration.customThreadFormat) + SetThreadFormat(*configuration.customThreadFormat); } void DAP::SetFrameFormat(llvm::StringRef format) { diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h index 8c4c11b9fe7eb..98c3cb18d3863 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h @@ -160,8 +160,8 @@ struct Configuration { /// If a variable is displayed using a synthetic children, also display the /// actual contents of the variable at the end under a [raw] entry. This is - /// useful when creating sythetic child plug-ins as it lets you see the actual - /// contents of the variable. + /// useful when creating synthetic child plug-ins as it lets you see the + /// actual contents of the variable. bool enableSyntheticChildDebugging = false; /// Enable language specific extended backtraces. @@ -170,7 +170,7 @@ struct Configuration { /// Stop at the entry point of the program when launching or attaching. bool stopOnEntry = false; - /// Optional timeout when waiting for the program to `runInTermianl` or + /// Optional timeout when waiting for the program to `runInTerminal` or /// attach. std::chrono::seconds timeout = std::chrono::seconds(30); @@ -187,10 +187,10 @@ struct Configuration { /// default frame names will be used. This might come with a performance cost /// because debug information might need to be processed to generate the /// description. - std::string customFrameFormat = ""; + std::optional customFrameFormat; /// Same as `customFrameFormat`, but for threads instead of stack frames. - std::string customThreadFormat = ""; + std::optional customThreadFormat; /// Specify a source path to remap "./" to allow full paths to be used when /// setting breakpoints in binaries that have relative source paths. @@ -275,8 +275,8 @@ struct LaunchRequestArguments { /// with values or just "VAR" for environment variables with no values. llvm::StringMap env; - /// If set, then the client stub should detach rather than killing the debugee - /// if it loses connection with lldb. + /// If set, then the client stub should detach rather than killing the + /// debuggee if it loses connection with lldb. bool detachOnError = false; /// Disable ASLR (Address Space Layout Randomization) when launching the >From dfcdb0d6f60845dd273003ca2636b9ed110ceb2e Mon Sep 17 00:00:00 2001 From: John Harrison Date: Thu, 1 May 2025 16:48:50 -0700 Subject: [PATCH 4/7] Fix merge and return an error if `attachCommands` fails to make a valid target. --- lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp index 0265da3cdc077..c20c8166e270b 100644 --- a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp @@ -80,8 +80,7 @@ Error AttachRequestHandler::Run(const AttachRequestArguments &args) const { // Disable async events so the attach will be successful when we return from // the launch call and the launch will happen synchronously ScopeSyncMode scope_sync_mode(dap.debugger); - - dap.debugger.SetAsync(false); + if (args.coreFile.empty()) { if (args.gdbRemotePort != LLDB_DAP_INVALID_PORT) { lldb::SBListener listener = dap.debugger.GetListener(); @@ -112,6 +111,9 @@ Error AttachRequestHandler::Run(const AttachRequestArguments &args) const { // The custom commands might have created a new target so we should use the // selected target after these commands are run. dap.target = dap.debugger.GetSelectedTarget(); + if (!dap.target.IsValid()) + return make_error( + "attachCommands failed to create a valid target"); // Make sure the process is attached and stopped before proceeding as the // the launch commands are not run using the synchronous mode. >From 163847c552cba14d16e622cd14a3be427768aab8 Mon Sep 17 00:00:00 2001 From: John Harrison Date: Fri, 2 May 2025 17:52:35 -0700 Subject: [PATCH 5/7] Remove the empty check on formatters, since that is a valid formatter from the CLI. --- lldb/tools/lldb-dap/DAP.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 8d714cadc7a84..4fef1cef70300 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -1222,8 +1222,6 @@ void DAP::SetConfiguration(const protocol::Configuration &config, } void DAP::SetFrameFormat(llvm::StringRef format) { - if (format.empty()) - return; lldb::SBError error; frame_format = lldb::SBFormat(format.str().c_str(), error); if (error.Fail()) { @@ -1236,8 +1234,6 @@ void DAP::SetFrameFormat(llvm::StringRef format) { } void DAP::SetThreadFormat(llvm::StringRef format) { - if (format.empty()) - return; lldb::SBError error; thread_format = lldb::SBFormat(format.str().c_str(), error); if (error.Fail()) { >From 063192b479f6c8a99ad3b405639afff0920c0e49 Mon Sep 17 00:00:00 2001 From: John Harrison Date: Tue, 6 May 2025 16:49:15 -0700 Subject: [PATCH 6/7] Apply clang-format. --- lldb/tools/lldb-dap/Handler/RequestHandler.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp index bf117bac4f36d..7c2a42ca0647e 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp @@ -215,7 +215,8 @@ llvm::Error BaseRequestHandler::LaunchProcess( // Make sure the process is launched and stopped at the entry point before // proceeding. - lldb::SBError error = dap.WaitForProcessToStop(arguments.configuration.timeout); + lldb::SBError error = + dap.WaitForProcessToStop(arguments.configuration.timeout); if (error.Fail()) return ToError(error); >From 48e34e75447c70c3a9bc961836e9a7e86a087377 Mon Sep 17 00:00:00 2001 From: John Harrison Date: Tue, 6 May 2025 17:01:01 -0700 Subject: [PATCH 7/7] Reworked attach and launch to have the same flow for collecting the initial_thread_list and simplifying the number of if branches in the attach request handler. --- .../lldb-dap/Handler/AttachRequestHandler.cpp | 51 +++++++++---------- .../lldb-dap/Handler/LaunchRequestHandler.cpp | 28 ++++++---- .../tools/lldb-dap/Handler/RequestHandler.cpp | 9 ---- 3 files changed, 42 insertions(+), 46 deletions(-) diff --git a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp index 48c8206801679..297cc30f4c844 100644 --- a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp @@ -78,34 +78,7 @@ Error AttachRequestHandler::Run(const AttachRequestArguments &args) const { // Perform the launch in synchronous mode so that we don't have to worry // about process state changes during the launch. ScopeSyncMode scope_sync_mode(dap.debugger); - if (args.attachCommands.empty()) { - // No "attachCommands", just attach normally. - if (args.coreFile.empty()) { - if (args.gdbRemotePort != LLDB_DAP_INVALID_PORT) { - // If port is specified and pid is not. - lldb::SBListener listener = dap.debugger.GetListener(); - - // If the user hasn't provided the hostname property, default - // localhost being used. - std::string connect_url = - llvm::formatv("connect://{0}:", args.gdbRemoteHostname); - connect_url += std::to_string(args.gdbRemotePort); - dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", - error); - } else { - // Attach by pid or process name. - lldb::SBAttachInfo attach_info; - if (args.pid != LLDB_INVALID_PROCESS_ID) - attach_info.SetProcessID(args.pid); - else if (!dap.configuration.program.empty()) - attach_info.SetExecutable(dap.configuration.program.data()); - attach_info.SetWaitForLaunch(args.waitFor, false /*async*/); - dap.target.Attach(attach_info, error); - } - } else { - dap.target.LoadCore(args.coreFile.data(), error); - } - } else { + if (!args.attachCommands.empty()) { // We have "attachCommands" that are a set of commands that are expected // to execute the commands after which a process should be created. If // there is no valid process after running these commands, we have failed. @@ -115,6 +88,28 @@ Error AttachRequestHandler::Run(const AttachRequestArguments &args) const { // The custom commands might have created a new target so we should use // the selected target after these commands are run. dap.target = dap.debugger.GetSelectedTarget(); + } else if (!args.coreFile.empty()) { + dap.target.LoadCore(args.coreFile.data(), error); + } else if (args.gdbRemotePort != LLDB_DAP_INVALID_PORT) { + // If port is specified and pid is not. + lldb::SBListener listener = dap.debugger.GetListener(); + + // If the user hasn't provided the hostname property, default + // localhost being used. + std::string connect_url = + llvm::formatv("connect://{0}:", args.gdbRemoteHostname); + connect_url += std::to_string(args.gdbRemotePort); + dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", + error); + } else { + // Attach by pid or process name. + lldb::SBAttachInfo attach_info; + if (args.pid != LLDB_INVALID_PROCESS_ID) + attach_info.SetProcessID(args.pid); + else if (!dap.configuration.program.empty()) + attach_info.SetExecutable(dap.configuration.program.data()); + attach_info.SetWaitForLaunch(args.waitFor, false /*async*/); + dap.target.Attach(attach_info, error); } } diff --git a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp index 4973b7b411bb2..c6243b597a973 100644 --- a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp @@ -66,15 +66,25 @@ Error LaunchRequestHandler::Run(const LaunchRequestArguments &arguments) const { } void LaunchRequestHandler::PostRun() const { - if (dap.target.GetProcess().IsValid()) { - // Attach happens when launching with runInTerminal. - SendProcessEvent(dap, dap.is_attach ? Attach : Launch); - - if (dap.stop_at_entry) - SendThreadStoppedEvent(dap); - else - dap.target.GetProcess().Continue(); - } + if (!dap.target.GetProcess().IsValid()) + return; + + // Clients can request a baseline of currently existing threads after + // we acknowledge the configurationDone request. + // Client requests the baseline of currently existing threads after + // a successful or attach by sending a 'threads' request + // right after receiving the configurationDone response. + // Obtain the list of threads before we resume the process + dap.initial_thread_list = + GetThreads(dap.target.GetProcess(), dap.thread_format); + + // Attach happens when launching with runInTerminal. + SendProcessEvent(dap, dap.is_attach ? Attach : Launch); + + if (dap.stop_at_entry) + SendThreadStoppedEvent(dap); + else + dap.target.GetProcess().Continue(); } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp index 7c2a42ca0647e..93bc80a38e29d 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp @@ -220,15 +220,6 @@ llvm::Error BaseRequestHandler::LaunchProcess( if (error.Fail()) return ToError(error); - // Clients can request a baseline of currently existing threads after - // we acknowledge the configurationDone request. - // Client requests the baseline of currently existing threads after - // a successful or attach by sending a 'threads' request - // right after receiving the configurationDone response. - // Obtain the list of threads before we resume the process - dap.initial_thread_list = - GetThreads(dap.target.GetProcess(), dap.thread_format); - return llvm::Error::success(); } From lldb-commits at lists.llvm.org Tue May 6 17:02:30 2025 From: lldb-commits at lists.llvm.org (Adrian Prantl via lldb-commits) Date: Tue, 06 May 2025 17:02:30 -0700 (PDT) Subject: [Lldb-commits] [lldb] Branch island with numbers (PR #138781) In-Reply-To: Message-ID: <681aa316.630a0220.31ff65.5936@mx.google.com> https://github.com/adrian-prantl approved this pull request. https://github.com/llvm/llvm-project/pull/138781 From lldb-commits at lists.llvm.org Tue May 6 17:02:30 2025 From: lldb-commits at lists.llvm.org (Adrian Prantl via lldb-commits) Date: Tue, 06 May 2025 17:02:30 -0700 (PDT) Subject: [Lldb-commits] [lldb] Branch island with numbers (PR #138781) In-Reply-To: Message-ID: <681aa316.170a0220.35dbc3.5532@mx.google.com> ================ @@ -1028,6 +1029,23 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, thread_plan_sp = std::make_shared( thread, load_addrs, stop_others); } + // One more case we have to consider is "branch islands". These are regular + // TEXT symbols but their names end in .island plus maybe a .digit suffix. + // They are to allow arm64 code to branch further than the size of the + // address slot allows. We just need to single-instruction step in that + // case. + static const char *g_branch_island_pattern = "\\.island\\.?[0-9]*$"; + static RegularExpression g_branch_island_regex(g_branch_island_pattern); + + bool is_branch_island = g_branch_island_regex.Execute(current_name); + if (!thread_plan_sp && is_branch_island) { + thread_plan_sp = std::make_shared( + thread, + /* step_over= */ false, /* stop_others */ false, eVoteNoOpinion, ---------------- adrian-prantl wrote: ```suggestion /*step_over=*/ false, /*stop_others=*/ false, eVoteNoOpinion, ``` https://github.com/llvm/llvm-project/pull/138781 From lldb-commits at lists.llvm.org Tue May 6 17:02:30 2025 From: lldb-commits at lists.llvm.org (Adrian Prantl via lldb-commits) Date: Tue, 06 May 2025 17:02:30 -0700 (PDT) Subject: [Lldb-commits] [lldb] Branch island with numbers (PR #138781) In-Reply-To: Message-ID: <681aa316.630a0220.30586.6303@mx.google.com> ================ @@ -0,0 +1,16 @@ +C_SOURCES := main.c foo.c +CFLAGS_EXTRAS := -std=c99 + +include Makefile.rules + +a.out: main.o padding1.o padding2.o padding3.o padding4.o foo.o + ${CC} ${LDFLAGS} foo.o padding1.o padding2.o padding3.o padding4.o main.o -o a.out + +%.o: $(SRCDIR)/%.s + ${CC} -c $< + +#padding1.o: padding1.s ---------------- adrian-prantl wrote: delete this? https://github.com/llvm/llvm-project/pull/138781 From lldb-commits at lists.llvm.org Tue May 6 17:02:30 2025 From: lldb-commits at lists.llvm.org (Adrian Prantl via lldb-commits) Date: Tue, 06 May 2025 17:02:30 -0700 (PDT) Subject: [Lldb-commits] [lldb] Branch island with numbers (PR #138781) In-Reply-To: Message-ID: <681aa316.050a0220.1b10d4.c28c@mx.google.com> ================ @@ -1028,6 +1029,23 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, thread_plan_sp = std::make_shared( thread, load_addrs, stop_others); } + // One more case we have to consider is "branch islands". These are regular + // TEXT symbols but their names end in .island plus maybe a .digit suffix. + // They are to allow arm64 code to branch further than the size of the + // address slot allows. We just need to single-instruction step in that + // case. + static const char *g_branch_island_pattern = "\\.island\\.?[0-9]*$"; ---------------- adrian-prantl wrote: This is pedantic, but maybe `"\\.island\\(.[0-9]+)?$"` would be more precise, since `foo.island.` with no number should probably not get matched? https://github.com/llvm/llvm-project/pull/138781 From lldb-commits at lists.llvm.org Tue May 6 17:02:31 2025 From: lldb-commits at lists.llvm.org (Adrian Prantl via lldb-commits) Date: Tue, 06 May 2025 17:02:31 -0700 (PDT) Subject: [Lldb-commits] [lldb] Branch island with numbers (PR #138781) In-Reply-To: Message-ID: <681aa317.170a0220.bcd9d.53b2@mx.google.com> ================ @@ -0,0 +1,3 @@ +.text ---------------- adrian-prantl wrote: This is the first cross-architecture assembler source file I've seen :-) https://github.com/llvm/llvm-project/pull/138781 From lldb-commits at lists.llvm.org Tue May 6 17:03:53 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Tue, 06 May 2025 17:03:53 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Migrate attach to typed RequestHandler. (PR #137911) In-Reply-To: Message-ID: <681aa369.050a0220.346761.f569@mx.google.com> ashgti wrote: @JDevlieghere I sync this with your change to the attach flow. After syncing I tried to simplify the AttachRequestHandler a bit since there were a lot of branches that were a bit hard to follow. LMKWYT https://github.com/llvm/llvm-project/pull/137911 From lldb-commits at lists.llvm.org Tue May 6 17:09:41 2025 From: lldb-commits at lists.llvm.org (LLVM Continuous Integration via lldb-commits) Date: Tue, 06 May 2025 17:09:41 -0700 (PDT) Subject: [Lldb-commits] [lldb] Branch island with numbers (PR #138781) In-Reply-To: Message-ID: <681aa4c5.050a0220.2f01fa.f6bd@mx.google.com> llvm-ci wrote: LLVM Buildbot has detected a new failure on builder `lldb-x86_64-debian` running on `lldb-x86_64-debian` while building `lldb` at step 6 "test". Full details are available at: https://lab.llvm.org/buildbot/#/builders/162/builds/21769
Here is the relevant piece of the build log for the reference ``` Step 6 (test) failure: build (failure) ... UNSUPPORTED: lldb-shell :: SymbolFile/PDB/function-level-linking.test (2882 of 2891) UNSUPPORTED: lldb-shell :: SymbolFile/DWARF/x86/dwarf5-macho.c (2883 of 2891) UNSUPPORTED: lldb-api :: macosx/branch-islands/TestBranchIslands.py (2884 of 2891) PASS: lldb-api :: tools/lldb-server/TestLldbGdbServer.py (2885 of 2891) PASS: lldb-api :: api/multithreaded/TestMultithreaded.py (2886 of 2891) PASS: lldb-api :: terminal/TestEditlineCompletions.py (2887 of 2891) PASS: lldb-api :: commands/process/attach/TestProcessAttach.py (2888 of 2891) PASS: lldb-api :: repl/clang/TestClangREPL.py (2889 of 2891) PASS: lldb-api :: tools/lldb-dap/attach/TestDAP_attachByPortNum.py (2890 of 2891) TIMEOUT: lldb-api :: tools/lldb-dap/breakpoint/TestDAP_setExceptionBreakpoints.py (2891 of 2891) ******************** TEST 'lldb-api :: tools/lldb-dap/breakpoint/TestDAP_setExceptionBreakpoints.py' FAILED ******************** Script: -- /usr/bin/python3 /home/worker/2.0.1/lldb-x86_64-debian/llvm-project/lldb/test/API/dotest.py -u CXXFLAGS -u CFLAGS --env LLVM_LIBS_DIR=/home/worker/2.0.1/lldb-x86_64-debian/build/./lib --env LLVM_INCLUDE_DIR=/home/worker/2.0.1/lldb-x86_64-debian/build/include --env LLVM_TOOLS_DIR=/home/worker/2.0.1/lldb-x86_64-debian/build/./bin --arch x86_64 --build-dir /home/worker/2.0.1/lldb-x86_64-debian/build/lldb-test-build.noindex --lldb-module-cache-dir /home/worker/2.0.1/lldb-x86_64-debian/build/lldb-test-build.noindex/module-cache-lldb/lldb-api --clang-module-cache-dir /home/worker/2.0.1/lldb-x86_64-debian/build/lldb-test-build.noindex/module-cache-clang/lldb-api --executable /home/worker/2.0.1/lldb-x86_64-debian/build/./bin/lldb --compiler /home/worker/2.0.1/lldb-x86_64-debian/build/./bin/clang --dsymutil /home/worker/2.0.1/lldb-x86_64-debian/build/./bin/dsymutil --make /usr/bin/gmake --llvm-tools-dir /home/worker/2.0.1/lldb-x86_64-debian/build/./bin --lldb-obj-root /home/worker/2.0.1/lldb-x86_64-debian/build/tools/lldb --lldb-libs-dir /home/worker/2.0.1/lldb-x86_64-debian/build/./lib -t /home/worker/2.0.1/lldb-x86_64-debian/llvm-project/lldb/test/API/tools/lldb-dap/breakpoint -p TestDAP_setExceptionBreakpoints.py -- Exit Code: -9 Timeout: Reached timeout of 600 seconds Command Output (stdout): -- lldb version 21.0.0git (https://github.com/llvm/llvm-project.git revision 11f33ab3850886510a831122078a155be7dc1167) clang revision 11f33ab3850886510a831122078a155be7dc1167 llvm revision 11f33ab3850886510a831122078a155be7dc1167 -- Command Output (stderr): -- Change dir to: /home/worker/2.0.1/lldb-x86_64-debian/llvm-project/lldb/test/API/tools/lldb-dap/breakpoint runCmd: settings clear --all output: runCmd: settings set symbols.enable-external-lookup false output: runCmd: settings set target.inherit-tcc true output: runCmd: settings set target.disable-aslr false output: runCmd: settings set target.detach-on-error false output: runCmd: settings set target.auto-apply-fixits false ```
https://github.com/llvm/llvm-project/pull/138781 From lldb-commits at lists.llvm.org Tue May 6 18:07:06 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Tue, 06 May 2025 18:07:06 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Ensure we acquire the SB API lock while handling requests. (PR #137026) In-Reply-To: Message-ID: <681ab23a.170a0220.899d4.4ba5@mx.google.com> jeffreytan81 wrote: > What is the other lock involved in the deadlock, other than the API mutex? It is the same lock (`target.GetAPIMutex()`). If you looked at the shared callstacks above, main thread is holding the API mutex in `BaseRequestHandler`, at the same time, event thread above tries to call `SBProcess::GetExitStatus()` which also tries to acquire the same API mutex internally. This caused deadlock. https://github.com/llvm/llvm-project/pull/137026 From lldb-commits at lists.llvm.org Tue May 6 18:19:46 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Tue, 06 May 2025 18:19:46 -0700 (PDT) Subject: [Lldb-commits] [lldb] 69a0af3 - [lldb-dap] Don't make stopOnAttach the default in attach tests Message-ID: <681ab532.050a0220.1f4c5.af64@mx.google.com> Author: Jonas Devlieghere Date: 2025-05-06T18:19:37-07:00 New Revision: 69a0af35a5860156836e9e295ecef9de3474db11 URL: https://github.com/llvm/llvm-project/commit/69a0af35a5860156836e9e295ecef9de3474db11 DIFF: https://github.com/llvm/llvm-project/commit/69a0af35a5860156836e9e295ecef9de3474db11.diff LOG: [lldb-dap] Don't make stopOnAttach the default in attach tests Make stopOnAttach=False the default again and explicitly pass stopOnAttach=True where the tests relies on that. I changed the default in the launch sequence PR (#138219) because that was implicitly the assumption (the tests never send the configurationDone request). Added: Modified: lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py Removed: ################################################################################ diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py index e10342b72f4f0..e0ce92256fac0 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py @@ -592,7 +592,7 @@ def request_attach( attachCommands=None, terminateCommands=None, coreFile=None, - stopOnAttach=True, + stopOnAttach=False, postRunCommands=None, sourceMap=None, gdbRemotePort=None, diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py index 958c7268c0c72..e4be91744fddc 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py @@ -340,7 +340,7 @@ def attach( exitCommands=None, attachCommands=None, coreFile=None, - stopOnAttach=True, + stopOnAttach=False, disconnectAutomatically=True, terminateCommands=None, postRunCommands=None, diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py index 741c011a3d692..e0ec87bd9a8d8 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py @@ -53,7 +53,7 @@ def test_by_pid(self): stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) - self.attach(pid=self.process.pid) + self.attach(pid=self.process.pid, stopOnAttach=True) self.set_and_hit_breakpoint(continueToExit=True) @skipIfNetBSD # Hangs on NetBSD as well @@ -71,7 +71,7 @@ def test_by_name(self): popen = self.spawnSubprocess(program, [pid_file_path]) lldbutil.wait_for_file_on_target(self, pid_file_path) - self.attach(program=program) + self.attach(program=program, stopOnAttach=True) self.set_and_hit_breakpoint(continueToExit=True) @skipUnlessDarwin @@ -91,7 +91,7 @@ def test_by_name_waitFor(self): ), ) self.spawn_thread.start() - self.attach(program=program, waitFor=True) + self.attach(program=program, stopOnAttach=True, waitFor=True) self.set_and_hit_breakpoint(continueToExit=True) @skipIfNetBSD # Hangs on NetBSD as well @@ -133,6 +133,7 @@ def test_commands(self): terminateCommands = ["expr 4+2"] self.attach( program=program, + stopOnAttach=True, attachCommands=attachCommands, initCommands=initCommands, preRunCommands=preRunCommands, @@ -201,6 +202,7 @@ def test_terminate_commands(self): terminateCommands = ["expr 4+2"] self.attach( program=program, + stopOnAttach=True, attachCommands=attachCommands, terminateCommands=terminateCommands, disconnectAutomatically=False, diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py index 7250e67ebcd8c..12fba1133b95b 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py @@ -79,7 +79,9 @@ def test_by_port(self): port, " Failed to read the port number from debug server pipe" ) - self.attach(program=program, gdbRemotePort=port, sourceInitFile=True) + self.attach( + program=program, stopOnAttach=True, gdbRemotePort=port, sourceInitFile=True + ) self.set_and_hit_breakpoint(continueToExit=True) self.process.terminate() @@ -101,6 +103,7 @@ def test_by_port_and_pid(self): response = self.attach( program=program, pid=pid, + stopOnAttach=True, gdbRemotePort=port, sourceInitFile=True, expectFailure=True, @@ -120,7 +123,11 @@ def test_by_invalid_port(self): port = 0 response = self.attach( - program=program, gdbRemotePort=port, sourceInitFile=True, expectFailure=True + program=program, + stopOnAttach=True, + gdbRemotePort=port, + sourceInitFile=True, + expectFailure=True, ) if not (response and response["success"]): self.assertFalse( @@ -144,7 +151,11 @@ def test_by_illegal_port(self): ) response = self.attach( - program=program, gdbRemotePort=port, sourceInitFile=True, expectFailure=True + program=program, + stopOnAttach=True, + gdbRemotePort=port, + sourceInitFile=True, + expectFailure=True, ) if not (response and response["success"]): self.assertFalse( From lldb-commits at lists.llvm.org Tue May 6 18:21:21 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Tue, 06 May 2025 18:21:21 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Re-enable the lldb-dap tests (PR #138791) Message-ID: https://github.com/JDevlieghere created https://github.com/llvm/llvm-project/pull/138791 Re-enable the lldb-dap tests. We've spent the last week improving the reliability of the test suite and the tests now pass reliably on macOS and Linux at desk. Let's see how things fare on the bots. Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Tue May 6 18:21:30 2025 From: lldb-commits at lists.llvm.org (Felipe de Azevedo Piovezan via lldb-commits) Date: Tue, 06 May 2025 18:21:30 -0700 (PDT) Subject: [Lldb-commits] [lldb] a123891 - Revert "Branch island with numbers (#138781)" Message-ID: <681ab59a.170a0220.115669.9359@mx.google.com> Author: Felipe de Azevedo Piovezan Date: 2025-05-06T18:20:25-07:00 New Revision: a1238911f43c1d46cc7d4e72e2a16c3c9a157d9a URL: https://github.com/llvm/llvm-project/commit/a1238911f43c1d46cc7d4e72e2a16c3c9a157d9a DIFF: https://github.com/llvm/llvm-project/commit/a1238911f43c1d46cc7d4e72e2a16c3c9a157d9a.diff LOG: Revert "Branch island with numbers (#138781)" This reverts commit 11f33ab3850886510a831122078a155be7dc1167. This is failing on CI. Added: Modified: lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp Removed: lldb/test/API/macosx/branch-islands/Makefile lldb/test/API/macosx/branch-islands/TestBranchIslands.py lldb/test/API/macosx/branch-islands/foo.c lldb/test/API/macosx/branch-islands/main.c lldb/test/API/macosx/branch-islands/padding1.s lldb/test/API/macosx/branch-islands/padding2.s lldb/test/API/macosx/branch-islands/padding3.s lldb/test/API/macosx/branch-islands/padding4.s ################################################################################ diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index 578ab12268ea3..e25c4ff55e408 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -26,7 +26,6 @@ #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Target/ThreadPlanRunToAddress.h" -#include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Utility/DataBuffer.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/LLDBLog.h" @@ -924,15 +923,15 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, if (current_symbol != nullptr) { std::vector
addresses; - ConstString current_name = - current_symbol->GetMangled().GetName(Mangled::ePreferMangled); if (current_symbol->IsTrampoline()) { + ConstString trampoline_name = + current_symbol->GetMangled().GetName(Mangled::ePreferMangled); - if (current_name) { + if (trampoline_name) { const ModuleList &images = target_sp->GetImages(); SymbolContextList code_symbols; - images.FindSymbolsWithNameAndType(current_name, eSymbolTypeCode, + images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeCode, code_symbols); for (const SymbolContext &context : code_symbols) { Address addr = context.GetFunctionOrSymbolAddress(); @@ -946,8 +945,8 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, } SymbolContextList reexported_symbols; - images.FindSymbolsWithNameAndType(current_name, eSymbolTypeReExported, - reexported_symbols); + images.FindSymbolsWithNameAndType( + trampoline_name, eSymbolTypeReExported, reexported_symbols); for (const SymbolContext &context : reexported_symbols) { if (context.symbol) { Symbol *actual_symbol = @@ -969,7 +968,7 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, } SymbolContextList indirect_symbols; - images.FindSymbolsWithNameAndType(current_name, eSymbolTypeResolver, + images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeResolver, indirect_symbols); for (const SymbolContext &context : indirect_symbols) { @@ -1029,23 +1028,6 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, thread_plan_sp = std::make_shared( thread, load_addrs, stop_others); } - // One more case we have to consider is "branch islands". These are regular - // TEXT symbols but their names end in .island plus maybe a .digit suffix. - // They are to allow arm64 code to branch further than the size of the - // address slot allows. We just need to single-instruction step in that - // case. - static const char *g_branch_island_pattern = "\\.island\\.?[0-9]*$"; - static RegularExpression g_branch_island_regex(g_branch_island_pattern); - - bool is_branch_island = g_branch_island_regex.Execute(current_name); - if (!thread_plan_sp && is_branch_island) { - thread_plan_sp = std::make_shared( - thread, - /* step_over= */ false, /* stop_others */ false, eVoteNoOpinion, - eVoteNoOpinion); - LLDB_LOG(log, "Stepping one instruction over branch island: '{0}'.", - current_name); - } } else { LLDB_LOGF(log, "Could not find symbol for step through."); } diff --git a/lldb/test/API/macosx/branch-islands/Makefile b/lldb/test/API/macosx/branch-islands/Makefile deleted file mode 100644 index 062e947f6d6ee..0000000000000 --- a/lldb/test/API/macosx/branch-islands/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -C_SOURCES := main.c foo.c -CFLAGS_EXTRAS := -std=c99 - -include Makefile.rules - -a.out: main.o padding1.o padding2.o padding3.o padding4.o foo.o - ${CC} ${LDFLAGS} foo.o padding1.o padding2.o padding3.o padding4.o main.o -o a.out - -%.o: $(SRCDIR)/%.s - ${CC} -c $< - -#padding1.o: padding1.s -# ${CC} -c $(SRCDIR)/padding1.s - -#padding2.o: padding2.s -# ${CC} -c $(SRCDIR)/padding2.s diff --git a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py deleted file mode 100644 index b397e0c229b08..0000000000000 --- a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py +++ /dev/null @@ -1,35 +0,0 @@ -""" -Make sure that we can step in across an arm64 branch island -""" - - -import lldb -import lldbsuite.test.lldbutil as lldbutil -from lldbsuite.test.lldbtest import * -from lldbsuite.test.decorators import * - - -class TestBranchIslandStepping(TestBase): - NO_DEBUG_INFO_TESTCASE = True - - @skipUnlessDarwin - def test_step_in_branch_island(self): - """Make sure we can step in across a branch island""" - self.build() - self.main_source_file = lldb.SBFileSpec("main.c") - self.do_test() - - def do_test(self): - (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( - self, "Set a breakpoint here", self.main_source_file - ) - - # Make sure that we did manage to generate a branch island for foo: - syms = target.FindSymbols("foo.island", lldb.eSymbolTypeCode) - self.assertEqual(len(syms), 1, "We did generate an island for foo") - - thread.StepInto() - stop_frame = thread.frames[0] - self.assertIn("foo", stop_frame.name, "Stepped into foo") - var = stop_frame.FindVariable("a_variable_in_foo") - self.assertTrue(var.IsValid(), "Found the variable in foo") diff --git a/lldb/test/API/macosx/branch-islands/foo.c b/lldb/test/API/macosx/branch-islands/foo.c deleted file mode 100644 index a5dd2e59e1d82..0000000000000 --- a/lldb/test/API/macosx/branch-islands/foo.c +++ /dev/null @@ -1,6 +0,0 @@ -#include - -void foo() { - int a_variable_in_foo = 10; - printf("I am foo: %d.\n", a_variable_in_foo); -} diff --git a/lldb/test/API/macosx/branch-islands/main.c b/lldb/test/API/macosx/branch-islands/main.c deleted file mode 100644 index b5578bdd715df..0000000000000 --- a/lldb/test/API/macosx/branch-islands/main.c +++ /dev/null @@ -1,6 +0,0 @@ -extern void foo(); - -int main() { - foo(); // Set a breakpoint here - return 0; -} diff --git a/lldb/test/API/macosx/branch-islands/padding1.s b/lldb/test/API/macosx/branch-islands/padding1.s deleted file mode 100644 index 4911e53b0240d..0000000000000 --- a/lldb/test/API/macosx/branch-islands/padding1.s +++ /dev/null @@ -1,3 +0,0 @@ -.text -_padding1: -.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding2.s b/lldb/test/API/macosx/branch-islands/padding2.s deleted file mode 100644 index 5ad1bad11263b..0000000000000 --- a/lldb/test/API/macosx/branch-islands/padding2.s +++ /dev/null @@ -1,3 +0,0 @@ -.text -_padding2: -.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding3.s b/lldb/test/API/macosx/branch-islands/padding3.s deleted file mode 100644 index 9f614eecf56d9..0000000000000 --- a/lldb/test/API/macosx/branch-islands/padding3.s +++ /dev/null @@ -1,3 +0,0 @@ -.text -_padding3: -.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding4.s b/lldb/test/API/macosx/branch-islands/padding4.s deleted file mode 100644 index 12896cf5e5b8e..0000000000000 --- a/lldb/test/API/macosx/branch-islands/padding4.s +++ /dev/null @@ -1,3 +0,0 @@ -.text -_padding4: -.space 120*1024*1024 From lldb-commits at lists.llvm.org Tue May 6 18:22:16 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Tue, 06 May 2025 18:22:16 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Re-enable the lldb-dap tests (PR #138791) In-Reply-To: Message-ID: <681ab5c8.170a0220.3b236c.4fc5@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Jonas Devlieghere (JDevlieghere)
Changes Re-enable the lldb-dap tests. We've spent the last week improving the reliability of the test suite and the tests now pass reliably on macOS and Linux at desk. Let's see how things fare on the bots. --- Full diff: https://github.com/llvm/llvm-project/pull/138791.diff 9 Files Affected: - (modified) lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py (-1) - (modified) lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py (+1) - (modified) lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_breakpointLocations.py (+1-2) - (modified) lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py (+1-2) - (modified) lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py (-2) - (modified) lldb/test/API/tools/lldb-dap/disassemble/TestDAP_disassemble.py (+1-2) - (modified) lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py (-2) - (modified) lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py (+1-2) - (modified) lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py (+1-2) ``````````diff diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py index e0ec87bd9a8d8..c8ab5e9bedc88 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py @@ -24,7 +24,6 @@ def spawn_and_wait(program, delay): process.wait() - at skip class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase): def set_and_hit_breakpoint(self, continueToExit=True): self.dap_server.wait_for_stopped() diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py index 12fba1133b95b..489beb5a55ad8 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py @@ -18,6 +18,7 @@ import socket + at skip("SBTarget::ConnectRemote is not synchronous") class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase): default_timeout = 20 diff --git a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_breakpointLocations.py b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_breakpointLocations.py index 4a99cacc761a3..1058157e2c668 100644 --- a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_breakpointLocations.py +++ b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_breakpointLocations.py @@ -11,8 +11,7 @@ import lldbdap_testcase import os -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip + class TestDAP_breakpointLocations(lldbdap_testcase.DAPTestCaseBase): def setUp(self): lldbdap_testcase.DAPTestCaseBase.setUp(self) diff --git a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py index 6c6681804f250..26df2573555df 100644 --- a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py +++ b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py @@ -11,8 +11,7 @@ import lldbdap_testcase import os -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip + class TestDAP_setBreakpoints(lldbdap_testcase.DAPTestCaseBase): def setUp(self): lldbdap_testcase.DAPTestCaseBase.setUp(self) diff --git a/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py b/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py index 4aecf9a665c06..223258fbdd3dc 100644 --- a/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py +++ b/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py @@ -6,8 +6,6 @@ from lldbsuite.test.decorators import * -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip class TestDAP_commands(lldbdap_testcase.DAPTestCaseBase): def test_command_directive_quiet_on_success(self): program = self.getBuildArtifact("a.out") diff --git a/lldb/test/API/tools/lldb-dap/disassemble/TestDAP_disassemble.py b/lldb/test/API/tools/lldb-dap/disassemble/TestDAP_disassemble.py index ebecb349ac177..9e8ef5b289f2e 100644 --- a/lldb/test/API/tools/lldb-dap/disassemble/TestDAP_disassemble.py +++ b/lldb/test/API/tools/lldb-dap/disassemble/TestDAP_disassemble.py @@ -10,8 +10,7 @@ import lldbdap_testcase import os -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip + class TestDAP_disassemble(lldbdap_testcase.DAPTestCaseBase): @skipIfWindows def test_disassemble(self): diff --git a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py index 19b682dfcd22d..372a9bb75e007 100644 --- a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py +++ b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py @@ -11,8 +11,6 @@ from lldbsuite.test.lldbtest import * -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase): def assertEvaluate(self, expression, regex): self.assertRegex( diff --git a/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py b/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py index c71ba871b8a22..ea43fccf016a7 100644 --- a/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py +++ b/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py @@ -10,8 +10,7 @@ import lldbdap_testcase import os -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip + class TestDAP_memory(lldbdap_testcase.DAPTestCaseBase): def test_memory_refs_variables(self): """ diff --git a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py index 560207bfbb66c..3b45cdc245838 100644 --- a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py +++ b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py @@ -17,8 +17,7 @@ def make_buffer_verify_dict(start_idx, count, offset=0): verify_dict["[%i]" % (i)] = {"type": "int", "value": str(i + offset)} return verify_dict -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip + class TestDAP_variables(lldbdap_testcase.DAPTestCaseBase): def verify_values(self, verify_dict, actual, varref_dict=None, expression=None): if "equals" in verify_dict: ``````````
https://github.com/llvm/llvm-project/pull/138791 From lldb-commits at lists.llvm.org Tue May 6 18:25:59 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Tue, 06 May 2025 18:25:59 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Re-enable the lldb-dap tests (PR #138791) In-Reply-To: Message-ID: <681ab6a7.620a0220.198627.bd3b@mx.google.com> https://github.com/kusmour approved this pull request. Let's go (fingercrossed https://github.com/llvm/llvm-project/pull/138791 From lldb-commits at lists.llvm.org Tue May 6 19:00:18 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Tue, 06 May 2025 19:00:18 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Ensure we acquire the SB API lock while handling requests. (PR #137026) In-Reply-To: Message-ID: <681abeb2.a70a0220.31c217.bb0b@mx.google.com> ashgti wrote: We could move the join to when the DAP::Loop exits, that would unblock the event handler, I think. https://github.com/llvm/llvm-project/pull/137026 From lldb-commits at lists.llvm.org Tue May 6 19:52:20 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Tue, 06 May 2025 19:52:20 -0700 (PDT) Subject: [Lldb-commits] [lldb] 5e70460 - Revert "[lldb-dap] Don't make stopOnAttach the default in attach tests" Message-ID: <681acae4.170a0220.20c4dc.5bbb@mx.google.com> Author: Jonas Devlieghere Date: 2025-05-06T19:52:00-07:00 New Revision: 5e70460d0155aacbd926f97a7c059de009b6e22d URL: https://github.com/llvm/llvm-project/commit/5e70460d0155aacbd926f97a7c059de009b6e22d DIFF: https://github.com/llvm/llvm-project/commit/5e70460d0155aacbd926f97a7c059de009b6e22d.diff LOG: Revert "[lldb-dap] Don't make stopOnAttach the default in attach tests" This reverts commit 69a0af35a5860156836e9e295ecef9de3474db11 as it's timing out on the bots. Added: Modified: lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py Removed: ################################################################################ diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py index e0ce92256fac0..e10342b72f4f0 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py @@ -592,7 +592,7 @@ def request_attach( attachCommands=None, terminateCommands=None, coreFile=None, - stopOnAttach=False, + stopOnAttach=True, postRunCommands=None, sourceMap=None, gdbRemotePort=None, diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py index e4be91744fddc..958c7268c0c72 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py @@ -340,7 +340,7 @@ def attach( exitCommands=None, attachCommands=None, coreFile=None, - stopOnAttach=False, + stopOnAttach=True, disconnectAutomatically=True, terminateCommands=None, postRunCommands=None, diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py index e0ec87bd9a8d8..741c011a3d692 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py @@ -53,7 +53,7 @@ def test_by_pid(self): stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) - self.attach(pid=self.process.pid, stopOnAttach=True) + self.attach(pid=self.process.pid) self.set_and_hit_breakpoint(continueToExit=True) @skipIfNetBSD # Hangs on NetBSD as well @@ -71,7 +71,7 @@ def test_by_name(self): popen = self.spawnSubprocess(program, [pid_file_path]) lldbutil.wait_for_file_on_target(self, pid_file_path) - self.attach(program=program, stopOnAttach=True) + self.attach(program=program) self.set_and_hit_breakpoint(continueToExit=True) @skipUnlessDarwin @@ -91,7 +91,7 @@ def test_by_name_waitFor(self): ), ) self.spawn_thread.start() - self.attach(program=program, stopOnAttach=True, waitFor=True) + self.attach(program=program, waitFor=True) self.set_and_hit_breakpoint(continueToExit=True) @skipIfNetBSD # Hangs on NetBSD as well @@ -133,7 +133,6 @@ def test_commands(self): terminateCommands = ["expr 4+2"] self.attach( program=program, - stopOnAttach=True, attachCommands=attachCommands, initCommands=initCommands, preRunCommands=preRunCommands, @@ -202,7 +201,6 @@ def test_terminate_commands(self): terminateCommands = ["expr 4+2"] self.attach( program=program, - stopOnAttach=True, attachCommands=attachCommands, terminateCommands=terminateCommands, disconnectAutomatically=False, diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py index 12fba1133b95b..7250e67ebcd8c 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py @@ -79,9 +79,7 @@ def test_by_port(self): port, " Failed to read the port number from debug server pipe" ) - self.attach( - program=program, stopOnAttach=True, gdbRemotePort=port, sourceInitFile=True - ) + self.attach(program=program, gdbRemotePort=port, sourceInitFile=True) self.set_and_hit_breakpoint(continueToExit=True) self.process.terminate() @@ -103,7 +101,6 @@ def test_by_port_and_pid(self): response = self.attach( program=program, pid=pid, - stopOnAttach=True, gdbRemotePort=port, sourceInitFile=True, expectFailure=True, @@ -123,11 +120,7 @@ def test_by_invalid_port(self): port = 0 response = self.attach( - program=program, - stopOnAttach=True, - gdbRemotePort=port, - sourceInitFile=True, - expectFailure=True, + program=program, gdbRemotePort=port, sourceInitFile=True, expectFailure=True ) if not (response and response["success"]): self.assertFalse( @@ -151,11 +144,7 @@ def test_by_illegal_port(self): ) response = self.attach( - program=program, - stopOnAttach=True, - gdbRemotePort=port, - sourceInitFile=True, - expectFailure=True, + program=program, gdbRemotePort=port, sourceInitFile=True, expectFailure=True ) if not (response and response["success"]): self.assertFalse( From lldb-commits at lists.llvm.org Tue May 6 20:00:26 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Tue, 06 May 2025 20:00:26 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Migrate attach to typed RequestHandler. (PR #137911) In-Reply-To: Message-ID: <681accca.170a0220.1d6021.52fd@mx.google.com> https://github.com/JDevlieghere edited https://github.com/llvm/llvm-project/pull/137911 From lldb-commits at lists.llvm.org Tue May 6 20:00:26 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Tue, 06 May 2025 20:00:26 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Migrate attach to typed RequestHandler. (PR #137911) In-Reply-To: Message-ID: <681accca.170a0220.15e6cd.7dab@mx.google.com> ================ @@ -675,12 +675,11 @@ lldb::SBTarget DAP::CreateTarget(lldb::SBError &error) { // enough information to determine correct arch and platform (or ELF can be // omitted at all), so it is good to leave the user an opportunity to specify // those. Any of those three can be left empty. - auto target = this->debugger.CreateTarget( - configuration.program.value_or("").data(), - configuration.targetTriple.value_or("").data(), - configuration.platformName.value_or("").data(), - true, // Add dependent modules. - error); + auto target = this->debugger.CreateTarget(configuration.program.data(), + configuration.targetTriple.data(), + configuration.platformName.data(), + true, // Add dependent modules. ---------------- JDevlieghere wrote: Since you're touching this... ```suggestion /*add_dependent_modules=*/true, ``` https://github.com/llvm/llvm-project/pull/137911 From lldb-commits at lists.llvm.org Tue May 6 20:00:26 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Tue, 06 May 2025 20:00:26 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Migrate attach to typed RequestHandler. (PR #137911) In-Reply-To: Message-ID: <681accca.630a0220.382d9.7e54@mx.google.com> ================ @@ -10,183 +10,133 @@ #include "EventHelper.h" #include "JSONUtils.h" #include "LLDBUtils.h" +#include "Protocol/ProtocolRequests.h" #include "RequestHandler.h" +#include "lldb/API/SBAttachInfo.h" #include "lldb/API/SBListener.h" +#include "lldb/lldb-defines.h" +#include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" +using namespace llvm; +using namespace lldb_dap::protocol; + namespace lldb_dap { -// "AttachRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "Attach request; value of command field is 'attach'.", -// "properties": { -// "command": { -// "type": "string", -// "enum": [ "attach" ] -// }, -// "arguments": { -// "$ref": "#/definitions/AttachRequestArguments" -// } -// }, -// "required": [ "command", "arguments" ] -// }] -// }, -// "AttachRequestArguments": { -// "type": "object", -// "description": "Arguments for 'attach' request.\nThe attach request has no -// standardized attributes." -// }, -// "AttachResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to 'attach' request. This is just an -// acknowledgement, so no body field is required." -// }] -// } -void AttachRequestHandler::operator()(const llvm::json::Object &request) const { - dap.is_attach = true; - llvm::json::Object response; - lldb::SBError error; - FillResponse(request, response); - const int invalid_port = 0; - const auto *arguments = request.getObject("arguments"); - const lldb::pid_t pid = - GetInteger(arguments, "pid").value_or(LLDB_INVALID_PROCESS_ID); - const auto gdb_remote_port = - GetInteger(arguments, "gdb-remote-port").value_or(invalid_port); - const auto gdb_remote_hostname = - GetString(arguments, "gdb-remote-hostname").value_or("localhost"); - const auto wait_for = GetBoolean(arguments, "waitFor").value_or(false); - dap.configuration.initCommands = GetStrings(arguments, "initCommands"); - dap.configuration.preRunCommands = GetStrings(arguments, "preRunCommands"); - dap.configuration.postRunCommands = GetStrings(arguments, "postRunCommands"); - dap.configuration.stopCommands = GetStrings(arguments, "stopCommands"); - dap.configuration.exitCommands = GetStrings(arguments, "exitCommands"); - dap.configuration.terminateCommands = - GetStrings(arguments, "terminateCommands"); - auto attachCommands = GetStrings(arguments, "attachCommands"); - llvm::StringRef core_file = GetString(arguments, "coreFile").value_or(""); - const uint64_t timeout_seconds = - GetInteger(arguments, "timeout").value_or(30); - dap.stop_at_entry = core_file.empty() - ? GetBoolean(arguments, "stopOnEntry").value_or(false) - : true; - const llvm::StringRef debuggerRoot = - GetString(arguments, "debuggerRoot").value_or(""); - dap.configuration.enableAutoVariableSummaries = - GetBoolean(arguments, "enableAutoVariableSummaries").value_or(false); - dap.configuration.enableSyntheticChildDebugging = - GetBoolean(arguments, "enableSyntheticChildDebugging").value_or(false); - dap.configuration.displayExtendedBacktrace = - GetBoolean(arguments, "displayExtendedBacktrace").value_or(false); - dap.configuration.commandEscapePrefix = - GetString(arguments, "commandEscapePrefix").value_or("`"); - dap.configuration.program = GetString(arguments, "program"); - dap.configuration.targetTriple = GetString(arguments, "targetTriple"); - dap.configuration.platformName = GetString(arguments, "platformName"); - dap.SetFrameFormat(GetString(arguments, "customFrameFormat").value_or("")); - dap.SetThreadFormat(GetString(arguments, "customThreadFormat").value_or("")); +/// The `attach` request is sent from the client to the debug adapter to attach +/// to a debuggee that is already running. +/// +/// Since attaching is debugger/runtime specific, the arguments for this request +/// are not part of this specification. +Error AttachRequestHandler::Run(const AttachRequestArguments &args) const { + // Validate that we have a well formed attach request. + if (args.attachCommands.empty() && args.coreFile.empty() && + args.configuration.program.empty() && + args.pid == LLDB_INVALID_PROCESS_ID && + args.gdbRemotePort == LLDB_DAP_INVALID_PORT) + return make_error( + "expected one of 'pid', 'program', 'attachCommands', " + "'coreFile' or 'gdb-remote-port' to be specified"); + + // Check if we have mutually exclusive arguments. + if ((args.pid != LLDB_INVALID_PROCESS_ID) && + (args.gdbRemotePort != LLDB_DAP_INVALID_PORT)) + return make_error( + "'pid' and 'gdb-remote-port' are mutually exclusive"); + + dap.SetConfiguration(args.configuration, /*is_attach=*/true); + if (!args.coreFile.empty()) + dap.stop_at_entry = true; PrintWelcomeMessage(); // This is a hack for loading DWARF in .o files on Mac where the .o files - // in the debug map of the main executable have relative paths which require - // the lldb-dap binary to have its working directory set to that relative - // root for the .o files in order to be able to load debug info. - if (!debuggerRoot.empty()) - llvm::sys::fs::set_current_path(debuggerRoot); + // in the debug map of the main executable have relative paths which + // require the lldb-dap binary to have its working directory set to that + // relative root for the .o files in order to be able to load debug info. + if (!dap.configuration.debuggerRoot.empty()) + sys::fs::set_current_path(dap.configuration.debuggerRoot); // Run any initialize LLDB commands the user specified in the launch.json - if (llvm::Error err = dap.RunInitCommands()) { - response["success"] = false; - EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; - } + if (llvm::Error err = dap.RunInitCommands()) + return err; - SetSourceMapFromArguments(*arguments); + dap.ConfigureSourceMaps(); - lldb::SBError status; - dap.SetTarget(dap.CreateTarget(status)); - if (status.Fail()) { - response["success"] = llvm::json::Value(false); - EmplaceSafeString(response, "message", status.GetCString()); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; - } + lldb::SBError error; + lldb::SBTarget target = dap.CreateTarget(error); + if (error.Fail()) + return ToError(error); + + dap.SetTarget(target); // Run any pre run LLDB commands the user specified in the launch.json - if (llvm::Error err = dap.RunPreRunCommands()) { - response["success"] = false; - EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; - } + if (Error err = dap.RunPreRunCommands()) + return err; - if ((pid == LLDB_INVALID_PROCESS_ID || gdb_remote_port == invalid_port) && - wait_for) { + if ((args.pid == LLDB_INVALID_PROCESS_ID || + args.gdbRemotePort == LLDB_DAP_INVALID_PORT) && + args.waitFor) { char attach_msg[256]; auto attach_msg_len = snprintf(attach_msg, sizeof(attach_msg), "Waiting to attach to \"%s\"...", dap.target.GetExecutable().GetFilename()); - dap.SendOutput(OutputType::Console, - llvm::StringRef(attach_msg, attach_msg_len)); + dap.SendOutput(OutputType::Console, StringRef(attach_msg, attach_msg_len)); ---------------- JDevlieghere wrote: ```suggestion dap.SendOutput(OutputType::Console, llvm::formatv("Waiting to attach to \"{0}\"...", dap.target.GetExecutable().GetFilename()).str()); ``` https://github.com/llvm/llvm-project/pull/137911 From lldb-commits at lists.llvm.org Tue May 6 20:00:27 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Tue, 06 May 2025 20:00:27 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Migrate attach to typed RequestHandler. (PR #137911) In-Reply-To: Message-ID: <681acccb.170a0220.88e13.5df1@mx.google.com> ================ @@ -183,7 +194,7 @@ struct Configuration { /// Specify a source path to remap "./" to allow full paths to be used when /// setting breakpoints in binaries that have relative source paths. - std::optional sourcePath; + std::string sourcePath = ""; ---------------- JDevlieghere wrote: Why not just? Same below. ```suggestion std::string sourcePath; ``` https://github.com/llvm/llvm-project/pull/137911 From lldb-commits at lists.llvm.org Tue May 6 20:00:26 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Tue, 06 May 2025 20:00:26 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Migrate attach to typed RequestHandler. (PR #137911) In-Reply-To: Message-ID: <681accca.170a0220.afc2d.b56e@mx.google.com> https://github.com/JDevlieghere approved this pull request. LGTM modulo nits. https://github.com/llvm/llvm-project/pull/137911 From lldb-commits at lists.llvm.org Tue May 6 20:00:39 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Tue, 06 May 2025 20:00:39 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Re-enable the lldb-dap tests (PR #138791) In-Reply-To: Message-ID: <681accd7.170a0220.e8c78.52f8@mx.google.com> ================ @@ -18,6 +18,7 @@ import socket + at skip("SBTarget::ConnectRemote is not synchronous") ---------------- ashgti wrote: Should we put a link to the bug to fix this test? https://github.com/llvm/llvm-project/pull/138791 From lldb-commits at lists.llvm.org Tue May 6 20:00:46 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Tue, 06 May 2025 20:00:46 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Re-enable the lldb-dap tests (PR #138791) In-Reply-To: Message-ID: <681accde.170a0220.be98b.5edc@mx.google.com> https://github.com/ashgti approved this pull request. https://github.com/llvm/llvm-project/pull/138791 From lldb-commits at lists.llvm.org Tue May 6 20:52:41 2025 From: lldb-commits at lists.llvm.org (Oliver Hunt via lldb-commits) Date: Tue, 06 May 2025 20:52:41 -0700 (PDT) Subject: [Lldb-commits] [clang] [lldb] [clang] Add `__ptrauth_restricted_intptr` qualifier (PR #137580) In-Reply-To: Message-ID: <681ad909.170a0220.249cc1.8592@mx.google.com> https://github.com/ojhunt updated https://github.com/llvm/llvm-project/pull/137580 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Tue May 6 22:14:58 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Tue, 06 May 2025 22:14:58 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Re-enable the lldb-dap tests (PR #138791) In-Reply-To: Message-ID: <681aec52.630a0220.ca9bd.7e87@mx.google.com> ================ @@ -18,6 +18,7 @@ import socket + at skip("SBTarget::ConnectRemote is not synchronous") ---------------- JDevlieghere wrote: I filed #138803 https://github.com/llvm/llvm-project/pull/138791 From lldb-commits at lists.llvm.org Tue May 6 22:16:55 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Tue, 06 May 2025 22:16:55 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Re-enable the lldb-dap tests (PR #138791) In-Reply-To: Message-ID: <681aecc7.630a0220.30586.8ef1@mx.google.com> https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/138791 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Tue May 6 22:31:39 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Tue, 06 May 2025 22:31:39 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Parse DWARF CFI for discontinuous functions (PR #137006) In-Reply-To: Message-ID: <681af03b.170a0220.2e2888.6eec@mx.google.com> ================ @@ -122,6 +122,13 @@ AddressRanges UnwindTable::GetAddressRanges(const Address &addr, return {}; } +static Address GetFunctionOrSymbolAddress(const Address &addr, + const SymbolContext &sc) { + if (Address result = sc.GetFunctionOrSymbolAddress(); result.IsValid()) ---------------- jasonmolenda wrote: lol at first I was like wait, does that actually work, but sure enough in C++17. https://github.com/llvm/llvm-project/pull/137006 From lldb-commits at lists.llvm.org Tue May 6 22:31:39 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Tue, 06 May 2025 22:31:39 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Parse DWARF CFI for discontinuous functions (PR #137006) In-Reply-To: Message-ID: <681af03b.050a0220.3a40a7.e753@mx.google.com> ================ @@ -17,23 +17,32 @@ image show-unwind --cached true -n foo # CHECK: UNWIND PLANS for {{.*}}`foo # -# CHECK: Assembly language inspection UnwindPlan: -# CHECK-NEXT: This UnwindPlan originally sourced from assembly insn profiling -# CHECK-NEXT: This UnwindPlan is sourced from the compiler: no. -# CHECK-NEXT: This UnwindPlan is valid at all instruction locations: yes. +# CHECK: eh_frame UnwindPlan: +# CHECK-NEXT: This UnwindPlan originally sourced from eh_frame CFI +# CHECK-NEXT: This UnwindPlan is sourced from the compiler: yes. +# CHECK-NEXT: This UnwindPlan is valid at all instruction locations: no. # CHECK-NEXT: This UnwindPlan is for a trap handler function: no. -# TODO: This address range isn't correct right now. We're just checking that -# it's a different range from the one in the next query. -# CHECK-NEXT: Address range of this UnwindPlan: [{{.*}}.text + 6-0x0000000000000046) +# CHECK-NEXT: Address range of this UnwindPlan: [{{.*}}.text + 6-0x0000000000000010)[{{.*}}.text + 17-0x000000000000001c)[{{.*}}.text + 44-0x0000000000000037)[{{.*}}.text + 56-0x000000000000003d) +# CHECK-NEXT: row[0]: 0: CFA=rsp +8 => rip=[CFA-8] +# CHECK-NEXT: row[1]: 1: CFA=rsp+16 => rbx=[CFA-16] rip=[CFA-8] +# CHECK-NEXT: row[2]: 11: CFA=rsp+16 => rbx=[CFA-16] rip=[CFA-8] +# CHECK-NEXT: row[3]: 15: CFA=rsp+32 => rbx=[CFA-16] rip=[CFA-8] +# CHECK-NEXT: row[4]: 38: CFA=rsp+16 => rbx=[CFA-16] rip=[CFA-8] +# CHECK-NEXT: row[5]: 42: CFA=rsp+32 => rbx=[CFA-16] rip=[CFA-8] +# CHECK-NEXT: row[6]: 50: CFA=rsp+32 => rbx=[CFA-16] rip=[CFA-8] +# CHECK-NEXT: row[7]: 54: CFA=rsp +8 => rbx=[CFA-16] rip=[CFA-8] +# CHECK-EMPTY: image show-unwind --cached true -n bar # CHECK: UNWIND PLANS for {{.*}}`bar -# CHECK: Assembly language inspection UnwindPlan: -# CHECK-NEXT: This UnwindPlan originally sourced from assembly insn profiling -# CHECK-NEXT: This UnwindPlan is sourced from the compiler: no. -# CHECK-NEXT: This UnwindPlan is valid at all instruction locations: yes. +# CHECK: eh_frame UnwindPlan: +# CHECK-NEXT: This UnwindPlan originally sourced from eh_frame CFI +# CHECK-NEXT: This UnwindPlan is sourced from the compiler: yes. +# CHECK-NEXT: This UnwindPlan is valid at all instruction locations: no. # CHECK-NEXT: This UnwindPlan is for a trap handler function: no. -# TODO: This address range isn't correct right now. We're just checking that -# it's a different range from the one in the previous query. -# CHECK-NEXT: Address range of this UnwindPlan: [{{.*}}.text + 35-0x0000000000000033) +# CHECK-NEXT: Address range of this UnwindPlan: [{{.*}}.text + 28-0x000000000000002c) ---------------- jasonmolenda wrote: obviously not relevant to this PR but man `Address range of this UnwindPlan: [{{.*}}.text + 28-0x000000000000002c` is ugly. I wonder if `AddressRange::Dump` should call `DumpAddress(s->AsRawOstream(), m_base_addr.GetOffset() + GetByteSize(), 1);` (`1` instead of `addr_size` as it does today) for `DumpStyleSectionNameOffset` - so it's not address-size zero padded - but I'm sure that would break some other tabular printing elsewhere, so nevermind. https://github.com/llvm/llvm-project/pull/137006 From lldb-commits at lists.llvm.org Tue May 6 22:31:39 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Tue, 06 May 2025 22:31:39 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Parse DWARF CFI for discontinuous functions (PR #137006) In-Reply-To: Message-ID: <681af03b.050a0220.18efb3.1f2a@mx.google.com> https://github.com/jasonmolenda approved this pull request. Sorry for taking so long to look at this one, it all looks reasonable and I'm fine with this. It would be valid for eh_frame entries which are only valid at throwable locations to not be scoped to a single function. If you had two functions next to each other that have the exact same unwind state in the body of the function, it would be valid eh_frame to have a single entry covering both of them. I only mention this because of the comment about a function starting at offset 0 in an UnwindPlan. In this scenario I describe, the unwind plan would have a single row at offset 0, starting at the start address of the first function and extending to the end of the second function. I've never seen a compiler emit eh_frame like this so it's not important in any way, but just thinking out loud. On Darwin systems we have a much more constrained format exclusively for exception throwing called compact unwind, and it does this exact trick for consecutive functions with the same unwind state. https://github.com/llvm/llvm-project/pull/137006 From lldb-commits at lists.llvm.org Tue May 6 22:31:38 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Tue, 06 May 2025 22:31:38 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Parse DWARF CFI for discontinuous functions (PR #137006) In-Reply-To: Message-ID: <681af03a.170a0220.202f91.664c@mx.google.com> https://github.com/jasonmolenda edited https://github.com/llvm/llvm-project/pull/137006 From lldb-commits at lists.llvm.org Tue May 6 22:34:39 2025 From: lldb-commits at lists.llvm.org (Jacques Pienaar via lldb-commits) Date: Tue, 06 May 2025 22:34:39 -0700 (PDT) Subject: [Lldb-commits] [clang] [lldb] [llvm] [mlir] [NFC][Support] Add llvm::uninitialized_copy (PR #138174) In-Reply-To: Message-ID: <681af0ef.170a0220.291246.dd94@mx.google.com> https://github.com/jpienaar approved this pull request. https://github.com/llvm/llvm-project/pull/138174 From lldb-commits at lists.llvm.org Tue May 6 22:50:03 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Tue, 06 May 2025 22:50:03 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) Message-ID: https://github.com/jasonmolenda created https://github.com/llvm/llvm-project/pull/138805 When a frameless function faults or is interrupted asynchronously, the UnwindPlan MAY have no register location rule for the return address register (lr on arm64); the value is simply live in the lr register when it was interrupted, and the frame below this on the stack -- e.g. sigtramp on a Unix system -- has the full register context, including that register. RegisterContextUnwind::SavedLocationForRegister, when asked to find the caller's pc value, will first see if there is a pc register location. If there isn't, on a Return Address Register architecture like arm/mips/riscv, we rewrite the register request from "pc" to "RA register", and search for a location. On frame 0 (the live frame) and an interrupted frame, the UnwindPlan may have no register location rule for the RA Reg, that is valid. A frameless function that never calls another may simply keep the return address in the live register the whole way. Our instruction emulation unwind plans explicitly add a rule (see Pavel's May 2024 change https://github.com/llvm/llvm-project/pull/91321 ), but an UnwindPlan sourced from debug_frame may not. I've got a case where this exactly happens - clang debug_frame for arm64 where there is no register location for the lr in a frameless function. There is a fault in the middle of this frameless function and we only get the lr value from the fault handler below this frame if lr has a register location of `IsSame`, in line with Pavel's 2024 change. Similar to how we see a request of the RA Reg from frame 0 after failing to find an unwind location for the pc register, the same style of special casing is needed when this is a function that was interrupted. Without this change, we can find the pc of the frame that was executing when it was interrupted, but we need $lr to find its caller, and we don't descend down to the trap handler to get that value, truncating the stack. rdar://145614545 >From 4fc9acd03a58a3617b113352c48e5dd2fdb58eda Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Tue, 6 May 2025 22:37:17 -0700 Subject: [PATCH] [lldb] Provide lr value in faulting frame on arm64 When a frameless function faults or is interrupted asynchronously, the UnwindPlan MAY have no register location rule for the return address register (lr on arm64); the value is simply live in the lr register when it was interrupted, and the frame below this on the stack -- e.g. sigtramp on a Unix system -- has the full register context, including that register. RegisterContextUnwind::SavedLocationForRegister, when asked to find the caller's pc value, will first see if there is a pc register location. If there isn't, on a Return Address Register architecture like arm/mips/riscv, we rewrite the register request from "pc" to "RA register", and search for a location. On frame 0 (the live frame) and an interrupted frame, the UnwindPlan may have no register location rule for the RA Reg, that is valid. A frameless function that never calls another may simply keep the return address in the live register the whole way. Our instruction emulation unwind plans explicitly add a rule (see Pavel's May 2024 change https://github.com/llvm/llvm-project/pull/91321 ), but an UnwindPlan sourced from debug_frame may not. I've got a case where this exactly happens - clang debug_frame for arm64 where there is no register location for the lr in a frameless function. There is a fault in the middle of this frameless function and we only get the lr value from the fault handler below this frame if lr has a register location of `IsSame`, in line with Pavel's 2024 change. Similar to how we see a request of the RA Reg from frame 0 after failing to find an unwind location for the pc register, the same style of special casing is needed when this is a function that was interrupted. Without this change, we can find the pc of the frame that was executing when it was interrupted, but we need $lr to find its caller, and we don't descend down to the trap handler to get that value, truncating the stack. rdar://145614545 --- lldb/source/Target/RegisterContextUnwind.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 3ed49e12476dd..23a86bee2518b 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -1377,6 +1377,7 @@ RegisterContextUnwind::SavedLocationForRegister( } } + // Check if the active_row has a register location listed. if (regnum.IsValid() && active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind), unwindplan_regloc)) { @@ -1390,11 +1391,10 @@ RegisterContextUnwind::SavedLocationForRegister( // This is frame 0 and we're retrieving the PC and it's saved in a Return // Address register and it hasn't been saved anywhere yet -- that is, // it's still live in the actual register. Handle this specially. - if (!have_unwindplan_regloc && return_address_reg.IsValid() && - IsFrameZero()) { - if (return_address_reg.GetAsKind(eRegisterKindLLDB) != - LLDB_INVALID_REGNUM) { + return_address_reg.GetAsKind(eRegisterKindLLDB) != + LLDB_INVALID_REGNUM) { + if (IsFrameZero()) { lldb_private::UnwindLLDB::ConcreteRegisterLocation new_regloc; new_regloc.type = UnwindLLDB::ConcreteRegisterLocation:: eRegisterInLiveRegisterContext; @@ -1408,6 +1408,17 @@ RegisterContextUnwind::SavedLocationForRegister( return_address_reg.GetAsKind(eRegisterKindLLDB), return_address_reg.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } else if (BehavesLikeZerothFrame()) { + // This function was interrupted asynchronously -- it faulted, + // an async interrupt, a timer fired, a debugger expression etc. + // The caller's pc is in the Return Address register, but the + // UnwindPlan for this function may have no location rule for + // the RA reg. + // This means that the caller's return address is in the RA reg + // when the function was interrupted--descend down one stack frame + // to retrieve it from the trap handler's saved context. + unwindplan_regloc.SetSame(); + have_unwindplan_regloc = true; } } From lldb-commits at lists.llvm.org Tue May 6 22:50:37 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Tue, 06 May 2025 22:50:37 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681af4ad.050a0220.27c5b.1f25@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Jason Molenda (jasonmolenda)
Changes When a frameless function faults or is interrupted asynchronously, the UnwindPlan MAY have no register location rule for the return address register (lr on arm64); the value is simply live in the lr register when it was interrupted, and the frame below this on the stack -- e.g. sigtramp on a Unix system -- has the full register context, including that register. RegisterContextUnwind::SavedLocationForRegister, when asked to find the caller's pc value, will first see if there is a pc register location. If there isn't, on a Return Address Register architecture like arm/mips/riscv, we rewrite the register request from "pc" to "RA register", and search for a location. On frame 0 (the live frame) and an interrupted frame, the UnwindPlan may have no register location rule for the RA Reg, that is valid. A frameless function that never calls another may simply keep the return address in the live register the whole way. Our instruction emulation unwind plans explicitly add a rule (see Pavel's May 2024 change https://github.com/llvm/llvm-project/pull/91321 ), but an UnwindPlan sourced from debug_frame may not. I've got a case where this exactly happens - clang debug_frame for arm64 where there is no register location for the lr in a frameless function. There is a fault in the middle of this frameless function and we only get the lr value from the fault handler below this frame if lr has a register location of `IsSame`, in line with Pavel's 2024 change. Similar to how we see a request of the RA Reg from frame 0 after failing to find an unwind location for the pc register, the same style of special casing is needed when this is a function that was interrupted. Without this change, we can find the pc of the frame that was executing when it was interrupted, but we need $lr to find its caller, and we don't descend down to the trap handler to get that value, truncating the stack. rdar://145614545 --- Full diff: https://github.com/llvm/llvm-project/pull/138805.diff 1 Files Affected: - (modified) lldb/source/Target/RegisterContextUnwind.cpp (+15-4) ``````````diff diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 3ed49e12476dd..23a86bee2518b 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -1377,6 +1377,7 @@ RegisterContextUnwind::SavedLocationForRegister( } } + // Check if the active_row has a register location listed. if (regnum.IsValid() && active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind), unwindplan_regloc)) { @@ -1390,11 +1391,10 @@ RegisterContextUnwind::SavedLocationForRegister( // This is frame 0 and we're retrieving the PC and it's saved in a Return // Address register and it hasn't been saved anywhere yet -- that is, // it's still live in the actual register. Handle this specially. - if (!have_unwindplan_regloc && return_address_reg.IsValid() && - IsFrameZero()) { - if (return_address_reg.GetAsKind(eRegisterKindLLDB) != - LLDB_INVALID_REGNUM) { + return_address_reg.GetAsKind(eRegisterKindLLDB) != + LLDB_INVALID_REGNUM) { + if (IsFrameZero()) { lldb_private::UnwindLLDB::ConcreteRegisterLocation new_regloc; new_regloc.type = UnwindLLDB::ConcreteRegisterLocation:: eRegisterInLiveRegisterContext; @@ -1408,6 +1408,17 @@ RegisterContextUnwind::SavedLocationForRegister( return_address_reg.GetAsKind(eRegisterKindLLDB), return_address_reg.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } else if (BehavesLikeZerothFrame()) { + // This function was interrupted asynchronously -- it faulted, + // an async interrupt, a timer fired, a debugger expression etc. + // The caller's pc is in the Return Address register, but the + // UnwindPlan for this function may have no location rule for + // the RA reg. + // This means that the caller's return address is in the RA reg + // when the function was interrupted--descend down one stack frame + // to retrieve it from the trap handler's saved context. + unwindplan_regloc.SetSame(); + have_unwindplan_regloc = true; } } ``````````
https://github.com/llvm/llvm-project/pull/138805 From lldb-commits at lists.llvm.org Tue May 6 22:51:42 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Tue, 06 May 2025 22:51:42 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681af4ee.630a0220.216826.85c9@mx.google.com> jasonmolenda wrote: While working on this bug, I started musing about how we could switch the logic for how to handle registers more into UnwindPlans, wrote a little discourse with the idea. https://discourse.llvm.org/t/unhappiness-with-the-lldb-unwinder-register-passing-up-the-stack-interrup-trap-sigtramp-frames/86058 It's not something I'll be doing in the near term, but I know I'd like to get back to this. https://github.com/llvm/llvm-project/pull/138805 From lldb-commits at lists.llvm.org Tue May 6 22:53:46 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Tue, 06 May 2025 22:53:46 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681af56a.170a0220.2ac16c.6c3c@mx.google.com> jasonmolenda wrote: Also while initially working on this issue I found I could fix it in two places in `SavedLocationForRegister` without considering the entirety of the method. This made me unhappy so I spent a bit of time going over all of `SavedLocationForRegister` until I felt confident I understood it, and then wrote the scariest NFC patch where I removed chunks of it (that are doing nothing) and restructured it so it was a lot easier to read, I thought. But I want to maybe bring that up separately because it's a lot of change and this method has a lot of edge cases - many tricky to test, and not tested well. https://github.com/llvm/llvm-project/pull/138805 From lldb-commits at lists.llvm.org Tue May 6 23:03:46 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Tue, 06 May 2025 23:03:46 -0700 (PDT) Subject: [Lldb-commits] [lldb] Extending LLDB to work on AIX (PR #102601) In-Reply-To: Message-ID: <681af7c2.170a0220.16da84.d83a@mx.google.com> https://github.com/ravindra-shinde2 updated https://github.com/llvm/llvm-project/pull/102601 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Tue May 6 23:39:39 2025 From: lldb-commits at lists.llvm.org (Hemang Gadhavi via lldb-commits) Date: Tue, 06 May 2025 23:39:39 -0700 (PDT) Subject: [Lldb-commits] [lldb] Extending LLDB to work on AIX (PR #102601) In-Reply-To: Message-ID: <681b002b.170a0220.32919e.8aae@mx.google.com> https://github.com/HemangGadhavi updated https://github.com/llvm/llvm-project/pull/102601 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Wed May 7 00:18:15 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 00:18:15 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][AIX] get host info for AIX (cont..) (PR #138687) In-Reply-To: Message-ID: <681b0937.050a0220.2126bd.398d@mx.google.com> https://github.com/labath edited https://github.com/llvm/llvm-project/pull/138687 From lldb-commits at lists.llvm.org Wed May 7 00:18:16 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 00:18:16 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][AIX] get host info for AIX (cont..) (PR #138687) In-Reply-To: Message-ID: <681b0938.170a0220.aef1e.6b74@mx.google.com> ================ @@ -133,7 +142,45 @@ static bool GetProcessAndStatInfo(::pid_t pid, uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) { - return 0; + static const char procdir[] = "/proc/"; + + DIR *dirproc = opendir(procdir); + if (dirproc) { + struct dirent *direntry = nullptr; + const uid_t our_uid = getuid(); + const lldb::pid_t our_pid = getpid(); + bool all_users = match_info.GetMatchAllUsers(); + + while ((direntry = readdir(dirproc)) != nullptr) { + if (!IsDirNumeric(direntry->d_name)) + continue; + + lldb::pid_t pid = atoi(direntry->d_name); + // Skip this process. + if (pid == our_pid) + continue; + + ProcessState State; + ProcessInstanceInfo process_info; + if (!GetProcessAndStatInfo(pid, process_info, State)) + continue; + + if (State == ProcessState::Zombie || + State == ProcessState::TracedOrStopped) + continue; + + // Check for user match if we're not matching all users and not running + // as root. + if (!all_users && (our_uid != 0) && (process_info.GetUserID() != our_uid)) + continue; + + if (match_info.Matches(process_info)) { + process_infos.push_back(process_info); + } ---------------- labath wrote: ```suggestion if (match_info.Matches(process_info)) process_infos.push_back(process_info); ``` https://github.com/llvm/llvm-project/pull/138687 From lldb-commits at lists.llvm.org Wed May 7 00:18:16 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 00:18:16 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][AIX] get host info for AIX (cont..) (PR #138687) In-Reply-To: Message-ID: <681b0938.170a0220.11e4f9.83e1@mx.google.com> https://github.com/labath commented: Could you also make unit tests for these two functions: - call GetProgramFileSpec and make sure the result is reasonable (exists?) - create a Process and make sure FindProcesses finds it (you can use [this](https://github.com/llvm/llvm-project/blob/52f568dbbb61ffe26b7973b482e0e504b405a0ab/lldb/unittests/Host/HostTest.cpp#L79) trick to re-execute yourself) https://github.com/llvm/llvm-project/pull/138687 From lldb-commits at lists.llvm.org Wed May 7 00:18:16 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 00:18:16 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][AIX] get host info for AIX (cont..) (PR #138687) In-Reply-To: Message-ID: <681b0938.170a0220.bcd9d.84f7@mx.google.com> ================ @@ -18,5 +20,18 @@ void HostInfoAIX::Terminate() { HostInfoBase::Terminate(); } FileSpec HostInfoAIX::GetProgramFileSpec() { static FileSpec g_program_filespec; + struct psinfo psinfoData; + auto BufferOrError = getProcFile(getpid(), "psinfo"); + if (BufferOrError) { + std::unique_ptr PsinfoBuffer = + std::move(*BufferOrError); + memcpy(&psinfoData, PsinfoBuffer->getBufferStart(), sizeof(psinfoData)); + llvm::StringRef exe_path( + psinfoData.pr_psargs, + strnlen(psinfoData.pr_psargs, sizeof(psinfoData.pr_psargs))); + if (!exe_path.empty()) { + g_program_filespec.SetFile(exe_path, FileSpec::Style::native); + } ---------------- labath wrote: ```suggestion if (!exe_path.empty()) g_program_filespec.SetFile(exe_path, FileSpec::Style::native); ``` https://github.com/llvm/llvm-project/pull/138687 From lldb-commits at lists.llvm.org Wed May 7 00:18:17 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 00:18:17 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][AIX] get host info for AIX (cont..) (PR #138687) In-Reply-To: Message-ID: <681b0939.170a0220.155d61.4825@mx.google.com> ================ @@ -133,7 +142,45 @@ static bool GetProcessAndStatInfo(::pid_t pid, uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) { - return 0; + static const char procdir[] = "/proc/"; + + DIR *dirproc = opendir(procdir); + if (dirproc) { + struct dirent *direntry = nullptr; + const uid_t our_uid = getuid(); + const lldb::pid_t our_pid = getpid(); + bool all_users = match_info.GetMatchAllUsers(); + + while ((direntry = readdir(dirproc)) != nullptr) { + if (!IsDirNumeric(direntry->d_name)) + continue; + + lldb::pid_t pid = atoi(direntry->d_name); ---------------- labath wrote: ```suggestion lldb::pid_t pid; if (!llvm::to_integer(direntry->d_name, pid)) continue; ``` .. and then delete IsDirNumeric https://github.com/llvm/llvm-project/pull/138687 From lldb-commits at lists.llvm.org Wed May 7 01:06:22 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Wed, 07 May 2025 01:06:22 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream Python scripts (PR #138028) In-Reply-To: Message-ID: <681b147e.170a0220.bdc3f.780e@mx.google.com> ================ @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +# Usage: convert-lldb-header-to-rpc-header.py +# This scripts takes common LLDB headers (such as lldb-defines.h) and replaces references to LLDB +# with those for RPC. This happens for: +# - namespace definitions +# - namespace usage +# - version string macros +# - ifdef/ifndef lines + +import argparse +import os +import re + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("input") + parser.add_argument("output") + args = parser.parse_args() + input_path = str(args.input) + output_path = str(args.output) + with open(input_path, "r") as input_file: + lines = input_file.readlines() + + with open(output_path, "w") as output_file: + for line in lines: + # NOTE: We do not use lldb-forward.h or lldb-versioning.h in RPC, so remove + # all includes that are found for these files. + if re.match( + r'#include "lldb/lldb-forward|#include "lldb/lldb-versioning', line + ): + continue + # For lldb-rpc-defines.h, replace the ifndef LLDB_LLDB_ portion with LLDB_RPC_ as we're not + # using LLDB private definitions in RPC. + elif re.match(r".+LLDB_LLDB_", line): + output_file.write(re.sub(r"LLDB_LLDB_", r"LLDB_RPC_", line)) + # Similarly to lldb-rpc-defines.h, replace the ifndef for LLDB_API in SBDefines.h to LLDB_RPC_API_ for the same reason. + elif re.match(r".+LLDB_API_", line): + output_file.write(re.sub(r"LLDB_API_", r"LLDB_RPC_API_", line)) ---------------- DavidSpickett wrote: Sure, makes sense. https://github.com/llvm/llvm-project/pull/138028 From lldb-commits at lists.llvm.org Wed May 7 01:14:13 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Wed, 07 May 2025 01:14:13 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Re-enable the lldb-dap tests (PR #138791) In-Reply-To: Message-ID: <681b1655.630a0220.235bba.8808@mx.google.com> https://github.com/DavidSpickett approved this pull request. Sure, let's give this a go now. (rather than end of week for a potential weekend of red) https://github.com/llvm/llvm-project/pull/138791 From lldb-commits at lists.llvm.org Wed May 7 01:28:49 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Wed, 07 May 2025 01:28:49 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Change the launch sequence (PR #138219) In-Reply-To: Message-ID: <681b19c1.170a0220.bcd9d.88a5@mx.google.com> DavidSpickett wrote: We have failing tests on Windows after this. Latest result at this time is: ``` ******************** Unresolved Tests (2): lldb-api :: tools/lldb-dap/completions/TestDAP_completions.py lldb-api :: tools/lldb-dap/startDebugging/TestDAP_startDebugging.py ******************** Timed Out Tests (1): lldb-api :: tools/lldb-dap/send-event/TestDAP_sendEvent.py ******************** Failed Tests (6): lldb-api :: tools/lldb-dap/console/TestDAP_console.py lldb-api :: tools/lldb-dap/console/TestDAP_redirection_to_console.py lldb-api :: tools/lldb-dap/launch/TestDAP_launch.py lldb-api :: tools/lldb-dap/stackTrace/TestDAP_stackTrace.py lldb-api :: tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py lldb-api :: tools/lldb-dap/variables/children/TestDAP_variables_children.py ``` Which includes some follow ups. https://github.com/llvm/llvm-project/pull/138219 From lldb-commits at lists.llvm.org Wed May 7 01:30:21 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Wed, 07 May 2025 01:30:21 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Re-enable the lldb-dap tests (PR #138791) In-Reply-To: Message-ID: <681b1a1d.050a0220.101898.f3cf@mx.google.com> https://github.com/DavidSpickett requested changes to this pull request. Actually the launch sequence changes are breaking Windows tests. I need to see that all green first. https://github.com/llvm/llvm-project/pull/138791 From lldb-commits at lists.llvm.org Wed May 7 01:54:11 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Wed, 07 May 2025 01:54:11 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681b1fb3.050a0220.2f2084.2fa1@mx.google.com> jasonmolenda wrote: to be clear, when I mention a big chunk of `SavedLocationForRegister` which I don't think is actually doing anything -- I refuse to look at the history, but it looks suspiciously like something I wrote a decade ago :) https://github.com/llvm/llvm-project/pull/138805 From lldb-commits at lists.llvm.org Wed May 7 01:59:28 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Wed, 07 May 2025 01:59:28 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681b20f0.170a0220.12d514.890e@mx.google.com> jasonmolenda wrote: I am also not thrilled with the GetAsKind method for register numbering returning LLDB_INVALID_REGNUM, the code reads so poorly in this method because of it. But returning an optional would make other code more verbose unless I carried optionals through a lot further. I haven't been annoyed enough to try doing that yet. https://github.com/llvm/llvm-project/pull/138805 From lldb-commits at lists.llvm.org Wed May 7 02:03:07 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 02:03:07 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681b21cb.170a0220.2d9b35.8472@mx.google.com> labath wrote: This makes sense to me, but is there any way to write a test case for it? It sounds like something similar to [this test](https://github.com/llvm/llvm-project/blob/main/lldb/test/Shell/Unwind/signal-in-leaf-function-aarch64.test) could work. In fact, given that this test is disabled on darwin, I'm wondering if this patch does not fix it by any chance? https://github.com/llvm/llvm-project/pull/138805 From lldb-commits at lists.llvm.org Wed May 7 02:05:08 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 02:05:08 -0700 (PDT) Subject: [Lldb-commits] [lldb] b643a52 - [lldb][debugserver] Add missing include to DNBTimer.h Message-ID: <681b2244.170a0220.28dfff.8e17@mx.google.com> Author: Pavel Labath Date: 2025-05-07T11:04:51+02:00 New Revision: b643a529dcd2b1b2e4e81c3be427edfcadc6d8fa URL: https://github.com/llvm/llvm-project/commit/b643a529dcd2b1b2e4e81c3be427edfcadc6d8fa DIFF: https://github.com/llvm/llvm-project/commit/b643a529dcd2b1b2e4e81c3be427edfcadc6d8fa.diff LOG: [lldb][debugserver] Add missing include to DNBTimer.h Added: Modified: lldb/tools/debugserver/source/DNBTimer.h Removed: ################################################################################ diff --git a/lldb/tools/debugserver/source/DNBTimer.h b/lldb/tools/debugserver/source/DNBTimer.h index ad15154245f35..cc409cfa7a57c 100644 --- a/lldb/tools/debugserver/source/DNBTimer.h +++ b/lldb/tools/debugserver/source/DNBTimer.h @@ -16,6 +16,7 @@ #include "DNBDefs.h" #include #include +#include #include class DNBTimer { From lldb-commits at lists.llvm.org Wed May 7 02:12:48 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Wed, 07 May 2025 02:12:48 -0700 (PDT) Subject: [Lldb-commits] [lldb] 47c7e73 - Revert "[lldb-dap] Change the launch sequence (#138219)" Message-ID: <681b2410.630a0220.13d4ab.9667@mx.google.com> Author: David Spickett Date: 2025-05-07T09:11:09Z New Revision: 47c7e73e5763f81f218cc4e1eae306d0427aa42d URL: https://github.com/llvm/llvm-project/commit/47c7e73e5763f81f218cc4e1eae306d0427aa42d DIFF: https://github.com/llvm/llvm-project/commit/47c7e73e5763f81f218cc4e1eae306d0427aa42d.diff LOG: Revert "[lldb-dap] Change the launch sequence (#138219)" This reverts commit ba29e60f9a2222bd5e883579bb78db13fc5a7588. As it broke tests on Windows on Arm: https://lab.llvm.org/buildbot/#/builders/141/builds/8500 ******************** Unresolved Tests (2): lldb-api :: tools/lldb-dap/completions/TestDAP_completions.py lldb-api :: tools/lldb-dap/startDebugging/TestDAP_startDebugging.py ******************** Timed Out Tests (1): lldb-api :: tools/lldb-dap/send-event/TestDAP_sendEvent.py ******************** Failed Tests (6): lldb-api :: tools/lldb-dap/console/TestDAP_console.py lldb-api :: tools/lldb-dap/console/TestDAP_redirection_to_console.py lldb-api :: tools/lldb-dap/launch/TestDAP_launch.py lldb-api :: tools/lldb-dap/stackTrace/TestDAP_stackTrace.py lldb-api :: tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py lldb-api :: tools/lldb-dap/variables/children/TestDAP_variables_children.py Added: Modified: lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py lldb/test/API/tools/lldb-dap/console/TestDAP_console.py lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py lldb/tools/lldb-dap/DAP.cpp lldb/tools/lldb-dap/DAP.h lldb/tools/lldb-dap/EventHelper.cpp lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp lldb/tools/lldb-dap/Handler/RequestHandler.cpp lldb/tools/lldb-dap/Handler/RequestHandler.h Removed: ################################################################################ diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py index e10342b72f4f0..6d9ab770684f1 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py @@ -132,6 +132,7 @@ def __init__(self, recv, send, init_commands, log_file=None): self.exit_status = None self.initialize_body = None self.thread_stop_reasons = {} + self.breakpoint_events = [] self.progress_events = [] self.reverse_requests = [] self.module_events = [] @@ -243,6 +244,13 @@ def handle_recv_packet(self, packet): self._process_stopped() tid = body["threadId"] self.thread_stop_reasons[tid] = body + elif event == "breakpoint": + # Breakpoint events come in when a breakpoint has locations + # added or removed. Keep track of them so we can look for them + # in tests. + self.breakpoint_events.append(packet) + # no need to add 'breakpoint' event packets to our packets list + return keepGoing elif event.startswith("progress"): # Progress events come in as 'progressStart', 'progressUpdate', # and 'progressEnd' events. Keep these around in case test @@ -404,15 +412,6 @@ def wait_for_stopped(self, timeout=None): self.threads = [] return stopped_events - def wait_for_breakpoint_events(self, timeout=None): - breakpoint_events = [] - while True: - event = self.wait_for_event("breakpoint", timeout=timeout) - if not event: - break - breakpoint_events.append(event) - return breakpoint_events - def wait_for_exited(self): event_dict = self.wait_for_event("exited") if event_dict is None: @@ -592,7 +591,6 @@ def request_attach( attachCommands=None, terminateCommands=None, coreFile=None, - stopOnAttach=True, postRunCommands=None, sourceMap=None, gdbRemotePort=None, @@ -622,8 +620,6 @@ def request_attach( args_dict["attachCommands"] = attachCommands if coreFile: args_dict["coreFile"] = coreFile - if stopOnAttach: - args_dict["stopOnEntry"] = stopOnAttach if postRunCommands: args_dict["postRunCommands"] = postRunCommands if sourceMap: @@ -636,7 +632,7 @@ def request_attach( response = self.send_recv(command_dict) if response["success"]: - self.wait_for_event("process") + self.wait_for_events(["process", "initialized"]) return response def request_breakpointLocations( @@ -670,6 +666,10 @@ def request_configurationDone(self): response = self.send_recv(command_dict) if response: self.configuration_done_sent = True + # Client requests the baseline of currently existing threads after + # a successful launch or attach. + # Kick off the threads request that follows + self.request_threads() return response def _process_stopped(self): @@ -887,7 +887,7 @@ def request_launch( response = self.send_recv(command_dict) if response["success"]: - self.wait_for_event("process") + self.wait_for_events(["process", "initialized"]) return response def request_next(self, threadId, granularity="statement"): @@ -1325,26 +1325,6 @@ def attach_options_specified(options): def run_vscode(dbg, args, options): dbg.request_initialize(options.sourceInitFile) - - if options.sourceBreakpoints: - source_to_lines = {} - for file_line in options.sourceBreakpoints: - (path, line) = file_line.split(":") - if len(path) == 0 or len(line) == 0: - print('error: invalid source with line "%s"' % (file_line)) - - else: - if path in source_to_lines: - source_to_lines[path].append(int(line)) - else: - source_to_lines[path] = [int(line)] - for source in source_to_lines: - dbg.request_setBreakpoints(source, source_to_lines[source]) - if options.funcBreakpoints: - dbg.request_setFunctionBreakpoints(options.funcBreakpoints) - - dbg.request_configurationDone() - if attach_options_specified(options): response = dbg.request_attach( program=options.program, @@ -1373,6 +1353,23 @@ def run_vscode(dbg, args, options): ) if response["success"]: + if options.sourceBreakpoints: + source_to_lines = {} + for file_line in options.sourceBreakpoints: + (path, line) = file_line.split(":") + if len(path) == 0 or len(line) == 0: + print('error: invalid source with line "%s"' % (file_line)) + + else: + if path in source_to_lines: + source_to_lines[path].append(int(line)) + else: + source_to_lines[path] = [int(line)] + for source in source_to_lines: + dbg.request_setBreakpoints(source, source_to_lines[source]) + if options.funcBreakpoints: + dbg.request_setFunctionBreakpoints(options.funcBreakpoints) + dbg.request_configurationDone() dbg.wait_for_stopped() else: if "message" in response: diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py index 958c7268c0c72..2c14bb35162b5 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py @@ -340,7 +340,6 @@ def attach( exitCommands=None, attachCommands=None, coreFile=None, - stopOnAttach=True, disconnectAutomatically=True, terminateCommands=None, postRunCommands=None, @@ -365,8 +364,6 @@ def cleanup(): self.addTearDownHook(cleanup) # Initialize and launch the program self.dap_server.request_initialize(sourceInitFile) - self.dap_server.wait_for_event("initialized") - self.dap_server.request_configurationDone() response = self.dap_server.request_attach( program=program, pid=pid, @@ -379,7 +376,6 @@ def cleanup(): attachCommands=attachCommands, terminateCommands=terminateCommands, coreFile=coreFile, - stopOnAttach=stopOnAttach, postRunCommands=postRunCommands, sourceMap=sourceMap, gdbRemotePort=gdbRemotePort, @@ -438,9 +434,6 @@ def cleanup(): # Initialize and launch the program self.dap_server.request_initialize(sourceInitFile) - self.dap_server.wait_for_event("initialized") - self.dap_server.request_configurationDone() - response = self.dap_server.request_launch( program, args=args, diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py index 741c011a3d692..f48d5a7db3c50 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py @@ -27,8 +27,6 @@ def spawn_and_wait(program, delay): @skip class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase): def set_and_hit_breakpoint(self, continueToExit=True): - self.dap_server.wait_for_stopped() - source = "main.c" breakpoint1_line = line_number(source, "// breakpoint 1") lines = [breakpoint1_line] diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py index 7250e67ebcd8c..7f93b9f2a3a22 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py @@ -18,17 +18,17 @@ import socket + at skip class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase): default_timeout = 20 def set_and_hit_breakpoint(self, continueToExit=True): - self.dap_server.wait_for_stopped() - source = "main.c" - breakpoint1_line = line_number(source, "// breakpoint 1") + main_source_path = os.path.join(os.getcwd(), source) + breakpoint1_line = line_number(main_source_path, "// breakpoint 1") lines = [breakpoint1_line] # Set breakpoint in the thread function so we can step the threads - breakpoint_ids = self.set_source_breakpoints(source, lines) + breakpoint_ids = self.set_source_breakpoints(main_source_path, lines) self.assertEqual( len(breakpoint_ids), len(lines), "expect correct number of breakpoints" ) diff --git a/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py b/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py index 8581f10cef22a..e5590e1b332a0 100644 --- a/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py +++ b/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py @@ -81,27 +81,52 @@ def test_breakpoint_events(self): breakpoint["verified"], "expect foo breakpoint to not be verified" ) - # Make sure we're stopped. - self.dap_server.wait_for_stopped() + # Get the stop at the entry point + self.continue_to_next_stop() - # Flush the breakpoint events. - self.dap_server.wait_for_breakpoint_events(timeout=5) + # We are now stopped at the entry point to the program. Shared + # libraries are not loaded yet (at least on macOS they aren't) and only + # the breakpoint in the main executable should be resolved. + self.assertEqual(len(self.dap_server.breakpoint_events), 1) + event = self.dap_server.breakpoint_events[0] + body = event["body"] + self.assertEqual( + body["reason"], "changed", "breakpoint event should say changed" + ) + breakpoint = body["breakpoint"] + self.assertEqual(breakpoint["id"], main_bp_id) + self.assertTrue(breakpoint["verified"], "main breakpoint should be resolved") + + # Clear the list of breakpoint events so we don't see this one again. + self.dap_server.breakpoint_events.clear() # Continue to the breakpoint self.continue_to_breakpoints(dap_breakpoint_ids) - verified_breakpoint_ids = [] - unverified_breakpoint_ids = [] - for breakpoint_event in self.dap_server.wait_for_breakpoint_events(timeout=5): - breakpoint = breakpoint_event["body"]["breakpoint"] - id = breakpoint["id"] - if breakpoint["verified"]: - verified_breakpoint_ids.append(id) - else: - unverified_breakpoint_ids.append(id) - - self.assertIn(main_bp_id, unverified_breakpoint_ids) - self.assertIn(foo_bp_id, unverified_breakpoint_ids) + # When the process launches, we first expect to see both the main and + # foo breakpoint as unresolved. + for event in self.dap_server.breakpoint_events[:2]: + body = event["body"] + self.assertEqual( + body["reason"], "changed", "breakpoint event should say changed" + ) + breakpoint = body["breakpoint"] + self.assertIn(str(breakpoint["id"]), dap_breakpoint_ids) + self.assertFalse(breakpoint["verified"], "breakpoint should be unresolved") - self.assertIn(main_bp_id, verified_breakpoint_ids) - self.assertIn(foo_bp_id, verified_breakpoint_ids) + # Then, once the dynamic loader has given us a load address, they + # should show up as resolved again. + for event in self.dap_server.breakpoint_events[3:]: + body = event["body"] + self.assertEqual( + body["reason"], "changed", "breakpoint event should say changed" + ) + breakpoint = body["breakpoint"] + self.assertIn(str(breakpoint["id"]), dap_breakpoint_ids) + self.assertTrue(breakpoint["verified"], "breakpoint should be resolved") + self.assertNotIn( + "source", + breakpoint, + "breakpoint event should not return a source object", + ) + self.assertIn("line", breakpoint, "breakpoint event should have line") diff --git a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py index 455ac84168baf..210e591bff426 100644 --- a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py +++ b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py @@ -44,9 +44,9 @@ def verify_completions(self, actual_list, expected_list, not_expected_list=[]): self.assertNotIn(not_expected_item, actual_list) - def setup_debugee(self, stopOnEntry=False): + def setup_debugee(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program, stopOnEntry=stopOnEntry) + self.build_and_launch(program) source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") @@ -235,7 +235,7 @@ def test_auto_completions(self): """ Tests completion requests in "repl-mode=auto" """ - self.setup_debugee(stopOnEntry=True) + self.setup_debugee() res = self.dap_server.request_evaluate( "`lldb-dap repl-mode auto", context="repl" diff --git a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py index 65a1bc04c7cd7..b07c4f871d73b 100644 --- a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py +++ b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py @@ -167,7 +167,7 @@ def test_exit_status_message_ok(self): def test_diagnositcs(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program, stopOnEntry=True) + self.build_and_launch(program) core = self.getBuildArtifact("minidump.core") self.yaml2obj("minidump.yaml", core) diff --git a/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py b/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py index 09e3f62f0eead..0cb792d662a80 100644 --- a/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py +++ b/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py @@ -31,7 +31,7 @@ def test_launch(self): created. """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program, stopOnEntry=True, disconnectAutomatically=False) + self.build_and_launch(program, disconnectAutomatically=False) # We set a breakpoint right before the side effect file is created self.set_source_breakpoints( @@ -39,11 +39,7 @@ def test_launch(self): ) self.continue_to_next_stop() - # verify we haven't produced the side effect file yet - self.assertFalse(os.path.exists(program + ".side_effect")) - self.dap_server.request_disconnect() - # verify we didn't produce the side effect file time.sleep(1) self.assertFalse(os.path.exists(program + ".side_effect")) diff --git a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py index 19b682dfcd22d..d97fda730c46a 100644 --- a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py +++ b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py @@ -10,7 +10,6 @@ from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * - # DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. @skip class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase): @@ -43,9 +42,7 @@ def run_test_evaluate_expressions( self.context = context program = self.getBuildArtifact("a.out") self.build_and_launch( - program, - enableAutoVariableSummaries=enableAutoVariableSummaries, - stopOnEntry=True, + program, enableAutoVariableSummaries=enableAutoVariableSummaries ) source = "main.cpp" self.set_source_breakpoints( diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py index 604a41678500c..931456299e03e 100644 --- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py +++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py @@ -88,8 +88,8 @@ def test_stopOnEntry(self): """ program = self.getBuildArtifact("a.out") self.build_and_launch(program, stopOnEntry=True) - - stopped_events = self.dap_server.wait_for_stopped() + self.set_function_breakpoints(["main"]) + stopped_events = self.continue_to_next_stop() for stopped_event in stopped_events: if "body" in stopped_event: body = stopped_event["body"] diff --git a/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py b/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py index 0f94b50c31fba..fee63655de0da 100755 --- a/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py +++ b/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py @@ -50,7 +50,7 @@ def verify_progress_events( @skipIfWindows def test(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program, stopOnEntry=True) + self.build_and_launch(program) progress_emitter = os.path.join(os.getcwd(), "Progress_emitter.py") self.dap_server.request_evaluate( f"`command script import {progress_emitter}", context="repl" diff --git a/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py b/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py index 81edcdf4bd0f9..c6f59949d668e 100644 --- a/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py +++ b/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py @@ -20,7 +20,7 @@ def assertEvaluate(self, expression, regex): def test_completions(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program, stopOnEntry=True) + self.build_and_launch(program) source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") diff --git a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py index 5f95c7bfb1556..36fa0bd40183f 100644 --- a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py +++ b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py @@ -22,6 +22,7 @@ def test_basic_functionality(self): [bp_A, bp_B] = self.set_source_breakpoints("main.c", [line_A, line_B]) # Verify we hit A, then B. + self.dap_server.request_configurationDone() self.verify_breakpoint_hit([bp_A]) self.dap_server.request_continue() self.verify_breakpoint_hit([bp_B]) diff --git a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py index eed769a5a0cc6..a94c9860c1508 100644 --- a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py +++ b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py @@ -74,6 +74,7 @@ def test_stopOnEntry(self): program = self.getBuildArtifact("a.out") self.build_and_launch(program, runInTerminal=True, stopOnEntry=True) [bp_main] = self.set_function_breakpoints(["main"]) + self.dap_server.request_configurationDone() # When using stopOnEntry, configurationDone doesn't result in a running # process, we should immediately get a stopped event instead. diff --git a/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py b/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py index 7e28a5af4331c..70c11a63a79f7 100644 --- a/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py +++ b/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py @@ -19,7 +19,7 @@ def test_stop_hooks_before_run(self): self.build_and_launch(program, stopOnEntry=True, preRunCommands=preRunCommands) # The first stop is on entry. - self.dap_server.wait_for_stopped() + self.continue_to_next_stop() breakpoint_ids = self.set_function_breakpoints(["main"]) # This request hangs if the race happens, because, in that case, the diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 62c60cc3a9b3b..4b631484c9fab 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -84,8 +84,8 @@ DAP::DAP(Log *log, const ReplMode default_repl_mode, : log(log), transport(transport), broadcaster("lldb-dap"), exception_breakpoints(), focus_tid(LLDB_INVALID_THREAD_ID), stop_at_entry(false), is_attach(false), - restarting_process_id(LLDB_INVALID_PROCESS_ID), configuration_done(false), - waiting_for_run_in_terminal(false), + restarting_process_id(LLDB_INVALID_PROCESS_ID), + configuration_done_sent(false), waiting_for_run_in_terminal(false), progress_event_reporter( [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }), reverse_request_seq(0), repl_mode(default_repl_mode) { @@ -893,19 +893,10 @@ llvm::Error DAP::Loop() { return errWrapper; } - // The launch sequence is special and we need to carefully handle - // packets in the right order. Until we've handled configurationDone, - bool add_to_pending_queue = false; - if (const protocol::Request *req = - std::get_if(&*next)) { - llvm::StringRef command = req->command; - if (command == "disconnect") - disconnecting = true; - if (!configuration_done) - add_to_pending_queue = - command != "initialize" && command != "configurationDone" && - command != "disconnect" && !command.ends_with("Breakpoints"); + std::get_if(&*next); + req && req->command == "disconnect") { + disconnecting = true; } const std::optional cancel_args = @@ -933,8 +924,7 @@ llvm::Error DAP::Loop() { { std::lock_guard guard(m_queue_mutex); - auto &queue = add_to_pending_queue ? m_pending_queue : m_queue; - queue.push_back(std::move(*next)); + m_queue.push_back(std::move(*next)); } m_queue_cv.notify_one(); } @@ -948,19 +938,16 @@ llvm::Error DAP::Loop() { StopEventHandlers(); }); - while (true) { + while (!disconnecting || !m_queue.empty()) { std::unique_lock lock(m_queue_mutex); m_queue_cv.wait(lock, [&] { return disconnecting || !m_queue.empty(); }); - if (disconnecting && m_queue.empty()) + if (m_queue.empty()) break; Message next = m_queue.front(); m_queue.pop_front(); - // Unlock while we're processing the event. - lock.unlock(); - if (!HandleObject(next)) return llvm::createStringError(llvm::inconvertibleErrorCode(), "unhandled packet"); @@ -1232,16 +1219,6 @@ void DAP::SetConfiguration(const protocol::Configuration &config, SetThreadFormat(*configuration.customThreadFormat); } -void DAP::SetConfigurationDone() { - { - std::lock_guard guard(m_queue_mutex); - std::copy(m_pending_queue.begin(), m_pending_queue.end(), - std::front_inserter(m_queue)); - configuration_done = true; - } - m_queue_cv.notify_all(); -} - void DAP::SetFrameFormat(llvm::StringRef format) { if (format.empty()) return; diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index b581ae759b1bc..88eedb0860cf1 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -188,7 +188,7 @@ struct DAP { // shutting down the entire adapter. When we're restarting, we keep the id of // the old process here so we can detect this case and keep running. lldb::pid_t restarting_process_id; - bool configuration_done; + bool configuration_done_sent; llvm::StringMap> request_handlers; bool waiting_for_run_in_terminal; ProgressEventReporter progress_event_reporter; @@ -251,8 +251,6 @@ struct DAP { /// Configures the debug adapter for launching/attaching. void SetConfiguration(const protocol::Configuration &confing, bool is_attach); - void SetConfigurationDone(); - /// Configure source maps based on the current `DAPConfiguration`. void ConfigureSourceMaps(); @@ -419,10 +417,8 @@ struct DAP { lldb::SBMutex GetAPIMutex() const { return target.GetAPIMutex(); } private: - /// Queue for all incoming messages. - std::deque m_queue; - std::deque m_pending_queue; std::mutex m_queue_mutex; + std::deque m_queue; std::condition_variable m_queue_cv; std::mutex m_cancelled_requests_mutex; diff --git a/lldb/tools/lldb-dap/EventHelper.cpp b/lldb/tools/lldb-dap/EventHelper.cpp index ed2d8700c26b0..2c659f39f4b66 100644 --- a/lldb/tools/lldb-dap/EventHelper.cpp +++ b/lldb/tools/lldb-dap/EventHelper.cpp @@ -222,7 +222,7 @@ void SendContinuedEvent(DAP &dap) { // If the focus thread is not set then we haven't reported any thread status // to the client, so nothing to report. - if (!dap.configuration_done || dap.focus_tid == LLDB_INVALID_THREAD_ID) { + if (!dap.configuration_done_sent || dap.focus_tid == LLDB_INVALID_THREAD_ID) { return; } diff --git a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp index 5dc9c3f9772e3..7a0f091128e4a 100644 --- a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp @@ -133,69 +133,60 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { dap.SendOutput(OutputType::Console, llvm::StringRef(attach_msg, attach_msg_len)); } + if (attachCommands.empty()) { + // No "attachCommands", just attach normally. - { - // Perform the launch in synchronous mode so that we don't have to worry - // about process state changes during the launch. + // Disable async events so the attach will be successful when we return from + // the launch call and the launch will happen synchronously ScopeSyncMode scope_sync_mode(dap.debugger); - if (attachCommands.empty()) { - // No "attachCommands", just attach normally. - if (core_file.empty()) { - if ((pid != LLDB_INVALID_PROCESS_ID) && - (gdb_remote_port != invalid_port)) { - // If both pid and port numbers are specified. - error.SetErrorString("The user can't specify both pid and port"); - } else if (gdb_remote_port != invalid_port) { - // If port is specified and pid is not. - lldb::SBListener listener = dap.debugger.GetListener(); - - // If the user hasn't provided the hostname property, default - // localhost being used. - std::string connect_url = - llvm::formatv("connect://{0}:", gdb_remote_hostname); - connect_url += std::to_string(gdb_remote_port); - dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", - error); - } else { - // Attach by pid or process name. - lldb::SBAttachInfo attach_info; - if (pid != LLDB_INVALID_PROCESS_ID) - attach_info.SetProcessID(pid); - else if (dap.configuration.program.has_value()) - attach_info.SetExecutable(dap.configuration.program->data()); - attach_info.SetWaitForLaunch(wait_for, false /*async*/); - dap.target.Attach(attach_info, error); - } + + if (core_file.empty()) { + if ((pid != LLDB_INVALID_PROCESS_ID) && + (gdb_remote_port != invalid_port)) { + // If both pid and port numbers are specified. + error.SetErrorString("The user can't specify both pid and port"); + } else if (gdb_remote_port != invalid_port) { + // If port is specified and pid is not. + lldb::SBListener listener = dap.debugger.GetListener(); + + // If the user hasn't provided the hostname property, default localhost + // being used. + std::string connect_url = + llvm::formatv("connect://{0}:", gdb_remote_hostname); + connect_url += std::to_string(gdb_remote_port); + dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", + error); } else { - dap.target.LoadCore(core_file.data(), error); + // Attach by pid or process name. + lldb::SBAttachInfo attach_info; + if (pid != LLDB_INVALID_PROCESS_ID) + attach_info.SetProcessID(pid); + else if (dap.configuration.program.has_value()) + attach_info.SetExecutable(dap.configuration.program->data()); + attach_info.SetWaitForLaunch(wait_for, false /*async*/); + dap.target.Attach(attach_info, error); } } else { - // We have "attachCommands" that are a set of commands that are expected - // to execute the commands after which a process should be created. If - // there is no valid process after running these commands, we have failed. - if (llvm::Error err = dap.RunAttachCommands(attachCommands)) { - response["success"] = false; - EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; - } - // The custom commands might have created a new target so we should use - // the selected target after these commands are run. - dap.target = dap.debugger.GetSelectedTarget(); + dap.target.LoadCore(core_file.data(), error); } - } - - // Make sure the process is attached and stopped. - error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds)); + } else { + // We have "attachCommands" that are a set of commands that are expected + // to execute the commands after which a process should be created. If there + // is no valid process after running these commands, we have failed. + if (llvm::Error err = dap.RunAttachCommands(attachCommands)) { + response["success"] = false; + EmplaceSafeString(response, "message", llvm::toString(std::move(err))); + dap.SendJSON(llvm::json::Value(std::move(response))); + return; + } + // The custom commands might have created a new target so we should use the + // selected target after these commands are run. + dap.target = dap.debugger.GetSelectedTarget(); - // Clients can request a baseline of currently existing threads after - // we acknowledge the configurationDone request. - // Client requests the baseline of currently existing threads after - // a successful or attach by sending a 'threads' request - // right after receiving the configurationDone response. - // Obtain the list of threads before we resume the process - dap.initial_thread_list = - GetThreads(dap.target.GetProcess(), dap.thread_format); + // Make sure the process is attached and stopped before proceeding as the + // the launch commands are not run using the synchronous mode. + error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds)); + } if (error.Success() && core_file.empty()) { auto attached_pid = dap.target.GetProcess().GetProcessID(); @@ -215,17 +206,9 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { } dap.SendJSON(llvm::json::Value(std::move(response))); - - // FIXME: Move this into PostRun. if (error.Success()) { - if (dap.target.GetProcess().IsValid()) { - SendProcessEvent(dap, Attach); - - if (dap.stop_at_entry) - SendThreadStoppedEvent(dap); - else - dap.target.GetProcess().Continue(); - } + SendProcessEvent(dap, Attach); + dap.SendJSON(CreateEventObject("initialized")); } } diff --git a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp index 802c28d7b8904..f39bbdefdbb95 100644 --- a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp @@ -47,11 +47,21 @@ namespace lldb_dap { void ConfigurationDoneRequestHandler::operator()( const llvm::json::Object &request) const { - dap.SetConfigurationDone(); - llvm::json::Object response; FillResponse(request, response); dap.SendJSON(llvm::json::Value(std::move(response))); + dap.configuration_done_sent = true; + if (dap.stop_at_entry) + SendThreadStoppedEvent(dap); + else { + // Client requests the baseline of currently existing threads after + // a successful launch or attach by sending a 'threads' request + // right after receiving the configurationDone response. + // Obtain the list of threads before we resume the process + dap.initial_thread_list = + GetThreads(dap.target.GetProcess(), dap.thread_format); + dap.target.GetProcess().Continue(); + } } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp index aa947d3cb5ab9..ce34c52bcc334 100644 --- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp @@ -140,28 +140,43 @@ static void EventThreadFunction(DAP &dap) { lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event); if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) { auto state = lldb::SBProcess::GetStateFromEvent(event); - - DAP_LOG(dap.log, "State = {0}", state); switch (state) { - case lldb::eStateConnected: - case lldb::eStateDetached: case lldb::eStateInvalid: + // Not a state event + break; case lldb::eStateUnloaded: break; + case lldb::eStateConnected: + break; case lldb::eStateAttaching: - case lldb::eStateCrashed: + break; case lldb::eStateLaunching: - case lldb::eStateStopped: + break; + case lldb::eStateStepping: + break; + case lldb::eStateCrashed: + break; + case lldb::eStateDetached: + break; case lldb::eStateSuspended: - // Only report a stopped event if the process was not - // automatically restarted. - if (!lldb::SBProcess::GetRestartedFromEvent(event)) { - SendStdOutStdErr(dap, process); - SendThreadStoppedEvent(dap); + break; + case lldb::eStateStopped: + // We launch and attach in synchronous mode then the first stop + // event will not be delivered. If we use "launchCommands" during a + // launch or "attachCommands" during an attach we might some process + // stop events which we do not want to send an event for. We will + // manually send a stopped event in request_configurationDone(...) + // so don't send any before then. + if (dap.configuration_done_sent) { + // Only report a stopped event if the process was not + // automatically restarted. + if (!lldb::SBProcess::GetRestartedFromEvent(event)) { + SendStdOutStdErr(dap, process); + SendThreadStoppedEvent(dap); + } } break; case lldb::eStateRunning: - case lldb::eStateStepping: dap.WillContinue(); SendContinuedEvent(dap); break; @@ -269,7 +284,6 @@ llvm::Expected InitializeRequestHandler::Run( // Do not source init files until in/out/err are configured. dap.debugger = lldb::SBDebugger::Create(false); dap.debugger.SetInputFile(dap.in); - dap.target = dap.debugger.GetDummyTarget(); llvm::Expected out_fd = dap.out.GetWriteFileDescriptor(); if (!out_fd) @@ -324,8 +338,4 @@ llvm::Expected InitializeRequestHandler::Run( return dap.GetCapabilities(); } -void InitializeRequestHandler::PostRun() const { - dap.SendJSON(CreateEventObject("initialized")); -} - } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp index 7e0e76935dd02..3e4532e754ec6 100644 --- a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp @@ -71,12 +71,9 @@ void LaunchRequestHandler::PostRun() const { if (dap.target.GetProcess().IsValid()) { // Attach happens when launching with runInTerminal. SendProcessEvent(dap, dap.is_attach ? Attach : Launch); - - if (dap.stop_at_entry) - SendThreadStoppedEvent(dap); - else - dap.target.GetProcess().Continue(); } + + dap.SendJSON(CreateEventObject("initialized")); } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp index 282c5f4ab15a5..7a75cd93abc19 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp @@ -8,7 +8,6 @@ #include "Handler/RequestHandler.h" #include "DAP.h" -#include "EventHelper.h" #include "Handler/ResponseHandler.h" #include "JSONUtils.h" #include "LLDBUtils.h" @@ -163,7 +162,7 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) { dap.target.GetProcess().Continue(); // Now that the actual target is just starting (i.e. exec was just invoked), - // we return the debugger to its sync state. + // we return the debugger to its async state. scope_sync_mode.reset(); // If sending the notification failed, the launcher should be dead by now and @@ -239,47 +238,35 @@ llvm::Error BaseRequestHandler::LaunchProcess( launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug | lldb::eLaunchFlagStopAtEntry); - { - // Perform the launch in synchronous mode so that we don't have to worry - // about process state changes during the launch. + if (arguments.runInTerminal) { + if (llvm::Error err = RunInTerminal(dap, arguments)) + return err; + } else if (launchCommands.empty()) { + lldb::SBError error; + // Disable async events so the launch will be successful when we return from + // the launch call and the launch will happen synchronously ScopeSyncMode scope_sync_mode(dap.debugger); - - if (arguments.runInTerminal) { - if (llvm::Error err = RunInTerminal(dap, arguments)) - return err; - } else if (launchCommands.empty()) { - lldb::SBError error; - dap.target.Launch(launch_info, error); - if (error.Fail()) - return llvm::make_error(error.GetCString()); - } else { - // Set the launch info so that run commands can access the configured - // launch details. - dap.target.SetLaunchInfo(launch_info); - if (llvm::Error err = dap.RunLaunchCommands(launchCommands)) - return err; - - // The custom commands might have created a new target so we should use - // the selected target after these commands are run. - dap.target = dap.debugger.GetSelectedTarget(); - } + dap.target.Launch(launch_info, error); + if (error.Fail()) + return llvm::make_error(error.GetCString()); + } else { + // Set the launch info so that run commands can access the configured + // launch details. + dap.target.SetLaunchInfo(launch_info); + if (llvm::Error err = dap.RunLaunchCommands(launchCommands)) + return err; + + // The custom commands might have created a new target so we should use the + // selected target after these commands are run. + dap.target = dap.debugger.GetSelectedTarget(); + // Make sure the process is launched and stopped at the entry point before + // proceeding as the launch commands are not run using the synchronous + // mode. + lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout); + if (error.Fail()) + return llvm::make_error(error.GetCString()); } - // Make sure the process is launched and stopped at the entry point before - // proceeding. - lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout); - if (error.Fail()) - return llvm::make_error(error.GetCString()); - - // Clients can request a baseline of currently existing threads after - // we acknowledge the configurationDone request. - // Client requests the baseline of currently existing threads after - // a successful or attach by sending a 'threads' request - // right after receiving the configurationDone response. - // Obtain the list of threads before we resume the process - dap.initial_thread_list = - GetThreads(dap.target.GetProcess(), dap.thread_format); - return llvm::Error::success(); } diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index 9e9cfb13d77b8..37cc902e1c98e 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.h +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h @@ -282,7 +282,6 @@ class InitializeRequestHandler static llvm::StringLiteral GetCommand() { return "initialize"; } llvm::Expected Run(const protocol::InitializeRequestArguments &args) const override; - void PostRun() const override; }; class LaunchRequestHandler From lldb-commits at lists.llvm.org Wed May 7 02:16:54 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 02:16:54 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fix dynamic type resolutions for core files (PR #138698) In-Reply-To: Message-ID: <681b2506.630a0220.3cca28.8b20@mx.google.com> https://github.com/labath updated https://github.com/llvm/llvm-project/pull/138698 >From 663db321027e05736344fe763a5473175f0c90f2 Mon Sep 17 00:00:00 2001 From: Pavel Labath Date: Tue, 6 May 2025 16:35:01 +0200 Subject: [PATCH 1/2] [lldb] Fix dynamic type resolutions for core files We're reading from the object's vtable to determine the pointer to the full object. The vtable is normally in the "rodata" section of the executable, which is often not included in the core file because it's not supposed to change and the debugger can extrapolate its contents from the executable file. We weren't doing that. This patch changes the read operation to use the target class (which falls back onto the executable module as expected) and adds the missing ReadSignedIntegerFromMemory API. The fix is tested by creating a core (minidump) file which deliberately omits the vtable pointer. --- lldb/include/lldb/Target/Target.h | 5 ++ .../ItaniumABI/ItaniumABILanguageRuntime.cpp | 2 +- lldb/source/Target/Target.cpp | 11 ++++ .../cpp/dynamic-value/TestDynamicValue.py | 50 +++++++++++++++++++ 4 files changed, 67 insertions(+), 1 deletion(-) diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h index 73f27dc934b46..0d4e11b65339e 100644 --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -1158,6 +1158,11 @@ class Target : public std::enable_shared_from_this, Status &error, bool force_live_memory = false); + int64_t ReadSignedIntegerFromMemory(const Address &addr, + size_t integer_byte_size, + int64_t fail_value, Status &error, + bool force_live_memory = false); + uint64_t ReadUnsignedIntegerFromMemory(const Address &addr, size_t integer_byte_size, uint64_t fail_value, Status &error, diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp index 8faf7135217ac..0d068ed5950d5 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp @@ -350,7 +350,7 @@ bool ItaniumABILanguageRuntime::GetDynamicTypeAndAddress( if (offset_to_top_location >= vtable_load_addr) return false; Status error; - const int64_t offset_to_top = m_process->ReadSignedIntegerFromMemory( + const int64_t offset_to_top = target.ReadSignedIntegerFromMemory( offset_to_top_location, addr_byte_size, INT64_MIN, error); if (offset_to_top == INT64_MIN) diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index e90e748191a7e..7f61f8689fb95 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -2270,6 +2270,17 @@ size_t Target::ReadScalarIntegerFromMemory(const Address &addr, uint32_t byte_si return 0; } +int64_t Target::ReadSignedIntegerFromMemory(const Address &addr, + size_t integer_byte_size, + int64_t fail_value, Status &error, + bool force_live_memory) { + Scalar scalar; + if (ReadScalarIntegerFromMemory(addr, integer_byte_size, false, scalar, error, + force_live_memory)) + return scalar.SLongLong(fail_value); + return fail_value; +} + uint64_t Target::ReadUnsignedIntegerFromMemory(const Address &addr, size_t integer_byte_size, uint64_t fail_value, Status &error, diff --git a/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py b/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py index 634bd13d7c71a..b15b5f1dde377 100644 --- a/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py +++ b/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py @@ -279,3 +279,53 @@ def test_from_forward_decl(self): "frame var -d run-target --ptr-depth=1 --show-types a", substrs=["(B *) a", "m_b_value = 10"], ) + + @no_debug_info_test + @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24663") + def test_from_core_file(self): + """Test fetching C++ dynamic values from core files. Specifically, test + that we can determine the dynamic type of the value if the core file + does not contain the type vtable.""" + self.build() + lldbutil.run_to_name_breakpoint(self, "take_A") + + # Get the address of our object and its vtable + a = self.frame().FindVariable("a") + self.assertSuccess(a.GetError()) + vtable = a.GetVTable() + self.assertSuccess(vtable.GetError()) + a = a.GetValueAsAddress() + vtable = vtable.GetValueAsAddress() + + # Create a core file which will only contain the memory region + # containing `a`. The object is on the stack, so this will automatically + # include the stack of the main thread. + core = self.getBuildArtifact("a.dmp") + options = lldb.SBSaveCoreOptions() + options.SetPluginName("minidump") + options.SetStyle(lldb.eSaveCoreCustomOnly) + options.SetOutputFile(lldb.SBFileSpec(core)) + region = lldb.SBMemoryRegionInfo() + self.assertSuccess(self.process().GetMemoryRegionInfo(a, region)) + self.assertSuccess(options.AddMemoryRegionToSave(region)) + + # Save the core file and load it. + self.assertSuccess(self.process().SaveCore(options)) + self.process().Kill() + error = lldb.SBError() + self.target().LoadCore(core, error) + self.assertSuccess(error) + + # Sanity check -- the process should be able to read the object but not + # its vtable.. + self.process().ReadPointerFromMemory(a, error) + self.assertSuccess(error) + self.process().ReadPointerFromMemory(vtable, error) + self.assertTrue(error.Fail()) + + # .. but we should still be able to see the dynamic type by reading the + # vtable from the executable file. + self.expect( + "frame var -d run-target --ptr-depth=1 --show-types a", + substrs=["(B *) a", "m_b_value = 10"], + ) >From d3ea31a3e77a133784e6ec38f0479b3284076964 Mon Sep 17 00:00:00 2001 From: Pavel Labath Date: Wed, 7 May 2025 11:16:36 +0200 Subject: [PATCH 2/2] xfail on darwin --- lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py b/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py index b15b5f1dde377..2b66b8e8a5357 100644 --- a/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py +++ b/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py @@ -282,6 +282,7 @@ def test_from_forward_decl(self): @no_debug_info_test @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24663") + @expectedFailureDarwin # dynamic loader unloads modules def test_from_core_file(self): """Test fetching C++ dynamic values from core files. Specifically, test that we can determine the dynamic type of the value if the core file From lldb-commits at lists.llvm.org Wed May 7 02:18:06 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 02:18:06 -0700 (PDT) Subject: [Lldb-commits] [lldb] 18c5ad5 - [lldb] Fix block address resolution for functions in multiple sections (#137955) Message-ID: <681b254e.050a0220.2f6125.13bd@mx.google.com> Author: Pavel Labath Date: 2025-05-07T11:18:03+02:00 New Revision: 18c5ad5c6c178365d270439742863e14c8981ea3 URL: https://github.com/llvm/llvm-project/commit/18c5ad5c6c178365d270439742863e14c8981ea3 DIFF: https://github.com/llvm/llvm-project/commit/18c5ad5c6c178365d270439742863e14c8981ea3.diff LOG: [lldb] Fix block address resolution for functions in multiple sections (#137955) Continuing the theme from #116777 and #124931, this patch ensures we compute the correct address when a functions is spread across multiple sections. Due to this, it's not sufficient to adjust the offset in the section+offset pair (Address::Slide). We must actually slide the file offset and then recompute the section using the result. I found this out due to a failure to disassemble some parts of the function, so I'm testing with that, although it's likely there are other things that were broken due to this. Added: lldb/test/Shell/Commands/command-disassemble-sections.s Modified: lldb/include/lldb/Symbol/Block.h lldb/source/Symbol/Block.cpp Removed: ################################################################################ diff --git a/lldb/include/lldb/Symbol/Block.h b/lldb/include/lldb/Symbol/Block.h index 501c912b674ac..601895631e148 100644 --- a/lldb/include/lldb/Symbol/Block.h +++ b/lldb/include/lldb/Symbol/Block.h @@ -354,7 +354,12 @@ class Block : public UserID, public SymbolContextScope { // Member variables. SymbolContextScope &m_parent_scope; collection m_children; + + /// Address ranges of this block. They are relative to the function entry + /// point so one must add/subtract GetFunction().GetAddress().GetFileAddress() + /// when converting from/to to the AddressRange representation. RangeList m_ranges; + lldb::InlineFunctionInfoSP m_inlineInfoSP; ///< Inlined function information. lldb::VariableListSP m_variable_list_sp; ///< The variable list for all local, ///static and parameter variables diff --git a/lldb/source/Symbol/Block.cpp b/lldb/source/Symbol/Block.cpp index 9d01293ea64e0..3de3e5eecbf35 100644 --- a/lldb/source/Symbol/Block.cpp +++ b/lldb/source/Symbol/Block.cpp @@ -283,39 +283,43 @@ uint32_t Block::GetRangeIndexContainingAddress(const Address &addr) { return m_ranges.FindEntryIndexThatContains(file_addr - func_file_addr); } +static AddressRange ToAddressRange(const Address &func_addr, + const Block::Range &block_range) { + assert(func_addr.GetModule()); + return AddressRange(func_addr.GetFileAddress() + block_range.base, + block_range.size, + func_addr.GetModule()->GetSectionList()); +} + bool Block::GetRangeAtIndex(uint32_t range_idx, AddressRange &range) { if (range_idx >= m_ranges.GetSize()) return false; - Function &function = GetFunction(); - const Range &vm_range = m_ranges.GetEntryRef(range_idx); - range.GetBaseAddress() = function.GetAddress(); - range.GetBaseAddress().Slide(vm_range.GetRangeBase()); - range.SetByteSize(vm_range.GetByteSize()); + Address addr = GetFunction().GetAddress(); + if (!addr.GetModule()) + return false; + + range = ToAddressRange(addr, m_ranges.GetEntryRef(range_idx)); return true; } AddressRanges Block::GetRanges() { + Address addr = GetFunction().GetAddress(); + if (!addr.GetModule()) + return {}; + AddressRanges ranges; - Function &function = GetFunction(); - for (size_t i = 0, e = m_ranges.GetSize(); i < e; ++i) { - ranges.emplace_back(); - auto &range = ranges.back(); - const Range &vm_range = m_ranges.GetEntryRef(i); - range.GetBaseAddress() = function.GetAddress(); - range.GetBaseAddress().Slide(vm_range.GetRangeBase()); - range.SetByteSize(vm_range.GetByteSize()); - } + for (size_t i = 0, e = m_ranges.GetSize(); i < e; ++i) + ranges.push_back(ToAddressRange(addr, m_ranges.GetEntryRef(i))); return ranges; } bool Block::GetStartAddress(Address &addr) { - if (m_ranges.IsEmpty()) + Address func_addr = GetFunction().GetAddress(); + if (!func_addr.GetModule() || m_ranges.IsEmpty()) return false; - Function &function = GetFunction(); - addr = function.GetAddress(); - addr.Slide(m_ranges.GetEntryRef(0).GetRangeBase()); + addr = ToAddressRange(func_addr, m_ranges.GetEntryRef(0)).GetBaseAddress(); return true; } diff --git a/lldb/test/Shell/Commands/command-disassemble-sections.s b/lldb/test/Shell/Commands/command-disassemble-sections.s new file mode 100644 index 0000000000000..d278527b898ce --- /dev/null +++ b/lldb/test/Shell/Commands/command-disassemble-sections.s @@ -0,0 +1,110 @@ +## Test disassembling of functions which are spread over multiple sections (ELF +## segments are modelled as LLDB sections). + + +# REQUIRES: x86, lld + +# RUN: split-file %s %t +# RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux %t/file.s -o %t/file.o +# RUN: ld.lld %t/file.o -o %t/file.out -T %t/file.lds +# RUN: %lldb %t/file.out -o "disassemble --name func1" -o exit | FileCheck %s + +# CHECK: (lldb) disassemble --name func1 +# CHECK: file.out`func1: +# CHECK-NEXT: file.out[0x0] <+0>: int $0x2a +# CHECK: file.out`func1: +# CHECK-NEXT: file.out[0x1000] <+4096>: int $0x2f + + +#--- file.lds +## Linker script placing the parts of the section into diff erent segments +## (typically one of these would be for the "hot" code). +PHDRS { + text1 PT_LOAD; + text2 PT_LOAD; +} +SECTIONS { + . = 0; + .text.part1 : { *(.text.part1) } :text1 + .text.part2 : { *(.text.part2) } :text2 +} + +#--- file.s +## A very simple function consisting of two parts and DWARF describing the +## function. + .section .text.part1,"ax", at progbits + .p2align 12 +func1: + int $42 +.Lfunc1_end: + + .section .text.part2,"ax", at progbits + .p2align 12 +func1.__part.1: + int $47 +.Lfunc1.__part.1_end: + + + + .section .debug_abbrev,"", at progbits + .byte 1 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 1 # DW_CHILDREN_yes + .byte 37 # DW_AT_producer + .byte 8 # DW_FORM_string + .byte 19 # DW_AT_language + .byte 5 # DW_FORM_data2 + .byte 17 # DW_AT_low_pc + .byte 1 # DW_FORM_addr + .byte 85 # DW_AT_ranges + .byte 23 # DW_FORM_sec_offset + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 2 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 0 # DW_CHILDREN_no + .byte 85 # DW_AT_ranges + .byte 23 # DW_FORM_sec_offset + .byte 3 # DW_AT_name + .byte 8 # DW_FORM_string + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + + .section .debug_info,"", at progbits +.Lcu_begin0: + .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit +.Ldebug_info_start0: + .short 5 # DWARF version number + .byte 1 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long .debug_abbrev # Offset Into Abbrev. Section + .byte 1 # Abbrev DW_TAG_compile_unit + .asciz "Hand-written DWARF" # DW_AT_producer + .short 29 # DW_AT_language + .quad 0 # DW_AT_low_pc + .long .Ldebug_ranges0 # DW_AT_ranges + .byte 2 # Abbrev DW_TAG_subprogram + .long .Ldebug_ranges0 # DW_AT_ranges + .asciz "func1" # DW_AT_name + .byte 0 # End Of Children Mark +.Ldebug_info_end0: + + .section .debug_rnglists,"", at progbits + .long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length +.Ldebug_list_header_start0: + .short 5 # Version + .byte 8 # Address size + .byte 0 # Segment selector size + .long 1 # Offset entry count +.Lrnglists_table_base0: + .long .Ldebug_ranges0-.Lrnglists_table_base0 +.Ldebug_ranges0: + .byte 6 # DW_RLE_start_end + .quad func1 + .quad .Lfunc1_end + .byte 6 # DW_RLE_start_end + .quad func1.__part.1 + .quad .Lfunc1.__part.1_end + .byte 0 # DW_RLE_end_of_list +.Ldebug_list_header_end0: From lldb-commits at lists.llvm.org Wed May 7 02:18:10 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 02:18:10 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fix block address resolution for functions in multiple sections (PR #137955) In-Reply-To: Message-ID: <681b2552.630a0220.30586.9fd4@mx.google.com> https://github.com/labath closed https://github.com/llvm/llvm-project/pull/137955 From lldb-commits at lists.llvm.org Wed May 7 02:19:21 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 02:19:21 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fix dynamic type resolutions for core files (PR #138698) In-Reply-To: Message-ID: <681b2599.170a0220.35dbc3.89bc@mx.google.com> github-actions[bot] wrote: :warning: Python code formatter, darker found issues in your code. :warning:
You can test this locally with the following command: ``````````bash darker --check --diff -r HEAD~1...HEAD lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py ``````````
View the diff from darker here. ``````````diff --- TestDynamicValue.py 2025-05-07 09:16:36.000000 +0000 +++ TestDynamicValue.py 2025-05-07 09:18:51.436953 +0000 @@ -280,11 +280,11 @@ substrs=["(B *) a", "m_b_value = 10"], ) @no_debug_info_test @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24663") - @expectedFailureDarwin # dynamic loader unloads modules + @expectedFailureDarwin # dynamic loader unloads modules def test_from_core_file(self): """Test fetching C++ dynamic values from core files. Specifically, test that we can determine the dynamic type of the value if the core file does not contain the type vtable.""" self.build() ``````````
https://github.com/llvm/llvm-project/pull/138698 From lldb-commits at lists.llvm.org Wed May 7 02:20:02 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 02:20:02 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Inherit DuplicateFileAction(HANDLE, HANDLE) handles on windows (PR #137978) In-Reply-To: Message-ID: <681b25c2.170a0220.2de5a7.7bc3@mx.google.com> labath wrote: Thanks. > Probalby we can remove `#ifndef _WIN32` in lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp, line 928 too around `launch_info.AppendDuplicateFileAction((int)pass_comm_fd, (int)pass_comm_fd);`. Indeed we should. In fact, that function is the reason I'm doing all this. :P https://github.com/llvm/llvm-project/pull/137978 From lldb-commits at lists.llvm.org Wed May 7 02:20:26 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Wed, 07 May 2025 02:20:26 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Change the launch sequence (PR #138219) In-Reply-To: Message-ID: <681b25da.050a0220.24fefa.fd07@mx.google.com> DavidSpickett wrote: I've reverted this. Please take a look at the logs of https://lab.llvm.org/buildbot/#/builders/141/builds/8500 and see if any of it makes sense to you. We didn't have failures on Linux (though a lot of tests are disabled, they would be disabled everywhere) so my first instinct would be any construct that might be different on Windows. https://github.com/llvm/llvm-project/pull/138219 From lldb-commits at lists.llvm.org Wed May 7 02:21:48 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 02:21:48 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Inherit DuplicateFileAction(HANDLE, HANDLE) handles on windows (PR #137978) In-Reply-To: Message-ID: <681b262c.650a0220.79609.e084@mx.google.com> https://github.com/labath updated https://github.com/llvm/llvm-project/pull/137978 >From 4eb94714ec3c8249fbccb10c2cfd29f8c45b9a07 Mon Sep 17 00:00:00 2001 From: Pavel Labath Date: Thu, 9 Jan 2025 15:32:11 +0100 Subject: [PATCH 1/2] [lldb] Inherit DuplicateFileAction(HANDLE, HANDLE) handles on windows This is a follow-up to #126935, which enables passing handles to a child process on windows systems. Unlike on unix-like systems, the handles need to be created with the "inheritable" flag because there's to way to change the flag value after it has been created. This is why I don't respect the child_process_inherit flag but rather always set the flag to true. (My next step is to delete the flag entirely.) This does mean that pipe may be created as inheritable even if its not necessary, but I think this is offset by the fact that windows (unlike unixes, which pass all ~O_CLOEXEC descriptors through execve and *all* descriptors through fork) has a way to specify the precise set of handles to pass to a specific child process. If this turns out to be insufficient, instead of a constructor flag, I'd rather go with creating a separate api to create an inheritable copy of a handle (as typically, you only want to inherit one end of the pipe). --- lldb/source/Host/windows/PipeWindows.cpp | 5 +- .../Host/windows/ProcessLauncherWindows.cpp | 73 +++++++++++++++---- lldb/tools/lldb-server/lldb-platform.cpp | 2 - lldb/unittests/Host/HostTest.cpp | 2 - 4 files changed, 63 insertions(+), 19 deletions(-) diff --git a/lldb/source/Host/windows/PipeWindows.cpp b/lldb/source/Host/windows/PipeWindows.cpp index e3f5b629a0590..1f7f6e03519d0 100644 --- a/lldb/source/Host/windows/PipeWindows.cpp +++ b/lldb/source/Host/windows/PipeWindows.cpp @@ -88,8 +88,9 @@ Status PipeWindows::CreateNew(llvm::StringRef name, std::string pipe_path = g_pipe_name_prefix.str(); pipe_path.append(name.str()); - SECURITY_ATTRIBUTES sa{sizeof(SECURITY_ATTRIBUTES), 0, - child_process_inherit ? TRUE : FALSE}; + // We always create inheritable handles, but we won't pass them to a child + // process unless explicitly requested (cf. ProcessLauncherWindows.cpp). + SECURITY_ATTRIBUTES sa{sizeof(SECURITY_ATTRIBUTES), 0, TRUE}; // Always open for overlapped i/o. We implement blocking manually in Read // and Write. diff --git a/lldb/source/Host/windows/ProcessLauncherWindows.cpp b/lldb/source/Host/windows/ProcessLauncherWindows.cpp index 065ba9271ad0d..bc35667ea9a23 100644 --- a/lldb/source/Host/windows/ProcessLauncherWindows.cpp +++ b/lldb/source/Host/windows/ProcessLauncherWindows.cpp @@ -10,6 +10,7 @@ #include "lldb/Host/HostProcess.h" #include "lldb/Host/ProcessLaunchInfo.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Program.h" @@ -65,14 +66,23 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, std::string executable; std::vector environment; - STARTUPINFO startupinfo = {}; + STARTUPINFOEX startupinfoex = {}; + STARTUPINFO &startupinfo = startupinfoex.StartupInfo; PROCESS_INFORMATION pi = {}; HANDLE stdin_handle = GetStdioHandle(launch_info, STDIN_FILENO); HANDLE stdout_handle = GetStdioHandle(launch_info, STDOUT_FILENO); HANDLE stderr_handle = GetStdioHandle(launch_info, STDERR_FILENO); - - startupinfo.cb = sizeof(startupinfo); + auto close_handles = llvm::make_scope_exit([&] { + if (stdin_handle) + ::CloseHandle(stdin_handle); + if (stdout_handle) + ::CloseHandle(stdout_handle); + if (stderr_handle) + ::CloseHandle(stderr_handle); + }); + + startupinfo.cb = sizeof(startupinfoex); startupinfo.dwFlags |= STARTF_USESTDHANDLES; startupinfo.hStdError = stderr_handle ? stderr_handle : ::GetStdHandle(STD_ERROR_HANDLE); @@ -81,6 +91,48 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, startupinfo.hStdOutput = stdout_handle ? stdout_handle : ::GetStdHandle(STD_OUTPUT_HANDLE); + std::vector inherited_handles; + if (startupinfo.hStdError) + inherited_handles.push_back(startupinfo.hStdError); + if (startupinfo.hStdInput) + inherited_handles.push_back(startupinfo.hStdInput); + if (startupinfo.hStdOutput) + inherited_handles.push_back(startupinfo.hStdOutput); + + size_t attributelist_size = 0; + InitializeProcThreadAttributeList(/*lpAttributeList=*/nullptr, + /*dwAttributeCount=*/1, /*dwFlags=*/0, + &attributelist_size); + + startupinfoex.lpAttributeList = + static_cast(malloc(attributelist_size)); + auto free_attributelist = + llvm::make_scope_exit([&] { free(startupinfoex.lpAttributeList); }); + if (!InitializeProcThreadAttributeList(startupinfoex.lpAttributeList, + /*dwAttributeCount=*/1, /*dwFlags=*/0, + &attributelist_size)) { + error = Status(::GetLastError(), eErrorTypeWin32); + return HostProcess(); + } + auto delete_attributelist = llvm::make_scope_exit( + [&] { DeleteProcThreadAttributeList(startupinfoex.lpAttributeList); }); + for (size_t i = 0; i < launch_info.GetNumFileActions(); ++i) { + const FileAction *act = launch_info.GetFileActionAtIndex(i); + if (act->GetAction() == FileAction::eFileActionDuplicate && + act->GetFD() == act->GetActionArgument()) + inherited_handles.push_back(reinterpret_cast(act->GetFD())); + } + if (!inherited_handles.empty()) { + if (!UpdateProcThreadAttribute( + startupinfoex.lpAttributeList, /*dwFlags=*/0, + PROC_THREAD_ATTRIBUTE_HANDLE_LIST, inherited_handles.data(), + inherited_handles.size() * sizeof(HANDLE), + /*lpPreviousValue=*/nullptr, /*lpReturnSize=*/nullptr)) { + error = Status(::GetLastError(), eErrorTypeWin32); + return HostProcess(); + } + } + const char *hide_console_var = getenv("LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE"); if (hide_console_var && @@ -89,7 +141,8 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, startupinfo.wShowWindow = SW_HIDE; } - DWORD flags = CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT; + DWORD flags = CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT | + EXTENDED_STARTUPINFO_PRESENT; if (launch_info.GetFlags().Test(eLaunchFlagDebug)) flags |= DEBUG_ONLY_THIS_PROCESS; @@ -114,9 +167,10 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, WCHAR *pwcommandLine = wcommandLine.empty() ? nullptr : &wcommandLine[0]; BOOL result = ::CreateProcessW( - wexecutable.c_str(), pwcommandLine, NULL, NULL, TRUE, flags, env_block, + wexecutable.c_str(), pwcommandLine, NULL, NULL, + /*bInheritHandles=*/!inherited_handles.empty(), flags, env_block, wworkingDirectory.size() == 0 ? NULL : wworkingDirectory.c_str(), - &startupinfo, &pi); + reinterpret_cast(&startupinfoex), &pi); if (!result) { // Call GetLastError before we make any other system calls. @@ -131,13 +185,6 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, ::CloseHandle(pi.hThread); } - if (stdin_handle) - ::CloseHandle(stdin_handle); - if (stdout_handle) - ::CloseHandle(stdout_handle); - if (stderr_handle) - ::CloseHandle(stderr_handle); - if (!result) return HostProcess(); diff --git a/lldb/tools/lldb-server/lldb-platform.cpp b/lldb/tools/lldb-server/lldb-platform.cpp index 10d79c63af994..5b0a8ade01025 100644 --- a/lldb/tools/lldb-server/lldb-platform.cpp +++ b/lldb/tools/lldb-server/lldb-platform.cpp @@ -274,10 +274,8 @@ static Status spawn_process(const char *progname, const FileSpec &prog, self_args.AppendArgument(llvm::StringRef("platform")); self_args.AppendArgument(llvm::StringRef("--child-platform-fd")); self_args.AppendArgument(llvm::to_string(shared_socket.GetSendableFD())); -#ifndef _WIN32 launch_info.AppendDuplicateFileAction((int)shared_socket.GetSendableFD(), (int)shared_socket.GetSendableFD()); -#endif if (gdb_port) { self_args.AppendArgument(llvm::StringRef("--gdbserver-port")); self_args.AppendArgument(llvm::to_string(gdb_port)); diff --git a/lldb/unittests/Host/HostTest.cpp b/lldb/unittests/Host/HostTest.cpp index 222de62ab6697..9e4390a48fb18 100644 --- a/lldb/unittests/Host/HostTest.cpp +++ b/lldb/unittests/Host/HostTest.cpp @@ -90,7 +90,6 @@ TEST(Host, LaunchProcessSetsArgv0) { ASSERT_THAT(exit_status.get_future().get(), 0); } -#ifdef LLVM_ON_UNIX TEST(Host, LaunchProcessDuplicatesHandle) { static constexpr llvm::StringLiteral test_msg("Hello subprocess!"); @@ -126,4 +125,3 @@ TEST(Host, LaunchProcessDuplicatesHandle) { ASSERT_THAT_EXPECTED(bytes_read, llvm::Succeeded()); ASSERT_EQ(llvm::StringRef(msg, *bytes_read), test_msg); } -#endif >From d6c7c4179c5775324dec01ca3a98117c856d94b6 Mon Sep 17 00:00:00 2001 From: Pavel Labath Date: Wed, 7 May 2025 11:21:21 +0200 Subject: [PATCH 2/2] unifdef fd passing code --- .../Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp index d8c7e436f3f8b..332b9255f226f 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -924,9 +924,7 @@ Status GDBRemoteCommunication::StartDebugserverProcess( debugserver_args.AppendArgument(fd_arg.GetString()); // Send "pass_comm_fd" down to the inferior so it can use it to // communicate back with this process. Ignored on Windows. -#ifndef _WIN32 launch_info.AppendDuplicateFileAction((int)pass_comm_fd, (int)pass_comm_fd); -#endif } // use native registers, not the GDB registers From lldb-commits at lists.llvm.org Wed May 7 02:22:57 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Wed, 07 May 2025 02:22:57 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream RPC server interface emitters (PR #138032) In-Reply-To: Message-ID: <681b2671.170a0220.1fe2ff.7f72@mx.google.com> ================ @@ -0,0 +1,592 @@ +//===-- RPCServerSourceEmitter.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 "RPCServerSourceEmitter.h" +#include "RPCCommon.h" + +#include "clang/AST/AST.h" +#include "clang/Frontend/CompilerInstance.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace clang; +using namespace lldb_rpc_gen; + +// For methods with pointer return types, it's important that we know how big +// the type of the pointee is. We must correctly size a buffer (in the form of a +// Bytes object) before we can actually use it. +static const std::map MethodsWithPointerReturnTypes = { + {"_ZN4lldb12SBModuleSpec12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 + {"_ZNK4lldb8SBModule12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 +}; + +void RPCServerSourceEmitter::EmitMethod(const Method &method) { + if (method.ContainsFunctionPointerParameter) + EmitCallbackFunction(method); + + EmitCommentHeader(method); + EmitFunctionHeader(method); + EmitFunctionBody(method); + EmitFunctionFooter(); +} + +void RPCServerSourceEmitter::EmitCommentHeader(const Method &method) { + std::string CommentLine; + llvm::raw_string_ostream CommentStream(CommentLine); + + CommentStream << "// " << method.QualifiedName << "(" + << method.CreateParamListAsString(eServer) << ")"; + if (method.IsConst) + CommentStream << " const"; + + EmitLine("//------------------------------------------------------------"); + EmitLine(CommentLine); + EmitLine("//------------------------------------------------------------"); +} + +void RPCServerSourceEmitter::EmitFunctionHeader(const Method &method) { + std::string FunctionHeader; + llvm::raw_string_ostream FunctionHeaderStream(FunctionHeader); + FunctionHeaderStream + << "bool rpc_server::" << method.MangledName + << "::HandleRPCCall(rpc_common::Connection &connection, RPCStream " + "&send, RPCStream &response) {"; + EmitLine(FunctionHeader); + IndentLevel++; +} + +void RPCServerSourceEmitter::EmitFunctionBody(const Method &method) { + EmitLine("// 1) Make local storage for incoming function arguments"); + EmitStorageForParameters(method); + EmitLine("// 2) Decode all function arguments"); + EmitDecodeForParameters(method); + EmitLine("// 3) Call the method and encode the return value"); + EmitMethodCallAndEncode(method); +} + +void RPCServerSourceEmitter::EmitFunctionFooter() { + EmitLine("return true;"); + IndentLevel--; + EmitLine("}"); +} + +void RPCServerSourceEmitter::EmitStorageForParameters(const Method &method) { + // If we have an instance method and it isn't a constructor, we'll need to + // emit a "this" pointer. + if (method.IsInstance && !method.IsCtor) + EmitStorageForOneParameter(method.ThisType, "this_ptr", method.Policy, + /* IsFollowedByLen = */ false); + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + EmitStorageForOneParameter(Iter->Type, Iter->Name, method.Policy, + Iter->IsFollowedByLen); + // Skip over the length parameter, we don't emit it. + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) + Iter++; + } +} + +void RPCServerSourceEmitter::EmitStorageForOneParameter( + QualType ParamType, const std::string &ParamName, + const PrintingPolicy &Policy, bool IsFollowedByLen) { + // First, we consider `const char *`, `const char **`. They have special + // server-side types. + if (TypeIsConstCharPtr(ParamType)) { + EmitLine("rpc_common::ConstCharPointer " + ParamName + ";"); ---------------- DavidSpickett wrote: Ok, I saw a note about this somewhere and thought it was literally a typedef not a wrapper object. https://github.com/llvm/llvm-project/pull/138032 From lldb-commits at lists.llvm.org Wed May 7 02:23:45 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 02:23:45 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fix dynamic type resolutions for core files (PR #138698) In-Reply-To: Message-ID: <681b26a1.170a0220.25fa84.8849@mx.google.com> https://github.com/labath updated https://github.com/llvm/llvm-project/pull/138698 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Wed May 7 02:24:05 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Wed, 07 May 2025 02:24:05 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream RPC server interface emitters (PR #138032) In-Reply-To: Message-ID: <681b26b5.050a0220.b8c2e.f0fa@mx.google.com> ================ @@ -0,0 +1,592 @@ +//===-- RPCServerSourceEmitter.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 "RPCServerSourceEmitter.h" +#include "RPCCommon.h" + +#include "clang/AST/AST.h" +#include "clang/Frontend/CompilerInstance.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace clang; +using namespace lldb_rpc_gen; + +// For methods with pointer return types, it's important that we know how big +// the type of the pointee is. We must correctly size a buffer (in the form of a +// Bytes object) before we can actually use it. +static const std::map MethodsWithPointerReturnTypes = { + {"_ZN4lldb12SBModuleSpec12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 + {"_ZNK4lldb8SBModule12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 +}; + +void RPCServerSourceEmitter::EmitMethod(const Method &method) { + if (method.ContainsFunctionPointerParameter) + EmitCallbackFunction(method); + + EmitCommentHeader(method); + EmitFunctionHeader(method); + EmitFunctionBody(method); + EmitFunctionFooter(); +} + +void RPCServerSourceEmitter::EmitCommentHeader(const Method &method) { + std::string CommentLine; + llvm::raw_string_ostream CommentStream(CommentLine); + + CommentStream << "// " << method.QualifiedName << "(" + << method.CreateParamListAsString(eServer) << ")"; + if (method.IsConst) + CommentStream << " const"; + + EmitLine("//------------------------------------------------------------"); + EmitLine(CommentLine); + EmitLine("//------------------------------------------------------------"); +} + +void RPCServerSourceEmitter::EmitFunctionHeader(const Method &method) { + std::string FunctionHeader; + llvm::raw_string_ostream FunctionHeaderStream(FunctionHeader); + FunctionHeaderStream + << "bool rpc_server::" << method.MangledName + << "::HandleRPCCall(rpc_common::Connection &connection, RPCStream " + "&send, RPCStream &response) {"; + EmitLine(FunctionHeader); + IndentLevel++; +} + +void RPCServerSourceEmitter::EmitFunctionBody(const Method &method) { + EmitLine("// 1) Make local storage for incoming function arguments"); + EmitStorageForParameters(method); + EmitLine("// 2) Decode all function arguments"); + EmitDecodeForParameters(method); + EmitLine("// 3) Call the method and encode the return value"); + EmitMethodCallAndEncode(method); +} + +void RPCServerSourceEmitter::EmitFunctionFooter() { + EmitLine("return true;"); + IndentLevel--; + EmitLine("}"); +} + +void RPCServerSourceEmitter::EmitStorageForParameters(const Method &method) { + // If we have an instance method and it isn't a constructor, we'll need to + // emit a "this" pointer. + if (method.IsInstance && !method.IsCtor) + EmitStorageForOneParameter(method.ThisType, "this_ptr", method.Policy, + /* IsFollowedByLen = */ false); + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + EmitStorageForOneParameter(Iter->Type, Iter->Name, method.Policy, + Iter->IsFollowedByLen); + // Skip over the length parameter, we don't emit it. + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) + Iter++; + } +} + +void RPCServerSourceEmitter::EmitStorageForOneParameter( + QualType ParamType, const std::string &ParamName, + const PrintingPolicy &Policy, bool IsFollowedByLen) { + // First, we consider `const char *`, `const char **`. They have special + // server-side types. + if (TypeIsConstCharPtr(ParamType)) { + EmitLine("rpc_common::ConstCharPointer " + ParamName + ";"); + return; + } else if (TypeIsConstCharPtrPtr(ParamType)) { + EmitLine("rpc_common::StringList " + ParamName + ";"); + return; + } + + QualType UnderlyingType = + lldb_rpc_gen::GetUnqualifiedUnderlyingType(ParamType); + const bool IsSBClass = lldb_rpc_gen::TypeIsSBClass(UnderlyingType); + + if (ParamType->isPointerType() && !IsSBClass) { + // Void pointer with no length is usually a baton for a callback. We're + // going to hold onto the pointer value so we can send it back to the + // client-side when we implement callbacks. + if (ParamType->isVoidPointerType() && !IsFollowedByLen) { + EmitLine("void * " + ParamName + " = nullptr;"); + return; + } + + if (!ParamType->isFunctionPointerType()) { + EmitLine("Bytes " + ParamName + ";"); + return; + } + + assert(ParamType->isFunctionPointerType() && "Unhandled pointer type"); + EmitLine("rpc_common::function_ptr_t " + ParamName + " = nullptr;"); + return; + } + + std::string StorageDeclaration; + llvm::raw_string_ostream StorageDeclarationStream(StorageDeclaration); + + UnderlyingType.print(StorageDeclarationStream, Policy); + StorageDeclarationStream << " "; + if (IsSBClass) + StorageDeclarationStream << "*"; + StorageDeclarationStream << ParamName; + if (IsSBClass) + StorageDeclarationStream << " = nullptr"; + else + StorageDeclarationStream << " = {}"; + StorageDeclarationStream << ";"; + EmitLine(StorageDeclaration); +} + +void RPCServerSourceEmitter::EmitDecodeForParameters(const Method &method) { + if (method.IsInstance && !method.IsCtor) + EmitDecodeForOneParameter(method.ThisType, "this_ptr", method.Policy); + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + EmitDecodeForOneParameter(Iter->Type, Iter->Name, method.Policy); + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) + Iter++; + } +} + +void RPCServerSourceEmitter::EmitDecodeForOneParameter( + QualType ParamType, const std::string &ParamName, + const PrintingPolicy &Policy) { + QualType UnderlyingType = + lldb_rpc_gen::GetUnqualifiedUnderlyingType(ParamType); + + if (TypeIsSBClass(UnderlyingType)) { + std::string DecodeLine; + llvm::raw_string_ostream DecodeLineStream(DecodeLine); + DecodeLineStream << ParamName << " = " + << "RPCServerObjectDecoder<"; + UnderlyingType.print(DecodeLineStream, Policy); + DecodeLineStream << ">(send, rpc_common::RPCPacket::ValueType::Argument);"; + EmitLine(DecodeLine); + EmitLine("if (!" + ParamName + ")"); + IndentLevel++; + EmitLine("return false;"); + IndentLevel--; + } else { + EmitLine("if (!RPCValueDecoder(send, " + "rpc_common::RPCPacket::ValueType::Argument, " + + ParamName + "))"); + IndentLevel++; + EmitLine("return false;"); + IndentLevel--; + } +} + +std::string RPCServerSourceEmitter::CreateMethodCall(const Method &method) { + std::string MethodCall; + llvm::raw_string_ostream MethodCallStream(MethodCall); + if (method.IsInstance) { + if (!method.IsCtor) + MethodCallStream << "this_ptr->"; + MethodCallStream << method.BaseName; + } else + MethodCallStream << method.QualifiedName; + + std::vector Args; + std::string FunctionPointerName; + for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { + std::string Arg; + // We must check for `const char *` and `const char **` first. + if (TypeIsConstCharPtr(Iter->Type)) { + // `const char *` is stored server-side as rpc_common::ConstCharPointer + Arg = Iter->Name + ".c_str()"; + } else if (TypeIsConstCharPtrPtr(Iter->Type)) { + // `const char **` is stored server-side as rpc_common::StringList + Arg = Iter->Name + ".argv()"; + } else if (lldb_rpc_gen::TypeIsSBClass(Iter->Type)) { + Arg = Iter->Name; + if (!Iter->Type->isPointerType()) + Arg = "*" + Iter->Name; + } else if (Iter->Type->isPointerType() && + !Iter->Type->isFunctionPointerType() && + (!Iter->Type->isVoidPointerType() || Iter->IsFollowedByLen)) { + // We move pointers between the server and client as 'Bytes' objects. + // Pointers with length arguments will have their length filled in below. + // Pointers with no length arguments are assumed to behave like an array + // with length of 1, except for void pointers which are handled + // differently. + Arg = "(" + Iter->Type.getAsString(method.Policy) + ")" + Iter->Name + + ".GetData()"; + } else if (Iter->Type->isFunctionPointerType()) { + // If we have a function pointer, we only want to pass something along if + // we got a real pointer. + Arg = Iter->Name + " ? " + method.MangledName + "_callback : nullptr"; + FunctionPointerName = Iter->Name; + } else if (Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen && + method.ContainsFunctionPointerParameter) { + // Assumptions: + // - This is assumed to be the baton for the function pointer. + // - This is assumed to come after the function pointer parameter. + // We always produce this regardless of the value of the baton argument. + Arg = "new CallbackInfo(" + FunctionPointerName + ", " + Iter->Name + + ", connection.GetConnectionID())"; + } else + Arg = Iter->Name; + + if (Iter->Type->isRValueReferenceType()) + Arg = "std::move(" + Arg + ")"; + Args.push_back(Arg); + + if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && + Iter->IsFollowedByLen) { + std::string LengthArg = Iter->Name + ".GetSize()"; + if (!Iter->Type->isVoidPointerType()) { + QualType UUT = lldb_rpc_gen::GetUnqualifiedUnderlyingType(Iter->Type); + LengthArg += " / sizeof(" + UUT.getAsString(method.Policy) + ")"; + } + Args.push_back(LengthArg); + Iter++; + } + } + MethodCallStream << "(" << llvm::join(Args, ", ") << ")"; + + return MethodCall; +} + +std::string RPCServerSourceEmitter::CreateEncodeLine(const std::string &Value, + bool IsEncodingSBClass) { + std::string EncodeLine; + llvm::raw_string_ostream EncodeLineStream(EncodeLine); + + if (IsEncodingSBClass) + EncodeLineStream << "RPCServerObjectEncoder("; + else + EncodeLineStream << "RPCValueEncoder("; + + EncodeLineStream + << "response, rpc_common::RPCPacket::ValueType::ReturnValue, "; + EncodeLineStream << Value; + EncodeLineStream << ");"; + return EncodeLine; +} + +// There are 4 cases to consider: +// - const SBClass &: No need to do anything. +// - const foo &: No need to do anything. +// - SBClass &: The server and the client hold on to IDs to refer to specific +// instances, so there's no need to send any information back to the client. +// - foo &: The client is sending us a value over the wire, but because the type +// is mutable, we must send the changed value back in case the method call +// mutated it. +// +// Updating a mutable reference is done as a return value from the RPC +// perspective. These return values need to be emitted after the method's return +// value, and they are emitted in the order in which they occur in the +// declaration. +void RPCServerSourceEmitter::EmitEncodesForMutableParameters( + const std::vector &Params) { + for (auto Iter = Params.begin(); Iter != Params.end(); Iter++) { + // No need to manually update an SBClass + if (lldb_rpc_gen::TypeIsSBClass(Iter->Type)) + continue; + + if (!Iter->Type->isReferenceType() && !Iter->Type->isPointerType()) + continue; + + // If we have a void pointer with no length, there's nothing to update. This + // is likely a baton for a callback. The same goes for function pointers. + if (Iter->Type->isFunctionPointerType() || + (Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen)) + continue; + + // No need to update pointers and references to const-qualified data. + QualType UnderlyingType = lldb_rpc_gen::GetUnderlyingType(Iter->Type); + if (UnderlyingType.isConstQualified()) + continue; + + const std::string EncodeLine = + CreateEncodeLine(Iter->Name, /* IsEncodingSBClass = */ false); + EmitLine(EncodeLine); + } +} + +// There are 3 possible scenarios that this method can encounter: +// 1. The method has no return value and is not a constructor. +// Only the method call itself is emitted. +// 2. The method is a constructor. +// The call to the constructor is emitted in the encode line. +// 3. The method has a return value. +// The method call is emitted and the return value is captured in a variable. +// After that, an encode call is emitted with the variable that captured the +// return value. +void RPCServerSourceEmitter::EmitMethodCallAndEncode(const Method &method) { + // FIXME: The hand-written lldb-rpc-server currently doesn't emit destructors + // for LocalObjectRefs... even if the type is an ObjectRef. What should we do + // here? + + const std::string MethodCall = CreateMethodCall(method); + + // If this function returns nothing, we just emit the call and update any + // mutable references. Note that constructors have return type `void` so we + // must explicitly check for that here. + if (!method.IsCtor && method.ReturnType->isVoidType()) { + EmitLine(MethodCall + ";"); + EmitEncodesForMutableParameters(method.Params); + return; + } + + static constexpr llvm::StringLiteral ReturnVariableName("__result"); + + // If this isn't a constructor, we'll need to store the result of the method + // call in a result variable. + if (!method.IsCtor) { + // We need to determine what the appropriate return type is. Here is the + // strategy: + // 1.) `SBFoo` -> `SBFoo &&` ---------------- DavidSpickett wrote: Understood, thanks. https://github.com/llvm/llvm-project/pull/138032 From lldb-commits at lists.llvm.org Wed May 7 02:25:06 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 02:25:06 -0700 (PDT) Subject: [Lldb-commits] =?utf-8?q?=5Blldb=5D_=5Blldb=5D_print_a_notice_whe?= =?utf-8?q?n_=60source_list=60_paging_reaches_the_end_of_th=E2=80=A6_=28PR?= =?utf-8?b?ICMxMzc1MTUp?= In-Reply-To: Message-ID: <681b26f2.630a0220.c88cc.b645@mx.google.com> hapeeeeee wrote: Hi @JDevlieghere , I've corrected the typos. Could you please review my changes again? https://github.com/llvm/llvm-project/pull/137515 From lldb-commits at lists.llvm.org Wed May 7 02:26:38 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Wed, 07 May 2025 02:26:38 -0700 (PDT) Subject: [Lldb-commits] [lldb] [DRAFT][lldb][RPC] Design doc for upstreaming PR (PR #138612) In-Reply-To: Message-ID: <681b274e.050a0220.2f1ad4.0397@mx.google.com> ================ @@ -0,0 +1,94 @@ +LLDB RPC Upstreaming Design Doc +=============================== + +This document aims to explain the general structure of the upstreaming patches for adding LLDB RPC. The 2 primary concepts explained here will be: + +* How LLDB RPC is used +* How the ``lldb-rpc-gen`` works and what it outputs + +LLDB RPC +********* + +LLDB RPC is a framework by which processes can communicate with LLDB out of process while maintaining compatibility with the SB API. More details are explained in the `RFC`_ for upstreaming LLDB RPC, but the main focus in this doc for this section will be how exactly the code is structured for the PRs that will upstream this code. + +The ``lldb-rpc-gen`` tool +************************* + +``lldb-rpc-gen`` is the tool that generates the main client and server interfaces for LLDB RPC. It is a ``ClangTool`` that reads all SB API header files and their functions and outputs the client/server interfaces and certain other pieces of code, such as RPC-specfic versions of Python bindings used for the test suite. There's 3 main components behind ``lldb-rpc-gen``: + +1. The ``lldb-rpc-gen`` tool itself, which contains the main driver that uses the ``ClangTool``. +2. The code that generates all interfaces, which we call "emitters". All generated code for the interfaces are in C++, so the server side has one emitter for its generated source code and another for its generated header code. The client side has the same. +3. All common code shared between all emitters, such as helper methods and information about exceptions to take when emitting. + +The `current PR`_ up for upstreaming LLDB RPC upstreams a subset of the code used for the tool. It upstreams the ``lldb-rpc-gen`` tool and all code needed for the server side emitters. Here's an example of what ``lldb-rpc-gen`` will output for the server side interface: + +Input +----- + +We'll use ``SBDebugger::CreateTarget(const char *filename)`` as an example. ``lldb-rpc-gen`` will read this method from ``SBDebugger.h``. The output is as follows. + +Source Code Output +------------------ + +:: + + bool rpc_server::_ZN4lldb10SBDebugger12CreateTargetEPKc::HandleRPCCall(rpc_common::Connection &connection, RPCStream &send, RPCStream &response) { + // 1) Make local storage for incoming function arguments + lldb::SBDebugger *this_ptr = nullptr; + rpc_common::ConstCharPointer filename; + // 2) Decode all function arguments + this_ptr = RPCServerObjectDecoder(send, rpc_common::RPCPacket::ValueType::Argument); + if (!this_ptr) + return false; + if (!RPCValueDecoder(send, rpc_common::RPCPacket::ValueType::Argument, filename)) + return false; + // 3) Call the method and encode the return value + lldb::SBTarget && __result = this_ptr->CreateTarget(filename.c_str()); + RPCServerObjectEncoder(response, rpc_common::RPCPacket::ValueType::ReturnValue, std::move(__result)); + return true; + } + +Function signature +~~~~~~~~~~~~~~~~~~ + +All server-side source code functions have a function signature that take the format ``bool rpc_server::::HandleRPCCall(rpc_common::Connection &connection, RPCStream &send, RPCStream &response)``. Here the ``connection`` is what's maintained between the client and server. The ``send`` variable is a byte stream that carries information sent from the client. ``response`` is also a byte stream that will be populated with the return value obtained from the call into the SB API function that will be sent back to the client. ---------------- DavidSpickett wrote: Ok so the thing you need to do is differentiate overloads, and mangled names are designed to do exactly that. Got it. Please add that justification to the doc. https://github.com/llvm/llvm-project/pull/138612 From lldb-commits at lists.llvm.org Wed May 7 02:28:54 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Wed, 07 May 2025 02:28:54 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream RPC server interface emitters (PR #138032) In-Reply-To: Message-ID: <681b27d6.170a0220.fb2ac.86ff@mx.google.com> ================ @@ -0,0 +1,592 @@ +//===-- RPCServerSourceEmitter.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 "RPCServerSourceEmitter.h" +#include "RPCCommon.h" + +#include "clang/AST/AST.h" +#include "clang/Frontend/CompilerInstance.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace clang; +using namespace lldb_rpc_gen; + +// For methods with pointer return types, it's important that we know how big +// the type of the pointee is. We must correctly size a buffer (in the form of a +// Bytes object) before we can actually use it. +static const std::map MethodsWithPointerReturnTypes = { ---------------- DavidSpickett wrote: So even if you did `sizeof(pointee type)`, it could still be pointing to more than one `uint8_t`? https://github.com/llvm/llvm-project/pull/138032 From lldb-commits at lists.llvm.org Wed May 7 02:29:22 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 02:29:22 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <681b27f2.170a0220.2ac82a.7f1c@mx.google.com> ================ @@ -280,6 +311,52 @@ void DILParser::BailOut(const std::string &error, uint32_t loc, m_dil_lexer.ResetTokenIdx(m_dil_lexer.NumLexedTokens() - 1); } +// Parse a numeric_literal. +// +// numeric_literal: +// ? Token::numeric_constant ? +// +ASTNodeUP DILParser::ParseNumericLiteral() { + Expect(Token::numeric_constant); + ASTNodeUP numeric_constant = ParseNumericConstant(); + if (numeric_constant->GetKind() == NodeKind::eErrorNode) { + BailOut(llvm::formatv("Failed to parse token as numeric-constant: {0}", + CurToken()), + CurToken().GetLocation(), CurToken().GetSpelling().length()); + return std::make_unique(); + } + m_dil_lexer.Advance(); + return numeric_constant; +} + +static constexpr std::pair type_suffixes[] = { + {"ull", lldb::eBasicTypeUnsignedLongLong}, + {"ul", lldb::eBasicTypeUnsignedLong}, + {"u", lldb::eBasicTypeUnsignedInt}, + {"ll", lldb::eBasicTypeLongLong}, + {"l", lldb::eBasicTypeLong}, +}; + +ASTNodeUP DILParser::ParseNumericConstant() { + Token token = CurToken(); + auto spelling = token.GetSpelling(); + llvm::StringRef spelling_ref = spelling; + lldb::BasicType type = lldb::eBasicTypeInt; + for (auto [suffix, t] : type_suffixes) { + if (spelling_ref.consume_back_insensitive(suffix)) { + type = t; + break; + } + } + llvm::APInt raw_value; + if (!spelling_ref.getAsInteger(0, raw_value)) { ---------------- labath wrote: It was mentioned (along with `'`) in our discussion on the number format a couple of months ago, but I don't think it needs to be implemented right now, so :+1: https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Wed May 7 02:32:01 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Wed, 07 May 2025 02:32:01 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Do not bump memory modificator ID when "internal" debugger memory is updated (PR #129092) In-Reply-To: Message-ID: <681b2891.170a0220.2d1032.3427@mx.google.com> DavidSpickett wrote: Random example - `llvm/test/Analysis/BasicAA/separate_storage.ll`: ``` define i8 @simple_no(ptr %p1, ptr %p2) { ; CHECK-LABEL: @simple_no( ; CHECK-NEXT: entry: ; CHECK-NEXT: store i8 0, ptr [[P1:%.*]], align 1 ; CHECK-NEXT: store i8 1, ptr [[P2:%.*]], align 1 ; CHECK-NEXT: [[LOADOFSTORE:%.*]] = load i8, ptr [[P1]], align 1 ; CHECK-NEXT: ret i8 [[LOADOFSTORE]] ; ``` https://llvm.org/docs/CommandGuide/FileCheck.html#filecheck-string-substitution-blocks https://github.com/llvm/llvm-project/pull/129092 From lldb-commits at lists.llvm.org Wed May 7 02:32:27 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 02:32:27 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <681b28ab.050a0220.2d5921.fc73@mx.google.com> ================ @@ -24,6 +28,8 @@ qualified_id = ["::"] [nested_name_specifier] unqualified_id identifier = ? C99 Identifier ? ; +numeric_literal = ? C99 Integer constant ? ; ---------------- labath wrote: I don't think you need to write the formal grammar. I'd be fine with just some text blurb about the accepted formats. Right now, we don't to anything fancy, so you could just say "decimal, hex, (and octal/binary?)" numbers are accepted. https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Wed May 7 02:32:42 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 02:32:42 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <681b28ba.170a0220.155d61.4f1c@mx.google.com> https://github.com/labath edited https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Wed May 7 02:54:07 2025 From: lldb-commits at lists.llvm.org (Hemang Gadhavi via lldb-commits) Date: Wed, 07 May 2025 02:54:07 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][AIX] get host info for AIX (cont..) (PR #138687) In-Reply-To: Message-ID: <681b2dbf.170a0220.398ca3.7af9@mx.google.com> https://github.com/HemangGadhavi updated https://github.com/llvm/llvm-project/pull/138687 >From a47e4642e6ebcbe6b5466ff118968bac83ccf1e9 Mon Sep 17 00:00:00 2001 From: HemangGadhavi Date: Tue, 6 May 2025 07:59:45 -0500 Subject: [PATCH] [lldb][AIX] get host info for AIX (cont..) --- lldb/source/Host/aix/Host.cpp | 49 +++++++++++++++++++++++++++- lldb/source/Host/aix/HostInfoAIX.cpp | 15 +++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/lldb/source/Host/aix/Host.cpp b/lldb/source/Host/aix/Host.cpp index a812e061ccae2..ead8202cbbdef 100644 --- a/lldb/source/Host/aix/Host.cpp +++ b/lldb/source/Host/aix/Host.cpp @@ -13,6 +13,7 @@ #include "lldb/Utility/ProcessInfo.h" #include "lldb/Utility/Status.h" #include "llvm/BinaryFormat/XCOFF.h" +#include #include #include @@ -41,6 +42,14 @@ static ProcessInstanceInfo::timespec convert(pr_timestruc64_t t) { return ts; } +static bool IsDirNumeric(const char *dname) { + for (; *dname; dname++) { + if (!isdigit(*dname)) + return false; + } + return true; +} + static bool GetStatusInfo(::pid_t pid, ProcessInstanceInfo &processInfo, ProcessState &State) { struct pstatus pstatusData; @@ -133,7 +142,45 @@ static bool GetProcessAndStatInfo(::pid_t pid, uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) { - return 0; + static const char procdir[] = "/proc/"; + + DIR *dirproc = opendir(procdir); + if (dirproc) { + struct dirent *direntry = nullptr; + const uid_t our_uid = getuid(); + const lldb::pid_t our_pid = getpid(); + bool all_users = match_info.GetMatchAllUsers(); + + while ((direntry = readdir(dirproc)) != nullptr) { + if (!IsDirNumeric(direntry->d_name)) + continue; + + lldb::pid_t pid = atoi(direntry->d_name); + // Skip this process. + if (pid == our_pid) + continue; + + ProcessState State; + ProcessInstanceInfo process_info; + if (!GetProcessAndStatInfo(pid, process_info, State)) + continue; + + if (State == ProcessState::Zombie || + State == ProcessState::TracedOrStopped) + continue; + + // Check for user match if we're not matching all users and not running + // as root. + if (!all_users && (our_uid != 0) && (process_info.GetUserID() != our_uid)) + continue; + + if (match_info.Matches(process_info)) { + process_infos.push_back(process_info); + } + } + closedir(dirproc); + } + return process_infos.size(); } bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) { diff --git a/lldb/source/Host/aix/HostInfoAIX.cpp b/lldb/source/Host/aix/HostInfoAIX.cpp index 61b47462dd647..d720f5c52d3b1 100644 --- a/lldb/source/Host/aix/HostInfoAIX.cpp +++ b/lldb/source/Host/aix/HostInfoAIX.cpp @@ -7,6 +7,8 @@ //===----------------------------------------------------------------------===// #include "lldb/Host/aix/HostInfoAIX.h" +#include "lldb/Host/posix/Support.h" +#include using namespace lldb_private; @@ -18,5 +20,18 @@ void HostInfoAIX::Terminate() { HostInfoBase::Terminate(); } FileSpec HostInfoAIX::GetProgramFileSpec() { static FileSpec g_program_filespec; + struct psinfo psinfoData; + auto BufferOrError = getProcFile(getpid(), "psinfo"); + if (BufferOrError) { + std::unique_ptr PsinfoBuffer = + std::move(*BufferOrError); + memcpy(&psinfoData, PsinfoBuffer->getBufferStart(), sizeof(psinfoData)); + llvm::StringRef exe_path( + psinfoData.pr_psargs, + strnlen(psinfoData.pr_psargs, sizeof(psinfoData.pr_psargs))); + if (!exe_path.empty()) { + g_program_filespec.SetFile(exe_path, FileSpec::Style::native); + } + } return g_program_filespec; } From lldb-commits at lists.llvm.org Wed May 7 03:04:19 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Wed, 07 May 2025 03:04:19 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][AArch64] Fix Apple M4 on Linux (PR #135563) In-Reply-To: Message-ID: <681b3023.170a0220.ab65.7e95@mx.google.com> DavidSpickett wrote: >From what I've seen, this is a decent start but there are further issues to be dealt with. Details on https://github.com/llvm/llvm-project/issues/138717. I have to work on some other SME changes first, so it will be a few weeks until I can do anything for this. @laverdet if you want to pursue this yourself in the meantime, feel free to do so. In which case you will find https://lldb.llvm.org/resources/debugging.html# useful, and you can try setting up the Foundation Model to test SVE+SME if you want, but since I'll want to test the changes myself anyway, easier to leave that to me. https://github.com/llvm/llvm-project/pull/135563 From lldb-commits at lists.llvm.org Wed May 7 03:05:04 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 03:05:04 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <681b3050.050a0220.1f4c5.e0fd@mx.google.com> labath wrote: > Why is that a bad thing? Can we do math operations later without types? Plus it's a unified interface, node evaluation returns a `ValueObject`. > > > ... > > I mean... this works and is a basis for future patches, why remove something that we will have to bring back shortly afterwards? After replacing frame var, DIL will just have a little bit of extra capabilities, like using another variable as an index. Because I wanted to avoid this getting bogged down in the discussion about the types of numbers. I don't really mind the "extra capability" of indexing an array with a another variable. The part I have problem with is the precedent it sets about the representation of numbers. You're right that we can't do math operations without (implicitly or explicitly) assigning them some type, but that's exactly the part I think needs more discussion. Right now, you're assigning the type based on the first type system you find (which is likely going to be C), but I think that's not a good choice. If you're debugging some swift code, I think you'd be surprised if `47` resolves to a C type. Using the type system of the currently selected frame might be better, but I'm not convinced that the right choice either. Since you support looking up global variables you can easily end up with a global variable from a different language, and then we have the question of what to do with expressions like `global_c_variable+(__swift int)1`. Normally, I would think we can just say we don't support arithmetic on values from different type systems, but if constants can end up with a foreign type system because of the context, then we'd probably want to support working with those.. somehow. Which brings me to another idea which might work (but again, I'm not saying it's the right one), which is to give constants some neutral type -- for example, the one (implicitly) defined by the operations on the `Scalar` class, and only convert it to a specific type system once it starts interacting with one. Or maybe do it the other way around, and convert everything into a Scalar once we start doing arithmetic. My point is: the space of possible options is very big here, but none of this is necessary to make array indexing (with a constant) work. I agree with your "it works" assertion (but I think that a much simpler implementation would also "work"), but not with the "it's a basis for future patches" part. I think that part needs more discussion. I don't think you need to change the evaluation interface for this -- yet. It's true that some of the ideas above would require that, but that's very much in the air. For now, it should be sufficient to make the array index a raw integer (instead of an AST node). Then there's nothing to evaluate, and you can just directly take the number and index with it. > We still need to implement bit extraction that current `frame var` allows, which looks like this: `integer[4-8]`, another node we will have to re-implement later if we redo how numbers are stored now. Oh wow, I didn't even know that existed. I guess that means we should implement that as well, but I don't think it changes the equation fundamentally. Right now, the two numbers can be stored as... numbers, and later that could be changed to a recursive `Evaluate` call. It should be a local change, if we end up going with the approach you have here. And if we end up with something completely different then it will be less code to change, as this implementation will be simpler. https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Wed May 7 03:07:10 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Wed, 07 May 2025 03:07:10 -0700 (PDT) Subject: [Lldb-commits] [lldb] 62385b8 - [lldb][docs] Correct spelling in debugging doc Message-ID: <681b30ce.170a0220.20e047.8859@mx.google.com> Author: David Spickett Date: 2025-05-07T10:07:02Z New Revision: 62385b848757f2dc35070eadb2ccd921508497dc URL: https://github.com/llvm/llvm-project/commit/62385b848757f2dc35070eadb2ccd921508497dc DIFF: https://github.com/llvm/llvm-project/commit/62385b848757f2dc35070eadb2ccd921508497dc.diff LOG: [lldb][docs] Correct spelling in debugging doc Added: Modified: lldb/docs/resources/debugging.rst Removed: ################################################################################ diff --git a/lldb/docs/resources/debugging.rst b/lldb/docs/resources/debugging.rst index 990a95f54e07f..ba23759b44cf5 100644 --- a/lldb/docs/resources/debugging.rst +++ b/lldb/docs/resources/debugging.rst @@ -236,7 +236,7 @@ in. For example, to find a process that acted as a ``gdbserver`` instance:: Remote Debugging ---------------- -If you want to debug part of LLDB running on a remote machine, the principals +If you want to debug part of LLDB running on a remote machine, the principles are the same but we will have to start debug servers, then attach debuggers to those servers. From lldb-commits at lists.llvm.org Wed May 7 04:08:42 2025 From: lldb-commits at lists.llvm.org (Ebuka Ezike via lldb-commits) Date: Wed, 07 May 2025 04:08:42 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Re-enable the lldb-dap tests (PR #138791) In-Reply-To: Message-ID: <681b3f3a.050a0220.c19c6.2dab@mx.google.com> da-viper wrote: is there a branch tracking the reverted changes #138219, I have an old windows 10 pc i could use to reproduce it. https://github.com/llvm/llvm-project/pull/138791 From lldb-commits at lists.llvm.org Wed May 7 05:07:57 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Wed, 07 May 2025 05:07:57 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Re-enable the lldb-dap tests (PR #138791) In-Reply-To: Message-ID: <681b4d1d.170a0220.216113.be38@mx.google.com> DavidSpickett wrote: As far as I can know, https://github.com/llvm/llvm-project/pull/138219 went onto main, so you can revert my revert to get that back. Then there was https://github.com/llvm/llvm-project/commit/69a0af35a5860156836e9e295ecef9de3474db11, which was later reverted in https://github.com/llvm/llvm-project/commit/5e70460d0155aacbd926f97a7c059de009b6e22d. I saw that cause timeouts on Linux but it didn't change the Windows results. So it should reproduce with just that first change reapplied to main. https://github.com/llvm/llvm-project/pull/138791 From lldb-commits at lists.llvm.org Wed May 7 05:09:42 2025 From: lldb-commits at lists.llvm.org (Michael Buch via lldb-commits) Date: Wed, 07 May 2025 05:09:42 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][DataFormatters] Change ExtractIndexFromString to return std::optional (PR #138297) In-Reply-To: Message-ID: <681b4d86.170a0220.2237be.127b@mx.google.com> https://github.com/Michael137 edited https://github.com/llvm/llvm-project/pull/138297 From lldb-commits at lists.llvm.org Wed May 7 05:09:47 2025 From: lldb-commits at lists.llvm.org (Michael Buch via lldb-commits) Date: Wed, 07 May 2025 05:09:47 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][DataFormatters] Change ExtractIndexFromString to return std::optional (PR #138297) In-Reply-To: Message-ID: <681b4d8b.170a0220.36f386.95c8@mx.google.com> https://github.com/Michael137 approved this pull request. https://github.com/llvm/llvm-project/pull/138297 From lldb-commits at lists.llvm.org Wed May 7 05:16:01 2025 From: lldb-commits at lists.llvm.org (Aaron Ballman via lldb-commits) Date: Wed, 07 May 2025 05:16:01 -0700 (PDT) Subject: [Lldb-commits] [clang] [lldb] [clang] Add `__ptrauth_restricted_intptr` qualifier (PR #137580) In-Reply-To: Message-ID: <681b4f01.050a0220.31513.0f56@mx.google.com> AaronBallman wrote: Thank you for your patience! In general, I think adding a new qualifier to Clang needs to come with significant justification for the implementation and maintenance costs. In this specific case, those costs are somewhat amortized because we already have the `__ptrauth` qualifier and this piggybacks off that work. But I don't think the benefits justify the costs: if the user writes `__ptrauth` on an integer type, we can determine whether that type is "good enough" or not and issue a diagnostic for invalid or suspicious uses, same as if the user spelled it `__ptrauth_restricted_intptr`. The implementation already has to update typechecking in various places regardless of which way we spell the qualifiers, so there should not be too much extra implementation burden from only using one qualifier. So on balance, I think we should only upstream the single qualifier and use a header file + feature tests for compatibility with older compilers. Is that something you can get behind? (You're still the expert in this space, so if I'm trivializing concerns, please speak up!) https://github.com/llvm/llvm-project/pull/137580 From lldb-commits at lists.llvm.org Wed May 7 05:28:06 2025 From: lldb-commits at lists.llvm.org (Santhosh Kumar Ellendula via lldb-commits) Date: Wed, 07 May 2025 05:28:06 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][lldb-dap] Added support for "WriteMemory" request. (PR #131820) In-Reply-To: Message-ID: <681b51d6.170a0220.959a3.879e@mx.google.com> https://github.com/santhoshe447 updated https://github.com/llvm/llvm-project/pull/131820 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Wed May 7 06:02:25 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 06:02:25 -0700 (PDT) Subject: [Lldb-commits] [lldb] 21501d1 - [lldb] Fix dynamic type resolutions for core files (#138698) Message-ID: <681b59e1.170a0220.23715f.9a6f@mx.google.com> Author: Pavel Labath Date: 2025-05-07T15:02:21+02:00 New Revision: 21501d1cf290a63760904fb125e77b432db49933 URL: https://github.com/llvm/llvm-project/commit/21501d1cf290a63760904fb125e77b432db49933 DIFF: https://github.com/llvm/llvm-project/commit/21501d1cf290a63760904fb125e77b432db49933.diff LOG: [lldb] Fix dynamic type resolutions for core files (#138698) We're reading from the object's vtable to determine the pointer to the full object. The vtable is normally in the "rodata" section of the executable, which is often not included in the core file because it's not supposed to change and the debugger can extrapolate its contents from the executable file. We weren't doing that. This patch changes the read operation to use the target class (which falls back onto the executable module as expected) and adds the missing ReadSignedIntegerFromMemory API. The fix is tested by creating a core (minidump) file which deliberately omits the vtable pointer. Added: Modified: lldb/include/lldb/Target/Target.h lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp lldb/source/Target/Target.cpp lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py Removed: ################################################################################ diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h index 73f27dc934b46..0d4e11b65339e 100644 --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -1158,6 +1158,11 @@ class Target : public std::enable_shared_from_this, Status &error, bool force_live_memory = false); + int64_t ReadSignedIntegerFromMemory(const Address &addr, + size_t integer_byte_size, + int64_t fail_value, Status &error, + bool force_live_memory = false); + uint64_t ReadUnsignedIntegerFromMemory(const Address &addr, size_t integer_byte_size, uint64_t fail_value, Status &error, diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp index 8faf7135217ac..0d068ed5950d5 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp @@ -350,7 +350,7 @@ bool ItaniumABILanguageRuntime::GetDynamicTypeAndAddress( if (offset_to_top_location >= vtable_load_addr) return false; Status error; - const int64_t offset_to_top = m_process->ReadSignedIntegerFromMemory( + const int64_t offset_to_top = target.ReadSignedIntegerFromMemory( offset_to_top_location, addr_byte_size, INT64_MIN, error); if (offset_to_top == INT64_MIN) diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index e90e748191a7e..7f61f8689fb95 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -2270,6 +2270,17 @@ size_t Target::ReadScalarIntegerFromMemory(const Address &addr, uint32_t byte_si return 0; } +int64_t Target::ReadSignedIntegerFromMemory(const Address &addr, + size_t integer_byte_size, + int64_t fail_value, Status &error, + bool force_live_memory) { + Scalar scalar; + if (ReadScalarIntegerFromMemory(addr, integer_byte_size, false, scalar, error, + force_live_memory)) + return scalar.SLongLong(fail_value); + return fail_value; +} + uint64_t Target::ReadUnsignedIntegerFromMemory(const Address &addr, size_t integer_byte_size, uint64_t fail_value, Status &error, diff --git a/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py b/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py index 634bd13d7c71a..97d539f7a807c 100644 --- a/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py +++ b/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py @@ -279,3 +279,54 @@ def test_from_forward_decl(self): "frame var -d run-target --ptr-depth=1 --show-types a", substrs=["(B *) a", "m_b_value = 10"], ) + + @no_debug_info_test + @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24663") + @expectedFailureDarwin # dynamic loader unloads modules + def test_from_core_file(self): + """Test fetching C++ dynamic values from core files. Specifically, test + that we can determine the dynamic type of the value if the core file + does not contain the type vtable.""" + self.build() + lldbutil.run_to_name_breakpoint(self, "take_A") + + # Get the address of our object and its vtable + a = self.frame().FindVariable("a") + self.assertSuccess(a.GetError()) + vtable = a.GetVTable() + self.assertSuccess(vtable.GetError()) + a = a.GetValueAsAddress() + vtable = vtable.GetValueAsAddress() + + # Create a core file which will only contain the memory region + # containing `a`. The object is on the stack, so this will automatically + # include the stack of the main thread. + core = self.getBuildArtifact("a.dmp") + options = lldb.SBSaveCoreOptions() + options.SetPluginName("minidump") + options.SetStyle(lldb.eSaveCoreCustomOnly) + options.SetOutputFile(lldb.SBFileSpec(core)) + region = lldb.SBMemoryRegionInfo() + self.assertSuccess(self.process().GetMemoryRegionInfo(a, region)) + self.assertSuccess(options.AddMemoryRegionToSave(region)) + + # Save the core file and load it. + self.assertSuccess(self.process().SaveCore(options)) + self.process().Kill() + error = lldb.SBError() + self.target().LoadCore(core, error) + self.assertSuccess(error) + + # Sanity check -- the process should be able to read the object but not + # its vtable.. + self.process().ReadPointerFromMemory(a, error) + self.assertSuccess(error) + self.process().ReadPointerFromMemory(vtable, error) + self.assertTrue(error.Fail()) + + # .. but we should still be able to see the dynamic type by reading the + # vtable from the executable file. + self.expect( + "frame var -d run-target --ptr-depth=1 --show-types a", + substrs=["(B *) a", "m_b_value = 10"], + ) From lldb-commits at lists.llvm.org Wed May 7 06:02:29 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 06:02:29 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fix dynamic type resolutions for core files (PR #138698) In-Reply-To: Message-ID: <681b59e5.050a0220.bf586.2688@mx.google.com> https://github.com/labath closed https://github.com/llvm/llvm-project/pull/138698 From lldb-commits at lists.llvm.org Wed May 7 06:02:49 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 06:02:49 -0700 (PDT) Subject: [Lldb-commits] [lldb] 7c5f5f3 - [lldb] Inherit DuplicateFileAction(HANDLE, HANDLE) handles on windows (#137978) Message-ID: <681b59f9.050a0220.8498b.43ff@mx.google.com> Author: Pavel Labath Date: 2025-05-07T15:02:45+02:00 New Revision: 7c5f5f3ef83b1d1d43d63862a8431af3dded15bb URL: https://github.com/llvm/llvm-project/commit/7c5f5f3ef83b1d1d43d63862a8431af3dded15bb DIFF: https://github.com/llvm/llvm-project/commit/7c5f5f3ef83b1d1d43d63862a8431af3dded15bb.diff LOG: [lldb] Inherit DuplicateFileAction(HANDLE, HANDLE) handles on windows (#137978) This is a follow-up to https://github.com/llvm/llvm-project/pull/126935, which enables passing handles to a child process on windows systems. Unlike on unix-like systems, the handles need to be created with the "inheritable" flag because there's to way to change the flag value after it has been created. This is why I don't respect the child_process_inherit flag but rather always set the flag to true. (My next step is to delete the flag entirely.) This does mean that pipe may be created as inheritable even if its not necessary, but I think this is offset by the fact that windows (unlike unixes, which pass all ~O_CLOEXEC descriptors through execve and *all* descriptors through fork) has a way to specify the precise set of handles to pass to a specific child process. If this turns out to be insufficient, instead of a constructor flag, I'd rather go with creating a separate api to create an inheritable copy of a handle (as typically, you only want to inherit one end of the pipe). Added: Modified: lldb/source/Host/windows/PipeWindows.cpp lldb/source/Host/windows/ProcessLauncherWindows.cpp lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp lldb/tools/lldb-server/lldb-platform.cpp lldb/unittests/Host/HostTest.cpp Removed: ################################################################################ diff --git a/lldb/source/Host/windows/PipeWindows.cpp b/lldb/source/Host/windows/PipeWindows.cpp index e3f5b629a0590..1f7f6e03519d0 100644 --- a/lldb/source/Host/windows/PipeWindows.cpp +++ b/lldb/source/Host/windows/PipeWindows.cpp @@ -88,8 +88,9 @@ Status PipeWindows::CreateNew(llvm::StringRef name, std::string pipe_path = g_pipe_name_prefix.str(); pipe_path.append(name.str()); - SECURITY_ATTRIBUTES sa{sizeof(SECURITY_ATTRIBUTES), 0, - child_process_inherit ? TRUE : FALSE}; + // We always create inheritable handles, but we won't pass them to a child + // process unless explicitly requested (cf. ProcessLauncherWindows.cpp). + SECURITY_ATTRIBUTES sa{sizeof(SECURITY_ATTRIBUTES), 0, TRUE}; // Always open for overlapped i/o. We implement blocking manually in Read // and Write. diff --git a/lldb/source/Host/windows/ProcessLauncherWindows.cpp b/lldb/source/Host/windows/ProcessLauncherWindows.cpp index 065ba9271ad0d..bc35667ea9a23 100644 --- a/lldb/source/Host/windows/ProcessLauncherWindows.cpp +++ b/lldb/source/Host/windows/ProcessLauncherWindows.cpp @@ -10,6 +10,7 @@ #include "lldb/Host/HostProcess.h" #include "lldb/Host/ProcessLaunchInfo.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Program.h" @@ -65,14 +66,23 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, std::string executable; std::vector environment; - STARTUPINFO startupinfo = {}; + STARTUPINFOEX startupinfoex = {}; + STARTUPINFO &startupinfo = startupinfoex.StartupInfo; PROCESS_INFORMATION pi = {}; HANDLE stdin_handle = GetStdioHandle(launch_info, STDIN_FILENO); HANDLE stdout_handle = GetStdioHandle(launch_info, STDOUT_FILENO); HANDLE stderr_handle = GetStdioHandle(launch_info, STDERR_FILENO); - - startupinfo.cb = sizeof(startupinfo); + auto close_handles = llvm::make_scope_exit([&] { + if (stdin_handle) + ::CloseHandle(stdin_handle); + if (stdout_handle) + ::CloseHandle(stdout_handle); + if (stderr_handle) + ::CloseHandle(stderr_handle); + }); + + startupinfo.cb = sizeof(startupinfoex); startupinfo.dwFlags |= STARTF_USESTDHANDLES; startupinfo.hStdError = stderr_handle ? stderr_handle : ::GetStdHandle(STD_ERROR_HANDLE); @@ -81,6 +91,48 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, startupinfo.hStdOutput = stdout_handle ? stdout_handle : ::GetStdHandle(STD_OUTPUT_HANDLE); + std::vector inherited_handles; + if (startupinfo.hStdError) + inherited_handles.push_back(startupinfo.hStdError); + if (startupinfo.hStdInput) + inherited_handles.push_back(startupinfo.hStdInput); + if (startupinfo.hStdOutput) + inherited_handles.push_back(startupinfo.hStdOutput); + + size_t attributelist_size = 0; + InitializeProcThreadAttributeList(/*lpAttributeList=*/nullptr, + /*dwAttributeCount=*/1, /*dwFlags=*/0, + &attributelist_size); + + startupinfoex.lpAttributeList = + static_cast(malloc(attributelist_size)); + auto free_attributelist = + llvm::make_scope_exit([&] { free(startupinfoex.lpAttributeList); }); + if (!InitializeProcThreadAttributeList(startupinfoex.lpAttributeList, + /*dwAttributeCount=*/1, /*dwFlags=*/0, + &attributelist_size)) { + error = Status(::GetLastError(), eErrorTypeWin32); + return HostProcess(); + } + auto delete_attributelist = llvm::make_scope_exit( + [&] { DeleteProcThreadAttributeList(startupinfoex.lpAttributeList); }); + for (size_t i = 0; i < launch_info.GetNumFileActions(); ++i) { + const FileAction *act = launch_info.GetFileActionAtIndex(i); + if (act->GetAction() == FileAction::eFileActionDuplicate && + act->GetFD() == act->GetActionArgument()) + inherited_handles.push_back(reinterpret_cast(act->GetFD())); + } + if (!inherited_handles.empty()) { + if (!UpdateProcThreadAttribute( + startupinfoex.lpAttributeList, /*dwFlags=*/0, + PROC_THREAD_ATTRIBUTE_HANDLE_LIST, inherited_handles.data(), + inherited_handles.size() * sizeof(HANDLE), + /*lpPreviousValue=*/nullptr, /*lpReturnSize=*/nullptr)) { + error = Status(::GetLastError(), eErrorTypeWin32); + return HostProcess(); + } + } + const char *hide_console_var = getenv("LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE"); if (hide_console_var && @@ -89,7 +141,8 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, startupinfo.wShowWindow = SW_HIDE; } - DWORD flags = CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT; + DWORD flags = CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT | + EXTENDED_STARTUPINFO_PRESENT; if (launch_info.GetFlags().Test(eLaunchFlagDebug)) flags |= DEBUG_ONLY_THIS_PROCESS; @@ -114,9 +167,10 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, WCHAR *pwcommandLine = wcommandLine.empty() ? nullptr : &wcommandLine[0]; BOOL result = ::CreateProcessW( - wexecutable.c_str(), pwcommandLine, NULL, NULL, TRUE, flags, env_block, + wexecutable.c_str(), pwcommandLine, NULL, NULL, + /*bInheritHandles=*/!inherited_handles.empty(), flags, env_block, wworkingDirectory.size() == 0 ? NULL : wworkingDirectory.c_str(), - &startupinfo, &pi); + reinterpret_cast(&startupinfoex), &pi); if (!result) { // Call GetLastError before we make any other system calls. @@ -131,13 +185,6 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, ::CloseHandle(pi.hThread); } - if (stdin_handle) - ::CloseHandle(stdin_handle); - if (stdout_handle) - ::CloseHandle(stdout_handle); - if (stderr_handle) - ::CloseHandle(stderr_handle); - if (!result) return HostProcess(); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp index d8c7e436f3f8b..332b9255f226f 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -924,9 +924,7 @@ Status GDBRemoteCommunication::StartDebugserverProcess( debugserver_args.AppendArgument(fd_arg.GetString()); // Send "pass_comm_fd" down to the inferior so it can use it to // communicate back with this process. Ignored on Windows. -#ifndef _WIN32 launch_info.AppendDuplicateFileAction((int)pass_comm_fd, (int)pass_comm_fd); -#endif } // use native registers, not the GDB registers diff --git a/lldb/tools/lldb-server/lldb-platform.cpp b/lldb/tools/lldb-server/lldb-platform.cpp index 10d79c63af994..5b0a8ade01025 100644 --- a/lldb/tools/lldb-server/lldb-platform.cpp +++ b/lldb/tools/lldb-server/lldb-platform.cpp @@ -274,10 +274,8 @@ static Status spawn_process(const char *progname, const FileSpec &prog, self_args.AppendArgument(llvm::StringRef("platform")); self_args.AppendArgument(llvm::StringRef("--child-platform-fd")); self_args.AppendArgument(llvm::to_string(shared_socket.GetSendableFD())); -#ifndef _WIN32 launch_info.AppendDuplicateFileAction((int)shared_socket.GetSendableFD(), (int)shared_socket.GetSendableFD()); -#endif if (gdb_port) { self_args.AppendArgument(llvm::StringRef("--gdbserver-port")); self_args.AppendArgument(llvm::to_string(gdb_port)); diff --git a/lldb/unittests/Host/HostTest.cpp b/lldb/unittests/Host/HostTest.cpp index 222de62ab6697..9e4390a48fb18 100644 --- a/lldb/unittests/Host/HostTest.cpp +++ b/lldb/unittests/Host/HostTest.cpp @@ -90,7 +90,6 @@ TEST(Host, LaunchProcessSetsArgv0) { ASSERT_THAT(exit_status.get_future().get(), 0); } -#ifdef LLVM_ON_UNIX TEST(Host, LaunchProcessDuplicatesHandle) { static constexpr llvm::StringLiteral test_msg("Hello subprocess!"); @@ -126,4 +125,3 @@ TEST(Host, LaunchProcessDuplicatesHandle) { ASSERT_THAT_EXPECTED(bytes_read, llvm::Succeeded()); ASSERT_EQ(llvm::StringRef(msg, *bytes_read), test_msg); } -#endif From lldb-commits at lists.llvm.org Wed May 7 06:02:52 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 06:02:52 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Inherit DuplicateFileAction(HANDLE, HANDLE) handles on windows (PR #137978) In-Reply-To: Message-ID: <681b59fc.050a0220.c19c6.4263@mx.google.com> https://github.com/labath closed https://github.com/llvm/llvm-project/pull/137978 From lldb-commits at lists.llvm.org Wed May 7 06:04:16 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 06:04:16 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Parse DWARF CFI for discontinuous functions (PR #137006) In-Reply-To: Message-ID: <681b5a50.050a0220.25f316.6154@mx.google.com> ================ @@ -17,23 +17,32 @@ image show-unwind --cached true -n foo # CHECK: UNWIND PLANS for {{.*}}`foo # -# CHECK: Assembly language inspection UnwindPlan: -# CHECK-NEXT: This UnwindPlan originally sourced from assembly insn profiling -# CHECK-NEXT: This UnwindPlan is sourced from the compiler: no. -# CHECK-NEXT: This UnwindPlan is valid at all instruction locations: yes. +# CHECK: eh_frame UnwindPlan: +# CHECK-NEXT: This UnwindPlan originally sourced from eh_frame CFI +# CHECK-NEXT: This UnwindPlan is sourced from the compiler: yes. +# CHECK-NEXT: This UnwindPlan is valid at all instruction locations: no. # CHECK-NEXT: This UnwindPlan is for a trap handler function: no. -# TODO: This address range isn't correct right now. We're just checking that -# it's a different range from the one in the next query. -# CHECK-NEXT: Address range of this UnwindPlan: [{{.*}}.text + 6-0x0000000000000046) +# CHECK-NEXT: Address range of this UnwindPlan: [{{.*}}.text + 6-0x0000000000000010)[{{.*}}.text + 17-0x000000000000001c)[{{.*}}.text + 44-0x0000000000000037)[{{.*}}.text + 56-0x000000000000003d) +# CHECK-NEXT: row[0]: 0: CFA=rsp +8 => rip=[CFA-8] +# CHECK-NEXT: row[1]: 1: CFA=rsp+16 => rbx=[CFA-16] rip=[CFA-8] +# CHECK-NEXT: row[2]: 11: CFA=rsp+16 => rbx=[CFA-16] rip=[CFA-8] +# CHECK-NEXT: row[3]: 15: CFA=rsp+32 => rbx=[CFA-16] rip=[CFA-8] +# CHECK-NEXT: row[4]: 38: CFA=rsp+16 => rbx=[CFA-16] rip=[CFA-8] +# CHECK-NEXT: row[5]: 42: CFA=rsp+32 => rbx=[CFA-16] rip=[CFA-8] +# CHECK-NEXT: row[6]: 50: CFA=rsp+32 => rbx=[CFA-16] rip=[CFA-8] +# CHECK-NEXT: row[7]: 54: CFA=rsp +8 => rbx=[CFA-16] rip=[CFA-8] +# CHECK-EMPTY: image show-unwind --cached true -n bar # CHECK: UNWIND PLANS for {{.*}}`bar -# CHECK: Assembly language inspection UnwindPlan: -# CHECK-NEXT: This UnwindPlan originally sourced from assembly insn profiling -# CHECK-NEXT: This UnwindPlan is sourced from the compiler: no. -# CHECK-NEXT: This UnwindPlan is valid at all instruction locations: yes. +# CHECK: eh_frame UnwindPlan: +# CHECK-NEXT: This UnwindPlan originally sourced from eh_frame CFI +# CHECK-NEXT: This UnwindPlan is sourced from the compiler: yes. +# CHECK-NEXT: This UnwindPlan is valid at all instruction locations: no. # CHECK-NEXT: This UnwindPlan is for a trap handler function: no. -# TODO: This address range isn't correct right now. We're just checking that -# it's a different range from the one in the previous query. -# CHECK-NEXT: Address range of this UnwindPlan: [{{.*}}.text + 35-0x0000000000000033) +# CHECK-NEXT: Address range of this UnwindPlan: [{{.*}}.text + 28-0x000000000000002c) ---------------- labath wrote: Yeah, it's quite a mess. I've been planning to look into that. https://github.com/llvm/llvm-project/pull/137006 From lldb-commits at lists.llvm.org Wed May 7 06:04:28 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 06:04:28 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Parse DWARF CFI for discontinuous functions (PR #137006) In-Reply-To: Message-ID: <681b5a5c.a70a0220.115f50.4c40@mx.google.com> https://github.com/labath closed https://github.com/llvm/llvm-project/pull/137006 From lldb-commits at lists.llvm.org Wed May 7 06:04:25 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 06:04:25 -0700 (PDT) Subject: [Lldb-commits] [lldb] d865f32 - [lldb] Parse DWARF CFI for discontinuous functions (#137006) Message-ID: <681b5a59.170a0220.1b6385.02d8@mx.google.com> Author: Pavel Labath Date: 2025-05-07T15:04:22+02:00 New Revision: d865f32fe820f543f0a53bfeba08774f2c270589 URL: https://github.com/llvm/llvm-project/commit/d865f32fe820f543f0a53bfeba08774f2c270589 DIFF: https://github.com/llvm/llvm-project/commit/d865f32fe820f543f0a53bfeba08774f2c270589.diff LOG: [lldb] Parse DWARF CFI for discontinuous functions (#137006) This patch uses the previously build infrastructure to parse multiple FDE entries into a single unwind plan. There is one catch though: we parse only one FDE entry per unwind range. This is not fully correct because lldb coalesces adjecant address ranges, which means that something that originally looked like two separate address ranges (and two FDE entries) may get merged into one because if the linker decides to put the two ranges next to each other. In this case, we will ignore the second FDE entry. It would be more correct to try to parse another entry when the one we found turns out to be short, but I'm not doing this (yet), because: - this is how we've done things so far (although, monolithic functions are unlikely to have more than one FDE entry) - in cases where we don't have debug info or (full) symbol tables, we can end up with "symbols" which appear to span many megabytes (potentially, the whole module). If we tried to fill short FDE entries, we could end up parsing the entire eh_frame section in a single go. In a way, this would be more correct, but it would also probably be very slow. I haven't quite decided what to do about this case yet, though it's not particularly likely to happen in the "production" cases as typically the functions are split into two parts (hot/cold) instead of one part per basic block. Added: Modified: lldb/include/lldb/Symbol/DWARFCallFrameInfo.h lldb/source/Symbol/DWARFCallFrameInfo.cpp lldb/source/Symbol/FuncUnwinders.cpp lldb/source/Symbol/UnwindTable.cpp lldb/source/Target/RegisterContextUnwind.cpp lldb/test/Shell/Unwind/Inputs/basic-block-sections-with-dwarf.s lldb/test/Shell/Unwind/basic-block-sections-with-dwarf-static.test lldb/unittests/Symbol/TestDWARFCallFrameInfo.cpp Removed: ################################################################################ diff --git a/lldb/include/lldb/Symbol/DWARFCallFrameInfo.h b/lldb/include/lldb/Symbol/DWARFCallFrameInfo.h index 679f652c7f2e0..c214ed1f60919 100644 --- a/lldb/include/lldb/Symbol/DWARFCallFrameInfo.h +++ b/lldb/include/lldb/Symbol/DWARFCallFrameInfo.h @@ -47,12 +47,15 @@ class DWARFCallFrameInfo { /// Return an UnwindPlan based on the call frame information encoded in the /// FDE of this DWARFCallFrameInfo section. The returned plan will be valid /// (at least) for the given address. - bool GetUnwindPlan(const Address &addr, UnwindPlan &unwind_plan); + std::unique_ptr GetUnwindPlan(const Address &addr); /// Return an UnwindPlan based on the call frame information encoded in the /// FDE of this DWARFCallFrameInfo section. The returned plan will be valid - /// (at least) for some address in the given range. - bool GetUnwindPlan(const AddressRange &range, UnwindPlan &unwind_plan); + /// (at least) for some address in the given ranges. If no unwind information + /// is found, nullptr is returned. \a addr represents the entry point of the + /// function. It corresponds to the offset zero in the returned UnwindPlan. + std::unique_ptr GetUnwindPlan(llvm::ArrayRef ranges, + const Address &addr); typedef RangeVector FunctionAddressAndSizeVector; diff --git a/lldb/source/Symbol/DWARFCallFrameInfo.cpp b/lldb/source/Symbol/DWARFCallFrameInfo.cpp index a763acb1fdf9e..cb8aa8a26c3f1 100644 --- a/lldb/source/Symbol/DWARFCallFrameInfo.cpp +++ b/lldb/source/Symbol/DWARFCallFrameInfo.cpp @@ -151,53 +151,57 @@ DWARFCallFrameInfo::DWARFCallFrameInfo(ObjectFile &objfile, SectionSP §ion_sp, Type type) : m_objfile(objfile), m_section_sp(section_sp), m_type(type) {} -bool DWARFCallFrameInfo::GetUnwindPlan(const Address &addr, - UnwindPlan &unwind_plan) { - return GetUnwindPlan(AddressRange(addr, 1), unwind_plan); +std::unique_ptr +DWARFCallFrameInfo::GetUnwindPlan(const Address &addr) { + return GetUnwindPlan({AddressRange(addr, 1)}, addr); } -bool DWARFCallFrameInfo::GetUnwindPlan(const AddressRange &range, - UnwindPlan &unwind_plan) { +std::unique_ptr +DWARFCallFrameInfo::GetUnwindPlan(llvm::ArrayRef ranges, + const Address &addr) { FDEEntryMap::Entry fde_entry; - Address addr = range.GetBaseAddress(); // Make sure that the Address we're searching for is the same object file as // this DWARFCallFrameInfo, we only store File offsets in m_fde_index. ModuleSP module_sp = addr.GetModule(); if (module_sp.get() == nullptr || module_sp->GetObjectFile() == nullptr || module_sp->GetObjectFile() != &m_objfile) - return false; + return nullptr; - std::optional entry = GetFirstFDEEntryInRange(range); - if (!entry) - return false; + std::vector valid_ranges; - std::optional fde = ParseFDE(entry->data, addr); - if (!fde) - return false; - - unwind_plan.SetSourceName(m_type == EH ? "eh_frame CFI" : "DWARF CFI"); + auto result = std::make_unique(GetRegisterKind()); + result->SetSourceName(m_type == EH ? "eh_frame CFI" : "DWARF CFI"); // In theory the debug_frame info should be valid at all call sites // ("asynchronous unwind info" as it is sometimes called) but in practice // gcc et al all emit call frame info for the prologue and call sites, but // not for the epilogue or all the other locations during the function // reliably. - unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); - unwind_plan.SetSourcedFromCompiler(eLazyBoolYes); - unwind_plan.SetRegisterKind(GetRegisterKind()); - - unwind_plan.SetPlanValidAddressRanges({fde->range}); - unwind_plan.SetUnwindPlanForSignalTrap(fde->for_signal_trap ? eLazyBoolYes - : eLazyBoolNo); - unwind_plan.SetReturnAddressRegister(fde->return_addr_reg_num); - int64_t slide = - fde->range.GetBaseAddress().GetFileAddress() - addr.GetFileAddress(); - for (UnwindPlan::Row &row : fde->rows) { - row.SlideOffset(slide); - unwind_plan.AppendRow(std::move(row)); + result->SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); + result->SetSourcedFromCompiler(eLazyBoolYes); + result->SetUnwindPlanForSignalTrap(eLazyBoolNo); + for (const AddressRange &range : ranges) { + std::optional entry = GetFirstFDEEntryInRange(range); + if (!entry) + continue; + std::optional fde = ParseFDE(entry->data, addr); + if (!fde) + continue; + int64_t slide = + fde->range.GetBaseAddress().GetFileAddress() - addr.GetFileAddress(); + valid_ranges.push_back(std::move(fde->range)); + if (fde->for_signal_trap) + result->SetUnwindPlanForSignalTrap(eLazyBoolYes); + result->SetReturnAddressRegister(fde->return_addr_reg_num); + for (UnwindPlan::Row &row : fde->rows) { + row.SlideOffset(slide); + result->AppendRow(std::move(row)); + } } - - return true; + result->SetPlanValidAddressRanges(std::move(valid_ranges)); + if (result->GetRowCount() == 0) + return nullptr; + return result; } bool DWARFCallFrameInfo::GetAddressRange(Address addr, AddressRange &range) { diff --git a/lldb/source/Symbol/FuncUnwinders.cpp b/lldb/source/Symbol/FuncUnwinders.cpp index 11600825e8e38..faec24cde7fdd 100644 --- a/lldb/source/Symbol/FuncUnwinders.cpp +++ b/lldb/source/Symbol/FuncUnwinders.cpp @@ -149,13 +149,9 @@ FuncUnwinders::GetEHFrameUnwindPlan(Target &target) { return m_unwind_plan_eh_frame_sp; m_tried_unwind_plan_eh_frame = true; - if (m_range.GetBaseAddress().IsValid()) { - DWARFCallFrameInfo *eh_frame = m_unwind_table.GetEHFrameInfo(); - if (eh_frame) { - auto plan_sp = std::make_shared(lldb::eRegisterKindGeneric); - if (eh_frame->GetUnwindPlan(m_range, *plan_sp)) - m_unwind_plan_eh_frame_sp = std::move(plan_sp); - } + if (m_addr.IsValid()) { + if (DWARFCallFrameInfo *eh_frame = m_unwind_table.GetEHFrameInfo()) + m_unwind_plan_eh_frame_sp = eh_frame->GetUnwindPlan(m_ranges, m_addr); } return m_unwind_plan_eh_frame_sp; } @@ -167,13 +163,10 @@ FuncUnwinders::GetDebugFrameUnwindPlan(Target &target) { return m_unwind_plan_debug_frame_sp; m_tried_unwind_plan_debug_frame = true; - if (m_range.GetBaseAddress().IsValid()) { - DWARFCallFrameInfo *debug_frame = m_unwind_table.GetDebugFrameInfo(); - if (debug_frame) { - auto plan_sp = std::make_shared(lldb::eRegisterKindGeneric); - if (debug_frame->GetUnwindPlan(m_range, *plan_sp)) - m_unwind_plan_debug_frame_sp = std::move(plan_sp); - } + if (!m_ranges.empty()) { + if (DWARFCallFrameInfo *debug_frame = m_unwind_table.GetDebugFrameInfo()) + m_unwind_plan_debug_frame_sp = + debug_frame->GetUnwindPlan(m_ranges, m_addr); } return m_unwind_plan_debug_frame_sp; } diff --git a/lldb/source/Symbol/UnwindTable.cpp b/lldb/source/Symbol/UnwindTable.cpp index 21ecd434d7212..3aca495696c84 100644 --- a/lldb/source/Symbol/UnwindTable.cpp +++ b/lldb/source/Symbol/UnwindTable.cpp @@ -122,6 +122,13 @@ AddressRanges UnwindTable::GetAddressRanges(const Address &addr, return {}; } +static Address GetFunctionOrSymbolAddress(const Address &addr, + const SymbolContext &sc) { + if (Address result = sc.GetFunctionOrSymbolAddress(); result.IsValid()) + return result; + return addr; +} + FuncUnwindersSP UnwindTable::GetFuncUnwindersContainingAddress(const Address &addr, const SymbolContext &sc) { @@ -131,25 +138,20 @@ UnwindTable::GetFuncUnwindersContainingAddress(const Address &addr, // There is an UnwindTable per object file, so we can safely use file handles addr_t file_addr = addr.GetFileAddress(); - iterator end = m_unwinds.end(); - iterator insert_pos = end; - if (!m_unwinds.empty()) { - insert_pos = m_unwinds.lower_bound(file_addr); - iterator pos = insert_pos; - if ((pos == m_unwinds.end()) || - (pos != m_unwinds.begin() && - pos->second->GetFunctionStartAddress() != addr)) - --pos; - + iterator insert_pos = m_unwinds.upper_bound(file_addr); + if (insert_pos != m_unwinds.begin()) { + auto pos = std::prev(insert_pos); if (pos->second->ContainsAddress(addr)) return pos->second; } + Address start_addr = GetFunctionOrSymbolAddress(addr, sc); AddressRanges ranges = GetAddressRanges(addr, sc); if (ranges.empty()) return nullptr; - auto func_unwinder_sp = std::make_shared(*this, addr, ranges); + auto func_unwinder_sp = + std::make_shared(*this, start_addr, ranges); for (const AddressRange &range : ranges) m_unwinds.emplace_hint(insert_pos, range.GetBaseAddress().GetFileAddress(), func_unwinder_sp); @@ -164,11 +166,12 @@ FuncUnwindersSP UnwindTable::GetUncachedFuncUnwindersContainingAddress( const Address &addr, const SymbolContext &sc) { Initialize(); + Address start_addr = GetFunctionOrSymbolAddress(addr, sc); AddressRanges ranges = GetAddressRanges(addr, sc); if (ranges.empty()) return nullptr; - return std::make_shared(*this, addr, std::move(ranges)); + return std::make_shared(*this, start_addr, std::move(ranges)); } void UnwindTable::Dump(Stream &s) { diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 3ed49e12476dd..4c760b84e45a5 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -868,13 +868,11 @@ RegisterContextUnwind::GetFullUnwindPlanForFrame() { // Even with -fomit-frame-pointer, we can try eh_frame to get back on // track. - DWARFCallFrameInfo *eh_frame = - pc_module_sp->GetUnwindTable().GetEHFrameInfo(); - if (eh_frame) { - auto unwind_plan_sp = - std::make_shared(lldb::eRegisterKindGeneric); - if (eh_frame->GetUnwindPlan(m_current_pc, *unwind_plan_sp)) - return unwind_plan_sp; + if (DWARFCallFrameInfo *eh_frame = + pc_module_sp->GetUnwindTable().GetEHFrameInfo()) { + if (std::unique_ptr plan_up = + eh_frame->GetUnwindPlan(m_current_pc)) + return plan_up; } ArmUnwindInfo *arm_exidx = @@ -1345,9 +1343,9 @@ RegisterContextUnwind::SavedLocationForRegister( // value instead of the Return Address register. // If $pc is not available, fall back to the RA reg. UnwindPlan::Row::AbstractRegisterLocation scratch; - if (m_frame_type == eTrapHandlerFrame && - active_row->GetRegisterInfo - (pc_regnum.GetAsKind (unwindplan_registerkind), scratch)) { + if (m_frame_type == eTrapHandlerFrame && active_row && + active_row->GetRegisterInfo( + pc_regnum.GetAsKind(unwindplan_registerkind), scratch)) { UnwindLogMsg("Providing pc register instead of rewriting to " "RA reg because this is a trap handler and there is " "a location for the saved pc register value."); @@ -1377,7 +1375,7 @@ RegisterContextUnwind::SavedLocationForRegister( } } - if (regnum.IsValid() && + if (regnum.IsValid() && active_row && active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind), unwindplan_regloc)) { have_unwindplan_regloc = true; diff --git a/lldb/test/Shell/Unwind/Inputs/basic-block-sections-with-dwarf.s b/lldb/test/Shell/Unwind/Inputs/basic-block-sections-with-dwarf.s index c405e51c227cb..ede04c88a030f 100644 --- a/lldb/test/Shell/Unwind/Inputs/basic-block-sections-with-dwarf.s +++ b/lldb/test/Shell/Unwind/Inputs/basic-block-sections-with-dwarf.s @@ -4,7 +4,9 @@ # int bar() { return foo(0); } # int foo(int flag) { return flag ? bar() : baz(); } # int main() { return foo(1); } -# The function bar has been placed "in the middle" of foo. +# The function bar has been placed "in the middle" of foo. The functions are not +# using the frame pointer register and the are deliberately adjusting the stack +# pointer to test that we're using the correct unwind row. .text @@ -20,26 +22,29 @@ baz: .type foo, at function foo: .cfi_startproc - pushq %rbp + pushq %rbx .cfi_def_cfa_offset 16 - .cfi_offset %rbp, -16 - movq %rsp, %rbp - .cfi_def_cfa_register %rbp - subq $16, %rsp - movl %edi, -8(%rbp) - cmpl $0, -8(%rbp) + .cfi_offset %rbx, -16 + movl %edi, %ebx + cmpl $0, %ebx je foo.__part.2 jmp foo.__part.1 .cfi_endproc .Lfoo_end: .size foo, .Lfoo_end-foo +# NB: Deliberately inserting padding to separate the two parts of the function +# as we're currently only parsing a single FDE entry from a (coalesced) address +# range. + nop + foo.__part.1: .cfi_startproc - .cfi_def_cfa %rbp, 16 - .cfi_offset %rbp, -16 + .cfi_def_cfa_offset 16 + .cfi_offset %rbx, -16 + subq $16, %rsp + .cfi_def_cfa_offset 32 callq bar - movl %eax, -4(%rbp) jmp foo.__part.3 .Lfoo.__part.1_end: .size foo.__part.1, .Lfoo.__part.1_end-foo.__part.1 @@ -47,8 +52,6 @@ foo.__part.1: bar: .cfi_startproc -# NB: Decrease the stack pointer to make the unwind info for this function -# diff erent from the surrounding foo function. subq $24, %rsp .cfi_def_cfa_offset 32 xorl %edi, %edi @@ -62,22 +65,26 @@ bar: foo.__part.2: .cfi_startproc - .cfi_def_cfa %rbp, 16 - .cfi_offset %rbp, -16 + .cfi_def_cfa_offset 16 + .cfi_offset %rbx, -16 + subq $16, %rsp + .cfi_def_cfa_offset 32 callq baz - movl %eax, -4(%rbp) jmp foo.__part.3 .Lfoo.__part.2_end: .size foo.__part.2, .Lfoo.__part.2_end-foo.__part.2 .cfi_endproc +# NB: Deliberately inserting padding to separate the two parts of the function +# as we're currently only parsing a single FDE entry from a (coalesced) address +# range. + nop + foo.__part.3: .cfi_startproc - .cfi_def_cfa %rbp, 16 - .cfi_offset %rbp, -16 - movl -4(%rbp), %eax - addq $16, %rsp - popq %rbp + .cfi_def_cfa_offset 32 + .cfi_offset %rbx, -16 + addq $24, %rsp .cfi_def_cfa %rsp, 8 retq .Lfoo.__part.3_end: @@ -186,9 +193,8 @@ main: .byte 86 .asciz "foo" # DW_AT_name .byte 4 # Abbrev [4] DW_TAG_formal_parameter - .byte 2 # DW_AT_location - .byte 145 - .byte 120 + .byte 1 # DW_AT_location + .byte 0x53 # DW_OP_reg3 .asciz "flag" # DW_AT_name .long .Lint-.Lcu_begin0 # DW_AT_type .byte 0 # End Of Children Mark diff --git a/lldb/test/Shell/Unwind/basic-block-sections-with-dwarf-static.test b/lldb/test/Shell/Unwind/basic-block-sections-with-dwarf-static.test index 9f94468ceecdb..a4ed73e14de01 100644 --- a/lldb/test/Shell/Unwind/basic-block-sections-with-dwarf-static.test +++ b/lldb/test/Shell/Unwind/basic-block-sections-with-dwarf-static.test @@ -17,23 +17,32 @@ image show-unwind --cached true -n foo # CHECK: UNWIND PLANS for {{.*}}`foo # -# CHECK: Assembly language inspection UnwindPlan: -# CHECK-NEXT: This UnwindPlan originally sourced from assembly insn profiling -# CHECK-NEXT: This UnwindPlan is sourced from the compiler: no. -# CHECK-NEXT: This UnwindPlan is valid at all instruction locations: yes. +# CHECK: eh_frame UnwindPlan: +# CHECK-NEXT: This UnwindPlan originally sourced from eh_frame CFI +# CHECK-NEXT: This UnwindPlan is sourced from the compiler: yes. +# CHECK-NEXT: This UnwindPlan is valid at all instruction locations: no. # CHECK-NEXT: This UnwindPlan is for a trap handler function: no. -# TODO: This address range isn't correct right now. We're just checking that -# it's a diff erent range from the one in the next query. -# CHECK-NEXT: Address range of this UnwindPlan: [{{.*}}.text + 6-0x0000000000000046) +# CHECK-NEXT: Address range of this UnwindPlan: [{{.*}}.text + 6-0x0000000000000010)[{{.*}}.text + 17-0x000000000000001c)[{{.*}}.text + 44-0x0000000000000037)[{{.*}}.text + 56-0x000000000000003d) +# CHECK-NEXT: row[0]: 0: CFA=rsp +8 => rip=[CFA-8] +# CHECK-NEXT: row[1]: 1: CFA=rsp+16 => rbx=[CFA-16] rip=[CFA-8] +# CHECK-NEXT: row[2]: 11: CFA=rsp+16 => rbx=[CFA-16] rip=[CFA-8] +# CHECK-NEXT: row[3]: 15: CFA=rsp+32 => rbx=[CFA-16] rip=[CFA-8] +# CHECK-NEXT: row[4]: 38: CFA=rsp+16 => rbx=[CFA-16] rip=[CFA-8] +# CHECK-NEXT: row[5]: 42: CFA=rsp+32 => rbx=[CFA-16] rip=[CFA-8] +# CHECK-NEXT: row[6]: 50: CFA=rsp+32 => rbx=[CFA-16] rip=[CFA-8] +# CHECK-NEXT: row[7]: 54: CFA=rsp +8 => rbx=[CFA-16] rip=[CFA-8] +# CHECK-EMPTY: image show-unwind --cached true -n bar # CHECK: UNWIND PLANS for {{.*}}`bar -# CHECK: Assembly language inspection UnwindPlan: -# CHECK-NEXT: This UnwindPlan originally sourced from assembly insn profiling -# CHECK-NEXT: This UnwindPlan is sourced from the compiler: no. -# CHECK-NEXT: This UnwindPlan is valid at all instruction locations: yes. +# CHECK: eh_frame UnwindPlan: +# CHECK-NEXT: This UnwindPlan originally sourced from eh_frame CFI +# CHECK-NEXT: This UnwindPlan is sourced from the compiler: yes. +# CHECK-NEXT: This UnwindPlan is valid at all instruction locations: no. # CHECK-NEXT: This UnwindPlan is for a trap handler function: no. -# TODO: This address range isn't correct right now. We're just checking that -# it's a diff erent range from the one in the previous query. -# CHECK-NEXT: Address range of this UnwindPlan: [{{.*}}.text + 35-0x0000000000000033) +# CHECK-NEXT: Address range of this UnwindPlan: [{{.*}}.text + 28-0x000000000000002c) +# CHECK-NEXT: row[0]: 0: CFA=rsp +8 => rip=[CFA-8] +# CHECK-NEXT: row[1]: 4: CFA=rsp+32 => rip=[CFA-8] +# CHECK-NEXT: row[2]: 15: CFA=rsp +8 => rip=[CFA-8] +# CHECK-EMPTY: diff --git a/lldb/unittests/Symbol/TestDWARFCallFrameInfo.cpp b/lldb/unittests/Symbol/TestDWARFCallFrameInfo.cpp index 86a6cf0cacb14..c1dcab02227da 100644 --- a/lldb/unittests/Symbol/TestDWARFCallFrameInfo.cpp +++ b/lldb/unittests/Symbol/TestDWARFCallFrameInfo.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "Plugins/ObjectFile/ELF/ObjectFileELF.h" @@ -236,12 +237,12 @@ void DWARFCallFrameInfoTest::TestBasic(DWARFCallFrameInfo::Type type, ConstString(symbol), eSymbolTypeAny); ASSERT_NE(nullptr, sym); - UnwindPlan plan(eRegisterKindGeneric); - ASSERT_TRUE(cfi.GetUnwindPlan(sym->GetAddress(), plan)); - ASSERT_EQ(3, plan.GetRowCount()); - EXPECT_EQ(GetExpectedRow0(), *plan.GetRowAtIndex(0)); - EXPECT_EQ(GetExpectedRow1(), *plan.GetRowAtIndex(1)); - EXPECT_EQ(GetExpectedRow2(), *plan.GetRowAtIndex(2)); + std::unique_ptr plan_up = cfi.GetUnwindPlan(sym->GetAddress()); + ASSERT_TRUE(plan_up); + ASSERT_EQ(3, plan_up->GetRowCount()); + EXPECT_THAT(plan_up->GetRowAtIndex(0), testing::Pointee(GetExpectedRow0())); + EXPECT_THAT(plan_up->GetRowAtIndex(1), testing::Pointee(GetExpectedRow1())); + EXPECT_THAT(plan_up->GetRowAtIndex(2), testing::Pointee(GetExpectedRow2())); } TEST_F(DWARFCallFrameInfoTest, Basic_dwarf3) { From lldb-commits at lists.llvm.org Wed May 7 06:46:17 2025 From: lldb-commits at lists.llvm.org (Rahul Joshi via lldb-commits) Date: Wed, 07 May 2025 06:46:17 -0700 (PDT) Subject: [Lldb-commits] [clang] [lldb] [llvm] [mlir] [NFC][Support] Add llvm::uninitialized_copy (PR #138174) In-Reply-To: Message-ID: <681b6429.170a0220.31efc4.3c4e@mx.google.com> jurahul wrote: Thanks @jpienaar. Given I have 2 approvals, will commit it today. https://github.com/llvm/llvm-project/pull/138174 From lldb-commits at lists.llvm.org Wed May 7 06:46:46 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 06:46:46 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fix dynamic type resolutions for core files (PR #138698) In-Reply-To: Message-ID: <681b6446.050a0220.ad00a.7b05@mx.google.com> labath wrote: @DavidSpickett, I am confused by the error on this arm builder: https://lab.llvm.org/buildbot/#/builders/18/builds/15619 It says arm is not supported, but I think that error message is coming from [here](https://github.com/llvm/llvm-project/blob/3feb8b42e973f935883bc9e779645ecdae1a586d/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp#L168), and that switch has a `llvm::Triple::ArchType::arm` case. Do you know how this could fire? Is it one of those `armebv7.92+polka_dots` triples that's throwing it off somehow? https://github.com/llvm/llvm-project/pull/138698 From lldb-commits at lists.llvm.org Wed May 7 06:50:39 2025 From: lldb-commits at lists.llvm.org (Rahul Joshi via lldb-commits) Date: Wed, 07 May 2025 06:50:39 -0700 (PDT) Subject: [Lldb-commits] [clang] [lldb] [llvm] [mlir] [NFC][Support] Add llvm::uninitialized_copy (PR #138174) In-Reply-To: Message-ID: <681b652f.050a0220.c19c6.7f4e@mx.google.com> https://github.com/jurahul updated https://github.com/llvm/llvm-project/pull/138174 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Wed May 7 07:01:49 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 07:01:49 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Inherit DuplicateFileAction(HANDLE, HANDLE) handles on windows (PR #137978) In-Reply-To: Message-ID: <681b67cd.050a0220.30cf71.7038@mx.google.com> labath wrote: (I see the failure on https://lab.llvm.org/buildbot/#/builders/197. I want to submit some additional logging code before I revert this.) https://github.com/llvm/llvm-project/pull/137978 From lldb-commits at lists.llvm.org Wed May 7 07:11:24 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 07:11:24 -0700 (PDT) Subject: [Lldb-commits] [lldb] 1815d62 - [lldb] Add more logging to a unit test Message-ID: <681b6a0c.050a0220.f9fc.88d7@mx.google.com> Author: Pavel Labath Date: 2025-05-07T16:11:09+02:00 New Revision: 1815d62d7c43455c55bfc1842e41a25ea04dcb9b URL: https://github.com/llvm/llvm-project/commit/1815d62d7c43455c55bfc1842e41a25ea04dcb9b DIFF: https://github.com/llvm/llvm-project/commit/1815d62d7c43455c55bfc1842e41a25ea04dcb9b.diff LOG: [lldb] Add more logging to a unit test to debug problems with #137978. Added: Modified: lldb/unittests/Host/HostTest.cpp Removed: ################################################################################ diff --git a/lldb/unittests/Host/HostTest.cpp b/lldb/unittests/Host/HostTest.cpp index 9e4390a48fb18..52224bfd28e61 100644 --- a/lldb/unittests/Host/HostTest.cpp +++ b/lldb/unittests/Host/HostTest.cpp @@ -106,6 +106,10 @@ TEST(Host, LaunchProcessDuplicatesHandle) { Pipe pipe; ASSERT_THAT_ERROR(pipe.CreateNew(/*child_process_inherit=*/false).takeError(), llvm::Succeeded()); + SCOPED_TRACE(llvm::formatv("Pipe handles are: {0}/{1}", + (uint64_t)pipe.GetReadPipe(), + (uint64_t)pipe.GetWritePipe()) + .str()); ProcessLaunchInfo info; info.SetExecutableFile(FileSpec(TestMainArgv0), /*add_exe_file_as_first_arg=*/true); From lldb-commits at lists.llvm.org Wed May 7 07:54:01 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 07:54:01 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fix dynamic type resolutions for core files (PR #138698) In-Reply-To: Message-ID: <681b7409.a70a0220.115f50.eb71@mx.google.com> labath wrote: Oh, it's [this one](https://github.com/llvm/llvm-project/blob/3feb8b42e973f935883bc9e779645ecdae1a586d/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp#L547). That makes more sense now. I'll skip the test on arm. Thanks for catching that. https://github.com/llvm/llvm-project/pull/138698 From lldb-commits at lists.llvm.org Wed May 7 07:55:34 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Wed, 07 May 2025 07:55:34 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fix dynamic type resolutions for core files (PR #138698) In-Reply-To: Message-ID: <681b7466.170a0220.38dc02.46a5@mx.google.com> DavidSpickett wrote: Yep, called from AddThreadList: ``` #0 0xffffffffedfbe37c llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/home/david.spickett/build-llvm-arm/local/lib/python3.10/dist-packages/lldb/_lldb.cpython-310-arm-linux-gnueabihf.so+0xdbe37c) #1 0xffffffffedfbca00 llvm::sys::RunSignalHandlers() (/home/david.spickett/build-llvm-arm/local/lib/python3.10/dist-packages/lldb/_lldb.cpython-310-arm-linux-gnueabihf.so+0xdbca00) #2 0xffffffffedfbcb44 SignalHandler(int, siginfo_t*, void*) Signals.cpp:0:0 #3 0xfffffffff7c3d6f0 __default_rt_sa_restorer ./signal/../sysdeps/unix/sysv/linux/arm/sigrestorer.S:80:0 #4 0xffffffffedd87604 MinidumpFileBuilder::AddThreadList() (/home/david.spickett/build-llvm-arm/local/lib/python3.10/dist-packages/lldb/_lldb.cpython-310-arm-linux-gnueabihf.so+0xb87604) ``` https://github.com/llvm/llvm-project/pull/138698 From lldb-commits at lists.llvm.org Wed May 7 07:58:21 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 07:58:21 -0700 (PDT) Subject: [Lldb-commits] [lldb] 461ba2d - [lldb] XFAIL TestDynamicValue.py:test_from_core_file on arm(32) Message-ID: <681b750d.050a0220.3ab5cd.0573@mx.google.com> Author: Pavel Labath Date: 2025-05-07T16:57:30+02:00 New Revision: 461ba2db5d15793d18a5c18ce417c30e335602cc URL: https://github.com/llvm/llvm-project/commit/461ba2db5d15793d18a5c18ce417c30e335602cc DIFF: https://github.com/llvm/llvm-project/commit/461ba2db5d15793d18a5c18ce417c30e335602cc.diff LOG: [lldb] XFAIL TestDynamicValue.py:test_from_core_file on arm(32) Minidump saving is not implemented there. Added: Modified: lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py Removed: ################################################################################ diff --git a/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py b/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py index 97d539f7a807c..cd95a9ff3fe8c 100644 --- a/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py +++ b/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py @@ -283,6 +283,7 @@ def test_from_forward_decl(self): @no_debug_info_test @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24663") @expectedFailureDarwin # dynamic loader unloads modules + @expectedFailureAll(archs=["arm"]) # Minidump saving not implemented def test_from_core_file(self): """Test fetching C++ dynamic values from core files. Specifically, test that we can determine the dynamic type of the value if the core file From lldb-commits at lists.llvm.org Wed May 7 08:14:47 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Wed, 07 May 2025 08:14:47 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Change the launch sequence (PR #138219) In-Reply-To: Message-ID: <681b78e7.170a0220.6f572.ac17@mx.google.com> JDevlieghere wrote: Looking at the logs, the common theme appears to be that all the test binaries exit before they hit any breakpoints. My best guess as to why this is only happening on Windows is the way the dynamic loader there works. If that's the case, then it means that those tests are missing necessary synchronization and we just happen to get away with it on Linux and Darwin. https://github.com/llvm/llvm-project/pull/138219 From lldb-commits at lists.llvm.org Wed May 7 08:28:42 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 08:28:42 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Call Target::ClearAllLoadedSections earlier (PR #138892) Message-ID: https://github.com/labath created https://github.com/llvm/llvm-project/pull/138892 Minidump files contain explicit information about load addresses of modules, so it can load them itself. This works on other platforms, but fails on darwin because DynamicLoaderDarwin nukes the loaded module list on initialization (which happens after the core file plugin has done its work). This used to work until #109477, which enabled the dynamic loader plugins for minidump files in order to get them to provide access to TLS. Clearing the load list makes sense, but I think we could do it earlier in the process, so that both Process and DynamicLoader plugins get a chance to load modules. This patch does that by calling the function early in the launch/attach/load core flows. This fixes TestDynamicValue.py:test_from_core_file on darwin. >From 8e2e42ee48cdd660214872e25dcf4ab64445f413 Mon Sep 17 00:00:00 2001 From: Pavel Labath Date: Wed, 7 May 2025 11:12:22 +0200 Subject: [PATCH] [lldb] Call Target::ClearAllLoadedSections earlier Minidump files contain explicit information about load addresses of modules, so it can load them itself. This works on other platforms, but fails on darwin because DynamicLoaderDarwin nukes the loaded module list on initialization (which happens after the core file plugin has done its work). This used to work until #109477, which enabled the dynamic loader plugins for minidump files in order to get them to provide access to TLS. Clearing the load list makes sense, but I think we could do it earlier in the process, so that both Process and DynamicLoader plugins get a chance to load modules. This patch does that by calling the function early in the launch/attach/load core flows. This fixes TestDynamicValue.py:test_from_core_file on darwin. --- .../Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp | 1 - lldb/source/Target/Process.cpp | 4 ++++ lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py | 1 - 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index e25c4ff55e408..8bf01aa168342 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -871,7 +871,6 @@ void DynamicLoaderDarwin::PrivateInitialize(Process *process) { StateAsCString(m_process->GetState())); Clear(true); m_process = process; - m_process->GetTarget().ClearAllLoadedSections(); } // Member function that gets called when the process state changes. diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index 13ff12b4ff953..7c5512598bbb6 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -2763,6 +2763,7 @@ Status Process::LaunchPrivate(ProcessLaunchInfo &launch_info, StateType &state, } if (state == eStateStopped || state == eStateCrashed) { + GetTarget().ClearAllLoadedSections(); DidLaunch(); // Now that we know the process type, update its signal responses from the @@ -2799,6 +2800,7 @@ Status Process::LaunchPrivate(ProcessLaunchInfo &launch_info, StateType &state, } Status Process::LoadCore() { + GetTarget().ClearAllLoadedSections(); Status error = DoLoadCore(); if (error.Success()) { ListenerSP listener_sp( @@ -3094,6 +3096,8 @@ void Process::CompleteAttach() { Log *log(GetLog(LLDBLog::Process | LLDBLog::Target)); LLDB_LOGF(log, "Process::%s()", __FUNCTION__); + GetTarget().ClearAllLoadedSections(); + // Let the process subclass figure out at much as it can about the process // before we go looking for a dynamic loader plug-in. ArchSpec process_arch; diff --git a/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py b/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py index cd95a9ff3fe8c..faa35421ff60b 100644 --- a/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py +++ b/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py @@ -282,7 +282,6 @@ def test_from_forward_decl(self): @no_debug_info_test @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24663") - @expectedFailureDarwin # dynamic loader unloads modules @expectedFailureAll(archs=["arm"]) # Minidump saving not implemented def test_from_core_file(self): """Test fetching C++ dynamic values from core files. Specifically, test From lldb-commits at lists.llvm.org Wed May 7 08:29:23 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 08:29:23 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Call Target::ClearAllLoadedSections earlier (PR #138892) In-Reply-To: Message-ID: <681b7c53.050a0220.339f90.f6c8@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Pavel Labath (labath)
Changes Minidump files contain explicit information about load addresses of modules, so it can load them itself. This works on other platforms, but fails on darwin because DynamicLoaderDarwin nukes the loaded module list on initialization (which happens after the core file plugin has done its work). This used to work until #109477, which enabled the dynamic loader plugins for minidump files in order to get them to provide access to TLS. Clearing the load list makes sense, but I think we could do it earlier in the process, so that both Process and DynamicLoader plugins get a chance to load modules. This patch does that by calling the function early in the launch/attach/load core flows. This fixes TestDynamicValue.py:test_from_core_file on darwin. --- Full diff: https://github.com/llvm/llvm-project/pull/138892.diff 3 Files Affected: - (modified) lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp (-1) - (modified) lldb/source/Target/Process.cpp (+4) - (modified) lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py (-1) ``````````diff diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index e25c4ff55e408..8bf01aa168342 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -871,7 +871,6 @@ void DynamicLoaderDarwin::PrivateInitialize(Process *process) { StateAsCString(m_process->GetState())); Clear(true); m_process = process; - m_process->GetTarget().ClearAllLoadedSections(); } // Member function that gets called when the process state changes. diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index 13ff12b4ff953..7c5512598bbb6 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -2763,6 +2763,7 @@ Status Process::LaunchPrivate(ProcessLaunchInfo &launch_info, StateType &state, } if (state == eStateStopped || state == eStateCrashed) { + GetTarget().ClearAllLoadedSections(); DidLaunch(); // Now that we know the process type, update its signal responses from the @@ -2799,6 +2800,7 @@ Status Process::LaunchPrivate(ProcessLaunchInfo &launch_info, StateType &state, } Status Process::LoadCore() { + GetTarget().ClearAllLoadedSections(); Status error = DoLoadCore(); if (error.Success()) { ListenerSP listener_sp( @@ -3094,6 +3096,8 @@ void Process::CompleteAttach() { Log *log(GetLog(LLDBLog::Process | LLDBLog::Target)); LLDB_LOGF(log, "Process::%s()", __FUNCTION__); + GetTarget().ClearAllLoadedSections(); + // Let the process subclass figure out at much as it can about the process // before we go looking for a dynamic loader plug-in. ArchSpec process_arch; diff --git a/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py b/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py index cd95a9ff3fe8c..faa35421ff60b 100644 --- a/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py +++ b/lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py @@ -282,7 +282,6 @@ def test_from_forward_decl(self): @no_debug_info_test @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24663") - @expectedFailureDarwin # dynamic loader unloads modules @expectedFailureAll(archs=["arm"]) # Minidump saving not implemented def test_from_core_file(self): """Test fetching C++ dynamic values from core files. Specifically, test ``````````
https://github.com/llvm/llvm-project/pull/138892 From lldb-commits at lists.llvm.org Wed May 7 08:46:34 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 08:46:34 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fix offset computation in RegisterContextUnwind (PR #137155) In-Reply-To: Message-ID: <681b805a.050a0220.2b11bb.0dc7@mx.google.com> https://github.com/labath updated https://github.com/llvm/llvm-project/pull/137155 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Wed May 7 08:46:50 2025 From: lldb-commits at lists.llvm.org (Pavel Labath via lldb-commits) Date: Wed, 07 May 2025 08:46:50 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fix offset computation in RegisterContextUnwind (PR #137155) In-Reply-To: Message-ID: <681b806a.170a0220.3296a6.c055@mx.google.com> https://github.com/labath edited https://github.com/llvm/llvm-project/pull/137155 From lldb-commits at lists.llvm.org Wed May 7 09:06:01 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Wed, 07 May 2025 09:06:01 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add IsCoreDumping to ProcessInstanceInfo (PR #138580) In-Reply-To: Message-ID: <681b84e9.050a0220.2075d.d5fc@mx.google.com> https://github.com/Jlalond closed https://github.com/llvm/llvm-project/pull/138580 From lldb-commits at lists.llvm.org Wed May 7 09:05:57 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 09:05:57 -0700 (PDT) Subject: [Lldb-commits] [lldb] 1ad57b5 - [LLDB] Add IsCoreDumping to ProcessInstanceInfo (#138580) Message-ID: <681b84e5.170a0220.199f8e.7536@mx.google.com> Author: Jacob Lalonde Date: 2025-05-07T09:05:53-07:00 New Revision: 1ad57b58d6ba53be99bd6f2fa928a126c9deb91c URL: https://github.com/llvm/llvm-project/commit/1ad57b58d6ba53be99bd6f2fa928a126c9deb91c DIFF: https://github.com/llvm/llvm-project/commit/1ad57b58d6ba53be99bd6f2fa928a126c9deb91c.diff LOG: [LLDB] Add IsCoreDumping to ProcessInstanceInfo (#138580) This is the first useful patch in the series related to enabling `PTRACE_SEIZE` for processes Coredumping. In order to make the decision if we want to seize or attach, we need to expose that in processinfo. Which we acquire by reading it from `/proc/pid/status` Note that in status it is `CoreDumping` not `Coredumping`, so I kept with that, even if I prefer `Coredumping` Added: Modified: lldb/include/lldb/Utility/ProcessInfo.h lldb/source/Host/linux/Host.cpp lldb/unittests/Host/posix/HostTest.cpp Removed: ################################################################################ diff --git a/lldb/include/lldb/Utility/ProcessInfo.h b/lldb/include/lldb/Utility/ProcessInfo.h index 78ade4bbb1ee6..24041faad80bf 100644 --- a/lldb/include/lldb/Utility/ProcessInfo.h +++ b/lldb/include/lldb/Utility/ProcessInfo.h @@ -247,6 +247,11 @@ class ProcessInstanceInfo : public ProcessInfo { std::optional IsZombie() const { return m_zombie; } + // proc/../status specifies CoreDumping as the field + // so we match the case here. + void SetIsCoreDumping(bool is_coredumping) { m_coredumping = is_coredumping; } + std::optional IsCoreDumping() const { return m_coredumping; } + void Dump(Stream &s, UserIDResolver &resolver) const; static void DumpTableHeader(Stream &s, bool show_args, bool verbose); @@ -266,6 +271,7 @@ class ProcessInstanceInfo : public ProcessInfo { struct timespec m_cumulative_system_time; std::optional m_priority_value = std::nullopt; std::optional m_zombie = std::nullopt; + std::optional m_coredumping = std::nullopt; }; typedef std::vector ProcessInstanceInfoList; diff --git a/lldb/source/Host/linux/Host.cpp b/lldb/source/Host/linux/Host.cpp index 8b475a7ab5003..b5f050426d88b 100644 --- a/lldb/source/Host/linux/Host.cpp +++ b/lldb/source/Host/linux/Host.cpp @@ -213,6 +213,11 @@ static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo, } else if (Line.consume_front("Tgid:")) { Line = Line.ltrim(); Line.consumeInteger(10, Tgid); + } else if (Line.consume_front("CoreDumping:")) { + uint32_t coredumping; + Line = Line.ltrim(); + if (!Line.consumeInteger(2, coredumping)) + ProcessInfo.SetIsCoreDumping(coredumping); } } return true; diff --git a/lldb/unittests/Host/posix/HostTest.cpp b/lldb/unittests/Host/posix/HostTest.cpp index 5d50de3524d1e..082edccf4e774 100644 --- a/lldb/unittests/Host/posix/HostTest.cpp +++ b/lldb/unittests/Host/posix/HostTest.cpp @@ -115,5 +115,8 @@ TEST_F(HostTest, GetProcessInfoSetsPriority) { } ASSERT_TRUE(Info.IsZombie().has_value()); ASSERT_FALSE(Info.IsZombie().value()); + + ASSERT_TRUE(Info.IsCoreDumping().has_value()); + ASSERT_FALSE(Info.IsCoreDumping().value()); } #endif From lldb-commits at lists.llvm.org Wed May 7 09:07:13 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Wed, 07 May 2025 09:07:13 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Ensure we acquire the SB API lock while handling requests. (PR #137026) In-Reply-To: Message-ID: <681b8531.170a0220.25a0ec.7071@mx.google.com> ashgti wrote: So, I was checking this and I see `lldb_dap::DAP::ResetDebuggerState() (/data/users/jeffreytan/llvm-sand/external/llvm-project/lldb/tools/lldb-dap/DAP.cpp:1411)` in the call stack. That function doesn't exist in the open source version of lldb-dap. I'm not sure what its doing but it looks like its trying to join the event thread. The `DAP::Loop` call is already cleaning up the event handler when it exits (here is the handler https://github.com/llvm/llvm-project/blob/32752913b12103431dc392242c3c808afb70bd15/lldb/tools/lldb-dap/DAP.cpp#L935-L939). I'm not sure I have enough information on this deadlock. Is the `ResetDebuggerState` function open sourced somewhere that you could share? Or have you considered using the server mode flag to have lldb-dap manage multiple sessions? https://github.com/llvm/llvm-project/pull/137026 From lldb-commits at lists.llvm.org Wed May 7 10:01:26 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Wed, 07 May 2025 10:01:26 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Change the launch sequence (PR #138219) In-Reply-To: Message-ID: <681b91e6.630a0220.88337.929d@mx.google.com> JDevlieghere wrote: The following two tests are both setting breakpoints without stopping at entry, which means that we could have run past the breakpoint by the time it's set: ``` lldb-api :: tools/lldb-dap/completions/TestDAP_completions.py lldb-api :: tools/lldb-dap/startDebugging/TestDAP_startDebugging.py ``` https://github.com/llvm/llvm-project/pull/138219 From lldb-commits at lists.llvm.org Wed May 7 10:19:12 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Wed, 07 May 2025 10:19:12 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Use -Wno-documentation-deprecated-sync if available (PR #138909) In-Reply-To: Message-ID: <681b9610.170a0220.c5ef.8cc9@mx.google.com> ================ @@ -16,6 +16,15 @@ if (CXX_SUPPORTS_DOCUMENTATION) PRIVATE -Wdocumentation) endif() +# Apply -Wno-documentation-deprecated-sync while we migrate away from +# report_fatal_error in llvm/include/llvm/Support/ErrorHandling.h +# and llvm/include/llvm/Support/Error.h. ---------------- bulbazord wrote: Is there an associated Issue on Github you could add here? If the migration completes and we forget to clean this up, how would a future contributor know if this migration finished? https://github.com/llvm/llvm-project/pull/138909 From lldb-commits at lists.llvm.org Wed May 7 10:25:44 2025 From: lldb-commits at lists.llvm.org (Kazu Hirata via lldb-commits) Date: Wed, 07 May 2025 10:25:44 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Use -Wno-documentation-deprecated-sync if available (PR #138909) In-Reply-To: Message-ID: <681b9798.630a0220.307541.87f5@mx.google.com> ================ @@ -16,6 +16,15 @@ if (CXX_SUPPORTS_DOCUMENTATION) PRIVATE -Wdocumentation) endif() +# Apply -Wno-documentation-deprecated-sync while we migrate away from +# report_fatal_error in llvm/include/llvm/Support/ErrorHandling.h +# and llvm/include/llvm/Support/Error.h. ---------------- kazutakahirata wrote: Good point! I just filed #138914. Let me update this PR to mention the issue. Thanks! https://github.com/llvm/llvm-project/pull/138909 From lldb-commits at lists.llvm.org Wed May 7 10:47:51 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Wed, 07 May 2025 10:47:51 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Fix the package.json and format lldb-dap typescript. (PR #138918) In-Reply-To: Message-ID: <681b9cc7.170a0220.20922a.4e17@mx.google.com> https://github.com/ashgti updated https://github.com/llvm/llvm-project/pull/138918 >From 05b709033990e6cdfd7cb9fdbd7e8eab260e7a62 Mon Sep 17 00:00:00 2001 From: John Harrison Date: Wed, 7 May 2025 10:46:55 -0700 Subject: [PATCH] [lldb-dap] Fix package.json after a bad merge. The package.json is currently malformed after a bad merge in 39e6e888a8155583713e1b8b256119a2be7902e0. --- lldb/tools/lldb-dap/package.json | 982 +++++++++++++++---------------- 1 file changed, 491 insertions(+), 491 deletions(-) diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json index 4734c9d7277bb..f66badc2a930f 100644 --- a/lldb/tools/lldb-dap/package.json +++ b/lldb/tools/lldb-dap/package.json @@ -242,527 +242,527 @@ } } } - ] - }, - "breakpoints": [ - { - "language": "ada" - }, - { - "language": "arm" - }, - { - "language": "asm" - }, - { - "language": "c" - }, - { - "language": "cpp" - }, - { - "language": "crystal" - }, - { - "language": "d" - }, - { - "language": "fortan" - }, - { - "language": "fortran-modern" - }, - { - "language": "nim" - }, - { - "language": "objective-c" - }, - { - "language": "objectpascal" - }, - { - "language": "pascal" - }, - { - "language": "rust" - }, - { - "language": "swift" - } - ], - "debuggers": [ - { - "type": "lldb-dap", - "label": "LLDB DAP Debugger", - "configurationAttributes": { - "launch": { - "required": [ - "program" - ], - "properties": { - "debugAdapterHostname": { - "type": "string", - "markdownDescription": "The hostname that an existing lldb-dap executable is listening on." - }, - "debugAdapterPort": { - "type": "number", - "markdownDescription": "The port that an existing lldb-dap executable is listening on." - }, - "debugAdapterExecutable": { - "type": "string", - "markdownDescription": "The absolute path to the LLDB debug adapter executable to use. Overrides any user or workspace settings." - }, - "debugAdapterArgs": { - "type": "array", - "items": { - "type": "string" + ], + "breakpoints": [ + { + "language": "ada" + }, + { + "language": "arm" + }, + { + "language": "asm" + }, + { + "language": "c" + }, + { + "language": "cpp" + }, + { + "language": "crystal" + }, + { + "language": "d" + }, + { + "language": "fortan" + }, + { + "language": "fortran-modern" + }, + { + "language": "nim" + }, + { + "language": "objective-c" + }, + { + "language": "objectpascal" + }, + { + "language": "pascal" + }, + { + "language": "rust" + }, + { + "language": "swift" + } + ], + "debuggers": [ + { + "type": "lldb-dap", + "label": "LLDB DAP Debugger", + "configurationAttributes": { + "launch": { + "required": [ + "program" + ], + "properties": { + "debugAdapterHostname": { + "type": "string", + "markdownDescription": "The hostname that an existing lldb-dap executable is listening on." }, - "markdownDescription": "The list of additional arguments used to launch the debug adapter executable. Overrides any user or workspace settings." - }, - "program": { - "type": "string", - "description": "Path to the program to debug." - }, - "args": { - "type": [ - "array" - ], - "items": { - "type": "string" - }, - "description": "Program arguments.", - "default": [] - }, - "cwd": { - "type": "string", - "description": "Program working directory.", - "default": "${workspaceRoot}" - }, - "env": { - "anyOf": [ - { - "type": "object", - "description": "Additional environment variables to set when launching the program. E.g. `{ \"FOO\": \"1\" }`", - "patternProperties": { - ".*": { - "type": "string" - } - }, - "default": {} + "debugAdapterPort": { + "type": "number", + "markdownDescription": "The port that an existing lldb-dap executable is listening on." + }, + "debugAdapterExecutable": { + "type": "string", + "markdownDescription": "The absolute path to the LLDB debug adapter executable to use. Overrides any user or workspace settings." + }, + "debugAdapterArgs": { + "type": "array", + "items": { + "type": "string" }, - { - "type": "array", - "description": "Additional environment variables to set when launching the program. E.g. `[\"FOO=1\", \"BAR\"]`", - "items": { - "type": "string", - "pattern": "^((\\w+=.*)|^\\w+)$" - }, - "default": [] - } - ] - }, - "stopOnEntry": { - "type": "boolean", - "description": "Automatically stop after launch.", - "default": false - }, - "disableASLR": { - "type": "boolean", - "description": "Enable or disable Address space layout randomization if the debugger supports it.", - "default": true - }, - "disableSTDIO": { - "type": "boolean", - "description": "Don't retrieve STDIN, STDOUT and STDERR as the program is running.", - "default": false - }, - "shellExpandArguments": { - "type": "boolean", - "description": "Expand program arguments as a shell would without actually launching the program in a shell.", - "default": false - }, - "detachOnError": { - "type": "boolean", - "description": "Detach from the program.", - "default": false - }, - "sourcePath": { - "type": "string", - "description": "Specify a source path to remap \"./\" to allow full paths to be used when setting breakpoints in binaries that have relative source paths." - }, - "sourceMap": { - "anyOf": [ - { - "type": "object", - "description": "Specify an object of path remappings; each entry has a key containing the source path and a value containing the destination path. E.g `{ \"/the/source/path\": \"/the/destination/path\" }`. Overrides sourcePath.", - "patternProperties": { - ".*": { - "type": "string" - } - }, - "default": {} + "markdownDescription": "The list of additional arguments used to launch the debug adapter executable. Overrides any user or workspace settings." + }, + "program": { + "type": "string", + "description": "Path to the program to debug." + }, + "args": { + "type": [ + "array" + ], + "items": { + "type": "string" }, - { - "type": "array", - "description": "Specify an array of path remappings; each element must itself be a two element array containing a source and destination path name. Overrides sourcePath.", - "items": { + "description": "Program arguments.", + "default": [] + }, + "cwd": { + "type": "string", + "description": "Program working directory.", + "default": "${workspaceRoot}" + }, + "env": { + "anyOf": [ + { + "type": "object", + "description": "Additional environment variables to set when launching the program. E.g. `{ \"FOO\": \"1\" }`", + "patternProperties": { + ".*": { + "type": "string" + } + }, + "default": {} + }, + { "type": "array", - "minItems": 2, - "maxItems": 2, + "description": "Additional environment variables to set when launching the program. E.g. `[\"FOO=1\", \"BAR\"]`", "items": { - "type": "string" - } + "type": "string", + "pattern": "^((\\w+=.*)|^\\w+)$" + }, + "default": [] + } + ] + }, + "stopOnEntry": { + "type": "boolean", + "description": "Automatically stop after launch.", + "default": false + }, + "disableASLR": { + "type": "boolean", + "description": "Enable or disable Address space layout randomization if the debugger supports it.", + "default": true + }, + "disableSTDIO": { + "type": "boolean", + "description": "Don't retrieve STDIN, STDOUT and STDERR as the program is running.", + "default": false + }, + "shellExpandArguments": { + "type": "boolean", + "description": "Expand program arguments as a shell would without actually launching the program in a shell.", + "default": false + }, + "detachOnError": { + "type": "boolean", + "description": "Detach from the program.", + "default": false + }, + "sourcePath": { + "type": "string", + "description": "Specify a source path to remap \"./\" to allow full paths to be used when setting breakpoints in binaries that have relative source paths." + }, + "sourceMap": { + "anyOf": [ + { + "type": "object", + "description": "Specify an object of path remappings; each entry has a key containing the source path and a value containing the destination path. E.g `{ \"/the/source/path\": \"/the/destination/path\" }`. Overrides sourcePath.", + "patternProperties": { + ".*": { + "type": "string" + } + }, + "default": {} }, - "default": [] - } - ] - }, - "debuggerRoot": { - "type": "string", - "description": "Specify a working directory to set the debug adapter to so relative object files can be located." - }, - "targetTriple": { - "type": "string", - "description": "Triplet of the target architecture to override value derived from the program file." - }, - "platformName": { - "type": "string", - "description": "Name of the execution platform to override value derived from the program file." - }, - "initCommands": { - "type": "array", - "items": { - "type": "string" + { + "type": "array", + "description": "Specify an array of path remappings; each element must itself be a two element array containing a source and destination path name. Overrides sourcePath.", + "items": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "string" + } + }, + "default": [] + } + ] }, - "description": "Initialization commands executed upon debugger startup.", - "default": [] - }, - "preRunCommands": { - "type": "array", - "items": { - "type": "string" + "debuggerRoot": { + "type": "string", + "description": "Specify a working directory to set the debug adapter to so relative object files can be located." }, - "description": "Commands executed just before the program is launched.", - "default": [] - }, - "postRunCommands": { - "type": "array", - "items": { - "type": "string" + "targetTriple": { + "type": "string", + "description": "Triplet of the target architecture to override value derived from the program file." }, - "description": "Commands executed just as soon as the program is successfully launched when it's in a stopped state prior to any automatic continuation.", - "default": [] - }, - "launchCommands": { - "type": "array", - "items": { - "type": "string" + "platformName": { + "type": "string", + "description": "Name of the execution platform to override value derived from the program file." }, - "description": "Custom commands that are executed instead of launching a process. A target will be created with the launch arguments prior to executing these commands. The commands may optionally create a new target and must perform a launch. A valid process must exist after these commands complete or the \"launch\" will fail. Launch the process with \"process launch -s\" to make the process to at the entry point since lldb-dap will auto resume if necessary.", - "default": [] - }, - "stopCommands": { - "type": "array", - "items": { - "type": "string" + "initCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Initialization commands executed upon debugger startup.", + "default": [] }, - "description": "Commands executed each time the program stops.", - "default": [] - }, - "exitCommands": { - "type": "array", - "items": { - "type": "string" + "preRunCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Commands executed just before the program is launched.", + "default": [] }, - "description": "Commands executed when the program exits.", - "default": [] - }, - "terminateCommands": { - "type": "array", - "items": { - "type": "string" + "postRunCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Commands executed just as soon as the program is successfully launched when it's in a stopped state prior to any automatic continuation.", + "default": [] }, - "description": "Commands executed when the debugging session ends.", - "default": [] - }, - "runInTerminal": { - "type": "boolean", - "description": "Launch the program inside an integrated terminal in the IDE. Useful for debugging interactive command line programs", - "default": false - }, - "timeout": { - "type": "number", - "description": "The time in seconds to wait for a program to stop at entry point when launching with \"launchCommands\". Defaults to 30 seconds." - }, - "enableAutoVariableSummaries": { - "type": "boolean", - "description": "Enable auto generated summaries for variables when no summaries exist for a given type. This feature can cause performance delays in large projects when viewing variables.", - "default": false - }, - "displayExtendedBacktrace": { - "type": "boolean", - "description": "Enable language specific extended backtraces.", - "default": false - }, - "enableSyntheticChildDebugging": { - "type": "boolean", - "description": "If a variable is displayed using a synthetic children, also display the actual contents of the variable at the end under a [raw] entry. This is useful when creating sythetic child plug-ins as it lets you see the actual contents of the variable.", - "default": false - }, - "commandEscapePrefix": { - "type": "string", - "description": "The escape prefix to use for executing regular LLDB commands in the Debug Console, instead of printing variables. Defaults to a back-tick (`). If it's an empty string, then all expression in the Debug Console are treated as regular LLDB commands.", - "default": "`" - }, - "customFrameFormat": { - "type": "string", - "description": "If non-empty, stack frames will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for frames. If the format string contains errors, an error message will be displayed on the Debug Console and the default frame names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.", - "default": "" - }, - "customThreadFormat": { - "type": "string", - "description": "If non-empty, threads will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for threads. If the format string contains errors, an error message will be displayed on the Debug Console and the default thread names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.", - "default": "" + "launchCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Custom commands that are executed instead of launching a process. A target will be created with the launch arguments prior to executing these commands. The commands may optionally create a new target and must perform a launch. A valid process must exist after these commands complete or the \"launch\" will fail. Launch the process with \"process launch -s\" to make the process to at the entry point since lldb-dap will auto resume if necessary.", + "default": [] + }, + "stopCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Commands executed each time the program stops.", + "default": [] + }, + "exitCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Commands executed when the program exits.", + "default": [] + }, + "terminateCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Commands executed when the debugging session ends.", + "default": [] + }, + "runInTerminal": { + "type": "boolean", + "description": "Launch the program inside an integrated terminal in the IDE. Useful for debugging interactive command line programs", + "default": false + }, + "timeout": { + "type": "number", + "description": "The time in seconds to wait for a program to stop at entry point when launching with \"launchCommands\". Defaults to 30 seconds." + }, + "enableAutoVariableSummaries": { + "type": "boolean", + "description": "Enable auto generated summaries for variables when no summaries exist for a given type. This feature can cause performance delays in large projects when viewing variables.", + "default": false + }, + "displayExtendedBacktrace": { + "type": "boolean", + "description": "Enable language specific extended backtraces.", + "default": false + }, + "enableSyntheticChildDebugging": { + "type": "boolean", + "description": "If a variable is displayed using a synthetic children, also display the actual contents of the variable at the end under a [raw] entry. This is useful when creating sythetic child plug-ins as it lets you see the actual contents of the variable.", + "default": false + }, + "commandEscapePrefix": { + "type": "string", + "description": "The escape prefix to use for executing regular LLDB commands in the Debug Console, instead of printing variables. Defaults to a back-tick (`). If it's an empty string, then all expression in the Debug Console are treated as regular LLDB commands.", + "default": "`" + }, + "customFrameFormat": { + "type": "string", + "description": "If non-empty, stack frames will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for frames. If the format string contains errors, an error message will be displayed on the Debug Console and the default frame names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.", + "default": "" + }, + "customThreadFormat": { + "type": "string", + "description": "If non-empty, threads will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for threads. If the format string contains errors, an error message will be displayed on the Debug Console and the default thread names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.", + "default": "" + } } - } - }, - "attach": { - "properties": { - "debugAdapterHostname": { - "type": "string", - "markdownDescription": "The hostname that an existing lldb-dap executable is listening on." - }, - "debugAdapterPort": { - "type": "number", - "markdownDescription": "The port that an existing lldb-dap executable is listening on." - }, - "debugAdapterExecutable": { - "type": "string", - "markdownDescription": "The absolute path to the LLDB debug adapter executable to use. Overrides any user or workspace settings." - }, - "debugAdapterArgs": { - "type": "array", - "items": { - "type": "string" + }, + "attach": { + "properties": { + "debugAdapterHostname": { + "type": "string", + "markdownDescription": "The hostname that an existing lldb-dap executable is listening on." }, - "markdownDescription": "The list of additional arguments used to launch the debug adapter executable. Overrides any user or workspace settings." - }, - "program": { - "type": "string", - "description": "Path to the program to attach to." - }, - "pid": { - "type": [ - "number", - "string" - ], - "description": "System process ID to attach to." - }, - "waitFor": { - "type": "boolean", - "description": "If set to true, then wait for the process to launch by looking for a process with a basename that matches `program`. No process ID needs to be specified when using this flag.", - "default": true - }, - "sourcePath": { - "type": "string", - "description": "Specify a source path to remap \"./\" to allow full paths to be used when setting breakpoints in binaries that have relative source paths." - }, - "sourceMap": { - "anyOf": [ - { - "type": "object", - "description": "Specify an object of path remappings; each entry has a key containing the source path and a value containing the destination path. E.g `{ \"/the/source/path\": \"/the/destination/path\" }`. Overrides sourcePath.", - "patternProperties": { - ".*": { - "type": "string" - } - }, - "default": {} + "debugAdapterPort": { + "type": "number", + "markdownDescription": "The port that an existing lldb-dap executable is listening on." + }, + "debugAdapterExecutable": { + "type": "string", + "markdownDescription": "The absolute path to the LLDB debug adapter executable to use. Overrides any user or workspace settings." + }, + "debugAdapterArgs": { + "type": "array", + "items": { + "type": "string" }, - { - "type": "array", - "description": "Specify an array of path remappings; each element must itself be a two element array containing a source and destination path name. Overrides sourcePath.", - "items": { + "markdownDescription": "The list of additional arguments used to launch the debug adapter executable. Overrides any user or workspace settings." + }, + "program": { + "type": "string", + "description": "Path to the program to attach to." + }, + "pid": { + "type": [ + "number", + "string" + ], + "description": "System process ID to attach to." + }, + "waitFor": { + "type": "boolean", + "description": "If set to true, then wait for the process to launch by looking for a process with a basename that matches `program`. No process ID needs to be specified when using this flag.", + "default": true + }, + "sourcePath": { + "type": "string", + "description": "Specify a source path to remap \"./\" to allow full paths to be used when setting breakpoints in binaries that have relative source paths." + }, + "sourceMap": { + "anyOf": [ + { + "type": "object", + "description": "Specify an object of path remappings; each entry has a key containing the source path and a value containing the destination path. E.g `{ \"/the/source/path\": \"/the/destination/path\" }`. Overrides sourcePath.", + "patternProperties": { + ".*": { + "type": "string" + } + }, + "default": {} + }, + { "type": "array", - "minItems": 2, - "maxItems": 2, + "description": "Specify an array of path remappings; each element must itself be a two element array containing a source and destination path name. Overrides sourcePath.", "items": { - "type": "string" - } - }, - "default": [] - } - ] - }, - "debuggerRoot": { - "type": "string", - "description": "Specify a working directory to set the debug adapter to so relative object files can be located." - }, - "targetTriple": { - "type": "string", - "description": "Triplet of the target architecture to override value derived from the program file." - }, - "platformName": { - "type": "string", - "description": "Name of the execution platform to override value derived from the program file." - }, - "attachCommands": { - "type": "array", - "items": { - "type": "string" + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "string" + } + }, + "default": [] + } + ] }, - "description": "Custom commands that are executed instead of attaching to a process ID or to a process by name. These commands may optionally create a new target and must perform an attach. A valid process must exist after these commands complete or the \"attach\" will fail.", - "default": [] - }, - "initCommands": { - "type": "array", - "items": { - "type": "string" + "debuggerRoot": { + "type": "string", + "description": "Specify a working directory to set the debug adapter to so relative object files can be located." }, - "description": "Initialization commands executed upon debugger startup.", - "default": [] - }, - "preRunCommands": { - "type": "array", - "items": { - "type": "string" + "targetTriple": { + "type": "string", + "description": "Triplet of the target architecture to override value derived from the program file." }, - "description": "Commands executed just before the program is attached to.", - "default": [] - }, - "postRunCommands": { - "type": "array", - "items": { - "type": "string" + "platformName": { + "type": "string", + "description": "Name of the execution platform to override value derived from the program file." }, - "description": "Commands executed just as soon as the program is successfully attached when it's in a stopped state prior to any automatic continuation.", - "default": [] - }, - "stopCommands": { - "type": "array", - "items": { - "type": "string" + "attachCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Custom commands that are executed instead of attaching to a process ID or to a process by name. These commands may optionally create a new target and must perform an attach. A valid process must exist after these commands complete or the \"attach\" will fail.", + "default": [] }, - "description": "Commands executed each time the program stops.", - "default": [] - }, - "exitCommands": { - "type": "array", - "items": { - "type": "string" + "initCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Initialization commands executed upon debugger startup.", + "default": [] }, - "description": "Commands executed when the program exits.", - "default": [] - }, - "terminateCommands": { - "type": "array", - "items": { - "type": "string" + "preRunCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Commands executed just before the program is attached to.", + "default": [] }, - "description": "Commands executed when the debugging session ends.", - "default": [] - }, - "coreFile": { - "type": "string", - "description": "Path to the core file to debug." - }, - "timeout": { - "type": "number", - "description": "The time in seconds to wait for a program to stop when attaching using \"attachCommands\". Defaults to 30 seconds." - }, - "gdb-remote-port": { - "type": [ - "number", - "string" - ], - "description": "TCP/IP port to attach to a remote system. Specifying both pid and port is an error." - }, - "gdb-remote-hostname": { - "type": "string", - "description": "The hostname to connect to a remote system. The default hostname being used localhost." - }, - "enableAutoVariableSummaries": { - "type": "boolean", - "description": "Enable auto generated summaries for variables when no summaries exist for a given type. This feature can cause performance delays in large projects when viewing variables.", - "default": false - }, - "displayExtendedBacktrace": { - "type": "boolean", - "description": "Enable language specific extended backtraces.", - "default": false - }, - "enableSyntheticChildDebugging": { - "type": "boolean", - "description": "If a variable is displayed using a synthetic children, also display the actual contents of the variable at the end under a [raw] entry. This is useful when creating sythetic child plug-ins as it lets you see the actual contents of the variable.", - "default": false - }, - "commandEscapePrefix": { - "type": "string", - "description": "The escape prefix character to use for executing regular LLDB commands in the Debug Console, instead of printing variables. Defaults to a back-tick (`). If empty, then all expression in the Debug Console are treated as regular LLDB commands.", - "default": "`" - }, - "customFrameFormat": { - "type": "string", - "description": "If non-empty, stack frames will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for frames. If the format string contains errors, an error message will be displayed on the Debug Console and the default frame names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.", - "default": "" - }, - "customThreadFormat": { - "type": "string", - "description": "If non-empty, threads will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for threads. If the format string contains errors, an error message will be displayed on the Debug Console and the default thread names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.", - "default": "" + "postRunCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Commands executed just as soon as the program is successfully attached when it's in a stopped state prior to any automatic continuation.", + "default": [] + }, + "stopCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Commands executed each time the program stops.", + "default": [] + }, + "exitCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Commands executed when the program exits.", + "default": [] + }, + "terminateCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Commands executed when the debugging session ends.", + "default": [] + }, + "coreFile": { + "type": "string", + "description": "Path to the core file to debug." + }, + "timeout": { + "type": "number", + "description": "The time in seconds to wait for a program to stop when attaching using \"attachCommands\". Defaults to 30 seconds." + }, + "gdb-remote-port": { + "type": [ + "number", + "string" + ], + "description": "TCP/IP port to attach to a remote system. Specifying both pid and port is an error." + }, + "gdb-remote-hostname": { + "type": "string", + "description": "The hostname to connect to a remote system. The default hostname being used localhost." + }, + "enableAutoVariableSummaries": { + "type": "boolean", + "description": "Enable auto generated summaries for variables when no summaries exist for a given type. This feature can cause performance delays in large projects when viewing variables.", + "default": false + }, + "displayExtendedBacktrace": { + "type": "boolean", + "description": "Enable language specific extended backtraces.", + "default": false + }, + "enableSyntheticChildDebugging": { + "type": "boolean", + "description": "If a variable is displayed using a synthetic children, also display the actual contents of the variable at the end under a [raw] entry. This is useful when creating sythetic child plug-ins as it lets you see the actual contents of the variable.", + "default": false + }, + "commandEscapePrefix": { + "type": "string", + "description": "The escape prefix character to use for executing regular LLDB commands in the Debug Console, instead of printing variables. Defaults to a back-tick (`). If empty, then all expression in the Debug Console are treated as regular LLDB commands.", + "default": "`" + }, + "customFrameFormat": { + "type": "string", + "description": "If non-empty, stack frames will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for frames. If the format string contains errors, an error message will be displayed on the Debug Console and the default frame names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.", + "default": "" + }, + "customThreadFormat": { + "type": "string", + "description": "If non-empty, threads will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for threads. If the format string contains errors, an error message will be displayed on the Debug Console and the default thread names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.", + "default": "" + } } } - } - }, - "initialConfigurations": [ - { - "type": "lldb-dap", - "request": "launch", - "name": "Debug", - "program": "${workspaceRoot}/", - "args": [], - "env": [], - "cwd": "${workspaceRoot}" - } - ], - "configurationSnippets": [ - { - "label": "LLDB: Launch", - "description": "", - "body": { + }, + "initialConfigurations": [ + { "type": "lldb-dap", "request": "launch", - "name": "${2:Launch}", - "program": "^\"\\${workspaceRoot}/${1:}\"", + "name": "Debug", + "program": "${workspaceRoot}/", "args": [], "env": [], - "cwd": "^\"\\${workspaceRoot}\"" - } - }, - { - "label": "LLDB: Attach", - "description": "", - "body": { - "type": "lldb-dap", - "request": "attach", - "name": "${2:Attach}", - "program": "${1:}", - "waitFor": true + "cwd": "${workspaceRoot}" } - }, - { - "label": "LLDB: Load Coredump", - "description": "", - "body": { - "type": "lldb-dap", - "request": "attach", - "name": "${2:Core}", - "program": "${1:}", - "coreFile": "${1:}.core" + ], + "configurationSnippets": [ + { + "label": "LLDB: Launch", + "description": "", + "body": { + "type": "lldb-dap", + "request": "launch", + "name": "${2:Launch}", + "program": "^\"\\${workspaceRoot}/${1:}\"", + "args": [], + "env": [], + "cwd": "^\"\\${workspaceRoot}\"" + } + }, + { + "label": "LLDB: Attach", + "description": "", + "body": { + "type": "lldb-dap", + "request": "attach", + "name": "${2:Attach}", + "program": "${1:}", + "waitFor": true + } + }, + { + "label": "LLDB: Load Coredump", + "description": "", + "body": { + "type": "lldb-dap", + "request": "attach", + "name": "${2:Core}", + "program": "${1:}", + "coreFile": "${1:}.core" + } } - } - ] - } - ] -} \ No newline at end of file + ] + } + ] + } +} From lldb-commits at lists.llvm.org Wed May 7 10:48:10 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Wed, 07 May 2025 10:48:10 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Fix package.json after a bad merge. (PR #138918) In-Reply-To: Message-ID: <681b9cda.170a0220.123fa8.8ee4@mx.google.com> https://github.com/ashgti edited https://github.com/llvm/llvm-project/pull/138918 From lldb-commits at lists.llvm.org Wed May 7 10:48:31 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Wed, 07 May 2025 10:48:31 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Fix package.json after a bad merge. (PR #138918) In-Reply-To: Message-ID: <681b9cef.170a0220.2c5fd2.0cfc@mx.google.com> https://github.com/ashgti edited https://github.com/llvm/llvm-project/pull/138918 From lldb-commits at lists.llvm.org Wed May 7 10:49:40 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Wed, 07 May 2025 10:49:40 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Fix package.json after a bad merge. (PR #138918) In-Reply-To: Message-ID: <681b9d34.170a0220.2958b9.112f@mx.google.com> ashgti wrote: > @ashgti Hey John, do you mind splitting this patch? Sure, I'll make this fixing the package.json. > As for the package.json, do we have any tests that try to npm build? Should we add them? I don't think they're run as part of the CI today. We could see if there is some way to integrate the npm build / tests into the lldb-dap CI somehow. https://github.com/llvm/llvm-project/pull/138918 From lldb-commits at lists.llvm.org Wed May 7 10:52:15 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Wed, 07 May 2025 10:52:15 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Fix package.json after a bad merge. (PR #138918) In-Reply-To: Message-ID: <681b9dcf.a70a0220.161bc.23d8@mx.google.com> https://github.com/Jlalond approved this pull request. Thanks @ashgti! https://github.com/llvm/llvm-project/pull/138918 From lldb-commits at lists.llvm.org Wed May 7 10:54:19 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Wed, 07 May 2025 10:54:19 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Format extension typescript. (PR #138925) Message-ID: https://github.com/ashgti created https://github.com/llvm/llvm-project/pull/138925 I think the format checker isn't checking typescript files. I ran `npm run format` to fix the extenion typescript. >From 389bcb54b146ff707933146d561a272024f415fb Mon Sep 17 00:00:00 2001 From: John Harrison Date: Wed, 7 May 2025 10:52:39 -0700 Subject: [PATCH] [lldb-dap] Format extenison typescript. I think the format checker isn't checking typescript files. I ran `npm run format` to fix the extenion typescript. --- .../src-ts/debug-configuration-provider.ts | 96 ++++++---- .../lldb-dap/src-ts/uri-launch-handler.ts | 168 ++++++++++-------- 2 files changed, 153 insertions(+), 111 deletions(-) diff --git a/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts b/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts index 8a4089008b2f9..c91b101f4a9ba 100644 --- a/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts +++ b/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts @@ -21,79 +21,97 @@ async function isServerModeSupported(exe: string): Promise { } interface BoolConfig { - type: 'boolean'; + type: "boolean"; default: boolean; } interface StringConfig { - type: 'string'; + type: "string"; default: string; } interface NumberConfig { - type: 'number'; + type: "number"; default: number; } interface StringArrayConfig { - type: 'stringArray'; + type: "stringArray"; default: string[]; } -type DefaultConfig = BoolConfig | NumberConfig | StringConfig | StringArrayConfig; +type DefaultConfig = + | BoolConfig + | NumberConfig + | StringConfig + | StringArrayConfig; const configurations: Record = { // Keys for debugger configurations. - "commandEscapePrefix": { type: "string", default: "`" }, - "customFrameFormat": { type: "string", default: "" }, - "customThreadFormat": { type: "string", default: "" }, - "detachOnError": { type: "boolean", default: false }, - "disableASLR": { type: "boolean", default: true }, - "disableSTDIO": { type: "boolean", default: false }, - "displayExtendedBacktrace": { type: "boolean", default: false }, - "enableAutoVariableSummaries": { type: "boolean", default: false }, - "enableSyntheticChildDebugging": { type: "boolean", default: false }, - "timeout": { type: "number", default: 30 }, + commandEscapePrefix: { type: "string", default: "`" }, + customFrameFormat: { type: "string", default: "" }, + customThreadFormat: { type: "string", default: "" }, + detachOnError: { type: "boolean", default: false }, + disableASLR: { type: "boolean", default: true }, + disableSTDIO: { type: "boolean", default: false }, + displayExtendedBacktrace: { type: "boolean", default: false }, + enableAutoVariableSummaries: { type: "boolean", default: false }, + enableSyntheticChildDebugging: { type: "boolean", default: false }, + timeout: { type: "number", default: 30 }, // Keys for platform / target configuration. - "platformName": { type: "string", default: "" }, - "targetTriple": { type: "string", default: "" }, + platformName: { type: "string", default: "" }, + targetTriple: { type: "string", default: "" }, // Keys for debugger command hooks. - "initCommands": { type: "stringArray", default: [] }, - "preRunCommands": { type: "stringArray", default: [] }, - "postRunCommands": { type: "stringArray", default: [] }, - "stopCommands": { type: "stringArray", default: [] }, - "exitCommands": { type: "stringArray", default: [] }, - "terminateCommands": { type: "stringArray", default: [] }, + initCommands: { type: "stringArray", default: [] }, + preRunCommands: { type: "stringArray", default: [] }, + postRunCommands: { type: "stringArray", default: [] }, + stopCommands: { type: "stringArray", default: [] }, + exitCommands: { type: "stringArray", default: [] }, + terminateCommands: { type: "stringArray", default: [] }, }; export class LLDBDapConfigurationProvider - implements vscode.DebugConfigurationProvider { - constructor(private readonly server: LLDBDapServer) { } + implements vscode.DebugConfigurationProvider +{ + constructor(private readonly server: LLDBDapServer) {} async resolveDebugConfiguration( folder: vscode.WorkspaceFolder | undefined, debugConfiguration: vscode.DebugConfiguration, - token?: vscode.CancellationToken): Promise { - let config = vscode.workspace.getConfiguration('lldb-dap.defaults'); + token?: vscode.CancellationToken, + ): Promise { + let config = vscode.workspace.getConfiguration("lldb-dap.defaults"); for (const [key, cfg] of Object.entries(configurations)) { - if (Reflect.has(debugConfiguration, key)) continue; + if (Reflect.has(debugConfiguration, key)) { + continue; + } const value = config.get(key); - if (value === cfg.default) continue; + if (!value || value === cfg.default) { + continue; + } switch (cfg.type) { - case 'string': - if (typeof value !== 'string') + case "string": + if (typeof value !== "string") { throw new Error(`Expected ${key} to be a string, got ${value}`); + } break; - case 'number': - if (typeof value !== 'number') + case "number": + if (typeof value !== "number") { throw new Error(`Expected ${key} to be a number, got ${value}`); + } break; - case 'boolean': - if (typeof value !== 'boolean') + case "boolean": + if (typeof value !== "boolean") { throw new Error(`Expected ${key} to be a boolean, got ${value}`); + } break; - case 'stringArray': - if (typeof value !== 'object' && Array.isArray(value)) - throw new Error(`Expected ${key} to be a array of strings, got ${value}`); - if ((value as string[]).length === 0) continue; + case "stringArray": + if (typeof value !== "object" && Array.isArray(value)) { + throw new Error( + `Expected ${key} to be a array of strings, got ${value}`, + ); + } + if ((value as string[]).length === 0) { + continue; + } break; } diff --git a/lldb/tools/lldb-dap/src-ts/uri-launch-handler.ts b/lldb/tools/lldb-dap/src-ts/uri-launch-handler.ts index 0c3b1e9a00d9e..d45c1820eec75 100644 --- a/lldb/tools/lldb-dap/src-ts/uri-launch-handler.ts +++ b/lldb/tools/lldb-dap/src-ts/uri-launch-handler.ts @@ -1,78 +1,102 @@ import * as vscode from "vscode"; export class LaunchUriHandler implements vscode.UriHandler { - async handleUri(uri: vscode.Uri) { - try { - const params = new URLSearchParams(uri.query); - if (uri.path == '/start') { - // Some properties have default values - let debugConfig: vscode.DebugConfiguration = { - type: 'lldb-dap', - request: 'launch', - name: '', - }; - // The `config` parameter allows providing a complete JSON-encoded configuration - const configJson = params.get("config"); - if (configJson !== null) { - Object.assign(debugConfig, JSON.parse(configJson)); - } - // Furthermore, some frequently used parameters can also be provided as separate parameters - const stringKeys = ["name", "request", "program", "cwd", "debuggerRoot"]; - const numberKeys = ["pid"]; - const arrayKeys = [ - "args", "initCommands", "preRunCommands", "stopCommands", "exitCommands", - "terminateCommands", "launchCommands", "attachCommands" - ]; - for (const key of stringKeys) { - const value = params.get(key); - if (value) { - debugConfig[key] = value; - } - } - for (const key of numberKeys) { - const value = params.get(key); - if (value) { - debugConfig[key] = Number(value); - } - } - for (const key of arrayKeys) { - // `getAll()` returns an array of strings. - const value = params.getAll(key); - if (value) { - debugConfig[key] = value; - } - } - // Report an error if we received any unknown parameters - const supportedKeys = new Set(["config"].concat(stringKeys).concat(numberKeys).concat(arrayKeys)); - const presentKeys = new Set(params.keys()); - // FIXME: Use `Set.difference` as soon as ES2024 is widely available - const unknownKeys = new Set(); - for (const k of presentKeys.keys()) { - if (!supportedKeys.has(k)) { - unknownKeys.add(k); - } - } - if (unknownKeys.size > 0) { - throw new Error(`Unsupported URL parameters: ${Array.from(unknownKeys.keys()).join(", ")}`); - } - // Prodide a default for the config name - const defaultName = debugConfig.request == 'launch' ? "URL-based Launch" : "URL-based Attach"; - debugConfig.name = debugConfig.name || debugConfig.program || defaultName; - // Force the type to `lldb-dap`. We don't want to allow launching any other - // Debug Adapters using this URI scheme. - if (debugConfig.type != "lldb-dap") { - throw new Error(`Unsupported debugger type: ${debugConfig.type}`); - } - await vscode.debug.startDebugging(undefined, debugConfig); - } else { - throw new Error(`Unsupported Uri path: ${uri.path}`); - } - } catch (err) { - if (err instanceof Error) { - await vscode.window.showErrorMessage(`Failed to handle lldb-dap URI request: ${err.message}`); - } else { - await vscode.window.showErrorMessage(`Failed to handle lldb-dap URI request: ${JSON.stringify(err)}`); - } + async handleUri(uri: vscode.Uri) { + try { + const params = new URLSearchParams(uri.query); + if (uri.path == "/start") { + // Some properties have default values + let debugConfig: vscode.DebugConfiguration = { + type: "lldb-dap", + request: "launch", + name: "", + }; + // The `config` parameter allows providing a complete JSON-encoded configuration + const configJson = params.get("config"); + if (configJson !== null) { + Object.assign(debugConfig, JSON.parse(configJson)); } + // Furthermore, some frequently used parameters can also be provided as separate parameters + const stringKeys = [ + "name", + "request", + "program", + "cwd", + "debuggerRoot", + ]; + const numberKeys = ["pid"]; + const arrayKeys = [ + "args", + "initCommands", + "preRunCommands", + "stopCommands", + "exitCommands", + "terminateCommands", + "launchCommands", + "attachCommands", + ]; + for (const key of stringKeys) { + const value = params.get(key); + if (value) { + debugConfig[key] = value; + } + } + for (const key of numberKeys) { + const value = params.get(key); + if (value) { + debugConfig[key] = Number(value); + } + } + for (const key of arrayKeys) { + // `getAll()` returns an array of strings. + const value = params.getAll(key); + if (value) { + debugConfig[key] = value; + } + } + // Report an error if we received any unknown parameters + const supportedKeys = new Set( + ["config"].concat(stringKeys).concat(numberKeys).concat(arrayKeys), + ); + const presentKeys = new Set(params.keys()); + // FIXME: Use `Set.difference` as soon as ES2024 is widely available + const unknownKeys = new Set(); + for (const k of presentKeys.keys()) { + if (!supportedKeys.has(k)) { + unknownKeys.add(k); + } + } + if (unknownKeys.size > 0) { + throw new Error( + `Unsupported URL parameters: ${Array.from(unknownKeys.keys()).join(", ")}`, + ); + } + // Prodide a default for the config name + const defaultName = + debugConfig.request == "launch" + ? "URL-based Launch" + : "URL-based Attach"; + debugConfig.name = + debugConfig.name || debugConfig.program || defaultName; + // Force the type to `lldb-dap`. We don't want to allow launching any other + // Debug Adapters using this URI scheme. + if (debugConfig.type != "lldb-dap") { + throw new Error(`Unsupported debugger type: ${debugConfig.type}`); + } + await vscode.debug.startDebugging(undefined, debugConfig); + } else { + throw new Error(`Unsupported Uri path: ${uri.path}`); + } + } catch (err) { + if (err instanceof Error) { + await vscode.window.showErrorMessage( + `Failed to handle lldb-dap URI request: ${err.message}`, + ); + } else { + await vscode.window.showErrorMessage( + `Failed to handle lldb-dap URI request: ${JSON.stringify(err)}`, + ); + } } + } } From lldb-commits at lists.llvm.org Wed May 7 10:54:56 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 10:54:56 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Format extension typescript. (PR #138925) In-Reply-To: Message-ID: <681b9e70.050a0220.1d7d9c.61a4@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: John Harrison (ashgti)
Changes I think the format checker isn't checking typescript files. I ran `npm run format` to fix the extenion typescript. --- Full diff: https://github.com/llvm/llvm-project/pull/138925.diff 2 Files Affected: - (modified) lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts (+57-39) - (modified) lldb/tools/lldb-dap/src-ts/uri-launch-handler.ts (+96-72) ``````````diff diff --git a/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts b/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts index 8a4089008b2f9..c91b101f4a9ba 100644 --- a/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts +++ b/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts @@ -21,79 +21,97 @@ async function isServerModeSupported(exe: string): Promise { } interface BoolConfig { - type: 'boolean'; + type: "boolean"; default: boolean; } interface StringConfig { - type: 'string'; + type: "string"; default: string; } interface NumberConfig { - type: 'number'; + type: "number"; default: number; } interface StringArrayConfig { - type: 'stringArray'; + type: "stringArray"; default: string[]; } -type DefaultConfig = BoolConfig | NumberConfig | StringConfig | StringArrayConfig; +type DefaultConfig = + | BoolConfig + | NumberConfig + | StringConfig + | StringArrayConfig; const configurations: Record = { // Keys for debugger configurations. - "commandEscapePrefix": { type: "string", default: "`" }, - "customFrameFormat": { type: "string", default: "" }, - "customThreadFormat": { type: "string", default: "" }, - "detachOnError": { type: "boolean", default: false }, - "disableASLR": { type: "boolean", default: true }, - "disableSTDIO": { type: "boolean", default: false }, - "displayExtendedBacktrace": { type: "boolean", default: false }, - "enableAutoVariableSummaries": { type: "boolean", default: false }, - "enableSyntheticChildDebugging": { type: "boolean", default: false }, - "timeout": { type: "number", default: 30 }, + commandEscapePrefix: { type: "string", default: "`" }, + customFrameFormat: { type: "string", default: "" }, + customThreadFormat: { type: "string", default: "" }, + detachOnError: { type: "boolean", default: false }, + disableASLR: { type: "boolean", default: true }, + disableSTDIO: { type: "boolean", default: false }, + displayExtendedBacktrace: { type: "boolean", default: false }, + enableAutoVariableSummaries: { type: "boolean", default: false }, + enableSyntheticChildDebugging: { type: "boolean", default: false }, + timeout: { type: "number", default: 30 }, // Keys for platform / target configuration. - "platformName": { type: "string", default: "" }, - "targetTriple": { type: "string", default: "" }, + platformName: { type: "string", default: "" }, + targetTriple: { type: "string", default: "" }, // Keys for debugger command hooks. - "initCommands": { type: "stringArray", default: [] }, - "preRunCommands": { type: "stringArray", default: [] }, - "postRunCommands": { type: "stringArray", default: [] }, - "stopCommands": { type: "stringArray", default: [] }, - "exitCommands": { type: "stringArray", default: [] }, - "terminateCommands": { type: "stringArray", default: [] }, + initCommands: { type: "stringArray", default: [] }, + preRunCommands: { type: "stringArray", default: [] }, + postRunCommands: { type: "stringArray", default: [] }, + stopCommands: { type: "stringArray", default: [] }, + exitCommands: { type: "stringArray", default: [] }, + terminateCommands: { type: "stringArray", default: [] }, }; export class LLDBDapConfigurationProvider - implements vscode.DebugConfigurationProvider { - constructor(private readonly server: LLDBDapServer) { } + implements vscode.DebugConfigurationProvider +{ + constructor(private readonly server: LLDBDapServer) {} async resolveDebugConfiguration( folder: vscode.WorkspaceFolder | undefined, debugConfiguration: vscode.DebugConfiguration, - token?: vscode.CancellationToken): Promise { - let config = vscode.workspace.getConfiguration('lldb-dap.defaults'); + token?: vscode.CancellationToken, + ): Promise { + let config = vscode.workspace.getConfiguration("lldb-dap.defaults"); for (const [key, cfg] of Object.entries(configurations)) { - if (Reflect.has(debugConfiguration, key)) continue; + if (Reflect.has(debugConfiguration, key)) { + continue; + } const value = config.get(key); - if (value === cfg.default) continue; + if (!value || value === cfg.default) { + continue; + } switch (cfg.type) { - case 'string': - if (typeof value !== 'string') + case "string": + if (typeof value !== "string") { throw new Error(`Expected ${key} to be a string, got ${value}`); + } break; - case 'number': - if (typeof value !== 'number') + case "number": + if (typeof value !== "number") { throw new Error(`Expected ${key} to be a number, got ${value}`); + } break; - case 'boolean': - if (typeof value !== 'boolean') + case "boolean": + if (typeof value !== "boolean") { throw new Error(`Expected ${key} to be a boolean, got ${value}`); + } break; - case 'stringArray': - if (typeof value !== 'object' && Array.isArray(value)) - throw new Error(`Expected ${key} to be a array of strings, got ${value}`); - if ((value as string[]).length === 0) continue; + case "stringArray": + if (typeof value !== "object" && Array.isArray(value)) { + throw new Error( + `Expected ${key} to be a array of strings, got ${value}`, + ); + } + if ((value as string[]).length === 0) { + continue; + } break; } diff --git a/lldb/tools/lldb-dap/src-ts/uri-launch-handler.ts b/lldb/tools/lldb-dap/src-ts/uri-launch-handler.ts index 0c3b1e9a00d9e..d45c1820eec75 100644 --- a/lldb/tools/lldb-dap/src-ts/uri-launch-handler.ts +++ b/lldb/tools/lldb-dap/src-ts/uri-launch-handler.ts @@ -1,78 +1,102 @@ import * as vscode from "vscode"; export class LaunchUriHandler implements vscode.UriHandler { - async handleUri(uri: vscode.Uri) { - try { - const params = new URLSearchParams(uri.query); - if (uri.path == '/start') { - // Some properties have default values - let debugConfig: vscode.DebugConfiguration = { - type: 'lldb-dap', - request: 'launch', - name: '', - }; - // The `config` parameter allows providing a complete JSON-encoded configuration - const configJson = params.get("config"); - if (configJson !== null) { - Object.assign(debugConfig, JSON.parse(configJson)); - } - // Furthermore, some frequently used parameters can also be provided as separate parameters - const stringKeys = ["name", "request", "program", "cwd", "debuggerRoot"]; - const numberKeys = ["pid"]; - const arrayKeys = [ - "args", "initCommands", "preRunCommands", "stopCommands", "exitCommands", - "terminateCommands", "launchCommands", "attachCommands" - ]; - for (const key of stringKeys) { - const value = params.get(key); - if (value) { - debugConfig[key] = value; - } - } - for (const key of numberKeys) { - const value = params.get(key); - if (value) { - debugConfig[key] = Number(value); - } - } - for (const key of arrayKeys) { - // `getAll()` returns an array of strings. - const value = params.getAll(key); - if (value) { - debugConfig[key] = value; - } - } - // Report an error if we received any unknown parameters - const supportedKeys = new Set(["config"].concat(stringKeys).concat(numberKeys).concat(arrayKeys)); - const presentKeys = new Set(params.keys()); - // FIXME: Use `Set.difference` as soon as ES2024 is widely available - const unknownKeys = new Set(); - for (const k of presentKeys.keys()) { - if (!supportedKeys.has(k)) { - unknownKeys.add(k); - } - } - if (unknownKeys.size > 0) { - throw new Error(`Unsupported URL parameters: ${Array.from(unknownKeys.keys()).join(", ")}`); - } - // Prodide a default for the config name - const defaultName = debugConfig.request == 'launch' ? "URL-based Launch" : "URL-based Attach"; - debugConfig.name = debugConfig.name || debugConfig.program || defaultName; - // Force the type to `lldb-dap`. We don't want to allow launching any other - // Debug Adapters using this URI scheme. - if (debugConfig.type != "lldb-dap") { - throw new Error(`Unsupported debugger type: ${debugConfig.type}`); - } - await vscode.debug.startDebugging(undefined, debugConfig); - } else { - throw new Error(`Unsupported Uri path: ${uri.path}`); - } - } catch (err) { - if (err instanceof Error) { - await vscode.window.showErrorMessage(`Failed to handle lldb-dap URI request: ${err.message}`); - } else { - await vscode.window.showErrorMessage(`Failed to handle lldb-dap URI request: ${JSON.stringify(err)}`); - } + async handleUri(uri: vscode.Uri) { + try { + const params = new URLSearchParams(uri.query); + if (uri.path == "/start") { + // Some properties have default values + let debugConfig: vscode.DebugConfiguration = { + type: "lldb-dap", + request: "launch", + name: "", + }; + // The `config` parameter allows providing a complete JSON-encoded configuration + const configJson = params.get("config"); + if (configJson !== null) { + Object.assign(debugConfig, JSON.parse(configJson)); } + // Furthermore, some frequently used parameters can also be provided as separate parameters + const stringKeys = [ + "name", + "request", + "program", + "cwd", + "debuggerRoot", + ]; + const numberKeys = ["pid"]; + const arrayKeys = [ + "args", + "initCommands", + "preRunCommands", + "stopCommands", + "exitCommands", + "terminateCommands", + "launchCommands", + "attachCommands", + ]; + for (const key of stringKeys) { + const value = params.get(key); + if (value) { + debugConfig[key] = value; + } + } + for (const key of numberKeys) { + const value = params.get(key); + if (value) { + debugConfig[key] = Number(value); + } + } + for (const key of arrayKeys) { + // `getAll()` returns an array of strings. + const value = params.getAll(key); + if (value) { + debugConfig[key] = value; + } + } + // Report an error if we received any unknown parameters + const supportedKeys = new Set( + ["config"].concat(stringKeys).concat(numberKeys).concat(arrayKeys), + ); + const presentKeys = new Set(params.keys()); + // FIXME: Use `Set.difference` as soon as ES2024 is widely available + const unknownKeys = new Set(); + for (const k of presentKeys.keys()) { + if (!supportedKeys.has(k)) { + unknownKeys.add(k); + } + } + if (unknownKeys.size > 0) { + throw new Error( + `Unsupported URL parameters: ${Array.from(unknownKeys.keys()).join(", ")}`, + ); + } + // Prodide a default for the config name + const defaultName = + debugConfig.request == "launch" + ? "URL-based Launch" + : "URL-based Attach"; + debugConfig.name = + debugConfig.name || debugConfig.program || defaultName; + // Force the type to `lldb-dap`. We don't want to allow launching any other + // Debug Adapters using this URI scheme. + if (debugConfig.type != "lldb-dap") { + throw new Error(`Unsupported debugger type: ${debugConfig.type}`); + } + await vscode.debug.startDebugging(undefined, debugConfig); + } else { + throw new Error(`Unsupported Uri path: ${uri.path}`); + } + } catch (err) { + if (err instanceof Error) { + await vscode.window.showErrorMessage( + `Failed to handle lldb-dap URI request: ${err.message}`, + ); + } else { + await vscode.window.showErrorMessage( + `Failed to handle lldb-dap URI request: ${JSON.stringify(err)}`, + ); + } } + } } ``````````
https://github.com/llvm/llvm-project/pull/138925 From lldb-commits at lists.llvm.org Wed May 7 10:55:14 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 10:55:14 -0700 (PDT) Subject: [Lldb-commits] [lldb] bf59716 - [lldb-dap] Fix package.json after a bad merge. (#138918) Message-ID: <681b9e82.630a0220.37da94.a4f6@mx.google.com> Author: John Harrison Date: 2025-05-07T10:55:11-07:00 New Revision: bf5971634a9244fd65c1bf8316b3d6ec407783ae URL: https://github.com/llvm/llvm-project/commit/bf5971634a9244fd65c1bf8316b3d6ec407783ae DIFF: https://github.com/llvm/llvm-project/commit/bf5971634a9244fd65c1bf8316b3d6ec407783ae.diff LOG: [lldb-dap] Fix package.json after a bad merge. (#138918) The package.json is currently malformed after a bad merge in 39e6e888a8155583713e1b8b256119a2be7902e0. Added: Modified: lldb/tools/lldb-dap/package.json Removed: ################################################################################ diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json index 4734c9d7277bb..f66badc2a930f 100644 --- a/lldb/tools/lldb-dap/package.json +++ b/lldb/tools/lldb-dap/package.json @@ -242,527 +242,527 @@ } } } - ] - }, - "breakpoints": [ - { - "language": "ada" - }, - { - "language": "arm" - }, - { - "language": "asm" - }, - { - "language": "c" - }, - { - "language": "cpp" - }, - { - "language": "crystal" - }, - { - "language": "d" - }, - { - "language": "fortan" - }, - { - "language": "fortran-modern" - }, - { - "language": "nim" - }, - { - "language": "objective-c" - }, - { - "language": "objectpascal" - }, - { - "language": "pascal" - }, - { - "language": "rust" - }, - { - "language": "swift" - } - ], - "debuggers": [ - { - "type": "lldb-dap", - "label": "LLDB DAP Debugger", - "configurationAttributes": { - "launch": { - "required": [ - "program" - ], - "properties": { - "debugAdapterHostname": { - "type": "string", - "markdownDescription": "The hostname that an existing lldb-dap executable is listening on." - }, - "debugAdapterPort": { - "type": "number", - "markdownDescription": "The port that an existing lldb-dap executable is listening on." - }, - "debugAdapterExecutable": { - "type": "string", - "markdownDescription": "The absolute path to the LLDB debug adapter executable to use. Overrides any user or workspace settings." - }, - "debugAdapterArgs": { - "type": "array", - "items": { - "type": "string" + ], + "breakpoints": [ + { + "language": "ada" + }, + { + "language": "arm" + }, + { + "language": "asm" + }, + { + "language": "c" + }, + { + "language": "cpp" + }, + { + "language": "crystal" + }, + { + "language": "d" + }, + { + "language": "fortan" + }, + { + "language": "fortran-modern" + }, + { + "language": "nim" + }, + { + "language": "objective-c" + }, + { + "language": "objectpascal" + }, + { + "language": "pascal" + }, + { + "language": "rust" + }, + { + "language": "swift" + } + ], + "debuggers": [ + { + "type": "lldb-dap", + "label": "LLDB DAP Debugger", + "configurationAttributes": { + "launch": { + "required": [ + "program" + ], + "properties": { + "debugAdapterHostname": { + "type": "string", + "markdownDescription": "The hostname that an existing lldb-dap executable is listening on." }, - "markdownDescription": "The list of additional arguments used to launch the debug adapter executable. Overrides any user or workspace settings." - }, - "program": { - "type": "string", - "description": "Path to the program to debug." - }, - "args": { - "type": [ - "array" - ], - "items": { - "type": "string" - }, - "description": "Program arguments.", - "default": [] - }, - "cwd": { - "type": "string", - "description": "Program working directory.", - "default": "${workspaceRoot}" - }, - "env": { - "anyOf": [ - { - "type": "object", - "description": "Additional environment variables to set when launching the program. E.g. `{ \"FOO\": \"1\" }`", - "patternProperties": { - ".*": { - "type": "string" - } - }, - "default": {} + "debugAdapterPort": { + "type": "number", + "markdownDescription": "The port that an existing lldb-dap executable is listening on." + }, + "debugAdapterExecutable": { + "type": "string", + "markdownDescription": "The absolute path to the LLDB debug adapter executable to use. Overrides any user or workspace settings." + }, + "debugAdapterArgs": { + "type": "array", + "items": { + "type": "string" }, - { - "type": "array", - "description": "Additional environment variables to set when launching the program. E.g. `[\"FOO=1\", \"BAR\"]`", - "items": { - "type": "string", - "pattern": "^((\\w+=.*)|^\\w+)$" - }, - "default": [] - } - ] - }, - "stopOnEntry": { - "type": "boolean", - "description": "Automatically stop after launch.", - "default": false - }, - "disableASLR": { - "type": "boolean", - "description": "Enable or disable Address space layout randomization if the debugger supports it.", - "default": true - }, - "disableSTDIO": { - "type": "boolean", - "description": "Don't retrieve STDIN, STDOUT and STDERR as the program is running.", - "default": false - }, - "shellExpandArguments": { - "type": "boolean", - "description": "Expand program arguments as a shell would without actually launching the program in a shell.", - "default": false - }, - "detachOnError": { - "type": "boolean", - "description": "Detach from the program.", - "default": false - }, - "sourcePath": { - "type": "string", - "description": "Specify a source path to remap \"./\" to allow full paths to be used when setting breakpoints in binaries that have relative source paths." - }, - "sourceMap": { - "anyOf": [ - { - "type": "object", - "description": "Specify an object of path remappings; each entry has a key containing the source path and a value containing the destination path. E.g `{ \"/the/source/path\": \"/the/destination/path\" }`. Overrides sourcePath.", - "patternProperties": { - ".*": { - "type": "string" - } - }, - "default": {} + "markdownDescription": "The list of additional arguments used to launch the debug adapter executable. Overrides any user or workspace settings." + }, + "program": { + "type": "string", + "description": "Path to the program to debug." + }, + "args": { + "type": [ + "array" + ], + "items": { + "type": "string" }, - { - "type": "array", - "description": "Specify an array of path remappings; each element must itself be a two element array containing a source and destination path name. Overrides sourcePath.", - "items": { + "description": "Program arguments.", + "default": [] + }, + "cwd": { + "type": "string", + "description": "Program working directory.", + "default": "${workspaceRoot}" + }, + "env": { + "anyOf": [ + { + "type": "object", + "description": "Additional environment variables to set when launching the program. E.g. `{ \"FOO\": \"1\" }`", + "patternProperties": { + ".*": { + "type": "string" + } + }, + "default": {} + }, + { "type": "array", - "minItems": 2, - "maxItems": 2, + "description": "Additional environment variables to set when launching the program. E.g. `[\"FOO=1\", \"BAR\"]`", "items": { - "type": "string" - } + "type": "string", + "pattern": "^((\\w+=.*)|^\\w+)$" + }, + "default": [] + } + ] + }, + "stopOnEntry": { + "type": "boolean", + "description": "Automatically stop after launch.", + "default": false + }, + "disableASLR": { + "type": "boolean", + "description": "Enable or disable Address space layout randomization if the debugger supports it.", + "default": true + }, + "disableSTDIO": { + "type": "boolean", + "description": "Don't retrieve STDIN, STDOUT and STDERR as the program is running.", + "default": false + }, + "shellExpandArguments": { + "type": "boolean", + "description": "Expand program arguments as a shell would without actually launching the program in a shell.", + "default": false + }, + "detachOnError": { + "type": "boolean", + "description": "Detach from the program.", + "default": false + }, + "sourcePath": { + "type": "string", + "description": "Specify a source path to remap \"./\" to allow full paths to be used when setting breakpoints in binaries that have relative source paths." + }, + "sourceMap": { + "anyOf": [ + { + "type": "object", + "description": "Specify an object of path remappings; each entry has a key containing the source path and a value containing the destination path. E.g `{ \"/the/source/path\": \"/the/destination/path\" }`. Overrides sourcePath.", + "patternProperties": { + ".*": { + "type": "string" + } + }, + "default": {} }, - "default": [] - } - ] - }, - "debuggerRoot": { - "type": "string", - "description": "Specify a working directory to set the debug adapter to so relative object files can be located." - }, - "targetTriple": { - "type": "string", - "description": "Triplet of the target architecture to override value derived from the program file." - }, - "platformName": { - "type": "string", - "description": "Name of the execution platform to override value derived from the program file." - }, - "initCommands": { - "type": "array", - "items": { - "type": "string" + { + "type": "array", + "description": "Specify an array of path remappings; each element must itself be a two element array containing a source and destination path name. Overrides sourcePath.", + "items": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "string" + } + }, + "default": [] + } + ] }, - "description": "Initialization commands executed upon debugger startup.", - "default": [] - }, - "preRunCommands": { - "type": "array", - "items": { - "type": "string" + "debuggerRoot": { + "type": "string", + "description": "Specify a working directory to set the debug adapter to so relative object files can be located." }, - "description": "Commands executed just before the program is launched.", - "default": [] - }, - "postRunCommands": { - "type": "array", - "items": { - "type": "string" + "targetTriple": { + "type": "string", + "description": "Triplet of the target architecture to override value derived from the program file." }, - "description": "Commands executed just as soon as the program is successfully launched when it's in a stopped state prior to any automatic continuation.", - "default": [] - }, - "launchCommands": { - "type": "array", - "items": { - "type": "string" + "platformName": { + "type": "string", + "description": "Name of the execution platform to override value derived from the program file." }, - "description": "Custom commands that are executed instead of launching a process. A target will be created with the launch arguments prior to executing these commands. The commands may optionally create a new target and must perform a launch. A valid process must exist after these commands complete or the \"launch\" will fail. Launch the process with \"process launch -s\" to make the process to at the entry point since lldb-dap will auto resume if necessary.", - "default": [] - }, - "stopCommands": { - "type": "array", - "items": { - "type": "string" + "initCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Initialization commands executed upon debugger startup.", + "default": [] }, - "description": "Commands executed each time the program stops.", - "default": [] - }, - "exitCommands": { - "type": "array", - "items": { - "type": "string" + "preRunCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Commands executed just before the program is launched.", + "default": [] }, - "description": "Commands executed when the program exits.", - "default": [] - }, - "terminateCommands": { - "type": "array", - "items": { - "type": "string" + "postRunCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Commands executed just as soon as the program is successfully launched when it's in a stopped state prior to any automatic continuation.", + "default": [] }, - "description": "Commands executed when the debugging session ends.", - "default": [] - }, - "runInTerminal": { - "type": "boolean", - "description": "Launch the program inside an integrated terminal in the IDE. Useful for debugging interactive command line programs", - "default": false - }, - "timeout": { - "type": "number", - "description": "The time in seconds to wait for a program to stop at entry point when launching with \"launchCommands\". Defaults to 30 seconds." - }, - "enableAutoVariableSummaries": { - "type": "boolean", - "description": "Enable auto generated summaries for variables when no summaries exist for a given type. This feature can cause performance delays in large projects when viewing variables.", - "default": false - }, - "displayExtendedBacktrace": { - "type": "boolean", - "description": "Enable language specific extended backtraces.", - "default": false - }, - "enableSyntheticChildDebugging": { - "type": "boolean", - "description": "If a variable is displayed using a synthetic children, also display the actual contents of the variable at the end under a [raw] entry. This is useful when creating sythetic child plug-ins as it lets you see the actual contents of the variable.", - "default": false - }, - "commandEscapePrefix": { - "type": "string", - "description": "The escape prefix to use for executing regular LLDB commands in the Debug Console, instead of printing variables. Defaults to a back-tick (`). If it's an empty string, then all expression in the Debug Console are treated as regular LLDB commands.", - "default": "`" - }, - "customFrameFormat": { - "type": "string", - "description": "If non-empty, stack frames will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for frames. If the format string contains errors, an error message will be displayed on the Debug Console and the default frame names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.", - "default": "" - }, - "customThreadFormat": { - "type": "string", - "description": "If non-empty, threads will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for threads. If the format string contains errors, an error message will be displayed on the Debug Console and the default thread names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.", - "default": "" + "launchCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Custom commands that are executed instead of launching a process. A target will be created with the launch arguments prior to executing these commands. The commands may optionally create a new target and must perform a launch. A valid process must exist after these commands complete or the \"launch\" will fail. Launch the process with \"process launch -s\" to make the process to at the entry point since lldb-dap will auto resume if necessary.", + "default": [] + }, + "stopCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Commands executed each time the program stops.", + "default": [] + }, + "exitCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Commands executed when the program exits.", + "default": [] + }, + "terminateCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Commands executed when the debugging session ends.", + "default": [] + }, + "runInTerminal": { + "type": "boolean", + "description": "Launch the program inside an integrated terminal in the IDE. Useful for debugging interactive command line programs", + "default": false + }, + "timeout": { + "type": "number", + "description": "The time in seconds to wait for a program to stop at entry point when launching with \"launchCommands\". Defaults to 30 seconds." + }, + "enableAutoVariableSummaries": { + "type": "boolean", + "description": "Enable auto generated summaries for variables when no summaries exist for a given type. This feature can cause performance delays in large projects when viewing variables.", + "default": false + }, + "displayExtendedBacktrace": { + "type": "boolean", + "description": "Enable language specific extended backtraces.", + "default": false + }, + "enableSyntheticChildDebugging": { + "type": "boolean", + "description": "If a variable is displayed using a synthetic children, also display the actual contents of the variable at the end under a [raw] entry. This is useful when creating sythetic child plug-ins as it lets you see the actual contents of the variable.", + "default": false + }, + "commandEscapePrefix": { + "type": "string", + "description": "The escape prefix to use for executing regular LLDB commands in the Debug Console, instead of printing variables. Defaults to a back-tick (`). If it's an empty string, then all expression in the Debug Console are treated as regular LLDB commands.", + "default": "`" + }, + "customFrameFormat": { + "type": "string", + "description": "If non-empty, stack frames will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for frames. If the format string contains errors, an error message will be displayed on the Debug Console and the default frame names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.", + "default": "" + }, + "customThreadFormat": { + "type": "string", + "description": "If non-empty, threads will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for threads. If the format string contains errors, an error message will be displayed on the Debug Console and the default thread names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.", + "default": "" + } } - } - }, - "attach": { - "properties": { - "debugAdapterHostname": { - "type": "string", - "markdownDescription": "The hostname that an existing lldb-dap executable is listening on." - }, - "debugAdapterPort": { - "type": "number", - "markdownDescription": "The port that an existing lldb-dap executable is listening on." - }, - "debugAdapterExecutable": { - "type": "string", - "markdownDescription": "The absolute path to the LLDB debug adapter executable to use. Overrides any user or workspace settings." - }, - "debugAdapterArgs": { - "type": "array", - "items": { - "type": "string" + }, + "attach": { + "properties": { + "debugAdapterHostname": { + "type": "string", + "markdownDescription": "The hostname that an existing lldb-dap executable is listening on." }, - "markdownDescription": "The list of additional arguments used to launch the debug adapter executable. Overrides any user or workspace settings." - }, - "program": { - "type": "string", - "description": "Path to the program to attach to." - }, - "pid": { - "type": [ - "number", - "string" - ], - "description": "System process ID to attach to." - }, - "waitFor": { - "type": "boolean", - "description": "If set to true, then wait for the process to launch by looking for a process with a basename that matches `program`. No process ID needs to be specified when using this flag.", - "default": true - }, - "sourcePath": { - "type": "string", - "description": "Specify a source path to remap \"./\" to allow full paths to be used when setting breakpoints in binaries that have relative source paths." - }, - "sourceMap": { - "anyOf": [ - { - "type": "object", - "description": "Specify an object of path remappings; each entry has a key containing the source path and a value containing the destination path. E.g `{ \"/the/source/path\": \"/the/destination/path\" }`. Overrides sourcePath.", - "patternProperties": { - ".*": { - "type": "string" - } - }, - "default": {} + "debugAdapterPort": { + "type": "number", + "markdownDescription": "The port that an existing lldb-dap executable is listening on." + }, + "debugAdapterExecutable": { + "type": "string", + "markdownDescription": "The absolute path to the LLDB debug adapter executable to use. Overrides any user or workspace settings." + }, + "debugAdapterArgs": { + "type": "array", + "items": { + "type": "string" }, - { - "type": "array", - "description": "Specify an array of path remappings; each element must itself be a two element array containing a source and destination path name. Overrides sourcePath.", - "items": { + "markdownDescription": "The list of additional arguments used to launch the debug adapter executable. Overrides any user or workspace settings." + }, + "program": { + "type": "string", + "description": "Path to the program to attach to." + }, + "pid": { + "type": [ + "number", + "string" + ], + "description": "System process ID to attach to." + }, + "waitFor": { + "type": "boolean", + "description": "If set to true, then wait for the process to launch by looking for a process with a basename that matches `program`. No process ID needs to be specified when using this flag.", + "default": true + }, + "sourcePath": { + "type": "string", + "description": "Specify a source path to remap \"./\" to allow full paths to be used when setting breakpoints in binaries that have relative source paths." + }, + "sourceMap": { + "anyOf": [ + { + "type": "object", + "description": "Specify an object of path remappings; each entry has a key containing the source path and a value containing the destination path. E.g `{ \"/the/source/path\": \"/the/destination/path\" }`. Overrides sourcePath.", + "patternProperties": { + ".*": { + "type": "string" + } + }, + "default": {} + }, + { "type": "array", - "minItems": 2, - "maxItems": 2, + "description": "Specify an array of path remappings; each element must itself be a two element array containing a source and destination path name. Overrides sourcePath.", "items": { - "type": "string" - } - }, - "default": [] - } - ] - }, - "debuggerRoot": { - "type": "string", - "description": "Specify a working directory to set the debug adapter to so relative object files can be located." - }, - "targetTriple": { - "type": "string", - "description": "Triplet of the target architecture to override value derived from the program file." - }, - "platformName": { - "type": "string", - "description": "Name of the execution platform to override value derived from the program file." - }, - "attachCommands": { - "type": "array", - "items": { - "type": "string" + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "string" + } + }, + "default": [] + } + ] }, - "description": "Custom commands that are executed instead of attaching to a process ID or to a process by name. These commands may optionally create a new target and must perform an attach. A valid process must exist after these commands complete or the \"attach\" will fail.", - "default": [] - }, - "initCommands": { - "type": "array", - "items": { - "type": "string" + "debuggerRoot": { + "type": "string", + "description": "Specify a working directory to set the debug adapter to so relative object files can be located." }, - "description": "Initialization commands executed upon debugger startup.", - "default": [] - }, - "preRunCommands": { - "type": "array", - "items": { - "type": "string" + "targetTriple": { + "type": "string", + "description": "Triplet of the target architecture to override value derived from the program file." }, - "description": "Commands executed just before the program is attached to.", - "default": [] - }, - "postRunCommands": { - "type": "array", - "items": { - "type": "string" + "platformName": { + "type": "string", + "description": "Name of the execution platform to override value derived from the program file." }, - "description": "Commands executed just as soon as the program is successfully attached when it's in a stopped state prior to any automatic continuation.", - "default": [] - }, - "stopCommands": { - "type": "array", - "items": { - "type": "string" + "attachCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Custom commands that are executed instead of attaching to a process ID or to a process by name. These commands may optionally create a new target and must perform an attach. A valid process must exist after these commands complete or the \"attach\" will fail.", + "default": [] }, - "description": "Commands executed each time the program stops.", - "default": [] - }, - "exitCommands": { - "type": "array", - "items": { - "type": "string" + "initCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Initialization commands executed upon debugger startup.", + "default": [] }, - "description": "Commands executed when the program exits.", - "default": [] - }, - "terminateCommands": { - "type": "array", - "items": { - "type": "string" + "preRunCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Commands executed just before the program is attached to.", + "default": [] }, - "description": "Commands executed when the debugging session ends.", - "default": [] - }, - "coreFile": { - "type": "string", - "description": "Path to the core file to debug." - }, - "timeout": { - "type": "number", - "description": "The time in seconds to wait for a program to stop when attaching using \"attachCommands\". Defaults to 30 seconds." - }, - "gdb-remote-port": { - "type": [ - "number", - "string" - ], - "description": "TCP/IP port to attach to a remote system. Specifying both pid and port is an error." - }, - "gdb-remote-hostname": { - "type": "string", - "description": "The hostname to connect to a remote system. The default hostname being used localhost." - }, - "enableAutoVariableSummaries": { - "type": "boolean", - "description": "Enable auto generated summaries for variables when no summaries exist for a given type. This feature can cause performance delays in large projects when viewing variables.", - "default": false - }, - "displayExtendedBacktrace": { - "type": "boolean", - "description": "Enable language specific extended backtraces.", - "default": false - }, - "enableSyntheticChildDebugging": { - "type": "boolean", - "description": "If a variable is displayed using a synthetic children, also display the actual contents of the variable at the end under a [raw] entry. This is useful when creating sythetic child plug-ins as it lets you see the actual contents of the variable.", - "default": false - }, - "commandEscapePrefix": { - "type": "string", - "description": "The escape prefix character to use for executing regular LLDB commands in the Debug Console, instead of printing variables. Defaults to a back-tick (`). If empty, then all expression in the Debug Console are treated as regular LLDB commands.", - "default": "`" - }, - "customFrameFormat": { - "type": "string", - "description": "If non-empty, stack frames will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for frames. If the format string contains errors, an error message will be displayed on the Debug Console and the default frame names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.", - "default": "" - }, - "customThreadFormat": { - "type": "string", - "description": "If non-empty, threads will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for threads. If the format string contains errors, an error message will be displayed on the Debug Console and the default thread names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.", - "default": "" + "postRunCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Commands executed just as soon as the program is successfully attached when it's in a stopped state prior to any automatic continuation.", + "default": [] + }, + "stopCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Commands executed each time the program stops.", + "default": [] + }, + "exitCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Commands executed when the program exits.", + "default": [] + }, + "terminateCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Commands executed when the debugging session ends.", + "default": [] + }, + "coreFile": { + "type": "string", + "description": "Path to the core file to debug." + }, + "timeout": { + "type": "number", + "description": "The time in seconds to wait for a program to stop when attaching using \"attachCommands\". Defaults to 30 seconds." + }, + "gdb-remote-port": { + "type": [ + "number", + "string" + ], + "description": "TCP/IP port to attach to a remote system. Specifying both pid and port is an error." + }, + "gdb-remote-hostname": { + "type": "string", + "description": "The hostname to connect to a remote system. The default hostname being used localhost." + }, + "enableAutoVariableSummaries": { + "type": "boolean", + "description": "Enable auto generated summaries for variables when no summaries exist for a given type. This feature can cause performance delays in large projects when viewing variables.", + "default": false + }, + "displayExtendedBacktrace": { + "type": "boolean", + "description": "Enable language specific extended backtraces.", + "default": false + }, + "enableSyntheticChildDebugging": { + "type": "boolean", + "description": "If a variable is displayed using a synthetic children, also display the actual contents of the variable at the end under a [raw] entry. This is useful when creating sythetic child plug-ins as it lets you see the actual contents of the variable.", + "default": false + }, + "commandEscapePrefix": { + "type": "string", + "description": "The escape prefix character to use for executing regular LLDB commands in the Debug Console, instead of printing variables. Defaults to a back-tick (`). If empty, then all expression in the Debug Console are treated as regular LLDB commands.", + "default": "`" + }, + "customFrameFormat": { + "type": "string", + "description": "If non-empty, stack frames will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for frames. If the format string contains errors, an error message will be displayed on the Debug Console and the default frame names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.", + "default": "" + }, + "customThreadFormat": { + "type": "string", + "description": "If non-empty, threads will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for threads. If the format string contains errors, an error message will be displayed on the Debug Console and the default thread names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.", + "default": "" + } } } - } - }, - "initialConfigurations": [ - { - "type": "lldb-dap", - "request": "launch", - "name": "Debug", - "program": "${workspaceRoot}/", - "args": [], - "env": [], - "cwd": "${workspaceRoot}" - } - ], - "configurationSnippets": [ - { - "label": "LLDB: Launch", - "description": "", - "body": { + }, + "initialConfigurations": [ + { "type": "lldb-dap", "request": "launch", - "name": "${2:Launch}", - "program": "^\"\\${workspaceRoot}/${1:}\"", + "name": "Debug", + "program": "${workspaceRoot}/", "args": [], "env": [], - "cwd": "^\"\\${workspaceRoot}\"" - } - }, - { - "label": "LLDB: Attach", - "description": "", - "body": { - "type": "lldb-dap", - "request": "attach", - "name": "${2:Attach}", - "program": "${1:}", - "waitFor": true + "cwd": "${workspaceRoot}" } - }, - { - "label": "LLDB: Load Coredump", - "description": "", - "body": { - "type": "lldb-dap", - "request": "attach", - "name": "${2:Core}", - "program": "${1:}", - "coreFile": "${1:}.core" + ], + "configurationSnippets": [ + { + "label": "LLDB: Launch", + "description": "", + "body": { + "type": "lldb-dap", + "request": "launch", + "name": "${2:Launch}", + "program": "^\"\\${workspaceRoot}/${1:}\"", + "args": [], + "env": [], + "cwd": "^\"\\${workspaceRoot}\"" + } + }, + { + "label": "LLDB: Attach", + "description": "", + "body": { + "type": "lldb-dap", + "request": "attach", + "name": "${2:Attach}", + "program": "${1:}", + "waitFor": true + } + }, + { + "label": "LLDB: Load Coredump", + "description": "", + "body": { + "type": "lldb-dap", + "request": "attach", + "name": "${2:Core}", + "program": "${1:}", + "coreFile": "${1:}.core" + } } - } - ] - } - ] -} \ No newline at end of file + ] + } + ] + } +} From lldb-commits at lists.llvm.org Wed May 7 10:55:17 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Wed, 07 May 2025 10:55:17 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Fix package.json after a bad merge. (PR #138918) In-Reply-To: Message-ID: <681b9e85.170a0220.369cb0.8e8d@mx.google.com> https://github.com/ashgti closed https://github.com/llvm/llvm-project/pull/138918 From lldb-commits at lists.llvm.org Wed May 7 11:08:59 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Wed, 07 May 2025 11:08:59 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Format extension typescript. (PR #138925) In-Reply-To: Message-ID: <681ba1bb.170a0220.2906d9.e7e0@mx.google.com> https://github.com/JDevlieghere approved this pull request. LGTM. Can we automate this with package.json? https://github.com/llvm/llvm-project/pull/138925 From lldb-commits at lists.llvm.org Wed May 7 11:10:34 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Wed, 07 May 2025 11:10:34 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Use -Wno-documentation-deprecated-sync if available (PR #138909) In-Reply-To: Message-ID: <681ba21a.a70a0220.3d9574.68b7@mx.google.com> https://github.com/JDevlieghere approved this pull request. https://github.com/llvm/llvm-project/pull/138909 From lldb-commits at lists.llvm.org Wed May 7 11:14:31 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 11:14:31 -0700 (PDT) Subject: [Lldb-commits] [lldb] 47218ea - [lldb] Use -Wno-documentation-deprecated-sync if available (#138909) Message-ID: <681ba307.170a0220.199f8e.a94a@mx.google.com> Author: Kazu Hirata Date: 2025-05-07T11:14:28-07:00 New Revision: 47218eadd8adf1926ced879caa50b8885d1b070d URL: https://github.com/llvm/llvm-project/commit/47218eadd8adf1926ced879caa50b8885d1b070d DIFF: https://github.com/llvm/llvm-project/commit/47218eadd8adf1926ced879caa50b8885d1b070d.diff LOG: [lldb] Use -Wno-documentation-deprecated-sync if available (#138909) report_fatal_error has been marked "@deprecated" in its comment, but the function itself is not marked with [[deprecated]] yet. This causes warnings like: llvm/include/llvm/Support/ErrorHandling.h:61:6: error: declaration is marked with '@deprecated' command but does not have a deprecation attribute [-Werror,-Wdocumentation-deprecated-sync] llvm/include/llvm/Support/Error.h:738:6: error: declaration is marked with '@deprecated' command but does not have a deprecation attribute [-Werror,-Wdocumentation-deprecated-sync] This patch disables the warning while we migrate away from report_fatal_error. Added: Modified: lldb/unittests/API/CMakeLists.txt Removed: ################################################################################ diff --git a/lldb/unittests/API/CMakeLists.txt b/lldb/unittests/API/CMakeLists.txt index 8bdc806878239..06ac49244176c 100644 --- a/lldb/unittests/API/CMakeLists.txt +++ b/lldb/unittests/API/CMakeLists.txt @@ -16,6 +16,17 @@ if (CXX_SUPPORTS_DOCUMENTATION) PRIVATE -Wdocumentation) endif() +# Apply -Wno-documentation-deprecated-sync while we migrate away from +# report_fatal_error in llvm/include/llvm/Support/ErrorHandling.h +# and llvm/include/llvm/Support/Error.h. +# Remove this block of code when the migration is complete. +# See https://github.com/llvm/llvm-project/issues/138914. +check_cxx_compiler_flag("-Wno-documentation-deprecated-sync" + CXX_SUPPORTS_NO_DOCUMENTATION_DEPRECATED_SYNC) +if (CXX_SUPPORTS_NO_DOCUMENTATION_DEPRECATED_SYNC) + target_compile_options(APITests + PRIVATE -Wno-documentation-deprecated-sync) +endif() if(Python3_RPATH) set_property(TARGET APITests APPEND PROPERTY BUILD_RPATH "${Python3_RPATH}") From lldb-commits at lists.llvm.org Wed May 7 11:14:35 2025 From: lldb-commits at lists.llvm.org (Kazu Hirata via lldb-commits) Date: Wed, 07 May 2025 11:14:35 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Use -Wno-documentation-deprecated-sync if available (PR #138909) In-Reply-To: Message-ID: <681ba30b.170a0220.c1a2e.1303@mx.google.com> https://github.com/kazutakahirata closed https://github.com/llvm/llvm-project/pull/138909 From lldb-commits at lists.llvm.org Wed May 7 11:17:26 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Wed, 07 May 2025 11:17:26 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Format extension typescript. (PR #138925) In-Reply-To: Message-ID: <681ba3b6.a70a0220.1f25b0.12fb@mx.google.com> ashgti wrote: I'm not sure, clang-format can format .json and .js/.ts files but I think the current config isn't applying it to those files. https://github.com/llvm/llvm-project/blob/main/llvm/utils/git/code-format-helper.py doesn't apply to those file extensions. We may be able to update that to use clang-format for these files, although the `npm run format` script is applying the prettier format. https://github.com/llvm/llvm-project/pull/138925 From lldb-commits at lists.llvm.org Wed May 7 11:19:40 2025 From: lldb-commits at lists.llvm.org (Dmitry Vasilyev via lldb-commits) Date: Wed, 07 May 2025 11:19:40 -0700 (PDT) Subject: [Lldb-commits] [lldb] Reapply "[lldb] Inherit DuplicateFileAction(HANDLE, HANDLE) handles on windows (#137978)" (PR #138896) In-Reply-To: Message-ID: <681ba43c.170a0220.ab65.9166@mx.google.com> https://github.com/slydiman approved this pull request. I have tested the failed Host.LaunchProcessDuplicatesHandle test on the setup similar to [lldb-remote-linux-win](https://lab.llvm.org/buildbot/#/builders/197). It is fixed now. https://github.com/llvm/llvm-project/pull/138896 From lldb-commits at lists.llvm.org Wed May 7 11:49:56 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 11:49:56 -0700 (PDT) Subject: [Lldb-commits] [lldb] 854b9e9 - [lldb-dap] Format extension typescript. (#138925) Message-ID: <681bab54.170a0220.b0aaa.15b1@mx.google.com> Author: John Harrison Date: 2025-05-07T11:49:53-07:00 New Revision: 854b9e931703dd1b9d8a2b0fe8da787f9e26058d URL: https://github.com/llvm/llvm-project/commit/854b9e931703dd1b9d8a2b0fe8da787f9e26058d DIFF: https://github.com/llvm/llvm-project/commit/854b9e931703dd1b9d8a2b0fe8da787f9e26058d.diff LOG: [lldb-dap] Format extension typescript. (#138925) I think the format checker isn't checking typescript files. I ran `npm run format` to fix the extenion typescript. Added: Modified: lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts lldb/tools/lldb-dap/src-ts/uri-launch-handler.ts Removed: ################################################################################ diff --git a/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts b/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts index 8a4089008b2f9..c91b101f4a9ba 100644 --- a/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts +++ b/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts @@ -21,79 +21,97 @@ async function isServerModeSupported(exe: string): Promise { } interface BoolConfig { - type: 'boolean'; + type: "boolean"; default: boolean; } interface StringConfig { - type: 'string'; + type: "string"; default: string; } interface NumberConfig { - type: 'number'; + type: "number"; default: number; } interface StringArrayConfig { - type: 'stringArray'; + type: "stringArray"; default: string[]; } -type DefaultConfig = BoolConfig | NumberConfig | StringConfig | StringArrayConfig; +type DefaultConfig = + | BoolConfig + | NumberConfig + | StringConfig + | StringArrayConfig; const configurations: Record = { // Keys for debugger configurations. - "commandEscapePrefix": { type: "string", default: "`" }, - "customFrameFormat": { type: "string", default: "" }, - "customThreadFormat": { type: "string", default: "" }, - "detachOnError": { type: "boolean", default: false }, - "disableASLR": { type: "boolean", default: true }, - "disableSTDIO": { type: "boolean", default: false }, - "displayExtendedBacktrace": { type: "boolean", default: false }, - "enableAutoVariableSummaries": { type: "boolean", default: false }, - "enableSyntheticChildDebugging": { type: "boolean", default: false }, - "timeout": { type: "number", default: 30 }, + commandEscapePrefix: { type: "string", default: "`" }, + customFrameFormat: { type: "string", default: "" }, + customThreadFormat: { type: "string", default: "" }, + detachOnError: { type: "boolean", default: false }, + disableASLR: { type: "boolean", default: true }, + disableSTDIO: { type: "boolean", default: false }, + displayExtendedBacktrace: { type: "boolean", default: false }, + enableAutoVariableSummaries: { type: "boolean", default: false }, + enableSyntheticChildDebugging: { type: "boolean", default: false }, + timeout: { type: "number", default: 30 }, // Keys for platform / target configuration. - "platformName": { type: "string", default: "" }, - "targetTriple": { type: "string", default: "" }, + platformName: { type: "string", default: "" }, + targetTriple: { type: "string", default: "" }, // Keys for debugger command hooks. - "initCommands": { type: "stringArray", default: [] }, - "preRunCommands": { type: "stringArray", default: [] }, - "postRunCommands": { type: "stringArray", default: [] }, - "stopCommands": { type: "stringArray", default: [] }, - "exitCommands": { type: "stringArray", default: [] }, - "terminateCommands": { type: "stringArray", default: [] }, + initCommands: { type: "stringArray", default: [] }, + preRunCommands: { type: "stringArray", default: [] }, + postRunCommands: { type: "stringArray", default: [] }, + stopCommands: { type: "stringArray", default: [] }, + exitCommands: { type: "stringArray", default: [] }, + terminateCommands: { type: "stringArray", default: [] }, }; export class LLDBDapConfigurationProvider - implements vscode.DebugConfigurationProvider { - constructor(private readonly server: LLDBDapServer) { } + implements vscode.DebugConfigurationProvider +{ + constructor(private readonly server: LLDBDapServer) {} async resolveDebugConfiguration( folder: vscode.WorkspaceFolder | undefined, debugConfiguration: vscode.DebugConfiguration, - token?: vscode.CancellationToken): Promise { - let config = vscode.workspace.getConfiguration('lldb-dap.defaults'); + token?: vscode.CancellationToken, + ): Promise { + let config = vscode.workspace.getConfiguration("lldb-dap.defaults"); for (const [key, cfg] of Object.entries(configurations)) { - if (Reflect.has(debugConfiguration, key)) continue; + if (Reflect.has(debugConfiguration, key)) { + continue; + } const value = config.get(key); - if (value === cfg.default) continue; + if (!value || value === cfg.default) { + continue; + } switch (cfg.type) { - case 'string': - if (typeof value !== 'string') + case "string": + if (typeof value !== "string") { throw new Error(`Expected ${key} to be a string, got ${value}`); + } break; - case 'number': - if (typeof value !== 'number') + case "number": + if (typeof value !== "number") { throw new Error(`Expected ${key} to be a number, got ${value}`); + } break; - case 'boolean': - if (typeof value !== 'boolean') + case "boolean": + if (typeof value !== "boolean") { throw new Error(`Expected ${key} to be a boolean, got ${value}`); + } break; - case 'stringArray': - if (typeof value !== 'object' && Array.isArray(value)) - throw new Error(`Expected ${key} to be a array of strings, got ${value}`); - if ((value as string[]).length === 0) continue; + case "stringArray": + if (typeof value !== "object" && Array.isArray(value)) { + throw new Error( + `Expected ${key} to be a array of strings, got ${value}`, + ); + } + if ((value as string[]).length === 0) { + continue; + } break; } diff --git a/lldb/tools/lldb-dap/src-ts/uri-launch-handler.ts b/lldb/tools/lldb-dap/src-ts/uri-launch-handler.ts index 0c3b1e9a00d9e..d45c1820eec75 100644 --- a/lldb/tools/lldb-dap/src-ts/uri-launch-handler.ts +++ b/lldb/tools/lldb-dap/src-ts/uri-launch-handler.ts @@ -1,78 +1,102 @@ import * as vscode from "vscode"; export class LaunchUriHandler implements vscode.UriHandler { - async handleUri(uri: vscode.Uri) { - try { - const params = new URLSearchParams(uri.query); - if (uri.path == '/start') { - // Some properties have default values - let debugConfig: vscode.DebugConfiguration = { - type: 'lldb-dap', - request: 'launch', - name: '', - }; - // The `config` parameter allows providing a complete JSON-encoded configuration - const configJson = params.get("config"); - if (configJson !== null) { - Object.assign(debugConfig, JSON.parse(configJson)); - } - // Furthermore, some frequently used parameters can also be provided as separate parameters - const stringKeys = ["name", "request", "program", "cwd", "debuggerRoot"]; - const numberKeys = ["pid"]; - const arrayKeys = [ - "args", "initCommands", "preRunCommands", "stopCommands", "exitCommands", - "terminateCommands", "launchCommands", "attachCommands" - ]; - for (const key of stringKeys) { - const value = params.get(key); - if (value) { - debugConfig[key] = value; - } - } - for (const key of numberKeys) { - const value = params.get(key); - if (value) { - debugConfig[key] = Number(value); - } - } - for (const key of arrayKeys) { - // `getAll()` returns an array of strings. - const value = params.getAll(key); - if (value) { - debugConfig[key] = value; - } - } - // Report an error if we received any unknown parameters - const supportedKeys = new Set(["config"].concat(stringKeys).concat(numberKeys).concat(arrayKeys)); - const presentKeys = new Set(params.keys()); - // FIXME: Use `Set. diff erence` as soon as ES2024 is widely available - const unknownKeys = new Set(); - for (const k of presentKeys.keys()) { - if (!supportedKeys.has(k)) { - unknownKeys.add(k); - } - } - if (unknownKeys.size > 0) { - throw new Error(`Unsupported URL parameters: ${Array.from(unknownKeys.keys()).join(", ")}`); - } - // Prodide a default for the config name - const defaultName = debugConfig.request == 'launch' ? "URL-based Launch" : "URL-based Attach"; - debugConfig.name = debugConfig.name || debugConfig.program || defaultName; - // Force the type to `lldb-dap`. We don't want to allow launching any other - // Debug Adapters using this URI scheme. - if (debugConfig.type != "lldb-dap") { - throw new Error(`Unsupported debugger type: ${debugConfig.type}`); - } - await vscode.debug.startDebugging(undefined, debugConfig); - } else { - throw new Error(`Unsupported Uri path: ${uri.path}`); - } - } catch (err) { - if (err instanceof Error) { - await vscode.window.showErrorMessage(`Failed to handle lldb-dap URI request: ${err.message}`); - } else { - await vscode.window.showErrorMessage(`Failed to handle lldb-dap URI request: ${JSON.stringify(err)}`); - } + async handleUri(uri: vscode.Uri) { + try { + const params = new URLSearchParams(uri.query); + if (uri.path == "/start") { + // Some properties have default values + let debugConfig: vscode.DebugConfiguration = { + type: "lldb-dap", + request: "launch", + name: "", + }; + // The `config` parameter allows providing a complete JSON-encoded configuration + const configJson = params.get("config"); + if (configJson !== null) { + Object.assign(debugConfig, JSON.parse(configJson)); } + // Furthermore, some frequently used parameters can also be provided as separate parameters + const stringKeys = [ + "name", + "request", + "program", + "cwd", + "debuggerRoot", + ]; + const numberKeys = ["pid"]; + const arrayKeys = [ + "args", + "initCommands", + "preRunCommands", + "stopCommands", + "exitCommands", + "terminateCommands", + "launchCommands", + "attachCommands", + ]; + for (const key of stringKeys) { + const value = params.get(key); + if (value) { + debugConfig[key] = value; + } + } + for (const key of numberKeys) { + const value = params.get(key); + if (value) { + debugConfig[key] = Number(value); + } + } + for (const key of arrayKeys) { + // `getAll()` returns an array of strings. + const value = params.getAll(key); + if (value) { + debugConfig[key] = value; + } + } + // Report an error if we received any unknown parameters + const supportedKeys = new Set( + ["config"].concat(stringKeys).concat(numberKeys).concat(arrayKeys), + ); + const presentKeys = new Set(params.keys()); + // FIXME: Use `Set. diff erence` as soon as ES2024 is widely available + const unknownKeys = new Set(); + for (const k of presentKeys.keys()) { + if (!supportedKeys.has(k)) { + unknownKeys.add(k); + } + } + if (unknownKeys.size > 0) { + throw new Error( + `Unsupported URL parameters: ${Array.from(unknownKeys.keys()).join(", ")}`, + ); + } + // Prodide a default for the config name + const defaultName = + debugConfig.request == "launch" + ? "URL-based Launch" + : "URL-based Attach"; + debugConfig.name = + debugConfig.name || debugConfig.program || defaultName; + // Force the type to `lldb-dap`. We don't want to allow launching any other + // Debug Adapters using this URI scheme. + if (debugConfig.type != "lldb-dap") { + throw new Error(`Unsupported debugger type: ${debugConfig.type}`); + } + await vscode.debug.startDebugging(undefined, debugConfig); + } else { + throw new Error(`Unsupported Uri path: ${uri.path}`); + } + } catch (err) { + if (err instanceof Error) { + await vscode.window.showErrorMessage( + `Failed to handle lldb-dap URI request: ${err.message}`, + ); + } else { + await vscode.window.showErrorMessage( + `Failed to handle lldb-dap URI request: ${JSON.stringify(err)}`, + ); + } } + } } From lldb-commits at lists.llvm.org Wed May 7 11:49:59 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Wed, 07 May 2025 11:49:59 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Format extension typescript. (PR #138925) In-Reply-To: Message-ID: <681bab57.170a0220.39873b.997f@mx.google.com> https://github.com/ashgti closed https://github.com/llvm/llvm-project/pull/138925 From lldb-commits at lists.llvm.org Wed May 7 12:00:06 2025 From: lldb-commits at lists.llvm.org (Dmitry Vasilyev via lldb-commits) Date: Wed, 07 May 2025 12:00:06 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][test] Disable flaky test_qThreadInfo_matches_qC_attach test on AArch64 Linux (PR #138940) Message-ID: https://github.com/slydiman created https://github.com/llvm/llvm-project/pull/138940 See #138085 for details. https://lab.llvm.org/buildbot/#/builders/59/builds/16937 https://lab.llvm.org/buildbot/#/builders/59/builds/17224 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Wed May 7 12:00:31 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 12:00:31 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][test] Disable flaky test_qThreadInfo_matches_qC_attach test on AArch64 Linux (PR #138940) In-Reply-To: Message-ID: <681badcf.050a0220.1cddbb.24eb@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Dmitry Vasilyev (slydiman)
Changes See #138085 for details. https://lab.llvm.org/buildbot/#/builders/59/builds/16937 https://lab.llvm.org/buildbot/#/builders/59/builds/17224 --- Full diff: https://github.com/llvm/llvm-project/pull/138940.diff 1 Files Affected: - (modified) lldb/test/API/tools/lldb-server/TestLldbGdbServer.py (+7) ``````````diff diff --git a/lldb/test/API/tools/lldb-server/TestLldbGdbServer.py b/lldb/test/API/tools/lldb-server/TestLldbGdbServer.py index 2c328125e3058..67690a275f0da 100644 --- a/lldb/test/API/tools/lldb-server/TestLldbGdbServer.py +++ b/lldb/test/API/tools/lldb-server/TestLldbGdbServer.py @@ -202,6 +202,13 @@ def test_qThreadInfo_matches_qC_launch(self): self.set_inferior_startup_launch() self.qThreadInfo_matches_qC() + # This test is flaky on AArch64 Linux. Sometimes it causes an unhandled Error: + # Operation not permitted in lldb_private::process_linux::NativeProcessLinux::Attach(int). + @skipIf( + oslist=["linux"], + archs=["aarch64"], + bugnumber="github.com/llvm/llvm-project/issues/138085", + ) @expectedFailureAll(oslist=["windows"]) # expect one more thread stopped def test_qThreadInfo_matches_qC_attach(self): self.build() ``````````
https://github.com/llvm/llvm-project/pull/138940 From lldb-commits at lists.llvm.org Wed May 7 12:08:27 2025 From: lldb-commits at lists.llvm.org (Mikhail Zakharov via lldb-commits) Date: Wed, 07 May 2025 12:08:27 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fixed TestProcessModificationIdOnExpr to work on both x86 and x64 architectures (PR #138941) Message-ID: https://github.com/real-mikhail created https://github.com/llvm/llvm-project/pull/138941 Original PR where test was introduced and improvements discussed: https://github.com/llvm/llvm-project/pull/129092#issuecomment-2855337004 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Wed May 7 12:09:02 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 12:09:02 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fixed TestProcessModificationIdOnExpr to work on both x86 and x64 architectures (PR #138941) In-Reply-To: Message-ID: <681bafce.170a0220.b3284.208b@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Mikhail Zakharov (real-mikhail)
Changes Original PR where test was introduced and improvements discussed: https://github.com/llvm/llvm-project/pull/129092#issuecomment-2855337004 --- Full diff: https://github.com/llvm/llvm-project/pull/138941.diff 1 Files Affected: - (modified) lldb/test/Shell/Expr/TestProcessModificationIdOnExpr.cpp (+30-12) ``````````diff diff --git a/lldb/test/Shell/Expr/TestProcessModificationIdOnExpr.cpp b/lldb/test/Shell/Expr/TestProcessModificationIdOnExpr.cpp index fe6c2fd93303c..4d67200745018 100644 --- a/lldb/test/Shell/Expr/TestProcessModificationIdOnExpr.cpp +++ b/lldb/test/Shell/Expr/TestProcessModificationIdOnExpr.cpp @@ -1,7 +1,7 @@ // Tests that ProcessModID.m_memory_id is not bumped when evaluating expressions without side effects. -// REQUIRES: target-windows && target-x86 -// Due to different implementations exact numbers (m_stop_id) are different on different OSs. So we lock this test to specific platform (Windows). It is limited to x86 because on x86, running get() +// REQUIRES: target-windows && (target-x86 || target-x86_64) +// Due to different implementations exact numbers (m_stop_id) are different on different OSs. So we lock this test to specific platform (Windows). It is limited to x86/x64 because on x86/x64, running get() // requires that we write the return address to the stack, this does not happen on AArch64. // RUN: %build %s -o %t @@ -11,10 +11,13 @@ // RUN: -o "process status -d" \ // RUN: -o "expr x.i != 42" \ // RUN: -o "process status -d" \ +// RUN: -o "process status -d" \ // RUN: -o "expr x.get()" \ // RUN: -o "process status -d" \ +// RUN: -o "process status -d" \ // RUN: -o "expr x.i = 10" \ // RUN: -o "process status -d" \ +// RUN: -o "process status -d" \ // RUN: -o "continue" \ // RUN: -o "process status -d" \ // RUN: -o "exit" | FileCheck %s -dump-input=fail @@ -36,34 +39,49 @@ int main() { } // CHECK-LABEL: process status -d -// CHECK: m_stop_id: 2 -// CHECK: m_memory_id: 0 +// CHECK: m_stop_id: [[#STOP_ID:]] +// CHECK: m_memory_id: [[#MEMORY_ID:]] // CHECK-LABEL: expr x.i != 42 // IDs are not changed when executing simple expressions // CHECK-LABEL: process status -d -// CHECK: m_stop_id: 2 -// CHECK: m_memory_id: 0 +// CHECK: m_stop_id: [[#STOP_ID]] +// CHECK: m_memory_id: [[#MEMORY_ID]] + +// CHECK-LABEL: process status -d +// Remember new values +// CHECK: m_stop_id: [[#STOP_ID:]] +// CHECK: m_memory_id: [[#MEMORY_ID:]] // CHECK-LABEL: expr x.get() // Expression causes ID to be bumped because LLDB has to execute function and in doing // so must write the return address to the stack. // CHECK-LABEL: process status -d -// CHECK: m_stop_id: 3 -// CHECK: m_memory_id: 1 +// CHECK-NOT: m_stop_id: [[#STOP_ID]] +// CHECK-NOT: m_memory_id: [[#MEMORY_ID]] + +// CHECK-LABEL: process status -d +// Remember new values +// CHECK: m_stop_id: [[#STOP_ID:]] +// CHECK: m_memory_id: [[#MEMORY_ID:]] // CHECK-LABEL: expr x.i = 10 // Expression causes MemoryID to be bumped because LLDB writes to non-cache memory // CHECK-LABEL: process status -d -// CHECK: m_stop_id: 3 -// CHECK: m_memory_id: 2 +// CHECK: m_stop_id: [[#STOP_ID]] +// CHECK-NOT: m_memory_id: [[#MEMORY_ID]] + +// CHECK-LABEL: process status -d +// Remember new values +// CHECK: m_stop_id: [[#STOP_ID:]] +// CHECK: m_memory_id: [[#MEMORY_ID:]] // CHECK-LABEL: continue // Continue causes StopID to be bumped because process is resumed // CHECK-LABEL: process status -d -// CHECK: m_stop_id: 4 -// CHECK: m_memory_id: 2 +// CHECK-NOT: m_stop_id: [[#STOP_ID]] +// CHECK: m_memory_id: [[#MEMORY_ID]] ``````````
https://github.com/llvm/llvm-project/pull/138941 From lldb-commits at lists.llvm.org Wed May 7 12:10:44 2025 From: lldb-commits at lists.llvm.org (Mikhail Zakharov via lldb-commits) Date: Wed, 07 May 2025 12:10:44 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Do not bump memory modificator ID when "internal" debugger memory is updated (PR #129092) In-Reply-To: Message-ID: <681bb034.050a0220.346761.63ba@mx.google.com> real-mikhail wrote: Thanks for the example and the link. I fixed test to work on both Windows-x86 and Windows-x64 and created new PR: https://github.com/llvm/llvm-project/pull/138941 https://github.com/llvm/llvm-project/pull/129092 From lldb-commits at lists.llvm.org Wed May 7 12:12:16 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Wed, 07 May 2025 12:12:16 -0700 (PDT) Subject: [Lldb-commits] =?utf-8?q?=5Blldb=5D_=5Blldb=5D_print_a_notice_whe?= =?utf-8?q?n_=60source_list=60_paging_reaches_the_end_of_th=E2=80=A6_=28PR?= =?utf-8?b?ICMxMzc1MTUp?= In-Reply-To: Message-ID: <681bb090.170a0220.2d1032.4dd6@mx.google.com> https://github.com/JDevlieghere approved this pull request. https://github.com/llvm/llvm-project/pull/137515 From lldb-commits at lists.llvm.org Wed May 7 12:21:56 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Wed, 07 May 2025 12:21:56 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB][Minidump] Add some buffer directories (PR #138943) Message-ID: https://github.com/Jlalond created https://github.com/llvm/llvm-project/pull/138943 Add a generous amount of buffer directories. I found out some LLDB forks (internal and external) had custom ranges that could fail because we didn't pre-account for those. To prevent this from being a problem, I've added a large number of buffer directories at the cost of 160 bytes. Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Wed May 7 12:22:28 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Wed, 07 May 2025 12:22:28 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB][Minidump] Add some buffer directories (PR #138943) In-Reply-To: Message-ID: <681bb2f4.650a0220.c8880.d8d1@mx.google.com> https://github.com/Jlalond edited https://github.com/llvm/llvm-project/pull/138943 From lldb-commits at lists.llvm.org Wed May 7 12:22:34 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 12:22:34 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB][Minidump] Add some buffer directories (PR #138943) In-Reply-To: Message-ID: <681bb2fa.170a0220.110510.21ec@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Jacob Lalonde (Jlalond)
Changes Add a generous amount of buffer directories. I found out some LLDB forks (internal and external) had custom ranges that could fail because we didn't pre-account for those. To prevent this from being a problem, I've added a large number of buffer directories at the cost of 240 bytes. --- Full diff: https://github.com/llvm/llvm-project/pull/138943.diff 1 Files Affected: - (modified) lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp (+5) ``````````diff diff --git a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp index 38806dfc8e5b5..b6c7e66e28793 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp +++ b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp @@ -75,6 +75,11 @@ Status MinidumpFileBuilder::AddHeaderAndCalculateDirectories() { } } + // Add a generous buffer of directories, these are quite small + // and forks may add new directories upstream LLDB hadn't accounted for + // when we started pre-calculating directory size, so this should account for that + m_expected_directories += 100; + m_saved_data_size += m_expected_directories * sizeof(llvm::minidump::Directory); Status error; ``````````
https://github.com/llvm/llvm-project/pull/138943 From lldb-commits at lists.llvm.org Wed May 7 12:25:24 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 12:25:24 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB][Minidump] Add some buffer directories (PR #138943) In-Reply-To: Message-ID: <681bb3a4.170a0220.24e1bd.968f@mx.google.com> github-actions[bot] wrote: :warning: C/C++ code formatter, clang-format found issues in your code. :warning:
You can test this locally with the following command: ``````````bash git-clang-format --diff HEAD~1 HEAD --extensions cpp -- lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp ``````````
View the diff from clang-format here. ``````````diff diff --git a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp index b6c7e66e2..d2ca5b26c 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp +++ b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp @@ -77,7 +77,8 @@ Status MinidumpFileBuilder::AddHeaderAndCalculateDirectories() { // Add a generous buffer of directories, these are quite small // and forks may add new directories upstream LLDB hadn't accounted for - // when we started pre-calculating directory size, so this should account for that + // when we started pre-calculating directory size, so this should account for + // that m_expected_directories += 100; m_saved_data_size += ``````````
https://github.com/llvm/llvm-project/pull/138943 From lldb-commits at lists.llvm.org Wed May 7 12:53:41 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 12:53:41 -0700 (PDT) Subject: [Lldb-commits] [lldb] 40941f1 - [LLDB][Minidump] Add some buffer directories (#138943) Message-ID: <681bba45.170a0220.31dbab.9e0a@mx.google.com> Author: Jacob Lalonde Date: 2025-05-07T12:53:37-07:00 New Revision: 40941f15962191d0236ecdc29cd6937abce974fb URL: https://github.com/llvm/llvm-project/commit/40941f15962191d0236ecdc29cd6937abce974fb DIFF: https://github.com/llvm/llvm-project/commit/40941f15962191d0236ecdc29cd6937abce974fb.diff LOG: [LLDB][Minidump] Add some buffer directories (#138943) Add a generous amount of buffer directories. I found out some LLDB forks (internal and external) had custom ranges that could fail because we didn't pre-account for those. To prevent this from being a problem, I've added a large number of buffer directories at the cost of 240 bytes. Added: Modified: lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp Removed: ################################################################################ diff --git a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp index 38806dfc8e5b5..d2ca5b26c9ec9 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp +++ b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp @@ -75,6 +75,12 @@ Status MinidumpFileBuilder::AddHeaderAndCalculateDirectories() { } } + // Add a generous buffer of directories, these are quite small + // and forks may add new directories upstream LLDB hadn't accounted for + // when we started pre-calculating directory size, so this should account for + // that + m_expected_directories += 100; + m_saved_data_size += m_expected_directories * sizeof(llvm::minidump::Directory); Status error; From lldb-commits at lists.llvm.org Wed May 7 12:53:43 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Wed, 07 May 2025 12:53:43 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB][Minidump] Add some buffer directories (PR #138943) In-Reply-To: Message-ID: <681bba47.170a0220.c7e4f.9ccf@mx.google.com> https://github.com/Jlalond closed https://github.com/llvm/llvm-project/pull/138943 From lldb-commits at lists.llvm.org Wed May 7 13:01:48 2025 From: lldb-commits at lists.llvm.org (Ely Ronnen via lldb-commits) Date: Wed, 07 May 2025 13:01:48 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Expose QueueThreadPlanForStepSingleInstruction function to SBThreadPlan (PR #137904) In-Reply-To: Message-ID: <681bbc2c.170a0220.11e4f9.a9be@mx.google.com> https://github.com/eronnen updated https://github.com/llvm/llvm-project/pull/137904 >From 998d59c6462326874d3fcd13f4cf92290c7bf5f9 Mon Sep 17 00:00:00 2001 From: Ely Ronnen Date: Wed, 30 Apr 2025 02:00:44 +0200 Subject: [PATCH 1/5] Expose QueueThreadPlanForStepSingleInstruction function to SBThreadPlan --- lldb/include/lldb/API/SBThreadPlan.h | 4 ++++ lldb/source/API/SBThreadPlan.cpp | 31 ++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/lldb/include/lldb/API/SBThreadPlan.h b/lldb/include/lldb/API/SBThreadPlan.h index d02ab80f76a76..ce1a1078d69b9 100644 --- a/lldb/include/lldb/API/SBThreadPlan.h +++ b/lldb/include/lldb/API/SBThreadPlan.h @@ -105,6 +105,10 @@ class LLDB_API SBThreadPlan { SBThreadPlan QueueThreadPlanForStepOut(uint32_t frame_idx_to_step_to, bool first_insn, SBError &error); + SBThreadPlan QueueThreadPlanForStepSingleInstruction(bool step_over); + SBThreadPlan QueueThreadPlanForStepSingleInstruction(bool step_over, + SBError &error); + SBThreadPlan QueueThreadPlanForRunToAddress(SBAddress address); SBThreadPlan QueueThreadPlanForRunToAddress(SBAddress address, SBError &error); diff --git a/lldb/source/API/SBThreadPlan.cpp b/lldb/source/API/SBThreadPlan.cpp index 083a050de8a38..a85afbb043875 100644 --- a/lldb/source/API/SBThreadPlan.cpp +++ b/lldb/source/API/SBThreadPlan.cpp @@ -325,6 +325,37 @@ SBThreadPlan::QueueThreadPlanForStepOut(uint32_t frame_idx_to_step_to, return SBThreadPlan(); } +SBThreadPlan +SBThreadPlan::QueueThreadPlanForStepSingleInstruction(bool step_over) { + LLDB_INSTRUMENT_VA(this, step_over); + + SBError error; + return QueueThreadPlanForStepSingleInstruction(step_over, error); +} + +SBThreadPlan +SBThreadPlan::QueueThreadPlanForStepSingleInstruction(bool step_over, + SBError &error) { + LLDB_INSTRUMENT_VA(this, step_over, error); + + ThreadPlanSP thread_plan_sp(GetSP()); + if (thread_plan_sp) { + Status plan_status; + SBThreadPlan plan( + thread_plan_sp->GetThread().QueueThreadPlanForStepSingleInstruction( + step_over, false, false, plan_status)); + + if (plan_status.Fail()) + error.SetErrorString(plan_status.AsCString()); + else + plan.GetSP()->SetPrivate(true); + + return plan; + } + + return SBThreadPlan(); +} + SBThreadPlan SBThreadPlan::QueueThreadPlanForRunToAddress(SBAddress sb_address) { LLDB_INSTRUMENT_VA(this, sb_address); >From 7cedaf50faba503816232fb63d077b6c7def26b1 Mon Sep 17 00:00:00 2001 From: Ely Ronnen Date: Wed, 30 Apr 2025 09:37:32 +0200 Subject: [PATCH 2/5] Add API tests for QueueThreadPlanForStepSingleInstruction --- .../functionalities/step_scripted/Steps.py | 16 +++++++++++ .../step_scripted/TestStepScripted.py | 28 +++++++++++++++++++ .../API/functionalities/step_scripted/main.c | 2 +- 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/lldb/test/API/functionalities/step_scripted/Steps.py b/lldb/test/API/functionalities/step_scripted/Steps.py index 3325dba753657..e9e0fae1b14be 100644 --- a/lldb/test/API/functionalities/step_scripted/Steps.py +++ b/lldb/test/API/functionalities/step_scripted/Steps.py @@ -45,6 +45,22 @@ def queue_child_thread_plan(self): return self.thread_plan.QueueThreadPlanForStepScripted("Steps.StepOut") +class StepSingleInstruction(StepWithChild): + def __init__(self, thread_plan, dict): + super().__init__(thread_plan) + + def queue_child_thread_plan(self): + return self.thread_plan.QueueThreadPlanForStepSingleInstruction(False) + + +class StepSingleInstructionWithStepOver(StepWithChild): + def __init__(self, thread_plan, dict): + super().__init__(thread_plan) + + def queue_child_thread_plan(self): + return self.thread_plan.QueueThreadPlanForStepSingleInstruction(True) + + # This plan does a step-over until a variable changes value. class StepUntil(StepWithChild): def __init__(self, thread_plan, args_data): diff --git a/lldb/test/API/functionalities/step_scripted/TestStepScripted.py b/lldb/test/API/functionalities/step_scripted/TestStepScripted.py index 53901718019f9..f65c366a09e87 100644 --- a/lldb/test/API/functionalities/step_scripted/TestStepScripted.py +++ b/lldb/test/API/functionalities/step_scripted/TestStepScripted.py @@ -44,6 +44,34 @@ def step_out_with_scripted_plan(self, name): stop_desc = thread.GetStopDescription(1000) self.assertIn("Stepping out from", stop_desc, "Got right description") + def test_step_single_instruction(self): + self.build() + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "Break on foo call", self.main_source_file + ) + + err = thread.StepUsingScriptedThreadPlan("Steps.StepSingleInstruction") + self.assertSuccess(err) + + # Verify that stepping a single instruction after "foo();" steps into `foo` + frame = thread.GetFrameAtIndex(0) + self.assertEqual("foo", frame.GetFunctionName()) + + def test_step_single_instruction_with_step_over(self): + self.build() + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "Break on foo call", self.main_source_file + ) + + err = thread.StepUsingScriptedThreadPlan( + "Steps.StepSingleInstructionWithStepOver" + ) + self.assertSuccess(err) + + # Verify that stepping over an instruction doesn't step into `foo` + frame = thread.GetFrameAtIndex(0) + self.assertEqual("main", frame.GetFunctionName()) + def test_misspelled_plan_name(self): """Test that we get a useful error if we misspell the plan class name""" self.build() diff --git a/lldb/test/API/functionalities/step_scripted/main.c b/lldb/test/API/functionalities/step_scripted/main.c index bfd8a35d55626..499a999e699d0 100644 --- a/lldb/test/API/functionalities/step_scripted/main.c +++ b/lldb/test/API/functionalities/step_scripted/main.c @@ -8,6 +8,6 @@ void foo() { } int main() { - foo(); + foo(); // Break on foo call. return 0; } >From b9658d5c538aa121214d32f46982e66f453d5483 Mon Sep 17 00:00:00 2001 From: Ely Ronnen Date: Wed, 30 Apr 2025 22:32:57 +0200 Subject: [PATCH 3/5] improve QueueThreadPlanForStepSingleInstruction tests --- .../step_scripted/TestStepScripted.py | 26 ++++++++++++++----- .../API/functionalities/step_scripted/main.c | 2 +- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/lldb/test/API/functionalities/step_scripted/TestStepScripted.py b/lldb/test/API/functionalities/step_scripted/TestStepScripted.py index f65c366a09e87..54bc154590ed0 100644 --- a/lldb/test/API/functionalities/step_scripted/TestStepScripted.py +++ b/lldb/test/API/functionalities/step_scripted/TestStepScripted.py @@ -44,11 +44,23 @@ def step_out_with_scripted_plan(self, name): stop_desc = thread.GetStopDescription(1000) self.assertIn("Stepping out from", stop_desc, "Got right description") - def test_step_single_instruction(self): + def run_until_branch_instruction(self): self.build() (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( - self, "Break on foo call", self.main_source_file + self, "Break on branch instruction", self.main_source_file + ) + + # Check that we landed in a call instruction + frame = thread.GetFrameAtIndex(0) + current_instruction = target.ReadInstructions(frame.GetPCAddress(), 1)[0] + self.assertEqual( + lldb.eInstructionControlFlowKindCall, + current_instruction.GetControlFlowKind(target), ) + return (target, process, thread, bkpt) + + def test_step_single_instruction(self): + (target, process, thread, bkpt) = self.run_until_branch_instruction() err = thread.StepUsingScriptedThreadPlan("Steps.StepSingleInstruction") self.assertSuccess(err) @@ -58,10 +70,11 @@ def test_step_single_instruction(self): self.assertEqual("foo", frame.GetFunctionName()) def test_step_single_instruction_with_step_over(self): - self.build() - (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( - self, "Break on foo call", self.main_source_file - ) + (target, process, thread, bkpt) = self.run_until_branch_instruction() + + frame = thread.GetFrameAtIndex(0) + next_instruction = target.ReadInstructions(frame.GetPCAddress(), 2)[1] + next_instruction_address = next_instruction.GetAddress() err = thread.StepUsingScriptedThreadPlan( "Steps.StepSingleInstructionWithStepOver" @@ -71,6 +84,7 @@ def test_step_single_instruction_with_step_over(self): # Verify that stepping over an instruction doesn't step into `foo` frame = thread.GetFrameAtIndex(0) self.assertEqual("main", frame.GetFunctionName()) + self.assertEqual(next_instruction_address, frame.GetPCAddress()) def test_misspelled_plan_name(self): """Test that we get a useful error if we misspell the plan class name""" diff --git a/lldb/test/API/functionalities/step_scripted/main.c b/lldb/test/API/functionalities/step_scripted/main.c index 499a999e699d0..9023120c44312 100644 --- a/lldb/test/API/functionalities/step_scripted/main.c +++ b/lldb/test/API/functionalities/step_scripted/main.c @@ -8,6 +8,6 @@ void foo() { } int main() { - foo(); // Break on foo call. + foo(); // Break on branch instruction. return 0; } >From 4d7a735bb8d7a8298d529ff4f8a90d48713b60a1 Mon Sep 17 00:00:00 2001 From: Ely Ronnen Date: Thu, 1 May 2025 21:52:11 +0200 Subject: [PATCH 4/5] force QueueThreadPlanForStepSingleInstruction to accept SBError --- lldb/include/lldb/API/SBThreadPlan.h | 1 - lldb/source/API/SBThreadPlan.cpp | 8 -------- lldb/test/API/functionalities/step_scripted/Steps.py | 4 ++-- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/lldb/include/lldb/API/SBThreadPlan.h b/lldb/include/lldb/API/SBThreadPlan.h index ce1a1078d69b9..1f0164efcfb98 100644 --- a/lldb/include/lldb/API/SBThreadPlan.h +++ b/lldb/include/lldb/API/SBThreadPlan.h @@ -105,7 +105,6 @@ class LLDB_API SBThreadPlan { SBThreadPlan QueueThreadPlanForStepOut(uint32_t frame_idx_to_step_to, bool first_insn, SBError &error); - SBThreadPlan QueueThreadPlanForStepSingleInstruction(bool step_over); SBThreadPlan QueueThreadPlanForStepSingleInstruction(bool step_over, SBError &error); diff --git a/lldb/source/API/SBThreadPlan.cpp b/lldb/source/API/SBThreadPlan.cpp index a85afbb043875..c8ca6c81a3efb 100644 --- a/lldb/source/API/SBThreadPlan.cpp +++ b/lldb/source/API/SBThreadPlan.cpp @@ -325,14 +325,6 @@ SBThreadPlan::QueueThreadPlanForStepOut(uint32_t frame_idx_to_step_to, return SBThreadPlan(); } -SBThreadPlan -SBThreadPlan::QueueThreadPlanForStepSingleInstruction(bool step_over) { - LLDB_INSTRUMENT_VA(this, step_over); - - SBError error; - return QueueThreadPlanForStepSingleInstruction(step_over, error); -} - SBThreadPlan SBThreadPlan::QueueThreadPlanForStepSingleInstruction(bool step_over, SBError &error) { diff --git a/lldb/test/API/functionalities/step_scripted/Steps.py b/lldb/test/API/functionalities/step_scripted/Steps.py index e9e0fae1b14be..bf4c1f38cbbb3 100644 --- a/lldb/test/API/functionalities/step_scripted/Steps.py +++ b/lldb/test/API/functionalities/step_scripted/Steps.py @@ -50,7 +50,7 @@ def __init__(self, thread_plan, dict): super().__init__(thread_plan) def queue_child_thread_plan(self): - return self.thread_plan.QueueThreadPlanForStepSingleInstruction(False) + return self.thread_plan.QueueThreadPlanForStepSingleInstruction(False, lldb.SBError()) class StepSingleInstructionWithStepOver(StepWithChild): @@ -58,7 +58,7 @@ def __init__(self, thread_plan, dict): super().__init__(thread_plan) def queue_child_thread_plan(self): - return self.thread_plan.QueueThreadPlanForStepSingleInstruction(True) + return self.thread_plan.QueueThreadPlanForStepSingleInstruction(True, lldb.SBError()) # This plan does a step-over until a variable changes value. >From 1faea156f7d65689a3cfba7bec1a42c6b5cca430 Mon Sep 17 00:00:00 2001 From: Ely Ronnen Date: Thu, 1 May 2025 21:54:52 +0200 Subject: [PATCH 5/5] format --- lldb/test/API/functionalities/step_scripted/Steps.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lldb/test/API/functionalities/step_scripted/Steps.py b/lldb/test/API/functionalities/step_scripted/Steps.py index bf4c1f38cbbb3..e2a03c9988111 100644 --- a/lldb/test/API/functionalities/step_scripted/Steps.py +++ b/lldb/test/API/functionalities/step_scripted/Steps.py @@ -50,7 +50,9 @@ def __init__(self, thread_plan, dict): super().__init__(thread_plan) def queue_child_thread_plan(self): - return self.thread_plan.QueueThreadPlanForStepSingleInstruction(False, lldb.SBError()) + return self.thread_plan.QueueThreadPlanForStepSingleInstruction( + False, lldb.SBError() + ) class StepSingleInstructionWithStepOver(StepWithChild): @@ -58,7 +60,9 @@ def __init__(self, thread_plan, dict): super().__init__(thread_plan) def queue_child_thread_plan(self): - return self.thread_plan.QueueThreadPlanForStepSingleInstruction(True, lldb.SBError()) + return self.thread_plan.QueueThreadPlanForStepSingleInstruction( + True, lldb.SBError() + ) # This plan does a step-over until a variable changes value. From lldb-commits at lists.llvm.org Wed May 7 13:39:45 2025 From: lldb-commits at lists.llvm.org (Felipe de Azevedo Piovezan via lldb-commits) Date: Wed, 07 May 2025 13:39:45 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fix dynamic type resolutions for core files (PR #138698) In-Reply-To: Message-ID: <681bc511.170a0220.d31b8.16c9@mx.google.com> felipepiovezan wrote: Btw I think this is causing an asan failure: https://green.lab.llvm.org/job/lldb-cmake-sanitized/1671/ ``` 2025-05-07T19:03:57.557Z] ==73219==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x0001070cf5b8 at pc 0x0001056051c0 bp 0x00016b0a3730 sp 0x00016b0a2ee0 [2025-05-07T19:03:57.557Z] READ of size 120 at 0x0001070cf5b8 thread T0 [2025-05-07T19:03:57.557Z] #0 0x1056051bc in __asan_memcpy+0x394 (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x511bc) [2025-05-07T19:03:57.557Z] #1 0x13656e5a4 in MinidumpFileBuilder::AddExceptions() MinidumpFileBuilder.cpp:714 [2025-05-07T19:03:57.557Z] #2 0x136565688 in ObjectFileMinidump::SaveCore(std::__1::shared_ptr const&, lldb_private::SaveCoreOptions&, lldb_private::Status&) ObjectFileMinidump.cpp:132 [2025-05-07T19:03:57.557Z] #3 0x1394034f4 in lldb_private::PluginManager::SaveCore(std::__1::shared_ptr const&, lldb_pri ... [2025-05-07T19:03:57.558Z] Address 0x0001070cf5b8 is located in stack of thread T0 at offset 440 in frame [2025-05-07T19:03:57.558Z] #0 0x13656dfa4 in MinidumpFileBuilder::AddExceptions() MinidumpFileBuilder.cpp:685 [2025-05-07T19:03:57.558Z] [2025-05-07T19:03:57.558Z] This frame has 10 object(s): [2025-05-07T19:03:57.558Z] [32, 40) 'ref.tmp.i.i' [2025-05-07T19:03:57.558Z] [64, 88) 'thread_list' (line 686) [2025-05-07T19:03:57.558Z] [128, 144) 'stop_info_sp' (line 690) [2025-05-07T19:03:57.558Z] [160, 192) 'ref.tmp' (line 698) [2025-05-07T19:03:57.558Z] [224, 240) 'reg_ctx_sp' (line 702) [2025-05-07T19:03:57.558Z] [256, 376) 'exp_record.sroa.10' (line 703) [2025-05-07T19:03:57.558Z] [416, 440) 'description' (line 711) [2025-05-07T19:03:57.558Z] [480, 648) 'exp_stream' (line 717) <== Memory access at offset 440 partially underflows this variable [2025-05-07T19:03:57.558Z] [720, 728) 'Iter' (line 722) [2025-05-07T19:03:57.558Z] [752, 760) 'ref.tmp151' (line 722) [2025-05-07T19:03:57.558Z] HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork [2025-05-07T19:03:57.558Z] (longjmp and C++ exceptions *are* supported) [2025-05-07T19:03:57.558Z] SUMMARY: AddressSanitizer: stack-buffer-overflow (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x511bc) in __asan_memcpy+0x394 [2025-05-07T19:03:57.558Z] Shadow bytes around the buggy address: [2025-05-07T19:03:57.558Z] 0x0001070cf300: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 [2025-05-07T19:03:57.558Z] 0x0001070cf380: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 [2025-05-07T19:03:57.558Z] 0x0001070cf400: f1 f1 f1 f1 f8 f2 f2 f2 00 00 00 f2 f2 f2 f2 f2 [2025-05-07T19:03:57.558Z] 0x0001070cf480: 00 00 f2 f2 f8 f8 f8 f8 f2 f2 f2 f2 00 00 f2 f2 [2025-05-07T19:03:57.558Z] 0x0001070cf500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f2 [2025-05-07T19:03:57.558Z] =>0x0001070cf580: f2 f2 f2 f2 00 00 00[f2]f2 f2 f2 f2 f8 f8 f8 f8 [2025-05-07T19:03:57.558Z] 0x0001070cf600: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 [2025-05-07T19:03:57.558Z] 0x0001070cf680: f8 f2 f2 f2 f2 f2 f2 f2 f2 f2 f8 f2 f2 f2 f8 f3 [2025-05-07T19:03:57.558Z] 0x0001070cf700: f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 [2025-05-07T19:03:57.558Z] 0x0001070cf780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [2025-05-07T19:03:57.558Z] 0x0001070cf800: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 [2025-05-07T19:03:57.558Z] Shadow byte legend (one shadow byte represents 8 application bytes): ``` https://github.com/llvm/llvm-project/pull/138698 From lldb-commits at lists.llvm.org Wed May 7 14:53:52 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 14:53:52 -0700 (PDT) Subject: [Lldb-commits] [clang] [flang] [lld] [lldb] [llvm] [mlir] [polly] [CMake] respect LLVMConfig.cmake's LLVM_DEFINITIONS in standalone builds (PR #138587) In-Reply-To: Message-ID: <681bd670.170a0220.35024e.2320@mx.google.com> https://github.com/jeremyd2019 edited https://github.com/llvm/llvm-project/pull/138587 From lldb-commits at lists.llvm.org Wed May 7 15:01:41 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Wed, 07 May 2025 15:01:41 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Adding a modules explorer to lldb-dap ext. (PR #138977) Message-ID: https://github.com/ashgti created https://github.com/llvm/llvm-project/pull/138977 This creates a very basic module explorer for tracking and displaying loaded modules, reported by lldb-dap for the active debug session. This includes a basic session tracker that we can use to observe the debug session and collect specific information for additional visualizations in the lldb-dap ext. Here is a screenshot of the current visualization in the tree view. There is some unfortunate wrapping of the path, but it shows the basic support that could be extended in the future. Screenshot 2025-05-07 at 2 52 50 PM From lldb-commits at lists.llvm.org Wed May 7 15:02:17 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 15:02:17 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Adding a modules explorer to lldb-dap ext. (PR #138977) In-Reply-To: Message-ID: <681bd869.170a0220.d302d.bd5d@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: John Harrison (ashgti)
Changes This creates a very basic module explorer for tracking and displaying loaded modules, reported by lldb-dap for the active debug session. This includes a basic session tracker that we can use to observe the debug session and collect specific information for additional visualizations in the lldb-dap ext. Here is a screenshot of the current visualization in the tree view. There is some unfortunate wrapping of the path, but it shows the basic support that could be extended in the future. <img width="1759" alt="Screenshot 2025-05-07 at 2 52 50 PM" src="https://github.com/user-attachments/assets/588baa2f-61d5-4434-8692-b1d0cce42875" /> --- Patch is 53.76 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/138977.diff 7 Files Affected: - (modified) lldb/tools/lldb-dap/package-lock.json (+10-2) - (modified) lldb/tools/lldb-dap/package.json (+498-487) - (modified) lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts (+3-2) - (added) lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts (+87) - (modified) lldb/tools/lldb-dap/src-ts/disposable-context.ts (+2-2) - (modified) lldb/tools/lldb-dap/src-ts/extension.ts (+18) - (added) lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts (+58) ``````````diff diff --git a/lldb/tools/lldb-dap/package-lock.json b/lldb/tools/lldb-dap/package-lock.json index ab5c7dc33a8e5..0a2b9e764067e 100644 --- a/lldb/tools/lldb-dap/package-lock.json +++ b/lldb/tools/lldb-dap/package-lock.json @@ -1,16 +1,17 @@ { "name": "lldb-dap", - "version": "0.2.10", + "version": "0.2.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "lldb-dap", - "version": "0.2.10", + "version": "0.2.13", "license": "Apache 2.0 License with LLVM exceptions", "devDependencies": { "@types/node": "^18.19.41", "@types/vscode": "1.75.0", + "@vscode/debugprotocol": "^1.68.0", "@vscode/vsce": "^3.2.2", "prettier": "^3.4.2", "prettier-plugin-curly": "^0.3.1", @@ -405,6 +406,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@vscode/debugprotocol": { + "version": "1.68.0", + "resolved": "https://registry.npmjs.org/@vscode/debugprotocol/-/debugprotocol-1.68.0.tgz", + "integrity": "sha512-2J27dysaXmvnfuhFGhfeuxfHRXunqNPxtBoR3koiTOA9rdxWNDTa1zIFLCFMSHJ9MPTPKFcBeblsyaCJCIlQxg==", + "dev": true, + "license": "MIT" + }, "node_modules/@vscode/vsce": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.2.2.tgz", diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json index 4734c9d7277bb..ae4973a1c6985 100644 --- a/lldb/tools/lldb-dap/package.json +++ b/lldb/tools/lldb-dap/package.json @@ -30,9 +30,10 @@ "devDependencies": { "@types/node": "^18.19.41", "@types/vscode": "1.75.0", + "@vscode/debugprotocol": "^1.68.0", "@vscode/vsce": "^3.2.2", - "prettier-plugin-curly": "^0.3.1", "prettier": "^3.4.2", + "prettier-plugin-curly": "^0.3.1", "typescript": "^5.7.3" }, "activationEvents": [ @@ -242,527 +243,537 @@ } } } - ] - }, - "breakpoints": [ - { - "language": "ada" - }, - { - "language": "arm" - }, - { - "language": "asm" - }, - { - "language": "c" - }, - { - "language": "cpp" - }, - { - "language": "crystal" - }, - { - "language": "d" - }, - { - "language": "fortan" - }, - { - "language": "fortran-modern" - }, - { - "language": "nim" - }, - { - "language": "objective-c" - }, - { - "language": "objectpascal" - }, - { - "language": "pascal" - }, - { - "language": "rust" - }, - { - "language": "swift" - } - ], - "debuggers": [ - { - "type": "lldb-dap", - "label": "LLDB DAP Debugger", - "configurationAttributes": { - "launch": { - "required": [ - "program" - ], - "properties": { - "debugAdapterHostname": { - "type": "string", - "markdownDescription": "The hostname that an existing lldb-dap executable is listening on." - }, - "debugAdapterPort": { - "type": "number", - "markdownDescription": "The port that an existing lldb-dap executable is listening on." - }, - "debugAdapterExecutable": { - "type": "string", - "markdownDescription": "The absolute path to the LLDB debug adapter executable to use. Overrides any user or workspace settings." - }, - "debugAdapterArgs": { - "type": "array", - "items": { - "type": "string" + ], + "breakpoints": [ + { + "language": "ada" + }, + { + "language": "arm" + }, + { + "language": "asm" + }, + { + "language": "c" + }, + { + "language": "cpp" + }, + { + "language": "crystal" + }, + { + "language": "d" + }, + { + "language": "fortan" + }, + { + "language": "fortran-modern" + }, + { + "language": "nim" + }, + { + "language": "objective-c" + }, + { + "language": "objectpascal" + }, + { + "language": "pascal" + }, + { + "language": "rust" + }, + { + "language": "swift" + } + ], + "debuggers": [ + { + "type": "lldb-dap", + "label": "LLDB DAP Debugger", + "configurationAttributes": { + "launch": { + "required": [ + "program" + ], + "properties": { + "debugAdapterHostname": { + "type": "string", + "markdownDescription": "The hostname that an existing lldb-dap executable is listening on." }, - "markdownDescription": "The list of additional arguments used to launch the debug adapter executable. Overrides any user or workspace settings." - }, - "program": { - "type": "string", - "description": "Path to the program to debug." - }, - "args": { - "type": [ - "array" - ], - "items": { - "type": "string" - }, - "description": "Program arguments.", - "default": [] - }, - "cwd": { - "type": "string", - "description": "Program working directory.", - "default": "${workspaceRoot}" - }, - "env": { - "anyOf": [ - { - "type": "object", - "description": "Additional environment variables to set when launching the program. E.g. `{ \"FOO\": \"1\" }`", - "patternProperties": { - ".*": { - "type": "string" - } - }, - "default": {} + "debugAdapterPort": { + "type": "number", + "markdownDescription": "The port that an existing lldb-dap executable is listening on." + }, + "debugAdapterExecutable": { + "type": "string", + "markdownDescription": "The absolute path to the LLDB debug adapter executable to use. Overrides any user or workspace settings." + }, + "debugAdapterArgs": { + "type": "array", + "items": { + "type": "string" }, - { - "type": "array", - "description": "Additional environment variables to set when launching the program. E.g. `[\"FOO=1\", \"BAR\"]`", - "items": { - "type": "string", - "pattern": "^((\\w+=.*)|^\\w+)$" - }, - "default": [] - } - ] - }, - "stopOnEntry": { - "type": "boolean", - "description": "Automatically stop after launch.", - "default": false - }, - "disableASLR": { - "type": "boolean", - "description": "Enable or disable Address space layout randomization if the debugger supports it.", - "default": true - }, - "disableSTDIO": { - "type": "boolean", - "description": "Don't retrieve STDIN, STDOUT and STDERR as the program is running.", - "default": false - }, - "shellExpandArguments": { - "type": "boolean", - "description": "Expand program arguments as a shell would without actually launching the program in a shell.", - "default": false - }, - "detachOnError": { - "type": "boolean", - "description": "Detach from the program.", - "default": false - }, - "sourcePath": { - "type": "string", - "description": "Specify a source path to remap \"./\" to allow full paths to be used when setting breakpoints in binaries that have relative source paths." - }, - "sourceMap": { - "anyOf": [ - { - "type": "object", - "description": "Specify an object of path remappings; each entry has a key containing the source path and a value containing the destination path. E.g `{ \"/the/source/path\": \"/the/destination/path\" }`. Overrides sourcePath.", - "patternProperties": { - ".*": { - "type": "string" - } - }, - "default": {} + "markdownDescription": "The list of additional arguments used to launch the debug adapter executable. Overrides any user or workspace settings." + }, + "program": { + "type": "string", + "description": "Path to the program to debug." + }, + "args": { + "type": [ + "array" + ], + "items": { + "type": "string" }, - { - "type": "array", - "description": "Specify an array of path remappings; each element must itself be a two element array containing a source and destination path name. Overrides sourcePath.", - "items": { + "description": "Program arguments.", + "default": [] + }, + "cwd": { + "type": "string", + "description": "Program working directory.", + "default": "${workspaceRoot}" + }, + "env": { + "anyOf": [ + { + "type": "object", + "description": "Additional environment variables to set when launching the program. E.g. `{ \"FOO\": \"1\" }`", + "patternProperties": { + ".*": { + "type": "string" + } + }, + "default": {} + }, + { "type": "array", - "minItems": 2, - "maxItems": 2, + "description": "Additional environment variables to set when launching the program. E.g. `[\"FOO=1\", \"BAR\"]`", "items": { - "type": "string" - } + "type": "string", + "pattern": "^((\\w+=.*)|^\\w+)$" + }, + "default": [] + } + ] + }, + "stopOnEntry": { + "type": "boolean", + "description": "Automatically stop after launch.", + "default": false + }, + "disableASLR": { + "type": "boolean", + "description": "Enable or disable Address space layout randomization if the debugger supports it.", + "default": true + }, + "disableSTDIO": { + "type": "boolean", + "description": "Don't retrieve STDIN, STDOUT and STDERR as the program is running.", + "default": false + }, + "shellExpandArguments": { + "type": "boolean", + "description": "Expand program arguments as a shell would without actually launching the program in a shell.", + "default": false + }, + "detachOnError": { + "type": "boolean", + "description": "Detach from the program.", + "default": false + }, + "sourcePath": { + "type": "string", + "description": "Specify a source path to remap \"./\" to allow full paths to be used when setting breakpoints in binaries that have relative source paths." + }, + "sourceMap": { + "anyOf": [ + { + "type": "object", + "description": "Specify an object of path remappings; each entry has a key containing the source path and a value containing the destination path. E.g `{ \"/the/source/path\": \"/the/destination/path\" }`. Overrides sourcePath.", + "patternProperties": { + ".*": { + "type": "string" + } + }, + "default": {} }, - "default": [] - } - ] - }, - "debuggerRoot": { - "type": "string", - "description": "Specify a working directory to set the debug adapter to so relative object files can be located." - }, - "targetTriple": { - "type": "string", - "description": "Triplet of the target architecture to override value derived from the program file." - }, - "platformName": { - "type": "string", - "description": "Name of the execution platform to override value derived from the program file." - }, - "initCommands": { - "type": "array", - "items": { - "type": "string" + { + "type": "array", + "description": "Specify an array of path remappings; each element must itself be a two element array containing a source and destination path name. Overrides sourcePath.", + "items": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "string" + } + }, + "default": [] + } + ] }, - "description": "Initialization commands executed upon debugger startup.", - "default": [] - }, - "preRunCommands": { - "type": "array", - "items": { - "type": "string" + "debuggerRoot": { + "type": "string", + "description": "Specify a working directory to set the debug adapter to so relative object files can be located." }, - "description": "Commands executed just before the program is launched.", - "default": [] - }, - "postRunCommands": { - "type": "array", - "items": { - "type": "string" + "targetTriple": { + "type": "string", + "description": "Triplet of the target architecture to override value derived from the program file." }, - "description": "Commands executed just as soon as the program is successfully launched when it's in a stopped state prior to any automatic continuation.", - "default": [] - }, - "launchCommands": { - "type": "array", - "items": { - "type": "string" + "platformName": { + "type": "string", + "description": "Name of the execution platform to override value derived from the program file." }, - "description": "Custom commands that are executed instead of launching a process. A target will be created with the launch arguments prior to executing these commands. The commands may optionally create a new target and must perform a launch. A valid process must exist after these commands complete or the \"launch\" will fail. Launch the process with \"process launch -s\" to make the process to at the entry point since lldb-dap will auto resume if necessary.", - "default": [] - }, - "stopCommands": { - "type": "array", - "items": { - "type": "string" + "initCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Initialization commands executed upon debugger startup.", + "default": [] }, - "description": "Commands executed each time the program stops.", - "default": [] - }, - "exitCommands": { - "type": "array", - "items": { - "type": "string" + "preRunCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Commands executed just before the program is launched.", + "default": [] }, - "description": "Commands executed when the program exits.", - "default": [] - }, - "terminateCommands": { - "type": "array", - "items": { - "type": "string" + "postRunCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Commands executed just as soon as the program is successfully launched when it's in a stopped state prior to any automatic continuation.", + "default": [] }, - "description": "Commands executed when the debugging session ends.", - "default": [] - }, - "runInTerminal": { - "type": "boolean", - "description": "Launch the program inside an integrated terminal in the IDE. Useful for debugging interactive command line programs", - "default": false - }, - "timeout": { - "type": "number", - "description": "The time in seconds to wait for a program to stop at entry point when launching with \"launchCommands\". Defaults to 30 seconds." - }, - "enableAutoVariableSummaries": { - "type": "boolean", - "description": "Enable auto generated summaries for variables when no summaries exist for a given type. This feature can cause performance delays in large projects when viewing variables.", - "default": false - }, - "displayExtendedBacktrace": { - "type": "boolean", - "description": "Enable language specific extended backtraces.", - "default": false - }, - "enableSyntheticChildDebugging": { - "type": "boolean", - "description": "If a variable is displayed using a synthetic children, also display the actual contents of the variable at the end under a [raw] entry. This is useful when creating sythetic child plug-ins as it lets you see the actual contents of the variable.", - "default": false - }, - "commandEscapePrefix": { - "type": "string", - "description": "The escape prefix to use for executing regular LLDB commands in the Debug Console, instead of printing variables. Defaults to a back-tick (`). If it's an empty string, then all expression in the Debug Console are treated as regular LLDB commands.", - "default": "`" - }, - "customFrameFormat": { - "type": "string", - "description": "If non-empty, stack frames will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for frames. If the format string contains errors, an error message will be displayed on the Debug Console and the default frame names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.", - "default": "" - }, - "cust... [truncated] ``````````
https://github.com/llvm/llvm-project/pull/138977 From lldb-commits at lists.llvm.org Wed May 7 15:03:57 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Wed, 07 May 2025 15:03:57 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Format extension typescript. (PR #138925) In-Reply-To: Message-ID: <681bd8cd.170a0220.357f90.247f@mx.google.com> ashgti wrote: Oh, that script should already be defined in the package.json, that was the script I used for formatting. https://github.com/llvm/llvm-project/pull/138925 From lldb-commits at lists.llvm.org Wed May 7 15:04:25 2025 From: lldb-commits at lists.llvm.org (Philip Reames via lldb-commits) Date: Wed, 07 May 2025 15:04:25 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][test] Disable flaky test_qThreadInfo_matches_qC_attach test on AArch64 Linux (PR #138940) In-Reply-To: Message-ID: <681bd8e9.170a0220.2b6b2.1bb4@mx.google.com> https://github.com/preames approved this pull request. LGTM https://github.com/llvm/llvm-project/pull/138940 From lldb-commits at lists.llvm.org Wed May 7 15:07:01 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Wed, 07 May 2025 15:07:01 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Adding a modules explorer to lldb-dap ext. (PR #138977) In-Reply-To: Message-ID: <681bd985.170a0220.1330.191d@mx.google.com> https://github.com/ashgti updated https://github.com/llvm/llvm-project/pull/138977 From lldb-commits at lists.llvm.org Wed May 7 15:27:37 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Wed, 07 May 2025 15:27:37 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Change the launch sequence (reland) (PR #138981) Message-ID: https://github.com/JDevlieghere created https://github.com/llvm/llvm-project/pull/138981 This PR changes how we treat the launch sequence in lldb-dap. - Send the initialized event after we finish handling the initialize request, rather than after we finish attaching or launching. - Delay handling the launch and attach request until we have handled the configurationDone request. The latter is now largely a NO-OP and only exists to signal lldb-dap that it can handle the launch and attach requests. - Delay handling the initial threads requests until we have handled the launch or attach request. - Make all attaching and launching synchronous, including when we have attach or launch commands. This removes the need to synchronize between the request and event thread. Background: https://discourse.llvm.org/t/reliability-of-the-lldb-dap-tests/86125 >From 8c7a01465891c83f7cc99ee95b0852c56e0f150f Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Tue, 6 May 2025 15:58:44 -0700 Subject: [PATCH 1/2] [lldb-dap] Change the launch sequence (#138219) This PR changes how we treat the launch sequence in lldb-dap. - Send the initialized event after we finish handling the initialize request, rather than after we finish attaching or launching. - Delay handling the launch and attach request until we have handled the configurationDone request. The latter is now largely a NO-OP and only exists to signal lldb-dap that it can handle the launch and attach requests. - Delay handling the initial threads requests until we have handled the launch or attach request. - Make all attaching and launching synchronous, including when we have attach or launch commands. This removes the need to synchronize between the request and event thread. Background: https://discourse.llvm.org/t/reliability-of-the-lldb-dap-tests/86125 --- .../test/tools/lldb-dap/dap_server.py | 65 +++++----- .../test/tools/lldb-dap/lldbdap_testcase.py | 7 ++ .../tools/lldb-dap/attach/TestDAP_attach.py | 2 + .../attach/TestDAP_attachByPortNum.py | 8 +- .../TestDAP_breakpointEvents.py | 61 +++------- .../completions/TestDAP_completions.py | 6 +- .../tools/lldb-dap/console/TestDAP_console.py | 2 +- .../lldb-dap/disconnect/TestDAP_disconnect.py | 6 +- .../lldb-dap/evaluate/TestDAP_evaluate.py | 5 +- .../tools/lldb-dap/launch/TestDAP_launch.py | 4 +- .../lldb-dap/progress/TestDAP_Progress.py | 2 +- .../repl-mode/TestDAP_repl_mode_detection.py | 2 +- .../tools/lldb-dap/restart/TestDAP_restart.py | 1 - .../restart/TestDAP_restart_runInTerminal.py | 1 - .../lldb-dap/stop-hooks/TestDAP_stop_hooks.py | 2 +- lldb/tools/lldb-dap/DAP.cpp | 39 ++++-- lldb/tools/lldb-dap/DAP.h | 8 +- lldb/tools/lldb-dap/EventHelper.cpp | 2 +- .../lldb-dap/Handler/AttachRequestHandler.cpp | 115 ++++++++++-------- .../ConfigurationDoneRequestHandler.cpp | 14 +-- .../Handler/InitializeRequestHandler.cpp | 44 +++---- .../lldb-dap/Handler/LaunchRequestHandler.cpp | 7 +- .../tools/lldb-dap/Handler/RequestHandler.cpp | 67 ++++++---- lldb/tools/lldb-dap/Handler/RequestHandler.h | 1 + 24 files changed, 252 insertions(+), 219 deletions(-) diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py index 6d9ab770684f1..e10342b72f4f0 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py @@ -132,7 +132,6 @@ def __init__(self, recv, send, init_commands, log_file=None): self.exit_status = None self.initialize_body = None self.thread_stop_reasons = {} - self.breakpoint_events = [] self.progress_events = [] self.reverse_requests = [] self.module_events = [] @@ -244,13 +243,6 @@ def handle_recv_packet(self, packet): self._process_stopped() tid = body["threadId"] self.thread_stop_reasons[tid] = body - elif event == "breakpoint": - # Breakpoint events come in when a breakpoint has locations - # added or removed. Keep track of them so we can look for them - # in tests. - self.breakpoint_events.append(packet) - # no need to add 'breakpoint' event packets to our packets list - return keepGoing elif event.startswith("progress"): # Progress events come in as 'progressStart', 'progressUpdate', # and 'progressEnd' events. Keep these around in case test @@ -412,6 +404,15 @@ def wait_for_stopped(self, timeout=None): self.threads = [] return stopped_events + def wait_for_breakpoint_events(self, timeout=None): + breakpoint_events = [] + while True: + event = self.wait_for_event("breakpoint", timeout=timeout) + if not event: + break + breakpoint_events.append(event) + return breakpoint_events + def wait_for_exited(self): event_dict = self.wait_for_event("exited") if event_dict is None: @@ -591,6 +592,7 @@ def request_attach( attachCommands=None, terminateCommands=None, coreFile=None, + stopOnAttach=True, postRunCommands=None, sourceMap=None, gdbRemotePort=None, @@ -620,6 +622,8 @@ def request_attach( args_dict["attachCommands"] = attachCommands if coreFile: args_dict["coreFile"] = coreFile + if stopOnAttach: + args_dict["stopOnEntry"] = stopOnAttach if postRunCommands: args_dict["postRunCommands"] = postRunCommands if sourceMap: @@ -632,7 +636,7 @@ def request_attach( response = self.send_recv(command_dict) if response["success"]: - self.wait_for_events(["process", "initialized"]) + self.wait_for_event("process") return response def request_breakpointLocations( @@ -666,10 +670,6 @@ def request_configurationDone(self): response = self.send_recv(command_dict) if response: self.configuration_done_sent = True - # Client requests the baseline of currently existing threads after - # a successful launch or attach. - # Kick off the threads request that follows - self.request_threads() return response def _process_stopped(self): @@ -887,7 +887,7 @@ def request_launch( response = self.send_recv(command_dict) if response["success"]: - self.wait_for_events(["process", "initialized"]) + self.wait_for_event("process") return response def request_next(self, threadId, granularity="statement"): @@ -1325,6 +1325,26 @@ def attach_options_specified(options): def run_vscode(dbg, args, options): dbg.request_initialize(options.sourceInitFile) + + if options.sourceBreakpoints: + source_to_lines = {} + for file_line in options.sourceBreakpoints: + (path, line) = file_line.split(":") + if len(path) == 0 or len(line) == 0: + print('error: invalid source with line "%s"' % (file_line)) + + else: + if path in source_to_lines: + source_to_lines[path].append(int(line)) + else: + source_to_lines[path] = [int(line)] + for source in source_to_lines: + dbg.request_setBreakpoints(source, source_to_lines[source]) + if options.funcBreakpoints: + dbg.request_setFunctionBreakpoints(options.funcBreakpoints) + + dbg.request_configurationDone() + if attach_options_specified(options): response = dbg.request_attach( program=options.program, @@ -1353,23 +1373,6 @@ def run_vscode(dbg, args, options): ) if response["success"]: - if options.sourceBreakpoints: - source_to_lines = {} - for file_line in options.sourceBreakpoints: - (path, line) = file_line.split(":") - if len(path) == 0 or len(line) == 0: - print('error: invalid source with line "%s"' % (file_line)) - - else: - if path in source_to_lines: - source_to_lines[path].append(int(line)) - else: - source_to_lines[path] = [int(line)] - for source in source_to_lines: - dbg.request_setBreakpoints(source, source_to_lines[source]) - if options.funcBreakpoints: - dbg.request_setFunctionBreakpoints(options.funcBreakpoints) - dbg.request_configurationDone() dbg.wait_for_stopped() else: if "message" in response: diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py index 2c14bb35162b5..958c7268c0c72 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py @@ -340,6 +340,7 @@ def attach( exitCommands=None, attachCommands=None, coreFile=None, + stopOnAttach=True, disconnectAutomatically=True, terminateCommands=None, postRunCommands=None, @@ -364,6 +365,8 @@ def cleanup(): self.addTearDownHook(cleanup) # Initialize and launch the program self.dap_server.request_initialize(sourceInitFile) + self.dap_server.wait_for_event("initialized") + self.dap_server.request_configurationDone() response = self.dap_server.request_attach( program=program, pid=pid, @@ -376,6 +379,7 @@ def cleanup(): attachCommands=attachCommands, terminateCommands=terminateCommands, coreFile=coreFile, + stopOnAttach=stopOnAttach, postRunCommands=postRunCommands, sourceMap=sourceMap, gdbRemotePort=gdbRemotePort, @@ -434,6 +438,9 @@ def cleanup(): # Initialize and launch the program self.dap_server.request_initialize(sourceInitFile) + self.dap_server.wait_for_event("initialized") + self.dap_server.request_configurationDone() + response = self.dap_server.request_launch( program, args=args, diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py index f48d5a7db3c50..741c011a3d692 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py @@ -27,6 +27,8 @@ def spawn_and_wait(program, delay): @skip class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase): def set_and_hit_breakpoint(self, continueToExit=True): + self.dap_server.wait_for_stopped() + source = "main.c" breakpoint1_line = line_number(source, "// breakpoint 1") lines = [breakpoint1_line] diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py index 7f93b9f2a3a22..7250e67ebcd8c 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py @@ -18,17 +18,17 @@ import socket - at skip class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase): default_timeout = 20 def set_and_hit_breakpoint(self, continueToExit=True): + self.dap_server.wait_for_stopped() + source = "main.c" - main_source_path = os.path.join(os.getcwd(), source) - breakpoint1_line = line_number(main_source_path, "// breakpoint 1") + breakpoint1_line = line_number(source, "// breakpoint 1") lines = [breakpoint1_line] # Set breakpoint in the thread function so we can step the threads - breakpoint_ids = self.set_source_breakpoints(main_source_path, lines) + breakpoint_ids = self.set_source_breakpoints(source, lines) self.assertEqual( len(breakpoint_ids), len(lines), "expect correct number of breakpoints" ) diff --git a/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py b/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py index e5590e1b332a0..8581f10cef22a 100644 --- a/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py +++ b/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py @@ -81,52 +81,27 @@ def test_breakpoint_events(self): breakpoint["verified"], "expect foo breakpoint to not be verified" ) - # Get the stop at the entry point - self.continue_to_next_stop() + # Make sure we're stopped. + self.dap_server.wait_for_stopped() - # We are now stopped at the entry point to the program. Shared - # libraries are not loaded yet (at least on macOS they aren't) and only - # the breakpoint in the main executable should be resolved. - self.assertEqual(len(self.dap_server.breakpoint_events), 1) - event = self.dap_server.breakpoint_events[0] - body = event["body"] - self.assertEqual( - body["reason"], "changed", "breakpoint event should say changed" - ) - breakpoint = body["breakpoint"] - self.assertEqual(breakpoint["id"], main_bp_id) - self.assertTrue(breakpoint["verified"], "main breakpoint should be resolved") - - # Clear the list of breakpoint events so we don't see this one again. - self.dap_server.breakpoint_events.clear() + # Flush the breakpoint events. + self.dap_server.wait_for_breakpoint_events(timeout=5) # Continue to the breakpoint self.continue_to_breakpoints(dap_breakpoint_ids) - # When the process launches, we first expect to see both the main and - # foo breakpoint as unresolved. - for event in self.dap_server.breakpoint_events[:2]: - body = event["body"] - self.assertEqual( - body["reason"], "changed", "breakpoint event should say changed" - ) - breakpoint = body["breakpoint"] - self.assertIn(str(breakpoint["id"]), dap_breakpoint_ids) - self.assertFalse(breakpoint["verified"], "breakpoint should be unresolved") + verified_breakpoint_ids = [] + unverified_breakpoint_ids = [] + for breakpoint_event in self.dap_server.wait_for_breakpoint_events(timeout=5): + breakpoint = breakpoint_event["body"]["breakpoint"] + id = breakpoint["id"] + if breakpoint["verified"]: + verified_breakpoint_ids.append(id) + else: + unverified_breakpoint_ids.append(id) - # Then, once the dynamic loader has given us a load address, they - # should show up as resolved again. - for event in self.dap_server.breakpoint_events[3:]: - body = event["body"] - self.assertEqual( - body["reason"], "changed", "breakpoint event should say changed" - ) - breakpoint = body["breakpoint"] - self.assertIn(str(breakpoint["id"]), dap_breakpoint_ids) - self.assertTrue(breakpoint["verified"], "breakpoint should be resolved") - self.assertNotIn( - "source", - breakpoint, - "breakpoint event should not return a source object", - ) - self.assertIn("line", breakpoint, "breakpoint event should have line") + self.assertIn(main_bp_id, unverified_breakpoint_ids) + self.assertIn(foo_bp_id, unverified_breakpoint_ids) + + self.assertIn(main_bp_id, verified_breakpoint_ids) + self.assertIn(foo_bp_id, verified_breakpoint_ids) diff --git a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py index 210e591bff426..455ac84168baf 100644 --- a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py +++ b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py @@ -44,9 +44,9 @@ def verify_completions(self, actual_list, expected_list, not_expected_list=[]): self.assertNotIn(not_expected_item, actual_list) - def setup_debugee(self): + def setup_debugee(self, stopOnEntry=False): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=stopOnEntry) source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") @@ -235,7 +235,7 @@ def test_auto_completions(self): """ Tests completion requests in "repl-mode=auto" """ - self.setup_debugee() + self.setup_debugee(stopOnEntry=True) res = self.dap_server.request_evaluate( "`lldb-dap repl-mode auto", context="repl" diff --git a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py index b07c4f871d73b..65a1bc04c7cd7 100644 --- a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py +++ b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py @@ -167,7 +167,7 @@ def test_exit_status_message_ok(self): def test_diagnositcs(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) core = self.getBuildArtifact("minidump.core") self.yaml2obj("minidump.yaml", core) diff --git a/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py b/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py index 0cb792d662a80..09e3f62f0eead 100644 --- a/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py +++ b/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py @@ -31,7 +31,7 @@ def test_launch(self): created. """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program, disconnectAutomatically=False) + self.build_and_launch(program, stopOnEntry=True, disconnectAutomatically=False) # We set a breakpoint right before the side effect file is created self.set_source_breakpoints( @@ -39,7 +39,11 @@ def test_launch(self): ) self.continue_to_next_stop() + # verify we haven't produced the side effect file yet + self.assertFalse(os.path.exists(program + ".side_effect")) + self.dap_server.request_disconnect() + # verify we didn't produce the side effect file time.sleep(1) self.assertFalse(os.path.exists(program + ".side_effect")) diff --git a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py index d97fda730c46a..19b682dfcd22d 100644 --- a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py +++ b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py @@ -10,6 +10,7 @@ from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * + # DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. @skip class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase): @@ -42,7 +43,9 @@ def run_test_evaluate_expressions( self.context = context program = self.getBuildArtifact("a.out") self.build_and_launch( - program, enableAutoVariableSummaries=enableAutoVariableSummaries + program, + enableAutoVariableSummaries=enableAutoVariableSummaries, + stopOnEntry=True, ) source = "main.cpp" self.set_source_breakpoints( diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py index 931456299e03e..604a41678500c 100644 --- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py +++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py @@ -88,8 +88,8 @@ def test_stopOnEntry(self): """ program = self.getBuildArtifact("a.out") self.build_and_launch(program, stopOnEntry=True) - self.set_function_breakpoints(["main"]) - stopped_events = self.continue_to_next_stop() + + stopped_events = self.dap_server.wait_for_stopped() for stopped_event in stopped_events: if "body" in stopped_event: body = stopped_event["body"] diff --git a/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py b/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py index fee63655de0da..0f94b50c31fba 100755 --- a/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py +++ b/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py @@ -50,7 +50,7 @@ def verify_progress_events( @skipIfWindows def test(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) progress_emitter = os.path.join(os.getcwd(), "Progress_emitter.py") self.dap_server.request_evaluate( f"`command script import {progress_emitter}", context="repl" diff --git a/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py b/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py index c6f59949d668e..81edcdf4bd0f9 100644 --- a/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py +++ b/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py @@ -20,7 +20,7 @@ def assertEvaluate(self, expression, regex): def test_completions(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") diff --git a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py index 36fa0bd40183f..5f95c7bfb1556 100644 --- a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py +++ b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py @@ -22,7 +22,6 @@ def test_basic_functionality(self): [bp_A, bp_B] = self.set_source_breakpoints("main.c", [line_A, line_B]) # Verify we hit A, then B. - self.dap_server.request_configurationDone() self.verify_breakpoint_hit([bp_A]) self.dap_server.request_continue() self.verify_breakpoint_hit([bp_B]) diff --git a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py index a94c9860c1508..eed769a5a0cc6 100644 --- a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py +++ b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py @@ -74,7 +74,6 @@ def test_stopOnEntry(self): program = self.getBuildArtifact("a.out") self.build_and_launch(program, runInTerminal=True, stopOnEntry=True) [bp_main] = self.set_function_breakpoints(["main"]) - self.dap_server.request_configurationDone() # When using stopOnEntry, configurationDone doesn't result in a running # process, we should immediately get a stopped event instead. diff --git a/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py b/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py index 70c11a63a79f7..7e28a5af4331c 100644 --- a/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py +++ b/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py @@ -19,7 +19,7 @@ def test_stop_hooks_before_run(self): self.build_and_launch(program, stopOnEntry=True, preRunCommands=preRunCommands) # The first stop is on entry. - self.continue_to_next_stop() + self.dap_server.wait_for_stopped() breakpoint_ids = self.set_function_breakpoints(["main"]) # This request hangs if the race happens, because, in that case, the diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 4b631484c9fab..62c60cc3a9b3b 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -84,8 +84,8 @@ DAP::DAP(Log *log, const ReplMode default_repl_mode, : log(log), transport(transport), broadcaster("lldb-dap"), exception_breakpoints(), focus_tid(LLDB_INVALID_THREAD_ID), stop_at_entry(false), is_attach(false), - restarting_process_id(LLDB_INVALID_PROCESS_ID), - configuration_done_sent(false), waiting_for_run_in_terminal(false), + restarting_process_id(LLDB_INVALID_PROCESS_ID), configuration_done(false), + waiting_for_run_in_terminal(false), progress_event_reporter( [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }), reverse_request_seq(0), repl_mode(default_repl_mode) { @@ -893,10 +893,19 @@ llvm::Error DAP::Loop() { return errWrapper; } + // The launch sequence is special and we need to carefully handle + // packets in the right order. Until we've handled configurationDone, + bool add_to_pending_queue = false; + if (const protocol::Request *req = - std::get_if(&*next); - req && req->command == "disconnect") { - disconnecting = true; + std::get_if(&*next)) { + llvm::StringRef command = req->command; + if (command == "disconnect") + disconnecting = true; + if (!configuration_done) + add_to_pending_queue = + command != "initialize" && command != "configurationDone" && + command != "disconnect" && !command.ends_with("Breakpoints"); } const std::optional cancel_args = @@ -924,7 +933,8 @@ llvm::Error DAP::Loop() { { std::lock_guard guard(m_queue_mutex); - m_queue.push_back(std::move(*next)); + auto &queue = add_to_pending_queue ? m_pending_queue : m_queue; + queue.push_back(std::move(*next)); } m_queue_cv.notify_one(); } @@ -938,16 +948,19 @@ llvm::Error DAP::Loop() { StopEventHandlers(); }); - while (!disconnecting || !m_queue.empty()) { + while (true) { std::unique_lock lock(m_queue_mutex); m_queue_cv.wait(lock, [&] { return disconnecting || !m_queue.empty(); }); - if (m_queue.empty()) + if (disconnecting && m_queue.empty()) break; Message next = m_queue.front(); m_queue.pop_front(); + // Unlock while we're processing the event. + lock.unlock(); + if (!HandleObject(next)) return llvm::createStringError(llvm::inconvertibleErrorCode(), "unhandled packet"); @@ -1219,6 +1232,16 @@ void DAP::SetConfiguration(const protocol::Configuration &config, SetThreadFormat(*configuration.customThreadFormat); } +void DAP::SetConfigurationDone() { + { + std::lock_guard guard(m_queue_mutex); + std::copy(m_pending_queue.begin(), m_pending_queue.end(), + std::front_inserter(m_queue)); + configuration_done = true; + } + m_queue_cv.notify_all(); +} + void DAP::SetFrameFormat(llvm::StringRef format) { if (format.empty()) return; diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index 88eedb0860cf1..b581ae759b1bc 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -188,7 +188,7 @@ struct DAP { // shutting down the entire adapter. When we're restarting, we keep the id of // the old process here so we can detect this case and keep running. lldb::pid_t restarting_process_id; - bool configuration_done_sent; + bool configuration_done; llvm::StringMap> request_handlers; bool waiting_for_run_in_terminal; ProgressEventReporter progress_event_reporter; @@ -251,6 +251,8 @@ struct DAP { /// Configures the debug adapter for launching/attaching. void SetConfiguration(const protocol::Configuration &confing, bool is_attach); + void SetConfigurationDone(); + /// Configure source maps based on the current `DAPConfiguration`. void ConfigureSourceMaps(); @@ -417,8 +419,10 @@ struct DAP { lldb::SBMutex GetAPIMutex() const { return target.GetAPIMutex(); } private: - std::mutex m_queue_mutex; + /// Queue for all incoming messages. std::deque m_queue; + std::deque m_pending_queue; + std::mutex m_queue_mutex; std::condition_variable m_queue_cv; std::mutex m_cancelled_requests_mutex; diff --git a/lldb/tools/lldb-dap/EventHelper.cpp b/lldb/tools/lldb-dap/EventHelper.cpp index 2c659f39f4b66..ed2d8700c26b0 100644 --- a/lldb/tools/lldb-dap/EventHelper.cpp +++ b/lldb/tools/lldb-dap/EventHelper.cpp @@ -222,7 +222,7 @@ void SendContinuedEvent(DAP &dap) { // If the focus thread is not set then we haven't reported any thread status // to the client, so nothing to report. - if (!dap.configuration_done_sent || dap.focus_tid == LLDB_INVALID_THREAD_ID) { + if (!dap.configuration_done || dap.focus_tid == LLDB_INVALID_THREAD_ID) { return; } diff --git a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp index 7a0f091128e4a..5dc9c3f9772e3 100644 --- a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp @@ -133,61 +133,70 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { dap.SendOutput(OutputType::Console, llvm::StringRef(attach_msg, attach_msg_len)); } - if (attachCommands.empty()) { - // No "attachCommands", just attach normally. - // Disable async events so the attach will be successful when we return from - // the launch call and the launch will happen synchronously + { + // Perform the launch in synchronous mode so that we don't have to worry + // about process state changes during the launch. ScopeSyncMode scope_sync_mode(dap.debugger); - - if (core_file.empty()) { - if ((pid != LLDB_INVALID_PROCESS_ID) && - (gdb_remote_port != invalid_port)) { - // If both pid and port numbers are specified. - error.SetErrorString("The user can't specify both pid and port"); - } else if (gdb_remote_port != invalid_port) { - // If port is specified and pid is not. - lldb::SBListener listener = dap.debugger.GetListener(); - - // If the user hasn't provided the hostname property, default localhost - // being used. - std::string connect_url = - llvm::formatv("connect://{0}:", gdb_remote_hostname); - connect_url += std::to_string(gdb_remote_port); - dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", - error); + if (attachCommands.empty()) { + // No "attachCommands", just attach normally. + if (core_file.empty()) { + if ((pid != LLDB_INVALID_PROCESS_ID) && + (gdb_remote_port != invalid_port)) { + // If both pid and port numbers are specified. + error.SetErrorString("The user can't specify both pid and port"); + } else if (gdb_remote_port != invalid_port) { + // If port is specified and pid is not. + lldb::SBListener listener = dap.debugger.GetListener(); + + // If the user hasn't provided the hostname property, default + // localhost being used. + std::string connect_url = + llvm::formatv("connect://{0}:", gdb_remote_hostname); + connect_url += std::to_string(gdb_remote_port); + dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", + error); + } else { + // Attach by pid or process name. + lldb::SBAttachInfo attach_info; + if (pid != LLDB_INVALID_PROCESS_ID) + attach_info.SetProcessID(pid); + else if (dap.configuration.program.has_value()) + attach_info.SetExecutable(dap.configuration.program->data()); + attach_info.SetWaitForLaunch(wait_for, false /*async*/); + dap.target.Attach(attach_info, error); + } } else { - // Attach by pid or process name. - lldb::SBAttachInfo attach_info; - if (pid != LLDB_INVALID_PROCESS_ID) - attach_info.SetProcessID(pid); - else if (dap.configuration.program.has_value()) - attach_info.SetExecutable(dap.configuration.program->data()); - attach_info.SetWaitForLaunch(wait_for, false /*async*/); - dap.target.Attach(attach_info, error); + dap.target.LoadCore(core_file.data(), error); } } else { - dap.target.LoadCore(core_file.data(), error); - } - } else { - // We have "attachCommands" that are a set of commands that are expected - // to execute the commands after which a process should be created. If there - // is no valid process after running these commands, we have failed. - if (llvm::Error err = dap.RunAttachCommands(attachCommands)) { - response["success"] = false; - EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; + // We have "attachCommands" that are a set of commands that are expected + // to execute the commands after which a process should be created. If + // there is no valid process after running these commands, we have failed. + if (llvm::Error err = dap.RunAttachCommands(attachCommands)) { + response["success"] = false; + EmplaceSafeString(response, "message", llvm::toString(std::move(err))); + dap.SendJSON(llvm::json::Value(std::move(response))); + return; + } + // The custom commands might have created a new target so we should use + // the selected target after these commands are run. + dap.target = dap.debugger.GetSelectedTarget(); } - // The custom commands might have created a new target so we should use the - // selected target after these commands are run. - dap.target = dap.debugger.GetSelectedTarget(); - - // Make sure the process is attached and stopped before proceeding as the - // the launch commands are not run using the synchronous mode. - error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds)); } + // Make sure the process is attached and stopped. + error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds)); + + // Clients can request a baseline of currently existing threads after + // we acknowledge the configurationDone request. + // Client requests the baseline of currently existing threads after + // a successful or attach by sending a 'threads' request + // right after receiving the configurationDone response. + // Obtain the list of threads before we resume the process + dap.initial_thread_list = + GetThreads(dap.target.GetProcess(), dap.thread_format); + if (error.Success() && core_file.empty()) { auto attached_pid = dap.target.GetProcess().GetProcessID(); if (attached_pid == LLDB_INVALID_PROCESS_ID) { @@ -206,9 +215,17 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { } dap.SendJSON(llvm::json::Value(std::move(response))); + + // FIXME: Move this into PostRun. if (error.Success()) { - SendProcessEvent(dap, Attach); - dap.SendJSON(CreateEventObject("initialized")); + if (dap.target.GetProcess().IsValid()) { + SendProcessEvent(dap, Attach); + + if (dap.stop_at_entry) + SendThreadStoppedEvent(dap); + else + dap.target.GetProcess().Continue(); + } } } diff --git a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp index f39bbdefdbb95..802c28d7b8904 100644 --- a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp @@ -47,21 +47,11 @@ namespace lldb_dap { void ConfigurationDoneRequestHandler::operator()( const llvm::json::Object &request) const { + dap.SetConfigurationDone(); + llvm::json::Object response; FillResponse(request, response); dap.SendJSON(llvm::json::Value(std::move(response))); - dap.configuration_done_sent = true; - if (dap.stop_at_entry) - SendThreadStoppedEvent(dap); - else { - // Client requests the baseline of currently existing threads after - // a successful launch or attach by sending a 'threads' request - // right after receiving the configurationDone response. - // Obtain the list of threads before we resume the process - dap.initial_thread_list = - GetThreads(dap.target.GetProcess(), dap.thread_format); - dap.target.GetProcess().Continue(); - } } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp index ce34c52bcc334..aa947d3cb5ab9 100644 --- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp @@ -140,43 +140,28 @@ static void EventThreadFunction(DAP &dap) { lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event); if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) { auto state = lldb::SBProcess::GetStateFromEvent(event); + + DAP_LOG(dap.log, "State = {0}", state); switch (state) { + case lldb::eStateConnected: + case lldb::eStateDetached: case lldb::eStateInvalid: - // Not a state event - break; case lldb::eStateUnloaded: break; - case lldb::eStateConnected: - break; case lldb::eStateAttaching: - break; - case lldb::eStateLaunching: - break; - case lldb::eStateStepping: - break; case lldb::eStateCrashed: - break; - case lldb::eStateDetached: - break; - case lldb::eStateSuspended: - break; + case lldb::eStateLaunching: case lldb::eStateStopped: - // We launch and attach in synchronous mode then the first stop - // event will not be delivered. If we use "launchCommands" during a - // launch or "attachCommands" during an attach we might some process - // stop events which we do not want to send an event for. We will - // manually send a stopped event in request_configurationDone(...) - // so don't send any before then. - if (dap.configuration_done_sent) { - // Only report a stopped event if the process was not - // automatically restarted. - if (!lldb::SBProcess::GetRestartedFromEvent(event)) { - SendStdOutStdErr(dap, process); - SendThreadStoppedEvent(dap); - } + case lldb::eStateSuspended: + // Only report a stopped event if the process was not + // automatically restarted. + if (!lldb::SBProcess::GetRestartedFromEvent(event)) { + SendStdOutStdErr(dap, process); + SendThreadStoppedEvent(dap); } break; case lldb::eStateRunning: + case lldb::eStateStepping: dap.WillContinue(); SendContinuedEvent(dap); break; @@ -284,6 +269,7 @@ llvm::Expected InitializeRequestHandler::Run( // Do not source init files until in/out/err are configured. dap.debugger = lldb::SBDebugger::Create(false); dap.debugger.SetInputFile(dap.in); + dap.target = dap.debugger.GetDummyTarget(); llvm::Expected out_fd = dap.out.GetWriteFileDescriptor(); if (!out_fd) @@ -338,4 +324,8 @@ llvm::Expected InitializeRequestHandler::Run( return dap.GetCapabilities(); } +void InitializeRequestHandler::PostRun() const { + dap.SendJSON(CreateEventObject("initialized")); +} + } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp index 3e4532e754ec6..7e0e76935dd02 100644 --- a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp @@ -71,9 +71,12 @@ void LaunchRequestHandler::PostRun() const { if (dap.target.GetProcess().IsValid()) { // Attach happens when launching with runInTerminal. SendProcessEvent(dap, dap.is_attach ? Attach : Launch); - } - dap.SendJSON(CreateEventObject("initialized")); + if (dap.stop_at_entry) + SendThreadStoppedEvent(dap); + else + dap.target.GetProcess().Continue(); + } } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp index 7a75cd93abc19..282c5f4ab15a5 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp @@ -8,6 +8,7 @@ #include "Handler/RequestHandler.h" #include "DAP.h" +#include "EventHelper.h" #include "Handler/ResponseHandler.h" #include "JSONUtils.h" #include "LLDBUtils.h" @@ -162,7 +163,7 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) { dap.target.GetProcess().Continue(); // Now that the actual target is just starting (i.e. exec was just invoked), - // we return the debugger to its async state. + // we return the debugger to its sync state. scope_sync_mode.reset(); // If sending the notification failed, the launcher should be dead by now and @@ -238,35 +239,47 @@ llvm::Error BaseRequestHandler::LaunchProcess( launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug | lldb::eLaunchFlagStopAtEntry); - if (arguments.runInTerminal) { - if (llvm::Error err = RunInTerminal(dap, arguments)) - return err; - } else if (launchCommands.empty()) { - lldb::SBError error; - // Disable async events so the launch will be successful when we return from - // the launch call and the launch will happen synchronously + { + // Perform the launch in synchronous mode so that we don't have to worry + // about process state changes during the launch. ScopeSyncMode scope_sync_mode(dap.debugger); - dap.target.Launch(launch_info, error); - if (error.Fail()) - return llvm::make_error(error.GetCString()); - } else { - // Set the launch info so that run commands can access the configured - // launch details. - dap.target.SetLaunchInfo(launch_info); - if (llvm::Error err = dap.RunLaunchCommands(launchCommands)) - return err; - - // The custom commands might have created a new target so we should use the - // selected target after these commands are run. - dap.target = dap.debugger.GetSelectedTarget(); - // Make sure the process is launched and stopped at the entry point before - // proceeding as the launch commands are not run using the synchronous - // mode. - lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout); - if (error.Fail()) - return llvm::make_error(error.GetCString()); + + if (arguments.runInTerminal) { + if (llvm::Error err = RunInTerminal(dap, arguments)) + return err; + } else if (launchCommands.empty()) { + lldb::SBError error; + dap.target.Launch(launch_info, error); + if (error.Fail()) + return llvm::make_error(error.GetCString()); + } else { + // Set the launch info so that run commands can access the configured + // launch details. + dap.target.SetLaunchInfo(launch_info); + if (llvm::Error err = dap.RunLaunchCommands(launchCommands)) + return err; + + // The custom commands might have created a new target so we should use + // the selected target after these commands are run. + dap.target = dap.debugger.GetSelectedTarget(); + } } + // Make sure the process is launched and stopped at the entry point before + // proceeding. + lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout); + if (error.Fail()) + return llvm::make_error(error.GetCString()); + + // Clients can request a baseline of currently existing threads after + // we acknowledge the configurationDone request. + // Client requests the baseline of currently existing threads after + // a successful or attach by sending a 'threads' request + // right after receiving the configurationDone response. + // Obtain the list of threads before we resume the process + dap.initial_thread_list = + GetThreads(dap.target.GetProcess(), dap.thread_format); + return llvm::Error::success(); } diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index 37cc902e1c98e..9e9cfb13d77b8 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.h +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h @@ -282,6 +282,7 @@ class InitializeRequestHandler static llvm::StringLiteral GetCommand() { return "initialize"; } llvm::Expected Run(const protocol::InitializeRequestArguments &args) const override; + void PostRun() const override; }; class LaunchRequestHandler >From 3e7b2457705d73c22c2607e02416f1ad9e57bedc Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Wed, 7 May 2025 15:26:01 -0700 Subject: [PATCH 2/2] Update more tests --- .../test/tools/lldb-dap/lldbdap_testcase.py | 48 +++++++++++++++++++ .../completions/TestDAP_completions.py | 22 +++++---- .../tools/lldb-dap/console/TestDAP_console.py | 9 ++-- .../console/TestDAP_redirection_to_console.py | 4 +- .../tools/lldb-dap/launch/TestDAP_launch.py | 2 + .../lldb-dap/send-event/TestDAP_sendEvent.py | 7 +-- .../lldb-dap/stackTrace/TestDAP_stackTrace.py | 2 +- .../TestDAP_stackTraceDisassemblyDisplay.py | 2 +- .../startDebugging/TestDAP_startDebugging.py | 3 +- .../children/TestDAP_variables_children.py | 4 +- 10 files changed, 79 insertions(+), 24 deletions(-) diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py index 958c7268c0c72..c5a7eb76a58c7 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py @@ -349,6 +349,8 @@ def attach( expectFailure=False, gdbRemotePort=None, gdbRemoteHostname=None, + sourceBreakpoints=None, + functionBreakpoints=None, ): """Build the default Makefile target, create the DAP debug adapter, and attach to the process. @@ -366,6 +368,26 @@ def cleanup(): # Initialize and launch the program self.dap_server.request_initialize(sourceInitFile) self.dap_server.wait_for_event("initialized") + + # Set source breakpoints as part of the launch sequence. + if sourceBreakpoints: + for source_path, lines in sourceBreakpoints: + response = self.dap_server.request_setBreakpoints(source_path, lines) + self.assertTrue( + response["success"], + "setBreakpoints failed (%s)" % (response), + ) + + # Set function breakpoints as part of the launch sequence. + if functionBreakpoints: + response = self.dap_server.request_setFunctionBreakpoints( + functionBreakpoints + ) + self.assertTrue( + response["success"], + "setFunctionBreakpoint failed (%s)" % (response), + ) + self.dap_server.request_configurationDone() response = self.dap_server.request_attach( program=program, @@ -423,6 +445,8 @@ def launch( commandEscapePrefix=None, customFrameFormat=None, customThreadFormat=None, + sourceBreakpoints=None, + functionBreakpoints=None, ): """Sending launch request to dap""" @@ -439,6 +463,26 @@ def cleanup(): # Initialize and launch the program self.dap_server.request_initialize(sourceInitFile) self.dap_server.wait_for_event("initialized") + + # Set source breakpoints as part of the launch sequence. + if sourceBreakpoints: + for source_path, lines in sourceBreakpoints: + response = self.dap_server.request_setBreakpoints(source_path, lines) + self.assertTrue( + response["success"], + "setBreakpoints failed (%s)" % (response), + ) + + # Set function breakpoints as part of the launch sequence. + if functionBreakpoints: + response = self.dap_server.request_setFunctionBreakpoints( + functionBreakpoints + ) + self.assertTrue( + response["success"], + "setFunctionBreakpoint failed (%s)" % (response), + ) + self.dap_server.request_configurationDone() response = self.dap_server.request_launch( @@ -511,6 +555,8 @@ def build_and_launch( customThreadFormat=None, launchCommands=None, expectFailure=False, + sourceBreakpoints=None, + functionBreakpoints=None, ): """Build the default Makefile target, create the DAP debug adapter, and launch the process. @@ -547,6 +593,8 @@ def build_and_launch( customThreadFormat=customThreadFormat, launchCommands=launchCommands, expectFailure=expectFailure, + sourceBreakpoints=sourceBreakpoints, + functionBreakpoints=functionBreakpoints, ) def getBuiltinDebugServerTool(self): diff --git a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py index 455ac84168baf..a94288c7a669e 100644 --- a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py +++ b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py @@ -2,7 +2,6 @@ Test lldb-dap completions request """ - import lldbdap_testcase import dap_server from lldbsuite.test import lldbutil @@ -32,6 +31,7 @@ variable_var1_completion = {"text": "var1", "label": "var1 -- int &"} variable_var2_completion = {"text": "var2", "label": "var2 -- int &"} + # Older version of libcxx produce slightly different typename strings for # templates like vector. @skipIf(compiler="clang", compiler_version=["<", "16.0"]) @@ -43,16 +43,22 @@ def verify_completions(self, actual_list, expected_list, not_expected_list=[]): for not_expected_item in not_expected_list: self.assertNotIn(not_expected_item, actual_list) - def setup_debugee(self, stopOnEntry=False): program = self.getBuildArtifact("a.out") - self.build_and_launch(program, stopOnEntry=stopOnEntry) - source = "main.cpp" - breakpoint1_line = line_number(source, "// breakpoint 1") - breakpoint2_line = line_number(source, "// breakpoint 2") - - self.set_source_breakpoints(source, [breakpoint1_line, breakpoint2_line]) + self.build_and_launch( + program, + stopOnEntry=stopOnEntry, + sourceBreakpoints=[ + ( + source, + [ + line_number(source, "// breakpoint 1"), + line_number(source, "// breakpoint 2"), + ], + ), + ], + ) def test_command_completions(self): """ diff --git a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py index 65a1bc04c7cd7..8642e317f9b3a 100644 --- a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py +++ b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py @@ -19,6 +19,7 @@ def get_subprocess(root_process, process_name): self.assertTrue(False, "No subprocess with name %s found" % process_name) + class TestDAP_console(lldbdap_testcase.DAPTestCaseBase): def check_lldb_command( self, lldb_command, contains_string, assert_msg, command_escape_prefix="`" @@ -52,7 +53,7 @@ def test_scopes_variables_setVariable_evaluate(self): character. """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") lines = [breakpoint1_line] @@ -81,7 +82,7 @@ def test_scopes_variables_setVariable_evaluate(self): def test_custom_escape_prefix(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program, commandEscapePrefix="::") + self.build_and_launch(program, stopOnEntry=True, commandEscapePrefix="::") source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line]) @@ -96,7 +97,7 @@ def test_custom_escape_prefix(self): def test_empty_escape_prefix(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program, commandEscapePrefix="") + self.build_and_launch(program, stopOnEntry=True, commandEscapePrefix="") source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line]) @@ -113,7 +114,7 @@ def test_empty_escape_prefix(self): def test_exit_status_message_sigterm(self): source = "main.cpp" program = self.getBuildArtifact("a.out") - self.build_and_launch(program, commandEscapePrefix="") + self.build_and_launch(program, stopOnEntry=True, commandEscapePrefix="") breakpoint1_line = line_number(source, "// breakpoint 1") breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line]) self.continue_to_breakpoints(breakpoint_ids) diff --git a/lldb/test/API/tools/lldb-dap/console/TestDAP_redirection_to_console.py b/lldb/test/API/tools/lldb-dap/console/TestDAP_redirection_to_console.py index e367c327d4295..23500bd6fe586 100644 --- a/lldb/test/API/tools/lldb-dap/console/TestDAP_redirection_to_console.py +++ b/lldb/test/API/tools/lldb-dap/console/TestDAP_redirection_to_console.py @@ -16,7 +16,9 @@ def test(self): """ program = self.getBuildArtifact("a.out") self.build_and_launch( - program, lldbDAPEnv={"LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION": ""} + program, + stopOnEntry=True, + lldbDAPEnv={"LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION": ""}, ) source = "main.cpp" diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py index 604a41678500c..1af661958dc09 100644 --- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py +++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py @@ -15,6 +15,7 @@ # Despite the test program printing correctly. See # https://github.com/llvm/llvm-project/issues/137599. + class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase): @skipIfWindows def test_default(self): @@ -357,6 +358,7 @@ def test_commands(self): terminateCommands = ["expr 4+2"] self.build_and_launch( program, + stopOnEntry=True, initCommands=initCommands, preRunCommands=preRunCommands, postRunCommands=postRunCommands, diff --git a/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py b/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py index ce262be161861..64cec70aa923b 100644 --- a/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py +++ b/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py @@ -16,12 +16,14 @@ def test_send_event(self): """ program = self.getBuildArtifact("a.out") source = "main.c" + breakpoint_line = line_number(source, "// breakpoint") custom_event_body = { "key": 321, "arr": [True], } self.build_and_launch( program, + sourceBreakpoints=[(source, [breakpoint_line])], stopCommands=[ "lldb-dap send-event my-custom-event-no-body", "lldb-dap send-event my-custom-event '{}'".format( @@ -30,11 +32,6 @@ def test_send_event(self): ], ) - breakpoint_line = line_number(source, "// breakpoint") - - self.set_source_breakpoints(source, [breakpoint_line]) - self.continue_to_next_stop() - custom_event = self.dap_server.wait_for_event( filter=["my-custom-event-no-body"] ) diff --git a/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py b/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py index 4e2a76cf76980..edf4adae14a3b 100644 --- a/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py +++ b/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py @@ -61,7 +61,7 @@ def test_stackTrace(self): Tests the 'stackTrace' packet and all its variants. """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) source = "main.c" self.source_path = os.path.join(os.getcwd(), source) self.recurse_end = line_number(source, "recurse end") diff --git a/lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py b/lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py index 08c225b3cada4..963d711978534 100644 --- a/lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py +++ b/lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py @@ -37,7 +37,7 @@ def build_and_run_until_breakpoint(self): breakpoint_line = line_number(other_source_file, "// Break here") program = self.getBuildArtifact("a.out") - self.build_and_launch(program, commandEscapePrefix="") + self.build_and_launch(program, stopOnEntry=True, commandEscapePrefix="") breakpoint_ids = self.set_source_breakpoints( other_source_file, [breakpoint_line] diff --git a/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py b/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py index fd452d91e472b..e37cd36d7f283 100644 --- a/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py +++ b/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py @@ -2,7 +2,6 @@ Test lldb-dap start-debugging reverse requests. """ - from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * import lldbdap_testcase @@ -16,7 +15,7 @@ def test_startDebugging(self): """ program = self.getBuildArtifact("a.out") source = "main.c" - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) breakpoint_line = line_number(source, "// breakpoint") diff --git a/lldb/test/API/tools/lldb-dap/variables/children/TestDAP_variables_children.py b/lldb/test/API/tools/lldb-dap/variables/children/TestDAP_variables_children.py index a9371e5c5fe68..eb09649f387d7 100644 --- a/lldb/test/API/tools/lldb-dap/variables/children/TestDAP_variables_children.py +++ b/lldb/test/API/tools/lldb-dap/variables/children/TestDAP_variables_children.py @@ -13,13 +13,13 @@ def test_get_num_children(self): program = self.getBuildArtifact("a.out") self.build_and_launch( program, + stopOnEntry=True, preRunCommands=[ "command script import '%s'" % self.getSourcePath("formatter.py") ], ) source = "main.cpp" breakpoint1_line = line_number(source, "// break here") - lines = [breakpoint1_line] breakpoint_ids = self.set_source_breakpoints( source, [line_number(source, "// break here")] @@ -47,7 +47,7 @@ def test_return_variable_with_children(self): Test the stepping out of a function with return value show the children correctly """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) function_name = "test_return_variable_with_children" breakpoint_ids = self.set_function_breakpoints([function_name]) From lldb-commits at lists.llvm.org Wed May 7 15:28:13 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 15:28:13 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Change the launch sequence (reland) (PR #138981) In-Reply-To: Message-ID: <681bde7d.170a0220.1d59aa.abd1@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Jonas Devlieghere (JDevlieghere)
Changes This PR changes how we treat the launch sequence in lldb-dap. - Send the initialized event after we finish handling the initialize request, rather than after we finish attaching or launching. - Delay handling the launch and attach request until we have handled the configurationDone request. The latter is now largely a NO-OP and only exists to signal lldb-dap that it can handle the launch and attach requests. - Delay handling the initial threads requests until we have handled the launch or attach request. - Make all attaching and launching synchronous, including when we have attach or launch commands. This removes the need to synchronize between the request and event thread. Background: https://discourse.llvm.org/t/reliability-of-the-lldb-dap-tests/86125 --- Patch is 54.05 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/138981.diff 30 Files Affected: - (modified) lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py (+34-31) - (modified) lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py (+55) - (modified) lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py (+2) - (modified) lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py (+4-4) - (modified) lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py (+18-43) - (modified) lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py (+16-10) - (modified) lldb/test/API/tools/lldb-dap/console/TestDAP_console.py (+6-5) - (modified) lldb/test/API/tools/lldb-dap/console/TestDAP_redirection_to_console.py (+3-1) - (modified) lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py (+5-1) - (modified) lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py (+4-1) - (modified) lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py (+4-2) - (modified) lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py (+1-1) - (modified) lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py (+1-1) - (modified) lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py (-1) - (modified) lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py (-1) - (modified) lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py (+2-5) - (modified) lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py (+1-1) - (modified) lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py (+1-1) - (modified) lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py (+1-2) - (modified) lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py (+1-1) - (modified) lldb/test/API/tools/lldb-dap/variables/children/TestDAP_variables_children.py (+2-2) - (modified) lldb/tools/lldb-dap/DAP.cpp (+31-8) - (modified) lldb/tools/lldb-dap/DAP.h (+6-2) - (modified) lldb/tools/lldb-dap/EventHelper.cpp (+1-1) - (modified) lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp (+66-49) - (modified) lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp (+2-12) - (modified) lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp (+17-27) - (modified) lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp (+5-2) - (modified) lldb/tools/lldb-dap/Handler/RequestHandler.cpp (+40-27) - (modified) lldb/tools/lldb-dap/Handler/RequestHandler.h (+1) ``````````diff diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py index 6d9ab770684f1..e10342b72f4f0 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py @@ -132,7 +132,6 @@ def __init__(self, recv, send, init_commands, log_file=None): self.exit_status = None self.initialize_body = None self.thread_stop_reasons = {} - self.breakpoint_events = [] self.progress_events = [] self.reverse_requests = [] self.module_events = [] @@ -244,13 +243,6 @@ def handle_recv_packet(self, packet): self._process_stopped() tid = body["threadId"] self.thread_stop_reasons[tid] = body - elif event == "breakpoint": - # Breakpoint events come in when a breakpoint has locations - # added or removed. Keep track of them so we can look for them - # in tests. - self.breakpoint_events.append(packet) - # no need to add 'breakpoint' event packets to our packets list - return keepGoing elif event.startswith("progress"): # Progress events come in as 'progressStart', 'progressUpdate', # and 'progressEnd' events. Keep these around in case test @@ -412,6 +404,15 @@ def wait_for_stopped(self, timeout=None): self.threads = [] return stopped_events + def wait_for_breakpoint_events(self, timeout=None): + breakpoint_events = [] + while True: + event = self.wait_for_event("breakpoint", timeout=timeout) + if not event: + break + breakpoint_events.append(event) + return breakpoint_events + def wait_for_exited(self): event_dict = self.wait_for_event("exited") if event_dict is None: @@ -591,6 +592,7 @@ def request_attach( attachCommands=None, terminateCommands=None, coreFile=None, + stopOnAttach=True, postRunCommands=None, sourceMap=None, gdbRemotePort=None, @@ -620,6 +622,8 @@ def request_attach( args_dict["attachCommands"] = attachCommands if coreFile: args_dict["coreFile"] = coreFile + if stopOnAttach: + args_dict["stopOnEntry"] = stopOnAttach if postRunCommands: args_dict["postRunCommands"] = postRunCommands if sourceMap: @@ -632,7 +636,7 @@ def request_attach( response = self.send_recv(command_dict) if response["success"]: - self.wait_for_events(["process", "initialized"]) + self.wait_for_event("process") return response def request_breakpointLocations( @@ -666,10 +670,6 @@ def request_configurationDone(self): response = self.send_recv(command_dict) if response: self.configuration_done_sent = True - # Client requests the baseline of currently existing threads after - # a successful launch or attach. - # Kick off the threads request that follows - self.request_threads() return response def _process_stopped(self): @@ -887,7 +887,7 @@ def request_launch( response = self.send_recv(command_dict) if response["success"]: - self.wait_for_events(["process", "initialized"]) + self.wait_for_event("process") return response def request_next(self, threadId, granularity="statement"): @@ -1325,6 +1325,26 @@ def attach_options_specified(options): def run_vscode(dbg, args, options): dbg.request_initialize(options.sourceInitFile) + + if options.sourceBreakpoints: + source_to_lines = {} + for file_line in options.sourceBreakpoints: + (path, line) = file_line.split(":") + if len(path) == 0 or len(line) == 0: + print('error: invalid source with line "%s"' % (file_line)) + + else: + if path in source_to_lines: + source_to_lines[path].append(int(line)) + else: + source_to_lines[path] = [int(line)] + for source in source_to_lines: + dbg.request_setBreakpoints(source, source_to_lines[source]) + if options.funcBreakpoints: + dbg.request_setFunctionBreakpoints(options.funcBreakpoints) + + dbg.request_configurationDone() + if attach_options_specified(options): response = dbg.request_attach( program=options.program, @@ -1353,23 +1373,6 @@ def run_vscode(dbg, args, options): ) if response["success"]: - if options.sourceBreakpoints: - source_to_lines = {} - for file_line in options.sourceBreakpoints: - (path, line) = file_line.split(":") - if len(path) == 0 or len(line) == 0: - print('error: invalid source with line "%s"' % (file_line)) - - else: - if path in source_to_lines: - source_to_lines[path].append(int(line)) - else: - source_to_lines[path] = [int(line)] - for source in source_to_lines: - dbg.request_setBreakpoints(source, source_to_lines[source]) - if options.funcBreakpoints: - dbg.request_setFunctionBreakpoints(options.funcBreakpoints) - dbg.request_configurationDone() dbg.wait_for_stopped() else: if "message" in response: diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py index 2c14bb35162b5..c5a7eb76a58c7 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py @@ -340,6 +340,7 @@ def attach( exitCommands=None, attachCommands=None, coreFile=None, + stopOnAttach=True, disconnectAutomatically=True, terminateCommands=None, postRunCommands=None, @@ -348,6 +349,8 @@ def attach( expectFailure=False, gdbRemotePort=None, gdbRemoteHostname=None, + sourceBreakpoints=None, + functionBreakpoints=None, ): """Build the default Makefile target, create the DAP debug adapter, and attach to the process. @@ -364,6 +367,28 @@ def cleanup(): self.addTearDownHook(cleanup) # Initialize and launch the program self.dap_server.request_initialize(sourceInitFile) + self.dap_server.wait_for_event("initialized") + + # Set source breakpoints as part of the launch sequence. + if sourceBreakpoints: + for source_path, lines in sourceBreakpoints: + response = self.dap_server.request_setBreakpoints(source_path, lines) + self.assertTrue( + response["success"], + "setBreakpoints failed (%s)" % (response), + ) + + # Set function breakpoints as part of the launch sequence. + if functionBreakpoints: + response = self.dap_server.request_setFunctionBreakpoints( + functionBreakpoints + ) + self.assertTrue( + response["success"], + "setFunctionBreakpoint failed (%s)" % (response), + ) + + self.dap_server.request_configurationDone() response = self.dap_server.request_attach( program=program, pid=pid, @@ -376,6 +401,7 @@ def cleanup(): attachCommands=attachCommands, terminateCommands=terminateCommands, coreFile=coreFile, + stopOnAttach=stopOnAttach, postRunCommands=postRunCommands, sourceMap=sourceMap, gdbRemotePort=gdbRemotePort, @@ -419,6 +445,8 @@ def launch( commandEscapePrefix=None, customFrameFormat=None, customThreadFormat=None, + sourceBreakpoints=None, + functionBreakpoints=None, ): """Sending launch request to dap""" @@ -434,6 +462,29 @@ def cleanup(): # Initialize and launch the program self.dap_server.request_initialize(sourceInitFile) + self.dap_server.wait_for_event("initialized") + + # Set source breakpoints as part of the launch sequence. + if sourceBreakpoints: + for source_path, lines in sourceBreakpoints: + response = self.dap_server.request_setBreakpoints(source_path, lines) + self.assertTrue( + response["success"], + "setBreakpoints failed (%s)" % (response), + ) + + # Set function breakpoints as part of the launch sequence. + if functionBreakpoints: + response = self.dap_server.request_setFunctionBreakpoints( + functionBreakpoints + ) + self.assertTrue( + response["success"], + "setFunctionBreakpoint failed (%s)" % (response), + ) + + self.dap_server.request_configurationDone() + response = self.dap_server.request_launch( program, args=args, @@ -504,6 +555,8 @@ def build_and_launch( customThreadFormat=None, launchCommands=None, expectFailure=False, + sourceBreakpoints=None, + functionBreakpoints=None, ): """Build the default Makefile target, create the DAP debug adapter, and launch the process. @@ -540,6 +593,8 @@ def build_and_launch( customThreadFormat=customThreadFormat, launchCommands=launchCommands, expectFailure=expectFailure, + sourceBreakpoints=sourceBreakpoints, + functionBreakpoints=functionBreakpoints, ) def getBuiltinDebugServerTool(self): diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py index f48d5a7db3c50..741c011a3d692 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py @@ -27,6 +27,8 @@ def spawn_and_wait(program, delay): @skip class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase): def set_and_hit_breakpoint(self, continueToExit=True): + self.dap_server.wait_for_stopped() + source = "main.c" breakpoint1_line = line_number(source, "// breakpoint 1") lines = [breakpoint1_line] diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py index 7f93b9f2a3a22..7250e67ebcd8c 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py @@ -18,17 +18,17 @@ import socket - at skip class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase): default_timeout = 20 def set_and_hit_breakpoint(self, continueToExit=True): + self.dap_server.wait_for_stopped() + source = "main.c" - main_source_path = os.path.join(os.getcwd(), source) - breakpoint1_line = line_number(main_source_path, "// breakpoint 1") + breakpoint1_line = line_number(source, "// breakpoint 1") lines = [breakpoint1_line] # Set breakpoint in the thread function so we can step the threads - breakpoint_ids = self.set_source_breakpoints(main_source_path, lines) + breakpoint_ids = self.set_source_breakpoints(source, lines) self.assertEqual( len(breakpoint_ids), len(lines), "expect correct number of breakpoints" ) diff --git a/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py b/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py index e5590e1b332a0..8581f10cef22a 100644 --- a/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py +++ b/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py @@ -81,52 +81,27 @@ def test_breakpoint_events(self): breakpoint["verified"], "expect foo breakpoint to not be verified" ) - # Get the stop at the entry point - self.continue_to_next_stop() + # Make sure we're stopped. + self.dap_server.wait_for_stopped() - # We are now stopped at the entry point to the program. Shared - # libraries are not loaded yet (at least on macOS they aren't) and only - # the breakpoint in the main executable should be resolved. - self.assertEqual(len(self.dap_server.breakpoint_events), 1) - event = self.dap_server.breakpoint_events[0] - body = event["body"] - self.assertEqual( - body["reason"], "changed", "breakpoint event should say changed" - ) - breakpoint = body["breakpoint"] - self.assertEqual(breakpoint["id"], main_bp_id) - self.assertTrue(breakpoint["verified"], "main breakpoint should be resolved") - - # Clear the list of breakpoint events so we don't see this one again. - self.dap_server.breakpoint_events.clear() + # Flush the breakpoint events. + self.dap_server.wait_for_breakpoint_events(timeout=5) # Continue to the breakpoint self.continue_to_breakpoints(dap_breakpoint_ids) - # When the process launches, we first expect to see both the main and - # foo breakpoint as unresolved. - for event in self.dap_server.breakpoint_events[:2]: - body = event["body"] - self.assertEqual( - body["reason"], "changed", "breakpoint event should say changed" - ) - breakpoint = body["breakpoint"] - self.assertIn(str(breakpoint["id"]), dap_breakpoint_ids) - self.assertFalse(breakpoint["verified"], "breakpoint should be unresolved") + verified_breakpoint_ids = [] + unverified_breakpoint_ids = [] + for breakpoint_event in self.dap_server.wait_for_breakpoint_events(timeout=5): + breakpoint = breakpoint_event["body"]["breakpoint"] + id = breakpoint["id"] + if breakpoint["verified"]: + verified_breakpoint_ids.append(id) + else: + unverified_breakpoint_ids.append(id) - # Then, once the dynamic loader has given us a load address, they - # should show up as resolved again. - for event in self.dap_server.breakpoint_events[3:]: - body = event["body"] - self.assertEqual( - body["reason"], "changed", "breakpoint event should say changed" - ) - breakpoint = body["breakpoint"] - self.assertIn(str(breakpoint["id"]), dap_breakpoint_ids) - self.assertTrue(breakpoint["verified"], "breakpoint should be resolved") - self.assertNotIn( - "source", - breakpoint, - "breakpoint event should not return a source object", - ) - self.assertIn("line", breakpoint, "breakpoint event should have line") + self.assertIn(main_bp_id, unverified_breakpoint_ids) + self.assertIn(foo_bp_id, unverified_breakpoint_ids) + + self.assertIn(main_bp_id, verified_breakpoint_ids) + self.assertIn(foo_bp_id, verified_breakpoint_ids) diff --git a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py index 210e591bff426..a94288c7a669e 100644 --- a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py +++ b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py @@ -2,7 +2,6 @@ Test lldb-dap completions request """ - import lldbdap_testcase import dap_server from lldbsuite.test import lldbutil @@ -32,6 +31,7 @@ variable_var1_completion = {"text": "var1", "label": "var1 -- int &"} variable_var2_completion = {"text": "var2", "label": "var2 -- int &"} + # Older version of libcxx produce slightly different typename strings for # templates like vector. @skipIf(compiler="clang", compiler_version=["<", "16.0"]) @@ -43,16 +43,22 @@ def verify_completions(self, actual_list, expected_list, not_expected_list=[]): for not_expected_item in not_expected_list: self.assertNotIn(not_expected_item, actual_list) - - def setup_debugee(self): + def setup_debugee(self, stopOnEntry=False): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) - source = "main.cpp" - breakpoint1_line = line_number(source, "// breakpoint 1") - breakpoint2_line = line_number(source, "// breakpoint 2") - - self.set_source_breakpoints(source, [breakpoint1_line, breakpoint2_line]) + self.build_and_launch( + program, + stopOnEntry=stopOnEntry, + sourceBreakpoints=[ + ( + source, + [ + line_number(source, "// breakpoint 1"), + line_number(source, "// breakpoint 2"), + ], + ), + ], + ) def test_command_completions(self): """ @@ -235,7 +241,7 @@ def test_auto_completions(self): """ Tests completion requests in "repl-mode=auto" """ - self.setup_debugee() + self.setup_debugee(stopOnEntry=True) res = self.dap_server.request_evaluate( "`lldb-dap repl-mode auto", context="repl" diff --git a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py index b07c4f871d73b..8642e317f9b3a 100644 --- a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py +++ b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py @@ -19,6 +19,7 @@ def get_subprocess(root_process, process_name): self.assertTrue(False, "No subprocess with name %s found" % process_name) + class TestDAP_console(lldbdap_testcase.DAPTestCaseBase): def check_lldb_command( self, lldb_command, contains_string, assert_msg, command_escape_prefix="`" @@ -52,7 +53,7 @@ def test_scopes_variables_setVariable_evaluate(self): character. """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") lines = [breakpoint1_line] @@ -81,7 +82,7 @@ def test_scopes_variables_setVariable_evaluate(self): def test_custom_escape_prefix(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program, commandEscapePrefix="::") + self.build_and_launch(program, stopOnEntry=True, commandEscapePrefix="::") source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line]) @@ -96,7 +97,7 @@ def test_custom_escape_prefix(self): def test_empty_escape_prefix(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program, commandEscapePrefix="") + self.build_and_launch(program, stopOnEntry=True, commandEscapePrefix="") source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line]) @@ -113,7 +114,7 @@ def test_empty_escape_prefix(self): def test_exit_status_message_sigterm(self): source = "main.cpp" program = self.getBuildArtifact("a.out") - self.build_and_launch(program, commandEscapePrefix="") + self.build_and_launch(program, stopOnEntry=True, commandEscapePrefix="") breakpoint1_line = line_number(source, "// breakpoint 1") breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line]) self.continue_to_breakpoints(breakpoint_ids) @@ -167,7 +168,7 @@ def test_exit_status_message_ok(self): de... [truncated] ``````````
https://github.com/llvm/llvm-project/pull/138981 From lldb-commits at lists.llvm.org Wed May 7 15:33:36 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Wed, 07 May 2025 15:33:36 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Adding a modules explorer to lldb-dap ext. (PR #138977) In-Reply-To: Message-ID: <681bdfc0.050a0220.230a99.4045@mx.google.com> https://github.com/JDevlieghere commented: This is really awesome and I'm surprised/impressed with how little TypeScript it takes to implement something like this. https://github.com/llvm/llvm-project/pull/138977 From lldb-commits at lists.llvm.org Wed May 7 15:33:36 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Wed, 07 May 2025 15:33:36 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Adding a modules explorer to lldb-dap ext. (PR #138977) In-Reply-To: Message-ID: <681bdfc0.170a0220.e0d4e.e492@mx.google.com> https://github.com/JDevlieghere edited https://github.com/llvm/llvm-project/pull/138977 From lldb-commits at lists.llvm.org Wed May 7 15:33:36 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Wed, 07 May 2025 15:33:36 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Adding a modules explorer to lldb-dap ext. (PR #138977) In-Reply-To: Message-ID: <681bdfc0.170a0220.10c52d.bf48@mx.google.com> ================ @@ -763,6 +764,16 @@ } ] } - ] + ], + "views": { + "debug": [ + { + "id": "lldb-dap.modulesExplorer", + "name": "LLDB Modules Explorer", ---------------- JDevlieghere wrote: Is "explorer" common terminology in VS Code for something like this? I've seen the term in the context of a file explorer and a remote (file) explorer but I don't remember seeing it in this context. If not, would "LLDB Modules" or even just "Modules" be descriptive enough for this view? https://github.com/llvm/llvm-project/pull/138977 From lldb-commits at lists.llvm.org Wed May 7 15:54:23 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Wed, 07 May 2025 15:54:23 -0700 (PDT) Subject: [Lldb-commits] [lldb] 0d0ef58 - [lldb][Darwin] Note why this test is xfail'ed on Message-ID: <681be49f.170a0220.bcd9d.b87a@mx.google.com> Author: Jason Molenda Date: 2025-05-07T15:53:30-07:00 New Revision: 0d0ef58c8facb0f2e5c2f4615b0d25f19e5abe01 URL: https://github.com/llvm/llvm-project/commit/0d0ef58c8facb0f2e5c2f4615b0d25f19e5abe01 DIFF: https://github.com/llvm/llvm-project/commit/0d0ef58c8facb0f2e5c2f4615b0d25f19e5abe01.diff LOG: [lldb][Darwin] Note why this test is xfail'ed on darwin - due to there not being any eh_frame instructions for _sigtramp from the system libraries. Added: Modified: lldb/test/Shell/Unwind/signal-in-leaf-function-aarch64.test Removed: ################################################################################ diff --git a/lldb/test/Shell/Unwind/signal-in-leaf-function-aarch64.test b/lldb/test/Shell/Unwind/signal-in-leaf-function-aarch64.test index 2ac2d4a750782..050c41c73f9c2 100644 --- a/lldb/test/Shell/Unwind/signal-in-leaf-function-aarch64.test +++ b/lldb/test/Shell/Unwind/signal-in-leaf-function-aarch64.test @@ -1,6 +1,8 @@ # REQUIRES: target-aarch64 && native # UNSUPPORTED: system-windows # llvm.org/pr91610, rdar://128031075 + +# Darwin _sigtramp doesn't have eh_frame instruction on AArch64 # XFAIL: system-darwin From lldb-commits at lists.llvm.org Wed May 7 15:54:30 2025 From: lldb-commits at lists.llvm.org (Ely Ronnen via lldb-commits) Date: Wed, 07 May 2025 15:54:30 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Expose QueueThreadPlanForStepSingleInstruction function to SBThreadPlan (PR #137904) In-Reply-To: Message-ID: <681be4a6.170a0220.bed2b.b3cb@mx.google.com> https://github.com/eronnen updated https://github.com/llvm/llvm-project/pull/137904 >From 998d59c6462326874d3fcd13f4cf92290c7bf5f9 Mon Sep 17 00:00:00 2001 From: Ely Ronnen Date: Wed, 30 Apr 2025 02:00:44 +0200 Subject: [PATCH 1/5] Expose QueueThreadPlanForStepSingleInstruction function to SBThreadPlan --- lldb/include/lldb/API/SBThreadPlan.h | 4 ++++ lldb/source/API/SBThreadPlan.cpp | 31 ++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/lldb/include/lldb/API/SBThreadPlan.h b/lldb/include/lldb/API/SBThreadPlan.h index d02ab80f76a76..ce1a1078d69b9 100644 --- a/lldb/include/lldb/API/SBThreadPlan.h +++ b/lldb/include/lldb/API/SBThreadPlan.h @@ -105,6 +105,10 @@ class LLDB_API SBThreadPlan { SBThreadPlan QueueThreadPlanForStepOut(uint32_t frame_idx_to_step_to, bool first_insn, SBError &error); + SBThreadPlan QueueThreadPlanForStepSingleInstruction(bool step_over); + SBThreadPlan QueueThreadPlanForStepSingleInstruction(bool step_over, + SBError &error); + SBThreadPlan QueueThreadPlanForRunToAddress(SBAddress address); SBThreadPlan QueueThreadPlanForRunToAddress(SBAddress address, SBError &error); diff --git a/lldb/source/API/SBThreadPlan.cpp b/lldb/source/API/SBThreadPlan.cpp index 083a050de8a38..a85afbb043875 100644 --- a/lldb/source/API/SBThreadPlan.cpp +++ b/lldb/source/API/SBThreadPlan.cpp @@ -325,6 +325,37 @@ SBThreadPlan::QueueThreadPlanForStepOut(uint32_t frame_idx_to_step_to, return SBThreadPlan(); } +SBThreadPlan +SBThreadPlan::QueueThreadPlanForStepSingleInstruction(bool step_over) { + LLDB_INSTRUMENT_VA(this, step_over); + + SBError error; + return QueueThreadPlanForStepSingleInstruction(step_over, error); +} + +SBThreadPlan +SBThreadPlan::QueueThreadPlanForStepSingleInstruction(bool step_over, + SBError &error) { + LLDB_INSTRUMENT_VA(this, step_over, error); + + ThreadPlanSP thread_plan_sp(GetSP()); + if (thread_plan_sp) { + Status plan_status; + SBThreadPlan plan( + thread_plan_sp->GetThread().QueueThreadPlanForStepSingleInstruction( + step_over, false, false, plan_status)); + + if (plan_status.Fail()) + error.SetErrorString(plan_status.AsCString()); + else + plan.GetSP()->SetPrivate(true); + + return plan; + } + + return SBThreadPlan(); +} + SBThreadPlan SBThreadPlan::QueueThreadPlanForRunToAddress(SBAddress sb_address) { LLDB_INSTRUMENT_VA(this, sb_address); >From 7cedaf50faba503816232fb63d077b6c7def26b1 Mon Sep 17 00:00:00 2001 From: Ely Ronnen Date: Wed, 30 Apr 2025 09:37:32 +0200 Subject: [PATCH 2/5] Add API tests for QueueThreadPlanForStepSingleInstruction --- .../functionalities/step_scripted/Steps.py | 16 +++++++++++ .../step_scripted/TestStepScripted.py | 28 +++++++++++++++++++ .../API/functionalities/step_scripted/main.c | 2 +- 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/lldb/test/API/functionalities/step_scripted/Steps.py b/lldb/test/API/functionalities/step_scripted/Steps.py index 3325dba753657..e9e0fae1b14be 100644 --- a/lldb/test/API/functionalities/step_scripted/Steps.py +++ b/lldb/test/API/functionalities/step_scripted/Steps.py @@ -45,6 +45,22 @@ def queue_child_thread_plan(self): return self.thread_plan.QueueThreadPlanForStepScripted("Steps.StepOut") +class StepSingleInstruction(StepWithChild): + def __init__(self, thread_plan, dict): + super().__init__(thread_plan) + + def queue_child_thread_plan(self): + return self.thread_plan.QueueThreadPlanForStepSingleInstruction(False) + + +class StepSingleInstructionWithStepOver(StepWithChild): + def __init__(self, thread_plan, dict): + super().__init__(thread_plan) + + def queue_child_thread_plan(self): + return self.thread_plan.QueueThreadPlanForStepSingleInstruction(True) + + # This plan does a step-over until a variable changes value. class StepUntil(StepWithChild): def __init__(self, thread_plan, args_data): diff --git a/lldb/test/API/functionalities/step_scripted/TestStepScripted.py b/lldb/test/API/functionalities/step_scripted/TestStepScripted.py index 53901718019f9..f65c366a09e87 100644 --- a/lldb/test/API/functionalities/step_scripted/TestStepScripted.py +++ b/lldb/test/API/functionalities/step_scripted/TestStepScripted.py @@ -44,6 +44,34 @@ def step_out_with_scripted_plan(self, name): stop_desc = thread.GetStopDescription(1000) self.assertIn("Stepping out from", stop_desc, "Got right description") + def test_step_single_instruction(self): + self.build() + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "Break on foo call", self.main_source_file + ) + + err = thread.StepUsingScriptedThreadPlan("Steps.StepSingleInstruction") + self.assertSuccess(err) + + # Verify that stepping a single instruction after "foo();" steps into `foo` + frame = thread.GetFrameAtIndex(0) + self.assertEqual("foo", frame.GetFunctionName()) + + def test_step_single_instruction_with_step_over(self): + self.build() + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "Break on foo call", self.main_source_file + ) + + err = thread.StepUsingScriptedThreadPlan( + "Steps.StepSingleInstructionWithStepOver" + ) + self.assertSuccess(err) + + # Verify that stepping over an instruction doesn't step into `foo` + frame = thread.GetFrameAtIndex(0) + self.assertEqual("main", frame.GetFunctionName()) + def test_misspelled_plan_name(self): """Test that we get a useful error if we misspell the plan class name""" self.build() diff --git a/lldb/test/API/functionalities/step_scripted/main.c b/lldb/test/API/functionalities/step_scripted/main.c index bfd8a35d55626..499a999e699d0 100644 --- a/lldb/test/API/functionalities/step_scripted/main.c +++ b/lldb/test/API/functionalities/step_scripted/main.c @@ -8,6 +8,6 @@ void foo() { } int main() { - foo(); + foo(); // Break on foo call. return 0; } >From b9658d5c538aa121214d32f46982e66f453d5483 Mon Sep 17 00:00:00 2001 From: Ely Ronnen Date: Wed, 30 Apr 2025 22:32:57 +0200 Subject: [PATCH 3/5] improve QueueThreadPlanForStepSingleInstruction tests --- .../step_scripted/TestStepScripted.py | 26 ++++++++++++++----- .../API/functionalities/step_scripted/main.c | 2 +- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/lldb/test/API/functionalities/step_scripted/TestStepScripted.py b/lldb/test/API/functionalities/step_scripted/TestStepScripted.py index f65c366a09e87..54bc154590ed0 100644 --- a/lldb/test/API/functionalities/step_scripted/TestStepScripted.py +++ b/lldb/test/API/functionalities/step_scripted/TestStepScripted.py @@ -44,11 +44,23 @@ def step_out_with_scripted_plan(self, name): stop_desc = thread.GetStopDescription(1000) self.assertIn("Stepping out from", stop_desc, "Got right description") - def test_step_single_instruction(self): + def run_until_branch_instruction(self): self.build() (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( - self, "Break on foo call", self.main_source_file + self, "Break on branch instruction", self.main_source_file + ) + + # Check that we landed in a call instruction + frame = thread.GetFrameAtIndex(0) + current_instruction = target.ReadInstructions(frame.GetPCAddress(), 1)[0] + self.assertEqual( + lldb.eInstructionControlFlowKindCall, + current_instruction.GetControlFlowKind(target), ) + return (target, process, thread, bkpt) + + def test_step_single_instruction(self): + (target, process, thread, bkpt) = self.run_until_branch_instruction() err = thread.StepUsingScriptedThreadPlan("Steps.StepSingleInstruction") self.assertSuccess(err) @@ -58,10 +70,11 @@ def test_step_single_instruction(self): self.assertEqual("foo", frame.GetFunctionName()) def test_step_single_instruction_with_step_over(self): - self.build() - (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( - self, "Break on foo call", self.main_source_file - ) + (target, process, thread, bkpt) = self.run_until_branch_instruction() + + frame = thread.GetFrameAtIndex(0) + next_instruction = target.ReadInstructions(frame.GetPCAddress(), 2)[1] + next_instruction_address = next_instruction.GetAddress() err = thread.StepUsingScriptedThreadPlan( "Steps.StepSingleInstructionWithStepOver" @@ -71,6 +84,7 @@ def test_step_single_instruction_with_step_over(self): # Verify that stepping over an instruction doesn't step into `foo` frame = thread.GetFrameAtIndex(0) self.assertEqual("main", frame.GetFunctionName()) + self.assertEqual(next_instruction_address, frame.GetPCAddress()) def test_misspelled_plan_name(self): """Test that we get a useful error if we misspell the plan class name""" diff --git a/lldb/test/API/functionalities/step_scripted/main.c b/lldb/test/API/functionalities/step_scripted/main.c index 499a999e699d0..9023120c44312 100644 --- a/lldb/test/API/functionalities/step_scripted/main.c +++ b/lldb/test/API/functionalities/step_scripted/main.c @@ -8,6 +8,6 @@ void foo() { } int main() { - foo(); // Break on foo call. + foo(); // Break on branch instruction. return 0; } >From 4d7a735bb8d7a8298d529ff4f8a90d48713b60a1 Mon Sep 17 00:00:00 2001 From: Ely Ronnen Date: Thu, 1 May 2025 21:52:11 +0200 Subject: [PATCH 4/5] force QueueThreadPlanForStepSingleInstruction to accept SBError --- lldb/include/lldb/API/SBThreadPlan.h | 1 - lldb/source/API/SBThreadPlan.cpp | 8 -------- lldb/test/API/functionalities/step_scripted/Steps.py | 4 ++-- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/lldb/include/lldb/API/SBThreadPlan.h b/lldb/include/lldb/API/SBThreadPlan.h index ce1a1078d69b9..1f0164efcfb98 100644 --- a/lldb/include/lldb/API/SBThreadPlan.h +++ b/lldb/include/lldb/API/SBThreadPlan.h @@ -105,7 +105,6 @@ class LLDB_API SBThreadPlan { SBThreadPlan QueueThreadPlanForStepOut(uint32_t frame_idx_to_step_to, bool first_insn, SBError &error); - SBThreadPlan QueueThreadPlanForStepSingleInstruction(bool step_over); SBThreadPlan QueueThreadPlanForStepSingleInstruction(bool step_over, SBError &error); diff --git a/lldb/source/API/SBThreadPlan.cpp b/lldb/source/API/SBThreadPlan.cpp index a85afbb043875..c8ca6c81a3efb 100644 --- a/lldb/source/API/SBThreadPlan.cpp +++ b/lldb/source/API/SBThreadPlan.cpp @@ -325,14 +325,6 @@ SBThreadPlan::QueueThreadPlanForStepOut(uint32_t frame_idx_to_step_to, return SBThreadPlan(); } -SBThreadPlan -SBThreadPlan::QueueThreadPlanForStepSingleInstruction(bool step_over) { - LLDB_INSTRUMENT_VA(this, step_over); - - SBError error; - return QueueThreadPlanForStepSingleInstruction(step_over, error); -} - SBThreadPlan SBThreadPlan::QueueThreadPlanForStepSingleInstruction(bool step_over, SBError &error) { diff --git a/lldb/test/API/functionalities/step_scripted/Steps.py b/lldb/test/API/functionalities/step_scripted/Steps.py index e9e0fae1b14be..bf4c1f38cbbb3 100644 --- a/lldb/test/API/functionalities/step_scripted/Steps.py +++ b/lldb/test/API/functionalities/step_scripted/Steps.py @@ -50,7 +50,7 @@ def __init__(self, thread_plan, dict): super().__init__(thread_plan) def queue_child_thread_plan(self): - return self.thread_plan.QueueThreadPlanForStepSingleInstruction(False) + return self.thread_plan.QueueThreadPlanForStepSingleInstruction(False, lldb.SBError()) class StepSingleInstructionWithStepOver(StepWithChild): @@ -58,7 +58,7 @@ def __init__(self, thread_plan, dict): super().__init__(thread_plan) def queue_child_thread_plan(self): - return self.thread_plan.QueueThreadPlanForStepSingleInstruction(True) + return self.thread_plan.QueueThreadPlanForStepSingleInstruction(True, lldb.SBError()) # This plan does a step-over until a variable changes value. >From 1faea156f7d65689a3cfba7bec1a42c6b5cca430 Mon Sep 17 00:00:00 2001 From: Ely Ronnen Date: Thu, 1 May 2025 21:54:52 +0200 Subject: [PATCH 5/5] format --- lldb/test/API/functionalities/step_scripted/Steps.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lldb/test/API/functionalities/step_scripted/Steps.py b/lldb/test/API/functionalities/step_scripted/Steps.py index bf4c1f38cbbb3..e2a03c9988111 100644 --- a/lldb/test/API/functionalities/step_scripted/Steps.py +++ b/lldb/test/API/functionalities/step_scripted/Steps.py @@ -50,7 +50,9 @@ def __init__(self, thread_plan, dict): super().__init__(thread_plan) def queue_child_thread_plan(self): - return self.thread_plan.QueueThreadPlanForStepSingleInstruction(False, lldb.SBError()) + return self.thread_plan.QueueThreadPlanForStepSingleInstruction( + False, lldb.SBError() + ) class StepSingleInstructionWithStepOver(StepWithChild): @@ -58,7 +60,9 @@ def __init__(self, thread_plan, dict): super().__init__(thread_plan) def queue_child_thread_plan(self): - return self.thread_plan.QueueThreadPlanForStepSingleInstruction(True, lldb.SBError()) + return self.thread_plan.QueueThreadPlanForStepSingleInstruction( + True, lldb.SBError() + ) # This plan does a step-over until a variable changes value. From lldb-commits at lists.llvm.org Wed May 7 16:17:42 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Wed, 07 May 2025 16:17:42 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Adding a modules explorer to lldb-dap ext. (PR #138977) In-Reply-To: Message-ID: <681bea16.170a0220.ab65.ad66@mx.google.com> https://github.com/ashgti updated https://github.com/llvm/llvm-project/pull/138977 >From 756174c282fc7101bfadb29556f39ce046ad0116 Mon Sep 17 00:00:00 2001 From: John Harrison Date: Wed, 7 May 2025 14:54:29 -0700 Subject: [PATCH 1/2] [lldb-dap] Adding a modules explorer to lldb-dap ext. This creates a very basic module explorer for tracking and displaying loaded modules, reported by lldb-dap for the active debug session. This includes a basic session tracker that we can use to observe the debug session and collect specific information for additional visualizations in the lldb-dap ext. --- lldb/tools/lldb-dap/package-lock.json | 12 ++- lldb/tools/lldb-dap/package.json | 15 +++- .../src-ts/debug-configuration-provider.ts | 2 +- .../lldb-dap/src-ts/debug-session-tracker.ts | 87 +++++++++++++++++++ .../lldb-dap/src-ts/disposable-context.ts | 4 +- lldb/tools/lldb-dap/src-ts/extension.ts | 18 ++++ .../src-ts/ui/modules-data-provider.ts | 58 +++++++++++++ 7 files changed, 189 insertions(+), 7 deletions(-) create mode 100644 lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts create mode 100644 lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts diff --git a/lldb/tools/lldb-dap/package-lock.json b/lldb/tools/lldb-dap/package-lock.json index ab5c7dc33a8e5..0a2b9e764067e 100644 --- a/lldb/tools/lldb-dap/package-lock.json +++ b/lldb/tools/lldb-dap/package-lock.json @@ -1,16 +1,17 @@ { "name": "lldb-dap", - "version": "0.2.10", + "version": "0.2.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "lldb-dap", - "version": "0.2.10", + "version": "0.2.13", "license": "Apache 2.0 License with LLVM exceptions", "devDependencies": { "@types/node": "^18.19.41", "@types/vscode": "1.75.0", + "@vscode/debugprotocol": "^1.68.0", "@vscode/vsce": "^3.2.2", "prettier": "^3.4.2", "prettier-plugin-curly": "^0.3.1", @@ -405,6 +406,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@vscode/debugprotocol": { + "version": "1.68.0", + "resolved": "https://registry.npmjs.org/@vscode/debugprotocol/-/debugprotocol-1.68.0.tgz", + "integrity": "sha512-2J27dysaXmvnfuhFGhfeuxfHRXunqNPxtBoR3koiTOA9rdxWNDTa1zIFLCFMSHJ9MPTPKFcBeblsyaCJCIlQxg==", + "dev": true, + "license": "MIT" + }, "node_modules/@vscode/vsce": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.2.2.tgz", diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json index f66badc2a930f..a8dce7cab8a79 100644 --- a/lldb/tools/lldb-dap/package.json +++ b/lldb/tools/lldb-dap/package.json @@ -30,9 +30,10 @@ "devDependencies": { "@types/node": "^18.19.41", "@types/vscode": "1.75.0", + "@vscode/debugprotocol": "^1.68.0", "@vscode/vsce": "^3.2.2", - "prettier-plugin-curly": "^0.3.1", "prettier": "^3.4.2", + "prettier-plugin-curly": "^0.3.1", "typescript": "^5.7.3" }, "activationEvents": [ @@ -763,6 +764,16 @@ } ] } - ] + ], + "views": { + "debug": [ + { + "id": "lldb-dap.modulesExplorer", + "name": "LLDB Modules Explorer", + "when": "inDebugMode && debugType == 'lldb-dap'", + "icon": "$(symbol-module)" + } + ] + } } } diff --git a/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts b/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts index c91b101f4a9ba..957bc5e1eb956 100644 --- a/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts +++ b/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts @@ -78,7 +78,7 @@ export class LLDBDapConfigurationProvider debugConfiguration: vscode.DebugConfiguration, token?: vscode.CancellationToken, ): Promise { - let config = vscode.workspace.getConfiguration("lldb-dap.defaults"); + let config = vscode.workspace.getConfiguration("lldb-dap"); for (const [key, cfg] of Object.entries(configurations)) { if (Reflect.has(debugConfiguration, key)) { continue; diff --git a/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts b/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts new file mode 100644 index 0000000000000..d756e9e743bb0 --- /dev/null +++ b/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts @@ -0,0 +1,87 @@ +import { DebugProtocol } from "@vscode/debugprotocol"; +import * as vscode from "vscode"; + +interface EventMap { + module: DebugProtocol.ModuleEvent; +} + +function isEvent( + message: DebugProtocol.ProtocolMessage, +): message is DebugProtocol.Event; +function isEvent( + message: DebugProtocol.ProtocolMessage, + event: K, +): message is EventMap[K]; +function isEvent( + message: DebugProtocol.ProtocolMessage, + event?: string, +): boolean { + return ( + message.type === "event" && + (!event || (message as DebugProtocol.Event).event === event) + ); +} + +export class DebugSessionTracker + implements vscode.DebugAdapterTrackerFactory, vscode.Disposable +{ + private modules = new Map(); + private modulesChanged = new vscode.EventEmitter(); + onDidChangeModules: vscode.Event = this.modulesChanged.event; + + dispose() { + this.modules.clear(); + this.modulesChanged.dispose(); + } + + createDebugAdapterTracker( + session: vscode.DebugSession, + ): vscode.ProviderResult { + return { + onDidSendMessage: (message) => this.onDidSendMessage(session, message), + onExit: () => this.onExit(session), + }; + } + + debugSessionModules(session: vscode.DebugSession): DebugProtocol.Module[] { + return this.modules.get(session) ?? []; + } + + private onExit(session: vscode.DebugSession) { + this.modules.delete(session); + } + + private onDidSendMessage( + session: vscode.DebugSession, + message: DebugProtocol.ProtocolMessage, + ) { + if (isEvent(message, "module")) { + const { module, reason } = message.body; + const modules = this.modules.get(session) ?? []; + switch (reason) { + case "new": + case "changed": { + const index = modules.findIndex((m) => m.id === module.id); + if (index !== -1) { + modules[index] = module; + } else { + modules.push(module); + } + break; + } + case "removed": { + const index = modules.findIndex((m) => m.id === module.id); + if (index !== -1) { + modules.splice(index, 1); + } + break; + } + default: + console.error("unexpected module event reason"); + break; + } + this.modules.set(session, modules); + this.modulesChanged.fire(); + } + } +} diff --git a/lldb/tools/lldb-dap/src-ts/disposable-context.ts b/lldb/tools/lldb-dap/src-ts/disposable-context.ts index 39d9f18d2d85f..42ece763d247f 100644 --- a/lldb/tools/lldb-dap/src-ts/disposable-context.ts +++ b/lldb/tools/lldb-dap/src-ts/disposable-context.ts @@ -21,7 +21,7 @@ export class DisposableContext implements vscode.Disposable { * * @param disposable The disposable to register. */ - public pushSubscription(disposable: vscode.Disposable) { - this._disposables.push(disposable); + public pushSubscription(...disposable: vscode.Disposable[]) { + this._disposables.push(...disposable); } } diff --git a/lldb/tools/lldb-dap/src-ts/extension.ts b/lldb/tools/lldb-dap/src-ts/extension.ts index 0b014f953d5ba..1bca842480809 100644 --- a/lldb/tools/lldb-dap/src-ts/extension.ts +++ b/lldb/tools/lldb-dap/src-ts/extension.ts @@ -5,6 +5,8 @@ import { DisposableContext } from "./disposable-context"; import { LaunchUriHandler } from "./uri-launch-handler"; import { LLDBDapConfigurationProvider } from "./debug-configuration-provider"; import { LLDBDapServer } from "./lldb-dap-server"; +import { DebugSessionTracker } from "./debug-session-tracker"; +import { ModuleDataProvider } from "./ui/modules-data-provider"; /** * This class represents the extension and manages its life cycle. Other extensions @@ -31,6 +33,22 @@ export class LLDBDapExtension extends DisposableContext { ), ); + const sessionTracker = new DebugSessionTracker(); + + this.pushSubscription( + vscode.debug.registerDebugAdapterTrackerFactory( + "lldb-dap", + sessionTracker, + ), + ); + + this.pushSubscription( + vscode.window.registerTreeDataProvider( + "lldb-dap.modulesExplorer", + new ModuleDataProvider(sessionTracker), + ), + ); + this.pushSubscription( vscode.window.registerUriHandler(new LaunchUriHandler()), ); diff --git a/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts b/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts new file mode 100644 index 0000000000000..d5cea513d7ef4 --- /dev/null +++ b/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts @@ -0,0 +1,58 @@ +import * as vscode from "vscode"; +import { DebugProtocol } from "@vscode/debugprotocol"; +import { DebugSessionTracker } from "../debug-session-tracker"; + +export class ModuleDataProvider + implements vscode.TreeDataProvider +{ + private changeTreeData = new vscode.EventEmitter(); + readonly onDidChangeTreeData = this.changeTreeData.event; + + constructor(private readonly tracker: DebugSessionTracker) { + tracker.onDidChangeModules(() => this.changeTreeData.fire()); + vscode.debug.onDidChangeActiveDebugSession(() => + this.changeTreeData.fire(), + ); + } + + getTreeItem(module: DebugProtocol.Module): vscode.TreeItem { + let treeItem = new vscode.TreeItem(/*label=*/ module.name); + if (module.path) { + treeItem.description = `${module.id} -- ${module.path}`; + } else { + treeItem.description = `${module.id}`; + } + + const tooltip = new vscode.MarkdownString(); + tooltip.appendMarkdown(`# Module '${module.name}'\n\n`); + tooltip.appendMarkdown(`- **id** : ${module.id}\n`); + if (module.addressRange) { + tooltip.appendMarkdown(`- **load address** : ${module.addressRange}\n`); + } + if (module.path) { + tooltip.appendMarkdown(`- **path** : ${module.path}\n`); + } + if (module.version) { + tooltip.appendMarkdown(`- **version** : ${module.version}\n`); + } + if (module.symbolStatus) { + tooltip.appendMarkdown(`- **symbol status** : ${module.symbolStatus}\n`); + } + if (module.symbolFilePath) { + tooltip.appendMarkdown( + `- **symbol file path** : ${module.symbolFilePath}\n`, + ); + } + + treeItem.tooltip = tooltip; + return treeItem; + } + + getChildren(): DebugProtocol.Module[] { + if (!vscode.debug.activeDebugSession) { + return []; + } + + return this.tracker.debugSessionModules(vscode.debug.activeDebugSession); + } +} >From da002083736f97692471d661cd31a64b37835205 Mon Sep 17 00:00:00 2001 From: John Harrison Date: Wed, 7 May 2025 16:16:37 -0700 Subject: [PATCH 2/2] Adjusting the modules tree view name and adding some documentation to help explain the code a bit. --- lldb/tools/lldb-dap/package.json | 4 ++-- .../lldb-dap/src-ts/debug-session-tracker.ts | 24 ++++++++++++++++++- lldb/tools/lldb-dap/src-ts/extension.ts | 24 +++++-------------- .../src-ts/ui/modules-data-provider.ts | 3 ++- 4 files changed, 33 insertions(+), 22 deletions(-) diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json index a8dce7cab8a79..1149a33719ae5 100644 --- a/lldb/tools/lldb-dap/package.json +++ b/lldb/tools/lldb-dap/package.json @@ -768,8 +768,8 @@ "views": { "debug": [ { - "id": "lldb-dap.modulesExplorer", - "name": "LLDB Modules Explorer", + "id": "lldb-dap.modules", + "name": "Modules", "when": "inDebugMode && debugType == 'lldb-dap'", "icon": "$(symbol-module)" } diff --git a/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts b/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts index d756e9e743bb0..1ce190938d9c7 100644 --- a/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts +++ b/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts @@ -1,10 +1,13 @@ import { DebugProtocol } from "@vscode/debugprotocol"; import * as vscode from "vscode"; +/** A helper type for mapping event types to their corresponding data type. */ +// prettier-ignore interface EventMap { - module: DebugProtocol.ModuleEvent; + "module": DebugProtocol.ModuleEvent; } +/** A type assertion to check if a ProtocolMessage is an event or if it is a specific event. */ function isEvent( message: DebugProtocol.ProtocolMessage, ): message is DebugProtocol.Event; @@ -22,11 +25,23 @@ function isEvent( ); } +/** Tracks lldb-dap sessions for data visualizers. */ export class DebugSessionTracker implements vscode.DebugAdapterTrackerFactory, vscode.Disposable { + /** + * Tracks active modules for each debug sessions. + * + * The modules are kept in an array to maintain the load order of the modules. + */ private modules = new Map(); private modulesChanged = new vscode.EventEmitter(); + + /** + * Fired when modules are changed for any active debug session. + * + * Use `debugSessionModules` to retieve the active modules for a given debug session. + */ onDidChangeModules: vscode.Event = this.modulesChanged.event; dispose() { @@ -43,12 +58,19 @@ export class DebugSessionTracker }; } + /** + * Retrieves the modules for the given debug session. + * + * Modules are returned in load order. + */ debugSessionModules(session: vscode.DebugSession): DebugProtocol.Module[] { return this.modules.get(session) ?? []; } + /** Clear information from the active session. */ private onExit(session: vscode.DebugSession) { this.modules.delete(session); + this.modulesChanged.fire(); } private onDidSendMessage( diff --git a/lldb/tools/lldb-dap/src-ts/extension.ts b/lldb/tools/lldb-dap/src-ts/extension.ts index 1bca842480809..a5c0a09ae60cf 100644 --- a/lldb/tools/lldb-dap/src-ts/extension.ts +++ b/lldb/tools/lldb-dap/src-ts/extension.ts @@ -6,7 +6,7 @@ import { LaunchUriHandler } from "./uri-launch-handler"; import { LLDBDapConfigurationProvider } from "./debug-configuration-provider"; import { LLDBDapServer } from "./lldb-dap-server"; import { DebugSessionTracker } from "./debug-session-tracker"; -import { ModuleDataProvider } from "./ui/modules-data-provider"; +import { ModulesDataProvider } from "./ui/modules-data-provider"; /** * This class represents the extension and manages its life cycle. Other extensions @@ -17,39 +17,27 @@ export class LLDBDapExtension extends DisposableContext { super(); const lldbDapServer = new LLDBDapServer(); - this.pushSubscription(lldbDapServer); + const sessionTracker = new DebugSessionTracker(); this.pushSubscription( + lldbDapServer, + sessionTracker, vscode.debug.registerDebugConfigurationProvider( "lldb-dap", new LLDBDapConfigurationProvider(lldbDapServer), ), - ); - - this.pushSubscription( vscode.debug.registerDebugAdapterDescriptorFactory( "lldb-dap", new LLDBDapDescriptorFactory(), ), - ); - - const sessionTracker = new DebugSessionTracker(); - - this.pushSubscription( vscode.debug.registerDebugAdapterTrackerFactory( "lldb-dap", sessionTracker, ), - ); - - this.pushSubscription( vscode.window.registerTreeDataProvider( - "lldb-dap.modulesExplorer", - new ModuleDataProvider(sessionTracker), + "lldb-dap.modules", + new ModulesDataProvider(sessionTracker), ), - ); - - this.pushSubscription( vscode.window.registerUriHandler(new LaunchUriHandler()), ); } diff --git a/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts b/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts index d5cea513d7ef4..5af3d52e9870c 100644 --- a/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts +++ b/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts @@ -2,7 +2,8 @@ import * as vscode from "vscode"; import { DebugProtocol } from "@vscode/debugprotocol"; import { DebugSessionTracker } from "../debug-session-tracker"; -export class ModuleDataProvider +/** A tree data provider for listing loaded modules for the active debug session. */ +export class ModulesDataProvider implements vscode.TreeDataProvider { private changeTreeData = new vscode.EventEmitter(); From lldb-commits at lists.llvm.org Wed May 7 16:18:06 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Wed, 07 May 2025 16:18:06 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Adding a modules explorer to lldb-dap ext. (PR #138977) In-Reply-To: Message-ID: <681bea2e.050a0220.60ca4.3cee@mx.google.com> ================ @@ -763,6 +764,16 @@ } ] } - ] + ], + "views": { + "debug": [ + { + "id": "lldb-dap.modulesExplorer", + "name": "LLDB Modules Explorer", ---------------- ashgti wrote: Updated to just be 'Modules'. https://github.com/llvm/llvm-project/pull/138977 From lldb-commits at lists.llvm.org Wed May 7 16:18:11 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Wed, 07 May 2025 16:18:11 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Change the launch sequence (reland) (PR #138981) In-Reply-To: Message-ID: <681bea33.170a0220.296daa.6689@mx.google.com> https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/138981 >From 8c7a01465891c83f7cc99ee95b0852c56e0f150f Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Tue, 6 May 2025 15:58:44 -0700 Subject: [PATCH 1/2] [lldb-dap] Change the launch sequence (#138219) This PR changes how we treat the launch sequence in lldb-dap. - Send the initialized event after we finish handling the initialize request, rather than after we finish attaching or launching. - Delay handling the launch and attach request until we have handled the configurationDone request. The latter is now largely a NO-OP and only exists to signal lldb-dap that it can handle the launch and attach requests. - Delay handling the initial threads requests until we have handled the launch or attach request. - Make all attaching and launching synchronous, including when we have attach or launch commands. This removes the need to synchronize between the request and event thread. Background: https://discourse.llvm.org/t/reliability-of-the-lldb-dap-tests/86125 --- .../test/tools/lldb-dap/dap_server.py | 65 +++++----- .../test/tools/lldb-dap/lldbdap_testcase.py | 7 ++ .../tools/lldb-dap/attach/TestDAP_attach.py | 2 + .../attach/TestDAP_attachByPortNum.py | 8 +- .../TestDAP_breakpointEvents.py | 61 +++------- .../completions/TestDAP_completions.py | 6 +- .../tools/lldb-dap/console/TestDAP_console.py | 2 +- .../lldb-dap/disconnect/TestDAP_disconnect.py | 6 +- .../lldb-dap/evaluate/TestDAP_evaluate.py | 5 +- .../tools/lldb-dap/launch/TestDAP_launch.py | 4 +- .../lldb-dap/progress/TestDAP_Progress.py | 2 +- .../repl-mode/TestDAP_repl_mode_detection.py | 2 +- .../tools/lldb-dap/restart/TestDAP_restart.py | 1 - .../restart/TestDAP_restart_runInTerminal.py | 1 - .../lldb-dap/stop-hooks/TestDAP_stop_hooks.py | 2 +- lldb/tools/lldb-dap/DAP.cpp | 39 ++++-- lldb/tools/lldb-dap/DAP.h | 8 +- lldb/tools/lldb-dap/EventHelper.cpp | 2 +- .../lldb-dap/Handler/AttachRequestHandler.cpp | 115 ++++++++++-------- .../ConfigurationDoneRequestHandler.cpp | 14 +-- .../Handler/InitializeRequestHandler.cpp | 44 +++---- .../lldb-dap/Handler/LaunchRequestHandler.cpp | 7 +- .../tools/lldb-dap/Handler/RequestHandler.cpp | 67 ++++++---- lldb/tools/lldb-dap/Handler/RequestHandler.h | 1 + 24 files changed, 252 insertions(+), 219 deletions(-) diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py index 6d9ab770684f1..e10342b72f4f0 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py @@ -132,7 +132,6 @@ def __init__(self, recv, send, init_commands, log_file=None): self.exit_status = None self.initialize_body = None self.thread_stop_reasons = {} - self.breakpoint_events = [] self.progress_events = [] self.reverse_requests = [] self.module_events = [] @@ -244,13 +243,6 @@ def handle_recv_packet(self, packet): self._process_stopped() tid = body["threadId"] self.thread_stop_reasons[tid] = body - elif event == "breakpoint": - # Breakpoint events come in when a breakpoint has locations - # added or removed. Keep track of them so we can look for them - # in tests. - self.breakpoint_events.append(packet) - # no need to add 'breakpoint' event packets to our packets list - return keepGoing elif event.startswith("progress"): # Progress events come in as 'progressStart', 'progressUpdate', # and 'progressEnd' events. Keep these around in case test @@ -412,6 +404,15 @@ def wait_for_stopped(self, timeout=None): self.threads = [] return stopped_events + def wait_for_breakpoint_events(self, timeout=None): + breakpoint_events = [] + while True: + event = self.wait_for_event("breakpoint", timeout=timeout) + if not event: + break + breakpoint_events.append(event) + return breakpoint_events + def wait_for_exited(self): event_dict = self.wait_for_event("exited") if event_dict is None: @@ -591,6 +592,7 @@ def request_attach( attachCommands=None, terminateCommands=None, coreFile=None, + stopOnAttach=True, postRunCommands=None, sourceMap=None, gdbRemotePort=None, @@ -620,6 +622,8 @@ def request_attach( args_dict["attachCommands"] = attachCommands if coreFile: args_dict["coreFile"] = coreFile + if stopOnAttach: + args_dict["stopOnEntry"] = stopOnAttach if postRunCommands: args_dict["postRunCommands"] = postRunCommands if sourceMap: @@ -632,7 +636,7 @@ def request_attach( response = self.send_recv(command_dict) if response["success"]: - self.wait_for_events(["process", "initialized"]) + self.wait_for_event("process") return response def request_breakpointLocations( @@ -666,10 +670,6 @@ def request_configurationDone(self): response = self.send_recv(command_dict) if response: self.configuration_done_sent = True - # Client requests the baseline of currently existing threads after - # a successful launch or attach. - # Kick off the threads request that follows - self.request_threads() return response def _process_stopped(self): @@ -887,7 +887,7 @@ def request_launch( response = self.send_recv(command_dict) if response["success"]: - self.wait_for_events(["process", "initialized"]) + self.wait_for_event("process") return response def request_next(self, threadId, granularity="statement"): @@ -1325,6 +1325,26 @@ def attach_options_specified(options): def run_vscode(dbg, args, options): dbg.request_initialize(options.sourceInitFile) + + if options.sourceBreakpoints: + source_to_lines = {} + for file_line in options.sourceBreakpoints: + (path, line) = file_line.split(":") + if len(path) == 0 or len(line) == 0: + print('error: invalid source with line "%s"' % (file_line)) + + else: + if path in source_to_lines: + source_to_lines[path].append(int(line)) + else: + source_to_lines[path] = [int(line)] + for source in source_to_lines: + dbg.request_setBreakpoints(source, source_to_lines[source]) + if options.funcBreakpoints: + dbg.request_setFunctionBreakpoints(options.funcBreakpoints) + + dbg.request_configurationDone() + if attach_options_specified(options): response = dbg.request_attach( program=options.program, @@ -1353,23 +1373,6 @@ def run_vscode(dbg, args, options): ) if response["success"]: - if options.sourceBreakpoints: - source_to_lines = {} - for file_line in options.sourceBreakpoints: - (path, line) = file_line.split(":") - if len(path) == 0 or len(line) == 0: - print('error: invalid source with line "%s"' % (file_line)) - - else: - if path in source_to_lines: - source_to_lines[path].append(int(line)) - else: - source_to_lines[path] = [int(line)] - for source in source_to_lines: - dbg.request_setBreakpoints(source, source_to_lines[source]) - if options.funcBreakpoints: - dbg.request_setFunctionBreakpoints(options.funcBreakpoints) - dbg.request_configurationDone() dbg.wait_for_stopped() else: if "message" in response: diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py index 2c14bb35162b5..958c7268c0c72 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py @@ -340,6 +340,7 @@ def attach( exitCommands=None, attachCommands=None, coreFile=None, + stopOnAttach=True, disconnectAutomatically=True, terminateCommands=None, postRunCommands=None, @@ -364,6 +365,8 @@ def cleanup(): self.addTearDownHook(cleanup) # Initialize and launch the program self.dap_server.request_initialize(sourceInitFile) + self.dap_server.wait_for_event("initialized") + self.dap_server.request_configurationDone() response = self.dap_server.request_attach( program=program, pid=pid, @@ -376,6 +379,7 @@ def cleanup(): attachCommands=attachCommands, terminateCommands=terminateCommands, coreFile=coreFile, + stopOnAttach=stopOnAttach, postRunCommands=postRunCommands, sourceMap=sourceMap, gdbRemotePort=gdbRemotePort, @@ -434,6 +438,9 @@ def cleanup(): # Initialize and launch the program self.dap_server.request_initialize(sourceInitFile) + self.dap_server.wait_for_event("initialized") + self.dap_server.request_configurationDone() + response = self.dap_server.request_launch( program, args=args, diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py index f48d5a7db3c50..741c011a3d692 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py @@ -27,6 +27,8 @@ def spawn_and_wait(program, delay): @skip class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase): def set_and_hit_breakpoint(self, continueToExit=True): + self.dap_server.wait_for_stopped() + source = "main.c" breakpoint1_line = line_number(source, "// breakpoint 1") lines = [breakpoint1_line] diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py index 7f93b9f2a3a22..7250e67ebcd8c 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py @@ -18,17 +18,17 @@ import socket - at skip class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase): default_timeout = 20 def set_and_hit_breakpoint(self, continueToExit=True): + self.dap_server.wait_for_stopped() + source = "main.c" - main_source_path = os.path.join(os.getcwd(), source) - breakpoint1_line = line_number(main_source_path, "// breakpoint 1") + breakpoint1_line = line_number(source, "// breakpoint 1") lines = [breakpoint1_line] # Set breakpoint in the thread function so we can step the threads - breakpoint_ids = self.set_source_breakpoints(main_source_path, lines) + breakpoint_ids = self.set_source_breakpoints(source, lines) self.assertEqual( len(breakpoint_ids), len(lines), "expect correct number of breakpoints" ) diff --git a/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py b/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py index e5590e1b332a0..8581f10cef22a 100644 --- a/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py +++ b/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py @@ -81,52 +81,27 @@ def test_breakpoint_events(self): breakpoint["verified"], "expect foo breakpoint to not be verified" ) - # Get the stop at the entry point - self.continue_to_next_stop() + # Make sure we're stopped. + self.dap_server.wait_for_stopped() - # We are now stopped at the entry point to the program. Shared - # libraries are not loaded yet (at least on macOS they aren't) and only - # the breakpoint in the main executable should be resolved. - self.assertEqual(len(self.dap_server.breakpoint_events), 1) - event = self.dap_server.breakpoint_events[0] - body = event["body"] - self.assertEqual( - body["reason"], "changed", "breakpoint event should say changed" - ) - breakpoint = body["breakpoint"] - self.assertEqual(breakpoint["id"], main_bp_id) - self.assertTrue(breakpoint["verified"], "main breakpoint should be resolved") - - # Clear the list of breakpoint events so we don't see this one again. - self.dap_server.breakpoint_events.clear() + # Flush the breakpoint events. + self.dap_server.wait_for_breakpoint_events(timeout=5) # Continue to the breakpoint self.continue_to_breakpoints(dap_breakpoint_ids) - # When the process launches, we first expect to see both the main and - # foo breakpoint as unresolved. - for event in self.dap_server.breakpoint_events[:2]: - body = event["body"] - self.assertEqual( - body["reason"], "changed", "breakpoint event should say changed" - ) - breakpoint = body["breakpoint"] - self.assertIn(str(breakpoint["id"]), dap_breakpoint_ids) - self.assertFalse(breakpoint["verified"], "breakpoint should be unresolved") + verified_breakpoint_ids = [] + unverified_breakpoint_ids = [] + for breakpoint_event in self.dap_server.wait_for_breakpoint_events(timeout=5): + breakpoint = breakpoint_event["body"]["breakpoint"] + id = breakpoint["id"] + if breakpoint["verified"]: + verified_breakpoint_ids.append(id) + else: + unverified_breakpoint_ids.append(id) - # Then, once the dynamic loader has given us a load address, they - # should show up as resolved again. - for event in self.dap_server.breakpoint_events[3:]: - body = event["body"] - self.assertEqual( - body["reason"], "changed", "breakpoint event should say changed" - ) - breakpoint = body["breakpoint"] - self.assertIn(str(breakpoint["id"]), dap_breakpoint_ids) - self.assertTrue(breakpoint["verified"], "breakpoint should be resolved") - self.assertNotIn( - "source", - breakpoint, - "breakpoint event should not return a source object", - ) - self.assertIn("line", breakpoint, "breakpoint event should have line") + self.assertIn(main_bp_id, unverified_breakpoint_ids) + self.assertIn(foo_bp_id, unverified_breakpoint_ids) + + self.assertIn(main_bp_id, verified_breakpoint_ids) + self.assertIn(foo_bp_id, verified_breakpoint_ids) diff --git a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py index 210e591bff426..455ac84168baf 100644 --- a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py +++ b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py @@ -44,9 +44,9 @@ def verify_completions(self, actual_list, expected_list, not_expected_list=[]): self.assertNotIn(not_expected_item, actual_list) - def setup_debugee(self): + def setup_debugee(self, stopOnEntry=False): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=stopOnEntry) source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") @@ -235,7 +235,7 @@ def test_auto_completions(self): """ Tests completion requests in "repl-mode=auto" """ - self.setup_debugee() + self.setup_debugee(stopOnEntry=True) res = self.dap_server.request_evaluate( "`lldb-dap repl-mode auto", context="repl" diff --git a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py index b07c4f871d73b..65a1bc04c7cd7 100644 --- a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py +++ b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py @@ -167,7 +167,7 @@ def test_exit_status_message_ok(self): def test_diagnositcs(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) core = self.getBuildArtifact("minidump.core") self.yaml2obj("minidump.yaml", core) diff --git a/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py b/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py index 0cb792d662a80..09e3f62f0eead 100644 --- a/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py +++ b/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py @@ -31,7 +31,7 @@ def test_launch(self): created. """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program, disconnectAutomatically=False) + self.build_and_launch(program, stopOnEntry=True, disconnectAutomatically=False) # We set a breakpoint right before the side effect file is created self.set_source_breakpoints( @@ -39,7 +39,11 @@ def test_launch(self): ) self.continue_to_next_stop() + # verify we haven't produced the side effect file yet + self.assertFalse(os.path.exists(program + ".side_effect")) + self.dap_server.request_disconnect() + # verify we didn't produce the side effect file time.sleep(1) self.assertFalse(os.path.exists(program + ".side_effect")) diff --git a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py index d97fda730c46a..19b682dfcd22d 100644 --- a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py +++ b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py @@ -10,6 +10,7 @@ from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * + # DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. @skip class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase): @@ -42,7 +43,9 @@ def run_test_evaluate_expressions( self.context = context program = self.getBuildArtifact("a.out") self.build_and_launch( - program, enableAutoVariableSummaries=enableAutoVariableSummaries + program, + enableAutoVariableSummaries=enableAutoVariableSummaries, + stopOnEntry=True, ) source = "main.cpp" self.set_source_breakpoints( diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py index 931456299e03e..604a41678500c 100644 --- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py +++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py @@ -88,8 +88,8 @@ def test_stopOnEntry(self): """ program = self.getBuildArtifact("a.out") self.build_and_launch(program, stopOnEntry=True) - self.set_function_breakpoints(["main"]) - stopped_events = self.continue_to_next_stop() + + stopped_events = self.dap_server.wait_for_stopped() for stopped_event in stopped_events: if "body" in stopped_event: body = stopped_event["body"] diff --git a/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py b/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py index fee63655de0da..0f94b50c31fba 100755 --- a/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py +++ b/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py @@ -50,7 +50,7 @@ def verify_progress_events( @skipIfWindows def test(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) progress_emitter = os.path.join(os.getcwd(), "Progress_emitter.py") self.dap_server.request_evaluate( f"`command script import {progress_emitter}", context="repl" diff --git a/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py b/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py index c6f59949d668e..81edcdf4bd0f9 100644 --- a/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py +++ b/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py @@ -20,7 +20,7 @@ def assertEvaluate(self, expression, regex): def test_completions(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") diff --git a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py index 36fa0bd40183f..5f95c7bfb1556 100644 --- a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py +++ b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py @@ -22,7 +22,6 @@ def test_basic_functionality(self): [bp_A, bp_B] = self.set_source_breakpoints("main.c", [line_A, line_B]) # Verify we hit A, then B. - self.dap_server.request_configurationDone() self.verify_breakpoint_hit([bp_A]) self.dap_server.request_continue() self.verify_breakpoint_hit([bp_B]) diff --git a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py index a94c9860c1508..eed769a5a0cc6 100644 --- a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py +++ b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py @@ -74,7 +74,6 @@ def test_stopOnEntry(self): program = self.getBuildArtifact("a.out") self.build_and_launch(program, runInTerminal=True, stopOnEntry=True) [bp_main] = self.set_function_breakpoints(["main"]) - self.dap_server.request_configurationDone() # When using stopOnEntry, configurationDone doesn't result in a running # process, we should immediately get a stopped event instead. diff --git a/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py b/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py index 70c11a63a79f7..7e28a5af4331c 100644 --- a/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py +++ b/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py @@ -19,7 +19,7 @@ def test_stop_hooks_before_run(self): self.build_and_launch(program, stopOnEntry=True, preRunCommands=preRunCommands) # The first stop is on entry. - self.continue_to_next_stop() + self.dap_server.wait_for_stopped() breakpoint_ids = self.set_function_breakpoints(["main"]) # This request hangs if the race happens, because, in that case, the diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 4b631484c9fab..62c60cc3a9b3b 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -84,8 +84,8 @@ DAP::DAP(Log *log, const ReplMode default_repl_mode, : log(log), transport(transport), broadcaster("lldb-dap"), exception_breakpoints(), focus_tid(LLDB_INVALID_THREAD_ID), stop_at_entry(false), is_attach(false), - restarting_process_id(LLDB_INVALID_PROCESS_ID), - configuration_done_sent(false), waiting_for_run_in_terminal(false), + restarting_process_id(LLDB_INVALID_PROCESS_ID), configuration_done(false), + waiting_for_run_in_terminal(false), progress_event_reporter( [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }), reverse_request_seq(0), repl_mode(default_repl_mode) { @@ -893,10 +893,19 @@ llvm::Error DAP::Loop() { return errWrapper; } + // The launch sequence is special and we need to carefully handle + // packets in the right order. Until we've handled configurationDone, + bool add_to_pending_queue = false; + if (const protocol::Request *req = - std::get_if(&*next); - req && req->command == "disconnect") { - disconnecting = true; + std::get_if(&*next)) { + llvm::StringRef command = req->command; + if (command == "disconnect") + disconnecting = true; + if (!configuration_done) + add_to_pending_queue = + command != "initialize" && command != "configurationDone" && + command != "disconnect" && !command.ends_with("Breakpoints"); } const std::optional cancel_args = @@ -924,7 +933,8 @@ llvm::Error DAP::Loop() { { std::lock_guard guard(m_queue_mutex); - m_queue.push_back(std::move(*next)); + auto &queue = add_to_pending_queue ? m_pending_queue : m_queue; + queue.push_back(std::move(*next)); } m_queue_cv.notify_one(); } @@ -938,16 +948,19 @@ llvm::Error DAP::Loop() { StopEventHandlers(); }); - while (!disconnecting || !m_queue.empty()) { + while (true) { std::unique_lock lock(m_queue_mutex); m_queue_cv.wait(lock, [&] { return disconnecting || !m_queue.empty(); }); - if (m_queue.empty()) + if (disconnecting && m_queue.empty()) break; Message next = m_queue.front(); m_queue.pop_front(); + // Unlock while we're processing the event. + lock.unlock(); + if (!HandleObject(next)) return llvm::createStringError(llvm::inconvertibleErrorCode(), "unhandled packet"); @@ -1219,6 +1232,16 @@ void DAP::SetConfiguration(const protocol::Configuration &config, SetThreadFormat(*configuration.customThreadFormat); } +void DAP::SetConfigurationDone() { + { + std::lock_guard guard(m_queue_mutex); + std::copy(m_pending_queue.begin(), m_pending_queue.end(), + std::front_inserter(m_queue)); + configuration_done = true; + } + m_queue_cv.notify_all(); +} + void DAP::SetFrameFormat(llvm::StringRef format) { if (format.empty()) return; diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index 88eedb0860cf1..b581ae759b1bc 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -188,7 +188,7 @@ struct DAP { // shutting down the entire adapter. When we're restarting, we keep the id of // the old process here so we can detect this case and keep running. lldb::pid_t restarting_process_id; - bool configuration_done_sent; + bool configuration_done; llvm::StringMap> request_handlers; bool waiting_for_run_in_terminal; ProgressEventReporter progress_event_reporter; @@ -251,6 +251,8 @@ struct DAP { /// Configures the debug adapter for launching/attaching. void SetConfiguration(const protocol::Configuration &confing, bool is_attach); + void SetConfigurationDone(); + /// Configure source maps based on the current `DAPConfiguration`. void ConfigureSourceMaps(); @@ -417,8 +419,10 @@ struct DAP { lldb::SBMutex GetAPIMutex() const { return target.GetAPIMutex(); } private: - std::mutex m_queue_mutex; + /// Queue for all incoming messages. std::deque m_queue; + std::deque m_pending_queue; + std::mutex m_queue_mutex; std::condition_variable m_queue_cv; std::mutex m_cancelled_requests_mutex; diff --git a/lldb/tools/lldb-dap/EventHelper.cpp b/lldb/tools/lldb-dap/EventHelper.cpp index 2c659f39f4b66..ed2d8700c26b0 100644 --- a/lldb/tools/lldb-dap/EventHelper.cpp +++ b/lldb/tools/lldb-dap/EventHelper.cpp @@ -222,7 +222,7 @@ void SendContinuedEvent(DAP &dap) { // If the focus thread is not set then we haven't reported any thread status // to the client, so nothing to report. - if (!dap.configuration_done_sent || dap.focus_tid == LLDB_INVALID_THREAD_ID) { + if (!dap.configuration_done || dap.focus_tid == LLDB_INVALID_THREAD_ID) { return; } diff --git a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp index 7a0f091128e4a..5dc9c3f9772e3 100644 --- a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp @@ -133,61 +133,70 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { dap.SendOutput(OutputType::Console, llvm::StringRef(attach_msg, attach_msg_len)); } - if (attachCommands.empty()) { - // No "attachCommands", just attach normally. - // Disable async events so the attach will be successful when we return from - // the launch call and the launch will happen synchronously + { + // Perform the launch in synchronous mode so that we don't have to worry + // about process state changes during the launch. ScopeSyncMode scope_sync_mode(dap.debugger); - - if (core_file.empty()) { - if ((pid != LLDB_INVALID_PROCESS_ID) && - (gdb_remote_port != invalid_port)) { - // If both pid and port numbers are specified. - error.SetErrorString("The user can't specify both pid and port"); - } else if (gdb_remote_port != invalid_port) { - // If port is specified and pid is not. - lldb::SBListener listener = dap.debugger.GetListener(); - - // If the user hasn't provided the hostname property, default localhost - // being used. - std::string connect_url = - llvm::formatv("connect://{0}:", gdb_remote_hostname); - connect_url += std::to_string(gdb_remote_port); - dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", - error); + if (attachCommands.empty()) { + // No "attachCommands", just attach normally. + if (core_file.empty()) { + if ((pid != LLDB_INVALID_PROCESS_ID) && + (gdb_remote_port != invalid_port)) { + // If both pid and port numbers are specified. + error.SetErrorString("The user can't specify both pid and port"); + } else if (gdb_remote_port != invalid_port) { + // If port is specified and pid is not. + lldb::SBListener listener = dap.debugger.GetListener(); + + // If the user hasn't provided the hostname property, default + // localhost being used. + std::string connect_url = + llvm::formatv("connect://{0}:", gdb_remote_hostname); + connect_url += std::to_string(gdb_remote_port); + dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", + error); + } else { + // Attach by pid or process name. + lldb::SBAttachInfo attach_info; + if (pid != LLDB_INVALID_PROCESS_ID) + attach_info.SetProcessID(pid); + else if (dap.configuration.program.has_value()) + attach_info.SetExecutable(dap.configuration.program->data()); + attach_info.SetWaitForLaunch(wait_for, false /*async*/); + dap.target.Attach(attach_info, error); + } } else { - // Attach by pid or process name. - lldb::SBAttachInfo attach_info; - if (pid != LLDB_INVALID_PROCESS_ID) - attach_info.SetProcessID(pid); - else if (dap.configuration.program.has_value()) - attach_info.SetExecutable(dap.configuration.program->data()); - attach_info.SetWaitForLaunch(wait_for, false /*async*/); - dap.target.Attach(attach_info, error); + dap.target.LoadCore(core_file.data(), error); } } else { - dap.target.LoadCore(core_file.data(), error); - } - } else { - // We have "attachCommands" that are a set of commands that are expected - // to execute the commands after which a process should be created. If there - // is no valid process after running these commands, we have failed. - if (llvm::Error err = dap.RunAttachCommands(attachCommands)) { - response["success"] = false; - EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; + // We have "attachCommands" that are a set of commands that are expected + // to execute the commands after which a process should be created. If + // there is no valid process after running these commands, we have failed. + if (llvm::Error err = dap.RunAttachCommands(attachCommands)) { + response["success"] = false; + EmplaceSafeString(response, "message", llvm::toString(std::move(err))); + dap.SendJSON(llvm::json::Value(std::move(response))); + return; + } + // The custom commands might have created a new target so we should use + // the selected target after these commands are run. + dap.target = dap.debugger.GetSelectedTarget(); } - // The custom commands might have created a new target so we should use the - // selected target after these commands are run. - dap.target = dap.debugger.GetSelectedTarget(); - - // Make sure the process is attached and stopped before proceeding as the - // the launch commands are not run using the synchronous mode. - error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds)); } + // Make sure the process is attached and stopped. + error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds)); + + // Clients can request a baseline of currently existing threads after + // we acknowledge the configurationDone request. + // Client requests the baseline of currently existing threads after + // a successful or attach by sending a 'threads' request + // right after receiving the configurationDone response. + // Obtain the list of threads before we resume the process + dap.initial_thread_list = + GetThreads(dap.target.GetProcess(), dap.thread_format); + if (error.Success() && core_file.empty()) { auto attached_pid = dap.target.GetProcess().GetProcessID(); if (attached_pid == LLDB_INVALID_PROCESS_ID) { @@ -206,9 +215,17 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { } dap.SendJSON(llvm::json::Value(std::move(response))); + + // FIXME: Move this into PostRun. if (error.Success()) { - SendProcessEvent(dap, Attach); - dap.SendJSON(CreateEventObject("initialized")); + if (dap.target.GetProcess().IsValid()) { + SendProcessEvent(dap, Attach); + + if (dap.stop_at_entry) + SendThreadStoppedEvent(dap); + else + dap.target.GetProcess().Continue(); + } } } diff --git a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp index f39bbdefdbb95..802c28d7b8904 100644 --- a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp @@ -47,21 +47,11 @@ namespace lldb_dap { void ConfigurationDoneRequestHandler::operator()( const llvm::json::Object &request) const { + dap.SetConfigurationDone(); + llvm::json::Object response; FillResponse(request, response); dap.SendJSON(llvm::json::Value(std::move(response))); - dap.configuration_done_sent = true; - if (dap.stop_at_entry) - SendThreadStoppedEvent(dap); - else { - // Client requests the baseline of currently existing threads after - // a successful launch or attach by sending a 'threads' request - // right after receiving the configurationDone response. - // Obtain the list of threads before we resume the process - dap.initial_thread_list = - GetThreads(dap.target.GetProcess(), dap.thread_format); - dap.target.GetProcess().Continue(); - } } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp index ce34c52bcc334..aa947d3cb5ab9 100644 --- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp @@ -140,43 +140,28 @@ static void EventThreadFunction(DAP &dap) { lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event); if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) { auto state = lldb::SBProcess::GetStateFromEvent(event); + + DAP_LOG(dap.log, "State = {0}", state); switch (state) { + case lldb::eStateConnected: + case lldb::eStateDetached: case lldb::eStateInvalid: - // Not a state event - break; case lldb::eStateUnloaded: break; - case lldb::eStateConnected: - break; case lldb::eStateAttaching: - break; - case lldb::eStateLaunching: - break; - case lldb::eStateStepping: - break; case lldb::eStateCrashed: - break; - case lldb::eStateDetached: - break; - case lldb::eStateSuspended: - break; + case lldb::eStateLaunching: case lldb::eStateStopped: - // We launch and attach in synchronous mode then the first stop - // event will not be delivered. If we use "launchCommands" during a - // launch or "attachCommands" during an attach we might some process - // stop events which we do not want to send an event for. We will - // manually send a stopped event in request_configurationDone(...) - // so don't send any before then. - if (dap.configuration_done_sent) { - // Only report a stopped event if the process was not - // automatically restarted. - if (!lldb::SBProcess::GetRestartedFromEvent(event)) { - SendStdOutStdErr(dap, process); - SendThreadStoppedEvent(dap); - } + case lldb::eStateSuspended: + // Only report a stopped event if the process was not + // automatically restarted. + if (!lldb::SBProcess::GetRestartedFromEvent(event)) { + SendStdOutStdErr(dap, process); + SendThreadStoppedEvent(dap); } break; case lldb::eStateRunning: + case lldb::eStateStepping: dap.WillContinue(); SendContinuedEvent(dap); break; @@ -284,6 +269,7 @@ llvm::Expected InitializeRequestHandler::Run( // Do not source init files until in/out/err are configured. dap.debugger = lldb::SBDebugger::Create(false); dap.debugger.SetInputFile(dap.in); + dap.target = dap.debugger.GetDummyTarget(); llvm::Expected out_fd = dap.out.GetWriteFileDescriptor(); if (!out_fd) @@ -338,4 +324,8 @@ llvm::Expected InitializeRequestHandler::Run( return dap.GetCapabilities(); } +void InitializeRequestHandler::PostRun() const { + dap.SendJSON(CreateEventObject("initialized")); +} + } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp index 3e4532e754ec6..7e0e76935dd02 100644 --- a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp @@ -71,9 +71,12 @@ void LaunchRequestHandler::PostRun() const { if (dap.target.GetProcess().IsValid()) { // Attach happens when launching with runInTerminal. SendProcessEvent(dap, dap.is_attach ? Attach : Launch); - } - dap.SendJSON(CreateEventObject("initialized")); + if (dap.stop_at_entry) + SendThreadStoppedEvent(dap); + else + dap.target.GetProcess().Continue(); + } } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp index 7a75cd93abc19..282c5f4ab15a5 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp @@ -8,6 +8,7 @@ #include "Handler/RequestHandler.h" #include "DAP.h" +#include "EventHelper.h" #include "Handler/ResponseHandler.h" #include "JSONUtils.h" #include "LLDBUtils.h" @@ -162,7 +163,7 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) { dap.target.GetProcess().Continue(); // Now that the actual target is just starting (i.e. exec was just invoked), - // we return the debugger to its async state. + // we return the debugger to its sync state. scope_sync_mode.reset(); // If sending the notification failed, the launcher should be dead by now and @@ -238,35 +239,47 @@ llvm::Error BaseRequestHandler::LaunchProcess( launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug | lldb::eLaunchFlagStopAtEntry); - if (arguments.runInTerminal) { - if (llvm::Error err = RunInTerminal(dap, arguments)) - return err; - } else if (launchCommands.empty()) { - lldb::SBError error; - // Disable async events so the launch will be successful when we return from - // the launch call and the launch will happen synchronously + { + // Perform the launch in synchronous mode so that we don't have to worry + // about process state changes during the launch. ScopeSyncMode scope_sync_mode(dap.debugger); - dap.target.Launch(launch_info, error); - if (error.Fail()) - return llvm::make_error(error.GetCString()); - } else { - // Set the launch info so that run commands can access the configured - // launch details. - dap.target.SetLaunchInfo(launch_info); - if (llvm::Error err = dap.RunLaunchCommands(launchCommands)) - return err; - - // The custom commands might have created a new target so we should use the - // selected target after these commands are run. - dap.target = dap.debugger.GetSelectedTarget(); - // Make sure the process is launched and stopped at the entry point before - // proceeding as the launch commands are not run using the synchronous - // mode. - lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout); - if (error.Fail()) - return llvm::make_error(error.GetCString()); + + if (arguments.runInTerminal) { + if (llvm::Error err = RunInTerminal(dap, arguments)) + return err; + } else if (launchCommands.empty()) { + lldb::SBError error; + dap.target.Launch(launch_info, error); + if (error.Fail()) + return llvm::make_error(error.GetCString()); + } else { + // Set the launch info so that run commands can access the configured + // launch details. + dap.target.SetLaunchInfo(launch_info); + if (llvm::Error err = dap.RunLaunchCommands(launchCommands)) + return err; + + // The custom commands might have created a new target so we should use + // the selected target after these commands are run. + dap.target = dap.debugger.GetSelectedTarget(); + } } + // Make sure the process is launched and stopped at the entry point before + // proceeding. + lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout); + if (error.Fail()) + return llvm::make_error(error.GetCString()); + + // Clients can request a baseline of currently existing threads after + // we acknowledge the configurationDone request. + // Client requests the baseline of currently existing threads after + // a successful or attach by sending a 'threads' request + // right after receiving the configurationDone response. + // Obtain the list of threads before we resume the process + dap.initial_thread_list = + GetThreads(dap.target.GetProcess(), dap.thread_format); + return llvm::Error::success(); } diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index 37cc902e1c98e..9e9cfb13d77b8 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.h +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h @@ -282,6 +282,7 @@ class InitializeRequestHandler static llvm::StringLiteral GetCommand() { return "initialize"; } llvm::Expected Run(const protocol::InitializeRequestArguments &args) const override; + void PostRun() const override; }; class LaunchRequestHandler >From 1ac9193a54cd4c98c875bc079154453af1a47850 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Wed, 7 May 2025 15:26:01 -0700 Subject: [PATCH 2/2] Update more tests --- .../test/tools/lldb-dap/lldbdap_testcase.py | 48 +++++++++++++++++++ .../completions/TestDAP_completions.py | 22 +++++---- .../tools/lldb-dap/console/TestDAP_console.py | 9 ++-- .../console/TestDAP_redirection_to_console.py | 4 +- .../lldb-dap/exception/TestDAP_exception.py | 3 +- .../tools/lldb-dap/launch/TestDAP_launch.py | 3 ++ .../lldb-dap/send-event/TestDAP_sendEvent.py | 7 +-- .../lldb-dap/stackTrace/TestDAP_stackTrace.py | 2 +- .../TestDAP_stackTraceDisassemblyDisplay.py | 2 +- .../startDebugging/TestDAP_startDebugging.py | 3 +- .../children/TestDAP_variables_children.py | 4 +- 11 files changed, 81 insertions(+), 26 deletions(-) diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py index 958c7268c0c72..c5a7eb76a58c7 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py @@ -349,6 +349,8 @@ def attach( expectFailure=False, gdbRemotePort=None, gdbRemoteHostname=None, + sourceBreakpoints=None, + functionBreakpoints=None, ): """Build the default Makefile target, create the DAP debug adapter, and attach to the process. @@ -366,6 +368,26 @@ def cleanup(): # Initialize and launch the program self.dap_server.request_initialize(sourceInitFile) self.dap_server.wait_for_event("initialized") + + # Set source breakpoints as part of the launch sequence. + if sourceBreakpoints: + for source_path, lines in sourceBreakpoints: + response = self.dap_server.request_setBreakpoints(source_path, lines) + self.assertTrue( + response["success"], + "setBreakpoints failed (%s)" % (response), + ) + + # Set function breakpoints as part of the launch sequence. + if functionBreakpoints: + response = self.dap_server.request_setFunctionBreakpoints( + functionBreakpoints + ) + self.assertTrue( + response["success"], + "setFunctionBreakpoint failed (%s)" % (response), + ) + self.dap_server.request_configurationDone() response = self.dap_server.request_attach( program=program, @@ -423,6 +445,8 @@ def launch( commandEscapePrefix=None, customFrameFormat=None, customThreadFormat=None, + sourceBreakpoints=None, + functionBreakpoints=None, ): """Sending launch request to dap""" @@ -439,6 +463,26 @@ def cleanup(): # Initialize and launch the program self.dap_server.request_initialize(sourceInitFile) self.dap_server.wait_for_event("initialized") + + # Set source breakpoints as part of the launch sequence. + if sourceBreakpoints: + for source_path, lines in sourceBreakpoints: + response = self.dap_server.request_setBreakpoints(source_path, lines) + self.assertTrue( + response["success"], + "setBreakpoints failed (%s)" % (response), + ) + + # Set function breakpoints as part of the launch sequence. + if functionBreakpoints: + response = self.dap_server.request_setFunctionBreakpoints( + functionBreakpoints + ) + self.assertTrue( + response["success"], + "setFunctionBreakpoint failed (%s)" % (response), + ) + self.dap_server.request_configurationDone() response = self.dap_server.request_launch( @@ -511,6 +555,8 @@ def build_and_launch( customThreadFormat=None, launchCommands=None, expectFailure=False, + sourceBreakpoints=None, + functionBreakpoints=None, ): """Build the default Makefile target, create the DAP debug adapter, and launch the process. @@ -547,6 +593,8 @@ def build_and_launch( customThreadFormat=customThreadFormat, launchCommands=launchCommands, expectFailure=expectFailure, + sourceBreakpoints=sourceBreakpoints, + functionBreakpoints=functionBreakpoints, ) def getBuiltinDebugServerTool(self): diff --git a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py index 455ac84168baf..a94288c7a669e 100644 --- a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py +++ b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py @@ -2,7 +2,6 @@ Test lldb-dap completions request """ - import lldbdap_testcase import dap_server from lldbsuite.test import lldbutil @@ -32,6 +31,7 @@ variable_var1_completion = {"text": "var1", "label": "var1 -- int &"} variable_var2_completion = {"text": "var2", "label": "var2 -- int &"} + # Older version of libcxx produce slightly different typename strings for # templates like vector. @skipIf(compiler="clang", compiler_version=["<", "16.0"]) @@ -43,16 +43,22 @@ def verify_completions(self, actual_list, expected_list, not_expected_list=[]): for not_expected_item in not_expected_list: self.assertNotIn(not_expected_item, actual_list) - def setup_debugee(self, stopOnEntry=False): program = self.getBuildArtifact("a.out") - self.build_and_launch(program, stopOnEntry=stopOnEntry) - source = "main.cpp" - breakpoint1_line = line_number(source, "// breakpoint 1") - breakpoint2_line = line_number(source, "// breakpoint 2") - - self.set_source_breakpoints(source, [breakpoint1_line, breakpoint2_line]) + self.build_and_launch( + program, + stopOnEntry=stopOnEntry, + sourceBreakpoints=[ + ( + source, + [ + line_number(source, "// breakpoint 1"), + line_number(source, "// breakpoint 2"), + ], + ), + ], + ) def test_command_completions(self): """ diff --git a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py index 65a1bc04c7cd7..8642e317f9b3a 100644 --- a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py +++ b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py @@ -19,6 +19,7 @@ def get_subprocess(root_process, process_name): self.assertTrue(False, "No subprocess with name %s found" % process_name) + class TestDAP_console(lldbdap_testcase.DAPTestCaseBase): def check_lldb_command( self, lldb_command, contains_string, assert_msg, command_escape_prefix="`" @@ -52,7 +53,7 @@ def test_scopes_variables_setVariable_evaluate(self): character. """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") lines = [breakpoint1_line] @@ -81,7 +82,7 @@ def test_scopes_variables_setVariable_evaluate(self): def test_custom_escape_prefix(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program, commandEscapePrefix="::") + self.build_and_launch(program, stopOnEntry=True, commandEscapePrefix="::") source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line]) @@ -96,7 +97,7 @@ def test_custom_escape_prefix(self): def test_empty_escape_prefix(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program, commandEscapePrefix="") + self.build_and_launch(program, stopOnEntry=True, commandEscapePrefix="") source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line]) @@ -113,7 +114,7 @@ def test_empty_escape_prefix(self): def test_exit_status_message_sigterm(self): source = "main.cpp" program = self.getBuildArtifact("a.out") - self.build_and_launch(program, commandEscapePrefix="") + self.build_and_launch(program, stopOnEntry=True, commandEscapePrefix="") breakpoint1_line = line_number(source, "// breakpoint 1") breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line]) self.continue_to_breakpoints(breakpoint_ids) diff --git a/lldb/test/API/tools/lldb-dap/console/TestDAP_redirection_to_console.py b/lldb/test/API/tools/lldb-dap/console/TestDAP_redirection_to_console.py index e367c327d4295..23500bd6fe586 100644 --- a/lldb/test/API/tools/lldb-dap/console/TestDAP_redirection_to_console.py +++ b/lldb/test/API/tools/lldb-dap/console/TestDAP_redirection_to_console.py @@ -16,7 +16,9 @@ def test(self): """ program = self.getBuildArtifact("a.out") self.build_and_launch( - program, lldbDAPEnv={"LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION": ""} + program, + stopOnEntry=True, + lldbDAPEnv={"LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION": ""}, ) source = "main.cpp" diff --git a/lldb/test/API/tools/lldb-dap/exception/TestDAP_exception.py b/lldb/test/API/tools/lldb-dap/exception/TestDAP_exception.py index 39d73737b7e8c..ec7387dabb0c2 100644 --- a/lldb/test/API/tools/lldb-dap/exception/TestDAP_exception.py +++ b/lldb/test/API/tools/lldb-dap/exception/TestDAP_exception.py @@ -2,7 +2,6 @@ Test exception behavior in DAP with signal. """ - from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * import lldbdap_testcase @@ -17,7 +16,7 @@ def test_stopped_description(self): """ program = self.getBuildArtifact("a.out") self.build_and_launch(program) - self.dap_server.request_continue() + self.assertTrue(self.verify_stop_exception_info("signal SIGABRT")) exceptionInfo = self.get_exceptionInfo() self.assertEqual(exceptionInfo["breakMode"], "always") diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py index 604a41678500c..e8e9181f8da8d 100644 --- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py +++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py @@ -15,6 +15,7 @@ # Despite the test program printing correctly. See # https://github.com/llvm/llvm-project/issues/137599. + class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase): @skipIfWindows def test_default(self): @@ -357,6 +358,7 @@ def test_commands(self): terminateCommands = ["expr 4+2"] self.build_and_launch( program, + stopOnEntry=True, initCommands=initCommands, preRunCommands=preRunCommands, postRunCommands=postRunCommands, @@ -530,6 +532,7 @@ def test_terminate_commands(self): terminateCommands = ["expr 4+2"] self.launch( program=program, + stopOnEntry=True, terminateCommands=terminateCommands, disconnectAutomatically=False, ) diff --git a/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py b/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py index ce262be161861..64cec70aa923b 100644 --- a/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py +++ b/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py @@ -16,12 +16,14 @@ def test_send_event(self): """ program = self.getBuildArtifact("a.out") source = "main.c" + breakpoint_line = line_number(source, "// breakpoint") custom_event_body = { "key": 321, "arr": [True], } self.build_and_launch( program, + sourceBreakpoints=[(source, [breakpoint_line])], stopCommands=[ "lldb-dap send-event my-custom-event-no-body", "lldb-dap send-event my-custom-event '{}'".format( @@ -30,11 +32,6 @@ def test_send_event(self): ], ) - breakpoint_line = line_number(source, "// breakpoint") - - self.set_source_breakpoints(source, [breakpoint_line]) - self.continue_to_next_stop() - custom_event = self.dap_server.wait_for_event( filter=["my-custom-event-no-body"] ) diff --git a/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py b/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py index 4e2a76cf76980..edf4adae14a3b 100644 --- a/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py +++ b/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py @@ -61,7 +61,7 @@ def test_stackTrace(self): Tests the 'stackTrace' packet and all its variants. """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) source = "main.c" self.source_path = os.path.join(os.getcwd(), source) self.recurse_end = line_number(source, "recurse end") diff --git a/lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py b/lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py index 08c225b3cada4..963d711978534 100644 --- a/lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py +++ b/lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py @@ -37,7 +37,7 @@ def build_and_run_until_breakpoint(self): breakpoint_line = line_number(other_source_file, "// Break here") program = self.getBuildArtifact("a.out") - self.build_and_launch(program, commandEscapePrefix="") + self.build_and_launch(program, stopOnEntry=True, commandEscapePrefix="") breakpoint_ids = self.set_source_breakpoints( other_source_file, [breakpoint_line] diff --git a/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py b/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py index fd452d91e472b..e37cd36d7f283 100644 --- a/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py +++ b/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py @@ -2,7 +2,6 @@ Test lldb-dap start-debugging reverse requests. """ - from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * import lldbdap_testcase @@ -16,7 +15,7 @@ def test_startDebugging(self): """ program = self.getBuildArtifact("a.out") source = "main.c" - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) breakpoint_line = line_number(source, "// breakpoint") diff --git a/lldb/test/API/tools/lldb-dap/variables/children/TestDAP_variables_children.py b/lldb/test/API/tools/lldb-dap/variables/children/TestDAP_variables_children.py index a9371e5c5fe68..eb09649f387d7 100644 --- a/lldb/test/API/tools/lldb-dap/variables/children/TestDAP_variables_children.py +++ b/lldb/test/API/tools/lldb-dap/variables/children/TestDAP_variables_children.py @@ -13,13 +13,13 @@ def test_get_num_children(self): program = self.getBuildArtifact("a.out") self.build_and_launch( program, + stopOnEntry=True, preRunCommands=[ "command script import '%s'" % self.getSourcePath("formatter.py") ], ) source = "main.cpp" breakpoint1_line = line_number(source, "// break here") - lines = [breakpoint1_line] breakpoint_ids = self.set_source_breakpoints( source, [line_number(source, "// break here")] @@ -47,7 +47,7 @@ def test_return_variable_with_children(self): Test the stepping out of a function with return value show the children correctly """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) function_name = "test_return_variable_with_children" breakpoint_ids = self.set_function_breakpoints([function_name]) From lldb-commits at lists.llvm.org Wed May 7 16:21:11 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Wed, 07 May 2025 16:21:11 -0700 (PDT) Subject: [Lldb-commits] [lldb] aeeb9a3 - [lldb-dap] Change the launch sequence (#138219) (reland) Message-ID: <681beae7.170a0220.4509c.2cde@mx.google.com> Author: Jonas Devlieghere Date: 2025-05-07T16:20:47-07:00 New Revision: aeeb9a3c09f40f42a1e8e5e3c8dbde3b260744bd URL: https://github.com/llvm/llvm-project/commit/aeeb9a3c09f40f42a1e8e5e3c8dbde3b260744bd DIFF: https://github.com/llvm/llvm-project/commit/aeeb9a3c09f40f42a1e8e5e3c8dbde3b260744bd.diff LOG: [lldb-dap] Change the launch sequence (#138219) (reland) This PR changes how we treat the launch sequence in lldb-dap. - Send the initialized event after we finish handling the initialize request, rather than after we finish attaching or launching. - Delay handling the launch and attach request until we have handled the configurationDone request. The latter is now largely a NO-OP and only exists to signal lldb-dap that it can handle the launch and attach requests. - Delay handling the initial threads requests until we have handled the launch or attach request. - Make all attaching and launching synchronous, including when we have attach or launch commands. This removes the need to synchronize between the request and event thread. Background: https://discourse.llvm.org/t/reliability-of-the-lldb-dap-tests/86125 Added: Modified: lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py lldb/test/API/tools/lldb-dap/console/TestDAP_console.py lldb/test/API/tools/lldb-dap/console/TestDAP_redirection_to_console.py lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py lldb/test/API/tools/lldb-dap/exception/TestDAP_exception.py lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py lldb/test/API/tools/lldb-dap/variables/children/TestDAP_variables_children.py lldb/tools/lldb-dap/DAP.cpp lldb/tools/lldb-dap/DAP.h lldb/tools/lldb-dap/EventHelper.cpp lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp lldb/tools/lldb-dap/Handler/RequestHandler.cpp lldb/tools/lldb-dap/Handler/RequestHandler.h Removed: ################################################################################ diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py index 6d9ab770684f1..e10342b72f4f0 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py @@ -132,7 +132,6 @@ def __init__(self, recv, send, init_commands, log_file=None): self.exit_status = None self.initialize_body = None self.thread_stop_reasons = {} - self.breakpoint_events = [] self.progress_events = [] self.reverse_requests = [] self.module_events = [] @@ -244,13 +243,6 @@ def handle_recv_packet(self, packet): self._process_stopped() tid = body["threadId"] self.thread_stop_reasons[tid] = body - elif event == "breakpoint": - # Breakpoint events come in when a breakpoint has locations - # added or removed. Keep track of them so we can look for them - # in tests. - self.breakpoint_events.append(packet) - # no need to add 'breakpoint' event packets to our packets list - return keepGoing elif event.startswith("progress"): # Progress events come in as 'progressStart', 'progressUpdate', # and 'progressEnd' events. Keep these around in case test @@ -412,6 +404,15 @@ def wait_for_stopped(self, timeout=None): self.threads = [] return stopped_events + def wait_for_breakpoint_events(self, timeout=None): + breakpoint_events = [] + while True: + event = self.wait_for_event("breakpoint", timeout=timeout) + if not event: + break + breakpoint_events.append(event) + return breakpoint_events + def wait_for_exited(self): event_dict = self.wait_for_event("exited") if event_dict is None: @@ -591,6 +592,7 @@ def request_attach( attachCommands=None, terminateCommands=None, coreFile=None, + stopOnAttach=True, postRunCommands=None, sourceMap=None, gdbRemotePort=None, @@ -620,6 +622,8 @@ def request_attach( args_dict["attachCommands"] = attachCommands if coreFile: args_dict["coreFile"] = coreFile + if stopOnAttach: + args_dict["stopOnEntry"] = stopOnAttach if postRunCommands: args_dict["postRunCommands"] = postRunCommands if sourceMap: @@ -632,7 +636,7 @@ def request_attach( response = self.send_recv(command_dict) if response["success"]: - self.wait_for_events(["process", "initialized"]) + self.wait_for_event("process") return response def request_breakpointLocations( @@ -666,10 +670,6 @@ def request_configurationDone(self): response = self.send_recv(command_dict) if response: self.configuration_done_sent = True - # Client requests the baseline of currently existing threads after - # a successful launch or attach. - # Kick off the threads request that follows - self.request_threads() return response def _process_stopped(self): @@ -887,7 +887,7 @@ def request_launch( response = self.send_recv(command_dict) if response["success"]: - self.wait_for_events(["process", "initialized"]) + self.wait_for_event("process") return response def request_next(self, threadId, granularity="statement"): @@ -1325,6 +1325,26 @@ def attach_options_specified(options): def run_vscode(dbg, args, options): dbg.request_initialize(options.sourceInitFile) + + if options.sourceBreakpoints: + source_to_lines = {} + for file_line in options.sourceBreakpoints: + (path, line) = file_line.split(":") + if len(path) == 0 or len(line) == 0: + print('error: invalid source with line "%s"' % (file_line)) + + else: + if path in source_to_lines: + source_to_lines[path].append(int(line)) + else: + source_to_lines[path] = [int(line)] + for source in source_to_lines: + dbg.request_setBreakpoints(source, source_to_lines[source]) + if options.funcBreakpoints: + dbg.request_setFunctionBreakpoints(options.funcBreakpoints) + + dbg.request_configurationDone() + if attach_options_specified(options): response = dbg.request_attach( program=options.program, @@ -1353,23 +1373,6 @@ def run_vscode(dbg, args, options): ) if response["success"]: - if options.sourceBreakpoints: - source_to_lines = {} - for file_line in options.sourceBreakpoints: - (path, line) = file_line.split(":") - if len(path) == 0 or len(line) == 0: - print('error: invalid source with line "%s"' % (file_line)) - - else: - if path in source_to_lines: - source_to_lines[path].append(int(line)) - else: - source_to_lines[path] = [int(line)] - for source in source_to_lines: - dbg.request_setBreakpoints(source, source_to_lines[source]) - if options.funcBreakpoints: - dbg.request_setFunctionBreakpoints(options.funcBreakpoints) - dbg.request_configurationDone() dbg.wait_for_stopped() else: if "message" in response: diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py index 2c14bb35162b5..c5a7eb76a58c7 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py @@ -340,6 +340,7 @@ def attach( exitCommands=None, attachCommands=None, coreFile=None, + stopOnAttach=True, disconnectAutomatically=True, terminateCommands=None, postRunCommands=None, @@ -348,6 +349,8 @@ def attach( expectFailure=False, gdbRemotePort=None, gdbRemoteHostname=None, + sourceBreakpoints=None, + functionBreakpoints=None, ): """Build the default Makefile target, create the DAP debug adapter, and attach to the process. @@ -364,6 +367,28 @@ def cleanup(): self.addTearDownHook(cleanup) # Initialize and launch the program self.dap_server.request_initialize(sourceInitFile) + self.dap_server.wait_for_event("initialized") + + # Set source breakpoints as part of the launch sequence. + if sourceBreakpoints: + for source_path, lines in sourceBreakpoints: + response = self.dap_server.request_setBreakpoints(source_path, lines) + self.assertTrue( + response["success"], + "setBreakpoints failed (%s)" % (response), + ) + + # Set function breakpoints as part of the launch sequence. + if functionBreakpoints: + response = self.dap_server.request_setFunctionBreakpoints( + functionBreakpoints + ) + self.assertTrue( + response["success"], + "setFunctionBreakpoint failed (%s)" % (response), + ) + + self.dap_server.request_configurationDone() response = self.dap_server.request_attach( program=program, pid=pid, @@ -376,6 +401,7 @@ def cleanup(): attachCommands=attachCommands, terminateCommands=terminateCommands, coreFile=coreFile, + stopOnAttach=stopOnAttach, postRunCommands=postRunCommands, sourceMap=sourceMap, gdbRemotePort=gdbRemotePort, @@ -419,6 +445,8 @@ def launch( commandEscapePrefix=None, customFrameFormat=None, customThreadFormat=None, + sourceBreakpoints=None, + functionBreakpoints=None, ): """Sending launch request to dap""" @@ -434,6 +462,29 @@ def cleanup(): # Initialize and launch the program self.dap_server.request_initialize(sourceInitFile) + self.dap_server.wait_for_event("initialized") + + # Set source breakpoints as part of the launch sequence. + if sourceBreakpoints: + for source_path, lines in sourceBreakpoints: + response = self.dap_server.request_setBreakpoints(source_path, lines) + self.assertTrue( + response["success"], + "setBreakpoints failed (%s)" % (response), + ) + + # Set function breakpoints as part of the launch sequence. + if functionBreakpoints: + response = self.dap_server.request_setFunctionBreakpoints( + functionBreakpoints + ) + self.assertTrue( + response["success"], + "setFunctionBreakpoint failed (%s)" % (response), + ) + + self.dap_server.request_configurationDone() + response = self.dap_server.request_launch( program, args=args, @@ -504,6 +555,8 @@ def build_and_launch( customThreadFormat=None, launchCommands=None, expectFailure=False, + sourceBreakpoints=None, + functionBreakpoints=None, ): """Build the default Makefile target, create the DAP debug adapter, and launch the process. @@ -540,6 +593,8 @@ def build_and_launch( customThreadFormat=customThreadFormat, launchCommands=launchCommands, expectFailure=expectFailure, + sourceBreakpoints=sourceBreakpoints, + functionBreakpoints=functionBreakpoints, ) def getBuiltinDebugServerTool(self): diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py index f48d5a7db3c50..741c011a3d692 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py @@ -27,6 +27,8 @@ def spawn_and_wait(program, delay): @skip class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase): def set_and_hit_breakpoint(self, continueToExit=True): + self.dap_server.wait_for_stopped() + source = "main.c" breakpoint1_line = line_number(source, "// breakpoint 1") lines = [breakpoint1_line] diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py index 7f93b9f2a3a22..7250e67ebcd8c 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py @@ -18,17 +18,17 @@ import socket - at skip class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase): default_timeout = 20 def set_and_hit_breakpoint(self, continueToExit=True): + self.dap_server.wait_for_stopped() + source = "main.c" - main_source_path = os.path.join(os.getcwd(), source) - breakpoint1_line = line_number(main_source_path, "// breakpoint 1") + breakpoint1_line = line_number(source, "// breakpoint 1") lines = [breakpoint1_line] # Set breakpoint in the thread function so we can step the threads - breakpoint_ids = self.set_source_breakpoints(main_source_path, lines) + breakpoint_ids = self.set_source_breakpoints(source, lines) self.assertEqual( len(breakpoint_ids), len(lines), "expect correct number of breakpoints" ) diff --git a/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py b/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py index e5590e1b332a0..8581f10cef22a 100644 --- a/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py +++ b/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py @@ -81,52 +81,27 @@ def test_breakpoint_events(self): breakpoint["verified"], "expect foo breakpoint to not be verified" ) - # Get the stop at the entry point - self.continue_to_next_stop() + # Make sure we're stopped. + self.dap_server.wait_for_stopped() - # We are now stopped at the entry point to the program. Shared - # libraries are not loaded yet (at least on macOS they aren't) and only - # the breakpoint in the main executable should be resolved. - self.assertEqual(len(self.dap_server.breakpoint_events), 1) - event = self.dap_server.breakpoint_events[0] - body = event["body"] - self.assertEqual( - body["reason"], "changed", "breakpoint event should say changed" - ) - breakpoint = body["breakpoint"] - self.assertEqual(breakpoint["id"], main_bp_id) - self.assertTrue(breakpoint["verified"], "main breakpoint should be resolved") - - # Clear the list of breakpoint events so we don't see this one again. - self.dap_server.breakpoint_events.clear() + # Flush the breakpoint events. + self.dap_server.wait_for_breakpoint_events(timeout=5) # Continue to the breakpoint self.continue_to_breakpoints(dap_breakpoint_ids) - # When the process launches, we first expect to see both the main and - # foo breakpoint as unresolved. - for event in self.dap_server.breakpoint_events[:2]: - body = event["body"] - self.assertEqual( - body["reason"], "changed", "breakpoint event should say changed" - ) - breakpoint = body["breakpoint"] - self.assertIn(str(breakpoint["id"]), dap_breakpoint_ids) - self.assertFalse(breakpoint["verified"], "breakpoint should be unresolved") + verified_breakpoint_ids = [] + unverified_breakpoint_ids = [] + for breakpoint_event in self.dap_server.wait_for_breakpoint_events(timeout=5): + breakpoint = breakpoint_event["body"]["breakpoint"] + id = breakpoint["id"] + if breakpoint["verified"]: + verified_breakpoint_ids.append(id) + else: + unverified_breakpoint_ids.append(id) - # Then, once the dynamic loader has given us a load address, they - # should show up as resolved again. - for event in self.dap_server.breakpoint_events[3:]: - body = event["body"] - self.assertEqual( - body["reason"], "changed", "breakpoint event should say changed" - ) - breakpoint = body["breakpoint"] - self.assertIn(str(breakpoint["id"]), dap_breakpoint_ids) - self.assertTrue(breakpoint["verified"], "breakpoint should be resolved") - self.assertNotIn( - "source", - breakpoint, - "breakpoint event should not return a source object", - ) - self.assertIn("line", breakpoint, "breakpoint event should have line") + self.assertIn(main_bp_id, unverified_breakpoint_ids) + self.assertIn(foo_bp_id, unverified_breakpoint_ids) + + self.assertIn(main_bp_id, verified_breakpoint_ids) + self.assertIn(foo_bp_id, verified_breakpoint_ids) diff --git a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py index 210e591bff426..a94288c7a669e 100644 --- a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py +++ b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py @@ -2,7 +2,6 @@ Test lldb-dap completions request """ - import lldbdap_testcase import dap_server from lldbsuite.test import lldbutil @@ -32,6 +31,7 @@ variable_var1_completion = {"text": "var1", "label": "var1 -- int &"} variable_var2_completion = {"text": "var2", "label": "var2 -- int &"} + # Older version of libcxx produce slightly diff erent typename strings for # templates like vector. @skipIf(compiler="clang", compiler_version=["<", "16.0"]) @@ -43,16 +43,22 @@ def verify_completions(self, actual_list, expected_list, not_expected_list=[]): for not_expected_item in not_expected_list: self.assertNotIn(not_expected_item, actual_list) - - def setup_debugee(self): + def setup_debugee(self, stopOnEntry=False): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) - source = "main.cpp" - breakpoint1_line = line_number(source, "// breakpoint 1") - breakpoint2_line = line_number(source, "// breakpoint 2") - - self.set_source_breakpoints(source, [breakpoint1_line, breakpoint2_line]) + self.build_and_launch( + program, + stopOnEntry=stopOnEntry, + sourceBreakpoints=[ + ( + source, + [ + line_number(source, "// breakpoint 1"), + line_number(source, "// breakpoint 2"), + ], + ), + ], + ) def test_command_completions(self): """ @@ -235,7 +241,7 @@ def test_auto_completions(self): """ Tests completion requests in "repl-mode=auto" """ - self.setup_debugee() + self.setup_debugee(stopOnEntry=True) res = self.dap_server.request_evaluate( "`lldb-dap repl-mode auto", context="repl" diff --git a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py index b07c4f871d73b..8642e317f9b3a 100644 --- a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py +++ b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py @@ -19,6 +19,7 @@ def get_subprocess(root_process, process_name): self.assertTrue(False, "No subprocess with name %s found" % process_name) + class TestDAP_console(lldbdap_testcase.DAPTestCaseBase): def check_lldb_command( self, lldb_command, contains_string, assert_msg, command_escape_prefix="`" @@ -52,7 +53,7 @@ def test_scopes_variables_setVariable_evaluate(self): character. """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") lines = [breakpoint1_line] @@ -81,7 +82,7 @@ def test_scopes_variables_setVariable_evaluate(self): def test_custom_escape_prefix(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program, commandEscapePrefix="::") + self.build_and_launch(program, stopOnEntry=True, commandEscapePrefix="::") source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line]) @@ -96,7 +97,7 @@ def test_custom_escape_prefix(self): def test_empty_escape_prefix(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program, commandEscapePrefix="") + self.build_and_launch(program, stopOnEntry=True, commandEscapePrefix="") source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line]) @@ -113,7 +114,7 @@ def test_empty_escape_prefix(self): def test_exit_status_message_sigterm(self): source = "main.cpp" program = self.getBuildArtifact("a.out") - self.build_and_launch(program, commandEscapePrefix="") + self.build_and_launch(program, stopOnEntry=True, commandEscapePrefix="") breakpoint1_line = line_number(source, "// breakpoint 1") breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line]) self.continue_to_breakpoints(breakpoint_ids) @@ -167,7 +168,7 @@ def test_exit_status_message_ok(self): def test_diagnositcs(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) core = self.getBuildArtifact("minidump.core") self.yaml2obj("minidump.yaml", core) diff --git a/lldb/test/API/tools/lldb-dap/console/TestDAP_redirection_to_console.py b/lldb/test/API/tools/lldb-dap/console/TestDAP_redirection_to_console.py index e367c327d4295..23500bd6fe586 100644 --- a/lldb/test/API/tools/lldb-dap/console/TestDAP_redirection_to_console.py +++ b/lldb/test/API/tools/lldb-dap/console/TestDAP_redirection_to_console.py @@ -16,7 +16,9 @@ def test(self): """ program = self.getBuildArtifact("a.out") self.build_and_launch( - program, lldbDAPEnv={"LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION": ""} + program, + stopOnEntry=True, + lldbDAPEnv={"LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION": ""}, ) source = "main.cpp" diff --git a/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py b/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py index 0cb792d662a80..09e3f62f0eead 100644 --- a/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py +++ b/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py @@ -31,7 +31,7 @@ def test_launch(self): created. """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program, disconnectAutomatically=False) + self.build_and_launch(program, stopOnEntry=True, disconnectAutomatically=False) # We set a breakpoint right before the side effect file is created self.set_source_breakpoints( @@ -39,7 +39,11 @@ def test_launch(self): ) self.continue_to_next_stop() + # verify we haven't produced the side effect file yet + self.assertFalse(os.path.exists(program + ".side_effect")) + self.dap_server.request_disconnect() + # verify we didn't produce the side effect file time.sleep(1) self.assertFalse(os.path.exists(program + ".side_effect")) diff --git a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py index d97fda730c46a..19b682dfcd22d 100644 --- a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py +++ b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py @@ -10,6 +10,7 @@ from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * + # DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. @skip class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase): @@ -42,7 +43,9 @@ def run_test_evaluate_expressions( self.context = context program = self.getBuildArtifact("a.out") self.build_and_launch( - program, enableAutoVariableSummaries=enableAutoVariableSummaries + program, + enableAutoVariableSummaries=enableAutoVariableSummaries, + stopOnEntry=True, ) source = "main.cpp" self.set_source_breakpoints( diff --git a/lldb/test/API/tools/lldb-dap/exception/TestDAP_exception.py b/lldb/test/API/tools/lldb-dap/exception/TestDAP_exception.py index 39d73737b7e8c..ec7387dabb0c2 100644 --- a/lldb/test/API/tools/lldb-dap/exception/TestDAP_exception.py +++ b/lldb/test/API/tools/lldb-dap/exception/TestDAP_exception.py @@ -2,7 +2,6 @@ Test exception behavior in DAP with signal. """ - from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * import lldbdap_testcase @@ -17,7 +16,7 @@ def test_stopped_description(self): """ program = self.getBuildArtifact("a.out") self.build_and_launch(program) - self.dap_server.request_continue() + self.assertTrue(self.verify_stop_exception_info("signal SIGABRT")) exceptionInfo = self.get_exceptionInfo() self.assertEqual(exceptionInfo["breakMode"], "always") diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py index 931456299e03e..e8e9181f8da8d 100644 --- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py +++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py @@ -15,6 +15,7 @@ # Despite the test program printing correctly. See # https://github.com/llvm/llvm-project/issues/137599. + class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase): @skipIfWindows def test_default(self): @@ -88,8 +89,8 @@ def test_stopOnEntry(self): """ program = self.getBuildArtifact("a.out") self.build_and_launch(program, stopOnEntry=True) - self.set_function_breakpoints(["main"]) - stopped_events = self.continue_to_next_stop() + + stopped_events = self.dap_server.wait_for_stopped() for stopped_event in stopped_events: if "body" in stopped_event: body = stopped_event["body"] @@ -357,6 +358,7 @@ def test_commands(self): terminateCommands = ["expr 4+2"] self.build_and_launch( program, + stopOnEntry=True, initCommands=initCommands, preRunCommands=preRunCommands, postRunCommands=postRunCommands, @@ -530,6 +532,7 @@ def test_terminate_commands(self): terminateCommands = ["expr 4+2"] self.launch( program=program, + stopOnEntry=True, terminateCommands=terminateCommands, disconnectAutomatically=False, ) diff --git a/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py b/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py index fee63655de0da..0f94b50c31fba 100755 --- a/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py +++ b/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py @@ -50,7 +50,7 @@ def verify_progress_events( @skipIfWindows def test(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) progress_emitter = os.path.join(os.getcwd(), "Progress_emitter.py") self.dap_server.request_evaluate( f"`command script import {progress_emitter}", context="repl" diff --git a/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py b/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py index c6f59949d668e..81edcdf4bd0f9 100644 --- a/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py +++ b/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py @@ -20,7 +20,7 @@ def assertEvaluate(self, expression, regex): def test_completions(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") diff --git a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py index 36fa0bd40183f..5f95c7bfb1556 100644 --- a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py +++ b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py @@ -22,7 +22,6 @@ def test_basic_functionality(self): [bp_A, bp_B] = self.set_source_breakpoints("main.c", [line_A, line_B]) # Verify we hit A, then B. - self.dap_server.request_configurationDone() self.verify_breakpoint_hit([bp_A]) self.dap_server.request_continue() self.verify_breakpoint_hit([bp_B]) diff --git a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py index a94c9860c1508..eed769a5a0cc6 100644 --- a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py +++ b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py @@ -74,7 +74,6 @@ def test_stopOnEntry(self): program = self.getBuildArtifact("a.out") self.build_and_launch(program, runInTerminal=True, stopOnEntry=True) [bp_main] = self.set_function_breakpoints(["main"]) - self.dap_server.request_configurationDone() # When using stopOnEntry, configurationDone doesn't result in a running # process, we should immediately get a stopped event instead. diff --git a/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py b/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py index ce262be161861..64cec70aa923b 100644 --- a/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py +++ b/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py @@ -16,12 +16,14 @@ def test_send_event(self): """ program = self.getBuildArtifact("a.out") source = "main.c" + breakpoint_line = line_number(source, "// breakpoint") custom_event_body = { "key": 321, "arr": [True], } self.build_and_launch( program, + sourceBreakpoints=[(source, [breakpoint_line])], stopCommands=[ "lldb-dap send-event my-custom-event-no-body", "lldb-dap send-event my-custom-event '{}'".format( @@ -30,11 +32,6 @@ def test_send_event(self): ], ) - breakpoint_line = line_number(source, "// breakpoint") - - self.set_source_breakpoints(source, [breakpoint_line]) - self.continue_to_next_stop() - custom_event = self.dap_server.wait_for_event( filter=["my-custom-event-no-body"] ) diff --git a/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py b/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py index 4e2a76cf76980..edf4adae14a3b 100644 --- a/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py +++ b/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py @@ -61,7 +61,7 @@ def test_stackTrace(self): Tests the 'stackTrace' packet and all its variants. """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) source = "main.c" self.source_path = os.path.join(os.getcwd(), source) self.recurse_end = line_number(source, "recurse end") diff --git a/lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py b/lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py index 08c225b3cada4..963d711978534 100644 --- a/lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py +++ b/lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py @@ -37,7 +37,7 @@ def build_and_run_until_breakpoint(self): breakpoint_line = line_number(other_source_file, "// Break here") program = self.getBuildArtifact("a.out") - self.build_and_launch(program, commandEscapePrefix="") + self.build_and_launch(program, stopOnEntry=True, commandEscapePrefix="") breakpoint_ids = self.set_source_breakpoints( other_source_file, [breakpoint_line] diff --git a/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py b/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py index fd452d91e472b..e37cd36d7f283 100644 --- a/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py +++ b/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py @@ -2,7 +2,6 @@ Test lldb-dap start-debugging reverse requests. """ - from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * import lldbdap_testcase @@ -16,7 +15,7 @@ def test_startDebugging(self): """ program = self.getBuildArtifact("a.out") source = "main.c" - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) breakpoint_line = line_number(source, "// breakpoint") diff --git a/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py b/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py index 70c11a63a79f7..7e28a5af4331c 100644 --- a/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py +++ b/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py @@ -19,7 +19,7 @@ def test_stop_hooks_before_run(self): self.build_and_launch(program, stopOnEntry=True, preRunCommands=preRunCommands) # The first stop is on entry. - self.continue_to_next_stop() + self.dap_server.wait_for_stopped() breakpoint_ids = self.set_function_breakpoints(["main"]) # This request hangs if the race happens, because, in that case, the diff --git a/lldb/test/API/tools/lldb-dap/variables/children/TestDAP_variables_children.py b/lldb/test/API/tools/lldb-dap/variables/children/TestDAP_variables_children.py index a9371e5c5fe68..eb09649f387d7 100644 --- a/lldb/test/API/tools/lldb-dap/variables/children/TestDAP_variables_children.py +++ b/lldb/test/API/tools/lldb-dap/variables/children/TestDAP_variables_children.py @@ -13,13 +13,13 @@ def test_get_num_children(self): program = self.getBuildArtifact("a.out") self.build_and_launch( program, + stopOnEntry=True, preRunCommands=[ "command script import '%s'" % self.getSourcePath("formatter.py") ], ) source = "main.cpp" breakpoint1_line = line_number(source, "// break here") - lines = [breakpoint1_line] breakpoint_ids = self.set_source_breakpoints( source, [line_number(source, "// break here")] @@ -47,7 +47,7 @@ def test_return_variable_with_children(self): Test the stepping out of a function with return value show the children correctly """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) function_name = "test_return_variable_with_children" breakpoint_ids = self.set_function_breakpoints([function_name]) diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 4b631484c9fab..62c60cc3a9b3b 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -84,8 +84,8 @@ DAP::DAP(Log *log, const ReplMode default_repl_mode, : log(log), transport(transport), broadcaster("lldb-dap"), exception_breakpoints(), focus_tid(LLDB_INVALID_THREAD_ID), stop_at_entry(false), is_attach(false), - restarting_process_id(LLDB_INVALID_PROCESS_ID), - configuration_done_sent(false), waiting_for_run_in_terminal(false), + restarting_process_id(LLDB_INVALID_PROCESS_ID), configuration_done(false), + waiting_for_run_in_terminal(false), progress_event_reporter( [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }), reverse_request_seq(0), repl_mode(default_repl_mode) { @@ -893,10 +893,19 @@ llvm::Error DAP::Loop() { return errWrapper; } + // The launch sequence is special and we need to carefully handle + // packets in the right order. Until we've handled configurationDone, + bool add_to_pending_queue = false; + if (const protocol::Request *req = - std::get_if(&*next); - req && req->command == "disconnect") { - disconnecting = true; + std::get_if(&*next)) { + llvm::StringRef command = req->command; + if (command == "disconnect") + disconnecting = true; + if (!configuration_done) + add_to_pending_queue = + command != "initialize" && command != "configurationDone" && + command != "disconnect" && !command.ends_with("Breakpoints"); } const std::optional cancel_args = @@ -924,7 +933,8 @@ llvm::Error DAP::Loop() { { std::lock_guard guard(m_queue_mutex); - m_queue.push_back(std::move(*next)); + auto &queue = add_to_pending_queue ? m_pending_queue : m_queue; + queue.push_back(std::move(*next)); } m_queue_cv.notify_one(); } @@ -938,16 +948,19 @@ llvm::Error DAP::Loop() { StopEventHandlers(); }); - while (!disconnecting || !m_queue.empty()) { + while (true) { std::unique_lock lock(m_queue_mutex); m_queue_cv.wait(lock, [&] { return disconnecting || !m_queue.empty(); }); - if (m_queue.empty()) + if (disconnecting && m_queue.empty()) break; Message next = m_queue.front(); m_queue.pop_front(); + // Unlock while we're processing the event. + lock.unlock(); + if (!HandleObject(next)) return llvm::createStringError(llvm::inconvertibleErrorCode(), "unhandled packet"); @@ -1219,6 +1232,16 @@ void DAP::SetConfiguration(const protocol::Configuration &config, SetThreadFormat(*configuration.customThreadFormat); } +void DAP::SetConfigurationDone() { + { + std::lock_guard guard(m_queue_mutex); + std::copy(m_pending_queue.begin(), m_pending_queue.end(), + std::front_inserter(m_queue)); + configuration_done = true; + } + m_queue_cv.notify_all(); +} + void DAP::SetFrameFormat(llvm::StringRef format) { if (format.empty()) return; diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index 88eedb0860cf1..b581ae759b1bc 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -188,7 +188,7 @@ struct DAP { // shutting down the entire adapter. When we're restarting, we keep the id of // the old process here so we can detect this case and keep running. lldb::pid_t restarting_process_id; - bool configuration_done_sent; + bool configuration_done; llvm::StringMap> request_handlers; bool waiting_for_run_in_terminal; ProgressEventReporter progress_event_reporter; @@ -251,6 +251,8 @@ struct DAP { /// Configures the debug adapter for launching/attaching. void SetConfiguration(const protocol::Configuration &confing, bool is_attach); + void SetConfigurationDone(); + /// Configure source maps based on the current `DAPConfiguration`. void ConfigureSourceMaps(); @@ -417,8 +419,10 @@ struct DAP { lldb::SBMutex GetAPIMutex() const { return target.GetAPIMutex(); } private: - std::mutex m_queue_mutex; + /// Queue for all incoming messages. std::deque m_queue; + std::deque m_pending_queue; + std::mutex m_queue_mutex; std::condition_variable m_queue_cv; std::mutex m_cancelled_requests_mutex; diff --git a/lldb/tools/lldb-dap/EventHelper.cpp b/lldb/tools/lldb-dap/EventHelper.cpp index 2c659f39f4b66..ed2d8700c26b0 100644 --- a/lldb/tools/lldb-dap/EventHelper.cpp +++ b/lldb/tools/lldb-dap/EventHelper.cpp @@ -222,7 +222,7 @@ void SendContinuedEvent(DAP &dap) { // If the focus thread is not set then we haven't reported any thread status // to the client, so nothing to report. - if (!dap.configuration_done_sent || dap.focus_tid == LLDB_INVALID_THREAD_ID) { + if (!dap.configuration_done || dap.focus_tid == LLDB_INVALID_THREAD_ID) { return; } diff --git a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp index 7a0f091128e4a..5dc9c3f9772e3 100644 --- a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp @@ -133,61 +133,70 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { dap.SendOutput(OutputType::Console, llvm::StringRef(attach_msg, attach_msg_len)); } - if (attachCommands.empty()) { - // No "attachCommands", just attach normally. - // Disable async events so the attach will be successful when we return from - // the launch call and the launch will happen synchronously + { + // Perform the launch in synchronous mode so that we don't have to worry + // about process state changes during the launch. ScopeSyncMode scope_sync_mode(dap.debugger); - - if (core_file.empty()) { - if ((pid != LLDB_INVALID_PROCESS_ID) && - (gdb_remote_port != invalid_port)) { - // If both pid and port numbers are specified. - error.SetErrorString("The user can't specify both pid and port"); - } else if (gdb_remote_port != invalid_port) { - // If port is specified and pid is not. - lldb::SBListener listener = dap.debugger.GetListener(); - - // If the user hasn't provided the hostname property, default localhost - // being used. - std::string connect_url = - llvm::formatv("connect://{0}:", gdb_remote_hostname); - connect_url += std::to_string(gdb_remote_port); - dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", - error); + if (attachCommands.empty()) { + // No "attachCommands", just attach normally. + if (core_file.empty()) { + if ((pid != LLDB_INVALID_PROCESS_ID) && + (gdb_remote_port != invalid_port)) { + // If both pid and port numbers are specified. + error.SetErrorString("The user can't specify both pid and port"); + } else if (gdb_remote_port != invalid_port) { + // If port is specified and pid is not. + lldb::SBListener listener = dap.debugger.GetListener(); + + // If the user hasn't provided the hostname property, default + // localhost being used. + std::string connect_url = + llvm::formatv("connect://{0}:", gdb_remote_hostname); + connect_url += std::to_string(gdb_remote_port); + dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", + error); + } else { + // Attach by pid or process name. + lldb::SBAttachInfo attach_info; + if (pid != LLDB_INVALID_PROCESS_ID) + attach_info.SetProcessID(pid); + else if (dap.configuration.program.has_value()) + attach_info.SetExecutable(dap.configuration.program->data()); + attach_info.SetWaitForLaunch(wait_for, false /*async*/); + dap.target.Attach(attach_info, error); + } } else { - // Attach by pid or process name. - lldb::SBAttachInfo attach_info; - if (pid != LLDB_INVALID_PROCESS_ID) - attach_info.SetProcessID(pid); - else if (dap.configuration.program.has_value()) - attach_info.SetExecutable(dap.configuration.program->data()); - attach_info.SetWaitForLaunch(wait_for, false /*async*/); - dap.target.Attach(attach_info, error); + dap.target.LoadCore(core_file.data(), error); } } else { - dap.target.LoadCore(core_file.data(), error); - } - } else { - // We have "attachCommands" that are a set of commands that are expected - // to execute the commands after which a process should be created. If there - // is no valid process after running these commands, we have failed. - if (llvm::Error err = dap.RunAttachCommands(attachCommands)) { - response["success"] = false; - EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; + // We have "attachCommands" that are a set of commands that are expected + // to execute the commands after which a process should be created. If + // there is no valid process after running these commands, we have failed. + if (llvm::Error err = dap.RunAttachCommands(attachCommands)) { + response["success"] = false; + EmplaceSafeString(response, "message", llvm::toString(std::move(err))); + dap.SendJSON(llvm::json::Value(std::move(response))); + return; + } + // The custom commands might have created a new target so we should use + // the selected target after these commands are run. + dap.target = dap.debugger.GetSelectedTarget(); } - // The custom commands might have created a new target so we should use the - // selected target after these commands are run. - dap.target = dap.debugger.GetSelectedTarget(); - - // Make sure the process is attached and stopped before proceeding as the - // the launch commands are not run using the synchronous mode. - error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds)); } + // Make sure the process is attached and stopped. + error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds)); + + // Clients can request a baseline of currently existing threads after + // we acknowledge the configurationDone request. + // Client requests the baseline of currently existing threads after + // a successful or attach by sending a 'threads' request + // right after receiving the configurationDone response. + // Obtain the list of threads before we resume the process + dap.initial_thread_list = + GetThreads(dap.target.GetProcess(), dap.thread_format); + if (error.Success() && core_file.empty()) { auto attached_pid = dap.target.GetProcess().GetProcessID(); if (attached_pid == LLDB_INVALID_PROCESS_ID) { @@ -206,9 +215,17 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { } dap.SendJSON(llvm::json::Value(std::move(response))); + + // FIXME: Move this into PostRun. if (error.Success()) { - SendProcessEvent(dap, Attach); - dap.SendJSON(CreateEventObject("initialized")); + if (dap.target.GetProcess().IsValid()) { + SendProcessEvent(dap, Attach); + + if (dap.stop_at_entry) + SendThreadStoppedEvent(dap); + else + dap.target.GetProcess().Continue(); + } } } diff --git a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp index f39bbdefdbb95..802c28d7b8904 100644 --- a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp @@ -47,21 +47,11 @@ namespace lldb_dap { void ConfigurationDoneRequestHandler::operator()( const llvm::json::Object &request) const { + dap.SetConfigurationDone(); + llvm::json::Object response; FillResponse(request, response); dap.SendJSON(llvm::json::Value(std::move(response))); - dap.configuration_done_sent = true; - if (dap.stop_at_entry) - SendThreadStoppedEvent(dap); - else { - // Client requests the baseline of currently existing threads after - // a successful launch or attach by sending a 'threads' request - // right after receiving the configurationDone response. - // Obtain the list of threads before we resume the process - dap.initial_thread_list = - GetThreads(dap.target.GetProcess(), dap.thread_format); - dap.target.GetProcess().Continue(); - } } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp index ce34c52bcc334..aa947d3cb5ab9 100644 --- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp @@ -140,43 +140,28 @@ static void EventThreadFunction(DAP &dap) { lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event); if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) { auto state = lldb::SBProcess::GetStateFromEvent(event); + + DAP_LOG(dap.log, "State = {0}", state); switch (state) { + case lldb::eStateConnected: + case lldb::eStateDetached: case lldb::eStateInvalid: - // Not a state event - break; case lldb::eStateUnloaded: break; - case lldb::eStateConnected: - break; case lldb::eStateAttaching: - break; - case lldb::eStateLaunching: - break; - case lldb::eStateStepping: - break; case lldb::eStateCrashed: - break; - case lldb::eStateDetached: - break; - case lldb::eStateSuspended: - break; + case lldb::eStateLaunching: case lldb::eStateStopped: - // We launch and attach in synchronous mode then the first stop - // event will not be delivered. If we use "launchCommands" during a - // launch or "attachCommands" during an attach we might some process - // stop events which we do not want to send an event for. We will - // manually send a stopped event in request_configurationDone(...) - // so don't send any before then. - if (dap.configuration_done_sent) { - // Only report a stopped event if the process was not - // automatically restarted. - if (!lldb::SBProcess::GetRestartedFromEvent(event)) { - SendStdOutStdErr(dap, process); - SendThreadStoppedEvent(dap); - } + case lldb::eStateSuspended: + // Only report a stopped event if the process was not + // automatically restarted. + if (!lldb::SBProcess::GetRestartedFromEvent(event)) { + SendStdOutStdErr(dap, process); + SendThreadStoppedEvent(dap); } break; case lldb::eStateRunning: + case lldb::eStateStepping: dap.WillContinue(); SendContinuedEvent(dap); break; @@ -284,6 +269,7 @@ llvm::Expected InitializeRequestHandler::Run( // Do not source init files until in/out/err are configured. dap.debugger = lldb::SBDebugger::Create(false); dap.debugger.SetInputFile(dap.in); + dap.target = dap.debugger.GetDummyTarget(); llvm::Expected out_fd = dap.out.GetWriteFileDescriptor(); if (!out_fd) @@ -338,4 +324,8 @@ llvm::Expected InitializeRequestHandler::Run( return dap.GetCapabilities(); } +void InitializeRequestHandler::PostRun() const { + dap.SendJSON(CreateEventObject("initialized")); +} + } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp index 3e4532e754ec6..7e0e76935dd02 100644 --- a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp @@ -71,9 +71,12 @@ void LaunchRequestHandler::PostRun() const { if (dap.target.GetProcess().IsValid()) { // Attach happens when launching with runInTerminal. SendProcessEvent(dap, dap.is_attach ? Attach : Launch); - } - dap.SendJSON(CreateEventObject("initialized")); + if (dap.stop_at_entry) + SendThreadStoppedEvent(dap); + else + dap.target.GetProcess().Continue(); + } } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp index 7a75cd93abc19..282c5f4ab15a5 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp @@ -8,6 +8,7 @@ #include "Handler/RequestHandler.h" #include "DAP.h" +#include "EventHelper.h" #include "Handler/ResponseHandler.h" #include "JSONUtils.h" #include "LLDBUtils.h" @@ -162,7 +163,7 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) { dap.target.GetProcess().Continue(); // Now that the actual target is just starting (i.e. exec was just invoked), - // we return the debugger to its async state. + // we return the debugger to its sync state. scope_sync_mode.reset(); // If sending the notification failed, the launcher should be dead by now and @@ -238,35 +239,47 @@ llvm::Error BaseRequestHandler::LaunchProcess( launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug | lldb::eLaunchFlagStopAtEntry); - if (arguments.runInTerminal) { - if (llvm::Error err = RunInTerminal(dap, arguments)) - return err; - } else if (launchCommands.empty()) { - lldb::SBError error; - // Disable async events so the launch will be successful when we return from - // the launch call and the launch will happen synchronously + { + // Perform the launch in synchronous mode so that we don't have to worry + // about process state changes during the launch. ScopeSyncMode scope_sync_mode(dap.debugger); - dap.target.Launch(launch_info, error); - if (error.Fail()) - return llvm::make_error(error.GetCString()); - } else { - // Set the launch info so that run commands can access the configured - // launch details. - dap.target.SetLaunchInfo(launch_info); - if (llvm::Error err = dap.RunLaunchCommands(launchCommands)) - return err; - - // The custom commands might have created a new target so we should use the - // selected target after these commands are run. - dap.target = dap.debugger.GetSelectedTarget(); - // Make sure the process is launched and stopped at the entry point before - // proceeding as the launch commands are not run using the synchronous - // mode. - lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout); - if (error.Fail()) - return llvm::make_error(error.GetCString()); + + if (arguments.runInTerminal) { + if (llvm::Error err = RunInTerminal(dap, arguments)) + return err; + } else if (launchCommands.empty()) { + lldb::SBError error; + dap.target.Launch(launch_info, error); + if (error.Fail()) + return llvm::make_error(error.GetCString()); + } else { + // Set the launch info so that run commands can access the configured + // launch details. + dap.target.SetLaunchInfo(launch_info); + if (llvm::Error err = dap.RunLaunchCommands(launchCommands)) + return err; + + // The custom commands might have created a new target so we should use + // the selected target after these commands are run. + dap.target = dap.debugger.GetSelectedTarget(); + } } + // Make sure the process is launched and stopped at the entry point before + // proceeding. + lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout); + if (error.Fail()) + return llvm::make_error(error.GetCString()); + + // Clients can request a baseline of currently existing threads after + // we acknowledge the configurationDone request. + // Client requests the baseline of currently existing threads after + // a successful or attach by sending a 'threads' request + // right after receiving the configurationDone response. + // Obtain the list of threads before we resume the process + dap.initial_thread_list = + GetThreads(dap.target.GetProcess(), dap.thread_format); + return llvm::Error::success(); } diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index 37cc902e1c98e..9e9cfb13d77b8 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.h +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h @@ -282,6 +282,7 @@ class InitializeRequestHandler static llvm::StringLiteral GetCommand() { return "initialize"; } llvm::Expected Run(const protocol::InitializeRequestArguments &args) const override; + void PostRun() const override; }; class LaunchRequestHandler From lldb-commits at lists.llvm.org Wed May 7 16:21:42 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Wed, 07 May 2025 16:21:42 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Change the launch sequence (reland) (PR #138981) In-Reply-To: Message-ID: <681beb06.050a0220.8114f.7ec4@mx.google.com> https://github.com/JDevlieghere closed https://github.com/llvm/llvm-project/pull/138981 From lldb-commits at lists.llvm.org Wed May 7 16:21:43 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Wed, 07 May 2025 16:21:43 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Change the launch sequence (reland) (PR #138981) In-Reply-To: Message-ID: <681beb07.170a0220.21bcb1.a055@mx.google.com> JDevlieghere wrote: aeeb9a3c09f40f42a1e8e5e3c8dbde3b260744bd https://github.com/llvm/llvm-project/pull/138981 From lldb-commits at lists.llvm.org Wed May 7 16:22:16 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Wed, 07 May 2025 16:22:16 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Migrate 'continue' request to new RequestHandler. (PR #138987) Message-ID: https://github.com/ashgti created https://github.com/llvm/llvm-project/pull/138987 This adds types for the 'continue' request and updates the existing handler to the new base class. Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Wed May 7 16:22:51 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 16:22:51 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Migrate 'continue' request to new RequestHandler. (PR #138987) In-Reply-To: Message-ID: <681beb4b.170a0220.347931.38ec@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: John Harrison (ashgti)
Changes This adds types for the 'continue' request and updates the existing handler to the new base class. --- Full diff: https://github.com/llvm/llvm-project/pull/138987.diff 4 Files Affected: - (modified) lldb/tools/lldb-dap/Handler/ContinueRequestHandler.cpp (+33-66) - (modified) lldb/tools/lldb-dap/Handler/RequestHandler.h (+6-3) - (modified) lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp (+12) - (modified) lldb/tools/lldb-dap/Protocol/ProtocolRequests.h (+25-3) ``````````diff diff --git a/lldb/tools/lldb-dap/Handler/ContinueRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ContinueRequestHandler.cpp index 214e3c59c594c..ca4c9141eca38 100644 --- a/lldb/tools/lldb-dap/Handler/ContinueRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/ContinueRequestHandler.cpp @@ -7,74 +7,41 @@ //===----------------------------------------------------------------------===// #include "DAP.h" -#include "JSONUtils.h" -#include "RequestHandler.h" +#include "Handler/RequestHandler.h" +#include "LLDBUtils.h" +#include "Protocol/ProtocolRequests.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBProcess.h" +#include "llvm/Support/Error.h" + +using namespace llvm; +using namespace lldb; +using namespace lldb_dap::protocol; namespace lldb_dap { -// "ContinueRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "Continue request; value of command field is 'continue'. -// The request starts the debuggee to run again.", -// "properties": { -// "command": { -// "type": "string", -// "enum": [ "continue" ] -// }, -// "arguments": { -// "$ref": "#/definitions/ContinueArguments" -// } -// }, -// "required": [ "command", "arguments" ] -// }] -// }, -// "ContinueArguments": { -// "type": "object", -// "description": "Arguments for 'continue' request.", -// "properties": { -// "threadId": { -// "type": "integer", -// "description": "Continue execution for the specified thread (if -// possible). If the backend cannot continue on a single -// thread but will continue on all threads, it should -// set the allThreadsContinued attribute in the response -// to true." -// } -// }, -// "required": [ "threadId" ] -// }, -// "ContinueResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to 'continue' request.", -// "properties": { -// "body": { -// "type": "object", -// "properties": { -// "allThreadsContinued": { -// "type": "boolean", -// "description": "If true, the continue request has ignored the -// specified thread and continued all threads -// instead. If this attribute is missing a value -// of 'true' is assumed for backward -// compatibility." -// } -// } -// } -// }, -// "required": [ "body" ] -// }] -// } -void ContinueRequestHandler::operator()( - const llvm::json::Object &request) const { - llvm::json::Object response; - FillResponse(request, response); - lldb::SBProcess process = dap.target.GetProcess(); - lldb::SBError error = process.Continue(); - llvm::json::Object body; - body.try_emplace("allThreadsContinued", true); - response.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(response))); +/// The request resumes execution of all threads. If the debug adapter supports +/// single thread execution (see capability +/// `supportsSingleThreadExecutionRequests`), setting the `singleThread` +/// argument to true resumes only the specified thread. If not all threads were +/// resumed, the `allThreadsContinued` attribute of the response should be set +/// to false. +Expected +ContinueRequestHandler::Run(const ContinueArguments &args) const { + SBProcess process = dap.target.GetProcess(); + SBError error; + + if (args.singleThread) + dap.GetLLDBThread(args.threadId).Resume(error); + else + error = process.Continue(); + + if (error.Fail()) + return ToError(error); + + ContinueResponseBody body; + body.allThreadsContinued = args.singleThread; + return body; } + } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index 37cc902e1c98e..c8f795e3dd33e 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.h +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h @@ -224,11 +224,14 @@ class CompletionsRequestHandler : public LegacyRequestHandler { void operator()(const llvm::json::Object &request) const override; }; -class ContinueRequestHandler : public LegacyRequestHandler { +class ContinueRequestHandler + : public RequestHandler> { public: - using LegacyRequestHandler::LegacyRequestHandler; + using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "continue"; } - void operator()(const llvm::json::Object &request) const override; + llvm::Expected + Run(const protocol::ContinueArguments &args) const override; }; class ConfigurationDoneRequestHandler : public LegacyRequestHandler { diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp index d9ffc0c04e134..950e8d17e3489 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp @@ -261,6 +261,18 @@ bool fromJSON(const json::Value &Params, LaunchRequestArguments &LRA, parseEnv(Params, LRA.env, P) && parseTimeout(Params, LRA.timeout, P); } +bool fromJSON(const llvm::json::Value &Params, ContinueArguments &CA, + llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("threadId", CA.threadId) && + O.mapOptional("singleThread", CA.singleThread); +} + +llvm::json::Value toJSON(const ContinueResponseBody &CRB) { + json::Object Body{{"allThreadsContinued", CRB.allThreadsContinued}}; + return std::move(Body); +} + bool fromJSON(const llvm::json::Value &Params, SetVariableArguments &SVA, llvm::json::Path P) { json::ObjectMapper O(Params, P); diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h index 110e90837c0cd..18222d61f9a14 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h @@ -294,6 +294,28 @@ bool fromJSON(const llvm::json::Value &, LaunchRequestArguments &, /// field is required. using LaunchResponseBody = VoidResponse; +/// Arguments for `continue` request. +struct ContinueArguments { + /// Specifies the active thread. If the debug adapter supports single thread + /// execution (see `supportsSingleThreadExecutionRequests`) and the argument + /// `singleThread` is true, only the thread with this ID is resumed. + lldb::tid_t threadId = LLDB_INVALID_THREAD_ID; + + /// If this flag is true, execution is resumed only for the thread with given + /// `threadId`. + bool singleThread = false; +}; +bool fromJSON(const llvm::json::Value &, ContinueArguments &, llvm::json::Path); + +/// Response to `continue` request. +struct ContinueResponseBody { + // If omitted or set to `true`, this response signals to the client that all + // threads have been resumed. The value `false` indicates that not all threads + // were resumed. + bool allThreadsContinued = true; +}; +llvm::json::Value toJSON(const ContinueResponseBody &); + /// Arguments for `setVariable` request. struct SetVariableArguments { /// The reference of the variable container. The `variablesReference` must @@ -390,7 +412,7 @@ llvm::json::Value toJSON(const SourceResponseBody &); struct NextArguments { /// Specifies the thread for which to resume execution for one step (of the /// given granularity). - uint64_t threadId = LLDB_INVALID_THREAD_ID; + lldb::tid_t threadId = LLDB_INVALID_THREAD_ID; /// If this flag is true, all other suspended threads are not resumed. bool singleThread = false; @@ -409,7 +431,7 @@ using NextResponse = VoidResponse; struct StepInArguments { /// Specifies the thread for which to resume execution for one step-into (of /// the given granularity). - uint64_t threadId = LLDB_INVALID_THREAD_ID; + lldb::tid_t threadId = LLDB_INVALID_THREAD_ID; /// If this flag is true, all other suspended threads are not resumed. bool singleThread = false; @@ -431,7 +453,7 @@ using StepInResponse = VoidResponse; struct StepOutArguments { /// Specifies the thread for which to resume execution for one step-out (of /// the given granularity). - uint64_t threadId = LLDB_INVALID_THREAD_ID; + lldb::tid_t threadId = LLDB_INVALID_THREAD_ID; /// If this flag is true, all other suspended threads are not resumed. std::optional singleThread; ``````````
https://github.com/llvm/llvm-project/pull/138987 From lldb-commits at lists.llvm.org Wed May 7 16:23:39 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Wed, 07 May 2025 16:23:39 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Migrate 'continue' request to new RequestHandler. (PR #138987) In-Reply-To: Message-ID: <681beb7b.170a0220.309dd7.232b@mx.google.com> https://github.com/JDevlieghere approved this pull request. Very straightforward. LGTM. https://github.com/llvm/llvm-project/pull/138987 From lldb-commits at lists.llvm.org Wed May 7 16:25:17 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Wed, 07 May 2025 16:25:17 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Adding a modules explorer to lldb-dap ext. (PR #138977) In-Reply-To: Message-ID: <681bebdd.050a0220.2f97c9.4e5e@mx.google.com> https://github.com/JDevlieghere approved this pull request. LGTM but please give @da-viper a chance to review this before merging. https://github.com/llvm/llvm-project/pull/138977 From lldb-commits at lists.llvm.org Wed May 7 19:02:14 2025 From: lldb-commits at lists.llvm.org (Oliver Hunt via lldb-commits) Date: Wed, 07 May 2025 19:02:14 -0700 (PDT) Subject: [Lldb-commits] [clang] [lldb] [clang] Add support for `__ptrauth` being applied to integer types (PR #137580) In-Reply-To: Message-ID: <681c10a6.050a0220.360762.5d1f@mx.google.com> https://github.com/ojhunt edited https://github.com/llvm/llvm-project/pull/137580 From lldb-commits at lists.llvm.org Wed May 7 20:35:41 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 20:35:41 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Fix GetIndexOfChildMemberWithName to handle anonymous structs. (PR #138487) In-Reply-To: Message-ID: <681c268d.170a0220.351507.8dfc@mx.google.com> https://github.com/cmtice updated https://github.com/llvm/llvm-project/pull/138487 >From 780371cf111c97e2bce5f030625ff557bb2f40b6 Mon Sep 17 00:00:00 2001 From: Caroline Tice Date: Sun, 4 May 2025 23:43:28 -0700 Subject: [PATCH 1/3] [LLDB] Fix GetIndexOfChildMemberWithName to handle anonymous structs. When handling anonymous structs, GetIndexOfChildMemberWithName needs to add the number of non-empty base classes to the child index, to get the actual correct index. It was not doing so. This fixes that. --- .../TypeSystem/Clang/TypeSystemClang.cpp | 9 ++++- .../lang/cpp/type_lookup_anon_struct/Makefile | 3 ++ .../TestCppTypeLookupAnonStruct.py | 27 +++++++++++++++ .../lang/cpp/type_lookup_anon_struct/main.cpp | 33 +++++++++++++++++++ 4 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 lldb/test/API/lang/cpp/type_lookup_anon_struct/Makefile create mode 100644 lldb/test/API/lang/cpp/type_lookup_anon_struct/TestCppTypeLookupAnonStruct.py create mode 100644 lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 1a2b3d4133e51..dd8d144510056 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -6743,7 +6743,14 @@ size_t TypeSystemClang::GetIndexOfChildMemberWithName( if (field_name.empty()) { CompilerType field_type = GetType(field->getType()); std::vector save_indices = child_indexes; - child_indexes.push_back(child_idx); + if (field_type.IsAnonymousType()) + // We have to add on the number of non-empty base classes to this + // index! + child_indexes.push_back( + child_idx + + TypeSystemClang::GetNumBaseClasses(cxx_record_decl, true)); + else + child_indexes.push_back(child_idx); if (field_type.GetIndexOfChildMemberWithName( name, omit_empty_base_classes, child_indexes)) return child_indexes.size(); diff --git a/lldb/test/API/lang/cpp/type_lookup_anon_struct/Makefile b/lldb/test/API/lang/cpp/type_lookup_anon_struct/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/lang/cpp/type_lookup_anon_struct/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/lang/cpp/type_lookup_anon_struct/TestCppTypeLookupAnonStruct.py b/lldb/test/API/lang/cpp/type_lookup_anon_struct/TestCppTypeLookupAnonStruct.py new file mode 100644 index 0000000000000..2295ecfe81ff7 --- /dev/null +++ b/lldb/test/API/lang/cpp/type_lookup_anon_struct/TestCppTypeLookupAnonStruct.py @@ -0,0 +1,27 @@ +""" +Test that we properly print multiple types. +""" + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +from lldbsuite.test import decorators + + +class TestTypeLookupAnonStruct(TestBase): + def test_lookup_anon_struct(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, '// Set breakpoint here', lldb.SBFileSpec('main.cpp') + ) + + self.expect_var_path('unnamed_derived.y', value='2') + self.expect_var_path('unnamed_derived.z', value='13') + self.expect('frame variable "derb.x"', error=True, + substrs=['"x" is not a member of "(DerivedB) derb"']) + self.expect('frame variable "derb.y"', error=True, + substrs=['"y" is not a member of "(DerivedB) derb"']) + self.expect_var_path('derb.w', value='14') + self.expect_var_path('derb.k', value='15') + self.expect_var_path('derb.a.x', value='1') + self.expect_var_path('derb.a.y', value='2') diff --git a/lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp b/lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp new file mode 100644 index 0000000000000..18dd997ea9563 --- /dev/null +++ b/lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp @@ -0,0 +1,33 @@ +int main(int argc, char** argv) { + struct A { + struct { + int x = 1; + }; + int y = 2; + } a; + + struct B { + // Anonymous struct inherits another struct. + struct : public A { + int z = 3; + }; + int w = 4; + A a; + } b; + + struct : public A { + struct { + int z = 13; + }; + } unnamed_derived; + + struct DerivedB : public B { + struct { + // `w` in anonymous struct overrides `w` from `B`. + int w = 14; + int k = 15; + }; + } derb; + + return 0; // Set breakpoint here +} >From 81870ff46edc647695720d5635e700e77c7ca620 Mon Sep 17 00:00:00 2001 From: Caroline Tice Date: Mon, 5 May 2025 21:45:50 -0700 Subject: [PATCH 2/3] Update to always add the number of base classes (not just for anonymous structs). Remove hard-codimg for omitting empty base classes. --- .../Plugins/TypeSystem/Clang/TypeSystemClang.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index dd8d144510056..71d4385447624 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -6743,14 +6743,11 @@ size_t TypeSystemClang::GetIndexOfChildMemberWithName( if (field_name.empty()) { CompilerType field_type = GetType(field->getType()); std::vector save_indices = child_indexes; - if (field_type.IsAnonymousType()) - // We have to add on the number of non-empty base classes to this - // index! - child_indexes.push_back( - child_idx + - TypeSystemClang::GetNumBaseClasses(cxx_record_decl, true)); - else - child_indexes.push_back(child_idx); + // We have to add on the number of non-empty base classes to this + // index! + child_indexes.push_back( + child_idx + TypeSystemClang::GetNumBaseClasses( + cxx_record_decl, omit_empty_base_classes)); if (field_type.GetIndexOfChildMemberWithName( name, omit_empty_base_classes, child_indexes)) return child_indexes.size(); >From b8bad29d8ab944c7ad7ea3e8fd1f2f4d6b27973b Mon Sep 17 00:00:00 2001 From: Caroline Tice Date: Wed, 7 May 2025 20:34:04 -0700 Subject: [PATCH 3/3] Clean up comments & formatting. Add tests with empty base classes. --- .../TypeSystem/Clang/TypeSystemClang.cpp | 2 - .../TestCppTypeLookupAnonStruct.py | 38 +++++++++---- .../lang/cpp/type_lookup_anon_struct/main.cpp | 53 +++++++++++++------ 3 files changed, 63 insertions(+), 30 deletions(-) diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 71d4385447624..b639c7948569a 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -6743,8 +6743,6 @@ size_t TypeSystemClang::GetIndexOfChildMemberWithName( if (field_name.empty()) { CompilerType field_type = GetType(field->getType()); std::vector save_indices = child_indexes; - // We have to add on the number of non-empty base classes to this - // index! child_indexes.push_back( child_idx + TypeSystemClang::GetNumBaseClasses( cxx_record_decl, omit_empty_base_classes)); diff --git a/lldb/test/API/lang/cpp/type_lookup_anon_struct/TestCppTypeLookupAnonStruct.py b/lldb/test/API/lang/cpp/type_lookup_anon_struct/TestCppTypeLookupAnonStruct.py index 2295ecfe81ff7..265a96f7da152 100644 --- a/lldb/test/API/lang/cpp/type_lookup_anon_struct/TestCppTypeLookupAnonStruct.py +++ b/lldb/test/API/lang/cpp/type_lookup_anon_struct/TestCppTypeLookupAnonStruct.py @@ -12,16 +12,32 @@ class TestTypeLookupAnonStruct(TestBase): def test_lookup_anon_struct(self): self.build() lldbutil.run_to_source_breakpoint( - self, '// Set breakpoint here', lldb.SBFileSpec('main.cpp') + self, "// Set breakpoint here", lldb.SBFileSpec("main.cpp") ) - self.expect_var_path('unnamed_derived.y', value='2') - self.expect_var_path('unnamed_derived.z', value='13') - self.expect('frame variable "derb.x"', error=True, - substrs=['"x" is not a member of "(DerivedB) derb"']) - self.expect('frame variable "derb.y"', error=True, - substrs=['"y" is not a member of "(DerivedB) derb"']) - self.expect_var_path('derb.w', value='14') - self.expect_var_path('derb.k', value='15') - self.expect_var_path('derb.a.x', value='1') - self.expect_var_path('derb.a.y', value='2') + self.expect_var_path("unnamed_derived.y", value="2") + self.expect_var_path("unnamed_derived.z", value="13") + self.expect( + 'frame variable "derb.x"', + error=True, + substrs=['"x" is not a member of "(DerivedB) derb"'] + ) + self.expect( + 'frame variable "derb.y"', + error=True, + substrs=['"y" is not a member of "(DerivedB) derb"'] + ) + self.expect_var_path("derb.w", value="14") + self.expect_var_path("derb.k", value="15") + self.expect_var_path("derb.a.x", value="1") + self.expect_var_path("derb.a.y", value="2") + + self.expect_var_path("multi1.m", value="16") + self.expect_var_path("multi1.y", value="30") + + self.expect_var_path("multi2.i", value="42") + self.expect_var_path("multi2.w", value="23") + self.expect_var_path("multi2.a.x", value="1") + self.expect_var_path("multi2.a.y", value="2") + self.expect_var_path("multi2.y", value="2") + self.expect_var_path("multi2.n", value="7") diff --git a/lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp b/lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp index 18dd997ea9563..a9288e6466e74 100644 --- a/lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp +++ b/lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp @@ -1,4 +1,4 @@ -int main(int argc, char** argv) { +int main(int argc, char **argv) { struct A { struct { int x = 1; @@ -6,7 +6,7 @@ int main(int argc, char** argv) { int y = 2; } a; - struct B { + struct B { // Anonymous struct inherits another struct. struct : public A { int z = 3; @@ -15,19 +15,38 @@ int main(int argc, char** argv) { A a; } b; - struct : public A { - struct { - int z = 13; - }; - } unnamed_derived; - - struct DerivedB : public B { - struct { - // `w` in anonymous struct overrides `w` from `B`. - int w = 14; - int k = 15; - }; - } derb; - - return 0; // Set breakpoint here + + struct EmptyBase { + }; + + struct : public A { + struct { + int z = 13; + }; + } unnamed_derived; + + struct DerivedB : public B { + struct { + // `w` in anonymous struct shadows `w` from `B`. + int w = 14; + int k = 15; + }; + } derb; + + struct MultiBase : public EmptyBase, public A { + struct { + int m = 16; + int y = 30; + }; + } multi1; + + struct MB2 : public B, EmptyBase, public A { + int i = 42; + struct { + int w = 23; + int n = 7; + }; + } multi2; + + return 0; // Set breakpoint here } From lldb-commits at lists.llvm.org Wed May 7 20:39:27 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Wed, 07 May 2025 20:39:27 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Re-enable the lldb-dap tests (PR #138791) In-Reply-To: Message-ID: <681c276f.050a0220.2f01fa.a1e4@mx.google.com> JDevlieghere wrote: I re-landed the launch sequence patch (aeeb9a3c09f40f42a1e8e5e3c8dbde3b260744bd) and the Windows bots are happy again. Given that was the only objections, I'm going to merge this so we can get some insights overnight. As I said in the description, I expect the tests to be reliable, but if they're not, feel free to revert this again but please include a link to the failures or list the affected tests so we can continue investigating. https://github.com/llvm/llvm-project/pull/138791 From lldb-commits at lists.llvm.org Wed May 7 20:39:40 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 20:39:40 -0700 (PDT) Subject: [Lldb-commits] [lldb] 92d3029 - [LLDB] Fix GetIndexOfChildMemberWithName to handle anonymous structs. (#138487) Message-ID: <681c277c.170a0220.26a001.268b@mx.google.com> Author: cmtice Date: 2025-05-07T20:39:37-07:00 New Revision: 92d3029fa4a9c6ce21c50590e57ae834ae3db3bc URL: https://github.com/llvm/llvm-project/commit/92d3029fa4a9c6ce21c50590e57ae834ae3db3bc DIFF: https://github.com/llvm/llvm-project/commit/92d3029fa4a9c6ce21c50590e57ae834ae3db3bc.diff LOG: [LLDB] Fix GetIndexOfChildMemberWithName to handle anonymous structs. (#138487) When handling anonymous structs, GetIndexOfChildMemberWithName needs to add the number of non-empty base classes to the child index, to get the actual correct index. It was not doing so. This fixes that. Added: lldb/test/API/lang/cpp/type_lookup_anon_struct/Makefile lldb/test/API/lang/cpp/type_lookup_anon_struct/TestCppTypeLookupAnonStruct.py lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp Modified: lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp Removed: ################################################################################ diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 45f044733c0ff..3b286885cc37f 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -6743,7 +6743,9 @@ size_t TypeSystemClang::GetIndexOfChildMemberWithName( if (field_name.empty()) { CompilerType field_type = GetType(field->getType()); std::vector save_indices = child_indexes; - child_indexes.push_back(child_idx); + child_indexes.push_back( + child_idx + TypeSystemClang::GetNumBaseClasses( + cxx_record_decl, omit_empty_base_classes)); if (field_type.GetIndexOfChildMemberWithName( name, omit_empty_base_classes, child_indexes)) return child_indexes.size(); diff --git a/lldb/test/API/lang/cpp/type_lookup_anon_struct/Makefile b/lldb/test/API/lang/cpp/type_lookup_anon_struct/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/lang/cpp/type_lookup_anon_struct/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/lang/cpp/type_lookup_anon_struct/TestCppTypeLookupAnonStruct.py b/lldb/test/API/lang/cpp/type_lookup_anon_struct/TestCppTypeLookupAnonStruct.py new file mode 100644 index 0000000000000..265a96f7da152 --- /dev/null +++ b/lldb/test/API/lang/cpp/type_lookup_anon_struct/TestCppTypeLookupAnonStruct.py @@ -0,0 +1,43 @@ +""" +Test that we properly print multiple types. +""" + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +from lldbsuite.test import decorators + + +class TestTypeLookupAnonStruct(TestBase): + def test_lookup_anon_struct(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "// Set breakpoint here", lldb.SBFileSpec("main.cpp") + ) + + self.expect_var_path("unnamed_derived.y", value="2") + self.expect_var_path("unnamed_derived.z", value="13") + self.expect( + 'frame variable "derb.x"', + error=True, + substrs=['"x" is not a member of "(DerivedB) derb"'] + ) + self.expect( + 'frame variable "derb.y"', + error=True, + substrs=['"y" is not a member of "(DerivedB) derb"'] + ) + self.expect_var_path("derb.w", value="14") + self.expect_var_path("derb.k", value="15") + self.expect_var_path("derb.a.x", value="1") + self.expect_var_path("derb.a.y", value="2") + + self.expect_var_path("multi1.m", value="16") + self.expect_var_path("multi1.y", value="30") + + self.expect_var_path("multi2.i", value="42") + self.expect_var_path("multi2.w", value="23") + self.expect_var_path("multi2.a.x", value="1") + self.expect_var_path("multi2.a.y", value="2") + self.expect_var_path("multi2.y", value="2") + self.expect_var_path("multi2.n", value="7") diff --git a/lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp b/lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp new file mode 100644 index 0000000000000..a9288e6466e74 --- /dev/null +++ b/lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp @@ -0,0 +1,52 @@ +int main(int argc, char **argv) { + struct A { + struct { + int x = 1; + }; + int y = 2; + } a; + + struct B { + // Anonymous struct inherits another struct. + struct : public A { + int z = 3; + }; + int w = 4; + A a; + } b; + + + struct EmptyBase { + }; + + struct : public A { + struct { + int z = 13; + }; + } unnamed_derived; + + struct DerivedB : public B { + struct { + // `w` in anonymous struct shadows `w` from `B`. + int w = 14; + int k = 15; + }; + } derb; + + struct MultiBase : public EmptyBase, public A { + struct { + int m = 16; + int y = 30; + }; + } multi1; + + struct MB2 : public B, EmptyBase, public A { + int i = 42; + struct { + int w = 23; + int n = 7; + }; + } multi2; + + return 0; // Set breakpoint here +} From lldb-commits at lists.llvm.org Wed May 7 20:39:43 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 20:39:43 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Fix GetIndexOfChildMemberWithName to handle anonymous structs. (PR #138487) In-Reply-To: Message-ID: <681c277f.170a0220.151c6a.c89a@mx.google.com> https://github.com/cmtice closed https://github.com/llvm/llvm-project/pull/138487 From lldb-commits at lists.llvm.org Wed May 7 20:40:07 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 20:40:07 -0700 (PDT) Subject: [Lldb-commits] [lldb] efce7a1 - [lldb-dap] Re-enable the lldb-dap tests (#138791) Message-ID: <681c2797.050a0220.1c65cf.5bf4@mx.google.com> Author: Jonas Devlieghere Date: 2025-05-07T20:40:04-07:00 New Revision: efce7a169e58ec8b27d266ec4dfb851f85a7c6c2 URL: https://github.com/llvm/llvm-project/commit/efce7a169e58ec8b27d266ec4dfb851f85a7c6c2 DIFF: https://github.com/llvm/llvm-project/commit/efce7a169e58ec8b27d266ec4dfb851f85a7c6c2.diff LOG: [lldb-dap] Re-enable the lldb-dap tests (#138791) Re-enable the lldb-dap tests. We've spent the last week improving the reliability of the test suite and the tests now pass reliably on macOS and Linux at desk. Let's see how things fare on the bots. Added: Modified: lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_breakpointLocations.py lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py lldb/test/API/tools/lldb-dap/disassemble/TestDAP_disassemble.py lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py Removed: ################################################################################ diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py index 741c011a3d692..b5569642f9d34 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py @@ -24,7 +24,6 @@ def spawn_and_wait(program, delay): process.wait() - at skip class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase): def set_and_hit_breakpoint(self, continueToExit=True): self.dap_server.wait_for_stopped() diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py index 7250e67ebcd8c..7c2b540195d15 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py @@ -18,6 +18,7 @@ import socket + at skip("https://github.com/llvm/llvm-project/issues/138803") class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase): default_timeout = 20 diff --git a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_breakpointLocations.py b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_breakpointLocations.py index 4a99cacc761a3..1058157e2c668 100644 --- a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_breakpointLocations.py +++ b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_breakpointLocations.py @@ -11,8 +11,7 @@ import lldbdap_testcase import os -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip + class TestDAP_breakpointLocations(lldbdap_testcase.DAPTestCaseBase): def setUp(self): lldbdap_testcase.DAPTestCaseBase.setUp(self) diff --git a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py index 6c6681804f250..26df2573555df 100644 --- a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py +++ b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py @@ -11,8 +11,7 @@ import lldbdap_testcase import os -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip + class TestDAP_setBreakpoints(lldbdap_testcase.DAPTestCaseBase): def setUp(self): lldbdap_testcase.DAPTestCaseBase.setUp(self) diff --git a/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py b/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py index 4aecf9a665c06..223258fbdd3dc 100644 --- a/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py +++ b/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py @@ -6,8 +6,6 @@ from lldbsuite.test.decorators import * -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip class TestDAP_commands(lldbdap_testcase.DAPTestCaseBase): def test_command_directive_quiet_on_success(self): program = self.getBuildArtifact("a.out") diff --git a/lldb/test/API/tools/lldb-dap/disassemble/TestDAP_disassemble.py b/lldb/test/API/tools/lldb-dap/disassemble/TestDAP_disassemble.py index ebecb349ac177..9e8ef5b289f2e 100644 --- a/lldb/test/API/tools/lldb-dap/disassemble/TestDAP_disassemble.py +++ b/lldb/test/API/tools/lldb-dap/disassemble/TestDAP_disassemble.py @@ -10,8 +10,7 @@ import lldbdap_testcase import os -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip + class TestDAP_disassemble(lldbdap_testcase.DAPTestCaseBase): @skipIfWindows def test_disassemble(self): diff --git a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py index 19b682dfcd22d..372a9bb75e007 100644 --- a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py +++ b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py @@ -11,8 +11,6 @@ from lldbsuite.test.lldbtest import * -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase): def assertEvaluate(self, expression, regex): self.assertRegex( diff --git a/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py b/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py index c71ba871b8a22..ea43fccf016a7 100644 --- a/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py +++ b/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py @@ -10,8 +10,7 @@ import lldbdap_testcase import os -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip + class TestDAP_memory(lldbdap_testcase.DAPTestCaseBase): def test_memory_refs_variables(self): """ diff --git a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py index 560207bfbb66c..3b45cdc245838 100644 --- a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py +++ b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py @@ -17,8 +17,7 @@ def make_buffer_verify_dict(start_idx, count, offset=0): verify_dict["[%i]" % (i)] = {"type": "int", "value": str(i + offset)} return verify_dict -# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. - at skip + class TestDAP_variables(lldbdap_testcase.DAPTestCaseBase): def verify_values(self, verify_dict, actual, varref_dict=None, expression=None): if "equals" in verify_dict: From lldb-commits at lists.llvm.org Wed May 7 20:40:11 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Wed, 07 May 2025 20:40:11 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Re-enable the lldb-dap tests (PR #138791) In-Reply-To: Message-ID: <681c279b.170a0220.a7366.3ef0@mx.google.com> https://github.com/JDevlieghere closed https://github.com/llvm/llvm-project/pull/138791 From lldb-commits at lists.llvm.org Wed May 7 21:14:21 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Wed, 07 May 2025 21:14:21 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681c2f9d.050a0220.ad00a.9059@mx.google.com> jasonmolenda wrote: FTR that test is skipped on darwin because _sigtramp in libc on aarch64 doesn't have any hand-supplied eh_frame right now :/ I have a test case put together, with a few functions written in assembly with hand-added cfi directives. It's probably darwin specific assembly, obviously aarch64 only, but it exercises exactly the bug I am fixing. will wrap it up into a proper test in a tiny bit (hopefully tonight) https://github.com/llvm/llvm-project/pull/138805 From lldb-commits at lists.llvm.org Wed May 7 21:52:53 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 21:52:53 -0700 (PDT) Subject: [Lldb-commits] [lldb] Extending LLDB to work on AIX (PR #102601) In-Reply-To: Message-ID: <681c38a5.170a0220.4509c.4a8e@mx.google.com> https://github.com/ravindra-shinde2 updated https://github.com/llvm/llvm-project/pull/102601 >From 39d395f75c306a0d932a783eef039fd93d66e246 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Wed, 7 Aug 2024 12:10:43 -0500 Subject: [PATCH 01/47] LLDB Support for AIX --- clang/lib/CodeGen/CGObjCMac.cpp | 6 +- lldb/CMakeLists.txt | 4 + lldb/cmake/modules/LLDBConfig.cmake | 2 +- lldb/include/lldb/Core/Module.h | 3 + lldb/include/lldb/Core/ModuleSpec.h | 23 +- lldb/include/lldb/Host/HostGetOpt.h | 2 +- lldb/include/lldb/Host/HostInfo.h | 3 + lldb/include/lldb/Host/HostInfoBase.h | 2 +- lldb/include/lldb/Host/XML.h | 5 + lldb/include/lldb/Host/aix/AbstractSocket.h | 25 + lldb/include/lldb/Host/aix/Host.h | 22 + lldb/include/lldb/Host/aix/HostInfoAIX.h | 42 + lldb/include/lldb/Host/aix/Ptrace.h | 62 + lldb/include/lldb/Host/aix/Support.h | 29 + lldb/include/lldb/Host/aix/Uio.h | 23 + lldb/include/lldb/Host/common/GetOptInc.h | 6 +- lldb/include/lldb/Symbol/ObjectFile.h | 5 + lldb/include/lldb/Target/ABI.h | 6 + lldb/include/lldb/Target/DynamicLoader.h | 6 + lldb/include/lldb/Target/Process.h | 14 + .../lldb/Target/RegisterContextUnwind.h | 4 + .../lldb/Target/ThreadPlanCallFunction.h | 6 + .../lldb/Utility/StringExtractorGDBRemote.h | 1 + lldb/include/lldb/lldb-private-enumerations.h | 1 + lldb/source/API/CMakeLists.txt | 108 + lldb/source/API/SBBreakpoint.cpp | 6 +- lldb/source/API/SBBreakpointLocation.cpp | 6 +- lldb/source/API/SBBreakpointName.cpp | 4 +- lldb/source/Core/DynamicLoader.cpp | 10 + lldb/source/Core/Mangled.cpp | 2 + lldb/source/Core/Module.cpp | 12 + lldb/source/Core/Section.cpp | 4 + lldb/source/Expression/DWARFExpression.cpp | 10 +- lldb/source/Host/CMakeLists.txt | 13 + lldb/source/Host/aix/AbstractSocket.cpp | 21 + lldb/source/Host/aix/Host.cpp | 304 +++ lldb/source/Host/aix/HostInfoAIX.cpp | 215 ++ lldb/source/Host/aix/Support.cpp | 44 + lldb/source/Host/common/GetOptInc.cpp | 2 +- lldb/source/Host/common/Host.cpp | 180 +- .../source/Host/common/LICENSE.aix-netbsd.txt | 125 + lldb/source/Host/common/XML.cpp | 3 + .../posix/ConnectionFileDescriptorPosix.cpp | 2 + lldb/source/Host/posix/FileSystemPosix.cpp | 2 + lldb/source/Host/posix/MainLoopPosix.cpp | 17 + .../Host/posix/ProcessLauncherPosixFork.cpp | 5 + lldb/source/Initialization/CMakeLists.txt | 2 +- .../SystemInitializerCommon.cpp | 4 +- .../Plugins/ABI/PowerPC/ABISysV_ppc64.cpp | 131 +- .../Plugins/ABI/PowerPC/ABISysV_ppc64.h | 6 + .../DynamicLoader/AIX-DYLD/CMakeLists.txt | 11 + .../AIX-DYLD/DynamicLoaderAIXDYLD.cpp | 272 +++ .../AIX-DYLD/DynamicLoaderAIXDYLD.h | 55 + .../Plugins/DynamicLoader/CMakeLists.txt | 1 + .../DynamicLoaderDarwinKernel.cpp | 4 +- .../MacOSX-DYLD/DynamicLoaderDarwin.cpp | 2 +- .../PPC64/EmulateInstructionPPC64.cpp | 196 +- .../PPC64/EmulateInstructionPPC64.h | 14 + ...nstrumentationRuntimeMainThreadChecker.cpp | 2 +- .../TSan/InstrumentationRuntimeTSan.cpp | 14 +- .../UBSan/InstrumentationRuntimeUBSan.cpp | 2 +- .../Plugins/JITLoader/GDB/JITLoaderGDB.cpp | 4 + lldb/source/Plugins/Language/ObjC/Cocoa.cpp | 2 + .../MemoryHistory/asan/MemoryHistoryASan.cpp | 2 +- .../BSD-Archive/ObjectContainerBSDArchive.cpp | 2 +- .../Big-Archive/CMakeLists.txt | 10 + .../Big-Archive/ObjectContainerBigArchive.cpp | 522 +++++ .../Big-Archive/ObjectContainerBigArchive.h | 177 ++ .../Plugins/ObjectContainer/CMakeLists.txt | 1 + lldb/source/Plugins/ObjectFile/CMakeLists.txt | 1 + .../ObjectFile/Mach-O/ObjectFileMachO.cpp | 6 +- .../Minidump/ObjectFileMinidump.cpp | 2 + .../Plugins/ObjectFile/PDB/ObjectFilePDB.cpp | 15 +- .../ObjectFile/PECOFF/ObjectFilePECOFF.cpp | 18 +- .../Plugins/ObjectFile/XCOFF/CMakeLists.txt | 13 + .../ObjectFile/XCOFF/ObjectFileXCOFF.cpp | 780 +++++++ .../ObjectFile/XCOFF/ObjectFileXCOFF.h | 243 ++ .../Python/OperatingSystemPython.cpp | 2 +- .../Plugins/Platform/AIX/CMakeLists.txt | 13 + .../Plugins/Platform/AIX/PlatformAIX.cpp | 471 ++++ .../source/Plugins/Platform/AIX/PlatformAIX.h | 74 + lldb/source/Plugins/Platform/CMakeLists.txt | 1 + .../source/Plugins/Process/AIX/CMakeLists.txt | 19 + .../Plugins/Process/AIX/NativeProcessAIX.cpp | 2048 +++++++++++++++++ .../Plugins/Process/AIX/NativeProcessAIX.h | 283 +++ .../Process/AIX/NativeRegisterContextAIX.cpp | 157 ++ .../Process/AIX/NativeRegisterContextAIX.h | 133 ++ .../AIX/NativeRegisterContextAIX_ppc64.cpp | 744 ++++++ .../AIX/NativeRegisterContextAIX_ppc64.h | 138 ++ .../Plugins/Process/AIX/NativeThreadAIX.cpp | 526 +++++ .../Plugins/Process/AIX/NativeThreadAIX.h | 126 + lldb/source/Plugins/Process/CMakeLists.txt | 3 + .../Process/Utility/InferiorCallPOSIX.cpp | 33 + .../Utility/RegisterInfoPOSIX_ppc64le.cpp | 4 + .../Plugins/Process/Utility/ThreadMemory.cpp | 2 +- .../Plugins/Process/gdb-remote/CMakeLists.txt | 5 + .../GDBRemoteCommunicationClient.cpp | 30 + .../gdb-remote/GDBRemoteCommunicationClient.h | 7 + .../GDBRemoteCommunicationServerLLGS.cpp | 28 + .../GDBRemoteCommunicationServerLLGS.h | 2 + .../Process/gdb-remote/ProcessGDBRemote.cpp | 13 +- .../Process/gdb-remote/ProcessGDBRemote.h | 8 + .../Process/mach-core/ProcessMachCore.cpp | 8 +- .../ScriptInterpreter/Python/CMakeLists.txt | 5 + .../SymbolFile/DWARF/DWARFFormValue.cpp | 4 + .../Plugins/SymbolFile/DWARF/DWARFUnit.cpp | 12 +- .../MacOSX/AppleGetThreadItemInfoHandler.cpp | 2 +- lldb/source/Symbol/DWARFCallFrameInfo.cpp | 4 +- lldb/source/Target/ABI.cpp | 9 + lldb/source/Target/CMakeLists.txt | 5 + lldb/source/Target/Process.cpp | 10 + lldb/source/Target/RegisterContextUnwind.cpp | 46 + lldb/source/Target/ThreadPlanCallFunction.cpp | 34 + lldb/source/Target/UnwindLLDB.cpp | 15 + lldb/source/Utility/ArchSpec.cpp | 18 +- .../Utility/StringExtractorGDBRemote.cpp | 2 + lldb/test/CMakeLists.txt | 2 +- lldb/test/Shell/Expr/TestIRMemoryMap.test | 2 +- lldb/test/Shell/Process/TestEnvironment.test | 2 +- lldb/tools/driver/CMakeLists.txt | 5 + lldb/tools/driver/Driver.cpp | 5 +- lldb/tools/lldb-dap/CMakeLists.txt | 4 + lldb/tools/lldb-server/CMakeLists.txt | 7 + .../lldb-server/SystemInitializerLLGS.cpp | 15 + lldb/tools/lldb-server/lldb-gdbserver.cpp | 4 + lldb/unittests/Host/FileSystemTest.cpp | 2 +- lldb/unittests/Host/posix/TerminalTest.cpp | 4 + llvm/include/llvm/Object/XCOFFObjectFile.h | 4 +- llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp | 15 +- 129 files changed, 8950 insertions(+), 76 deletions(-) create mode 100644 lldb/include/lldb/Host/aix/AbstractSocket.h create mode 100644 lldb/include/lldb/Host/aix/Host.h create mode 100644 lldb/include/lldb/Host/aix/HostInfoAIX.h create mode 100644 lldb/include/lldb/Host/aix/Ptrace.h create mode 100644 lldb/include/lldb/Host/aix/Support.h create mode 100644 lldb/include/lldb/Host/aix/Uio.h create mode 100644 lldb/source/Host/aix/AbstractSocket.cpp create mode 100644 lldb/source/Host/aix/Host.cpp create mode 100644 lldb/source/Host/aix/HostInfoAIX.cpp create mode 100644 lldb/source/Host/aix/Support.cpp create mode 100644 lldb/source/Host/common/LICENSE.aix-netbsd.txt create mode 100644 lldb/source/Plugins/DynamicLoader/AIX-DYLD/CMakeLists.txt create mode 100644 lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp create mode 100644 lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h create mode 100644 lldb/source/Plugins/ObjectContainer/Big-Archive/CMakeLists.txt create mode 100644 lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.cpp create mode 100644 lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.h create mode 100644 lldb/source/Plugins/ObjectFile/XCOFF/CMakeLists.txt create mode 100644 lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp create mode 100644 lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.h create mode 100644 lldb/source/Plugins/Platform/AIX/CMakeLists.txt create mode 100644 lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp create mode 100644 lldb/source/Plugins/Platform/AIX/PlatformAIX.h create mode 100644 lldb/source/Plugins/Process/AIX/CMakeLists.txt create mode 100644 lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp create mode 100644 lldb/source/Plugins/Process/AIX/NativeProcessAIX.h create mode 100644 lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.cpp create mode 100644 lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.h create mode 100644 lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.cpp create mode 100644 lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.h create mode 100644 lldb/source/Plugins/Process/AIX/NativeThreadAIX.cpp create mode 100644 lldb/source/Plugins/Process/AIX/NativeThreadAIX.h diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index 30f3911a8b03c..fc91981db68c1 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -5052,10 +5052,14 @@ std::string CGObjCCommonMac::GetSectionName(StringRef Section, case llvm::Triple::COFF: assert(Section.starts_with("__") && "expected the name to begin with __"); return ("." + Section.substr(2) + "$B").str(); + case llvm::Triple::XCOFF: + // Hack to allow "p 10+1" on AIX for lldb + assert(Section.substr(0, 2) == "__" && + "expected the name to begin with __"); + return Section.substr(2).str(); case llvm::Triple::Wasm: case llvm::Triple::GOFF: case llvm::Triple::SPIRV: - case llvm::Triple::XCOFF: case llvm::Triple::DXContainer: llvm::report_fatal_error( "Objective-C support is unimplemented for object file format"); diff --git a/lldb/CMakeLists.txt b/lldb/CMakeLists.txt index 59cdc4593463c..2e9ae0d0b3221 100644 --- a/lldb/CMakeLists.txt +++ b/lldb/CMakeLists.txt @@ -38,6 +38,10 @@ endif() include(LLDBConfig) include(AddLLDB) +if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX") + add_definitions("-D__AIX__") +endif() + # Define the LLDB_CONFIGURATION_xxx matching the build type. if(uppercase_CMAKE_BUILD_TYPE STREQUAL "DEBUG" ) add_definitions(-DLLDB_CONFIGURATION_DEBUG) diff --git a/lldb/cmake/modules/LLDBConfig.cmake b/lldb/cmake/modules/LLDBConfig.cmake index a60921990cf77..a0f118a11984c 100644 --- a/lldb/cmake/modules/LLDBConfig.cmake +++ b/lldb/cmake/modules/LLDBConfig.cmake @@ -299,7 +299,7 @@ endif() # Figure out if lldb could use lldb-server. If so, then we'll # ensure we build lldb-server when an lldb target is being built. -if (CMAKE_SYSTEM_NAME MATCHES "Android|Darwin|FreeBSD|Linux|NetBSD|Windows") +if (CMAKE_SYSTEM_NAME MATCHES "Android|Darwin|FreeBSD|Linux|NetBSD|Windows|AIX") set(LLDB_CAN_USE_LLDB_SERVER ON) else() set(LLDB_CAN_USE_LLDB_SERVER OFF) diff --git a/lldb/include/lldb/Core/Module.h b/lldb/include/lldb/Core/Module.h index 5589c1c9a350d..3829386562795 100644 --- a/lldb/include/lldb/Core/Module.h +++ b/lldb/include/lldb/Core/Module.h @@ -196,6 +196,9 @@ class Module : public std::enable_shared_from_this, bool SetLoadAddress(Target &target, lldb::addr_t value, bool value_is_offset, bool &changed); + bool SetLoadAddressByType(Target &target, lldb::addr_t value, + bool value_is_offset, bool &changed, int type_id); + /// \copydoc SymbolContextScope::CalculateSymbolContext(SymbolContext*) /// /// \see SymbolContextScope diff --git a/lldb/include/lldb/Core/ModuleSpec.h b/lldb/include/lldb/Core/ModuleSpec.h index 4cbbbfa8a26e1..4fe06412b6b0b 100644 --- a/lldb/include/lldb/Core/ModuleSpec.h +++ b/lldb/include/lldb/Core/ModuleSpec.h @@ -21,6 +21,7 @@ #include #include +#include namespace lldb_private { @@ -41,8 +42,26 @@ class ModuleSpec { } ModuleSpec(const FileSpec &file_spec, const ArchSpec &arch) - : m_file(file_spec), m_arch(arch), m_object_offset(0), - m_object_size(FileSystem::Instance().GetByteSize(file_spec)) {} + : m_arch(arch), m_object_offset(0) { + // parse object inside module format for example: /usr/ccs/lib/libc.a(shr_64.o) + llvm::SmallString<256> path_with_object; + file_spec.GetPath(path_with_object); + if (strstr(path_with_object.c_str(), "(") != nullptr) { + char *part; + char *str = (char *)path_with_object.c_str(); + part = strtok(str, "()"); + assert(part); + llvm::StringRef file_name(part); + part = strtok(nullptr, "()"); + assert(part); + m_object_name = ConstString(part); + m_file = FileSpec(file_name); + m_object_size = FileSystem::Instance().GetByteSize(m_file); + } else { + m_file = file_spec; + m_object_size = FileSystem::Instance().GetByteSize(file_spec); + } + } FileSpec *GetFileSpecPtr() { return (m_file ? &m_file : nullptr); } diff --git a/lldb/include/lldb/Host/HostGetOpt.h b/lldb/include/lldb/Host/HostGetOpt.h index 52cfdf4dbb89c..f450e561d6afb 100644 --- a/lldb/include/lldb/Host/HostGetOpt.h +++ b/lldb/include/lldb/Host/HostGetOpt.h @@ -9,7 +9,7 @@ #ifndef LLDB_HOST_HOSTGETOPT_H #define LLDB_HOST_HOSTGETOPT_H -#if !defined(_MSC_VER) && !defined(__NetBSD__) +#if !defined(_MSC_VER) && !defined(__NetBSD__) && !defined(__AIX__) #include #include diff --git a/lldb/include/lldb/Host/HostInfo.h b/lldb/include/lldb/Host/HostInfo.h index b7010d69d88e7..156df8cf6901d 100644 --- a/lldb/include/lldb/Host/HostInfo.h +++ b/lldb/include/lldb/Host/HostInfo.h @@ -55,6 +55,9 @@ #elif defined(__APPLE__) #include "lldb/Host/macosx/HostInfoMacOSX.h" #define HOST_INFO_TYPE HostInfoMacOSX +#elif defined(__AIX__) +#include "lldb/Host/aix/HostInfoAIX.h" +#define HOST_INFO_TYPE HostInfoAIX #else #include "lldb/Host/posix/HostInfoPosix.h" #define HOST_INFO_TYPE HostInfoPosix diff --git a/lldb/include/lldb/Host/HostInfoBase.h b/lldb/include/lldb/Host/HostInfoBase.h index 705aad559f3b7..29e6acf39bfb2 100644 --- a/lldb/include/lldb/Host/HostInfoBase.h +++ b/lldb/include/lldb/Host/HostInfoBase.h @@ -149,6 +149,7 @@ class HostInfoBase { return {}; } + static bool ComputeSharedLibraryDirectory(FileSpec &file_spec); /// Returns the distribution id of the host /// /// This will be something like "ubuntu", "fedora", etc. on Linux. @@ -158,7 +159,6 @@ class HostInfoBase { static llvm::StringRef GetDistributionId() { return llvm::StringRef(); } protected: - static bool ComputeSharedLibraryDirectory(FileSpec &file_spec); static bool ComputeSupportExeDirectory(FileSpec &file_spec); static bool ComputeProcessTempFileDirectory(FileSpec &file_spec); static bool ComputeGlobalTempFileDirectory(FileSpec &file_spec); diff --git a/lldb/include/lldb/Host/XML.h b/lldb/include/lldb/Host/XML.h index da0f9cd7aa8c0..cf359f7726d5d 100644 --- a/lldb/include/lldb/Host/XML.h +++ b/lldb/include/lldb/Host/XML.h @@ -11,6 +11,11 @@ #include "lldb/Host/Config.h" +#if defined(__AIX__) +//FIXME for AIX +#undef LLDB_ENABLE_LIBXML2 +#endif + #if LLDB_ENABLE_LIBXML2 #include #endif diff --git a/lldb/include/lldb/Host/aix/AbstractSocket.h b/lldb/include/lldb/Host/aix/AbstractSocket.h new file mode 100644 index 0000000000000..78a567a6b9095 --- /dev/null +++ b/lldb/include/lldb/Host/aix/AbstractSocket.h @@ -0,0 +1,25 @@ +//===-- AbstractSocket.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AbstractSocket_h_ +#define liblldb_AbstractSocket_h_ + +#include "lldb/Host/posix/DomainSocket.h" + +namespace lldb_private { +class AbstractSocket : public DomainSocket { +public: + AbstractSocket(bool child_processes_inherit); + +protected: + size_t GetNameOffset() const override; + void DeleteSocketFile(llvm::StringRef name) override; +}; +} + +#endif // ifndef liblldb_AbstractSocket_h_ diff --git a/lldb/include/lldb/Host/aix/Host.h b/lldb/include/lldb/Host/aix/Host.h new file mode 100644 index 0000000000000..1e3487752995f --- /dev/null +++ b/lldb/include/lldb/Host/aix/Host.h @@ -0,0 +1,22 @@ +//===-- Host.h --------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_HOST_AIX_HOST_H +#define LLDB_HOST_AIX_HOST_H + +#include "lldb/lldb-types.h" +#include + +namespace lldb_private { + +// Get PID (i.e. the primary thread ID) corresponding to the specified TID. +std::optional getPIDForTID(lldb::pid_t tid); + +} // namespace lldb_private + +#endif // #ifndef LLDB_HOST_AIX_HOST_H diff --git a/lldb/include/lldb/Host/aix/HostInfoAIX.h b/lldb/include/lldb/Host/aix/HostInfoAIX.h new file mode 100644 index 0000000000000..ced4cf34d38a8 --- /dev/null +++ b/lldb/include/lldb/Host/aix/HostInfoAIX.h @@ -0,0 +1,42 @@ +//===-- HostInfoAIX.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_Host_aix_HostInfoAIX_h_ +#define lldb_Host_aix_HostInfoAIX_h_ + +#include "lldb/Host/posix/HostInfoPosix.h" +#include "lldb/Utility/FileSpec.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/VersionTuple.h" + +#include + +namespace lldb_private { + +class HostInfoAIX : public HostInfoPosix { + friend class HostInfoBase; + +public: + static void Initialize(SharedLibraryDirectoryHelper *helper = nullptr); + static void Terminate(); + + static llvm::VersionTuple GetOSVersion(); + static std::optional GetOSBuildString(); + static llvm::StringRef GetDistributionId(); + static FileSpec GetProgramFileSpec(); + +protected: + static bool ComputeSupportExeDirectory(FileSpec &file_spec); + static bool ComputeSystemPluginsDirectory(FileSpec &file_spec); + static bool ComputeUserPluginsDirectory(FileSpec &file_spec); + static void ComputeHostArchitectureSupport(ArchSpec &arch_32, + ArchSpec &arch_64); +}; +} + +#endif diff --git a/lldb/include/lldb/Host/aix/Ptrace.h b/lldb/include/lldb/Host/aix/Ptrace.h new file mode 100644 index 0000000000000..88928f18102d7 --- /dev/null +++ b/lldb/include/lldb/Host/aix/Ptrace.h @@ -0,0 +1,62 @@ +//===-- Ptrace.h ------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// This file defines ptrace functions & structures + +#ifndef liblldb_Host_aix_Ptrace_h_ +#define liblldb_Host_aix_Ptrace_h_ + +#include + +#define DEBUG_PTRACE_MAXBYTES 20 + +// Support ptrace extensions even when compiled without required kernel support +#ifndef PTRACE_GETREGS +#define PTRACE_GETREGS (PT_COMMAND_MAX+1) +#endif +#ifndef PTRACE_SETREGS +#define PTRACE_SETREGS (PT_COMMAND_MAX+2) +#endif +#ifndef PTRACE_GETFPREGS +#define PTRACE_GETFPREGS (PT_COMMAND_MAX+3) +#endif +#ifndef PTRACE_SETFPREGS +#define PTRACE_SETFPREGS (PT_COMMAND_MAX+4) +#endif +#ifndef PTRACE_GETREGSET +#define PTRACE_GETREGSET 0x4204 +#endif +#ifndef PTRACE_SETREGSET +#define PTRACE_SETREGSET 0x4205 +#endif +#ifndef PTRACE_GET_THREAD_AREA +#define PTRACE_GET_THREAD_AREA (PT_COMMAND_MAX+5) +#endif +#ifndef PTRACE_ARCH_PRCTL +#define PTRACE_ARCH_PRCTL (PT_COMMAND_MAX+6) +#endif +#ifndef ARCH_GET_FS +#define ARCH_SET_GS 0x1001 +#define ARCH_SET_FS 0x1002 +#define ARCH_GET_FS 0x1003 +#define ARCH_GET_GS 0x1004 +#endif +#ifndef PTRACE_PEEKMTETAGS +#define PTRACE_PEEKMTETAGS (PT_COMMAND_MAX+7) +#endif +#ifndef PTRACE_POKEMTETAGS +#define PTRACE_POKEMTETAGS (PT_COMMAND_MAX+8) +#endif +#ifndef PTRACE_GETVRREGS +#define PTRACE_GETVRREGS (PT_COMMAND_MAX+9) +#endif +#ifndef PTRACE_GETVSRREGS +#define PTRACE_GETVSRREGS (PT_COMMAND_MAX+10) +#endif + +#endif // liblldb_Host_aix_Ptrace_h_ diff --git a/lldb/include/lldb/Host/aix/Support.h b/lldb/include/lldb/Host/aix/Support.h new file mode 100644 index 0000000000000..27d6c2b50a35b --- /dev/null +++ b/lldb/include/lldb/Host/aix/Support.h @@ -0,0 +1,29 @@ +//===-- Support.h -----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_HOST_AIX_SUPPORT_H +#define LLDB_HOST_AIX_SUPPORT_H + +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/MemoryBuffer.h" +#include + +namespace lldb_private { + +llvm::ErrorOr> +getProcFile(::pid_t pid, ::pid_t tid, const llvm::Twine &file); + +llvm::ErrorOr> +getProcFile(::pid_t pid, const llvm::Twine &file); + +llvm::ErrorOr> +getProcFile(const llvm::Twine &file); + +} // namespace lldb_private + +#endif // #ifndef LLDB_HOST_AIX_SUPPORT_H diff --git a/lldb/include/lldb/Host/aix/Uio.h b/lldb/include/lldb/Host/aix/Uio.h new file mode 100644 index 0000000000000..acf79ecc6a1d0 --- /dev/null +++ b/lldb/include/lldb/Host/aix/Uio.h @@ -0,0 +1,23 @@ +//===-- Uio.h ---------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Host_aix_Uio_h_ +#define liblldb_Host_aix_Uio_h_ + +#include "lldb/Host/Config.h" +#include + +// We shall provide our own implementation of process_vm_readv if it is not +// present +#if !HAVE_PROCESS_VM_READV +ssize_t process_vm_readv(::pid_t pid, const struct iovec *local_iov, + unsigned long liovcnt, const struct iovec *remote_iov, + unsigned long riovcnt, unsigned long flags); +#endif + +#endif // liblldb_Host_aix_Uio_h_ diff --git a/lldb/include/lldb/Host/common/GetOptInc.h b/lldb/include/lldb/Host/common/GetOptInc.h index 3fb9add479541..ebb475bfaf6b8 100644 --- a/lldb/include/lldb/Host/common/GetOptInc.h +++ b/lldb/include/lldb/Host/common/GetOptInc.h @@ -11,11 +11,11 @@ #include "lldb/lldb-defines.h" -#if defined(_MSC_VER) +#if defined(_MSC_VER) || defined(__AIX__) #define REPLACE_GETOPT #define REPLACE_GETOPT_LONG #endif -#if defined(_MSC_VER) || defined(__NetBSD__) +#if defined(_MSC_VER) || defined(__NetBSD__) || defined(__AIX__) #define REPLACE_GETOPT_LONG_ONLY #endif @@ -35,7 +35,7 @@ struct option { int val; }; -int getopt(int argc, char *const argv[], const char *optstring); +int getopt(int argc, char *const argv[], const char *optstring) throw(); // from getopt.h extern char *optarg; diff --git a/lldb/include/lldb/Symbol/ObjectFile.h b/lldb/include/lldb/Symbol/ObjectFile.h index 8592323322e38..bf66ccec263d2 100644 --- a/lldb/include/lldb/Symbol/ObjectFile.h +++ b/lldb/include/lldb/Symbol/ObjectFile.h @@ -401,6 +401,11 @@ class ObjectFile : public std::enable_shared_from_this, return false; } + virtual bool SetLoadAddressByType(Target &target, lldb::addr_t value, + bool value_is_offset, int type_id) { + return false; + } + /// Gets whether endian swapping should occur when extracting data from this /// object file. /// diff --git a/lldb/include/lldb/Target/ABI.h b/lldb/include/lldb/Target/ABI.h index 7b646d743346b..281a89951ef88 100644 --- a/lldb/include/lldb/Target/ABI.h +++ b/lldb/include/lldb/Target/ABI.h @@ -47,6 +47,12 @@ class ABI : public PluginInterface { lldb::addr_t returnAddress, llvm::ArrayRef args) const = 0; + virtual bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t tocAddress, + lldb::addr_t returnAddress, + llvm::ArrayRef args) const; + // Prepare trivial call used from ThreadPlanFunctionCallUsingABI // AD: // . Because i don't want to change other ABI's this is not declared pure diff --git a/lldb/include/lldb/Target/DynamicLoader.h b/lldb/include/lldb/Target/DynamicLoader.h index 0629e2faae7e9..7dccd317c2dca 100644 --- a/lldb/include/lldb/Target/DynamicLoader.h +++ b/lldb/include/lldb/Target/DynamicLoader.h @@ -359,6 +359,12 @@ class DynamicLoader : public PluginInterface { lldb::addr_t base_addr, bool base_addr_is_offset); + virtual void UpdateLoadedSectionsByType(lldb::ModuleSP module, + lldb::addr_t link_map_addr, + lldb::addr_t base_addr, + bool base_addr_is_offset, + int type_id); + // Utility method so base classes can share implementation of // UpdateLoadedSections void UpdateLoadedSectionsCommon(lldb::ModuleSP module, lldb::addr_t base_addr, diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index cf16fbc812aa4..886ca766112c8 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -63,6 +63,10 @@ #include "llvm/Support/Threading.h" #include "llvm/Support/VersionTuple.h" +#if defined(__AIX__) +struct ld_xinfo; +#endif + namespace lldb_private { template struct Range; @@ -1915,6 +1919,10 @@ class Process : public std::enable_shared_from_this, Status GetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo &range_info); +#if defined(__AIX__) + Status GetLDXINFO(struct ld_xinfo *info_ptr); +#endif + /// Obtain all the mapped memory regions within this process. /// /// \param[out] region_list @@ -2855,6 +2863,12 @@ void PruneThreadPlans(); return Status("Process::DoGetMemoryRegionInfo() not supported"); } +#if defined(__AIX__) + virtual Status DoGetLDXINFO(struct ld_xinfo *info_ptr) { + return Status("Process::DoGetLDXINFO() not supported"); + } +#endif + /// Provide an override value in the subclass for lldb's /// CPU-based logic for whether watchpoint exceptions are /// received before or after an instruction executes. diff --git a/lldb/include/lldb/Target/RegisterContextUnwind.h b/lldb/include/lldb/Target/RegisterContextUnwind.h index ef8ae88403866..00a95853800ed 100644 --- a/lldb/include/lldb/Target/RegisterContextUnwind.h +++ b/lldb/include/lldb/Target/RegisterContextUnwind.h @@ -67,6 +67,10 @@ class RegisterContextUnwind : public lldb_private::RegisterContext { bool ReadPC(lldb::addr_t &start_pc); +#ifdef __AIX__ + bool ReadLR(lldb::addr_t &lr); +#endif + // Indicates whether this frame *behaves* like frame zero -- the currently // executing frame -- or not. This can be true in the middle of the stack // above asynchronous trap handlers (sigtramp) for instance. diff --git a/lldb/include/lldb/Target/ThreadPlanCallFunction.h b/lldb/include/lldb/Target/ThreadPlanCallFunction.h index cb6e7caebb4ad..7880db1592e04 100644 --- a/lldb/include/lldb/Target/ThreadPlanCallFunction.h +++ b/lldb/include/lldb/Target/ThreadPlanCallFunction.h @@ -27,6 +27,12 @@ class ThreadPlanCallFunction : public ThreadPlan { llvm::ArrayRef args, const EvaluateExpressionOptions &options); + ThreadPlanCallFunction(Thread &thread, const Address &function, + const Address &toc, + const CompilerType &return_type, + llvm::ArrayRef args, + const EvaluateExpressionOptions &options); + ThreadPlanCallFunction(Thread &thread, const Address &function, const EvaluateExpressionOptions &options); diff --git a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h index dd468ef5bddef..9953bd6c24588 100644 --- a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h +++ b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h @@ -61,6 +61,7 @@ class StringExtractorGDBRemote : public StringExtractor { eServerPacketType_qQueryGDBServer, eServerPacketType_qKillSpawnedProcess, eServerPacketType_qLaunchSuccess, + eServerPacketType_qLDXINFO, eServerPacketType_qModuleInfo, eServerPacketType_qProcessInfoPID, eServerPacketType_qSpeedTest, diff --git a/lldb/include/lldb/lldb-private-enumerations.h b/lldb/include/lldb/lldb-private-enumerations.h index c24a3538f58da..98c1e956bf8f7 100644 --- a/lldb/include/lldb/lldb-private-enumerations.h +++ b/lldb/include/lldb/lldb-private-enumerations.h @@ -65,6 +65,7 @@ enum ArchitectureType { eArchTypeMachO, eArchTypeELF, eArchTypeCOFF, + eArchTypeXCOFF, kNumArchTypes }; diff --git a/lldb/source/API/CMakeLists.txt b/lldb/source/API/CMakeLists.txt index a32bc58507d8e..3ecdb11daef7d 100644 --- a/lldb/source/API/CMakeLists.txt +++ b/lldb/source/API/CMakeLists.txt @@ -40,6 +40,113 @@ add_custom_target(lldb-sbapi-dwarf-enums DEPENDS ${sb_languages_file}) set_target_properties(lldb-sbapi-dwarf-enums PROPERTIES FOLDER "LLDB/Tablegenning") +if(CMAKE_SYSTEM_NAME MATCHES "AIX") +add_lldb_library(liblldb STATIC ${option_framework} + SBAddress.cpp + SBAddressRange.cpp + SBAddressRangeList.cpp + SBAttachInfo.cpp + SBBlock.cpp + SBBreakpoint.cpp + SBBreakpointLocation.cpp + SBBreakpointName.cpp + SBBreakpointOptionCommon.cpp + SBBroadcaster.cpp + SBCommandInterpreter.cpp + SBCommandInterpreterRunOptions.cpp + SBCommandReturnObject.cpp + SBCommunication.cpp + SBCompileUnit.cpp + SBSaveCoreOptions.cpp + SBData.cpp + SBDebugger.cpp + SBDeclaration.cpp + SBEnvironment.cpp + SBError.cpp + SBEvent.cpp + SBExecutionContext.cpp + SBExpressionOptions.cpp + SBFileSpec.cpp + SBFile.cpp + SBFileSpecList.cpp + SBFormat.cpp + SBFrame.cpp + SBFunction.cpp + SBHostOS.cpp + SBInstruction.cpp + SBInstructionList.cpp + SBLanguageRuntime.cpp + SBLaunchInfo.cpp + SBLineEntry.cpp + SBListener.cpp + SBMemoryRegionInfo.cpp + SBMemoryRegionInfoList.cpp + SBModule.cpp + SBModuleSpec.cpp + SBPlatform.cpp + SBProcess.cpp + SBProcessInfo.cpp + SBProcessInfoList.cpp + SBQueue.cpp + SBQueueItem.cpp + SBReproducer.cpp + SBScriptObject.cpp + SBSection.cpp + SBSourceManager.cpp + SBStatisticsOptions.cpp + SBStream.cpp + SBStringList.cpp + SBStructuredData.cpp + SBSymbol.cpp + SBSymbolContext.cpp + SBSymbolContextList.cpp + SBTarget.cpp + SBThread.cpp + SBThreadCollection.cpp + SBThreadPlan.cpp + SBTrace.cpp + SBTraceCursor.cpp + SBType.cpp + SBTypeCategory.cpp + SBTypeEnumMember.cpp + SBTypeFilter.cpp + SBTypeFormat.cpp + SBTypeNameSpecifier.cpp + SBTypeSummary.cpp + SBTypeSynthetic.cpp + SBValue.cpp + SBValueList.cpp + SBVariablesOptions.cpp + SBWatchpoint.cpp + SBWatchpointOptions.cpp + SBUnixSignals.cpp + SystemInitializerFull.cpp + ${lldb_python_wrapper} + ${lldb_lua_wrapper} + + DEPENDS + lldb-sbapi-dwarf-enums + + LINK_LIBS + lldbBreakpoint + lldbCore + lldbDataFormatters + lldbExpression + lldbHost + lldbInitialization + lldbInterpreter + lldbSymbol + lldbTarget + lldbUtility + lldbVersion + ${LLDB_ALL_PLUGINS} + LINK_COMPONENTS + Support + + ${option_install_prefix} +) + +else() add_lldb_library(liblldb SHARED ${option_framework} SBAddress.cpp SBAddressRange.cpp @@ -144,6 +251,7 @@ add_lldb_library(liblldb SHARED ${option_framework} ${option_install_prefix} ) +endif() # lib/pythonX.Y/dist-packages/lldb/_lldb.so is a symlink to lib/liblldb.so, # which depends on lib/libLLVM*.so (BUILD_SHARED_LIBS) or lib/libLLVM-10git.so diff --git a/lldb/source/API/SBBreakpoint.cpp b/lldb/source/API/SBBreakpoint.cpp index 3d908047f9455..728fe04d14d92 100644 --- a/lldb/source/API/SBBreakpoint.cpp +++ b/lldb/source/API/SBBreakpoint.cpp @@ -342,7 +342,7 @@ uint32_t SBBreakpoint::GetIgnoreCount() const { return count; } -void SBBreakpoint::SetThreadID(tid_t tid) { +void SBBreakpoint::SetThreadID(lldb::tid_t tid) { LLDB_INSTRUMENT_VA(this, tid); BreakpointSP bkpt_sp = GetSP(); @@ -353,10 +353,10 @@ void SBBreakpoint::SetThreadID(tid_t tid) { } } -tid_t SBBreakpoint::GetThreadID() { +lldb::tid_t SBBreakpoint::GetThreadID() { LLDB_INSTRUMENT_VA(this); - tid_t tid = LLDB_INVALID_THREAD_ID; + lldb::tid_t tid = LLDB_INVALID_THREAD_ID; BreakpointSP bkpt_sp = GetSP(); if (bkpt_sp) { std::lock_guard guard( diff --git a/lldb/source/API/SBBreakpointLocation.cpp b/lldb/source/API/SBBreakpointLocation.cpp index 75b66364d4f1a..fad9a4076a54f 100644 --- a/lldb/source/API/SBBreakpointLocation.cpp +++ b/lldb/source/API/SBBreakpointLocation.cpp @@ -302,7 +302,7 @@ bool SBBreakpointLocation::GetCommandLineCommands(SBStringList &commands) { return has_commands; } -void SBBreakpointLocation::SetThreadID(tid_t thread_id) { +void SBBreakpointLocation::SetThreadID(lldb::tid_t thread_id) { LLDB_INSTRUMENT_VA(this, thread_id); BreakpointLocationSP loc_sp = GetSP(); @@ -313,10 +313,10 @@ void SBBreakpointLocation::SetThreadID(tid_t thread_id) { } } -tid_t SBBreakpointLocation::GetThreadID() { +lldb::tid_t SBBreakpointLocation::GetThreadID() { LLDB_INSTRUMENT_VA(this); - tid_t tid = LLDB_INVALID_THREAD_ID; + lldb::tid_t tid = LLDB_INVALID_THREAD_ID; BreakpointLocationSP loc_sp = GetSP(); if (loc_sp) { std::lock_guard guard( diff --git a/lldb/source/API/SBBreakpointName.cpp b/lldb/source/API/SBBreakpointName.cpp index 7f63aaf6fa7d5..5c7c0a8f6504b 100644 --- a/lldb/source/API/SBBreakpointName.cpp +++ b/lldb/source/API/SBBreakpointName.cpp @@ -347,7 +347,7 @@ bool SBBreakpointName::GetAutoContinue() { return bp_name->GetOptions().IsAutoContinue(); } -void SBBreakpointName::SetThreadID(tid_t tid) { +void SBBreakpointName::SetThreadID(lldb::tid_t tid) { LLDB_INSTRUMENT_VA(this, tid); BreakpointName *bp_name = GetBreakpointName(); @@ -361,7 +361,7 @@ void SBBreakpointName::SetThreadID(tid_t tid) { UpdateName(*bp_name); } -tid_t SBBreakpointName::GetThreadID() { +lldb::tid_t SBBreakpointName::GetThreadID() { LLDB_INSTRUMENT_VA(this); BreakpointName *bp_name = GetBreakpointName(); diff --git a/lldb/source/Core/DynamicLoader.cpp b/lldb/source/Core/DynamicLoader.cpp index 7758a87403b5a..ea43a7f98b69f 100644 --- a/lldb/source/Core/DynamicLoader.cpp +++ b/lldb/source/Core/DynamicLoader.cpp @@ -113,6 +113,16 @@ void DynamicLoader::UpdateLoadedSections(ModuleSP module, addr_t link_map_addr, UpdateLoadedSectionsCommon(module, base_addr, base_addr_is_offset); } +void DynamicLoader::UpdateLoadedSectionsByType(lldb::ModuleSP module, + lldb::addr_t link_map_addr, + lldb::addr_t base_addr, + bool base_addr_is_offset, + int type_id) { + bool changed; + module->SetLoadAddressByType(m_process->GetTarget(), base_addr, base_addr_is_offset, + changed, type_id); +} + void DynamicLoader::UpdateLoadedSectionsCommon(ModuleSP module, addr_t base_addr, bool base_addr_is_offset) { diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp index 387c4fac6b0f8..43c5b043ef7a2 100644 --- a/lldb/source/Core/Mangled.cpp +++ b/lldb/source/Core/Mangled.cpp @@ -167,12 +167,14 @@ static char *GetItaniumDemangledStr(const char *M) { "Expected demangled_size to return length including trailing null"); } +#if !defined(__AIX__) if (Log *log = GetLog(LLDBLog::Demangle)) { if (demangled_cstr) LLDB_LOGF(log, "demangled itanium: %s -> \"%s\"", M, demangled_cstr); else LLDB_LOGF(log, "demangled itanium: %s -> error: failed to demangle", M); } +#endif return demangled_cstr; } diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp index f9d7832254f46..044a5d29978e8 100644 --- a/lldb/source/Core/Module.cpp +++ b/lldb/source/Core/Module.cpp @@ -1510,6 +1510,18 @@ bool Module::SetLoadAddress(Target &target, lldb::addr_t value, return false; } +bool Module::SetLoadAddressByType(Target &target, lldb::addr_t value, + bool value_is_offset, bool &changed, int type_id) { + ObjectFile *object_file = GetObjectFile(); + if (object_file != nullptr) { + changed = object_file->SetLoadAddressByType(target, value, value_is_offset, type_id); + return true; + } else { + changed = false; + } + return false; +} + bool Module::MatchesModuleSpec(const ModuleSpec &module_ref) { const UUID &uuid = module_ref.GetUUID(); diff --git a/lldb/source/Core/Section.cpp b/lldb/source/Core/Section.cpp index 0763e88d4608f..9ed55853930a6 100644 --- a/lldb/source/Core/Section.cpp +++ b/lldb/source/Core/Section.cpp @@ -263,6 +263,10 @@ bool Section::ResolveContainedAddress(addr_t offset, Address &so_addr, bool Section::ContainsFileAddress(addr_t vm_addr) const { const addr_t file_addr = GetFileAddress(); +#ifdef __AIX__ + if (file_addr == 0) + return false; +#endif if (file_addr != LLDB_INVALID_ADDRESS && !IsThreadSpecific()) { if (file_addr <= vm_addr) { const addr_t offset = (vm_addr - file_addr) * m_target_byte_size; diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp index 444e44b392891..c1feec990f989 100644 --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -130,7 +130,7 @@ static llvm::Error ReadRegisterValueAsScalar(RegisterContext *reg_ctx, /// Return the length in bytes of the set of operands for \p op. No guarantees /// are made on the state of \p data after this call. -static offset_t GetOpcodeDataSize(const DataExtractor &data, +static lldb::offset_t GetOpcodeDataSize(const DataExtractor &data, const lldb::offset_t data_offset, const uint8_t op, const DWARFUnit *dwarf_cu) { lldb::offset_t offset = data_offset; @@ -358,7 +358,7 @@ lldb::addr_t DWARFExpression::GetLocation_DW_OP_addr(const DWARFUnit *dwarf_cu, error = true; break; } - const offset_t op_arg_size = + const lldb::offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op, dwarf_cu); if (op_arg_size == LLDB_INVALID_OFFSET) { error = true; @@ -418,7 +418,7 @@ bool DWARFExpression::Update_DW_OP_addr(const DWARFUnit *dwarf_cu, m_data.SetData(encoder.GetDataBuffer()); return true; } - const offset_t op_arg_size = + const lldb::offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op, dwarf_cu); if (op_arg_size == LLDB_INVALID_OFFSET) break; @@ -435,7 +435,7 @@ bool DWARFExpression::ContainsThreadLocalStorage( if (op == DW_OP_form_tls_address || op == DW_OP_GNU_push_tls_address) return true; - const offset_t op_arg_size = + const lldb::offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op, dwarf_cu); if (op_arg_size == LLDB_INVALID_OFFSET) return false; @@ -515,7 +515,7 @@ bool DWARFExpression::LinkThreadLocalStorage( } if (!decoded_data) { - const offset_t op_arg_size = + const lldb::offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op, dwarf_cu); if (op_arg_size == LLDB_INVALID_OFFSET) return false; diff --git a/lldb/source/Host/CMakeLists.txt b/lldb/source/Host/CMakeLists.txt index c2e091ee8555b..5374b16881950 100644 --- a/lldb/source/Host/CMakeLists.txt +++ b/lldb/source/Host/CMakeLists.txt @@ -7,6 +7,11 @@ if (APPLE AND LLVM_ENABLE_LOCAL_SUBMODULE_VISIBILITY) endif() endif() +if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX") + remove_definitions("-D_XOPEN_SOURCE=700") + add_definitions("-D_ALL_SOURCE") +endif() + macro(add_host_subdirectory group) list(APPEND HOST_SOURCES ${ARGN}) source_group(${group} FILES ${ARGN}) @@ -133,6 +138,14 @@ else() openbsd/Host.cpp openbsd/HostInfoOpenBSD.cpp ) + + elseif (CMAKE_SYSTEM_NAME MATCHES "AIX") + add_host_subdirectory(aix + aix/AbstractSocket.cpp + aix/Host.cpp + aix/HostInfoAIX.cpp + aix/Support.cpp + ) endif() endif() diff --git a/lldb/source/Host/aix/AbstractSocket.cpp b/lldb/source/Host/aix/AbstractSocket.cpp new file mode 100644 index 0000000000000..bfb67d452f7ec --- /dev/null +++ b/lldb/source/Host/aix/AbstractSocket.cpp @@ -0,0 +1,21 @@ +//===-- AbstractSocket.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 "lldb/Host/aix/AbstractSocket.h" + +#include "llvm/ADT/StringRef.h" + +using namespace lldb; +using namespace lldb_private; + +AbstractSocket::AbstractSocket(bool child_processes_inherit) + : DomainSocket(ProtocolUnixAbstract, child_processes_inherit) {} + +size_t AbstractSocket::GetNameOffset() const { return 1; } + +void AbstractSocket::DeleteSocketFile(llvm::StringRef name) {} diff --git a/lldb/source/Host/aix/Host.cpp b/lldb/source/Host/aix/Host.cpp new file mode 100644 index 0000000000000..d82cb9049d389 --- /dev/null +++ b/lldb/source/Host/aix/Host.cpp @@ -0,0 +1,304 @@ +//===-- source/Host/aix/Host.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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/ScopedPrinter.h" + +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/ProcessInfo.h" +#include "lldb/Utility/Status.h" + +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/aix/Host.h" +#include "lldb/Host/aix/Support.h" +#include "lldb/Utility/DataExtractor.h" +#include "llvm/BinaryFormat/XCOFF.h" + +#include +#include + +using namespace llvm; +using namespace lldb; +using namespace lldb_private; + +namespace { +enum class ProcessState { + Unknown, + Dead, + DiskSleep, + Idle, + Paging, + Parked, + Running, + Sleeping, + TracedOrStopped, + Zombie, +}; +} + +namespace lldb_private { +class ProcessLaunchInfo; +} + +static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo, + ProcessState &State, ::pid_t &TracerPid, + ::pid_t &Tgid) { + Log *log = GetLog(LLDBLog::Host); + + auto BufferOrError = getProcFile(Pid, "status"); + if (!BufferOrError) + return false; + + llvm::StringRef Rest = BufferOrError.get()->getBuffer(); + while (!Rest.empty()) { + llvm::StringRef Line; + std::tie(Line, Rest) = Rest.split('\n'); + + if (Line.consume_front("Gid:")) { + // Real, effective, saved set, and file system GIDs. Read the first two. + Line = Line.ltrim(); + uint32_t RGid, EGid; + Line.consumeInteger(10, RGid); + Line = Line.ltrim(); + Line.consumeInteger(10, EGid); + + ProcessInfo.SetGroupID(RGid); + ProcessInfo.SetEffectiveGroupID(EGid); + } else if (Line.consume_front("Uid:")) { + // Real, effective, saved set, and file system UIDs. Read the first two. + Line = Line.ltrim(); + uint32_t RUid, EUid; + Line.consumeInteger(10, RUid); + Line = Line.ltrim(); + Line.consumeInteger(10, EUid); + + ProcessInfo.SetUserID(RUid); + ProcessInfo.SetEffectiveUserID(EUid); + } else if (Line.consume_front("PPid:")) { + ::pid_t PPid; + Line.ltrim().consumeInteger(10, PPid); + ProcessInfo.SetParentProcessID(PPid); + } else if (Line.consume_front("State:")) { + State = llvm::StringSwitch(Line.ltrim().take_front(1)) + .Case("D", ProcessState::DiskSleep) + .Case("I", ProcessState::Idle) + .Case("R", ProcessState::Running) + .Case("S", ProcessState::Sleeping) + .CaseLower("T", ProcessState::TracedOrStopped) + .Case("W", ProcessState::Paging) + .Case("P", ProcessState::Parked) + .Case("X", ProcessState::Dead) + .Case("Z", ProcessState::Zombie) + .Default(ProcessState::Unknown); + if (State == ProcessState::Unknown) { + LLDB_LOG(log, "Unknown process state {0}", Line); + } + } else if (Line.consume_front("TracerPid:")) { + Line = Line.ltrim(); + Line.consumeInteger(10, TracerPid); + } else if (Line.consume_front("Tgid:")) { + Line = Line.ltrim(); + Line.consumeInteger(10, Tgid); + } + } + return true; +} + +static bool IsDirNumeric(const char *dname) { + for (; *dname; dname++) { + if (!isdigit(*dname)) + return false; + } + return true; +} + +static void GetProcessArgs(::pid_t pid, ProcessInstanceInfo &process_info) { + auto BufferOrError = getProcFile(pid, "cmdline"); + if (!BufferOrError) + return; + std::unique_ptr Cmdline = std::move(*BufferOrError); + + llvm::StringRef Arg0, Rest; + std::tie(Arg0, Rest) = Cmdline->getBuffer().split('\0'); + process_info.SetArg0(Arg0); + while (!Rest.empty()) { + llvm::StringRef Arg; + std::tie(Arg, Rest) = Rest.split('\0'); + process_info.GetArguments().AppendArgument(Arg); + } +} + +static void GetExePathAndArch(::pid_t pid, ProcessInstanceInfo &process_info) { + Log *log = GetLog(LLDBLog::Process); + std::string ExePath(PATH_MAX, '\0'); + std::string Basename(PATH_MAX, '\0'); + struct psinfo psinfoData; + + // We can't use getProcFile here because proc/[pid]/exe is a symbolic link. + llvm::SmallString<64> ProcExe; + (llvm::Twine("/proc/") + llvm::Twine(pid) + "/cwd").toVector(ProcExe); + + ssize_t len = readlink(ProcExe.c_str(), &ExePath[0], PATH_MAX); + if (len > 0) { + ExePath.resize(len); + + //FIXME: hack to get basename + struct stat statData; + + std::ostringstream oss; + + oss << "/proc/" << std::dec << pid << "/psinfo"; + assert(stat(oss.str().c_str(), &statData) == 0); + + const int fd = open(oss.str().c_str(), O_RDONLY); + assert (fd >= 0); + + ssize_t readNum = read(fd, &psinfoData, sizeof(psinfoData)); + assert (readNum >= 0); + + close (fd); + } else { + LLDB_LOG(log, "failed to read link exe link for {0}: {1}", pid, + Status(errno, eErrorTypePOSIX)); + ExePath.resize(0); + } + + llvm::StringRef PathRef = std::string(&(psinfoData.pr_psargs[0])); + + if (!PathRef.empty()) { + process_info.GetExecutableFile().SetFile(PathRef, FileSpec::Style::native); + ArchSpec arch_spec = ArchSpec(); + arch_spec.SetArchitecture(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE, llvm::Triple::AIX); + process_info.SetArchitecture(arch_spec); + } +} + +static void GetProcessEnviron(::pid_t pid, ProcessInstanceInfo &process_info) { + // Get the process environment. + auto BufferOrError = getProcFile(pid, "environ"); + if (!BufferOrError) + return; + + std::unique_ptr Environ = std::move(*BufferOrError); + llvm::StringRef Rest = Environ->getBuffer(); + while (!Rest.empty()) { + llvm::StringRef Var; + std::tie(Var, Rest) = Rest.split('\0'); + process_info.GetEnvironment().insert(Var); + } +} + +static bool GetProcessAndStatInfo(::pid_t pid, + ProcessInstanceInfo &process_info, + ProcessState &State, ::pid_t &tracerpid) { + ::pid_t tgid; + tracerpid = 0; + process_info.Clear(); + + process_info.SetProcessID(pid); + + GetExePathAndArch(pid, process_info); + GetProcessArgs(pid, process_info); + GetProcessEnviron(pid, process_info); + + // Get User and Group IDs and get tracer pid. + if (!GetStatusInfo(pid, process_info, State, tracerpid, tgid)) + return false; + + return true; +} + +uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) { + static const char procdir[] = "/proc/"; + + DIR *dirproc = opendir(procdir); + if (dirproc) { + struct dirent *direntry = nullptr; + const uid_t our_uid = getuid(); + const lldb::pid_t our_pid = getpid(); + bool all_users = match_info.GetMatchAllUsers(); + + while ((direntry = readdir(dirproc)) != nullptr) { + /* + if (direntry->d_type != DT_DIR || !IsDirNumeric(direntry->d_name)) + continue; + */ + + lldb::pid_t pid = atoi(direntry->d_name); + + // Skip this process. + if (pid == our_pid) + continue; + + ::pid_t tracerpid; + ProcessState State; + ProcessInstanceInfo process_info; + + if (!GetProcessAndStatInfo(pid, process_info, State, tracerpid)) + continue; + + // Skip if process is being debugged. + if (tracerpid != 0) + continue; + + if (State == ProcessState::Zombie) + continue; + + // Check for user match if we're not matching all users and not running + // as root. + if (!all_users && (our_uid != 0) && (process_info.GetUserID() != our_uid)) + continue; + + if (match_info.Matches(process_info)) { + process_infos.push_back(process_info); + } + } + + closedir(dirproc); + } + + return process_infos.size(); +} + +bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) { + ::pid_t tracerpid; + ProcessState State; + return GetProcessAndStatInfo(pid, process_info, State, tracerpid); +} + +Environment Host::GetEnvironment() { return Environment(environ); } + +Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) { + return Status("unimplemented"); +} + +std::optional lldb_private::getPIDForTID(lldb::pid_t tid) { + ::pid_t tracerpid, tgid = LLDB_INVALID_PROCESS_ID; + ProcessInstanceInfo process_info; + ProcessState state; + + if (!GetStatusInfo(tid, process_info, state, tracerpid, tgid) || + tgid == LLDB_INVALID_PROCESS_ID) + return std::nullopt; + return tgid; +} diff --git a/lldb/source/Host/aix/HostInfoAIX.cpp b/lldb/source/Host/aix/HostInfoAIX.cpp new file mode 100644 index 0000000000000..8bda09e01741b --- /dev/null +++ b/lldb/source/Host/aix/HostInfoAIX.cpp @@ -0,0 +1,215 @@ +//===-- HostInfoAIX.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 "lldb/Host/aix/HostInfoAIX.h" +#include "lldb/Host/Config.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" + +#include "llvm/Support/Threading.h" + +#include +#include +#include +#include +#include + +#include +#include + +using namespace lldb_private; + +namespace { +struct HostInfoAIXFields { + llvm::once_flag m_distribution_once_flag; + std::string m_distribution_id; + llvm::once_flag m_os_version_once_flag; + llvm::VersionTuple m_os_version; +}; +} // namespace + +static HostInfoAIXFields *g_fields = nullptr; + +void HostInfoAIX::Initialize(SharedLibraryDirectoryHelper *helper) { + HostInfoPosix::Initialize(helper); + + g_fields = new HostInfoAIXFields(); +} + +void HostInfoAIX::Terminate() { + assert(g_fields && "Missing call to Initialize?"); + delete g_fields; + g_fields = nullptr; + HostInfoBase::Terminate(); +} + +llvm::VersionTuple HostInfoAIX::GetOSVersion() { + assert(g_fields && "Missing call to Initialize?"); + llvm::call_once(g_fields->m_os_version_once_flag, []() { + struct utsname un; + if (uname(&un) != 0) + return; + + llvm::StringRef release = un.release; + // The kernel release string can include a lot of stuff (e.g. + // 4.9.0-6-amd64). We're only interested in the numbered prefix. + release = release.substr(0, release.find_first_not_of("0123456789.")); + g_fields->m_os_version.tryParse(release); + }); + + return g_fields->m_os_version; +} + +std::optional HostInfoAIX::GetOSBuildString() { + struct utsname un; + ::memset(&un, 0, sizeof(utsname)); + + if (uname(&un) < 0) + return std::nullopt; + + return std::string(un.release); +} + +llvm::StringRef HostInfoAIX::GetDistributionId() { + assert(g_fields && "Missing call to Initialize?"); + // Try to run 'lbs_release -i', and use that response for the distribution + // id. + llvm::call_once(g_fields->m_distribution_once_flag, []() { + Log *log = GetLog(LLDBLog::Host); + LLDB_LOGF(log, "attempting to determine AIX distribution..."); + + // check if the lsb_release command exists at one of the following paths + const char *const exe_paths[] = {"/bin/lsb_release", + "/usr/bin/lsb_release"}; + + for (size_t exe_index = 0; + exe_index < sizeof(exe_paths) / sizeof(exe_paths[0]); ++exe_index) { + const char *const get_distribution_info_exe = exe_paths[exe_index]; + if (access(get_distribution_info_exe, F_OK)) { + // this exe doesn't exist, move on to next exe + LLDB_LOGF(log, "executable doesn't exist: %s", + get_distribution_info_exe); + continue; + } + + // execute the distribution-retrieval command, read output + std::string get_distribution_id_command(get_distribution_info_exe); + get_distribution_id_command += " -i"; + + FILE *file = popen(get_distribution_id_command.c_str(), "r"); + if (!file) { + LLDB_LOGF(log, + "failed to run command: \"%s\", cannot retrieve " + "platform information", + get_distribution_id_command.c_str()); + break; + } + + // retrieve the distribution id string. + char distribution_id[256] = {'\0'}; + if (fgets(distribution_id, sizeof(distribution_id) - 1, file) != + nullptr) { + LLDB_LOGF(log, "distribution id command returned \"%s\"", + distribution_id); + + const char *const distributor_id_key = "Distributor ID:\t"; + if (strstr(distribution_id, distributor_id_key)) { + // strip newlines + std::string id_string(distribution_id + strlen(distributor_id_key)); + id_string.erase(std::remove(id_string.begin(), id_string.end(), '\n'), + id_string.end()); + + // lower case it and convert whitespace to underscores + std::transform( + id_string.begin(), id_string.end(), id_string.begin(), + [](char ch) { return tolower(isspace(ch) ? '_' : ch); }); + + g_fields->m_distribution_id = id_string; + LLDB_LOGF(log, "distribution id set to \"%s\"", + g_fields->m_distribution_id.c_str()); + } else { + LLDB_LOGF(log, "failed to find \"%s\" field in \"%s\"", + distributor_id_key, distribution_id); + } + } else { + LLDB_LOGF(log, + "failed to retrieve distribution id, \"%s\" returned no" + " lines", + get_distribution_id_command.c_str()); + } + + // clean up the file + pclose(file); + } + }); + + return g_fields->m_distribution_id; +} + +FileSpec HostInfoAIX::GetProgramFileSpec() { + static FileSpec g_program_filespec; + + if (!g_program_filespec) { + char exe_path[PATH_MAX]; + ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1); + if (len > 0) { + exe_path[len] = 0; + g_program_filespec.SetFile(exe_path, FileSpec::Style::native); + } + } + + return g_program_filespec; +} + +bool HostInfoAIX::ComputeSupportExeDirectory(FileSpec &file_spec) { + if (HostInfoPosix::ComputeSupportExeDirectory(file_spec) && + file_spec.IsAbsolute() && FileSystem::Instance().Exists(file_spec)) + return true; + file_spec.SetDirectory(GetProgramFileSpec().GetDirectory()); + return !file_spec.GetDirectory().IsEmpty(); +} + +bool HostInfoAIX::ComputeSystemPluginsDirectory(FileSpec &file_spec) { + FileSpec temp_file("/usr/" LLDB_INSTALL_LIBDIR_BASENAME "/lldb/plugins"); + FileSystem::Instance().Resolve(temp_file); + file_spec.SetDirectory(temp_file.GetPath()); + return true; +} + +bool HostInfoAIX::ComputeUserPluginsDirectory(FileSpec &file_spec) { + // XDG Base Directory Specification + // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html If + // XDG_DATA_HOME exists, use that, otherwise use ~/.local/share/lldb. + const char *xdg_data_home = getenv("XDG_DATA_HOME"); + if (xdg_data_home && xdg_data_home[0]) { + std::string user_plugin_dir(xdg_data_home); + user_plugin_dir += "/lldb"; + file_spec.SetDirectory(user_plugin_dir.c_str()); + } else + file_spec.SetDirectory("~/.local/share/lldb"); + return true; +} + +void HostInfoAIX::ComputeHostArchitectureSupport(ArchSpec &arch_32, + ArchSpec &arch_64) { + HostInfoPosix::ComputeHostArchitectureSupport(arch_32, arch_64); + + const char *distribution_id = GetDistributionId().data(); + + // On Linux, "unknown" in the vendor slot isn't what we want for the default + // triple. It's probably an artifact of config.guess. + if (arch_32.IsValid()) { + if (arch_32.GetTriple().getVendor() == llvm::Triple::UnknownVendor) + arch_32.GetTriple().setVendorName(llvm::StringRef()); + } + if (arch_64.IsValid()) { + if (arch_64.GetTriple().getVendor() == llvm::Triple::UnknownVendor) + arch_64.GetTriple().setVendorName(llvm::StringRef()); + } +} diff --git a/lldb/source/Host/aix/Support.cpp b/lldb/source/Host/aix/Support.cpp new file mode 100644 index 0000000000000..1bf2662190127 --- /dev/null +++ b/lldb/source/Host/aix/Support.cpp @@ -0,0 +1,44 @@ +//===-- Support.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 "lldb/Host/aix/Support.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "llvm/Support/MemoryBuffer.h" + +llvm::ErrorOr> +lldb_private::getProcFile(::pid_t pid, ::pid_t tid, const llvm::Twine &file) { + Log *log = GetLog(LLDBLog::Host); + std::string File = + ("/proc/" + llvm::Twine(pid) + "/task/" + llvm::Twine(tid) + "/" + file) + .str(); + auto Ret = llvm::MemoryBuffer::getFileAsStream(File); + if (!Ret) + LLDB_LOG(log, "Failed to open {0}: {1}", File, Ret.getError().message()); + return Ret; +} + +llvm::ErrorOr> +lldb_private::getProcFile(::pid_t pid, const llvm::Twine &file) { + Log *log = GetLog(LLDBLog::Host); + std::string File = ("/proc/" + llvm::Twine(pid) + "/" + file).str(); + auto Ret = llvm::MemoryBuffer::getFileAsStream(File); + if (!Ret) + LLDB_LOG(log, "Failed to open {0}: {1}", File, Ret.getError().message()); + return Ret; +} + +llvm::ErrorOr> +lldb_private::getProcFile(const llvm::Twine &file) { + Log *log = GetLog(LLDBLog::Host); + std::string File = ("/proc/" + file).str(); + auto Ret = llvm::MemoryBuffer::getFileAsStream(File); + if (!Ret) + LLDB_LOG(log, "Failed to open {0}: {1}", File, Ret.getError().message()); + return Ret; +} diff --git a/lldb/source/Host/common/GetOptInc.cpp b/lldb/source/Host/common/GetOptInc.cpp index c2044b6873221..e0ae2aa1774b3 100644 --- a/lldb/source/Host/common/GetOptInc.cpp +++ b/lldb/source/Host/common/GetOptInc.cpp @@ -409,7 +409,7 @@ static int getopt_internal(int nargc, char *const *nargv, const char *options, * [eventually this will replace the BSD getopt] */ #if defined(REPLACE_GETOPT) -int getopt(int nargc, char *const *nargv, const char *options) { +int getopt(int nargc, char *const *nargv, const char *options) throw() { /* * We don't pass FLAG_PERMUTE to getopt_internal() since diff --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp index e03d36e9cad4a..2fd7111a94fb2 100644 --- a/lldb/source/Host/common/Host.cpp +++ b/lldb/source/Host/common/Host.cpp @@ -357,9 +357,183 @@ bool Host::ResolveExecutableInBundle(FileSpec &file) { return false; } #ifndef _WIN32 +#if defined(__AIX__) + +#include +extern char **p_xargv; + +/* Fix missing Dl_info & dladdr in AIX + * The code is taken from netbsd.org (src/crypto/external/bsd/openssl/dist/crypto/dso/dso_dlfcn.c) + * except strlcpy & strlcat (those are taken from openbsd.org (src/lib/libc/string)) + */ +/*- + * See IBM's AIX Version 7.2, Technical Reference: + * Base Operating System and Extensions, Volume 1 and 2 + * https://www.ibm.com/support/knowledgecenter/ssw_aix_72/com.ibm.aix.base/technicalreferences.htm + */ +#include +#include + +/* strlcpy: + * Copy string src to buffer dst of size dsize. At most dsize-1 + * chars will be copied. Always NUL terminates (unless dsize == 0). + * Returns strlen(src); if retval >= dsize, truncation occurred. + */ +size_t strlcpy(char *dst, const char *src, size_t dsize) +{ + const char *osrc = src; + size_t nleft = dsize; + + /* Copy as many bytes as will fit. */ + if (nleft != 0) { + while (--nleft != 0) { + if ((*dst++ = *src++) == '\0') { + break; + } + } + } + + /* Not enough room in dst, add NUL and traverse rest of src. */ + if (nleft == 0) { + if (dsize != 0) { + *dst = '\0'; /* NUL-terminate dst */ + } + while (*src++) { + ; + } + } + + return src - osrc - 1; /* count does not include NUL */ +} + +/* strlcat: + * Appends src to string dst of size dsize (unlike strncat, dsize is the + * full size of dst, not space left). At most dsize-1 characters + * will be copied. Always NUL terminates (unless dsize <= strlen(dst)). + * Returns strlen(src) + MIN(dsize, strlen(initial dst)). + * If retval >= dsize, truncation occurred. + */ +size_t strlcat(char *dst, const char *src, size_t dsize) +{ + const char *odst = dst; + const char *osrc = src; + size_t n = dsize; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end. */ + while (n-- != 0 && *dst != '\0') { + dst++; + } + dlen = dst - odst; + n = dsize - dlen; + + if (n-- == 0) { + return dlen + strlen(src); + } + while (*src != '\0') { + if (n != 0) { + *dst++ = *src; + n--; + } + src++; + } + *dst = '\0'; + + return dlen + src - osrc; /* count does not include NUL */ +} + +/* ~ 64 * (sizeof(struct ld_info) + _XOPEN_PATH_MAX + _XOPEN_NAME_MAX) */ +# define DLFCN_LDINFO_SIZE 86976 +typedef struct Dl_info { + const char *dli_fname; +} Dl_info; +/* + * This dladdr()-implementation will also find the ptrgl (Pointer Glue) virtual + * address of a function, which is just located in the DATA segment instead of + * the TEXT segment. + */ +static int dladdr(const void *ptr, Dl_info *dl) +{ + uintptr_t addr = (uintptr_t)ptr; + struct ld_info *ldinfos; + struct ld_info *next_ldi; + struct ld_info *this_ldi; + + if ((ldinfos = (struct ld_info *)malloc(DLFCN_LDINFO_SIZE)) == NULL) { + dl->dli_fname = NULL; + return 0; + } + + if ((loadquery(L_GETINFO, (void *)ldinfos, DLFCN_LDINFO_SIZE)) < 0) { + /*- + * Error handling is done through errno and dlerror() reading errno: + * ENOMEM (ldinfos buffer is too small), + * EINVAL (invalid flags), + * EFAULT (invalid ldinfos ptr) + */ + free((void *)ldinfos); + dl->dli_fname = NULL; + return 0; + } + next_ldi = ldinfos; + + do { + this_ldi = next_ldi; + if (((addr >= (uintptr_t)this_ldi->ldinfo_textorg) + && (addr < ((uintptr_t)this_ldi->ldinfo_textorg + + this_ldi->ldinfo_textsize))) + || ((addr >= (uintptr_t)this_ldi->ldinfo_dataorg) + && (addr < ((uintptr_t)this_ldi->ldinfo_dataorg + + this_ldi->ldinfo_datasize)))) { + char *buffer = NULL; + char *member = NULL; + size_t buffer_sz; + size_t member_len; + + buffer_sz = strlen(this_ldi->ldinfo_filename) + 1; + member = this_ldi->ldinfo_filename + buffer_sz; + if ((member_len = strlen(member)) > 0) { + buffer_sz += 1 + member_len + 1; + } + if ((buffer = (char *)malloc(buffer_sz)) != NULL) { + strlcpy(buffer, this_ldi->ldinfo_filename, buffer_sz); + if (member_len > 0) { + /* + * Need to respect a possible member name and not just + * returning the path name in this case. See docs: + * sys/ldr.h, loadquery() and dlopen()/RTLD_MEMBER. + */ + strlcat(buffer, "(", buffer_sz); + strlcat(buffer, member, buffer_sz); + strlcat(buffer, ")", buffer_sz); + } + dl->dli_fname = buffer; + } + break; + } else { + next_ldi = (struct ld_info *)((uintptr_t)this_ldi + + this_ldi->ldinfo_next); + } + } while (this_ldi->ldinfo_next); + free((void *)ldinfos); + return dl->dli_fname != NULL; +} + +#endif + FileSpec Host::GetModuleFileSpecForHostAddress(const void *host_addr) { FileSpec module_filespec; #if !defined(__ANDROID__) +#ifdef __AIX__ + if (host_addr == reinterpret_cast(HostInfoBase::ComputeSharedLibraryDirectory)) { + // FIXME: AIX dladdr return "lldb" for this case + if (p_xargv[0]) { + module_filespec.SetFile(p_xargv[0], FileSpec::Style::native); + FileSystem::Instance().Resolve(module_filespec); + return module_filespec; + } + } +#endif Dl_info info; if (::dladdr(host_addr, &info)) { if (info.dli_fname) { @@ -373,12 +547,6 @@ FileSpec Host::GetModuleFileSpecForHostAddress(const void *host_addr) { #endif -#if !defined(__linux__) -bool Host::FindProcessThreads(const lldb::pid_t pid, TidMap &tids_to_attach) { - return false; -} -#endif - struct ShellInfo { ShellInfo() : process_reaped(false) {} diff --git a/lldb/source/Host/common/LICENSE.aix-netbsd.txt b/lldb/source/Host/common/LICENSE.aix-netbsd.txt new file mode 100644 index 0000000000000..9601ab43575f9 --- /dev/null +++ b/lldb/source/Host/common/LICENSE.aix-netbsd.txt @@ -0,0 +1,125 @@ + + LICENSE ISSUES + ============== + + The OpenSSL toolkit stays under a double license, i.e. both the conditions of + the OpenSSL License and the original SSLeay license apply to the toolkit. + See below for the actual license texts. + + OpenSSL License + --------------- + +/* ==================================================================== + * Copyright (c) 1998-2019 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core at openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay at cryptsoft.com). This product includes software written by Tim + * Hudson (tjh at cryptsoft.com). + * + */ + + Original SSLeay License + ----------------------- + +/* Copyright (C) 1995-1998 Eric Young (eay at cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay at cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh at cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay at cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh at cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + diff --git a/lldb/source/Host/common/XML.cpp b/lldb/source/Host/common/XML.cpp index f480ef3166a44..62cac78aaac23 100644 --- a/lldb/source/Host/common/XML.cpp +++ b/lldb/source/Host/common/XML.cpp @@ -10,6 +10,9 @@ #include "lldb/Host/XML.h" #include "llvm/ADT/StringExtras.h" +#if defined(__AIX__) +#undef LLDB_ENABLE_LIBXML2 +#endif using namespace lldb; using namespace lldb_private; diff --git a/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp b/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp index fceeff08ed9d3..143254bb12901 100644 --- a/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp +++ b/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp @@ -721,6 +721,7 @@ ConnectionFileDescriptor::ConnectFD(llvm::StringRef s, ConnectionStatus ConnectionFileDescriptor::ConnectFile( llvm::StringRef s, socket_id_callback_type socket_id_callback, Status *error_ptr) { +#if !defined(__AIX__) #if LLDB_ENABLE_POSIX std::string addr_str = s.str(); // file:///PATH @@ -753,6 +754,7 @@ ConnectionStatus ConnectionFileDescriptor::ConnectFile( m_io_sp = std::make_shared(fd, File::eOpenOptionReadWrite, true); return eConnectionStatusSuccess; #endif // LLDB_ENABLE_POSIX +#endif llvm_unreachable("this function should be only called w/ LLDB_ENABLE_POSIX"); } diff --git a/lldb/source/Host/posix/FileSystemPosix.cpp b/lldb/source/Host/posix/FileSystemPosix.cpp index cdb76da626bc9..a7c50f6a3c835 100644 --- a/lldb/source/Host/posix/FileSystemPosix.cpp +++ b/lldb/source/Host/posix/FileSystemPosix.cpp @@ -11,7 +11,9 @@ // C includes #include #include +#if !defined(__AIX__) #include +#endif #include #include #include diff --git a/lldb/source/Host/posix/MainLoopPosix.cpp b/lldb/source/Host/posix/MainLoopPosix.cpp index 5fe4d015251c8..e5be0db4cf19b 100644 --- a/lldb/source/Host/posix/MainLoopPosix.cpp +++ b/lldb/source/Host/posix/MainLoopPosix.cpp @@ -179,9 +179,21 @@ Status MainLoopPosix::RunImpl::Poll() { read_fds.push_back(pfd); } +#if defined(__AIX__) + sigset_t origmask; + int timeout; + + timeout = -1; + pthread_sigmask(SIG_SETMASK, &sigmask, &origmask); + int ready = poll(read_fds.data(), read_fds.size(), timeout); + pthread_sigmask(SIG_SETMASK, &origmask, nullptr); + if (ready == -1 && errno != EINTR) + return Status(errno, eErrorTypePOSIX); +#else if (ppoll(read_fds.data(), read_fds.size(), nullptr, &sigmask) == -1 && errno != EINTR) return Status(errno, eErrorTypePOSIX); +#endif return Status(); } @@ -312,8 +324,13 @@ MainLoopPosix::RegisterSignal(int signo, const Callback &callback, // If we're using kqueue, the signal needs to be unblocked in order to // receive it. If using pselect/ppoll, we need to block it, and later unblock // it as a part of the system call. +#if defined(__AIX__) + //FIXME: where is signal unblocked? + ret = pthread_sigmask(SIG_UNBLOCK, &new_action.sa_mask, &old_set); +#else ret = pthread_sigmask(HAVE_SYS_EVENT_H ? SIG_UNBLOCK : SIG_BLOCK, &new_action.sa_mask, &old_set); +#endif assert(ret == 0 && "pthread_sigmask failed"); info.was_blocked = sigismember(&old_set, signo); auto insert_ret = m_signals.insert({signo, info}); diff --git a/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp b/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp index 0a832ebad13a7..cd106f605b1f4 100644 --- a/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp +++ b/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp @@ -193,8 +193,13 @@ struct ForkLaunchInfo { } // Start tracing this child that is about to exec. +#if !defined(__AIX__) if (ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1) ExitWithError(error_fd, "ptrace"); +#else + if (ptrace64(PT_TRACE_ME, 0, 0, 0, nullptr) == -1) + ExitWithError(error_fd, "ptrace"); +#endif } // Execute. We should never return... diff --git a/lldb/source/Initialization/CMakeLists.txt b/lldb/source/Initialization/CMakeLists.txt index c1a167826f76f..9f94830c8509f 100644 --- a/lldb/source/Initialization/CMakeLists.txt +++ b/lldb/source/Initialization/CMakeLists.txt @@ -1,4 +1,4 @@ -if ( CMAKE_SYSTEM_NAME MATCHES "Linux|Android|FreeBSD|NetBSD" ) +if ( CMAKE_SYSTEM_NAME MATCHES "Linux|Android|FreeBSD|NetBSD|AIX" ) list(APPEND EXTRA_PLUGINS lldbPluginProcessPOSIX) endif() diff --git a/lldb/source/Initialization/SystemInitializerCommon.cpp b/lldb/source/Initialization/SystemInitializerCommon.cpp index 1a172a95aa147..4b01442a94bac 100644 --- a/lldb/source/Initialization/SystemInitializerCommon.cpp +++ b/lldb/source/Initialization/SystemInitializerCommon.cpp @@ -19,7 +19,7 @@ #include "lldb/Version/Version.h" #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ - defined(__OpenBSD__) + defined(__OpenBSD__) || defined(__AIX__) #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #endif @@ -79,7 +79,7 @@ llvm::Error SystemInitializerCommon::Initialize() { process_gdb_remote::ProcessGDBRemoteLog::Initialize(); #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ - defined(__OpenBSD__) + defined(__OpenBSD__) || defined(__AIX__) ProcessPOSIXLog::Initialize(); #endif #if defined(_WIN32) diff --git a/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.cpp b/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.cpp index eac058701313b..feb0d7c0e09be 100644 --- a/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.cpp +++ b/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.cpp @@ -156,6 +156,9 @@ bool ABISysV_ppc64::PrepareTrivialCall(Thread &thread, addr_t sp, if (!reg_ctx->WriteRegisterFromUnsigned(r12_reg_info, func_addr)) return false; +#if defined(__AIX__) + assert(0); +#else // Read TOC pointer value. reg_value = reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0); @@ -171,6 +174,132 @@ bool ABISysV_ppc64::PrepareTrivialCall(Thread &thread, addr_t sp, (uint64_t)reg_value); if (!process_sp->WritePointerToMemory(sp + stack_offset, reg_value, error)) return false; +#endif + + // Read the current SP value. + reg_value = reg_ctx->ReadRegisterAsUnsigned(sp_reg_info, 0); + + // Save current SP onto the stack. + LLDB_LOGF(log, "Writing SP at SP(0x%" PRIx64 ")+0: 0x%" PRIx64, (uint64_t)sp, + (uint64_t)reg_value); + if (!process_sp->WritePointerToMemory(sp, reg_value, error)) + return false; + + // %r1 is set to the actual stack value. + LLDB_LOGF(log, "Writing SP: 0x%" PRIx64, (uint64_t)sp); + + if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_info, sp)) + return false; + + // %pc is set to the address of the called function. + + LLDB_LOGF(log, "Writing IP: 0x%" PRIx64, (uint64_t)func_addr); + + if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_info, func_addr)) + return false; + + return true; +} + +bool ABISysV_ppc64::PrepareTrivialCall(Thread &thread, addr_t sp, + addr_t func_addr, addr_t toc_addr, + addr_t return_addr, + llvm::ArrayRef args) const { + Log *log = GetLog(LLDBLog::Expressions); + + if (log) { + StreamString s; + s.Printf("ABISysV_ppc64::PrepareTrivialCall (tid = 0x%" PRIx64 + ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64 + ", return_addr = 0x%" PRIx64, + thread.GetID(), (uint64_t)sp, (uint64_t)func_addr, + (uint64_t)return_addr); + + for (size_t i = 0; i < args.size(); ++i) + s.Printf(", arg%" PRIu64 " = 0x%" PRIx64, static_cast(i + 1), + args[i]); + s.PutCString(")"); + log->PutString(s.GetString()); + } + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + if (!reg_ctx) + return false; + + const RegisterInfo *reg_info = nullptr; + + if (args.size() > 8) // TODO handle more than 8 arguments + return false; + + for (size_t i = 0; i < args.size(); ++i) { + reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_ARG1 + i); + LLDB_LOGF(log, "About to write arg%" PRIu64 " (0x%" PRIx64 ") into %s", + static_cast(i + 1), args[i], reg_info->name); + if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i])) + return false; + } + + // First, align the SP + + LLDB_LOGF(log, "16-byte aligning SP: 0x%" PRIx64 " to 0x%" PRIx64, + (uint64_t)sp, (uint64_t)(sp & ~0xfull)); + + sp &= ~(0xfull); // 16-byte alignment + + sp -= 544; // allocate frame to save TOC, RA and SP. + + Status error; + uint64_t reg_value; + const RegisterInfo *pc_reg_info = + reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + const RegisterInfo *sp_reg_info = + reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + ProcessSP process_sp(thread.GetProcess()); + const RegisterInfo *lr_reg_info = + reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA); + const RegisterInfo *r2_reg_info = reg_ctx->GetRegisterInfoAtIndex(2); + const RegisterInfo *r12_reg_info = reg_ctx->GetRegisterInfoAtIndex(12); + + // Save return address onto the stack. + LLDB_LOGF(log, + "Pushing the return address onto the stack: 0x%" PRIx64 + "(+16): 0x%" PRIx64, + (uint64_t)sp, (uint64_t)return_addr); + if (!process_sp->WritePointerToMemory(sp + 16, return_addr, error)) + return false; + + // Write the return address to link register. + LLDB_LOGF(log, "Writing LR: 0x%" PRIx64, (uint64_t)return_addr); + if (!reg_ctx->WriteRegisterFromUnsigned(lr_reg_info, return_addr)) + return false; + + // Write target address to %r12 register. + LLDB_LOGF(log, "Writing R12: 0x%" PRIx64, (uint64_t)func_addr); + if (!reg_ctx->WriteRegisterFromUnsigned(r12_reg_info, func_addr)) + return false; + +#if defined(__AIX__) + LLDB_LOGF(log, "Writing R2: 0x%" PRIx64, (uint64_t)toc_addr); + if (!reg_ctx->WriteRegisterFromUnsigned(r2_reg_info, toc_addr)) + return false; +#else + // Read TOC pointer value. + reg_value = reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0); + + // Write TOC pointer onto the stack. + uint64_t stack_offset; + if (GetByteOrder() == lldb::eByteOrderLittle) + stack_offset = 24; + else + stack_offset = 40; + + LLDB_LOGF(log, "Writing R2 (TOC) at SP(0x%" PRIx64 ")+%d: 0x%" PRIx64, + (uint64_t)(sp + stack_offset), (int)stack_offset, + (uint64_t)reg_value); + if (!process_sp->WritePointerToMemory(sp + stack_offset, reg_value, error)) + return false; +#endif // Read the current SP value. reg_value = reg_ctx->ReadRegisterAsUnsigned(sp_reg_info, 0); @@ -641,7 +770,7 @@ class ReturnValueExtractor { DataExtractor de(&raw_data, sizeof(raw_data), m_byte_order, m_addr_size); - offset_t offset = 0; + lldb::offset_t offset = 0; std::optional byte_size = type.GetByteSize(m_process_sp.get()); if (!byte_size) return {}; diff --git a/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.h b/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.h index bfa96cc0df703..d752a8ded9748 100644 --- a/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.h +++ b/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.h @@ -23,6 +23,12 @@ class ABISysV_ppc64 : public lldb_private::RegInfoBasedABI { lldb::addr_t returnAddress, llvm::ArrayRef args) const override; + bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t tocAddress, + lldb::addr_t returnAddress, + llvm::ArrayRef args) const override; + bool GetArgumentValues(lldb_private::Thread &thread, lldb_private::ValueList &values) const override; diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/CMakeLists.txt b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/CMakeLists.txt new file mode 100644 index 0000000000000..02fe0d617955a --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/CMakeLists.txt @@ -0,0 +1,11 @@ +add_definitions("-D_ALL_SOURCE") + +add_lldb_library(lldbPluginDynamicLoaderAIXDYLD PLUGIN + DynamicLoaderAIXDYLD.cpp + + LINK_LIBS + lldbCore + lldbTarget + LINK_COMPONENTS + Support + ) diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp new file mode 100644 index 0000000000000..62663974134b0 --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp @@ -0,0 +1,272 @@ +//===-- DynamicLoaderAIXDYLD.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 "DynamicLoaderAIXDYLD.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadPlanStepInstruction.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#if defined(__AIX__) +#include +#endif + +/*#include "llvm/ADT/Triple.h" +*/ + +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(DynamicLoaderAIXDYLD) + +DynamicLoaderAIXDYLD::DynamicLoaderAIXDYLD(Process *process) + : DynamicLoader(process) {} + +DynamicLoaderAIXDYLD::~DynamicLoaderAIXDYLD() = default; + +void DynamicLoaderAIXDYLD::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); +} + +void DynamicLoaderAIXDYLD::Terminate() {} + +llvm::StringRef DynamicLoaderAIXDYLD::GetPluginDescriptionStatic() { + return "Dynamic loader plug-in that watches for shared library " + "loads/unloads in AIX processes."; +} + +DynamicLoader *DynamicLoaderAIXDYLD::CreateInstance(Process *process, + bool force) { + bool should_create = force; + if (!should_create) { + const llvm::Triple &triple_ref = + process->GetTarget().GetArchitecture().GetTriple(); + if (triple_ref.getOS() == llvm::Triple::AIX) + should_create = true; + } + + if (should_create) + return new DynamicLoaderAIXDYLD(process); + + return nullptr; +} + +void DynamicLoaderAIXDYLD::OnLoadModule(lldb::ModuleSP module_sp, + const ModuleSpec module_spec, + lldb::addr_t module_addr) { + + // Resolve the module unless we already have one. + if (!module_sp) { + Status error; + module_sp = m_process->GetTarget().GetOrCreateModule(module_spec, + true /* notify */, &error); + if (error.Fail()) + return; + } + + m_loaded_modules[module_sp] = module_addr; + UpdateLoadedSectionsCommon(module_sp, module_addr, false); + ModuleList module_list; + module_list.Append(module_sp); + m_process->GetTarget().ModulesDidLoad(module_list); +} + +void DynamicLoaderAIXDYLD::OnUnloadModule(lldb::addr_t module_addr) { + Address resolved_addr; + if (!m_process->GetTarget().ResolveLoadAddress(module_addr, resolved_addr)) + return; + + ModuleSP module_sp = resolved_addr.GetModule(); + if (module_sp) { + m_loaded_modules.erase(module_sp); + UnloadSectionsCommon(module_sp); + ModuleList module_list; + module_list.Append(module_sp); + m_process->GetTarget().ModulesDidUnload(module_list, false); + } +} + +lldb::addr_t DynamicLoaderAIXDYLD::GetLoadAddress(ModuleSP executable) { + // First, see if the load address is already cached. + auto it = m_loaded_modules.find(executable); + if (it != m_loaded_modules.end() && it->second != LLDB_INVALID_ADDRESS) + return it->second; + + lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; + + // Second, try to get it through the process plugins. For a remote process, + // the remote platform will be responsible for providing it. + FileSpec file_spec(executable->GetPlatformFileSpec()); + bool is_loaded = false; + Status status = + m_process->GetFileLoadAddress(file_spec, is_loaded, load_addr); + // Servers other than lldb server could respond with a bogus address. + if (status.Success() && is_loaded && load_addr != LLDB_INVALID_ADDRESS) { + m_loaded_modules[executable] = load_addr; + return load_addr; + } + + //// Hack to try set breakpoint + //Breakpoint *dyld_break = m_process->GetTarget().CreateBreakpoint(0x100000638, true, false).get(); + //dyld_break->SetCallback(DynamicLoaderAIXDYLD::NotifyBreakpointHit, this, true); + //dyld_break->SetBreakpointKind("hack-debug"); + + return LLDB_INVALID_ADDRESS; +} + +bool DynamicLoaderAIXDYLD::NotifyBreakpointHit( + void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, + lldb::user_id_t break_loc_id) { +} + +void DynamicLoaderAIXDYLD::DidAttach() { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); + + ModuleSP executable = GetTargetExecutable(); + + if (!executable.get()) + return; + + // Try to fetch the load address of the file from the process, since there + // could be randomization of the load address. + lldb::addr_t load_addr = GetLoadAddress(executable); + if (load_addr == LLDB_INVALID_ADDRESS) + return; + + // Request the process base address. + lldb::addr_t image_base = m_process->GetImageInfoAddress(); + if (image_base == load_addr) + return; + + // Rebase the process's modules if there is a mismatch. + UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_addr, false); + + ModuleList module_list; + module_list.Append(executable); + m_process->GetTarget().ModulesDidLoad(module_list); + auto error = m_process->LoadModules(); + LLDB_LOG_ERROR(log, std::move(error), "failed to load modules: {0}"); + +#if defined(__AIX__) + // Get struct ld_xinfo (FIXME) + struct ld_xinfo ldinfo[64]; + Status status = m_process->GetLDXINFO(&(ldinfo[0])); + if (status.Fail()) { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOG(log, "LDXINFO failed: {0}", status); + return; + } + struct ld_xinfo *ptr = &(ldinfo[0]); + bool skip_current = true; + while (ptr != nullptr) { + char *pathName = (char *)ptr + ptr->ldinfo_filename; + char *memberName = pathName + (strlen(pathName) + 1); + if (!skip_current) { + // FIXME: buffer size + char pathWithMember[128] = {0}; + if (strlen(memberName) > 0) { + sprintf(pathWithMember, "%s(%s)", pathName, memberName); + } else { + sprintf(pathWithMember, "%s", pathName); + } + FileSpec file(pathWithMember); + ModuleSpec module_spec(file, m_process->GetTarget().GetArchitecture()); + if (ModuleSP module_sp = m_process->GetTarget().GetOrCreateModule(module_spec, true /* notify */)) { + UpdateLoadedSectionsByType(module_sp, LLDB_INVALID_ADDRESS, (lldb::addr_t)ptr->ldinfo_textorg, false, 1); + UpdateLoadedSectionsByType(module_sp, LLDB_INVALID_ADDRESS, (lldb::addr_t)ptr->ldinfo_dataorg, false, 2); + // FIXME: .tdata, .bss + } + } else { + skip_current = false; + } + if (ptr->ldinfo_next == 0) { + ptr = nullptr; + } else { + ptr = (struct ld_xinfo *)((char *)ptr + ptr->ldinfo_next); + } + } +#endif +} + +void DynamicLoaderAIXDYLD::DidLaunch() { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); + + ModuleSP executable = GetTargetExecutable(); + if (!executable.get()) + return; + + lldb::addr_t load_addr = GetLoadAddress(executable); + if (load_addr != LLDB_INVALID_ADDRESS) { + // Update the loaded sections so that the breakpoints can be resolved. + UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_addr, false); + + ModuleList module_list; + module_list.Append(executable); + m_process->GetTarget().ModulesDidLoad(module_list); + auto error = m_process->LoadModules(); + LLDB_LOG_ERROR(log, std::move(error), "failed to load modules: {0}"); + } + +#if defined(__AIX__) + // Get struct ld_xinfo (FIXME) + struct ld_xinfo ldinfo[64]; + Status status = m_process->GetLDXINFO(&(ldinfo[0])); + if (status.Fail()) { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOG(log, "LDXINFO failed: {0}", status); + return; + } + struct ld_xinfo *ptr = &(ldinfo[0]); + bool skip_current = true; + while (ptr != nullptr) { + char *pathName = (char *)ptr + ptr->ldinfo_filename; + char *memberName = pathName + (strlen(pathName) + 1); + if (!skip_current) { + // FIXME: buffer size + char pathWithMember[128] = {0}; + if (strlen(memberName) > 0) { + sprintf(pathWithMember, "%s(%s)", pathName, memberName); + } else { + sprintf(pathWithMember, "%s", pathName); + } + FileSpec file(pathWithMember); + ModuleSpec module_spec(file, m_process->GetTarget().GetArchitecture()); + if (ModuleSP module_sp = m_process->GetTarget().GetOrCreateModule(module_spec, true /* notify */)) { + UpdateLoadedSectionsByType(module_sp, LLDB_INVALID_ADDRESS, (lldb::addr_t)ptr->ldinfo_textorg, false, 1); + UpdateLoadedSectionsByType(module_sp, LLDB_INVALID_ADDRESS, (lldb::addr_t)ptr->ldinfo_dataorg, false, 2); + // FIXME: .tdata, .bss + } + } else { + skip_current = false; + } + if (ptr->ldinfo_next == 0) { + ptr = nullptr; + } else { + ptr = (struct ld_xinfo *)((char *)ptr + ptr->ldinfo_next); + } + } +#endif +} + +Status DynamicLoaderAIXDYLD::CanLoadImage() { return Status(); } + +ThreadPlanSP +DynamicLoaderAIXDYLD::GetStepThroughTrampolinePlan(Thread &thread, + bool stop) { + //FIXME + return ThreadPlanSP(); +} diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h new file mode 100644 index 0000000000000..ae4b7aca66dcc --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h @@ -0,0 +1,55 @@ +//===-- DynamicLoaderAIXDYLD.h ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_DYNAMICLOADER_AIX_DYLD_DYNAMICLOADERAIXDYLD_H +#define LLDB_SOURCE_PLUGINS_DYNAMICLOADER_AIX_DYLD_DYNAMICLOADERAIXDYLD_H + +#include "lldb/Target/DynamicLoader.h" +#include "lldb/lldb-forward.h" + +#include + +namespace lldb_private { + +class DynamicLoaderAIXDYLD : public DynamicLoader { +public: + DynamicLoaderAIXDYLD(Process *process); + + ~DynamicLoaderAIXDYLD() override; + + static void Initialize(); + static void Terminate(); + static llvm::StringRef GetPluginNameStatic() { return "windows-dyld"; } + static llvm::StringRef GetPluginDescriptionStatic(); + + static DynamicLoader *CreateInstance(Process *process, bool force); + + void OnLoadModule(lldb::ModuleSP module_sp, const ModuleSpec module_spec, + lldb::addr_t module_addr); + void OnUnloadModule(lldb::addr_t module_addr); + + void DidAttach() override; + void DidLaunch() override; + Status CanLoadImage() override; + lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread, + bool stop) override; + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + static bool NotifyBreakpointHit(void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id); + +protected: + lldb::addr_t GetLoadAddress(lldb::ModuleSP executable); + +private: + std::map m_loaded_modules; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_DYNAMICLOADER_AIX_DYLD_DYNAMICLOADERWAIXDYLD_H diff --git a/lldb/source/Plugins/DynamicLoader/CMakeLists.txt b/lldb/source/Plugins/DynamicLoader/CMakeLists.txt index 30607159acdc0..4f3fb693faae1 100644 --- a/lldb/source/Plugins/DynamicLoader/CMakeLists.txt +++ b/lldb/source/Plugins/DynamicLoader/CMakeLists.txt @@ -5,4 +5,5 @@ add_subdirectory(POSIX-DYLD) add_subdirectory(Static) add_subdirectory(Hexagon-DYLD) add_subdirectory(Windows-DYLD) +add_subdirectory(AIX-DYLD) add_subdirectory(wasm-DYLD) diff --git a/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp b/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp index 20e5652c65bf8..26abea0fdd24d 100644 --- a/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp +++ b/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp @@ -256,7 +256,7 @@ DynamicLoaderDarwinKernel::SearchForKernelWithDebugHints(Process *process) { if (process->ReadMemoryFromInferior (kernel_addresses_64[i], uval, 8, read_err) == 8) { DataExtractor data (&uval, 8, process->GetByteOrder(), process->GetAddressByteSize()); - offset_t offset = 0; + lldb::offset_t offset = 0; uint64_t addr = data.GetU64 (&offset); if (CheckForKernelImageAtAddress(addr, process).IsValid()) { return addr; @@ -270,7 +270,7 @@ DynamicLoaderDarwinKernel::SearchForKernelWithDebugHints(Process *process) { if (process->ReadMemoryFromInferior (kernel_addresses_32[i], uval, 4, read_err) == 4) { DataExtractor data (&uval, 4, process->GetByteOrder(), process->GetAddressByteSize()); - offset_t offset = 0; + lldb::offset_t offset = 0; uint32_t addr = data.GetU32 (&offset); if (CheckForKernelImageAtAddress(addr, process).IsValid()) { return addr; diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index 3863b6b3520db..624848dee6ec3 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -1151,7 +1151,7 @@ DynamicLoaderDarwin::GetThreadLocalData(const lldb::ModuleSP module_sp, // TLS data for the pthread_key on a specific thread yet. If we have we // can re-use it since its location will not change unless the process // execs. - const tid_t tid = thread_sp->GetID(); + const lldb::tid_t tid = thread_sp->GetID(); auto tid_pos = m_tid_to_tls_map.find(tid); if (tid_pos != m_tid_to_tls_map.end()) { auto tls_pos = tid_pos->second.find(key); diff --git a/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.cpp b/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.cpp index 3035c51341778..d14ae2daeb47d 100644 --- a/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.cpp +++ b/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.cpp @@ -146,7 +146,25 @@ EmulateInstructionPPC64::GetOpcodeForInstruction(uint32_t opcode) { {0xfc000000, 0x38000000, &EmulateInstructionPPC64::EmulateADDI, "addi RT, RA, SI"}, {0xfc000003, 0xe8000000, &EmulateInstructionPPC64::EmulateLD, - "ld RT, DS(RA)"}}; + "ld RT, DS(RA)"}, +// {0xffff0003, 0x40820000, &EmulateInstructionPPC64::EmulateBNE, +// "bne TARGET"}, + {0xfc000002, 0x48000000, &EmulateInstructionPPC64::EmulateB, + "b TARGET"}, + {0xfc000003, 0x48000002, &EmulateInstructionPPC64::EmulateBA, + "ba TARGET"}, + {0xfc000003, 0x48000003, &EmulateInstructionPPC64::EmulateBLA, + "bla TARGET"}, + {0xfc000002, 0x40000000, &EmulateInstructionPPC64::EmulateBC, + "bc BO,BI,TARGET"}, + {0xfc000002, 0x40000002, &EmulateInstructionPPC64::EmulateBCA, + "bca BO,BI,TARGET"}, + {0xfc0007fe, 0x4c000020, &EmulateInstructionPPC64::EmulateBCLR, + "bclr BO,BI,BH"}, + {0xfc0007fe, 0x4c000420, &EmulateInstructionPPC64::EmulateBCCTR, + "bcctr BO,BI,BH"}, + {0xfc0007fe, 0x4c000460, &EmulateInstructionPPC64::EmulateBCTAR, + "bctar BO,BI,BH"}}; static const size_t k_num_ppc_opcodes = std::size(g_opcodes); for (size_t i = 0; i < k_num_ppc_opcodes; ++i) { @@ -169,12 +187,13 @@ bool EmulateInstructionPPC64::EvaluateInstruction(uint32_t evaluate_options) { bool success = false; - uint32_t orig_pc_value = 0; + uint64_t orig_pc_value = 0; if (auto_advance_pc) { orig_pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); if (!success) return false; + LLDB_LOG(GetLog(LLDBLog::Unwind), "orig_pc_value:{0}", orig_pc_value); } // Call the Emulate... function. @@ -183,11 +202,13 @@ bool EmulateInstructionPPC64::EvaluateInstruction(uint32_t evaluate_options) { return false; if (auto_advance_pc) { - uint32_t new_pc_value = + uint64_t new_pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); if (!success) return false; + LLDB_LOG(GetLog(LLDBLog::Unwind), "new_pc_value:{0}", new_pc_value); + if (new_pc_value == orig_pc_value) { EmulateInstruction::Context context; context.type = eContextAdvancePC; @@ -389,5 +410,174 @@ bool EmulateInstructionPPC64::EmulateADDI(uint32_t opcode) { return false; WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_r1_ppc64le, r1 + si_val); LLDB_LOG(log, "EmulateADDI: success!"); + + // FIX the next-pc + uint64_t pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); + uint64_t next_pc = pc_value + 4; + ctx.type = eContextAdjustPC; + WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_pc_ppc64le, next_pc); + + return true; +} + +bool EmulateInstructionPPC64::EmulateBC(uint32_t opcode) { + // FIXME:32bit M + uint32_t M = 0; + uint32_t target32 = Bits32(opcode, 15, 2) << 2; + uint64_t target = (uint64_t)target32 + ((target32 & 0x8000) ? 0xffffffffffff0000UL : 0); + uint32_t BO = Bits32(opcode, 25, 21); + bool success; + uint64_t ctr_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_ctr_ppc64le, 0, &success); + if ((~BO) & (1U << 2)) + ctr_value = ctr_value - 1; + bool ctr_ok = (bool)(BO & (1U << 2)) | ((bool)(ctr_value != 0) ^ (bool)(BO & (1U << 1))); + uint64_t cr_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_cr_ppc64le, 0, &success); + uint32_t BI = Bits32(opcode, 20, 16); + bool cond_ok = (bool)(BO & (1U << 4)) | (bool)(((cr_value >> (63 - (BI + 32))) & 1U) == ((BO >> 3) & 1U)); + + uint64_t pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); + uint64_t next_pc = pc_value + 4; + if (ctr_ok & cond_ok) + next_pc = pc_value + target; + + Context ctx; + ctx.type = eContextAdjustPC; + WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_pc_ppc64le, next_pc); + Log *log = GetLog(LLDBLog::Unwind); + LLDB_LOG(log, "EmulateBC: success!"); + return true; +} + +bool EmulateInstructionPPC64::EmulateBCA(uint32_t opcode) { + // FIXME:32bit M + uint32_t M = 0; + uint32_t target32 = Bits32(opcode, 15, 2) << 2; + uint64_t target = (uint64_t)target32 + ((target32 & 0x8000) ? 0xffffffffffff0000UL : 0); + uint32_t BO = Bits32(opcode, 25, 21); + bool success; + uint64_t ctr_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_ctr_ppc64le, 0, &success); + if ((~BO) & (1U << 2)) + ctr_value = ctr_value - 1; + bool ctr_ok = (bool)(BO & (1U << 2)) | ((bool)(ctr_value != 0) ^ (bool)(BO & (1U << 1))); + uint64_t cr_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_cr_ppc64le, 0, &success); + uint32_t BI = Bits32(opcode, 20, 16); + bool cond_ok = (bool)(BO & (1U << 4)) | (bool)(((cr_value >> (63 - (BI + 32))) & 1U) == ((BO >> 3) & 1U)); + + uint64_t pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); + uint64_t next_pc = pc_value + 4; + if (ctr_ok & cond_ok) + next_pc = target; + + Context ctx; + ctx.type = eContextAdjustPC; + WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_pc_ppc64le, next_pc); + Log *log = GetLog(LLDBLog::Unwind); + LLDB_LOG(log, "EmulateBCA: success!"); + return true; +} + +bool EmulateInstructionPPC64::EmulateBCLR(uint32_t opcode) { + // FIXME:32bit M + uint32_t M = 0; + uint32_t BO = Bits32(opcode, 25, 21); + bool success; + uint64_t ctr_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_ctr_ppc64le, 0, &success); + if ((~BO) & (1U << 2)) + ctr_value = ctr_value - 1; + bool ctr_ok = (bool)(BO & (1U << 2)) | ((bool)(ctr_value != 0) ^ (bool)(BO & (1U << 1))); + uint64_t cr_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_cr_ppc64le, 0, &success); + uint32_t BI = Bits32(opcode, 20, 16); + bool cond_ok = (bool)(BO & (1U << 4)) | (bool)(((cr_value >> (63 - (BI + 32))) & 1U) == ((BO >> 3) & 1U)); + + uint64_t pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); + uint64_t next_pc = pc_value + 4; + if (ctr_ok & cond_ok) { + next_pc = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_lr_ppc64le, 0, &success); + next_pc &= ~((1UL << 2) - 1); + } + + Context ctx; + ctx.type = eContextAdjustPC; + WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_pc_ppc64le, next_pc); + Log *log = GetLog(LLDBLog::Unwind); + LLDB_LOG(log, "EmulateBCLR: success!"); + return true; +} + +bool EmulateInstructionPPC64::EmulateBCCTR(uint32_t opcode) { + // FIXME:32bit M + uint32_t M = 0; + uint32_t BO = Bits32(opcode, 25, 21); + bool success; + uint64_t cr_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_cr_ppc64le, 0, &success); + uint32_t BI = Bits32(opcode, 20, 16); + bool cond_ok = (bool)(BO & (1U << 4)) | (bool)(((cr_value >> (63 - (BI + 32))) & 1U) == ((BO >> 3) & 1U)); + + Log *log = GetLog(LLDBLog::Unwind); + uint64_t pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); + uint64_t next_pc = pc_value + 4; + if (cond_ok) { + next_pc = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_ctr_ppc64le, 0, &success); + next_pc &= ~((1UL << 2) - 1); + if (next_pc < 0x4000000) { + LLDB_LOGF(log, "EmulateBCCTR: next address %lx out of range, emulate by goto LR!"); + next_pc = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_lr_ppc64le, 0, &success); + } + } + + Context ctx; + ctx.type = eContextAdjustPC; + WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_pc_ppc64le, next_pc); + LLDB_LOG(log, "EmulateBCCTR: success!"); + return true; +} + +bool EmulateInstructionPPC64::EmulateBCTAR(uint32_t opcode) { + // Not supported yet. + LLDB_LOG(GetLog(LLDBLog::Unwind), "EmulateBCTAR: not supported!"); + assert(0); + return false; +} + +bool EmulateInstructionPPC64::EmulateB(uint32_t opcode) { + uint32_t target32 = Bits32(opcode, 25, 2) << 2; + uint64_t target = (uint64_t)target32 + ((target32 & 0x2000000) ? 0xfffffffffc000000UL : 0); + + bool success; + uint64_t pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); + uint64_t next_pc = pc_value + target; + + Context ctx; + ctx.type = eContextAdjustPC; + WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_pc_ppc64le, next_pc); + Log *log = GetLog(LLDBLog::Unwind); + LLDB_LOG(log, "EmulateB: success!"); + return true; +} + +bool EmulateInstructionPPC64::EmulateBA(uint32_t opcode) { + Log *log = GetLog(LLDBLog::Unwind); + + bool success; + uint64_t next_pc = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_lr_ppc64le, 0, &success); + + Context ctx; + ctx.type = eContextAdjustPC; + WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_pc_ppc64le, next_pc); + LLDB_LOG(log, "EmulateBA: emulate by branch to lr!"); + return true; +} + +bool EmulateInstructionPPC64::EmulateBLA(uint32_t opcode) { + Log *log = GetLog(LLDBLog::Unwind); + + bool success; + uint64_t pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); + uint64_t next_pc = pc_value + 4; + + Context ctx; + ctx.type = eContextAdjustPC; + WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_pc_ppc64le, next_pc); + LLDB_LOG(log, "EmulateBLA: emulate by branch to lr!"); return true; } diff --git a/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h b/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h index a9424f16b0ad0..1576c9700e557 100644 --- a/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h +++ b/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h @@ -39,6 +39,12 @@ class EmulateInstructionPPC64 : public EmulateInstruction { return true; case eInstructionTypePCModifying: +#if defined(__AIX__) + return true; +#else + return false; +#endif + case eInstructionTypeAll: return false; } @@ -84,6 +90,14 @@ class EmulateInstructionPPC64 : public EmulateInstruction { bool EmulateSTD(uint32_t opcode); bool EmulateOR(uint32_t opcode); bool EmulateADDI(uint32_t opcode); + bool EmulateB(uint32_t opcode); + bool EmulateBA(uint32_t opcode); + bool EmulateBLA(uint32_t opcode); + bool EmulateBC(uint32_t opcode); + bool EmulateBCA(uint32_t opcode); + bool EmulateBCLR(uint32_t opcode); + bool EmulateBCCTR(uint32_t opcode); + bool EmulateBCTAR(uint32_t opcode); }; } // namespace lldb_private diff --git a/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/InstrumentationRuntimeMainThreadChecker.cpp b/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/InstrumentationRuntimeMainThreadChecker.cpp index b7cd2b1ac6bf6..876e74056face 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/InstrumentationRuntimeMainThreadChecker.cpp +++ b/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/InstrumentationRuntimeMainThreadChecker.cpp @@ -261,7 +261,7 @@ InstrumentationRuntimeMainThreadChecker::GetBacktracesFromExtendedStopInfo( StructuredData::ObjectSP thread_id_obj = info->GetObjectForDotSeparatedPath("tid"); - tid_t tid = thread_id_obj ? thread_id_obj->GetUnsignedIntegerValue() : 0; + lldb::tid_t tid = thread_id_obj ? thread_id_obj->GetUnsignedIntegerValue() : 0; // We gather symbolication addresses above, so no need for HistoryThread to // try to infer the call addresses. diff --git a/lldb/source/Plugins/InstrumentationRuntime/TSan/InstrumentationRuntimeTSan.cpp b/lldb/source/Plugins/InstrumentationRuntime/TSan/InstrumentationRuntimeTSan.cpp index b2781aa5e7db1..7a827a3ea76f9 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/TSan/InstrumentationRuntimeTSan.cpp +++ b/lldb/source/Plugins/InstrumentationRuntime/TSan/InstrumentationRuntimeTSan.cpp @@ -770,13 +770,13 @@ std::string InstrumentationRuntimeTSan::GetLocationDescription( Sprintf("Location is a %ld-byte heap object at 0x%llx", size, addr); } } else if (type == "stack") { - tid_t tid = loc->GetAsDictionary() + lldb::tid_t tid = loc->GetAsDictionary() ->GetValueForKey("thread_id") ->GetUnsignedIntegerValue(); result = Sprintf("Location is stack of thread %d", tid); } else if (type == "tls") { - tid_t tid = loc->GetAsDictionary() + lldb::tid_t tid = loc->GetAsDictionary() ->GetValueForKey("thread_id") ->GetUnsignedIntegerValue(); @@ -948,7 +948,7 @@ static std::string GenerateThreadName(const std::string &path, if (path == "mops") { size_t size = o->GetObjectForDotSeparatedPath("size")->GetUnsignedIntegerValue(); - tid_t thread_id = + lldb::tid_t thread_id = o->GetObjectForDotSeparatedPath("thread_id")->GetUnsignedIntegerValue(); bool is_write = o->GetObjectForDotSeparatedPath("is_write")->GetBooleanValue(); @@ -979,7 +979,7 @@ static std::string GenerateThreadName(const std::string &path, } if (path == "threads") { - tid_t thread_id = + lldb::tid_t thread_id = o->GetObjectForDotSeparatedPath("thread_id")->GetUnsignedIntegerValue(); result = Sprintf("Thread %zu created", thread_id); } @@ -987,7 +987,7 @@ static std::string GenerateThreadName(const std::string &path, if (path == "locs") { std::string type = std::string( o->GetAsDictionary()->GetValueForKey("type")->GetStringValue()); - tid_t thread_id = + lldb::tid_t thread_id = o->GetObjectForDotSeparatedPath("thread_id")->GetUnsignedIntegerValue(); int fd = o->GetObjectForDotSeparatedPath("file_descriptor") ->GetSignedIntegerValue(); @@ -1007,7 +1007,7 @@ static std::string GenerateThreadName(const std::string &path, } if (path == "stacks") { - tid_t thread_id = + lldb::tid_t thread_id = o->GetObjectForDotSeparatedPath("thread_id")->GetUnsignedIntegerValue(); result = Sprintf("Thread %" PRIu64, thread_id); } @@ -1034,7 +1034,7 @@ static void AddThreadsForPath(const std::string &path, StructuredData::ObjectSP thread_id_obj = o->GetObjectForDotSeparatedPath("thread_os_id"); - tid_t tid = + lldb::tid_t tid = thread_id_obj ? thread_id_obj->GetUnsignedIntegerValue() : 0; ThreadSP new_thread_sp = diff --git a/lldb/source/Plugins/InstrumentationRuntime/UBSan/InstrumentationRuntimeUBSan.cpp b/lldb/source/Plugins/InstrumentationRuntime/UBSan/InstrumentationRuntimeUBSan.cpp index 1c58922e8d36c..de9719ad4a89e 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/UBSan/InstrumentationRuntimeUBSan.cpp +++ b/lldb/source/Plugins/InstrumentationRuntime/UBSan/InstrumentationRuntimeUBSan.cpp @@ -321,7 +321,7 @@ InstrumentationRuntimeUBSan::GetBacktracesFromExtendedStopInfo( StructuredData::ObjectSP thread_id_obj = info->GetObjectForDotSeparatedPath("tid"); - tid_t tid = thread_id_obj ? thread_id_obj->GetUnsignedIntegerValue() : 0; + lldb::tid_t tid = thread_id_obj ? thread_id_obj->GetUnsignedIntegerValue() : 0; // We gather symbolication addresses above, so no need for HistoryThread to // try to infer the call addresses. diff --git a/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp b/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp index 1688fb27430a7..690fb0d60a09a 100644 --- a/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp +++ b/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp @@ -194,6 +194,10 @@ void JITLoaderGDB::SetJITBreakpoint(lldb_private::ModuleList &module_list) { if (jit_addr == LLDB_INVALID_ADDRESS) return; +#if defined(__AIX__) + return; +#endif + m_jit_descriptor_addr = GetSymbolAddress( module_list, ConstString("__jit_debug_descriptor"), eSymbolTypeData); if (m_jit_descriptor_addr == LLDB_INVALID_ADDRESS) { diff --git a/lldb/source/Plugins/Language/ObjC/Cocoa.cpp b/lldb/source/Plugins/Language/ObjC/Cocoa.cpp index 341923108e321..fb5bc2c58e6fb 100644 --- a/lldb/source/Plugins/Language/ObjC/Cocoa.cpp +++ b/lldb/source/Plugins/Language/ObjC/Cocoa.cpp @@ -1227,6 +1227,7 @@ bool lldb_private::formatters::ObjCSELSummaryProvider( time_t lldb_private::formatters::GetOSXEpoch() { static time_t epoch = 0; if (!epoch) { +#if !defined(__AIX__) #ifndef _WIN32 tzset(); tm tm_epoch; @@ -1240,6 +1241,7 @@ time_t lldb_private::formatters::GetOSXEpoch() { tm_epoch.tm_gmtoff = 0; tm_epoch.tm_zone = nullptr; epoch = timegm(&tm_epoch); +#endif #endif } return epoch; diff --git a/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp b/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp index 6efd2516578ff..fe6c5a0544be3 100644 --- a/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp +++ b/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp @@ -107,7 +107,7 @@ static void CreateHistoryThreadFromValueObject(ProcessSP process_sp, return; int count = count_sp->GetValueAsUnsigned(0); - tid_t tid = tid_sp->GetValueAsUnsigned(0) + 1; + lldb::tid_t tid = tid_sp->GetValueAsUnsigned(0) + 1; if (count <= 0) return; diff --git a/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp index 7aa5b8d81890a..5ea55772c3aba 100644 --- a/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp +++ b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp @@ -8,7 +8,7 @@ #include "ObjectContainerBSDArchive.h" -#if defined(_WIN32) || defined(__ANDROID__) +#if defined(_WIN32) || defined(__ANDROID__) || defined(__AIX__) // Defines from ar, missing on Windows #define SARMAG 8 #define ARFMAG "`\n" diff --git a/lldb/source/Plugins/ObjectContainer/Big-Archive/CMakeLists.txt b/lldb/source/Plugins/ObjectContainer/Big-Archive/CMakeLists.txt new file mode 100644 index 0000000000000..612a36265b536 --- /dev/null +++ b/lldb/source/Plugins/ObjectContainer/Big-Archive/CMakeLists.txt @@ -0,0 +1,10 @@ +add_lldb_library(lldbPluginObjectContainerBigArchive PLUGIN + ObjectContainerBigArchive.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbSymbol + LINK_COMPONENTS + Support + ) diff --git a/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.cpp b/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.cpp new file mode 100644 index 0000000000000..050ad73f1d19a --- /dev/null +++ b/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.cpp @@ -0,0 +1,522 @@ +//===-- ObjectContainerBigArchive.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 "ObjectContainerBigArchive.h" + +#if defined(_WIN32) || defined(__ANDROID__) || defined(__AIX__) +// Defines from ar, missing on Windows +#define ARMAG "!\n" +#define SARMAG 8 +#define ARFMAG "`\n" + +typedef struct ar_hdr { + char ar_name[16]; + char ar_date[12]; + char ar_uid[6], ar_gid[6]; + char ar_mode[8]; + char ar_size[10]; + char ar_fmag[2]; +} ar_hdr; +#else +#include +#endif + +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/Timer.h" + +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Object/Archive.h" +#include "llvm/Support/Chrono.h" + +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(ObjectContainerBigArchive) + +ObjectContainerBigArchive::Object::Object() : ar_name() {} + +void ObjectContainerBigArchive::Object::Clear() { + ar_name.Clear(); + modification_time = 0; + uid = 0; + gid = 0; + mode = 0; + size = 0; + file_offset = 0; + file_size = 0; +} + +lldb::offset_t +ObjectContainerBigArchive::Object::Extract(const DataExtractor &data, + lldb::offset_t offset) { + size_t ar_name_len = 0; + std::string str; + char *err; + + // File header + // + // The common format is as follows. + // + // Offset Length Name Format + // 0 16 File name ASCII right padded with spaces (no spaces + // allowed in file name) + // 16 12 File mod Decimal as cstring right padded with + // spaces + // 28 6 Owner ID Decimal as cstring right padded with + // spaces + // 34 6 Group ID Decimal as cstring right padded with + // spaces + // 40 8 File mode Octal as cstring right padded with + // spaces + // 48 10 File byte size Decimal as cstring right padded with + // spaces + // 58 2 File magic 0x60 0x0A + + // Make sure there is enough data for the file header and bail if not + if (!data.ValidOffsetForDataOfSize(offset, 60)) + return LLDB_INVALID_OFFSET; + + str.assign((const char *)data.GetData(&offset, 16), 16); + if (llvm::StringRef(str).starts_with("#1/")) { + // If the name is longer than 16 bytes, or contains an embedded space then + // it will use this format where the length of the name is here and the + // name characters are after this header. + ar_name_len = strtoul(str.c_str() + 3, &err, 10); + } else { + // Strip off any trailing spaces. + const size_t last_pos = str.find_last_not_of(' '); + if (last_pos != std::string::npos) { + if (last_pos + 1 < 16) + str.erase(last_pos + 1); + } + ar_name.SetCString(str.c_str()); + } + + str.assign((const char *)data.GetData(&offset, 12), 12); + modification_time = strtoul(str.c_str(), &err, 10); + + str.assign((const char *)data.GetData(&offset, 6), 6); + uid = strtoul(str.c_str(), &err, 10); + + str.assign((const char *)data.GetData(&offset, 6), 6); + gid = strtoul(str.c_str(), &err, 10); + + str.assign((const char *)data.GetData(&offset, 8), 8); + mode = strtoul(str.c_str(), &err, 8); + + str.assign((const char *)data.GetData(&offset, 10), 10); + size = strtoul(str.c_str(), &err, 10); + + str.assign((const char *)data.GetData(&offset, 2), 2); + if (str == ARFMAG) { + if (ar_name_len > 0) { + const void *ar_name_ptr = data.GetData(&offset, ar_name_len); + // Make sure there was enough data for the string value and bail if not + if (ar_name_ptr == nullptr) + return LLDB_INVALID_OFFSET; + str.assign((const char *)ar_name_ptr, ar_name_len); + ar_name.SetCString(str.c_str()); + } + file_offset = offset; + file_size = size - ar_name_len; + return offset; + } + return LLDB_INVALID_OFFSET; +} + +ObjectContainerBigArchive::Archive::Archive(const lldb_private::ArchSpec &arch, + const llvm::sys::TimePoint<> &time, + lldb::offset_t file_offset, + lldb_private::DataExtractor &data) + : m_arch(arch), m_modification_time(time), m_file_offset(file_offset), + m_objects(), m_data(data) {} + +ObjectContainerBigArchive::Archive::~Archive() = default; + +size_t ObjectContainerBigArchive::Archive::ParseObjects() { + DataExtractor &data = m_data; + std::string str; + lldb::offset_t offset = 0; + str.assign((const char *)data.GetData(&offset, (sizeof(llvm::object::BigArchiveMagic) - 1)), + (sizeof(llvm::object::BigArchiveMagic) - 1)); + if (str == llvm::object::BigArchiveMagic) { + llvm::Error err = llvm::Error::success(); + llvm::object::BigArchive bigAr(llvm::MemoryBufferRef(toStringRef(m_data.GetData()), llvm::StringRef("")), err); + if (err) + return 0; + + for (const llvm::object::Archive::Child &child : bigAr.children(err)) { + if (err) + continue; + if (!child.getParent()) + continue; + Object obj; + obj.Clear(); + // FIXME: check errors + llvm::Expected childNameOrErr = child.getName(); + if (!childNameOrErr) + continue; + obj.ar_name.SetCString(childNameOrErr->str().c_str()); + llvm::Expected> lastModifiedOrErr = child.getLastModified(); + if (!lastModifiedOrErr) + continue; + obj.modification_time = (uint32_t)llvm::sys::toTimeT(*(lastModifiedOrErr)); + llvm::Expected getUIDOrErr = child.getUID(); + if (!getUIDOrErr) + continue; + obj.uid = (uint16_t)*getUIDOrErr; + llvm::Expected getGIDOrErr = child.getGID(); + if (!getGIDOrErr) + continue; + obj.gid = (uint16_t)*getGIDOrErr; + llvm::Expected getAccessModeOrErr = child.getAccessMode(); + if (!getAccessModeOrErr) + continue; + obj.mode = (uint16_t)*getAccessModeOrErr; + llvm::Expected getRawSizeOrErr = child.getRawSize(); + if (!getRawSizeOrErr) + continue; + obj.size = (uint32_t)*getRawSizeOrErr; + + obj.file_offset = (lldb::offset_t)child.getDataOffset(); + + llvm::Expected getSizeOrErr = child.getSize(); + if (!getSizeOrErr) + continue; + obj.file_size = (lldb::offset_t)*getSizeOrErr; + + size_t obj_idx = m_objects.size(); + m_objects.push_back(obj); + // Insert all of the C strings out of order for now... + m_object_name_to_index_map.Append(obj.ar_name, obj_idx); + } + if (err) + return 0; + + // Now sort all of the object name pointers + m_object_name_to_index_map.Sort(); + } + return m_objects.size(); +} + +ObjectContainerBigArchive::Object * +ObjectContainerBigArchive::Archive::FindObject( + ConstString object_name, const llvm::sys::TimePoint<> &object_mod_time) { + const ObjectNameToIndexMap::Entry *match = + m_object_name_to_index_map.FindFirstValueForName(object_name); + if (!match) + return nullptr; + if (object_mod_time == llvm::sys::TimePoint<>()) + return &m_objects[match->value]; + + const uint64_t object_modification_date = llvm::sys::toTimeT(object_mod_time); + if (m_objects[match->value].modification_time == object_modification_date) + return &m_objects[match->value]; + + const ObjectNameToIndexMap::Entry *next_match = + m_object_name_to_index_map.FindNextValueForName(match); + while (next_match) { + if (m_objects[next_match->value].modification_time == + object_modification_date) + return &m_objects[next_match->value]; + next_match = m_object_name_to_index_map.FindNextValueForName(next_match); + } + + return nullptr; +} + +ObjectContainerBigArchive::Archive::shared_ptr +ObjectContainerBigArchive::Archive::FindCachedArchive( + const FileSpec &file, const ArchSpec &arch, + const llvm::sys::TimePoint<> &time, lldb::offset_t file_offset) { + std::lock_guard guard(Archive::GetArchiveCacheMutex()); + shared_ptr archive_sp; + Archive::Map &archive_map = Archive::GetArchiveCache(); + Archive::Map::iterator pos = archive_map.find(file); + // Don't cache a value for "archive_map.end()" below since we might delete an + // archive entry... + while (pos != archive_map.end() && pos->first == file) { + bool match = true; + if (arch.IsValid() && + !pos->second->GetArchitecture().IsCompatibleMatch(arch)) + match = false; + else if (file_offset != LLDB_INVALID_OFFSET && + pos->second->GetFileOffset() != file_offset) + match = false; + if (match) { + if (pos->second->GetModificationTime() == time) { + return pos->second; + } else { + // We have a file at the same path with the same architecture whose + // modification time doesn't match. It doesn't make sense for us to + // continue to use this Big archive since we cache only the object info + // which consists of file time info and also the file offset and file + // size of any contained objects. Since this information is now out of + // date, we won't get the correct information if we go and extract the + // file data, so we should remove the old and outdated entry. + archive_map.erase(pos); + pos = archive_map.find(file); + continue; // Continue to next iteration so we don't increment pos + // below... + } + } + ++pos; + } + return archive_sp; +} + +ObjectContainerBigArchive::Archive::shared_ptr +ObjectContainerBigArchive::Archive::ParseAndCacheArchiveForFile( + const FileSpec &file, const ArchSpec &arch, + const llvm::sys::TimePoint<> &time, lldb::offset_t file_offset, + DataExtractor &data) { + shared_ptr archive_sp(new Archive(arch, time, file_offset, data)); + if (archive_sp) { + const size_t num_objects = archive_sp->ParseObjects(); + if (num_objects > 0) { + std::lock_guard guard( + Archive::GetArchiveCacheMutex()); + Archive::GetArchiveCache().insert(std::make_pair(file, archive_sp)); + } else { + archive_sp.reset(); + } + } + return archive_sp; +} + +ObjectContainerBigArchive::Archive::Map & +ObjectContainerBigArchive::Archive::GetArchiveCache() { + static Archive::Map g_archive_map; + return g_archive_map; +} + +std::recursive_mutex & +ObjectContainerBigArchive::Archive::GetArchiveCacheMutex() { + static std::recursive_mutex g_archive_map_mutex; + return g_archive_map_mutex; +} + +void ObjectContainerBigArchive::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + GetModuleSpecifications); +} + +void ObjectContainerBigArchive::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +ObjectContainer *ObjectContainerBigArchive::CreateInstance( + const lldb::ModuleSP &module_sp, DataBufferSP &data_sp, + lldb::offset_t data_offset, const FileSpec *file, + lldb::offset_t file_offset, lldb::offset_t length) { + ConstString object_name(module_sp->GetObjectName()); + if (!object_name) + return nullptr; + + if (data_sp) { + // We have data, which means this is the first 512 bytes of the file Check + // to see if the magic bytes match and if they do, read the entire table of + // contents for the archive and cache it + DataExtractor data; + data.SetData(data_sp, data_offset, length); + if (file && data_sp && ObjectContainerBigArchive::MagicBytesMatch(data)) { + LLDB_SCOPED_TIMERF( + "ObjectContainerBigArchive::CreateInstance (module = %s, file = " + "%p, file_offset = 0x%8.8" PRIx64 ", file_size = 0x%8.8" PRIx64 ")", + module_sp->GetFileSpec().GetPath().c_str(), + static_cast(file), static_cast(file_offset), + static_cast(length)); + + // Map the entire .a file to be sure that we don't lose any data if the + // file gets updated by a new build while this .a file is being used for + // debugging + DataBufferSP archive_data_sp = + FileSystem::Instance().CreateDataBuffer(*file, length, file_offset); + if (!archive_data_sp) + return nullptr; + + lldb::offset_t archive_data_offset = 0; + + Archive::shared_ptr archive_sp(Archive::FindCachedArchive( + *file, module_sp->GetArchitecture(), module_sp->GetModificationTime(), + file_offset)); + std::unique_ptr container_up( + new ObjectContainerBigArchive(module_sp, archive_data_sp, + archive_data_offset, file, file_offset, + length)); + + if (container_up) { + if (archive_sp) { + // We already have this archive in our cache, use it + container_up->SetArchive(archive_sp); + return container_up.release(); + } else if (container_up->ParseHeader()) + return container_up.release(); + } + } + } else { + // No data, just check for a cached archive + Archive::shared_ptr archive_sp(Archive::FindCachedArchive( + *file, module_sp->GetArchitecture(), module_sp->GetModificationTime(), + file_offset)); + if (archive_sp) { + std::unique_ptr container_up( + new ObjectContainerBigArchive(module_sp, data_sp, data_offset, file, + file_offset, length)); + + if (container_up) { + // We already have this archive in our cache, use it + container_up->SetArchive(archive_sp); + return container_up.release(); + } + } + } + return nullptr; +} + +bool ObjectContainerBigArchive::MagicBytesMatch(const DataExtractor &data) { + uint32_t offset = 0; + const char *armag = (const char *)data.PeekData(offset, (sizeof(llvm::object::BigArchiveMagic) - 1)); + if (armag && ::strncmp(armag, llvm::object::BigArchiveMagic, (sizeof(llvm::object::BigArchiveMagic) - 1)) == 0) + return true; + return false; +} + +ObjectContainerBigArchive::ObjectContainerBigArchive( + const lldb::ModuleSP &module_sp, DataBufferSP &data_sp, + lldb::offset_t data_offset, const lldb_private::FileSpec *file, + lldb::offset_t file_offset, lldb::offset_t size) + : ObjectContainer(module_sp, file, file_offset, size, data_sp, data_offset), + m_archive_sp() {} +void ObjectContainerBigArchive::SetArchive(Archive::shared_ptr &archive_sp) { + m_archive_sp = archive_sp; +} + +ObjectContainerBigArchive::~ObjectContainerBigArchive() = default; + +bool ObjectContainerBigArchive::ParseHeader() { + if (m_archive_sp.get() == nullptr) { + if (m_data.GetByteSize() > 0) { + ModuleSP module_sp(GetModule()); + if (module_sp) { + m_archive_sp = Archive::ParseAndCacheArchiveForFile( + m_file, module_sp->GetArchitecture(), + module_sp->GetModificationTime(), m_offset, m_data); + } + // Clear the m_data that contains the entire archive data and let our + // m_archive_sp hold onto the data. + m_data.Clear(); + } + } + return m_archive_sp.get() != nullptr; +} + +void ObjectContainerBigArchive::Object::Dump(Stream *s) const { + printf("name = \"%s\"\n", ar_name.GetCString()); + printf("mtime = 0x%8.8" PRIx32 "\n", modification_time); + printf("size = 0x%8.8" PRIx32 " (%" PRIu32 ")\n", size, size); + printf("file_offset = 0x%16.16" PRIx64 " (%" PRIu64 ")\n", file_offset, + file_offset); + printf("file_size = 0x%16.16" PRIx64 " (%" PRIu64 ")\n\n", file_size, + file_size); +} + +ObjectFileSP ObjectContainerBigArchive::GetObjectFile(const FileSpec *file) { + ModuleSP module_sp(GetModule()); + if (module_sp) { + if (module_sp->GetObjectName() && m_archive_sp) { + Object *object = m_archive_sp->FindObject( + module_sp->GetObjectName(), module_sp->GetObjectModificationTime()); + if (object) { + lldb::offset_t data_offset = object->file_offset; + return ObjectFile::FindPlugin( + module_sp, file, m_offset + object->file_offset, object->file_size, + m_archive_sp->GetData().GetSharedDataBuffer(), data_offset); + } + } + } + return ObjectFileSP(); +} + +size_t ObjectContainerBigArchive::GetModuleSpecifications( + const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, lldb::offset_t file_offset, + lldb::offset_t file_size, lldb_private::ModuleSpecList &specs) { + + // We have data, which means this is the first 512 bytes of the file Check to + // see if the magic bytes match and if they do, read the entire table of + // contents for the archive and cache it + DataExtractor data; + data.SetData(data_sp, data_offset, data_sp->GetByteSize()); + if (!file || !data_sp || !ObjectContainerBigArchive::MagicBytesMatch(data)) + return 0; + + const size_t initial_count = specs.GetSize(); + llvm::sys::TimePoint<> file_mod_time = FileSystem::Instance().GetModificationTime(file); + Archive::shared_ptr archive_sp( + Archive::FindCachedArchive(file, ArchSpec(), file_mod_time, file_offset)); + bool set_archive_arch = false; + if (!archive_sp) { + set_archive_arch = true; + data_sp = + FileSystem::Instance().CreateDataBuffer(file, file_size, file_offset); + if (data_sp) { + data.SetData(data_sp, 0, data_sp->GetByteSize()); + archive_sp = Archive::ParseAndCacheArchiveForFile( + file, ArchSpec(), file_mod_time, file_offset, data); + } + } + + if (archive_sp) { + const size_t num_objects = archive_sp->GetNumObjects(); + for (size_t idx = 0; idx < num_objects; ++idx) { + const Object *object = archive_sp->GetObjectAtIndex(idx); + if (object) { + const lldb::offset_t object_file_offset = + file_offset + object->file_offset; + if (object->file_offset < file_size && file_size > object_file_offset) { + if (ObjectFile::GetModuleSpecifications( + file, object_file_offset, file_size - object_file_offset, + specs)) { + ModuleSpec &spec = + specs.GetModuleSpecRefAtIndex(specs.GetSize() - 1); + llvm::sys::TimePoint<> object_mod_time( + std::chrono::seconds(object->modification_time)); + spec.GetObjectName() = object->ar_name; + spec.SetObjectOffset(object_file_offset); + spec.SetObjectSize(file_size - object_file_offset); + spec.GetObjectModificationTime() = object_mod_time; + } + } + } + } + } + const size_t end_count = specs.GetSize(); + size_t num_specs_added = end_count - initial_count; + if (set_archive_arch && num_specs_added > 0) { + // The archive was created but we didn't have an architecture so we need to + // set it + for (size_t i = initial_count; i < end_count; ++i) { + ModuleSpec module_spec; + if (specs.GetModuleSpecAtIndex(i, module_spec)) { + if (module_spec.GetArchitecture().IsValid()) { + archive_sp->SetArchitecture(module_spec.GetArchitecture()); + break; + } + } + } + } + return num_specs_added; +} diff --git a/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.h b/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.h new file mode 100644 index 0000000000000..ad9b814048a87 --- /dev/null +++ b/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.h @@ -0,0 +1,177 @@ +//===-- ObjectContainerBigArchive.h -----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_OBJECTCONTAINER_BIG_ARCHIVE_OBJECTCONTAINERBIGARCHIVE_H +#define LLDB_SOURCE_PLUGINS_OBJECTCONTAINER_BIG_ARCHIVE_OBJECTCONTAINERBIGARCHIVE_H + +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Symbol/ObjectContainer.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/FileSpec.h" + +#include "llvm/Support/Chrono.h" + +#include +#include +#include + +class ObjectContainerBigArchive : public lldb_private::ObjectContainer { +public: + ObjectContainerBigArchive(const lldb::ModuleSP &module_sp, + lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec *file, + lldb::offset_t offset, lldb::offset_t length); + + ~ObjectContainerBigArchive() override; + + // Static Functions + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "big-archive"; } + + static llvm::StringRef GetPluginDescriptionStatic() { + return "Big Archive object container reader."; + } + + static lldb_private::ObjectContainer * + CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, const lldb_private::FileSpec *file, + lldb::offset_t offset, lldb::offset_t length); + + static size_t GetModuleSpecifications(const lldb_private::FileSpec &file, + lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb_private::ModuleSpecList &specs); + + static bool MagicBytesMatch(const lldb_private::DataExtractor &data); + + // Member Functions + bool ParseHeader() override; + + size_t GetNumObjects() const override { + if (m_archive_sp) + return m_archive_sp->GetNumObjects(); + return 0; + } + + lldb::ObjectFileSP GetObjectFile(const lldb_private::FileSpec *file) override; + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + +protected: + struct Object { + Object(); + + void Clear(); + + lldb::offset_t Extract(const lldb_private::DataExtractor &data, + lldb::offset_t offset); + /// Object name in the archive. + lldb_private::ConstString ar_name; + + /// Object modification time in the archive. + uint32_t modification_time = 0; + + /// Object user id in the archive. + uint16_t uid = 0; + + /// Object group id in the archive. + uint16_t gid = 0; + + /// Object octal file permissions in the archive. + uint16_t mode = 0; + + /// Object size in bytes in the archive. + uint32_t size = 0; + + /// File offset in bytes from the beginning of the file of the object data. + lldb::offset_t file_offset = 0; + + /// Length of the object data. + lldb::offset_t file_size = 0; + + void Dump(lldb_private::Stream *s) const; + }; + + class Archive { + public: + typedef std::shared_ptr shared_ptr; + typedef std::multimap Map; + + Archive(const lldb_private::ArchSpec &arch, + const llvm::sys::TimePoint<> &mod_time, lldb::offset_t file_offset, + lldb_private::DataExtractor &data); + + ~Archive(); + + static Map &GetArchiveCache(); + + static std::recursive_mutex &GetArchiveCacheMutex(); + + static Archive::shared_ptr FindCachedArchive( + const lldb_private::FileSpec &file, const lldb_private::ArchSpec &arch, + const llvm::sys::TimePoint<> &mod_time, lldb::offset_t file_offset); + + static Archive::shared_ptr ParseAndCacheArchiveForFile( + const lldb_private::FileSpec &file, const lldb_private::ArchSpec &arch, + const llvm::sys::TimePoint<> &mod_time, lldb::offset_t file_offset, + lldb_private::DataExtractor &data); + + size_t GetNumObjects() const { return m_objects.size(); } + + const Object *GetObjectAtIndex(size_t idx) { + if (idx < m_objects.size()) + return &m_objects[idx]; + return nullptr; + } + + size_t ParseObjects(); + + Object *FindObject(lldb_private::ConstString object_name, + const llvm::sys::TimePoint<> &object_mod_time); + + lldb::offset_t GetFileOffset() const { return m_file_offset; } + + const llvm::sys::TimePoint<> &GetModificationTime() { + return m_modification_time; + } + + const lldb_private::ArchSpec &GetArchitecture() const { return m_arch; } + + void SetArchitecture(const lldb_private::ArchSpec &arch) { m_arch = arch; } + + bool HasNoExternalReferences() const; + + lldb_private::DataExtractor &GetData() { return m_data; } + + protected: + typedef lldb_private::UniqueCStringMap ObjectNameToIndexMap; + // Member Variables + lldb_private::ArchSpec m_arch; + llvm::sys::TimePoint<> m_modification_time; + lldb::offset_t m_file_offset; + std::vector m_objects; + ObjectNameToIndexMap m_object_name_to_index_map; + lldb_private::DataExtractor m_data; ///< The data for this object container + ///so we don't lose data if the .a files + ///gets modified + }; + + void SetArchive(Archive::shared_ptr &archive_sp); + + Archive::shared_ptr m_archive_sp; +}; + +#endif // LLDB_SOURCE_PLUGINS_OBJECTCONTAINER_BIG_ARCHIVE_OBJECTCONTAINERBIGARCHIVE_H diff --git a/lldb/source/Plugins/ObjectContainer/CMakeLists.txt b/lldb/source/Plugins/ObjectContainer/CMakeLists.txt index cda0c8151dd8a..2492798bb13ef 100644 --- a/lldb/source/Plugins/ObjectContainer/CMakeLists.txt +++ b/lldb/source/Plugins/ObjectContainer/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(BSD-Archive) +add_subdirectory(Big-Archive) add_subdirectory(Universal-Mach-O) add_subdirectory(Mach-O-Fileset) diff --git a/lldb/source/Plugins/ObjectFile/CMakeLists.txt b/lldb/source/Plugins/ObjectFile/CMakeLists.txt index 773241c8944c8..7abd0c96f4fd7 100644 --- a/lldb/source/Plugins/ObjectFile/CMakeLists.txt +++ b/lldb/source/Plugins/ObjectFile/CMakeLists.txt @@ -6,5 +6,6 @@ add_subdirectory(Mach-O) add_subdirectory(Minidump) add_subdirectory(PDB) add_subdirectory(PECOFF) +add_subdirectory(XCOFF) add_subdirectory(Placeholder) add_subdirectory(wasm) diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp index ce095bcc48374..bcb6330cbb1f9 100644 --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -5673,7 +5673,7 @@ bool ObjectFileMachO::GetCorefileMainBinaryInfo(addr_t &value, return false; } -bool ObjectFileMachO::GetCorefileThreadExtraInfos(std::vector &tids) { +bool ObjectFileMachO::GetCorefileThreadExtraInfos(std::vector &tids) { tids.clear(); ModuleSP module_sp(GetModule()); if (module_sp) { @@ -5724,8 +5724,8 @@ bool ObjectFileMachO::GetCorefileThreadExtraInfos(std::vector &tids) { return false; } StructuredData::Dictionary *thread = *maybe_thread; - tid_t tid = LLDB_INVALID_THREAD_ID; - if (thread->GetValueForKeyAsInteger("thread_id", tid)) + lldb::tid_t tid = LLDB_INVALID_THREAD_ID; + if (thread->GetValueForKeyAsInteger("thread_id", tid)) if (tid == 0) tid = LLDB_INVALID_THREAD_ID; tids.push_back(tid); diff --git a/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp b/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp index faa144bfb5f6a..d27cdfc60de85 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp +++ b/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp @@ -51,7 +51,9 @@ size_t ObjectFileMinidump::GetModuleSpecifications( const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp, lldb::offset_t data_offset, lldb::offset_t file_offset, lldb::offset_t length, lldb_private::ModuleSpecList &specs) { +#if !defined(__AIX__) specs.Clear(); +#endif return 0; } diff --git a/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp b/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp index f0832dbf07347..75cc54e4f0d48 100644 --- a/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp +++ b/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp @@ -116,18 +116,31 @@ size_t ObjectFilePDB::GetModuleSpecifications( ModuleSpec module_spec(file); llvm::BumpPtrAllocator allocator; std::unique_ptr pdb_file = loadPDBFile(file.GetPath(), allocator); - if (!pdb_file) + if (!pdb_file){ +#if !defined(__AIX__) return initial_count; +#else + return specs.GetSize() - initial_count; +#endif + } auto info_stream = pdb_file->getPDBInfoStream(); if (!info_stream) { llvm::consumeError(info_stream.takeError()); +#if !defined(__AIX__) return initial_count; +#else + return specs.GetSize() - initial_count; +#endif } auto dbi_stream = pdb_file->getPDBDbiStream(); if (!dbi_stream) { llvm::consumeError(dbi_stream.takeError()); +#if !defined(__AIX__) return initial_count; +#else + return specs.GetSize() - initial_count; +#endif } lldb_private::UUID &uuid = module_spec.GetUUID(); diff --git a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp index bda691ade8af0..db8fa78043fdc 100644 --- a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp @@ -252,8 +252,13 @@ size_t ObjectFilePECOFF::GetModuleSpecifications( lldb::offset_t data_offset, lldb::offset_t file_offset, lldb::offset_t length, lldb_private::ModuleSpecList &specs) { const size_t initial_count = specs.GetSize(); - if (!data_sp || !ObjectFilePECOFF::MagicBytesMatch(data_sp)) + if (!data_sp || !ObjectFilePECOFF::MagicBytesMatch(data_sp)){ +#if !defined(__AIX__) return initial_count; +#else + return specs.GetSize() - initial_count; +#endif + } Log *log = GetLog(LLDBLog::Object); @@ -266,12 +271,21 @@ size_t ObjectFilePECOFF::GetModuleSpecifications( if (!binary) { LLDB_LOG_ERROR(log, binary.takeError(), "Failed to create binary for file ({1}): {0}", file); +#if !defined(__AIX__) return initial_count; +#else + return specs.GetSize() - initial_count; +#endif } auto *COFFObj = llvm::dyn_cast(binary->get()); - if (!COFFObj) + if (!COFFObj){ +#if !defined(__AIX__) return initial_count; +#else + return specs.GetSize() - initial_count; +#endif + } ModuleSpec module_spec(file); ArchSpec &spec = module_spec.GetArchitecture(); diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/CMakeLists.txt b/lldb/source/Plugins/ObjectFile/XCOFF/CMakeLists.txt new file mode 100644 index 0000000000000..8840248574c88 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/XCOFF/CMakeLists.txt @@ -0,0 +1,13 @@ +add_lldb_library(lldbPluginObjectFileXCOFF PLUGIN + ObjectFileXCOFF.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbSymbol + lldbTarget + LINK_COMPONENTS + BinaryFormat + Object + Support + ) diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp new file mode 100644 index 0000000000000..a4d9ea295b4c3 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp @@ -0,0 +1,780 @@ +//===-- ObjectFileXCOFF.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 "ObjectFileXCOFF.h" + +#include +#include +#include +#include + +#include "lldb/Utility/FileSpecList.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Progress.h" +#include "lldb/Core/Section.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/LZMA.h" +#include "lldb/Symbol/DWARFCallFrameInfo.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RangeMap.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/Timer.h" +#include "llvm/ADT/IntervalMap.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/XCOFF.h" +#include "llvm/Object/Decompressor.h" +#include "llvm/Support/CRC.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Object/XCOFFObjectFile.h" + +using namespace llvm; +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(ObjectFileXCOFF) + +char ObjectFileXCOFF::ID; + +// FIXME: target 64bit at this moment. + +// Static methods. +void ObjectFileXCOFF::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + CreateMemoryInstance, GetModuleSpecifications); +} + +void ObjectFileXCOFF::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +bool UGLY_FLAG_FOR_AIX __attribute__((weak)) = false; + +ObjectFile *ObjectFileXCOFF::CreateInstance(const lldb::ModuleSP &module_sp, + DataBufferSP data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec *file, + lldb::offset_t file_offset, + lldb::offset_t length) { + if (!data_sp) { + data_sp = MapFileData(*file, length, file_offset); + if (!data_sp) + return nullptr; + data_offset = 0; + } + + if (!ObjectFileXCOFF::MagicBytesMatch(data_sp, data_offset, length)) + return nullptr; + + // Update the data to contain the entire file if it doesn't already + if (data_sp->GetByteSize() < length) { + data_sp = MapFileData(*file, length, file_offset); + if (!data_sp) + return nullptr; + data_offset = 0; + } + auto objfile_up = std::make_unique( + module_sp, data_sp, data_offset, file, file_offset, length); + if (!objfile_up) + return nullptr; + + // Cache xcoff binary. + if (!objfile_up->CreateBinary()) + return nullptr; + + if (!objfile_up->ParseHeader()) + //FIXME objfile leak + return nullptr; + + UGLY_FLAG_FOR_AIX = true; + return objfile_up.release(); +} + +bool ObjectFileXCOFF::CreateBinary() { + if (m_binary) + return true; + + Log *log = GetLog(LLDBLog::Object); + + auto binary = llvm::object::XCOFFObjectFile::createObjectFile(llvm::MemoryBufferRef( + toStringRef(m_data.GetData()), m_file.GetFilename().GetStringRef()), + file_magic::xcoff_object_64); + if (!binary) { + LLDB_LOG_ERROR(log, binary.takeError(), + "Failed to create binary for file ({1}): {0}", m_file); + return false; + } + + // Make sure we only handle COFF format. + m_binary = + llvm::unique_dyn_cast(std::move(*binary)); + if (!m_binary) + return false; + + LLDB_LOG(log, "this = {0}, module = {1} ({2}), file = {3}, binary = {4}", + this, GetModule().get(), GetModule()->GetSpecificationDescription(), + m_file.GetPath(), m_binary.get()); + return true; +} + +ObjectFile *ObjectFileXCOFF::CreateMemoryInstance( + const lldb::ModuleSP &module_sp, WritableDataBufferSP data_sp, + const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) { + return nullptr; +} + +size_t ObjectFileXCOFF::GetModuleSpecifications( + const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, lldb::offset_t file_offset, + lldb::offset_t length, lldb_private::ModuleSpecList &specs) { + const size_t initial_count = specs.GetSize(); + + if (ObjectFileXCOFF::MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) { + ArchSpec arch_spec = ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); + ModuleSpec spec(file, arch_spec); + spec.GetArchitecture().SetArchitecture(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE, llvm::Triple::AIX); + specs.Append(spec); + } + return specs.GetSize() - initial_count; +} + +static uint32_t XCOFFHeaderSizeFromMagic(uint32_t magic) { + switch (magic) { + /* TODO: 32bit not supported yet + case XCOFF::XCOFF32: + return sizeof(struct llvm::object::XCOFFFileHeader32); + */ + + case XCOFF::XCOFF64: + return sizeof(struct llvm::object::XCOFFFileHeader64); + break; + + default: + break; + } + return 0; +} + +bool ObjectFileXCOFF::MagicBytesMatch(DataBufferSP &data_sp, + lldb::addr_t data_offset, + lldb::addr_t data_length) { + lldb_private::DataExtractor data; + data.SetData(data_sp, data_offset, data_length); + lldb::offset_t offset = 0; + uint16_t magic = data.GetU16(&offset); + return XCOFFHeaderSizeFromMagic(magic) != 0; +} + +bool ObjectFileXCOFF::ParseHeader() { + ModuleSP module_sp(GetModule()); + if (module_sp) { + std::lock_guard guard(module_sp->GetMutex()); + m_sect_headers.clear(); + lldb::offset_t offset = 0; + + if (ParseXCOFFHeader(m_data, &offset, m_xcoff_header)) { + m_data.SetAddressByteSize(GetAddressByteSize()); + if (m_xcoff_header.auxhdrsize > 0) + ParseXCOFFOptionalHeader(m_data, &offset); + ParseSectionHeaders(offset); + } + return true; + } + + return false; +} + +bool ObjectFileXCOFF::ParseXCOFFHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset_ptr, + xcoff_header_t &xcoff_header) { + //FIXME: data.ValidOffsetForDataOfSize + xcoff_header.magic = data.GetU16(offset_ptr); + xcoff_header.nsects = data.GetU16(offset_ptr); + xcoff_header.modtime = data.GetU32(offset_ptr); + xcoff_header.symoff = data.GetU64(offset_ptr); + xcoff_header.auxhdrsize = data.GetU16(offset_ptr); + xcoff_header.flags = data.GetU16(offset_ptr); + xcoff_header.nsyms = data.GetU32(offset_ptr); + return true; +} + +bool ObjectFileXCOFF::ParseXCOFFOptionalHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset_ptr) { + lldb::offset_t init_offset = *offset_ptr; + //FIXME: data.ValidOffsetForDataOfSize + m_xcoff_aux_header.AuxMagic = data.GetU16(offset_ptr); + m_xcoff_aux_header.Version = data.GetU16(offset_ptr); + m_xcoff_aux_header.ReservedForDebugger = data.GetU32(offset_ptr); + m_xcoff_aux_header.TextStartAddr = data.GetU64(offset_ptr); + m_xcoff_aux_header.DataStartAddr = data.GetU64(offset_ptr); + m_xcoff_aux_header.TOCAnchorAddr = data.GetU64(offset_ptr); + m_xcoff_aux_header.SecNumOfEntryPoint = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfText = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfData = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfTOC = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfLoader = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfBSS = data.GetU16(offset_ptr); + m_xcoff_aux_header.MaxAlignOfText = data.GetU16(offset_ptr); + m_xcoff_aux_header.MaxAlignOfData = data.GetU16(offset_ptr); + m_xcoff_aux_header.ModuleType = data.GetU16(offset_ptr); + m_xcoff_aux_header.CpuFlag = data.GetU8(offset_ptr); + m_xcoff_aux_header.CpuType = data.GetU8(offset_ptr); + m_xcoff_aux_header.TextPageSize = data.GetU8(offset_ptr); + m_xcoff_aux_header.DataPageSize = data.GetU8(offset_ptr); + m_xcoff_aux_header.StackPageSize = data.GetU8(offset_ptr); + m_xcoff_aux_header.FlagAndTDataAlignment = data.GetU8(offset_ptr); + m_xcoff_aux_header.TextSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.InitDataSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.BssDataSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.EntryPointAddr = data.GetU64(offset_ptr); + m_xcoff_aux_header.MaxStackSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.MaxDataSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.SecNumOfTData = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfTBSS = data.GetU16(offset_ptr); + m_xcoff_aux_header.XCOFF64Flag = data.GetU16(offset_ptr); + lldb::offset_t last_offset = *offset_ptr; + if ((last_offset - init_offset) < m_xcoff_header.auxhdrsize) + *offset_ptr += (m_xcoff_header.auxhdrsize - (last_offset - init_offset)); + return true; +} + +bool ObjectFileXCOFF::ParseSectionHeaders( + uint32_t section_header_data_offset) { + const uint32_t nsects = m_xcoff_header.nsects; + m_sect_headers.clear(); + + if (nsects > 0) { + const size_t section_header_byte_size = nsects * m_binary->getSectionHeaderSize(); + lldb_private::DataExtractor section_header_data = + ReadImageData(section_header_data_offset, section_header_byte_size); + + lldb::offset_t offset = 0; + //FIXME: section_header_data.ValidOffsetForDataOfSize + m_sect_headers.resize(nsects); + + for (uint32_t idx = 0; idx < nsects; ++idx) { + const void *name_data = section_header_data.GetData(&offset, 8); + if (name_data) { + memcpy(m_sect_headers[idx].name, name_data, 8); + m_sect_headers[idx].phyaddr = section_header_data.GetU64(&offset); + m_sect_headers[idx].vmaddr = section_header_data.GetU64(&offset); + m_sect_headers[idx].size = section_header_data.GetU64(&offset); + m_sect_headers[idx].offset = section_header_data.GetU64(&offset); + m_sect_headers[idx].reloff = section_header_data.GetU64(&offset); + m_sect_headers[idx].lineoff = section_header_data.GetU64(&offset); + m_sect_headers[idx].nreloc = section_header_data.GetU32(&offset); + m_sect_headers[idx].nline = section_header_data.GetU32(&offset); + m_sect_headers[idx].flags = section_header_data.GetU32(&offset); + offset += 4; + } else { + offset += (m_binary->getSectionHeaderSize() - 8); + } + } + } + + return !m_sect_headers.empty(); +} + +lldb_private::DataExtractor ObjectFileXCOFF::ReadImageData(uint32_t offset, size_t size) { + if (!size) + return {}; + + if (m_data.ValidOffsetForDataOfSize(offset, size)) + return lldb_private::DataExtractor(m_data, offset, size); + + assert(0); + ProcessSP process_sp(m_process_wp.lock()); + lldb_private::DataExtractor data; + if (process_sp) { + auto data_up = std::make_unique(size, 0); + Status readmem_error; + size_t bytes_read = + process_sp->ReadMemory(offset, data_up->GetBytes(), + data_up->GetByteSize(), readmem_error); + if (bytes_read == size) { + DataBufferSP buffer_sp(data_up.release()); + data.SetData(buffer_sp, 0, buffer_sp->GetByteSize()); + } + } + return data; +} + +bool ObjectFileXCOFF::SetLoadAddress(Target &target, lldb::addr_t value, + bool value_is_offset) { + bool changed = false; + ModuleSP module_sp = GetModule(); + if (module_sp) { + size_t num_loaded_sections = 0; + SectionList *section_list = GetSectionList(); + if (section_list) { + const size_t num_sections = section_list->GetSize(); + size_t sect_idx = 0; + + for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) { + // Iterate through the object file sections to find all of the sections + // that have SHF_ALLOC in their flag bits. + SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); + if (section_sp && !section_sp->IsThreadSpecific()) { + bool use_offset = false; + if (strcmp(section_sp->GetName().AsCString(), ".text") == 0 || + strcmp(section_sp->GetName().AsCString(), ".data") == 0 || + strcmp(section_sp->GetName().AsCString(), ".bss") == 0) + use_offset = true; + + if (target.GetSectionLoadList().SetSectionLoadAddress( + section_sp, (use_offset ? + (section_sp->GetFileOffset() + value) : (section_sp->GetFileAddress() + value)))) + ++num_loaded_sections; + } + } + changed = num_loaded_sections > 0; + } + } + return changed; +} + +bool ObjectFileXCOFF::SetLoadAddressByType(Target &target, lldb::addr_t value, + bool value_is_offset, int type_id) { + bool changed = false; + ModuleSP module_sp = GetModule(); + if (module_sp) { + size_t num_loaded_sections = 0; + SectionList *section_list = GetSectionList(); + if (section_list) { + const size_t num_sections = section_list->GetSize(); + size_t sect_idx = 0; + + for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) { + // Iterate through the object file sections to find all of the sections + // that have SHF_ALLOC in their flag bits. + SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); + if (type_id == 1 && section_sp && strcmp(section_sp->GetName().AsCString(), ".text") == 0) { + if (!section_sp->IsThreadSpecific()) { + if (target.GetSectionLoadList().SetSectionLoadAddress( + section_sp, section_sp->GetFileOffset() + value)) + ++num_loaded_sections; + } + } else if (type_id == 2 && section_sp && strcmp(section_sp->GetName().AsCString(), ".data") == 0) { + if (!section_sp->IsThreadSpecific()) { + if (target.GetSectionLoadList().SetSectionLoadAddress( + section_sp, section_sp->GetFileAddress() + value)) + ++num_loaded_sections; + } + } + } + changed = num_loaded_sections > 0; + } + } + return changed; +} + +ByteOrder ObjectFileXCOFF::GetByteOrder() const { + return eByteOrderBig; +} + +bool ObjectFileXCOFF::IsExecutable() const { + return true; +} + +uint32_t ObjectFileXCOFF::GetAddressByteSize() const { + if (m_xcoff_header.magic == XCOFF::XCOFF64) + return 8; + else if (m_xcoff_header.magic == XCOFF::XCOFF32) + return 4; + return 4; +} + +AddressClass ObjectFileXCOFF::GetAddressClass(addr_t file_addr) { + return AddressClass::eUnknown; +} + +lldb::SymbolType ObjectFileXCOFF::MapSymbolType(llvm::object::SymbolRef::Type sym_type) { + if (sym_type == llvm::object::SymbolRef::ST_Function) + return lldb::eSymbolTypeCode; + else if (sym_type == llvm::object::SymbolRef::ST_Data) + return lldb::eSymbolTypeData; + return lldb::eSymbolTypeInvalid; +} + +void ObjectFileXCOFF::ParseSymtab(Symtab &lldb_symtab) { + SectionList *sect_list = GetSectionList(); + const uint32_t num_syms = m_xcoff_header.nsyms; + uint32_t sidx = 0; + if (num_syms > 0 && m_xcoff_header.symoff > 0) { + const uint32_t symbol_size = XCOFF::SymbolTableEntrySize; + const size_t symbol_data_size = num_syms * symbol_size; + lldb_private::DataExtractor symtab_data = + ReadImageData(m_xcoff_header.symoff, symbol_data_size); + + lldb::offset_t offset = 0; + std::string symbol_name; + Symbol *symbols = lldb_symtab.Resize(num_syms); + llvm::object::symbol_iterator SI = m_binary->symbol_begin(); + for (uint32_t i = 0; i < num_syms; ++i, ++SI) { + xcoff_symbol_t symbol; + const uint32_t symbol_offset = offset; + symbol.value = symtab_data.GetU64(&offset); + symbol.offset = symtab_data.GetU32(&offset); + Expected symbol_name_or_err = m_binary->getStringTableEntry(symbol.offset); + if (!symbol_name_or_err) { + consumeError(symbol_name_or_err.takeError()); + return; + } + StringRef symbol_name_str = symbol_name_or_err.get(); + symbol_name.assign(symbol_name_str.data()); + symbol.sect = symtab_data.GetU16(&offset); + symbol.type = symtab_data.GetU16(&offset); + symbol.storage = symtab_data.GetU8(&offset); + symbol.naux = symtab_data.GetU8(&offset); + // Allow C_HIDEXT TOC symbol, and check others. + if (symbol.storage == XCOFF::C_HIDEXT && strcmp(symbol_name.c_str(), "TOC") != 0) { + if (symbol.naux == 0) + continue; + if (symbol.naux > 1) { + i += symbol.naux; + offset += symbol.naux * symbol_size; + continue; + } + /* Allow XCOFF::C_HIDEXT with following SMC and AT: + StorageMappingClass: XMC_PR (0x0) + Auxiliary Type: AUX_CSECT (0xFB) + */ + xcoff_sym_csect_aux_entry_t symbol_aux; + symbol_aux.section_or_len_low_byte = symtab_data.GetU32(&offset); + symbol_aux.parameter_hash_index = symtab_data.GetU32(&offset); + symbol_aux.type_check_sect_num = symtab_data.GetU16(&offset); + symbol_aux.symbol_alignment_and_type = symtab_data.GetU8(&offset); + symbol_aux.storage_mapping_class = symtab_data.GetU8(&offset); + symbol_aux.section_or_len_high_byte = symtab_data.GetU32(&offset); + symbol_aux.pad = symtab_data.GetU8(&offset); + symbol_aux.aux_type = symtab_data.GetU8(&offset); + offset -= symbol.naux * symbol_size; + if (symbol_aux.storage_mapping_class != XCOFF::XMC_PR || symbol_aux.aux_type != XCOFF::AUX_CSECT) { + i += symbol.naux; + offset += symbol.naux * symbol_size; + continue; + } + } + // Remove the dot prefix for demangle + if (symbol_name_str.size() > 1 && symbol_name_str.data()[0] == '.') { + symbols[sidx].GetMangled().SetValue(ConstString(symbol_name.c_str() + 1)); + } else { + symbols[sidx].GetMangled().SetValue(ConstString(symbol_name.c_str())); + } + if ((int16_t)symbol.sect >= 1) { + Address symbol_addr(sect_list->GetSectionAtIndex((size_t)(symbol.sect - 1)), + (symbol.value - sect_list->GetSectionAtIndex((size_t)(symbol.sect - 1))->GetFileAddress())); + symbols[sidx].GetAddressRef() = symbol_addr; + + Expected sym_type_or_err = SI->getType(); + if (!sym_type_or_err) { + consumeError(sym_type_or_err.takeError()); + return; + } + symbols[sidx].SetType(MapSymbolType(sym_type_or_err.get())); + } + ++sidx; + + if (symbol.naux > 0) { + i += symbol.naux; + offset += symbol.naux * symbol_size; + } + } + lldb_symtab.Resize(sidx); + } +} + +bool ObjectFileXCOFF::IsStripped() { + return false; +} + +void ObjectFileXCOFF::CreateSections(SectionList &unified_section_list) { + if (m_sections_up) + return; + m_sections_up = std::make_unique(); + ModuleSP module_sp(GetModule()); + if (module_sp) { + std::lock_guard guard(module_sp->GetMutex()); + + const uint32_t nsects = m_sect_headers.size(); + ModuleSP module_sp(GetModule()); + for (uint32_t idx = 0; idx < nsects; ++idx) { + llvm::StringRef sect_name = GetSectionName(m_sect_headers[idx]); + ConstString const_sect_name(sect_name); + SectionType section_type = GetSectionType(sect_name, m_sect_headers[idx]); + + SectionSP section_sp(new Section( + module_sp, // Module to which this section belongs + this, // Object file to which this section belongs + idx + 1, // Section ID is the 1 based section index. + const_sect_name, // Name of this section + section_type, + m_sect_headers[idx].vmaddr, // File VM address == addresses as + // they are found in the object file + m_sect_headers[idx].size, // VM size in bytes of this section + m_sect_headers[idx].offset, // Offset to the data for this section in the file + m_sect_headers[idx].size, // Size in bytes of this section as found in the file + 0, // FIXME: alignment + m_sect_headers[idx].flags)); // Flags for this section + + // FIXME + uint32_t permissions = 0; + permissions |= ePermissionsReadable; + if (m_sect_headers[idx].flags & (XCOFF::STYP_DATA | XCOFF::STYP_BSS)) + permissions |= ePermissionsWritable; + if (m_sect_headers[idx].flags & XCOFF::STYP_TEXT) + permissions |= ePermissionsExecutable; + section_sp->SetPermissions(permissions); + + m_sections_up->AddSection(section_sp); + unified_section_list.AddSection(section_sp); + } + } +} + +llvm::StringRef ObjectFileXCOFF::GetSectionName(const section_header_t §) { + llvm::StringRef hdr_name(sect.name, std::size(sect.name)); + hdr_name = hdr_name.split('\0').first; + if (hdr_name.consume_front("/")) { + lldb::offset_t stroff; + if (!to_integer(hdr_name, stroff, 10)) + return ""; + lldb::offset_t string_file_offset = + m_xcoff_header.symoff + (m_xcoff_header.nsyms * static_cast(XCOFF::SymbolTableEntrySize)) + stroff; + if (const char *name = m_data.GetCStr(&string_file_offset)) + return name; + return ""; + } + return hdr_name; +} + +SectionType ObjectFileXCOFF::GetSectionType(llvm::StringRef sect_name, + const section_header_t §) { + if (sect.flags & XCOFF::STYP_TEXT) + return eSectionTypeCode; + if (sect.flags & XCOFF::STYP_DATA) + return eSectionTypeData; + if (sect.flags & XCOFF::STYP_BSS) + return eSectionTypeZeroFill; + if (sect.flags & XCOFF::STYP_DWARF) { + SectionType section_type = + llvm::StringSwitch(sect_name) + .Case(".dwinfo", eSectionTypeDWARFDebugInfo) + .Case(".dwline", eSectionTypeDWARFDebugLine) + .Case(".dwabrev", eSectionTypeDWARFDebugAbbrev) + .Default(eSectionTypeInvalid); + + if (section_type != eSectionTypeInvalid) + return section_type; + } + return eSectionTypeOther; +} + +void ObjectFileXCOFF::Dump(Stream *s) { +} + +ArchSpec ObjectFileXCOFF::GetArchitecture() { + ArchSpec arch_spec = ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); + return arch_spec; +} + +UUID ObjectFileXCOFF::GetUUID() { + return UUID(); +} + +std::optional ObjectFileXCOFF::GetDebugLink() { + return std::nullopt; +} + +uint32_t ObjectFileXCOFF::ParseDependentModules() { + ModuleSP module_sp(GetModule()); + if (!module_sp) + return 0; + + std::lock_guard guard(module_sp->GetMutex()); + if (m_deps_filespec) + return m_deps_filespec->GetSize(); + + // Cache coff binary if it is not done yet. + if (!CreateBinary()) + return 0; + + Log *log = GetLog(LLDBLog::Object); + LLDB_LOG(log, "this = {0}, module = {1} ({2}), file = {3}, binary = {4}", + this, GetModule().get(), GetModule()->GetSpecificationDescription(), + m_file.GetPath(), m_binary.get()); + + m_deps_filespec = FileSpecList(); + + auto ImportFilesOrError = m_binary->getImportFileTable(); + if (!ImportFilesOrError) { + consumeError(ImportFilesOrError.takeError()); + return 0; + } + +#if 0 + StringRef ImportFileTable = ImportFilesOrError.get(); + const char *CurrentStr = ImportFileTable.data(); + const char *TableEnd = ImportFileTable.end(); + const char *Basename = nullptr; + + for (size_t StrIndex = 0; CurrentStr < TableEnd; + ++StrIndex, CurrentStr += strlen(CurrentStr) + 1) { + if (StrIndex >= 3 && StrIndex % 3 == 1) { + // base_name + llvm::StringRef dll_name(CurrentStr); + Basename = CurrentStr; + + // At this moment we only have the base name of the DLL. The full path can + // only be seen after the dynamic loading. Our best guess is Try to get it + // with the help of the object file's directory. + llvm::SmallString<128> dll_fullpath; + FileSpec dll_specs(dll_name); + // FIXME: hack to get libc.a loaded + if (strcmp(CurrentStr, "libc.a") == 0) { + dll_specs.GetDirectory().SetString("/usr/lib"); + } else { + dll_specs.GetDirectory().SetString(m_file.GetDirectory().GetCString()); + } + + if (!llvm::sys::fs::real_path(dll_specs.GetPath(), dll_fullpath)) + //m_deps_filespec->EmplaceBack(dll_fullpath); + m_deps_filespec->EmplaceBack("/usr/lib/libc.a(shr_64.o)"); + else { + // Known DLLs or DLL not found in the object file directory. + m_deps_filespec->EmplaceBack(dll_name); + } + } else if (StrIndex >= 3 && StrIndex % 3 == 2) { + // archive_member_name + if (strcmp(CurrentStr, "") == 0) { + continue; + } + assert(strcmp(Basename, "") != 0); + std::map>::iterator iter = m_deps_base_members.find(std::string(Basename)); + if (iter == m_deps_base_members.end()) { + m_deps_base_members[std::string(Basename)] = std::vector(); + iter = m_deps_base_members.find(std::string(Basename)); + } + iter->second.push_back(std::string(CurrentStr)); + } + } +#endif + return m_deps_filespec->GetSize(); +} + +uint32_t ObjectFileXCOFF::GetDependentModules(FileSpecList &files) { + auto num_modules = ParseDependentModules(); + auto original_size = files.GetSize(); + + for (unsigned i = 0; i < num_modules; ++i) + files.AppendIfUnique(m_deps_filespec->GetFileSpecAtIndex(i)); + + return files.GetSize() - original_size; +} + +Address ObjectFileXCOFF::GetImageInfoAddress(Target *target) { + return Address(); +} + +lldb_private::Address ObjectFileXCOFF::GetEntryPointAddress() { + if (m_entry_point_address.IsValid()) + return m_entry_point_address; + + if (!ParseHeader() || !IsExecutable()) + return m_entry_point_address; + + SectionList *section_list = GetSectionList(); + addr_t vm_addr = m_xcoff_aux_header.EntryPointAddr; + SectionSP section_sp( + section_list->FindSectionContainingFileAddress(vm_addr)); + if (section_sp) { + lldb::offset_t offset_ptr = section_sp->GetFileOffset() + (vm_addr - section_sp->GetFileAddress()); + vm_addr = m_data.GetU64(&offset_ptr); + } + + if (!section_list) + m_entry_point_address.SetOffset(vm_addr); + else + m_entry_point_address.ResolveAddressUsingFileSections(vm_addr, + section_list); + + return m_entry_point_address; +} + +lldb_private::Address ObjectFileXCOFF::GetBaseAddress() { + return lldb_private::Address(); +} + +ObjectFile::Type ObjectFileXCOFF::CalculateType() { + if (m_xcoff_header.flags & XCOFF::F_EXEC) + return eTypeExecutable; + else if (m_xcoff_header.flags & XCOFF::F_SHROBJ) + return eTypeSharedLibrary; + return eTypeUnknown; +} + +ObjectFile::Strata ObjectFileXCOFF::CalculateStrata() { + return eStrataUnknown; +} + +llvm::StringRef +ObjectFileXCOFF::StripLinkerSymbolAnnotations(llvm::StringRef symbol_name) const { + return llvm::StringRef(); +} + +void ObjectFileXCOFF::RelocateSection(lldb_private::Section *section) +{ +} + +std::vector +ObjectFileXCOFF::GetLoadableData(Target &target) { + std::vector loadables; + return loadables; +} + +lldb::WritableDataBufferSP +ObjectFileXCOFF::MapFileDataWritable(const FileSpec &file, uint64_t Size, + uint64_t Offset) { + return FileSystem::Instance().CreateWritableDataBuffer(file.GetPath(), Size, + Offset); +} + +ObjectFileXCOFF::ObjectFileXCOFF(const lldb::ModuleSP &module_sp, + DataBufferSP data_sp, lldb::offset_t data_offset, + const FileSpec *file, lldb::offset_t file_offset, + lldb::offset_t length) + : ObjectFile(module_sp, file, file_offset, length, data_sp, data_offset), + m_xcoff_header(), m_sect_headers(), m_deps_filespec(), m_deps_base_members(), + m_entry_point_address() { + ::memset(&m_xcoff_header, 0, sizeof(m_xcoff_header)); + if (file) + m_file = *file; +} + +ObjectFileXCOFF::ObjectFileXCOFF(const lldb::ModuleSP &module_sp, + DataBufferSP header_data_sp, + const lldb::ProcessSP &process_sp, + addr_t header_addr) + : ObjectFile(module_sp, process_sp, header_addr, header_data_sp), + m_xcoff_header(), m_sect_headers(), m_deps_filespec(), m_deps_base_members(), + m_entry_point_address() { + ::memset(&m_xcoff_header, 0, sizeof(m_xcoff_header)); +} diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.h b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.h new file mode 100644 index 0000000000000..5a12d16886489 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.h @@ -0,0 +1,243 @@ +//===-- ObjectFileXCOFF.h --------------------------------------- -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_OBJECTFILE_XCOFF_OBJECTFILEXCOFF_H +#define LLDB_SOURCE_PLUGINS_OBJECTFILE_XCOFF_OBJECTFILEXCOFF_H + +#include + +#include + +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/UUID.h" +#include "lldb/lldb-private.h" +#include "llvm/Object/XCOFFObjectFile.h" + +/// \class ObjectFileXCOFF +/// Generic XCOFF object file reader. +/// +/// This class provides a generic XCOFF (32/64 bit) reader plugin implementing +/// the ObjectFile protocol. +class ObjectFileXCOFF : public lldb_private::ObjectFile { +public: + // Static Functions + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "xcoff"; } + + static llvm::StringRef GetPluginDescriptionStatic() { + return "XCOFF object file reader."; + } + + static lldb_private::ObjectFile * + CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP data_sp, + lldb::offset_t data_offset, const lldb_private::FileSpec *file, + lldb::offset_t file_offset, lldb::offset_t length); + + static lldb_private::ObjectFile *CreateMemoryInstance( + const lldb::ModuleSP &module_sp, lldb::WritableDataBufferSP data_sp, + const lldb::ProcessSP &process_sp, lldb::addr_t header_addr); + + static size_t GetModuleSpecifications(const lldb_private::FileSpec &file, + lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb_private::ModuleSpecList &specs); + + static bool MagicBytesMatch(lldb::DataBufferSP &data_sp, lldb::addr_t offset, + lldb::addr_t length); + + static lldb::SymbolType MapSymbolType(llvm::object::SymbolRef::Type sym_type); + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + // LLVM RTTI support + static char ID; + bool isA(const void *ClassID) const override { + return ClassID == &ID || ObjectFile::isA(ClassID); + } + static bool classof(const ObjectFile *obj) { return obj->isA(&ID); } + + // ObjectFile Protocol. + bool ParseHeader() override; + + bool SetLoadAddress(lldb_private::Target &target, lldb::addr_t value, + bool value_is_offset) override; + + bool SetLoadAddressByType(lldb_private::Target &target, lldb::addr_t value, + bool value_is_offset, int type_id) override; + + lldb::ByteOrder GetByteOrder() const override; + + bool IsExecutable() const override; + + uint32_t GetAddressByteSize() const override; + + lldb_private::AddressClass GetAddressClass(lldb::addr_t file_addr) override; + + void ParseSymtab(lldb_private::Symtab &symtab) override; + + bool IsStripped() override; + + void CreateSections(lldb_private::SectionList &unified_section_list) override; + + void Dump(lldb_private::Stream *s) override; + + lldb_private::ArchSpec GetArchitecture() override; + + lldb_private::UUID GetUUID() override; + + /// Return the contents of the .gnu_debuglink section, if the object file + /// contains it. + std::optional GetDebugLink(); + + uint32_t GetDependentModules(lldb_private::FileSpecList &files) override; + + lldb_private::Address + GetImageInfoAddress(lldb_private::Target *target) override; + + lldb_private::Address GetEntryPointAddress() override; + + lldb_private::Address GetBaseAddress() override; + + ObjectFile::Type CalculateType() override; + + ObjectFile::Strata CalculateStrata() override; + + llvm::StringRef + StripLinkerSymbolAnnotations(llvm::StringRef symbol_name) const override; + + void RelocateSection(lldb_private::Section *section) override; + + lldb_private::DataExtractor ReadImageData(uint32_t offset, size_t size); + + ObjectFileXCOFF(const lldb::ModuleSP &module_sp, lldb::DataBufferSP data_sp, + lldb::offset_t data_offset, const lldb_private::FileSpec *file, + lldb::offset_t offset, lldb::offset_t length); + + ObjectFileXCOFF(const lldb::ModuleSP &module_sp, + lldb::DataBufferSP header_data_sp, + const lldb::ProcessSP &process_sp, lldb::addr_t header_addr); + +protected: + + typedef struct xcoff_header { + uint16_t magic; + uint16_t nsects; + uint32_t modtime; + uint64_t symoff; + uint32_t nsyms; + uint16_t auxhdrsize; + uint16_t flags; + } xcoff_header_t; + + typedef struct xcoff_aux_header { + uint16_t AuxMagic; + uint16_t Version; + uint32_t ReservedForDebugger; + uint64_t TextStartAddr; + uint64_t DataStartAddr; + uint64_t TOCAnchorAddr; + uint16_t SecNumOfEntryPoint; + uint16_t SecNumOfText; + uint16_t SecNumOfData; + uint16_t SecNumOfTOC; + uint16_t SecNumOfLoader; + uint16_t SecNumOfBSS; + uint16_t MaxAlignOfText; + uint16_t MaxAlignOfData; + uint16_t ModuleType; + uint8_t CpuFlag; + uint8_t CpuType; + uint8_t TextPageSize; + uint8_t DataPageSize; + uint8_t StackPageSize; + uint8_t FlagAndTDataAlignment; + uint64_t TextSize; + uint64_t InitDataSize; + uint64_t BssDataSize; + uint64_t EntryPointAddr; + uint64_t MaxStackSize; + uint64_t MaxDataSize; + uint16_t SecNumOfTData; + uint16_t SecNumOfTBSS; + uint16_t XCOFF64Flag; + } xcoff_aux_header_t; + + typedef struct section_header { + char name[8]; + uint64_t phyaddr; // Physical Addr + uint64_t vmaddr; // Virtual Addr + uint64_t size; // Section size + uint64_t offset; // File offset to raw data + uint64_t reloff; // Offset to relocations + uint64_t lineoff; // Offset to line table entries + uint32_t nreloc; // Number of relocation entries + uint32_t nline; // Number of line table entries + uint32_t flags; + } section_header_t; + + typedef struct xcoff_symbol { + uint64_t value; + uint32_t offset; + uint16_t sect; + uint16_t type; + uint8_t storage; + uint8_t naux; + } xcoff_symbol_t; + + typedef struct xcoff_sym_csect_aux_entry { + uint32_t section_or_len_low_byte; + uint32_t parameter_hash_index; + uint16_t type_check_sect_num; + uint8_t symbol_alignment_and_type; + uint8_t storage_mapping_class; + uint32_t section_or_len_high_byte; + uint8_t pad; + uint8_t aux_type; + } xcoff_sym_csect_aux_entry_t; + + static bool ParseXCOFFHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset_ptr, + xcoff_header_t &xcoff_header); + bool ParseXCOFFOptionalHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset_ptr); + bool ParseSectionHeaders(uint32_t offset); + + std::vector + GetLoadableData(lldb_private::Target &target) override; + + static lldb::WritableDataBufferSP + MapFileDataWritable(const lldb_private::FileSpec &file, uint64_t Size, + uint64_t Offset); + llvm::StringRef GetSectionName(const section_header_t §); + static lldb::SectionType GetSectionType(llvm::StringRef sect_name, + const section_header_t §); + + uint32_t ParseDependentModules(); + typedef std::vector SectionHeaderColl; + +private: + bool CreateBinary(); + + xcoff_header_t m_xcoff_header; + xcoff_aux_header_t m_xcoff_aux_header; + SectionHeaderColl m_sect_headers; + std::unique_ptr m_binary; + lldb_private::Address m_entry_point_address; + std::optional m_deps_filespec; + std::map> m_deps_base_members; +}; + +#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_ELF_OBJECTFILEELF_H diff --git a/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp b/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp index e026ffefd645e..106e38b6e25ae 100644 --- a/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp +++ b/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp @@ -227,7 +227,7 @@ ThreadSP OperatingSystemPython::CreateThreadFromThreadInfo( ThreadList &old_thread_list, std::vector &core_used_map, bool *did_create_ptr) { ThreadSP thread_sp; - tid_t tid = LLDB_INVALID_THREAD_ID; + lldb::tid_t tid = LLDB_INVALID_THREAD_ID; if (!thread_dict.GetValueForKeyAsInteger("tid", tid)) return ThreadSP(); diff --git a/lldb/source/Plugins/Platform/AIX/CMakeLists.txt b/lldb/source/Plugins/Platform/AIX/CMakeLists.txt new file mode 100644 index 0000000000000..85ff0a315eabd --- /dev/null +++ b/lldb/source/Plugins/Platform/AIX/CMakeLists.txt @@ -0,0 +1,13 @@ +add_definitions("-D_ALL_SOURCE") + +add_lldb_library(lldbPluginPlatformAIX PLUGIN + PlatformAIX.cpp + + LINK_LIBS + lldbBreakpoint + lldbCore + lldbHost + lldbInterpreter + lldbTarget + lldbPluginPlatformPOSIX + ) diff --git a/lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp b/lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp new file mode 100644 index 0000000000000..b6b08b73bec41 --- /dev/null +++ b/lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp @@ -0,0 +1,471 @@ +//===-- PlatformAIX.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 "PlatformAIX.h" +#include "lldb/Host/Config.h" + +#include +#if LLDB_ENABLE_POSIX +#include +#endif + +#include "Utility/ARM64_DWARF_Registers.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/StreamString.h" + +// Define these constants from AIX mman.h for use when targeting remote aix +// systems even when host has different values. + +#if defined(__AIX__) +#include +#endif + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::platform_aix; + +LLDB_PLUGIN_DEFINE(PlatformAIX) + +static uint32_t g_initialize_count = 0; + + +PlatformSP PlatformAIX::CreateInstance(bool force, const ArchSpec *arch) { + Log *log = GetLog(LLDBLog::Platform); + LLDB_LOG(log, "force = {0}, arch=({1}, {2})", force, + arch ? arch->GetArchitectureName() : "", + arch ? arch->GetTriple().getTriple() : ""); + + bool create = force; + if (!create && arch && arch->IsValid()) { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getOS()) { + case llvm::Triple::AIX: + create = true; + break; + + default: + break; + } + } + + LLDB_LOG(log, "create = {0}", create); + if (create) { + return PlatformSP(new PlatformAIX(false)); + } + return PlatformSP(); +} + +llvm::StringRef PlatformAIX::GetPluginDescriptionStatic(bool is_host) { + if (is_host) + return "Local AIX user platform plug-in."; + return "Remote AIX user platform plug-in."; +} + +void PlatformAIX::Initialize() { + PlatformPOSIX::Initialize(); + + if (g_initialize_count++ == 0) { +#if defined(__AIX__) + PlatformSP default_platform_sp(new PlatformAIX(true)); + default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); + Platform::SetHostPlatform(default_platform_sp); +#endif + PluginManager::RegisterPlugin( + PlatformAIX::GetPluginNameStatic(false), + PlatformAIX::GetPluginDescriptionStatic(false), + PlatformAIX::CreateInstance, nullptr); + } +} + +void PlatformAIX::Terminate() { + if (g_initialize_count > 0) { + if (--g_initialize_count == 0) { + PluginManager::UnregisterPlugin(PlatformAIX::CreateInstance); + } + } + + PlatformPOSIX::Terminate(); +} + +/// Default Constructor +PlatformAIX::PlatformAIX(bool is_host) + : PlatformPOSIX(is_host) // This is the local host platform +{ + if (is_host) { + ArchSpec hostArch = HostInfo::GetArchitecture(HostInfo::eArchKindDefault); + m_supported_architectures.push_back(hostArch); + if (hostArch.GetTriple().isArch64Bit()) { + m_supported_architectures.push_back( + HostInfo::GetArchitecture(HostInfo::eArchKind32)); + } + } else { + m_supported_architectures = CreateArchList( + {llvm::Triple::x86_64, llvm::Triple::x86, llvm::Triple::arm, + llvm::Triple::aarch64, llvm::Triple::mips64, llvm::Triple::mips64, + llvm::Triple::hexagon, llvm::Triple::mips, llvm::Triple::mips64el, + llvm::Triple::mipsel, llvm::Triple::systemz}, + llvm::Triple::AIX); + } +} + +std::vector +PlatformAIX::GetSupportedArchitectures(const ArchSpec &process_host_arch) { + if (m_remote_platform_sp) + return m_remote_platform_sp->GetSupportedArchitectures(process_host_arch); + return m_supported_architectures; +} + +void PlatformAIX::GetStatus(Stream &strm) { + Platform::GetStatus(strm); + +#if LLDB_ENABLE_POSIX + // Display local kernel information only when we are running in host mode. + // Otherwise, we would end up printing non-AIX information (when running on + // Mac OS for example). + if (IsHost()) { + struct utsname un; + + if (uname(&un)) + return; + + strm.Printf(" Kernel: %s\n", un.sysname); + strm.Printf(" Release: %s\n", un.release); + strm.Printf(" Version: %s\n", un.version); + } +#endif +} + +uint32_t +PlatformAIX::GetResumeCountForLaunchInfo(ProcessLaunchInfo &launch_info) { + uint32_t resume_count = 0; + + // Always resume past the initial stop when we use eLaunchFlagDebug + if (launch_info.GetFlags().Test(eLaunchFlagDebug)) { + // Resume past the stop for the final exec into the true inferior. + ++resume_count; + } + + // If we're not launching a shell, we're done. + const FileSpec &shell = launch_info.GetShell(); + if (!shell) + return resume_count; + + std::string shell_string = shell.GetPath(); + // We're in a shell, so for sure we have to resume past the shell exec. + ++resume_count; + + // Figure out what shell we're planning on using. + const char *shell_name = strrchr(shell_string.c_str(), '/'); + if (shell_name == nullptr) + shell_name = shell_string.c_str(); + else + shell_name++; + + if (strcmp(shell_name, "csh") == 0 || strcmp(shell_name, "tcsh") == 0 || + strcmp(shell_name, "zsh") == 0 || strcmp(shell_name, "sh") == 0) { + // These shells seem to re-exec themselves. Add another resume. + ++resume_count; + } + + return resume_count; +} + +bool PlatformAIX::CanDebugProcess() { + if (IsHost()) { + return true; + } else { + // If we're connected, we can debug. + return IsConnected(); + } +} + +void PlatformAIX::CalculateTrapHandlerSymbolNames() { + m_trap_handlers.push_back(ConstString("_sigtramp")); + m_trap_handlers.push_back(ConstString("__kernel_rt_sigreturn")); + m_trap_handlers.push_back(ConstString("__restore_rt")); +} + +static lldb::UnwindPlanSP GetAArch64TrapHanlderUnwindPlan(ConstString name) { + UnwindPlanSP unwind_plan_sp; + if (name != "__kernel_rt_sigreturn") + return unwind_plan_sp; + + UnwindPlan::RowSP row = std::make_shared(); + row->SetOffset(0); + + // In the signal trampoline frame, sp points to an rt_sigframe[1], which is: + // - 128-byte siginfo struct + // - ucontext struct: + // - 8-byte long (uc_flags) + // - 8-byte pointer (uc_link) + // - 24-byte stack_t + // - 128-byte signal set + // - 8 bytes of padding because sigcontext has 16-byte alignment + // - sigcontext/mcontext_t + // [1] + // https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/signal.c + int32_t offset = 128 + 8 + 8 + 24 + 128 + 8; + // Then sigcontext[2] is: + // - 8 byte fault address + // - 31 8 byte registers + // - 8 byte sp + // - 8 byte pc + // [2] + // https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/sigcontext.h + + // Skip fault address + offset += 8; + row->GetCFAValue().SetIsRegisterPlusOffset(arm64_dwarf::sp, offset); + + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x0, 0 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x1, 1 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x2, 2 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x3, 3 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x4, 4 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x5, 5 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x6, 6 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x7, 7 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x8, 8 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x9, 9 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x10, 10 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x11, 11 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x12, 12 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x13, 13 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x14, 14 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x15, 15 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x16, 16 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x17, 17 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x18, 18 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x19, 19 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x20, 20 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x21, 21 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x22, 22 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x23, 23 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x24, 24 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x25, 25 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x26, 26 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x27, 27 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x28, 28 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::fp, 29 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x30, 30 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::sp, 31 * 8, false); + row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::pc, 32 * 8, false); + + // The sigcontext may also contain floating point and SVE registers. + // However this would require a dynamic unwind plan so they are not included + // here. + + unwind_plan_sp = std::make_shared(eRegisterKindDWARF); + unwind_plan_sp->AppendRow(row); + unwind_plan_sp->SetSourceName("AArch64 AIX sigcontext"); + unwind_plan_sp->SetSourcedFromCompiler(eLazyBoolYes); + // Because sp is the same throughout the function + unwind_plan_sp->SetUnwindPlanValidAtAllInstructions(eLazyBoolYes); + unwind_plan_sp->SetUnwindPlanForSignalTrap(eLazyBoolYes); + + return unwind_plan_sp; +} + +lldb::UnwindPlanSP +PlatformAIX::GetTrapHandlerUnwindPlan(const llvm::Triple &triple, + ConstString name) { + if (triple.isAArch64()) + return GetAArch64TrapHanlderUnwindPlan(name); + + return {}; +} + +MmapArgList PlatformAIX::GetMmapArgumentList(const ArchSpec &arch, + addr_t addr, addr_t length, + unsigned prot, unsigned flags, + addr_t fd, addr_t offset) { +#if defined(__AIX__) + unsigned flags_platform = MAP_VARIABLE | MAP_PRIVATE | MAP_ANONYMOUS; +#else + unsigned flags_platform = 0; +#endif + MmapArgList args({addr, length, prot, flags_platform, fd, offset}); + return args; +} + +CompilerType PlatformAIX::GetSiginfoType(const llvm::Triple &triple) { + if (!m_type_system_up) + m_type_system_up.reset(new TypeSystemClang("siginfo", triple)); + TypeSystemClang *ast = m_type_system_up.get(); + + bool si_errno_then_code = true; + + switch (triple.getArch()) { + case llvm::Triple::mips: + case llvm::Triple::mipsel: + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + // mips has si_code and si_errno swapped + si_errno_then_code = false; + break; + default: + break; + } + + // generic types + CompilerType int_type = ast->GetBasicType(eBasicTypeInt); + CompilerType uint_type = ast->GetBasicType(eBasicTypeUnsignedInt); + CompilerType short_type = ast->GetBasicType(eBasicTypeShort); + CompilerType long_type = ast->GetBasicType(eBasicTypeLong); + CompilerType voidp_type = ast->GetBasicType(eBasicTypeVoid).GetPointerType(); + + // platform-specific types + CompilerType &pid_type = int_type; + CompilerType &uid_type = uint_type; + CompilerType &clock_type = long_type; + CompilerType &band_type = long_type; + + CompilerType sigval_type = ast->CreateRecordType( + nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "__lldb_sigval_t", + llvm::to_underlying(clang::TagTypeKind::Union), lldb::eLanguageTypeC); + ast->StartTagDeclarationDefinition(sigval_type); + ast->AddFieldToRecordType(sigval_type, "sival_int", int_type, + lldb::eAccessPublic, 0); + ast->AddFieldToRecordType(sigval_type, "sival_ptr", voidp_type, + lldb::eAccessPublic, 0); + ast->CompleteTagDeclarationDefinition(sigval_type); + + CompilerType sigfault_bounds_type = ast->CreateRecordType( + nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "", + llvm::to_underlying(clang::TagTypeKind::Union), lldb::eLanguageTypeC); + ast->StartTagDeclarationDefinition(sigfault_bounds_type); + ast->AddFieldToRecordType(sigfault_bounds_type, "_addr_bnd", + ast->CreateStructForIdentifier(ConstString(), + { + {"_lower", voidp_type}, + {"_upper", voidp_type}, + }), + lldb::eAccessPublic, 0); + ast->AddFieldToRecordType(sigfault_bounds_type, "_pkey", uint_type, + lldb::eAccessPublic, 0); + ast->CompleteTagDeclarationDefinition(sigfault_bounds_type); + + // siginfo_t + CompilerType siginfo_type = ast->CreateRecordType( + nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "__lldb_siginfo_t", + llvm::to_underlying(clang::TagTypeKind::Struct), lldb::eLanguageTypeC); + ast->StartTagDeclarationDefinition(siginfo_type); + ast->AddFieldToRecordType(siginfo_type, "si_signo", int_type, + lldb::eAccessPublic, 0); + + if (si_errno_then_code) { + ast->AddFieldToRecordType(siginfo_type, "si_errno", int_type, + lldb::eAccessPublic, 0); + ast->AddFieldToRecordType(siginfo_type, "si_code", int_type, + lldb::eAccessPublic, 0); + } else { + ast->AddFieldToRecordType(siginfo_type, "si_code", int_type, + lldb::eAccessPublic, 0); + ast->AddFieldToRecordType(siginfo_type, "si_errno", int_type, + lldb::eAccessPublic, 0); + } + + // the structure is padded on 64-bit arches to fix alignment + if (triple.isArch64Bit()) + ast->AddFieldToRecordType(siginfo_type, "__pad0", int_type, + lldb::eAccessPublic, 0); + + // union used to hold the signal data + CompilerType union_type = ast->CreateRecordType( + nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "", + llvm::to_underlying(clang::TagTypeKind::Union), lldb::eLanguageTypeC); + ast->StartTagDeclarationDefinition(union_type); + + ast->AddFieldToRecordType( + union_type, "_kill", + ast->CreateStructForIdentifier(ConstString(), + { + {"si_pid", pid_type}, + {"si_uid", uid_type}, + }), + lldb::eAccessPublic, 0); + + ast->AddFieldToRecordType( + union_type, "_timer", + ast->CreateStructForIdentifier(ConstString(), + { + {"si_tid", int_type}, + {"si_overrun", int_type}, + {"si_sigval", sigval_type}, + }), + lldb::eAccessPublic, 0); + + ast->AddFieldToRecordType( + union_type, "_rt", + ast->CreateStructForIdentifier(ConstString(), + { + {"si_pid", pid_type}, + {"si_uid", uid_type}, + {"si_sigval", sigval_type}, + }), + lldb::eAccessPublic, 0); + + ast->AddFieldToRecordType( + union_type, "_sigchld", + ast->CreateStructForIdentifier(ConstString(), + { + {"si_pid", pid_type}, + {"si_uid", uid_type}, + {"si_status", int_type}, + {"si_utime", clock_type}, + {"si_stime", clock_type}, + }), + lldb::eAccessPublic, 0); + + ast->AddFieldToRecordType( + union_type, "_sigfault", + ast->CreateStructForIdentifier(ConstString(), + { + {"si_addr", voidp_type}, + {"si_addr_lsb", short_type}, + {"_bounds", sigfault_bounds_type}, + }), + lldb::eAccessPublic, 0); + + ast->AddFieldToRecordType( + union_type, "_sigpoll", + ast->CreateStructForIdentifier(ConstString(), + { + {"si_band", band_type}, + {"si_fd", int_type}, + }), + lldb::eAccessPublic, 0); + + // NB: SIGSYS is not present on ia64 but we don't seem to support that + ast->AddFieldToRecordType( + union_type, "_sigsys", + ast->CreateStructForIdentifier(ConstString(), + { + {"_call_addr", voidp_type}, + {"_syscall", int_type}, + {"_arch", uint_type}, + }), + lldb::eAccessPublic, 0); + + ast->CompleteTagDeclarationDefinition(union_type); + ast->AddFieldToRecordType(siginfo_type, "_sifields", union_type, + lldb::eAccessPublic, 0); + + ast->CompleteTagDeclarationDefinition(siginfo_type); + return siginfo_type; +} diff --git a/lldb/source/Plugins/Platform/AIX/PlatformAIX.h b/lldb/source/Plugins/Platform/AIX/PlatformAIX.h new file mode 100644 index 0000000000000..3ae8089a48d71 --- /dev/null +++ b/lldb/source/Plugins/Platform/AIX/PlatformAIX.h @@ -0,0 +1,74 @@ +//===-- PlatformAIX.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PLATFORM_AIX_PLATFORMAIX_H +#define LLDB_SOURCE_PLUGINS_PLATFORM_AIX_PLATFORMAIX_H + +#include "Plugins/Platform/POSIX/PlatformPOSIX.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" + +namespace lldb_private { +namespace platform_aix { + +class PlatformAIX : public PlatformPOSIX { +public: + PlatformAIX(bool is_host); + + static void Initialize(); + + static void Terminate(); + + // lldb_private::PluginInterface functions + static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch); + + static llvm::StringRef GetPluginNameStatic(bool is_host) { + return is_host ? Platform::GetHostPlatformName() : "remote-AIX"; + } + + static llvm::StringRef GetPluginDescriptionStatic(bool is_host); + + llvm::StringRef GetPluginName() override { + return GetPluginNameStatic(IsHost()); + } + + // lldb_private::Platform functions + llvm::StringRef GetDescription() override { + return GetPluginDescriptionStatic(IsHost()); + } + + void GetStatus(Stream &strm) override; + + std::vector + GetSupportedArchitectures(const ArchSpec &process_host_arch) override; + + uint32_t GetResumeCountForLaunchInfo(ProcessLaunchInfo &launch_info) override; + + bool CanDebugProcess() override; + + void CalculateTrapHandlerSymbolNames() override; + + lldb::UnwindPlanSP GetTrapHandlerUnwindPlan(const llvm::Triple &triple, + ConstString name) override; + + MmapArgList GetMmapArgumentList(const ArchSpec &arch, lldb::addr_t addr, + lldb::addr_t length, unsigned prot, + unsigned flags, lldb::addr_t fd, + lldb::addr_t offset) override; + + CompilerType GetSiginfoType(const llvm::Triple &triple) override; + + std::vector m_supported_architectures; + +private: + std::unique_ptr m_type_system_up; +}; + +} // namespace platform_AIX +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PLATFORM_AIX_PLATFORMAIX_H diff --git a/lldb/source/Plugins/Platform/CMakeLists.txt b/lldb/source/Plugins/Platform/CMakeLists.txt index 6869587f917eb..9d0afd97cff85 100644 --- a/lldb/source/Plugins/Platform/CMakeLists.txt +++ b/lldb/source/Plugins/Platform/CMakeLists.txt @@ -8,3 +8,4 @@ add_subdirectory(OpenBSD) add_subdirectory(POSIX) add_subdirectory(QemuUser) add_subdirectory(Windows) +add_subdirectory(AIX) diff --git a/lldb/source/Plugins/Process/AIX/CMakeLists.txt b/lldb/source/Plugins/Process/AIX/CMakeLists.txt new file mode 100644 index 0000000000000..e9d83266f5857 --- /dev/null +++ b/lldb/source/Plugins/Process/AIX/CMakeLists.txt @@ -0,0 +1,19 @@ +add_definitions("-D_ALL_SOURCE") + +add_lldb_library(lldbPluginProcessAIX + NativeProcessAIX.cpp + NativeRegisterContextAIX.cpp + NativeRegisterContextAIX_ppc64.cpp + NativeThreadAIX.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbSymbol + lldbTarget + lldbUtility + lldbPluginProcessPOSIX + lldbPluginProcessUtility + LINK_COMPONENTS + Support + ) diff --git a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp new file mode 100644 index 0000000000000..882f20d30a3bf --- /dev/null +++ b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp @@ -0,0 +1,2048 @@ +//===-- NativeProcessAIX.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 "NativeProcessAIX.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "NativeThreadAIX.h" +#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" +//#include "Plugins/Process/Utility/LinuxProcMaps.h" +//#include "Procfs.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/ProcessLaunchInfo.h" +#include "lldb/Host/PseudoTerminal.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Host/common/NativeRegisterContext.h" +#include "lldb/Host/aix/Ptrace.h" +//#include "lldb/Host/linux/Host.h" +//#include "lldb/Host/linux/Uio.h" +#include "lldb/Host/posix/ProcessLauncherPosixFork.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/StringExtractor.h" +#include "llvm/ADT/ScopeExit.h" +#include "llvm/Support/Errno.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Threading.h" + +#include +#include +#include +#include +//#include +#include +#include +#include +#include + +#ifdef __aarch64__ +#include +#include +#endif + +// Support hardware breakpoints in case it has not been defined +#ifndef TRAP_HWBKPT +#define TRAP_HWBKPT 4 +#endif + +#ifndef HWCAP2_MTE +#define HWCAP2_MTE (1 << 18) +#endif + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_aix; +using namespace llvm; + +// Private bits we only need internally. + +static bool ProcessVmReadvSupported() { + static bool is_supported; + static llvm::once_flag flag; + + llvm::call_once(flag, [] { + Log *log = GetLog(POSIXLog::Process); + + uint32_t source = 0x47424742; + uint32_t dest = 0; + + struct iovec local, remote; + remote.iov_base = &source; + local.iov_base = &dest; + remote.iov_len = local.iov_len = sizeof source; + +#if 0 + // We shall try if cross-process-memory reads work by attempting to read a + // value from our own process. + ssize_t res = process_vm_readv(getpid(), &local, 1, &remote, 1, 0); + is_supported = (res == sizeof(source) && source == dest); + if (is_supported) + LLDB_LOG(log, + "Detected kernel support for process_vm_readv syscall. " + "Fast memory reads enabled."); + else + LLDB_LOG(log, + "syscall process_vm_readv failed (error: {0}). Fast memory " + "reads disabled.", + llvm::sys::StrError()); +#endif + }); + + return is_supported; +} + +static void MaybeLogLaunchInfo(const ProcessLaunchInfo &info) { + Log *log = GetLog(POSIXLog::Process); + if (!log) + return; + + if (const FileAction *action = info.GetFileActionForFD(STDIN_FILENO)) + LLDB_LOG(log, "setting STDIN to '{0}'", action->GetFileSpec()); + else + LLDB_LOG(log, "leaving STDIN as is"); + + if (const FileAction *action = info.GetFileActionForFD(STDOUT_FILENO)) + LLDB_LOG(log, "setting STDOUT to '{0}'", action->GetFileSpec()); + else + LLDB_LOG(log, "leaving STDOUT as is"); + + if (const FileAction *action = info.GetFileActionForFD(STDERR_FILENO)) + LLDB_LOG(log, "setting STDERR to '{0}'", action->GetFileSpec()); + else + LLDB_LOG(log, "leaving STDERR as is"); + + int i = 0; + for (const char **args = info.GetArguments().GetConstArgumentVector(); *args; + ++args, ++i) + LLDB_LOG(log, "arg {0}: '{1}'", i, *args); +} + +static void DisplayBytes(StreamString &s, void *bytes, uint32_t count) { + uint8_t *ptr = (uint8_t *)bytes; + const uint32_t loop_count = std::min(DEBUG_PTRACE_MAXBYTES, count); + for (uint32_t i = 0; i < loop_count; i++) { + s.Printf("[%x]", *ptr); + ptr++; + } +} + +static void PtraceDisplayBytes(int &req, void *data, size_t data_size) { + Log *log = GetLog(POSIXLog::Ptrace); + if (!log) + return; + StreamString buf; + + switch (req) { + case PTRACE_POKETEXT: { + DisplayBytes(buf, &data, 8); + LLDB_LOGV(log, "PTRACE_POKETEXT {0}", buf.GetData()); + break; + } + case PTRACE_POKEDATA: { + DisplayBytes(buf, &data, 8); + LLDB_LOGV(log, "PTRACE_POKEDATA {0}", buf.GetData()); + break; + } + case PTRACE_POKEUSER: { + DisplayBytes(buf, &data, 8); + LLDB_LOGV(log, "PTRACE_POKEUSER {0}", buf.GetData()); + break; + } + case PTRACE_SETREGS: { + DisplayBytes(buf, data, data_size); + LLDB_LOGV(log, "PTRACE_SETREGS {0}", buf.GetData()); + break; + } + case PTRACE_SETFPREGS: { + DisplayBytes(buf, data, data_size); + LLDB_LOGV(log, "PTRACE_SETFPREGS {0}", buf.GetData()); + break; + } +#if 0 + case PTRACE_SETSIGINFO: { + DisplayBytes(buf, data, sizeof(siginfo_t)); + LLDB_LOGV(log, "PTRACE_SETSIGINFO {0}", buf.GetData()); + break; + } +#endif + case PTRACE_SETREGSET: { + // Extract iov_base from data, which is a pointer to the struct iovec + DisplayBytes(buf, *(void **)data, data_size); + LLDB_LOGV(log, "PTRACE_SETREGSET {0}", buf.GetData()); + break; + } + default: {} + } +} + +static constexpr unsigned k_ptrace_word_size = sizeof(void *); +static_assert(sizeof(long) >= k_ptrace_word_size, + "Size of long must be larger than ptrace word size"); + +// Simple helper function to ensure flags are enabled on the given file +// descriptor. +static Status EnsureFDFlags(int fd, int flags) { + Status error; + + int status = fcntl(fd, F_GETFL); + if (status == -1) { + error.SetErrorToErrno(); + return error; + } + + if (fcntl(fd, F_SETFL, status | flags) == -1) { + error.SetErrorToErrno(); + return error; + } + + return error; +} + +#if 0 +static llvm::Error AddPtraceScopeNote(llvm::Error original_error) { + Expected ptrace_scope = GetPtraceScope(); + if (auto E = ptrace_scope.takeError()) { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "error reading value of ptrace_scope: {0}", E); + + // The original error is probably more interesting than not being able to + // read or interpret ptrace_scope. + return original_error; + } + + // We only have suggestions to provide for 1-3. + switch (*ptrace_scope) { + case 1: + case 2: + return llvm::createStringError( + std::error_code(errno, std::generic_category()), + "The current value of ptrace_scope is %d, which can cause ptrace to " + "fail to attach to a running process. To fix this, run:\n" + "\tsudo sysctl -w kernel.yama.ptrace_scope=0\n" + "For more information, see: " + "https://www.kernel.org/doc/Documentation/security/Yama.txt.", + *ptrace_scope); + case 3: + return llvm::createStringError( + std::error_code(errno, std::generic_category()), + "The current value of ptrace_scope is 3, which will cause ptrace to " + "fail to attach to a running process. This value cannot be changed " + "without rebooting.\n" + "For more information, see: " + "https://www.kernel.org/doc/Documentation/security/Yama.txt."); + case 0: + default: + return original_error; + } +} +#endif + +NativeProcessAIX::Manager::Manager(MainLoop &mainloop) + : NativeProcessProtocol::Manager(mainloop) { + Status status; + m_sigchld_handle = mainloop.RegisterSignal( + SIGCHLD, [this](MainLoopBase &) { SigchldHandler(); }, status); + assert(m_sigchld_handle && status.Success()); +} + +// Public Static Methods + +llvm::Expected> +NativeProcessAIX::Manager::Launch(ProcessLaunchInfo &launch_info, + NativeDelegate &native_delegate) { + Log *log = GetLog(POSIXLog::Process); + + MaybeLogLaunchInfo(launch_info); + + Status status; + ::pid_t pid = ProcessLauncherPosixFork() + .LaunchProcess(launch_info, status) + .GetProcessId(); + LLDB_LOG(log, "pid = {0:x}", pid); + if (status.Fail()) { + LLDB_LOG(log, "failed to launch process: {0}", status); + return status.ToError(); + } + + // Wait for the child process to trap on its call to execve. + int wstatus = 0; + ::pid_t wpid = llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, &wstatus, 0); + assert(wpid == pid); + UNUSED_IF_ASSERT_DISABLED(wpid); + if (!WIFSTOPPED(wstatus)) { + LLDB_LOG(log, "Could not sync with inferior process: wstatus={1}", + WaitStatus::Decode(wstatus)); + return llvm::make_error("Could not sync with inferior process", + llvm::inconvertibleErrorCode()); + } + LLDB_LOG(log, "inferior started, now in stopped state"); + + ProcessInstanceInfo Info; + if (!Host::GetProcessInfo(pid, Info)) { + return llvm::make_error("Cannot get process architectrue", + llvm::inconvertibleErrorCode()); + } + /*llvm::Expected arch_or = + NativeRegisterContextAIX::DetermineArchitecture(pid); + if (!arch_or) + return arch_or.takeError();*/ + + // Set the architecture to the exe architecture. + LLDB_LOG(log, "pid = {0}, detected architecture {1}", pid, + Info.GetArchitecture().GetArchitectureName()); + + return std::unique_ptr(new NativeProcessAIX( + pid, launch_info.GetPTY().ReleasePrimaryFileDescriptor(), native_delegate, + Info.GetArchitecture(), *this, {pid})); +} + +llvm::Expected> +NativeProcessAIX::Manager::Attach( + lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate) { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "pid = {0:x}", pid); + + ProcessInstanceInfo Info; + if (!Host::GetProcessInfo(pid, Info)) { + return llvm::make_error("Cannot get process architectrue", + llvm::inconvertibleErrorCode()); + } + auto tids_or = NativeProcessAIX::Attach(pid); + if (!tids_or) + return tids_or.takeError(); +#if 0 + ArrayRef<::pid_t> tids = *tids_or; + llvm::Expected arch_or = + NativeRegisterContextAIX::DetermineArchitecture(tids[0]); + if (!arch_or) + return arch_or.takeError(); +#endif + + return std::unique_ptr( + new NativeProcessAIX(pid, -1, native_delegate, Info.GetArchitecture(), *this, *tids_or)); +} + +lldb::addr_t NativeProcessAIX::GetSharedLibraryInfoAddress() { + // punt on this for now + return LLDB_INVALID_ADDRESS; +} + +NativeProcessAIX::Extension +NativeProcessAIX::Manager::GetSupportedExtensions() const { + NativeProcessAIX::Extension supported = + Extension::multiprocess | Extension::fork | Extension::vfork | + Extension::pass_signals | Extension::auxv | Extension::libraries_svr4 | + Extension::siginfo_read; + +#ifdef __aarch64__ + // At this point we do not have a process so read auxv directly. + if ((getauxval(AT_HWCAP2) & HWCAP2_MTE)) + supported |= Extension::memory_tagging; +#endif + + return supported; +} + +static std::optional> WaitPid() { + Log *log = GetLog(POSIXLog::Process); + + int status; + ::pid_t wait_pid = llvm::sys::RetryAfterSignal( + -1, ::waitpid, -1, &status, /*__WALL | __WNOTHREAD |*/ WNOHANG); + + if (wait_pid == 0) + return std::nullopt; + + if (wait_pid == -1) { + Status error(errno, eErrorTypePOSIX); + LLDB_LOG(log, "waitpid(-1, &status, _) failed: {1}", error); + return std::nullopt; + } + + WaitStatus wait_status = WaitStatus::Decode(status); + + LLDB_LOG(log, "waitpid(-1, &status, _) = {0}, status = {1}", wait_pid, + wait_status); + return std::make_pair(wait_pid, wait_status); +} + +void NativeProcessAIX::Manager::SigchldHandler() { + Log *log = GetLog(POSIXLog::Process); + while (true) { + auto wait_result = WaitPid(); + if (!wait_result) + return; + lldb::pid_t pid = wait_result->first; + WaitStatus status = wait_result->second; + + // Ask each process whether it wants to handle the event. Each event should + // be handled by exactly one process, but thread creation events require + // special handling. + // Thread creation consists of two events (one on the parent and one on the + // child thread) and they can arrive in any order nondeterministically. The + // parent event carries the information about the child thread, but not + // vice-versa. This means that if the child event arrives first, it may not + // be handled by any process (because it doesn't know the thread belongs to + // it). + bool handled = llvm::any_of(m_processes, [&](NativeProcessAIX *process) { + return process->TryHandleWaitStatus(pid, status); + }); + if (!handled) { + if (status.type == WaitStatus::Stop && status.status == SIGSTOP) { + // Store the thread creation event for later collection. + m_unowned_threads.insert(pid); + } else { + LLDB_LOG(log, "Ignoring waitpid event {0} for pid {1}", status, pid); + } + } + } +} + +void NativeProcessAIX::Manager::CollectThread(::pid_t tid) { + Log *log = GetLog(POSIXLog::Process); + + if (m_unowned_threads.erase(tid)) + return; // We've encountered this thread already. + + // The TID is not tracked yet, let's wait for it to appear. + int status = -1; + LLDB_LOG(log, + "received clone event for tid {0}. tid not tracked yet, " + "waiting for it to appear...", + tid); + ::pid_t wait_pid = + llvm::sys::RetryAfterSignal(-1, ::waitpid, tid, &status, P_ALL/*__WALL*/); + + // It's theoretically possible to get other events if the entire process was + // SIGKILLed before we got a chance to check this. In that case, we'll just + // clean everything up when we get the process exit event. + + LLDB_LOG(log, + "waitpid({0}, &status, __WALL) => {1} (errno: {2}, status = {3})", + tid, wait_pid, errno, WaitStatus::Decode(status)); +} + +// Public Instance Methods + +NativeProcessAIX::NativeProcessAIX(::pid_t pid, int terminal_fd, + NativeDelegate &delegate, + const ArchSpec &arch, Manager &manager, + llvm::ArrayRef<::pid_t> tids) + : NativeProcessProtocol(pid, terminal_fd, delegate), m_manager(manager), + m_arch(arch) { + manager.AddProcess(*this); + if (m_terminal_fd != -1) { + Status status = EnsureFDFlags(m_terminal_fd, O_NONBLOCK); + assert(status.Success()); + } + + for (const auto &tid : tids) { + NativeThreadAIX &thread = AddThread(tid, /*resume*/ false); + ThreadWasCreated(thread); + } + + // Let our process instance know the thread has stopped. + SetCurrentThreadID(tids[0]); + SetState(StateType::eStateStopped, false); +} + +llvm::Expected> NativeProcessAIX::Attach(::pid_t pid) { + Log *log = GetLog(POSIXLog::Process); + + Status status; + if ((status = PtraceWrapper(PT_ATTACH, pid)).Fail()) { + return status.ToError(); + } + + int wpid = + llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, nullptr, WNOHANG); + if (wpid <= 0) { + return llvm::errorCodeToError( + std::error_code(errno, std::generic_category())); + } + + LLDB_LOG(log, "adding pid = {0}", pid); + + std::vector<::pid_t> tids; + tids.push_back(pid); + return std::move(tids); +} + +bool NativeProcessAIX::TryHandleWaitStatus(lldb::pid_t pid, + WaitStatus status) { + if (pid == GetID() && + (status.type == WaitStatus::Exit || status.type == WaitStatus::Signal)) { + // The process exited. We're done monitoring. Report to delegate. + SetExitStatus(status, true); + return true; + } + if (NativeThreadAIX *thread = GetThreadByID(pid)) { + MonitorCallback(*thread, status); + return true; + } + return false; +} + +// Handles all waitpid events from the inferior process. +void NativeProcessAIX::MonitorCallback(NativeThreadAIX &thread, + WaitStatus status) { + Log *log = GetLog(LLDBLog::Process); + + // Certain activities differ based on whether the pid is the tid of the main + // thread. + const bool is_main_thread = (thread.GetID() == GetID()); + + // Handle when the thread exits. + if (status.type == WaitStatus::Exit || status.type == WaitStatus::Signal) { + LLDB_LOG(log, + "got exit status({0}) , tid = {1} ({2} main thread), process " + "state = {3}", + status, thread.GetID(), is_main_thread ? "is" : "is not", + GetState()); + + // This is a thread that exited. Ensure we're not tracking it anymore. + StopTrackingThread(thread); + + assert(!is_main_thread && "Main thread exits handled elsewhere"); + return; + } + + int8_t signo = GetSignalInfo(status); + + // Get details on the signal raised. + if (signo) { + // We have retrieved the signal info. Dispatch appropriately. + if (signo == SIGTRAP) + MonitorSIGTRAP(status, thread); + else + MonitorSignal(status, thread); + } else { + assert(0); + } +} + + +void NativeProcessAIX::MonitorSIGTRAP(const WaitStatus status, + NativeThreadAIX &thread) { + Log *log = GetLog(POSIXLog::Process); + const bool is_main_thread = (thread.GetID() == GetID()); + + NativeRegisterContextAIX ®_ctx = thread.GetRegisterContext(); + const RegisterInfo *pc_info = reg_ctx.GetRegisterInfoByName("pc", 0); + RegisterValue pc_value; + + switch (status.status) { + case SIGTRAP: + // Determine the source of SIGTRAP by checking current instruction: + // if that is trap instruction, then this is breakpoint, otherwise + // this is watchpoint. + reg_ctx.ReadRegister(pc_info, pc_value); + + MonitorBreakpoint(thread); + break; + default: + LLDB_LOG(log, "received unknown SIGTRAP stop event ({0}, pid {1} tid {2}", + status.status, GetID(), thread.GetID()); + MonitorSignal(status, thread); + break; + } +} + +void NativeProcessAIX::MonitorTrace(NativeThreadAIX &thread) { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "received trace event, pid = {0}", thread.GetID()); + + // This thread is currently stopped. + thread.SetStoppedByTrace(); + + StopRunningThreads(thread.GetID()); +} + +void NativeProcessAIX::MonitorBreakpoint(NativeThreadAIX &thread) { + Log *log = GetLog(LLDBLog::Process | LLDBLog::Breakpoints); + LLDB_LOG(log, "received breakpoint event, pid = {0}", thread.GetID()); + + // Mark the thread as stopped at breakpoint. + thread.SetStoppedByBreakpoint(); + FixupBreakpointPCAsNeeded(thread); + + if (m_threads_stepping_with_breakpoint.find(thread.GetID()) != + m_threads_stepping_with_breakpoint.end()) + thread.SetStoppedByTrace(); + + StopRunningThreads(thread.GetID()); +} + +void NativeProcessAIX::MonitorWatchpoint(NativeThreadAIX &thread, + uint32_t wp_index) { + Log *log = GetLog(LLDBLog::Process | LLDBLog::Watchpoints); + LLDB_LOG(log, "received watchpoint event, pid = {0}, wp_index = {1}", + thread.GetID(), wp_index); + + // Mark the thread as stopped at watchpoint. The address is at + // (lldb::addr_t)info->si_addr if we need it. + thread.SetStoppedByWatchpoint(wp_index); + + // We need to tell all other running threads before we notify the delegate + // about this stop. + StopRunningThreads(thread.GetID()); +} + +void NativeProcessAIX::MonitorSignal(const WaitStatus status, + NativeThreadAIX &thread) { + int8_t signo = GetSignalInfo(status); +#if 0 + const bool is_from_llgs = info.si_pid == getpid(); +#endif + + Log *log = GetLog(POSIXLog::Process); + + // POSIX says that process behaviour is undefined after it ignores a SIGFPE, + // SIGILL, SIGSEGV, or SIGBUS *unless* that signal was generated by a kill(2) + // or raise(3). Similarly for tgkill(2) on AIX. + // + // IOW, user generated signals never generate what we consider to be a + // "crash". + // + // Similarly, ACK signals generated by this monitor. + + // Handle the signal. + LLDB_LOG(log, + "received signal {0} ({1}) with code NA, (siginfo pid = {2}, " + "waitpid pid = {3})", + Host::GetSignalAsCString(signo), signo, thread.GetID(), GetID()); + +#if 0 + // Check for thread stop notification. + // FIXME + if (is_from_llgs /*&& (info.si_code == SI_TKILL)*/ && (signo == SIGSTOP)) { + // This is a tgkill()-based stop. + LLDB_LOG(log, "pid {0} tid {1}, thread stopped", GetID(), thread.GetID()); + + // Check that we're not already marked with a stop reason. Note this thread + // really shouldn't already be marked as stopped - if we were, that would + // imply that the kernel signaled us with the thread stopping which we + // handled and marked as stopped, and that, without an intervening resume, + // we received another stop. It is more likely that we are missing the + // marking of a run state somewhere if we find that the thread was marked + // as stopped. + const StateType thread_state = thread.GetState(); + if (!StateIsStoppedState(thread_state, false)) { + // An inferior thread has stopped because of a SIGSTOP we have sent it. + // Generally, these are not important stops and we don't want to report + // them as they are just used to stop other threads when one thread (the + // one with the *real* stop reason) hits a breakpoint (watchpoint, + // etc...). However, in the case of an asynchronous Interrupt(), this + // *is* the real stop reason, so we leave the signal intact if this is + // the thread that was chosen as the triggering thread. + if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID) { + if (m_pending_notification_tid == thread.GetID()) + thread.SetStoppedBySignal(SIGSTOP, &info); + else + thread.SetStoppedWithNoReason(); + + SetCurrentThreadID(thread.GetID()); + SignalIfAllThreadsStopped(); + } else { + // We can end up here if stop was initiated by LLGS but by this time a + // thread stop has occurred - maybe initiated by another event. + Status error = ResumeThread(thread, thread.GetState(), 0); + if (error.Fail()) + LLDB_LOG(log, "failed to resume thread {0}: {1}", thread.GetID(), + error); + } + } else { + LLDB_LOG(log, + "pid {0} tid {1}, thread was already marked as a stopped " + "state (state={2}), leaving stop signal as is", + GetID(), thread.GetID(), thread_state); + SignalIfAllThreadsStopped(); + } + + // Done handling. + return; + } +#endif + + // Check if debugger should stop at this signal or just ignore it and resume + // the inferior. + if (m_signals_to_ignore.contains(signo) || signo == SIGCHLD) { + ResumeThread(thread, thread.GetState(), signo); + return; + } + + // This thread is stopped. + LLDB_LOG(log, "received signal {0}", Host::GetSignalAsCString(signo)); + thread.SetStoppedBySignal(signo); + + // Send a stop to the debugger after we get all other threads to stop. + StopRunningThreads(thread.GetID()); +} + +bool NativeProcessAIX::MonitorClone(NativeThreadAIX &parent, + lldb::pid_t child_pid, int event) { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "parent_tid={0}, child_pid={1}, event={2}", parent.GetID(), + child_pid, event); + + // WaitForCloneNotification(child_pid); + + switch (event) { +#if 0 + case PTRACE_EVENT_CLONE: { + // PTRACE_EVENT_CLONE can either mean a new thread or a new process. + // Try to grab the new process' PGID to figure out which one it is. + // If PGID is the same as the PID, then it's a new process. Otherwise, + // it's a thread. + auto tgid_ret = getPIDForTID(child_pid); + if (tgid_ret != child_pid) { + // A new thread should have PGID matching our process' PID. + assert(!tgid_ret || tgid_ret.getValue() == GetID()); + + NativeThreadAIX &child_thread = AddThread(child_pid, /*resume*/ true); + ThreadWasCreated(child_thread); + + // Resume the parent. + ResumeThread(parent, parent.GetState(), LLDB_INVALID_SIGNAL_NUMBER); + break; + } + } + LLVM_FALLTHROUGH; + case PTRACE_EVENT_FORK: + case PTRACE_EVENT_VFORK: { + bool is_vfork = event == PTRACE_EVENT_VFORK; + std::unique_ptr child_process{new NativeProcessAIX( + static_cast<::pid_t>(child_pid), m_terminal_fd, m_delegate, m_arch, + m_main_loop, {static_cast<::pid_t>(child_pid)})}; + if (!is_vfork) + child_process->m_software_breakpoints = m_software_breakpoints; + + Extension expected_ext = is_vfork ? Extension::vfork : Extension::fork; + if (bool(m_enabled_extensions & expected_ext)) { + m_delegate.NewSubprocess(this, std::move(child_process)); + // NB: non-vfork clone() is reported as fork + parent.SetStoppedByFork(is_vfork, child_pid); + StopRunningThreads(parent.GetID()); + } else { + child_process->Detach(); + ResumeThread(parent, parent.GetState(), LLDB_INVALID_SIGNAL_NUMBER); + } + break; + } +#endif + default: + llvm_unreachable("unknown clone_info.event"); + } + + return true; +} + +bool NativeProcessAIX::SupportHardwareSingleStepping() const { + return false; +} + +Status NativeProcessAIX::Resume(const ResumeActionList &resume_actions) { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "pid {0}", GetID()); + + bool software_single_step = !SupportHardwareSingleStepping(); + + if (software_single_step) { + for (const auto &thread : m_threads) { + assert(thread && "thread list should not contain NULL threads"); + + const ResumeAction *const action = + resume_actions.GetActionForThread(thread->GetID(), true); + if (action == nullptr) + continue; + + if (action->state == eStateStepping) { + Status error = SetupSoftwareSingleStepping( + static_cast(*thread)); + if (error.Fail()) + return error; + } + } + } + + for (const auto &thread : m_threads) { + assert(thread && "thread list should not contain NULL threads"); + + const ResumeAction *const action = + resume_actions.GetActionForThread(thread->GetID(), true); + + if (action == nullptr) { + LLDB_LOG(log, "no action specified for pid {0} tid {1}", GetID(), + thread->GetID()); + continue; + } + + LLDB_LOG(log, "processing resume action state {0} for pid {1} tid {2}", + action->state, GetID(), thread->GetID()); + + switch (action->state) { + case eStateRunning: + case eStateStepping: { + // Run the thread, possibly feeding it the signal. + const int signo = action->signal; + Status error = ResumeThread(static_cast(*thread), + action->state, signo); + if (error.Fail()) + return Status("NativeProcessAIX::%s: failed to resume thread " + "for pid %" PRIu64 ", tid %" PRIu64 ", error = %s", + __FUNCTION__, GetID(), thread->GetID(), + error.AsCString()); + + break; + } + + case eStateSuspended: + case eStateStopped: + break; + + default: + return Status("NativeProcessAIX::%s (): unexpected state %s specified " + "for pid %" PRIu64 ", tid %" PRIu64, + __FUNCTION__, StateAsCString(action->state), GetID(), + thread->GetID()); + } + } + + return Status(); +} + +Status NativeProcessAIX::Halt() { + Status error; + + if (kill(GetID(), SIGSTOP) != 0) + error.SetErrorToErrno(); + + return error; +} + +Status NativeProcessAIX::Detach() { + Status error; + + // Tell ptrace to detach from the process. + if (GetID() == LLDB_INVALID_PROCESS_ID) + return error; + + // Cancel out any SIGSTOPs we may have sent while stopping the process. + // Otherwise, the process may stop as soon as we detach from it. + kill(GetID(), SIGCONT); + + for (const auto &thread : m_threads) { + Status e = Detach(thread->GetID()); + if (e.Fail()) + error = + e; // Save the error, but still attempt to detach from other threads. + } + + return error; +} + +Status NativeProcessAIX::Signal(int signo) { + Status error; + + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "sending signal {0} ({1}) to pid {1}", signo, + Host::GetSignalAsCString(signo), GetID()); + + if (kill(GetID(), signo)) + error.SetErrorToErrno(); + + return error; +} + +Status NativeProcessAIX::Interrupt() { + // Pick a running thread (or if none, a not-dead stopped thread) as the + // chosen thread that will be the stop-reason thread. + Log *log = GetLog(POSIXLog::Process); + + NativeThreadProtocol *running_thread = nullptr; + NativeThreadProtocol *stopped_thread = nullptr; + + LLDB_LOG(log, "selecting running thread for interrupt target"); + for (const auto &thread : m_threads) { + // If we have a running or stepping thread, we'll call that the target of + // the interrupt. + const auto thread_state = thread->GetState(); + if (thread_state == eStateRunning || thread_state == eStateStepping) { + running_thread = thread.get(); + break; + } else if (!stopped_thread && StateIsStoppedState(thread_state, true)) { + // Remember the first non-dead stopped thread. We'll use that as a + // backup if there are no running threads. + stopped_thread = thread.get(); + } + } + + if (!running_thread && !stopped_thread) { + Status error("found no running/stepping or live stopped threads as target " + "for interrupt"); + LLDB_LOG(log, "skipping due to error: {0}", error); + + return error; + } + + NativeThreadProtocol *deferred_signal_thread = + running_thread ? running_thread : stopped_thread; + + LLDB_LOG(log, "pid {0} {1} tid {2} chosen for interrupt target", GetID(), + running_thread ? "running" : "stopped", + deferred_signal_thread->GetID()); + + StopRunningThreads(deferred_signal_thread->GetID()); + + return Status(); +} + +Status NativeProcessAIX::Kill() { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "pid {0}", GetID()); + + Status error; + + switch (m_state) { + case StateType::eStateInvalid: + case StateType::eStateExited: + case StateType::eStateCrashed: + case StateType::eStateDetached: + case StateType::eStateUnloaded: + // Nothing to do - the process is already dead. + LLDB_LOG(log, "ignored for PID {0} due to current state: {1}", GetID(), + m_state); + return error; + + case StateType::eStateConnected: + case StateType::eStateAttaching: + case StateType::eStateLaunching: + case StateType::eStateStopped: + case StateType::eStateRunning: + case StateType::eStateStepping: + case StateType::eStateSuspended: + // We can try to kill a process in these states. + break; + } + + if (kill(GetID(), SIGKILL) != 0) { + error.SetErrorToErrno(); + return error; + } + + return error; +} + +Status NativeProcessAIX::GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) { + // FIXME review that the final memory region returned extends to the end of + // the virtual address space, + // with no perms if it is not mapped. + + // Use an approach that reads memory regions from /proc/{pid}/maps. Assume + // proc maps entries are in ascending order. + // FIXME assert if we find differently. + + if (m_supports_mem_region == LazyBool::eLazyBoolNo) { + // We're done. + return Status("unsupported"); + } + + Status error = PopulateMemoryRegionCache(); + if (error.Fail()) { + return error; + } + + lldb::addr_t prev_base_address = 0; + + // FIXME start by finding the last region that is <= target address using + // binary search. Data is sorted. + // There can be a ton of regions on pthreads apps with lots of threads. + for (auto it = m_mem_region_cache.begin(); it != m_mem_region_cache.end(); + ++it) { + MemoryRegionInfo &proc_entry_info = it->first; + + // Sanity check assumption that /proc/{pid}/maps entries are ascending. + assert((proc_entry_info.GetRange().GetRangeBase() >= prev_base_address) && + "descending /proc/pid/maps entries detected, unexpected"); + prev_base_address = proc_entry_info.GetRange().GetRangeBase(); + UNUSED_IF_ASSERT_DISABLED(prev_base_address); + + // If the target address comes before this entry, indicate distance to next + // region. + if (load_addr < proc_entry_info.GetRange().GetRangeBase()) { + range_info.GetRange().SetRangeBase(load_addr); + range_info.GetRange().SetByteSize( + proc_entry_info.GetRange().GetRangeBase() - load_addr); + range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); + + return error; + } else if (proc_entry_info.GetRange().Contains(load_addr)) { + // The target address is within the memory region we're processing here. + range_info = proc_entry_info; + return error; + } + + // The target memory address comes somewhere after the region we just + // parsed. + } + + // If we made it here, we didn't find an entry that contained the given + // address. Return the load_addr as start and the amount of bytes betwwen + // load address and the end of the memory as size. + range_info.GetRange().SetRangeBase(load_addr); + range_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); + range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); + return error; +} + +Status NativeProcessAIX::PopulateMemoryRegionCache() { + Log *log = GetLog(POSIXLog::Process); + + // If our cache is empty, pull the latest. There should always be at least + // one memory region if memory region handling is supported. + if (!m_mem_region_cache.empty()) { + LLDB_LOG(log, "reusing {0} cached memory region entries", + m_mem_region_cache.size()); + return Status(); + } + + Status Result; +#if 0 + AIXMapCallback callback = [&](llvm::Expected Info) { + if (Info) { + FileSpec file_spec(Info->GetName().GetCString()); + FileSystem::Instance().Resolve(file_spec); + m_mem_region_cache.emplace_back(*Info, file_spec); + return true; + } + + Result = Info.takeError(); + m_supports_mem_region = LazyBool::eLazyBoolNo; + LLDB_LOG(log, "failed to parse proc maps: {0}", Result); + return false; + }; + + // AIX kernel since 2.6.14 has /proc/{pid}/smaps + // if CONFIG_PROC_PAGE_MONITOR is enabled + auto BufferOrError = getProcFile(GetID(), GetCurrentThreadID(), "smaps"); + if (BufferOrError) + ParseAIXSMapRegions(BufferOrError.get()->getBuffer(), callback); + else { + BufferOrError = getProcFile(GetID(), GetCurrentThreadID(), "maps"); + if (!BufferOrError) { + m_supports_mem_region = LazyBool::eLazyBoolNo; + return BufferOrError.getError(); + } + + ParseAIXMapRegions(BufferOrError.get()->getBuffer(), callback); + } + + if (Result.Fail()) + return Result; + + if (m_mem_region_cache.empty()) { + // No entries after attempting to read them. This shouldn't happen if + // /proc/{pid}/maps is supported. Assume we don't support map entries via + // procfs. + m_supports_mem_region = LazyBool::eLazyBoolNo; + LLDB_LOG(log, + "failed to find any procfs maps entries, assuming no support " + "for memory region metadata retrieval"); + return Status("not supported"); + } + + LLDB_LOG(log, "read {0} memory region entries from /proc/{1}/maps", + m_mem_region_cache.size(), GetID()); + + // We support memory retrieval, remember that. + m_supports_mem_region = LazyBool::eLazyBoolYes; +#endif + return Status(); +} + +void NativeProcessAIX::DoStopIDBumped(uint32_t newBumpId) { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "newBumpId={0}", newBumpId); + LLDB_LOG(log, "clearing {0} entries from memory region cache", + m_mem_region_cache.size()); + m_mem_region_cache.clear(); +} + +llvm::Expected +NativeProcessAIX::Syscall(llvm::ArrayRef args) { + PopulateMemoryRegionCache(); + auto region_it = llvm::find_if(m_mem_region_cache, [](const auto &pair) { + return pair.first.GetExecutable() == MemoryRegionInfo::eYes && + pair.first.GetShared() != MemoryRegionInfo::eYes; + }); + if (region_it == m_mem_region_cache.end()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "No executable memory region found!"); + + addr_t exe_addr = region_it->first.GetRange().GetRangeBase(); + + NativeThreadAIX &thread = *GetCurrentThread(); + assert(thread.GetState() == eStateStopped); + NativeRegisterContextAIX ®_ctx = thread.GetRegisterContext(); + + NativeRegisterContextAIX::SyscallData syscall_data = + *reg_ctx.GetSyscallData(); + + WritableDataBufferSP registers_sp; + if (llvm::Error Err = reg_ctx.ReadAllRegisterValues(registers_sp).ToError()) + return std::move(Err); + auto restore_regs = llvm::make_scope_exit( + [&] { reg_ctx.WriteAllRegisterValues(registers_sp); }); + + llvm::SmallVector memory(syscall_data.Insn.size()); + size_t bytes_read; + if (llvm::Error Err = + ReadMemory(exe_addr, memory.data(), memory.size(), bytes_read) + .ToError()) { + return std::move(Err); + } + + auto restore_mem = llvm::make_scope_exit( + [&] { WriteMemory(exe_addr, memory.data(), memory.size(), bytes_read); }); + + if (llvm::Error Err = reg_ctx.SetPC(exe_addr).ToError()) + return std::move(Err); + + for (const auto &zip : llvm::zip_first(args, syscall_data.Args)) { + if (llvm::Error Err = + reg_ctx + .WriteRegisterFromUnsigned(std::get<1>(zip), std::get<0>(zip)) + .ToError()) { + return std::move(Err); + } + } + if (llvm::Error Err = WriteMemory(exe_addr, syscall_data.Insn.data(), + syscall_data.Insn.size(), bytes_read) + .ToError()) + return std::move(Err); + + m_mem_region_cache.clear(); + + // With software single stepping the syscall insn buffer must also include a + // trap instruction to stop the process. + int req = SupportHardwareSingleStepping() ? PTRACE_SINGLESTEP : PTRACE_CONT; + if (llvm::Error Err = + PtraceWrapper(req, thread.GetID(), nullptr, nullptr).ToError()) + return std::move(Err); + + //FIXME + int status; + ::pid_t wait_pid = llvm::sys::RetryAfterSignal(-1, ::waitpid, thread.GetID(), + &status, P_ALL/*__WALL*/); + if (wait_pid == -1) { + return llvm::errorCodeToError( + std::error_code(errno, std::generic_category())); + } + assert((unsigned)wait_pid == thread.GetID()); + + uint64_t result = reg_ctx.ReadRegisterAsUnsigned(syscall_data.Result, -ESRCH); + + // Values larger than this are actually negative errno numbers. + uint64_t errno_threshold = + (uint64_t(-1) >> (64 - 8 * m_arch.GetAddressByteSize())) - 0x1000; + if (result > errno_threshold) { + return llvm::errorCodeToError( + std::error_code(-result & 0xfff, std::generic_category())); + } + + return result; +} + +llvm::Expected +NativeProcessAIX::AllocateMemory(size_t size, uint32_t permissions) { + + std::optional mmap_data = + GetCurrentThread()->GetRegisterContext().GetMmapData(); + if (!mmap_data) + return llvm::make_error(); + + unsigned prot = PROT_NONE; + assert((permissions & (ePermissionsReadable | ePermissionsWritable | + ePermissionsExecutable)) == permissions && + "Unknown permission!"); + if (permissions & ePermissionsReadable) + prot |= PROT_READ; + if (permissions & ePermissionsWritable) + prot |= PROT_WRITE; + if (permissions & ePermissionsExecutable) + prot |= PROT_EXEC; + + llvm::Expected Result = + Syscall({mmap_data->SysMmap, 0, size, prot, MAP_ANONYMOUS | MAP_PRIVATE, + uint64_t(-1), 0}); + if (Result) + m_allocated_memory.try_emplace(*Result, size); + return Result; +} + +llvm::Error NativeProcessAIX::DeallocateMemory(lldb::addr_t addr) { + std::optional mmap_data = + GetCurrentThread()->GetRegisterContext().GetMmapData(); + if (!mmap_data) + return llvm::make_error(); + + auto it = m_allocated_memory.find(addr); + if (it == m_allocated_memory.end()) + return llvm::createStringError(llvm::errc::invalid_argument, + "Memory not allocated by the debugger."); + + llvm::Expected Result = + Syscall({mmap_data->SysMunmap, addr, it->second}); + if (!Result) + return Result.takeError(); + + m_allocated_memory.erase(it); + return llvm::Error::success(); +} + +Status NativeProcessAIX::ReadMemoryTags(int32_t type, lldb::addr_t addr, + size_t len, + std::vector &tags) { + llvm::Expected details = + GetCurrentThread()->GetRegisterContext().GetMemoryTaggingDetails(type); + if (!details) + return Status(details.takeError()); + + // Ignore 0 length read + if (!len) + return Status(); + + // lldb will align the range it requests but it is not required to by + // the protocol so we'll do it again just in case. + // Remove tag bits too. Ptrace calls may work regardless but that + // is not a guarantee. + MemoryTagManager::TagRange range(details->manager->RemoveTagBits(addr), len); + range = details->manager->ExpandToGranule(range); + + // Allocate enough space for all tags to be read + size_t num_tags = range.GetByteSize() / details->manager->GetGranuleSize(); + tags.resize(num_tags * details->manager->GetTagSizeInBytes()); + + struct iovec tags_iovec; + uint8_t *dest = tags.data(); + lldb::addr_t read_addr = range.GetRangeBase(); + + // This call can return partial data so loop until we error or + // get all tags back. + while (num_tags) { + tags_iovec.iov_base = dest; + tags_iovec.iov_len = num_tags; + + Status error = NativeProcessAIX::PtraceWrapper( + details->ptrace_read_req, GetCurrentThreadID(), + reinterpret_cast(read_addr), static_cast(&tags_iovec), + 0, nullptr); + + if (error.Fail()) { + // Discard partial reads + tags.resize(0); + return error; + } + + size_t tags_read = tags_iovec.iov_len; + assert(tags_read && (tags_read <= num_tags)); + + dest += tags_read * details->manager->GetTagSizeInBytes(); + read_addr += details->manager->GetGranuleSize() * tags_read; + num_tags -= tags_read; + } + + return Status(); +} + +Status NativeProcessAIX::WriteMemoryTags(int32_t type, lldb::addr_t addr, + size_t len, + const std::vector &tags) { + llvm::Expected details = + GetCurrentThread()->GetRegisterContext().GetMemoryTaggingDetails(type); + if (!details) + return Status(details.takeError()); + + // Ignore 0 length write + if (!len) + return Status(); + + // lldb will align the range it requests but it is not required to by + // the protocol so we'll do it again just in case. + // Remove tag bits too. Ptrace calls may work regardless but that + // is not a guarantee. + MemoryTagManager::TagRange range(details->manager->RemoveTagBits(addr), len); + range = details->manager->ExpandToGranule(range); + + // Not checking number of tags here, we may repeat them below + llvm::Expected> unpacked_tags_or_err = + details->manager->UnpackTagsData(tags); + if (!unpacked_tags_or_err) + return Status(unpacked_tags_or_err.takeError()); + + llvm::Expected> repeated_tags_or_err = + details->manager->RepeatTagsForRange(*unpacked_tags_or_err, range); + if (!repeated_tags_or_err) + return Status(repeated_tags_or_err.takeError()); + + // Repack them for ptrace to use + llvm::Expected> final_tag_data = + details->manager->PackTags(*repeated_tags_or_err); + if (!final_tag_data) + return Status(final_tag_data.takeError()); + + struct iovec tags_vec; + uint8_t *src = final_tag_data->data(); + lldb::addr_t write_addr = range.GetRangeBase(); + // unpacked tags size because the number of bytes per tag might not be 1 + size_t num_tags = repeated_tags_or_err->size(); + + // This call can partially write tags, so we loop until we + // error or all tags have been written. + while (num_tags > 0) { + tags_vec.iov_base = src; + tags_vec.iov_len = num_tags; + + Status error = NativeProcessAIX::PtraceWrapper( + details->ptrace_write_req, GetCurrentThreadID(), + reinterpret_cast(write_addr), static_cast(&tags_vec), 0, + nullptr); + + if (error.Fail()) { + // Don't attempt to restore the original values in the case of a partial + // write + return error; + } + + size_t tags_written = tags_vec.iov_len; + assert(tags_written && (tags_written <= num_tags)); + + src += tags_written * details->manager->GetTagSizeInBytes(); + write_addr += details->manager->GetGranuleSize() * tags_written; + num_tags -= tags_written; + } + + return Status(); +} + +size_t NativeProcessAIX::UpdateThreads() { + // The NativeProcessAIX monitoring threads are always up to date with + // respect to thread state and they keep the thread list populated properly. + // All this method needs to do is return the thread count. + return m_threads.size(); +} + +Status NativeProcessAIX::SetBreakpoint(lldb::addr_t addr, uint32_t size, + bool hardware) { + if (hardware) + return SetHardwareBreakpoint(addr, size); + else + return SetSoftwareBreakpoint(addr, size); +} + +Status NativeProcessAIX::RemoveBreakpoint(lldb::addr_t addr, bool hardware) { + if (hardware) + return RemoveHardwareBreakpoint(addr); + else + return NativeProcessProtocol::RemoveBreakpoint(addr); +} + +llvm::Expected> +NativeProcessAIX::GetSoftwareBreakpointTrapOpcode(size_t size_hint) { + // The ARM reference recommends the use of 0xe7fddefe and 0xdefe but the + // linux kernel does otherwise. + static const uint8_t g_arm_opcode[] = {0xf0, 0x01, 0xf0, 0xe7}; + static const uint8_t g_thumb_opcode[] = {0x01, 0xde}; + + switch (GetArchitecture().GetMachine()) { + case llvm::Triple::arm: + switch (size_hint) { + case 2: + return llvm::ArrayRef(g_thumb_opcode); + case 4: + return llvm::ArrayRef(g_arm_opcode); + default: + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Unrecognised trap opcode size hint!"); + } + default: + return NativeProcessProtocol::GetSoftwareBreakpointTrapOpcode(size_hint); + } +} + +Status NativeProcessAIX::ReadMemory(lldb::addr_t addr, void *buf, size_t size, + size_t &bytes_read) { + unsigned char *dst = static_cast(buf); + size_t remainder; + long data; + + Log *log = GetLog(POSIXLog::Memory); + LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size); + + for (bytes_read = 0; bytes_read < size; bytes_read += remainder) { + Status error = NativeProcessAIX::PtraceWrapper( + PT_READ_BLOCK, GetCurrentThreadID(), (void *)addr, nullptr, sizeof(data), &data); + if (error.Fail()) + return error; + + remainder = size - bytes_read; + remainder = remainder > k_ptrace_word_size ? k_ptrace_word_size : remainder; + + // Copy the data into our buffer + memcpy(dst, &data, remainder); + + LLDB_LOG(log, "[{0:x}]:{1:x}", addr, data); + addr += k_ptrace_word_size; + dst += k_ptrace_word_size; + } + return Status(); +} + +Status NativeProcessAIX::WriteMemory(lldb::addr_t addr, const void *buf, + size_t size, size_t &bytes_written) { + const unsigned char *src = static_cast(buf); + size_t remainder; + Status error; + + Log *log = GetLog(POSIXLog::Memory); + LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size); + + error = NativeProcessAIX::PtraceWrapper( + PT_WRITE_BLOCK, GetCurrentThreadID(), (void *)addr, nullptr, (int)size, (long *)buf); + if (error.Fail()) + return error; + + bytes_written = size; + return error; +} + +int8_t NativeProcessAIX::GetSignalInfo(WaitStatus wstatus) const { + return wstatus.status; +} + +Status NativeProcessAIX::GetEventMessage(lldb::tid_t tid, + unsigned long *message) { + //FIXME + return PtraceWrapper(PT_CLEAR/*PTRACE_GETEVENTMSG*/, tid, nullptr, message); +} + +Status NativeProcessAIX::Detach(lldb::tid_t tid) { + if (tid == LLDB_INVALID_THREAD_ID) + return Status(); + + return PtraceWrapper(PT_DETACH, tid); +} + +bool NativeProcessAIX::HasThreadNoLock(lldb::tid_t thread_id) { + for (const auto &thread : m_threads) { + assert(thread && "thread list should not contain NULL threads"); + if (thread->GetID() == thread_id) { + // We have this thread. + return true; + } + } + + // We don't have this thread. + return false; +} + +void NativeProcessAIX::StopTrackingThread(NativeThreadAIX &thread) { + Log *const log = GetLog(POSIXLog::Thread); + lldb::tid_t thread_id = thread.GetID(); + LLDB_LOG(log, "tid: {0}", thread_id); + + auto it = llvm::find_if(m_threads, [&](const auto &thread_up) { + return thread_up.get() == &thread; + }); + assert(it != m_threads.end()); + m_threads.erase(it); + + NotifyTracersOfThreadDestroyed(thread_id); + SignalIfAllThreadsStopped(); +} + +void NativeProcessAIX::NotifyTracersProcessDidStop() { +} + +void NativeProcessAIX::NotifyTracersProcessWillResume() { +} + +Status NativeProcessAIX::NotifyTracersOfNewThread(lldb::tid_t tid) { + Log *log = GetLog(POSIXLog::Thread); + Status error; + return error; +} + +Status NativeProcessAIX::NotifyTracersOfThreadDestroyed(lldb::tid_t tid) { + Log *log = GetLog(POSIXLog::Thread); + Status error; + return error; +} + +NativeThreadAIX &NativeProcessAIX::AddThread(lldb::tid_t thread_id, + bool resume) { + Log *log = GetLog(POSIXLog::Thread); + LLDB_LOG(log, "pid {0} adding thread with tid {1}", GetID(), thread_id); + + assert(!HasThreadNoLock(thread_id) && + "attempted to add a thread by id that already exists"); + + // If this is the first thread, save it as the current thread + if (m_threads.empty()) + SetCurrentThreadID(thread_id); + + m_threads.push_back(std::make_unique(*this, thread_id)); + NativeThreadAIX &thread = + static_cast(*m_threads.back()); + + Status tracing_error = NotifyTracersOfNewThread(thread.GetID()); + if (tracing_error.Fail()) { + thread.SetStoppedByProcessorTrace(tracing_error.AsCString()); + StopRunningThreads(thread.GetID()); + } else if (resume) + ResumeThread(thread, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER); + else + thread.SetStoppedBySignal(SIGSTOP); + + return thread; +} + +Status NativeProcessAIX::GetLoadedModuleFileSpec(const char *module_path, + FileSpec &file_spec) { + Status error = PopulateMemoryRegionCache(); + if (error.Fail()) + return error; + + FileSpec module_file_spec(module_path); + FileSystem::Instance().Resolve(module_file_spec); + + file_spec.Clear(); + for (const auto &it : m_mem_region_cache) { + if (it.second.GetFilename() == module_file_spec.GetFilename()) { + file_spec = it.second; + return Status(); + } + } + return Status("Module file (%s) not found in /proc/%" PRIu64 "/maps file!", + module_file_spec.GetFilename().AsCString(), GetID()); +} + +Status NativeProcessAIX::GetFileLoadAddress(const llvm::StringRef &file_name, + lldb::addr_t &load_addr) { + load_addr = LLDB_INVALID_ADDRESS; + + NativeThreadAIX &thread = *GetCurrentThread(); + NativeRegisterContextAIX ®_ctx = thread.GetRegisterContext(); + + // FIXME: buffer size + struct ld_xinfo info[64]; + if (ptrace64(PT_LDXINFO, reg_ctx.GetThread().GetID(), (long long)&(info[0]), sizeof(info), nullptr) == 0) { + load_addr = (unsigned long)info[0].ldinfo_textorg; + return Status(); + } + return Status("No load address found for specified file."); +} + +NativeThreadAIX *NativeProcessAIX::GetThreadByID(lldb::tid_t tid) { + return static_cast( + NativeProcessProtocol::GetThreadByID(tid)); +} + +NativeThreadAIX *NativeProcessAIX::GetCurrentThread() { + return static_cast( + NativeProcessProtocol::GetCurrentThread()); +} + +Status NativeProcessAIX::ResumeThread(NativeThreadAIX &thread, + lldb::StateType state, int signo) { + Log *const log = GetLog(POSIXLog::Thread); + LLDB_LOG(log, "tid: {0}", thread.GetID()); + + // Before we do the resume below, first check if we have a pending stop + // notification that is currently waiting for all threads to stop. This is + // potentially a buggy situation since we're ostensibly waiting for threads + // to stop before we send out the pending notification, and here we are + // resuming one before we send out the pending stop notification. + if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID) { + LLDB_LOG(log, + "about to resume tid {0} per explicit request but we have a " + "pending stop notification (tid {1}) that is actively " + "waiting for this thread to stop. Valid sequence of events?", + thread.GetID(), m_pending_notification_tid); + } + + // Request a resume. We expect this to be synchronous and the system to + // reflect it is running after this completes. + switch (state) { + case eStateRunning: { + const auto resume_result = thread.Resume(signo); + if (resume_result.Success()) + SetState(eStateRunning, true); + return resume_result; + } + case eStateStepping: { + const auto step_result = thread.SingleStep(signo); + if (step_result.Success()) + SetState(eStateRunning, true); + return step_result; + } + default: + LLDB_LOG(log, "Unhandled state {0}.", state); + llvm_unreachable("Unhandled state for resume"); + } +} + +//===----------------------------------------------------------------------===// + +void NativeProcessAIX::StopRunningThreads(const lldb::tid_t triggering_tid) { + Log *const log = GetLog(POSIXLog::Thread); + LLDB_LOG(log, "about to process event: (triggering_tid: {0})", + triggering_tid); + + m_pending_notification_tid = triggering_tid; + + // Request a stop for all the thread stops that need to be stopped and are + // not already known to be stopped. + for (const auto &thread : m_threads) { + if (StateIsRunningState(thread->GetState())) + static_cast(thread.get())->RequestStop(); + } + + SignalIfAllThreadsStopped(); + LLDB_LOG(log, "event processing done"); +} + +void NativeProcessAIX::SignalIfAllThreadsStopped() { + if (m_pending_notification_tid == LLDB_INVALID_THREAD_ID) + return; // No pending notification. Nothing to do. + + for (const auto &thread_sp : m_threads) { + if (StateIsRunningState(thread_sp->GetState())) + return; // Some threads are still running. Don't signal yet. + } + + // We have a pending notification and all threads have stopped. + Log *log = GetLog(LLDBLog::Process | LLDBLog::Breakpoints); + + // Clear any temporary breakpoints we used to implement software single + // stepping. + for (const auto &thread_info : m_threads_stepping_with_breakpoint) { + Status error = RemoveBreakpoint(thread_info.second); + if (error.Fail()) + LLDB_LOG(log, "pid = {0} remove stepping breakpoint: {1}", + thread_info.first, error); + } + m_threads_stepping_with_breakpoint.clear(); + + // Notify the delegate about the stop + SetCurrentThreadID(m_pending_notification_tid); + SetState(StateType::eStateStopped, true); + m_pending_notification_tid = LLDB_INVALID_THREAD_ID; +} + +void NativeProcessAIX::ThreadWasCreated(NativeThreadAIX &thread) { + Log *const log = GetLog(POSIXLog::Thread); + LLDB_LOG(log, "tid: {0}", thread.GetID()); + + if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID && + StateIsRunningState(thread.GetState())) { + // We will need to wait for this new thread to stop as well before firing + // the notification. + thread.RequestStop(); + } +} + +#define DECLARE_REGISTER_INFOS_PPC64LE_STRUCT +#include "Plugins/Process/Utility/RegisterInfos_ppc64le.h" +#undef DECLARE_REGISTER_INFOS_PPC64LE_STRUCT + +static void GetRegister(lldb::pid_t pid, long long addr, void *buf) { + uint64_t val = 0; + ptrace64(PT_READ_GPR, pid, addr, 0, (int *)&val); + *(uint64_t *)buf = llvm::byteswap(val); +} + +static void SetRegister(lldb::pid_t pid, long long addr, void *buf) { + uint64_t val = llvm::byteswap(*(uint64_t *)buf); + ptrace64(PT_WRITE_GPR, pid, addr, 0, (int *)&val); +} + +static void GetFPRegister(lldb::pid_t pid, long long addr, void *buf) { + uint64_t val = 0; + ptrace64(PT_READ_FPR, pid, addr, 0, (int *)&val); + *(uint64_t *)buf = llvm::byteswap(val); +} + +static void GetVMRegister(lldb::tid_t tid, long long addr, void *buf) { + uint64_t val = 0; + ptrace64(PTT_READ_VEC, tid, addr, 0, (int *)&val); + //*(uint64_t *)buf = llvm::byteswap(val); +} + +static void GetVSRegister(lldb::tid_t tid, long long addr, void *buf) { + uint64_t val = 0; + ptrace64(PTT_READ_VSX, tid, addr, 0, (int *)&val); + //*(uint64_t *)buf = llvm::byteswap(val); +} + +// Wrapper for ptrace to catch errors and log calls. Note that ptrace sets +// errno on error because -1 can be a valid result (i.e. for PTRACE_PEEK*) +Status NativeProcessAIX::PtraceWrapper(int req, lldb::pid_t pid, void *addr, + void *data, size_t data_size, + long *result) { + Status error; + long int ret; + + Log *log = GetLog(POSIXLog::Ptrace); + + PtraceDisplayBytes(req, data, data_size); + + errno = 0; + + // for PTT_* + const char procdir[] = "/proc/"; + const char lwpdir[] = "/lwp/"; + std::string process_task_dir = procdir + std::to_string(pid) + lwpdir; + DIR *dirproc = opendir(process_task_dir.c_str()); + + lldb::tid_t tid = 0; + if (dirproc) { + struct dirent *direntry = nullptr; + while ((direntry = readdir(dirproc)) != nullptr) { + if (strcmp(direntry->d_name, ".") == 0 || strcmp(direntry->d_name, "..") == 0) { + continue; + } + tid = atoi(direntry->d_name); + break; + } + closedir(dirproc); + } + + if (req == PTRACE_GETREGS) { + GetRegister(pid, GPR0, &(((GPR *)data)->r0)); + GetRegister(pid, GPR1, &(((GPR *)data)->r1)); + GetRegister(pid, GPR2, &(((GPR *)data)->r2)); + GetRegister(pid, GPR3, &(((GPR *)data)->r3)); + GetRegister(pid, GPR4, &(((GPR *)data)->r4)); + GetRegister(pid, GPR5, &(((GPR *)data)->r5)); + GetRegister(pid, GPR6, &(((GPR *)data)->r6)); + GetRegister(pid, GPR7, &(((GPR *)data)->r7)); + GetRegister(pid, GPR8, &(((GPR *)data)->r8)); + GetRegister(pid, GPR9, &(((GPR *)data)->r9)); + GetRegister(pid, GPR10, &(((GPR *)data)->r10)); + GetRegister(pid, GPR11, &(((GPR *)data)->r11)); + GetRegister(pid, GPR12, &(((GPR *)data)->r12)); + GetRegister(pid, GPR13, &(((GPR *)data)->r13)); + GetRegister(pid, GPR14, &(((GPR *)data)->r14)); + GetRegister(pid, GPR15, &(((GPR *)data)->r15)); + GetRegister(pid, GPR16, &(((GPR *)data)->r16)); + GetRegister(pid, GPR17, &(((GPR *)data)->r17)); + GetRegister(pid, GPR18, &(((GPR *)data)->r18)); + GetRegister(pid, GPR19, &(((GPR *)data)->r19)); + GetRegister(pid, GPR20, &(((GPR *)data)->r20)); + GetRegister(pid, GPR21, &(((GPR *)data)->r21)); + GetRegister(pid, GPR22, &(((GPR *)data)->r22)); + GetRegister(pid, GPR23, &(((GPR *)data)->r23)); + GetRegister(pid, GPR24, &(((GPR *)data)->r24)); + GetRegister(pid, GPR25, &(((GPR *)data)->r25)); + GetRegister(pid, GPR26, &(((GPR *)data)->r26)); + GetRegister(pid, GPR27, &(((GPR *)data)->r27)); + GetRegister(pid, GPR28, &(((GPR *)data)->r28)); + GetRegister(pid, GPR29, &(((GPR *)data)->r29)); + GetRegister(pid, GPR30, &(((GPR *)data)->r30)); + GetRegister(pid, GPR31, &(((GPR *)data)->r31)); + GetRegister(pid, IAR, &(((GPR *)data)->pc)); + GetRegister(pid, MSR, &(((GPR *)data)->msr)); + //FIXME: origr3/softe/trap on AIX? + GetRegister(pid, CTR, &(((GPR *)data)->ctr)); + GetRegister(pid, LR, &(((GPR *)data)->lr)); + GetRegister(pid, XER, &(((GPR *)data)->xer)); + GetRegister(pid, CR, &(((GPR *)data)->cr)); + } else if (req == PTRACE_SETREGS) { + SetRegister(pid, GPR0, &(((GPR *)data)->r0)); + SetRegister(pid, GPR1, &(((GPR *)data)->r1)); + SetRegister(pid, GPR2, &(((GPR *)data)->r2)); + SetRegister(pid, GPR3, &(((GPR *)data)->r3)); + SetRegister(pid, GPR4, &(((GPR *)data)->r4)); + SetRegister(pid, GPR5, &(((GPR *)data)->r5)); + SetRegister(pid, GPR6, &(((GPR *)data)->r6)); + SetRegister(pid, GPR7, &(((GPR *)data)->r7)); + SetRegister(pid, GPR8, &(((GPR *)data)->r8)); + SetRegister(pid, GPR9, &(((GPR *)data)->r9)); + SetRegister(pid, GPR10, &(((GPR *)data)->r10)); + SetRegister(pid, GPR11, &(((GPR *)data)->r11)); + SetRegister(pid, GPR12, &(((GPR *)data)->r12)); + SetRegister(pid, GPR13, &(((GPR *)data)->r13)); + SetRegister(pid, GPR14, &(((GPR *)data)->r14)); + SetRegister(pid, GPR15, &(((GPR *)data)->r15)); + SetRegister(pid, GPR16, &(((GPR *)data)->r16)); + SetRegister(pid, GPR17, &(((GPR *)data)->r17)); + SetRegister(pid, GPR18, &(((GPR *)data)->r18)); + SetRegister(pid, GPR19, &(((GPR *)data)->r19)); + SetRegister(pid, GPR20, &(((GPR *)data)->r20)); + SetRegister(pid, GPR21, &(((GPR *)data)->r21)); + SetRegister(pid, GPR22, &(((GPR *)data)->r22)); + SetRegister(pid, GPR23, &(((GPR *)data)->r23)); + SetRegister(pid, GPR24, &(((GPR *)data)->r24)); + SetRegister(pid, GPR25, &(((GPR *)data)->r25)); + SetRegister(pid, GPR26, &(((GPR *)data)->r26)); + SetRegister(pid, GPR27, &(((GPR *)data)->r27)); + SetRegister(pid, GPR28, &(((GPR *)data)->r28)); + SetRegister(pid, GPR29, &(((GPR *)data)->r29)); + SetRegister(pid, GPR30, &(((GPR *)data)->r30)); + SetRegister(pid, GPR31, &(((GPR *)data)->r31)); + SetRegister(pid, IAR, &(((GPR *)data)->pc)); + SetRegister(pid, MSR, &(((GPR *)data)->msr)); + //FIXME: origr3/softe/trap on AIX? + SetRegister(pid, CTR, &(((GPR *)data)->ctr)); + SetRegister(pid, LR, &(((GPR *)data)->lr)); + SetRegister(pid, XER, &(((GPR *)data)->xer)); + SetRegister(pid, CR, &(((GPR *)data)->cr)); + } else if (req == PTRACE_GETFPREGS) { + GetFPRegister(pid, FPR0, &(((FPR *)data)->f0)); + GetFPRegister(pid, FPR1, &(((FPR *)data)->f1)); + GetFPRegister(pid, FPR2, &(((FPR *)data)->f2)); + GetFPRegister(pid, FPR3, &(((FPR *)data)->f3)); + GetFPRegister(pid, FPR4, &(((FPR *)data)->f4)); + GetFPRegister(pid, FPR5, &(((FPR *)data)->f5)); + GetFPRegister(pid, FPR6, &(((FPR *)data)->f6)); + GetFPRegister(pid, FPR7, &(((FPR *)data)->f7)); + GetFPRegister(pid, FPR8, &(((FPR *)data)->f8)); + GetFPRegister(pid, FPR9, &(((FPR *)data)->f9)); + GetFPRegister(pid, FPR10, &(((FPR *)data)->f10)); + GetFPRegister(pid, FPR11, &(((FPR *)data)->f11)); + GetFPRegister(pid, FPR12, &(((FPR *)data)->f12)); + GetFPRegister(pid, FPR13, &(((FPR *)data)->f13)); + GetFPRegister(pid, FPR14, &(((FPR *)data)->f14)); + GetFPRegister(pid, FPR15, &(((FPR *)data)->f15)); + GetFPRegister(pid, FPR16, &(((FPR *)data)->f16)); + GetFPRegister(pid, FPR17, &(((FPR *)data)->f17)); + GetFPRegister(pid, FPR18, &(((FPR *)data)->f18)); + GetFPRegister(pid, FPR19, &(((FPR *)data)->f19)); + GetFPRegister(pid, FPR20, &(((FPR *)data)->f20)); + GetFPRegister(pid, FPR21, &(((FPR *)data)->f21)); + GetFPRegister(pid, FPR22, &(((FPR *)data)->f22)); + GetFPRegister(pid, FPR23, &(((FPR *)data)->f23)); + GetFPRegister(pid, FPR24, &(((FPR *)data)->f24)); + GetFPRegister(pid, FPR25, &(((FPR *)data)->f25)); + GetFPRegister(pid, FPR26, &(((FPR *)data)->f26)); + GetFPRegister(pid, FPR27, &(((FPR *)data)->f27)); + GetFPRegister(pid, FPR28, &(((FPR *)data)->f28)); + GetFPRegister(pid, FPR29, &(((FPR *)data)->f29)); + GetFPRegister(pid, FPR30, &(((FPR *)data)->f30)); + GetFPRegister(pid, FPR31, &(((FPR *)data)->f31)); + GetFPRegister(pid, FPSCR, &(((FPR *)data)->fpscr)); + } else if (req == PTRACE_GETVRREGS && tid) { + GetVMRegister(tid, VR0, &(((VMX *)data)->vr0[0])); + GetVMRegister(tid, VR1, &(((VMX *)data)->vr1[0])); + GetVMRegister(tid, VR2, &(((VMX *)data)->vr2[0])); + GetVMRegister(tid, VR3, &(((VMX *)data)->vr3[0])); + GetVMRegister(tid, VR4, &(((VMX *)data)->vr4[0])); + GetVMRegister(tid, VR5, &(((VMX *)data)->vr5[0])); + GetVMRegister(tid, VR6, &(((VMX *)data)->vr6[0])); + GetVMRegister(tid, VR7, &(((VMX *)data)->vr7[0])); + GetVMRegister(tid, VR8, &(((VMX *)data)->vr8[0])); + GetVMRegister(tid, VR9, &(((VMX *)data)->vr9[0])); + GetVMRegister(tid, VR10, &(((VMX *)data)->vr10[0])); + GetVMRegister(tid, VR11, &(((VMX *)data)->vr11[0])); + GetVMRegister(tid, VR12, &(((VMX *)data)->vr12[0])); + GetVMRegister(tid, VR13, &(((VMX *)data)->vr13[0])); + GetVMRegister(tid, VR14, &(((VMX *)data)->vr14[0])); + GetVMRegister(tid, VR15, &(((VMX *)data)->vr15[0])); + GetVMRegister(tid, VR16, &(((VMX *)data)->vr16[0])); + GetVMRegister(tid, VR17, &(((VMX *)data)->vr17[0])); + GetVMRegister(tid, VR18, &(((VMX *)data)->vr18[0])); + GetVMRegister(tid, VR19, &(((VMX *)data)->vr19[0])); + GetVMRegister(tid, VR20, &(((VMX *)data)->vr20[0])); + GetVMRegister(tid, VR21, &(((VMX *)data)->vr21[0])); + GetVMRegister(tid, VR22, &(((VMX *)data)->vr22[0])); + GetVMRegister(tid, VR23, &(((VMX *)data)->vr23[0])); + GetVMRegister(tid, VR24, &(((VMX *)data)->vr24[0])); + GetVMRegister(tid, VR25, &(((VMX *)data)->vr25[0])); + GetVMRegister(tid, VR26, &(((VMX *)data)->vr26[0])); + GetVMRegister(tid, VR27, &(((VMX *)data)->vr27[0])); + GetVMRegister(tid, VR28, &(((VMX *)data)->vr28[0])); + GetVMRegister(tid, VR29, &(((VMX *)data)->vr29[0])); + GetVMRegister(tid, VR30, &(((VMX *)data)->vr30[0])); + GetVMRegister(tid, VR31, &(((VMX *)data)->vr31[0])); + GetVMRegister(tid, VSCR, &(((VMX *)data)->vscr[0])); + GetVMRegister(tid, VRSAVE, &(((VMX *)data)->vrsave)); + } else if (req == PTRACE_GETVSRREGS && tid) { + GetVSRegister(tid, VSR0, &(((VSX *)data)->vs0[0])); + GetVSRegister(tid, VSR1, &(((VSX *)data)->vs1[0])); + GetVSRegister(tid, VSR2, &(((VSX *)data)->vs2[0])); + GetVSRegister(tid, VSR3, &(((VSX *)data)->vs3[0])); + GetVSRegister(tid, VSR4, &(((VSX *)data)->vs4[0])); + GetVSRegister(tid, VSR5, &(((VSX *)data)->vs5[0])); + GetVSRegister(tid, VSR6, &(((VSX *)data)->vs6[0])); + GetVSRegister(tid, VSR7, &(((VSX *)data)->vs7[0])); + GetVSRegister(tid, VSR8, &(((VSX *)data)->vs8[0])); + GetVSRegister(tid, VSR9, &(((VSX *)data)->vs9[0])); + GetVSRegister(tid, VSR10, &(((VSX *)data)->vs10[0])); + GetVSRegister(tid, VSR11, &(((VSX *)data)->vs11[0])); + GetVSRegister(tid, VSR12, &(((VSX *)data)->vs12[0])); + GetVSRegister(tid, VSR13, &(((VSX *)data)->vs13[0])); + GetVSRegister(tid, VSR14, &(((VSX *)data)->vs14[0])); + GetVSRegister(tid, VSR15, &(((VSX *)data)->vs15[0])); + GetVSRegister(tid, VSR16, &(((VSX *)data)->vs16[0])); + GetVSRegister(tid, VSR17, &(((VSX *)data)->vs17[0])); + GetVSRegister(tid, VSR18, &(((VSX *)data)->vs18[0])); + GetVSRegister(tid, VSR19, &(((VSX *)data)->vs19[0])); + GetVSRegister(tid, VSR20, &(((VSX *)data)->vs20[0])); + GetVSRegister(tid, VSR21, &(((VSX *)data)->vs21[0])); + GetVSRegister(tid, VSR22, &(((VSX *)data)->vs22[0])); + GetVSRegister(tid, VSR23, &(((VSX *)data)->vs23[0])); + GetVSRegister(tid, VSR24, &(((VSX *)data)->vs24[0])); + GetVSRegister(tid, VSR25, &(((VSX *)data)->vs25[0])); + GetVSRegister(tid, VSR26, &(((VSX *)data)->vs26[0])); + GetVSRegister(tid, VSR27, &(((VSX *)data)->vs27[0])); + GetVSRegister(tid, VSR28, &(((VSX *)data)->vs28[0])); + GetVSRegister(tid, VSR29, &(((VSX *)data)->vs29[0])); + GetVSRegister(tid, VSR30, &(((VSX *)data)->vs30[0])); + GetVSRegister(tid, VSR31, &(((VSX *)data)->vs31[0])); + GetVSRegister(tid, VSR32, &(((VSX *)data)->vs32[0])); + GetVSRegister(tid, VSR33, &(((VSX *)data)->vs33[0])); + GetVSRegister(tid, VSR34, &(((VSX *)data)->vs34[0])); + GetVSRegister(tid, VSR35, &(((VSX *)data)->vs35[0])); + GetVSRegister(tid, VSR36, &(((VSX *)data)->vs36[0])); + GetVSRegister(tid, VSR37, &(((VSX *)data)->vs37[0])); + GetVSRegister(tid, VSR38, &(((VSX *)data)->vs38[0])); + GetVSRegister(tid, VSR39, &(((VSX *)data)->vs39[0])); + GetVSRegister(tid, VSR40, &(((VSX *)data)->vs40[0])); + GetVSRegister(tid, VSR41, &(((VSX *)data)->vs41[0])); + GetVSRegister(tid, VSR42, &(((VSX *)data)->vs42[0])); + GetVSRegister(tid, VSR43, &(((VSX *)data)->vs43[0])); + GetVSRegister(tid, VSR44, &(((VSX *)data)->vs44[0])); + GetVSRegister(tid, VSR45, &(((VSX *)data)->vs45[0])); + GetVSRegister(tid, VSR46, &(((VSX *)data)->vs46[0])); + GetVSRegister(tid, VSR47, &(((VSX *)data)->vs47[0])); + GetVSRegister(tid, VSR48, &(((VSX *)data)->vs48[0])); + GetVSRegister(tid, VSR49, &(((VSX *)data)->vs49[0])); + GetVSRegister(tid, VSR50, &(((VSX *)data)->vs50[0])); + GetVSRegister(tid, VSR51, &(((VSX *)data)->vs51[0])); + GetVSRegister(tid, VSR52, &(((VSX *)data)->vs52[0])); + GetVSRegister(tid, VSR53, &(((VSX *)data)->vs53[0])); + GetVSRegister(tid, VSR54, &(((VSX *)data)->vs54[0])); + GetVSRegister(tid, VSR55, &(((VSX *)data)->vs55[0])); + GetVSRegister(tid, VSR56, &(((VSX *)data)->vs56[0])); + GetVSRegister(tid, VSR57, &(((VSX *)data)->vs57[0])); + GetVSRegister(tid, VSR58, &(((VSX *)data)->vs58[0])); + GetVSRegister(tid, VSR59, &(((VSX *)data)->vs59[0])); + GetVSRegister(tid, VSR60, &(((VSX *)data)->vs60[0])); + GetVSRegister(tid, VSR61, &(((VSX *)data)->vs61[0])); + GetVSRegister(tid, VSR62, &(((VSX *)data)->vs62[0])); + GetVSRegister(tid, VSR63, &(((VSX *)data)->vs63[0])); + } else if (req < PT_COMMAND_MAX) { + if (req == PT_CONTINUE) { +#if 0 + // Use PTT_CONTINUE + const char procdir[] = "/proc/"; + const char lwpdir[] = "/lwp/"; + std::string process_task_dir = procdir + std::to_string(pid) + lwpdir; + DIR *dirproc = opendir(process_task_dir.c_str()); + + struct ptthreads64 pts; + int idx = 0; + lldb::tid_t tid = 0; + if (dirproc) { + struct dirent *direntry = nullptr; + while ((direntry = readdir(dirproc)) != nullptr) { + if (strcmp(direntry->d_name, ".") == 0 || strcmp(direntry->d_name, "..") == 0) { + continue; + } + tid = atoi(direntry->d_name); + pts.th[idx++] = tid; + } + closedir(dirproc); + } + pts.th[idx] = 0; + ret = ptrace64(PTT_CONTINUE, tid, (long long)1, (int)(size_t)data, (int *)&pts); +#else + int buf; + ptrace64(req, pid, 1, (int)(size_t)data, &buf); +#endif + } else if (req == PT_READ_BLOCK) { + ptrace64(req, pid, (long long)addr, (int)data_size, (int *)result); + } else if (req == PT_WRITE_BLOCK) { + ptrace64(req, pid, (long long)addr, (int)data_size, (int *)result); + } else if (req == PT_ATTACH) { + ptrace64(req, pid, 0, 0, nullptr); + } else if (req == PT_WATCH) { + ptrace64(req, pid, (long long)addr, (int)data_size, nullptr); + } else if (req == PT_DETACH) { + ptrace64(req, pid, 0, 0, nullptr); + } else { + assert(0 && "Not supported yet."); + } + } else { + assert(0 && "Not supported yet."); + } + + if (errno) { + error.SetErrorToErrno(); + ret = -1; + } + + LLDB_LOG(log, "ptrace({0}, {1}, {2}, {3}, {4})={5:x}", req, pid, addr, data, + data_size, ret); + + PtraceDisplayBytes(req, data, data_size); + + if (error.Fail()) + LLDB_LOG(log, "ptrace() failed: {0}", error); + + return error; +} + +llvm::Expected NativeProcessAIX::TraceSupported() { + return NativeProcessProtocol::TraceSupported(); +} + +Error NativeProcessAIX::TraceStart(StringRef json_request, StringRef type) { + return NativeProcessProtocol::TraceStart(json_request, type); +} + +Error NativeProcessAIX::TraceStop(const TraceStopRequest &request) { + return NativeProcessProtocol::TraceStop(request); +} + +Expected NativeProcessAIX::TraceGetState(StringRef type) { + return NativeProcessProtocol::TraceGetState(type); +} + +Expected> NativeProcessAIX::TraceGetBinaryData( + const TraceGetBinaryDataRequest &request) { + return NativeProcessProtocol::TraceGetBinaryData(request); +} diff --git a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.h b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.h new file mode 100644 index 0000000000000..bdb6f7c500885 --- /dev/null +++ b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.h @@ -0,0 +1,283 @@ +//===-- NativeProcessAIX.h ---------------------------------- -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_NativeProcessAIX_H_ +#define liblldb_NativeProcessAIX_H_ + +#include +#include + +#include "lldb/Host/Debug.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/lldb-types.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "lldb/Host/aix/Support.h" + +#include "NativeThreadAIX.h" +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "Plugins/Process/Utility/NativeProcessSoftwareSingleStep.h" + +namespace lldb_private { +class Status; +class Scalar; + +namespace process_aix { +/// \class NativeProcessAIX +/// Manages communication with the inferior (debugee) process. +/// +/// Upon construction, this class prepares and launches an inferior process +/// for debugging. +/// +/// Changes in the inferior process state are broadcasted. +class NativeProcessAIX : public NativeProcessProtocol, + private NativeProcessSoftwareSingleStep { +public: + class Manager : public NativeProcessProtocol::Manager { + public: + Manager(MainLoop &mainloop); + + llvm::Expected> + Launch(ProcessLaunchInfo &launch_info, + NativeDelegate &native_delegate) override; + + llvm::Expected> + Attach(lldb::pid_t pid, NativeDelegate &native_delegate) override; + + Extension GetSupportedExtensions() const override; + + void AddProcess(NativeProcessAIX &process) { + m_processes.insert(&process); + } + + void RemoveProcess(NativeProcessAIX &process) { + m_processes.erase(&process); + } + + // Collect an event for the given tid, waiting for it if necessary. + void CollectThread(::pid_t tid); + + private: + MainLoop::SignalHandleUP m_sigchld_handle; + + llvm::SmallPtrSet m_processes; + + // Threads (events) which haven't been claimed by any process. + llvm::DenseSet<::pid_t> m_unowned_threads; + + void SigchldHandler(); + }; + + // NativeProcessProtocol Interface + + ~NativeProcessAIX() override { m_manager.RemoveProcess(*this); } + + Status Resume(const ResumeActionList &resume_actions) override; + + Status Halt() override; + + Status Detach() override; + + Status Signal(int signo) override; + + Status Interrupt() override; + + Status Kill() override; + + lldb::addr_t GetSharedLibraryInfoAddress() override; + + Status GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) override; + + Status ReadMemory(lldb::addr_t addr, void *buf, size_t size, + size_t &bytes_read) override; + + Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size, + size_t &bytes_written) override; + + llvm::Expected AllocateMemory(size_t size, + uint32_t permissions) override; + + llvm::Error DeallocateMemory(lldb::addr_t addr) override; + + Status ReadMemoryTags(int32_t type, lldb::addr_t addr, size_t len, + std::vector &tags) override; + + Status WriteMemoryTags(int32_t type, lldb::addr_t addr, size_t len, + const std::vector &tags) override; + + size_t UpdateThreads() override; + + const ArchSpec &GetArchitecture() const override { return m_arch; } + + Status SetBreakpoint(lldb::addr_t addr, uint32_t size, + bool hardware) override; + + Status RemoveBreakpoint(lldb::addr_t addr, bool hardware = false) override; + + void DoStopIDBumped(uint32_t newBumpId) override; + + Status GetLoadedModuleFileSpec(const char *module_path, + FileSpec &file_spec) override; + + Status GetFileLoadAddress(const llvm::StringRef &file_name, + lldb::addr_t &load_addr) override; + + NativeThreadAIX *GetThreadByID(lldb::tid_t id); + NativeThreadAIX *GetCurrentThread(); + + llvm::ErrorOr> + GetAuxvData() const override { + // Not available on this target. + return llvm::errc::not_supported; + } + + /// Tracing + /// These methods implement the jLLDBTrace packets + /// \{ + llvm::Error TraceStart(llvm::StringRef json_request, + llvm::StringRef type) override; + + llvm::Error TraceStop(const TraceStopRequest &request) override; + + llvm::Expected + TraceGetState(llvm::StringRef type) override; + + llvm::Expected> + TraceGetBinaryData(const TraceGetBinaryDataRequest &request) override; + + llvm::Expected TraceSupported() override; + /// } + + // Interface used by NativeRegisterContext-derived classes. + static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr = nullptr, + void *data = nullptr, size_t data_size = 0, + long *result = nullptr); + + bool SupportHardwareSingleStepping() const; + + /// Writes a siginfo_t structure corresponding to the given thread ID to the + /// memory region pointed to by \p siginfo. + int8_t GetSignalInfo(WaitStatus wstatus) const; + +protected: + llvm::Expected> + GetSoftwareBreakpointTrapOpcode(size_t size_hint) override; + + llvm::Expected Syscall(llvm::ArrayRef args); + +private: + Manager &m_manager; + /*MainLoop::SignalHandleUP m_sigchld_handle;*/ + ArchSpec m_arch; + /*MainLoop& m_main_loop;*/ + + LazyBool m_supports_mem_region = eLazyBoolCalculate; + std::vector> m_mem_region_cache; + + lldb::tid_t m_pending_notification_tid = LLDB_INVALID_THREAD_ID; + + /// Inferior memory (allocated by us) and its size. + llvm::DenseMap m_allocated_memory; + + // Private Instance Methods + NativeProcessAIX(::pid_t pid, int terminal_fd, NativeDelegate &delegate, + const ArchSpec &arch, Manager &manager, + llvm::ArrayRef<::pid_t> tids); + + // Returns a list of process threads that we have attached to. + static llvm::Expected> Attach(::pid_t pid); + + static Status SetDefaultPtraceOpts(const lldb::pid_t); + + bool TryHandleWaitStatus(lldb::pid_t pid, WaitStatus status); + + void MonitorCallback(NativeThreadAIX &thread, WaitStatus status); + + void MonitorSIGTRAP(const WaitStatus status, NativeThreadAIX &thread); + + void MonitorTrace(NativeThreadAIX &thread); + + void MonitorBreakpoint(NativeThreadAIX &thread); + + void MonitorWatchpoint(NativeThreadAIX &thread, uint32_t wp_index); + + void MonitorSignal(const WaitStatus status, NativeThreadAIX &thread); + + bool HasThreadNoLock(lldb::tid_t thread_id); + + void StopTrackingThread(NativeThreadAIX &thread); + + /// Create a new thread. + /// + /// If process tracing is enabled and the thread can't be traced, then the + /// thread is left stopped with a \a eStopReasonProcessorTrace status, and + /// then the process is stopped. + /// + /// \param[in] resume + /// If a tracing error didn't happen, then resume the thread after + /// creation if \b true, or leave it stopped with SIGSTOP if \b false. + NativeThreadAIX &AddThread(lldb::tid_t thread_id, bool resume); + + /// Start tracing a new thread if process tracing is enabled. + /// + /// Trace mechanisms should modify this method to provide automatic tracing + /// for new threads. + Status NotifyTracersOfNewThread(lldb::tid_t tid); + + /// Stop tracing threads upon a destroy event. + /// + /// Trace mechanisms should modify this method to provide automatic trace + /// stopping for threads being destroyed. + Status NotifyTracersOfThreadDestroyed(lldb::tid_t tid); + + void NotifyTracersProcessWillResume() override; + + void NotifyTracersProcessDidStop() override; + /// Writes the raw event message code (vis-a-vis PTRACE_GETEVENTMSG) + /// corresponding to the given thread ID to the memory pointed to by @p + /// message. + Status GetEventMessage(lldb::tid_t tid, unsigned long *message); + + void NotifyThreadDeath(lldb::tid_t tid); + + Status Detach(lldb::tid_t tid); + + // This method is requests a stop on all threads which are still running. It + // sets up a + // deferred delegate notification, which will fire once threads report as + // stopped. The + // triggerring_tid will be set as the current thread (main stop reason). + void StopRunningThreads(lldb::tid_t triggering_tid); + + // Notify the delegate if all threads have stopped. + void SignalIfAllThreadsStopped(); + + // Resume the given thread, optionally passing it the given signal. The type + // of resume + // operation (continue, single-step) depends on the state parameter. + Status ResumeThread(NativeThreadAIX &thread, lldb::StateType state, + int signo); + + void ThreadWasCreated(NativeThreadAIX &thread); + + void SigchldHandler(); + + Status PopulateMemoryRegionCache(); + + // Handle a clone()-like event. + bool MonitorClone(NativeThreadAIX &parent, lldb::pid_t child_pid, + int event); +}; + +} // namespace process_aix +} // namespace lldb_private + +#endif // #ifndef liblldb_NativeProcessAIX_H_ diff --git a/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.cpp b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.cpp new file mode 100644 index 0000000000000..0859f9501c1b6 --- /dev/null +++ b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.cpp @@ -0,0 +1,157 @@ +//===-- NativeRegisterContextAIX.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 "NativeRegisterContextAIX.h" + +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/Host/common/NativeThreadProtocol.h" +#include "lldb/Utility/RegisterValue.h" + +#include "Plugins/Process/AIX/NativeProcessAIX.h" +#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" +#include "lldb/Host/aix/Ptrace.h" + +using namespace lldb_private; +using namespace lldb_private::process_aix; + +lldb::ByteOrder NativeRegisterContextAIX::GetByteOrder() const { + return m_thread.GetProcess().GetByteOrder(); +} + +Status NativeRegisterContextAIX::ReadRegisterRaw(uint32_t reg_index, + RegisterValue ®_value) { + const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(reg_index); + if (!reg_info) + return Status("register %" PRIu32 " not found", reg_index); + + return DoReadRegisterValue(GetPtraceOffset(reg_index), reg_info->name, + reg_info->byte_size, reg_value); +} + +Status +NativeRegisterContextAIX::WriteRegisterRaw(uint32_t reg_index, + const RegisterValue ®_value) { + uint32_t reg_to_write = reg_index; + RegisterValue value_to_write = reg_value; + + // Check if this is a subregister of a full register. + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_index); + if (reg_info->invalidate_regs && + (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM)) { + Status error; + + RegisterValue full_value; + uint32_t full_reg = reg_info->invalidate_regs[0]; + const RegisterInfo *full_reg_info = GetRegisterInfoAtIndex(full_reg); + + // Read the full register. + error = ReadRegister(full_reg_info, full_value); + if (error.Fail()) + return error; + + lldb::ByteOrder byte_order = GetByteOrder(); + uint8_t dst[RegisterValue::kMaxRegisterByteSize]; + + // Get the bytes for the full register. + const uint32_t dest_size = full_value.GetAsMemoryData( + *full_reg_info, dst, sizeof(dst), byte_order, error); + if (error.Success() && dest_size) { + uint8_t src[RegisterValue::kMaxRegisterByteSize]; + + // Get the bytes for the source data. + const uint32_t src_size = reg_value.GetAsMemoryData( + *reg_info, src, sizeof(src), byte_order, error); + if (error.Success() && src_size && (src_size < dest_size)) { + // Copy the src bytes to the destination. + memcpy(dst + (reg_info->byte_offset & 0x1), src, src_size); + // Set this full register as the value to write. + value_to_write.SetBytes(dst, full_value.GetByteSize(), byte_order); + value_to_write.SetType(*full_reg_info); + reg_to_write = full_reg; + } + } + } + + const RegisterInfo *const register_to_write_info_p = + GetRegisterInfoAtIndex(reg_to_write); + assert(register_to_write_info_p && + "register to write does not have valid RegisterInfo"); + if (!register_to_write_info_p) + return Status("NativeRegisterContextAIX::%s failed to get RegisterInfo " + "for write register index %" PRIu32, + __FUNCTION__, reg_to_write); + + return DoWriteRegisterValue(GetPtraceOffset(reg_index), reg_info->name, + reg_value); +} + +Status NativeRegisterContextAIX::ReadGPR() { + return NativeProcessAIX::PtraceWrapper( + PTRACE_GETREGS, m_thread.GetID(), nullptr, GetGPRBuffer(), GetGPRSize()); +} + +Status NativeRegisterContextAIX::WriteGPR() { + return NativeProcessAIX::PtraceWrapper( + PTRACE_SETREGS, m_thread.GetID(), nullptr, GetGPRBuffer(), GetGPRSize()); +} + +Status NativeRegisterContextAIX::ReadFPR() { + return NativeProcessAIX::PtraceWrapper(PTRACE_GETFPREGS, m_thread.GetID(), + nullptr, GetFPRBuffer(), + GetFPRSize()); +} + +Status NativeRegisterContextAIX::WriteFPR() { + return NativeProcessAIX::PtraceWrapper(PTRACE_SETFPREGS, m_thread.GetID(), + nullptr, GetFPRBuffer(), + GetFPRSize()); +} + +Status NativeRegisterContextAIX::ReadRegisterSet(void *buf, size_t buf_size, + unsigned int regset) { + return NativeProcessAIX::PtraceWrapper(PTRACE_GETREGSET, m_thread.GetID(), + static_cast(®set), buf, + buf_size); +} + +Status NativeRegisterContextAIX::WriteRegisterSet(void *buf, size_t buf_size, + unsigned int regset) { + return NativeProcessAIX::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), + static_cast(®set), buf, + buf_size); +} + +Status NativeRegisterContextAIX::DoReadRegisterValue(uint32_t offset, + const char *reg_name, + uint32_t size, + RegisterValue &value) { + Log *log = GetLog(POSIXLog::Registers); + + long data; + Status error = NativeProcessAIX::PtraceWrapper( + PTRACE_PEEKUSER, m_thread.GetID(), reinterpret_cast(offset), + nullptr, 0, &data); + + if (error.Success()) + // First cast to an unsigned of the same size to avoid sign extension. + value.SetUInt(static_cast(data), size); + + LLDB_LOG(log, "{0}: {1:x}", reg_name, data); + return error; +} + +Status NativeRegisterContextAIX::DoWriteRegisterValue( + uint32_t offset, const char *reg_name, const RegisterValue &value) { + Log *log = GetLog(POSIXLog::Registers); + + void *buf = reinterpret_cast(value.GetAsUInt64()); + LLDB_LOG(log, "{0}: {1}", reg_name, buf); + + return NativeProcessAIX::PtraceWrapper( + PTRACE_POKEUSER, m_thread.GetID(), reinterpret_cast(offset), buf); +} diff --git a/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.h b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.h new file mode 100644 index 0000000000000..9c2a326856c0b --- /dev/null +++ b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.h @@ -0,0 +1,133 @@ +//===-- NativeRegisterContextAIX.h ----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_NativeRegisterContextAIX_h +#define lldb_NativeRegisterContextAIX_h + +#include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h" +#include "lldb/Host/common/NativeThreadProtocol.h" +#include "lldb/Target/MemoryTagManager.h" +#include "llvm/Support/Error.h" + +namespace lldb_private { +namespace process_aix { + +class NativeThreadAIX; + +class NativeRegisterContextAIX + : public virtual NativeRegisterContextRegisterInfo { +public: + // This function is implemented in the NativeRegisterContextAIX_* subclasses + // to create a new instance of the host specific NativeRegisterContextAIX. + // The implementations can't collide as only one NativeRegisterContextAIX_* + // variant should be compiled into the final executable. + static std::unique_ptr + CreateHostNativeRegisterContextAIX(const ArchSpec &target_arch, + NativeThreadAIX &native_thread); + + // Invalidates cached values in register context data structures + virtual void InvalidateAllRegisters(){} + + struct SyscallData { + /// The syscall instruction. If the architecture uses software + /// single-stepping, the instruction should also be followed by a trap to + /// ensure the process is stopped after the syscall. + llvm::ArrayRef Insn; + + /// Registers used for syscall arguments. The first register is used to + /// store the syscall number. + llvm::ArrayRef Args; + + uint32_t Result; ///< Register containing the syscall result. + }; + /// Return architecture-specific data needed to make inferior syscalls, if + /// they are supported. + virtual std::optional GetSyscallData() { return std::nullopt; } + + struct MmapData { + // Syscall numbers can be found (e.g.) in /usr/include/asm/unistd.h for the + // relevant architecture. + unsigned SysMmap; ///< mmap syscall number. + unsigned SysMunmap; ///< munmap syscall number + }; + /// Return the architecture-specific data needed to make mmap syscalls, if + /// they are supported. + virtual std::optional GetMmapData() { return std::nullopt; } + + struct MemoryTaggingDetails { + /// Object with tag handling utilities. If the function below returns + /// a valid structure, you can assume that this pointer is valid. + std::unique_ptr manager; + int ptrace_read_req; /// ptrace operation number for memory tag read + int ptrace_write_req; /// ptrace operation number for memory tag write + }; + /// Return architecture specific data needed to use memory tags, + /// if they are supported. + virtual llvm::Expected + GetMemoryTaggingDetails(int32_t type) { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "Architecture does not support memory tagging"); + } + +protected: + // NB: This constructor is here only because gcc<=6.5 requires a virtual base + // class initializer on abstract class (even though it is never used). It can + // be deleted once we move to gcc>=7.0. + NativeRegisterContextAIX(NativeThreadProtocol &thread) + : NativeRegisterContextRegisterInfo(thread, nullptr) {} + + lldb::ByteOrder GetByteOrder() const; + + virtual Status ReadRegisterRaw(uint32_t reg_index, RegisterValue ®_value); + + virtual Status WriteRegisterRaw(uint32_t reg_index, + const RegisterValue ®_value); + + virtual Status ReadRegisterSet(void *buf, size_t buf_size, + unsigned int regset); + + virtual Status WriteRegisterSet(void *buf, size_t buf_size, + unsigned int regset); + + virtual Status ReadGPR(); + + virtual Status WriteGPR(); + + virtual Status ReadFPR(); + + virtual Status WriteFPR(); + + virtual void *GetGPRBuffer() = 0; + + virtual size_t GetGPRSize() const { + return GetRegisterInfoInterface().GetGPRSize(); + } + + virtual void *GetFPRBuffer() = 0; + + virtual size_t GetFPRSize() = 0; + + virtual uint32_t GetPtraceOffset(uint32_t reg_index) { + return GetRegisterInfoAtIndex(reg_index)->byte_offset; + } + + // The Do*** functions are executed on the privileged thread and can perform + // ptrace + // operations directly. + virtual Status DoReadRegisterValue(uint32_t offset, const char *reg_name, + uint32_t size, RegisterValue &value); + + virtual Status DoWriteRegisterValue(uint32_t offset, const char *reg_name, + const RegisterValue &value); +}; + +} // namespace process_aix +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextAIX_h diff --git a/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.cpp b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.cpp new file mode 100644 index 0000000000000..1996373791748 --- /dev/null +++ b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.cpp @@ -0,0 +1,744 @@ +//===-- NativeRegisterContextAIX_ppc64.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 +// +//===----------------------------------------------------------------------===// + +// This implementation is related to the OpenPOWER ABI for Power Architecture +// 64-bit ELF V2 ABI + +#if defined(__powerpc64__) + +#include "NativeRegisterContextAIX_ppc64.h" + +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Status.h" +#include "lldb/Host/aix/Ptrace.h" + +#include "Plugins/Process/AIX/NativeProcessAIX.h" +#include "Plugins/Process/Linux/Procfs.h" +#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" +#include "Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.h" + +// System includes - They have to be included after framework includes because +// they define some macros which collide with variable names in other modules +#include +#include +#include +#include + +#define REG_CONTEXT_SIZE \ + (GetGPRSize() + GetFPRSize() + sizeof(m_vmx_ppc64le) + sizeof(m_vsx_ppc64le)) +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_aix; + +static const uint32_t g_gpr_regnums_ppc64le[] = { + gpr_r0_ppc64le, gpr_r1_ppc64le, gpr_r2_ppc64le, gpr_r3_ppc64le, + gpr_r4_ppc64le, gpr_r5_ppc64le, gpr_r6_ppc64le, gpr_r7_ppc64le, + gpr_r8_ppc64le, gpr_r9_ppc64le, gpr_r10_ppc64le, gpr_r11_ppc64le, + gpr_r12_ppc64le, gpr_r13_ppc64le, gpr_r14_ppc64le, gpr_r15_ppc64le, + gpr_r16_ppc64le, gpr_r17_ppc64le, gpr_r18_ppc64le, gpr_r19_ppc64le, + gpr_r20_ppc64le, gpr_r21_ppc64le, gpr_r22_ppc64le, gpr_r23_ppc64le, + gpr_r24_ppc64le, gpr_r25_ppc64le, gpr_r26_ppc64le, gpr_r27_ppc64le, + gpr_r28_ppc64le, gpr_r29_ppc64le, gpr_r30_ppc64le, gpr_r31_ppc64le, + gpr_pc_ppc64le, gpr_msr_ppc64le, gpr_origr3_ppc64le, gpr_ctr_ppc64le, + gpr_lr_ppc64le, gpr_xer_ppc64le, gpr_cr_ppc64le, gpr_softe_ppc64le, + gpr_trap_ppc64le, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; + +static const uint32_t g_fpr_regnums_ppc64le[] = { + fpr_f0_ppc64le, fpr_f1_ppc64le, fpr_f2_ppc64le, fpr_f3_ppc64le, + fpr_f4_ppc64le, fpr_f5_ppc64le, fpr_f6_ppc64le, fpr_f7_ppc64le, + fpr_f8_ppc64le, fpr_f9_ppc64le, fpr_f10_ppc64le, fpr_f11_ppc64le, + fpr_f12_ppc64le, fpr_f13_ppc64le, fpr_f14_ppc64le, fpr_f15_ppc64le, + fpr_f16_ppc64le, fpr_f17_ppc64le, fpr_f18_ppc64le, fpr_f19_ppc64le, + fpr_f20_ppc64le, fpr_f21_ppc64le, fpr_f22_ppc64le, fpr_f23_ppc64le, + fpr_f24_ppc64le, fpr_f25_ppc64le, fpr_f26_ppc64le, fpr_f27_ppc64le, + fpr_f28_ppc64le, fpr_f29_ppc64le, fpr_f30_ppc64le, fpr_f31_ppc64le, + fpr_fpscr_ppc64le, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; + +static const uint32_t g_vmx_regnums_ppc64le[] = { + vmx_vr0_ppc64le, vmx_vr1_ppc64le, vmx_vr2_ppc64le, vmx_vr3_ppc64le, + vmx_vr4_ppc64le, vmx_vr5_ppc64le, vmx_vr6_ppc64le, vmx_vr7_ppc64le, + vmx_vr8_ppc64le, vmx_vr9_ppc64le, vmx_vr10_ppc64le, vmx_vr11_ppc64le, + vmx_vr12_ppc64le, vmx_vr13_ppc64le, vmx_vr14_ppc64le, vmx_vr15_ppc64le, + vmx_vr16_ppc64le, vmx_vr17_ppc64le, vmx_vr18_ppc64le, vmx_vr19_ppc64le, + vmx_vr20_ppc64le, vmx_vr21_ppc64le, vmx_vr22_ppc64le, vmx_vr23_ppc64le, + vmx_vr24_ppc64le, vmx_vr25_ppc64le, vmx_vr26_ppc64le, vmx_vr27_ppc64le, + vmx_vr28_ppc64le, vmx_vr29_ppc64le, vmx_vr30_ppc64le, vmx_vr31_ppc64le, + vmx_vscr_ppc64le, vmx_vrsave_ppc64le, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; + +static const uint32_t g_vsx_regnums_ppc64le[] = { + vsx_vs0_ppc64le, vsx_vs1_ppc64le, vsx_vs2_ppc64le, vsx_vs3_ppc64le, + vsx_vs4_ppc64le, vsx_vs5_ppc64le, vsx_vs6_ppc64le, vsx_vs7_ppc64le, + vsx_vs8_ppc64le, vsx_vs9_ppc64le, vsx_vs10_ppc64le, vsx_vs11_ppc64le, + vsx_vs12_ppc64le, vsx_vs13_ppc64le, vsx_vs14_ppc64le, vsx_vs15_ppc64le, + vsx_vs16_ppc64le, vsx_vs17_ppc64le, vsx_vs18_ppc64le, vsx_vs19_ppc64le, + vsx_vs20_ppc64le, vsx_vs21_ppc64le, vsx_vs22_ppc64le, vsx_vs23_ppc64le, + vsx_vs24_ppc64le, vsx_vs25_ppc64le, vsx_vs26_ppc64le, vsx_vs27_ppc64le, + vsx_vs28_ppc64le, vsx_vs29_ppc64le, vsx_vs30_ppc64le, vsx_vs31_ppc64le, + vsx_vs32_ppc64le, vsx_vs33_ppc64le, vsx_vs34_ppc64le, vsx_vs35_ppc64le, + vsx_vs36_ppc64le, vsx_vs37_ppc64le, vsx_vs38_ppc64le, vsx_vs39_ppc64le, + vsx_vs40_ppc64le, vsx_vs41_ppc64le, vsx_vs42_ppc64le, vsx_vs43_ppc64le, + vsx_vs44_ppc64le, vsx_vs45_ppc64le, vsx_vs46_ppc64le, vsx_vs47_ppc64le, + vsx_vs48_ppc64le, vsx_vs49_ppc64le, vsx_vs50_ppc64le, vsx_vs51_ppc64le, + vsx_vs52_ppc64le, vsx_vs53_ppc64le, vsx_vs54_ppc64le, vsx_vs55_ppc64le, + vsx_vs56_ppc64le, vsx_vs57_ppc64le, vsx_vs58_ppc64le, vsx_vs59_ppc64le, + vsx_vs60_ppc64le, vsx_vs61_ppc64le, vsx_vs62_ppc64le, vsx_vs63_ppc64le, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; + +// Number of register sets provided by this context. +static constexpr int k_num_register_sets = 4; + +static const RegisterSet g_reg_sets_ppc64le[k_num_register_sets] = { + {"General Purpose Registers", "gpr", k_num_gpr_registers_ppc64le, + g_gpr_regnums_ppc64le}, + {"Floating Point Registers", "fpr", k_num_fpr_registers_ppc64le, + g_fpr_regnums_ppc64le}, + {"AltiVec/VMX Registers", "vmx", k_num_vmx_registers_ppc64le, + g_vmx_regnums_ppc64le}, + {"VSX Registers", "vsx", k_num_vsx_registers_ppc64le, + g_vsx_regnums_ppc64le}, +}; + +std::unique_ptr +NativeRegisterContextAIX::CreateHostNativeRegisterContextAIX( + const ArchSpec &target_arch, NativeThreadAIX &native_thread) { + switch (target_arch.GetMachine()) { + case llvm::Triple::ppc64: + return std::make_unique(target_arch, + native_thread); + default: + llvm_unreachable("have no register context for architecture"); + } +} + +NativeRegisterContextAIX_ppc64::NativeRegisterContextAIX_ppc64( + const ArchSpec &target_arch, NativeThreadProtocol &native_thread) + : NativeRegisterContextRegisterInfo( + native_thread, new RegisterInfoPOSIX_ppc64le(target_arch)), + NativeRegisterContextAIX(native_thread) { + if (target_arch.GetMachine() != llvm::Triple::ppc64) { + llvm_unreachable("Unhandled target architecture."); + } + + ::memset(&m_gpr_ppc64le, 0, sizeof(m_gpr_ppc64le)); + ::memset(&m_fpr_ppc64le, 0, sizeof(m_fpr_ppc64le)); + ::memset(&m_vmx_ppc64le, 0, sizeof(m_vmx_ppc64le)); + ::memset(&m_vsx_ppc64le, 0, sizeof(m_vsx_ppc64le)); + ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs)); +} + +uint32_t NativeRegisterContextAIX_ppc64::GetRegisterSetCount() const { + return k_num_register_sets; +} + +const RegisterSet * +NativeRegisterContextAIX_ppc64::GetRegisterSet(uint32_t set_index) const { + if (set_index < k_num_register_sets) + return &g_reg_sets_ppc64le[set_index]; + + return nullptr; +} + +uint32_t NativeRegisterContextAIX_ppc64::GetUserRegisterCount() const { + uint32_t count = 0; + for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) + count += g_reg_sets_ppc64le[set_index].num_registers; + return count; +} + +Status NativeRegisterContextAIX_ppc64::ReadRegister( + const RegisterInfo *reg_info, RegisterValue ®_value) { + Status error; + + if (!reg_info) { + error.SetErrorString("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + + if (IsFPR(reg)) { + error = ReadFPR(); + if (error.Fail()) + return error; + + // Get pointer to m_fpr_ppc64le variable and set the data from it. + uint32_t fpr_offset = CalculateFprOffset(reg_info); + assert(fpr_offset < sizeof m_fpr_ppc64le); + uint8_t *src = (uint8_t *)&m_fpr_ppc64le + fpr_offset; + reg_value.SetFromMemoryData(*reg_info, src, reg_info->byte_size, + eByteOrderLittle, error); + } else if (IsVSX(reg)) { + uint32_t vsx_offset = CalculateVsxOffset(reg_info); + assert(vsx_offset < sizeof(m_vsx_ppc64le)); + + if (vsx_offset < sizeof(m_vsx_ppc64le) / 2) { + error = ReadVSX(); + if (error.Fail()) + return error; + + error = ReadFPR(); + if (error.Fail()) + return error; + + uint64_t value[2]; + uint8_t *dst, *src; + dst = (uint8_t *)&value; + src = (uint8_t *)&m_vsx_ppc64le + vsx_offset / 2; + ::memcpy(dst, src, 8); + dst += 8; + src = (uint8_t *)&m_fpr_ppc64le + vsx_offset / 2; + ::memcpy(dst, src, 8); + reg_value.SetFromMemoryData(*reg_info, &value, reg_info->byte_size, + eByteOrderLittle, error); + } else { + error = ReadVMX(); + if (error.Fail()) + return error; + + // Get pointer to m_vmx_ppc64le variable and set the data from it. + uint32_t vmx_offset = vsx_offset - sizeof(m_vsx_ppc64le) / 2; + uint8_t *src = (uint8_t *)&m_vmx_ppc64le + vmx_offset; + reg_value.SetFromMemoryData(*reg_info, src, reg_info->byte_size, + eByteOrderLittle, error); + } + } else if (IsVMX(reg)) { + error = ReadVMX(); + if (error.Fail()) + return error; + + // Get pointer to m_vmx_ppc64le variable and set the data from it. + uint32_t vmx_offset = CalculateVmxOffset(reg_info); + assert(vmx_offset < sizeof m_vmx_ppc64le); + uint8_t *src = (uint8_t *)&m_vmx_ppc64le + vmx_offset; + reg_value.SetFromMemoryData(*reg_info, src, reg_info->byte_size, + eByteOrderLittle, error); + } else if (IsGPR(reg)) { + error = ReadGPR(); + if (error.Fail()) + return error; + + uint8_t *src = (uint8_t *) &m_gpr_ppc64le + reg_info->byte_offset; + reg_value.SetFromMemoryData(*reg_info, src, reg_info->byte_size, + eByteOrderLittle, error); + } else { + return Status("failed - register wasn't recognized to be a GPR, FPR, VSX " + "or VMX, read strategy unknown"); + } + + return error; +} + +Status NativeRegisterContextAIX_ppc64::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue ®_value) { + Status error; + if (!reg_info) + return Status("reg_info NULL"); + + const uint32_t reg_index = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg_index == LLDB_INVALID_REGNUM) + return Status("no lldb regnum for %s", reg_info && reg_info->name + ? reg_info->name + : ""); + + if (IsGPR(reg_index)) { + error = ReadGPR(); + if (error.Fail()) + return error; + + uint8_t *dst = (uint8_t *)&m_gpr_ppc64le + reg_info->byte_offset; + ::memcpy(dst, reg_value.GetBytes(), reg_value.GetByteSize()); + *(uint64_t *)dst = llvm::byteswap(*(uint64_t *)dst); + + error = WriteGPR(); + if (error.Fail()) + return error; + + return Status(); + } + + if (IsFPR(reg_index)) { + error = ReadFPR(); + if (error.Fail()) + return error; + + // Get pointer to m_fpr_ppc64le variable and set the data to it. + uint32_t fpr_offset = CalculateFprOffset(reg_info); + assert(fpr_offset < GetFPRSize()); + uint8_t *dst = (uint8_t *)&m_fpr_ppc64le + fpr_offset; + ::memcpy(dst, reg_value.GetBytes(), reg_value.GetByteSize()); + + error = WriteFPR(); + if (error.Fail()) + return error; + + return Status(); + } + + if (IsVMX(reg_index)) { + error = ReadVMX(); + if (error.Fail()) + return error; + + // Get pointer to m_vmx_ppc64le variable and set the data to it. + uint32_t vmx_offset = CalculateVmxOffset(reg_info); + assert(vmx_offset < sizeof(m_vmx_ppc64le)); + uint8_t *dst = (uint8_t *)&m_vmx_ppc64le + vmx_offset; + ::memcpy(dst, reg_value.GetBytes(), reg_value.GetByteSize()); + + error = WriteVMX(); + if (error.Fail()) + return error; + + return Status(); + } + + if (IsVSX(reg_index)) { + uint32_t vsx_offset = CalculateVsxOffset(reg_info); + assert(vsx_offset < sizeof(m_vsx_ppc64le)); + + if (vsx_offset < sizeof(m_vsx_ppc64le) / 2) { + error = ReadVSX(); + if (error.Fail()) + return error; + + error = ReadFPR(); + if (error.Fail()) + return error; + + uint64_t value[2]; + ::memcpy(value, reg_value.GetBytes(), 16); + uint8_t *dst, *src; + src = (uint8_t *)value; + dst = (uint8_t *)&m_vsx_ppc64le + vsx_offset / 2; + ::memcpy(dst, src, 8); + src += 8; + dst = (uint8_t *)&m_fpr_ppc64le + vsx_offset / 2; + ::memcpy(dst, src, 8); + + WriteVSX(); + WriteFPR(); + } else { + error = ReadVMX(); + if (error.Fail()) + return error; + + // Get pointer to m_vmx_ppc64le variable and set the data from it. + uint32_t vmx_offset = vsx_offset - sizeof(m_vsx_ppc64le) / 2; + uint8_t *dst = (uint8_t *)&m_vmx_ppc64le + vmx_offset; + ::memcpy(dst, reg_value.GetBytes(), reg_value.GetByteSize()); + WriteVMX(); + } + + return Status(); + } + + return Status("failed - register wasn't recognized to be a GPR, FPR, VSX " + "or VMX, write strategy unknown"); +} + +Status NativeRegisterContextAIX_ppc64::ReadAllRegisterValues( + lldb::WritableDataBufferSP &data_sp) { + Status error; + + data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0)); + error = ReadGPR(); + if (error.Fail()) + return error; + + error = ReadFPR(); + if (error.Fail()) + return error; + + error = ReadVMX(); + if (error.Fail()) + return error; + + error = ReadVSX(); + if (error.Fail()) + return error; + + uint8_t *dst = data_sp->GetBytes(); + ::memcpy(dst, &m_gpr_ppc64le, GetGPRSize()); + dst += GetGPRSize(); + ::memcpy(dst, &m_fpr_ppc64le, GetFPRSize()); + dst += GetFPRSize(); + ::memcpy(dst, &m_vmx_ppc64le, sizeof(m_vmx_ppc64le)); + dst += sizeof(m_vmx_ppc64le); + ::memcpy(dst, &m_vsx_ppc64le, sizeof(m_vsx_ppc64le)); + + return error; +} + +Status NativeRegisterContextAIX_ppc64::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + Status error; + + if (!data_sp) { + error.SetErrorStringWithFormat( + "NativeRegisterContextAIX_ppc64::%s invalid data_sp provided", + __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) { + error.SetErrorStringWithFormat( + "NativeRegisterContextAIX_ppc64::%s data_sp contained mismatched " + "data size, expected %" PRIu64 ", actual %" PRIu64, + __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize()); + return error; + } + + const uint8_t *src = data_sp->GetBytes(); + if (src == nullptr) { + error.SetErrorStringWithFormat("NativeRegisterContextAIX_ppc64::%s " + "DataBuffer::GetBytes() returned a null " + "pointer", + __FUNCTION__); + return error; + } + + ::memcpy(&m_gpr_ppc64le, src, GetGPRSize()); + error = WriteGPR(); + + if (error.Fail()) + return error; + + src += GetGPRSize(); + ::memcpy(&m_fpr_ppc64le, src, GetFPRSize()); + + error = WriteFPR(); + if (error.Fail()) + return error; + + src += GetFPRSize(); + ::memcpy(&m_vmx_ppc64le, src, sizeof(m_vmx_ppc64le)); + + error = WriteVMX(); + if (error.Fail()) + return error; + + src += sizeof(m_vmx_ppc64le); + ::memcpy(&m_vsx_ppc64le, src, sizeof(m_vsx_ppc64le)); + error = WriteVSX(); + + return error; +} + +bool NativeRegisterContextAIX_ppc64::IsGPR(unsigned reg) const { + return reg <= k_last_gpr_ppc64le; // GPR's come first. +} + +bool NativeRegisterContextAIX_ppc64::IsFPR(unsigned reg) const { + return (k_first_fpr_ppc64le <= reg && reg <= k_last_fpr_ppc64le); +} + +uint32_t NativeRegisterContextAIX_ppc64::CalculateFprOffset( + const RegisterInfo *reg_info) const { + return reg_info->byte_offset - + GetRegisterInfoAtIndex(k_first_fpr_ppc64le)->byte_offset; +} + +uint32_t NativeRegisterContextAIX_ppc64::CalculateVmxOffset( + const RegisterInfo *reg_info) const { + return reg_info->byte_offset - + GetRegisterInfoAtIndex(k_first_vmx_ppc64le)->byte_offset; +} + +uint32_t NativeRegisterContextAIX_ppc64::CalculateVsxOffset( + const RegisterInfo *reg_info) const { + return reg_info->byte_offset - + GetRegisterInfoAtIndex(k_first_vsx_ppc64le)->byte_offset; +} + +Status NativeRegisterContextAIX_ppc64::ReadVMX() { + return NativeProcessAIX::PtraceWrapper(PTRACE_GETVRREGS, m_thread.GetID(), + nullptr, &m_vmx_ppc64le, + sizeof(m_vmx_ppc64le)); +} + +Status NativeRegisterContextAIX_ppc64::WriteVMX() { + //FIXME + int regset = 0/*NT_PPC_VMX*/; + return NativeProcessAIX::PtraceWrapper(PT_CLEAR/*PTRACE_SETVRREGS*/, m_thread.GetID(), + ®set, &m_vmx_ppc64le, + sizeof(m_vmx_ppc64le)); +} + +Status NativeRegisterContextAIX_ppc64::ReadVSX() { + return NativeProcessAIX::PtraceWrapper(PTRACE_GETVSRREGS, m_thread.GetID(), + nullptr, &m_vsx_ppc64le, + sizeof(m_vsx_ppc64le)); +} + +Status NativeRegisterContextAIX_ppc64::WriteVSX() { + //FIXME + int regset = 0/*NT_PPC_VSX*/; + return NativeProcessAIX::PtraceWrapper(PT_CLEAR/*PTRACE_SETVSRREGS*/, m_thread.GetID(), + ®set, &m_vsx_ppc64le, + sizeof(m_vsx_ppc64le)); +} + +bool NativeRegisterContextAIX_ppc64::IsVMX(unsigned reg) { + return (reg >= k_first_vmx_ppc64le) && (reg <= k_last_vmx_ppc64le); +} + +bool NativeRegisterContextAIX_ppc64::IsVSX(unsigned reg) { + return (reg >= k_first_vsx_ppc64le) && (reg <= k_last_vsx_ppc64le); +} + +uint32_t NativeRegisterContextAIX_ppc64::NumSupportedHardwareWatchpoints() { + Log *log = GetLog(POSIXLog::Watchpoints); + + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + + if (error.Fail()) + return 0; + + LLDB_LOG(log, "{0}", m_max_hwp_supported); + return m_max_hwp_supported; +} + +uint32_t NativeRegisterContextAIX_ppc64::SetHardwareWatchpoint( + lldb::addr_t addr, size_t size, uint32_t watch_flags) { + Log *log = GetLog(POSIXLog::Watchpoints); + LLDB_LOG(log, "addr: {0:x}, size: {1:x} watch_flags: {2:x}", addr, size, + watch_flags); + + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + + if (error.Fail()) + return LLDB_INVALID_INDEX32; + + uint32_t control_value = 0, wp_index = 0; + lldb::addr_t real_addr = addr; + uint32_t rw_mode = 0; + + // Check if we are setting watchpoint other than read/write/access Update + // watchpoint flag to match ppc64le write-read bit configuration. + switch (watch_flags) { + case eWatchpointKindWrite: + //FIXME + //rw_mode = 0/*PPC_BREAKPOINT_TRIGGER_WRITE*/; + watch_flags = 2; + break; + // Watchpoint read not supported + case eWatchpointKindRead: + case (eWatchpointKindRead | eWatchpointKindWrite): + default: + return LLDB_INVALID_INDEX32; + } + + // Check if size has a valid hardware watchpoint length. + if (size != 8) + return LLDB_INVALID_INDEX32; + + // Check 8-byte alignment for hardware watchpoint target address. Below is a + // hack to recalculate address and size in order to make sure we can watch + // non 8-byte aligned addresses as well. + if (addr & 0x07) { + + addr_t begin = llvm::alignDown(addr, 8); + addr_t end = llvm::alignTo(addr + size, 8); + size = llvm::PowerOf2Ceil(end - begin); + + addr = addr & (~0x07); + } + + // Setup control value + control_value = watch_flags << 3; + control_value |= ((1 << size) - 1) << 5; + control_value |= (2 << 1) | 1; + + // Iterate over stored watchpoints and find a free wp_index + wp_index = LLDB_INVALID_INDEX32; + for (uint32_t i = 0; i < m_max_hwp_supported; i++) { + if ((m_hwp_regs[i].control & 1) == 0) { + wp_index = i; // Mark last free slot + } else if (m_hwp_regs[i].address == addr) { + return LLDB_INVALID_INDEX32; // We do not support duplicate watchpoints. + } + } + + if (wp_index == LLDB_INVALID_INDEX32) + return LLDB_INVALID_INDEX32; + + // Update watchpoint in local cache + m_hwp_regs[wp_index].real_addr = real_addr; + m_hwp_regs[wp_index].address = addr; + m_hwp_regs[wp_index].control = control_value; + //m_hwp_regs[wp_index].mode = rw_mode; + + // PTRACE call to set corresponding watchpoint register. + error = WriteHardwareDebugRegs(); + + if (error.Fail()) { + m_hwp_regs[wp_index].address = 0; + m_hwp_regs[wp_index].control &= llvm::maskTrailingZeros(1); + + return LLDB_INVALID_INDEX32; + } + + return wp_index; +} + +bool NativeRegisterContextAIX_ppc64::ClearHardwareWatchpoint( + uint32_t wp_index) { + Log *log = GetLog(POSIXLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + + if (error.Fail()) + return false; + + if (wp_index >= m_max_hwp_supported) + return false; + + // Create a backup we can revert to in case of failure. + lldb::addr_t tempAddr = m_hwp_regs[wp_index].address; + uint32_t tempControl = m_hwp_regs[wp_index].control; + long *tempSlot = reinterpret_cast(m_hwp_regs[wp_index].slot); + + // Update watchpoint in local cache + m_hwp_regs[wp_index].control &= llvm::maskTrailingZeros(1); + m_hwp_regs[wp_index].address = 0; + m_hwp_regs[wp_index].slot = 0; + m_hwp_regs[wp_index].mode = 0; + + // Ptrace call to update hardware debug registers + //FIXME + error = NativeProcessAIX::PtraceWrapper(PT_CLEAR/*PPC_PTRACE_DELHWDEBUG*/, + m_thread.GetID(), 0, tempSlot); + + if (error.Fail()) { + m_hwp_regs[wp_index].control = tempControl; + m_hwp_regs[wp_index].address = tempAddr; + m_hwp_regs[wp_index].slot = reinterpret_cast(tempSlot); + + return false; + } + + return true; +} + +uint32_t +NativeRegisterContextAIX_ppc64::GetWatchpointSize(uint32_t wp_index) { + Log *log = GetLog(POSIXLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + unsigned control = (m_hwp_regs[wp_index].control >> 5) & 0xff; + if (llvm::isPowerOf2_32(control + 1)) { + return llvm::popcount(control); + } + + return 0; +} + +bool NativeRegisterContextAIX_ppc64::WatchpointIsEnabled( + uint32_t wp_index) { + Log *log = GetLog(POSIXLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + return !!((m_hwp_regs[wp_index].control & 0x1) == 0x1); +} + +Status NativeRegisterContextAIX_ppc64::GetWatchpointHitIndex( + uint32_t &wp_index, lldb::addr_t trap_addr) { + Log *log = GetLog(POSIXLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}, trap_addr: {1:x}", wp_index, trap_addr); + + uint32_t watch_size; + lldb::addr_t watch_addr; + + for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) { + watch_size = GetWatchpointSize(wp_index); + watch_addr = m_hwp_regs[wp_index].address; + + if (WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr && + trap_addr <= watch_addr + watch_size) { + m_hwp_regs[wp_index].hit_addr = trap_addr; + return Status(); + } + } + + wp_index = LLDB_INVALID_INDEX32; + return Status(); +} + +lldb::addr_t +NativeRegisterContextAIX_ppc64::GetWatchpointAddress(uint32_t wp_index) { + Log *log = GetLog(POSIXLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + if (wp_index >= m_max_hwp_supported) + return LLDB_INVALID_ADDRESS; + + if (WatchpointIsEnabled(wp_index)) + return m_hwp_regs[wp_index].real_addr; + else + return LLDB_INVALID_ADDRESS; +} + +lldb::addr_t +NativeRegisterContextAIX_ppc64::GetWatchpointHitAddress(uint32_t wp_index) { + Log *log = GetLog(POSIXLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + if (wp_index >= m_max_hwp_supported) + return LLDB_INVALID_ADDRESS; + + if (WatchpointIsEnabled(wp_index)) + return m_hwp_regs[wp_index].hit_addr; + + return LLDB_INVALID_ADDRESS; +} + +Status NativeRegisterContextAIX_ppc64::ReadHardwareDebugInfo() { + if (!m_refresh_hwdebug_info) { + return Status(); + } + + m_max_hwp_supported = 1; + m_max_hbp_supported = 0; + m_refresh_hwdebug_info = false; + + return Status(); +} + +Status NativeRegisterContextAIX_ppc64::WriteHardwareDebugRegs() { + Status error; + long ret; + + for (uint32_t i = 0; i < m_max_hwp_supported; i++) { + if ((m_hwp_regs[i].control & 1) == 0) + continue; + + error = NativeProcessAIX::PtraceWrapper(PT_WATCH, m_thread.GetID(), (void *)m_hwp_regs[i].address, nullptr, 8, &ret); + + if (error.Fail()) + return error; + + m_hwp_regs[i].slot = ret; + } + return error; +} + +#endif // defined(__powerpc64__) diff --git a/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.h b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.h new file mode 100644 index 0000000000000..a29f786f2313a --- /dev/null +++ b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.h @@ -0,0 +1,138 @@ +//===-- NativeRegisterContextAIX_ppc64.h --------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// This implementation is related to the OpenPOWER ABI for Power Architecture +// 64-bit ELF V2 ABI + +#if defined(__powerpc64__) + +#ifndef lldb_NativeRegisterContextAIX_ppc64_h +#define lldb_NativeRegisterContextAIX_ppc64_h + +#include "Plugins/Process/AIX/NativeRegisterContextAIX.h" +#include "Plugins/Process/Utility/lldb-ppc64le-register-enums.h" + +#define DECLARE_REGISTER_INFOS_PPC64LE_STRUCT +#include "Plugins/Process/Utility/RegisterInfos_ppc64le.h" +#undef DECLARE_REGISTER_INFOS_PPC64LE_STRUCT + +namespace lldb_private { +namespace process_aix { + +class NativeProcessAIX; + +class NativeRegisterContextAIX_ppc64 : public NativeRegisterContextAIX { +public: + NativeRegisterContextAIX_ppc64(const ArchSpec &target_arch, + NativeThreadProtocol &native_thread); + + uint32_t GetRegisterSetCount() const override; + + uint32_t GetUserRegisterCount() const override; + + const RegisterSet *GetRegisterSet(uint32_t set_index) const override; + + Status ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) override; + + Status WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) override; + + Status ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override; + + Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + // Hardware watchpoint management functions + + uint32_t NumSupportedHardwareWatchpoints() override; + + uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags) override; + + bool ClearHardwareWatchpoint(uint32_t hw_index) override; + + Status GetWatchpointHitIndex(uint32_t &wp_index, + lldb::addr_t trap_addr) override; + + lldb::addr_t GetWatchpointHitAddress(uint32_t wp_index) override; + + lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override; + + uint32_t GetWatchpointSize(uint32_t wp_index); + + bool WatchpointIsEnabled(uint32_t wp_index); + +protected: + bool IsVMX(unsigned reg); + + bool IsVSX(unsigned reg); + + Status ReadVMX(); + + Status WriteVMX(); + + Status ReadVSX(); + + Status WriteVSX(); + + void *GetGPRBuffer() override { return &m_gpr_ppc64le; } + + void *GetFPRBuffer() override { return &m_fpr_ppc64le; } + + size_t GetFPRSize() override { return sizeof(m_fpr_ppc64le); } + +private: + GPR m_gpr_ppc64le; // 64-bit general purpose registers. + FPR m_fpr_ppc64le; // floating-point registers including extended register. + VMX m_vmx_ppc64le; // VMX registers. + VSX m_vsx_ppc64le; // Last lower bytes from first VSX registers. + + bool IsGPR(unsigned reg) const; + + bool IsFPR(unsigned reg) const; + + bool IsVMX(unsigned reg) const; + + bool IsVSX(unsigned reg) const; + + uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const; + + uint32_t CalculateVmxOffset(const RegisterInfo *reg_info) const; + + uint32_t CalculateVsxOffset(const RegisterInfo *reg_info) const; + + Status ReadHardwareDebugInfo(); + + Status WriteHardwareDebugRegs(); + + // Debug register info for hardware watchpoints management. + struct DREG { + lldb::addr_t address; // Breakpoint/watchpoint address value. + lldb::addr_t hit_addr; // Address at which last watchpoint trigger + // exception occurred. + lldb::addr_t real_addr; // Address value that should cause target to stop. + uint32_t control; // Breakpoint/watchpoint control value. + uint32_t refcount; // Serves as enable/disable and reference counter. + long slot; // Saves the value returned from PTRACE_SETHWDEBUG. + int mode; // Defines if watchpoint is read/write/access. + }; + + std::array m_hwp_regs; + + // 16 is just a maximum value, query hardware for actual watchpoint count + uint32_t m_max_hwp_supported = 16; + uint32_t m_max_hbp_supported = 16; + bool m_refresh_hwdebug_info = true; +}; + +} // namespace process_aix +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextAIX_ppc64_h + +#endif // defined(__powerpc64__) diff --git a/lldb/source/Plugins/Process/AIX/NativeThreadAIX.cpp b/lldb/source/Plugins/Process/AIX/NativeThreadAIX.cpp new file mode 100644 index 0000000000000..e07daccdff550 --- /dev/null +++ b/lldb/source/Plugins/Process/AIX/NativeThreadAIX.cpp @@ -0,0 +1,526 @@ +//===-- NativeThreadAIX.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 "NativeThreadAIX.h" + +#include +#include + +#include "NativeProcessAIX.h" +#include "NativeRegisterContextAIX.h" + +#include "lldb/Host/HostNativeThread.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" +#include "lldb/lldb-enumerations.h" + +#include "llvm/ADT/SmallString.h" + +#include "Plugins/Process/POSIX/CrashReason.h" + +#include +#include +#include + +#if 0 +#include +// Try to define a macro to encapsulate the tgkill syscall +#define tgkill(pid, tid, sig) \ + syscall(__NR_tgkill, static_cast<::pid_t>(pid), static_cast<::pid_t>(tid), \ + sig) +#endif + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_aix; + +namespace { +void LogThreadStopInfo(Log &log, const ThreadStopInfo &stop_info, + const char *const header) { + switch (stop_info.reason) { + case eStopReasonNone: + log.Printf("%s: %s no stop reason", __FUNCTION__, header); + return; + case eStopReasonTrace: + log.Printf("%s: %s trace, stopping signal 0x%" PRIx32, __FUNCTION__, header, + stop_info.signo); + return; + case eStopReasonBreakpoint: + log.Printf("%s: %s breakpoint, stopping signal 0x%" PRIx32, __FUNCTION__, + header, stop_info.signo); + return; + case eStopReasonWatchpoint: + log.Printf("%s: %s watchpoint, stopping signal 0x%" PRIx32, __FUNCTION__, + header, stop_info.signo); + return; + case eStopReasonSignal: + log.Printf("%s: %s signal 0x%02" PRIx32, __FUNCTION__, header, + stop_info.signo); + return; + case eStopReasonException: + log.Printf("%s: %s exception type 0x%02" PRIx64, __FUNCTION__, header, + stop_info.details.exception.type); + return; + case eStopReasonExec: + log.Printf("%s: %s exec, stopping signal 0x%" PRIx32, __FUNCTION__, header, + stop_info.signo); + return; + case eStopReasonPlanComplete: + log.Printf("%s: %s plan complete", __FUNCTION__, header); + return; + case eStopReasonThreadExiting: + log.Printf("%s: %s thread exiting", __FUNCTION__, header); + return; + case eStopReasonInstrumentation: + log.Printf("%s: %s instrumentation", __FUNCTION__, header); + return; + case eStopReasonProcessorTrace: + log.Printf("%s: %s processor trace", __FUNCTION__, header); + return; + default: + log.Printf("%s: %s invalid stop reason %" PRIu32, __FUNCTION__, header, + static_cast(stop_info.reason)); + } +} +} + +NativeThreadAIX::NativeThreadAIX(NativeProcessAIX &process, + lldb::tid_t tid) + : NativeThreadProtocol(process, tid), m_state(StateType::eStateInvalid), + m_stop_info(), + m_reg_context_up( + NativeRegisterContextAIX::CreateHostNativeRegisterContextAIX( + process.GetArchitecture(), *this)), + m_stop_description() {} + +std::string NativeThreadAIX::GetName() { + NativeProcessAIX &process = GetProcess(); + + auto BufferOrError = getProcFile(process.GetID(), GetID(), "comm"); + if (!BufferOrError) + return ""; + return std::string(BufferOrError.get()->getBuffer().rtrim('\n')); +} + +lldb::StateType NativeThreadAIX::GetState() { return m_state; } + +bool NativeThreadAIX::GetStopReason(ThreadStopInfo &stop_info, + std::string &description) { + Log *log = GetLog(LLDBLog::Thread); + + description.clear(); + + switch (m_state) { + case eStateStopped: + case eStateCrashed: + case eStateExited: + case eStateSuspended: + case eStateUnloaded: + if (log) + LogThreadStopInfo(*log, m_stop_info, "m_stop_info in thread:"); + stop_info = m_stop_info; + description = m_stop_description; + if (log) + LogThreadStopInfo(*log, stop_info, "returned stop_info:"); + + return true; + + case eStateInvalid: + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + case eStateDetached: + if (log) { + LLDB_LOGF(log, + "NativeThreadAIX::%s tid %" PRIu64 + " in state %s cannot answer stop reason", + __FUNCTION__, GetID(), StateAsCString(m_state)); + } + return false; + } + llvm_unreachable("unhandled StateType!"); +} + +Status NativeThreadAIX::SetWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags, bool hardware) { + if (!hardware) + return Status("not implemented"); + if (m_state == eStateLaunching) + return Status(); + Status error = RemoveWatchpoint(addr); + if (error.Fail()) + return error; + uint32_t wp_index = + m_reg_context_up->SetHardwareWatchpoint(addr, size, watch_flags); + if (wp_index == LLDB_INVALID_INDEX32) + return Status("Setting hardware watchpoint failed."); + m_watchpoint_index_map.insert({addr, wp_index}); + return Status(); +} + +Status NativeThreadAIX::RemoveWatchpoint(lldb::addr_t addr) { + auto wp = m_watchpoint_index_map.find(addr); + if (wp == m_watchpoint_index_map.end()) + return Status(); + uint32_t wp_index = wp->second; + m_watchpoint_index_map.erase(wp); + if (m_reg_context_up->ClearHardwareWatchpoint(wp_index)) + return Status(); + return Status("Clearing hardware watchpoint failed."); +} + +Status NativeThreadAIX::SetHardwareBreakpoint(lldb::addr_t addr, + size_t size) { + if (m_state == eStateLaunching) + return Status(); + + Status error = RemoveHardwareBreakpoint(addr); + if (error.Fail()) + return error; + + uint32_t bp_index = m_reg_context_up->SetHardwareBreakpoint(addr, size); + + if (bp_index == LLDB_INVALID_INDEX32) + return Status("Setting hardware breakpoint failed."); + + m_hw_break_index_map.insert({addr, bp_index}); + return Status(); +} + +Status NativeThreadAIX::RemoveHardwareBreakpoint(lldb::addr_t addr) { + auto bp = m_hw_break_index_map.find(addr); + if (bp == m_hw_break_index_map.end()) + return Status(); + + uint32_t bp_index = bp->second; + if (m_reg_context_up->ClearHardwareBreakpoint(bp_index)) { + m_hw_break_index_map.erase(bp); + return Status(); + } + + return Status("Clearing hardware breakpoint failed."); +} + +Status NativeThreadAIX::Resume(uint32_t signo) { + const StateType new_state = StateType::eStateRunning; + MaybeLogStateChange(new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonNone; + m_stop_description.clear(); + + // If watchpoints have been set, but none on this thread, then this is a new + // thread. So set all existing watchpoints. + if (m_watchpoint_index_map.empty()) { + NativeProcessAIX &process = GetProcess(); + + const auto &watchpoint_map = process.GetWatchpointMap(); + m_reg_context_up->ClearAllHardwareWatchpoints(); + for (const auto &pair : watchpoint_map) { + const auto &wp = pair.second; + SetWatchpoint(wp.m_addr, wp.m_size, wp.m_watch_flags, wp.m_hardware); + } + } + + // Set all active hardware breakpoint on all threads. + if (m_hw_break_index_map.empty()) { + NativeProcessAIX &process = GetProcess(); + + const auto &hw_breakpoint_map = process.GetHardwareBreakpointMap(); + m_reg_context_up->ClearAllHardwareBreakpoints(); + for (const auto &pair : hw_breakpoint_map) { + const auto &bp = pair.second; + SetHardwareBreakpoint(bp.m_addr, bp.m_size); + } + } + + intptr_t data = 0; + + if (signo != LLDB_INVALID_SIGNAL_NUMBER) + data = signo; + + return NativeProcessAIX::PtraceWrapper(PT_CONTINUE, GetID(), nullptr, + reinterpret_cast(data)); +} + +Status NativeThreadAIX::SingleStep(uint32_t signo) { + const StateType new_state = StateType::eStateStepping; + MaybeLogStateChange(new_state); + m_state = new_state; + m_stop_info.reason = StopReason::eStopReasonNone; + + intptr_t data = 0; + if (signo != LLDB_INVALID_SIGNAL_NUMBER) + data = signo; + + // If hardware single-stepping is not supported, we just do a continue. The + // breakpoint on the next instruction has been setup in + // NativeProcessAIX::Resume. + return NativeProcessAIX::PtraceWrapper( + GetProcess().SupportHardwareSingleStepping() ? PT_STEP : PT_CONTINUE, + m_tid, nullptr, reinterpret_cast(data)); +} + +void NativeThreadAIX::SetStoppedBySignal(uint32_t signo, + const siginfo_t *info) { + Log *log = GetLog(LLDBLog::Thread); + LLDB_LOGF(log, "NativeThreadAIX::%s called with signal 0x%02" PRIx32, + __FUNCTION__, signo); + + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonSignal; + m_stop_info.signo = signo; + + m_stop_description.clear(); + switch (signo) { + case SIGSEGV: + case SIGBUS: + case SIGFPE: + case SIGILL: + break; + } +} + +void NativeThreadAIX::AnnotateSyncTagCheckFault(const siginfo_t *info) { + int32_t allocation_tag_type = 0; + switch (GetProcess().GetArchitecture().GetMachine()) { + default: + return; + } + + auto details = + GetRegisterContext().GetMemoryTaggingDetails(allocation_tag_type); + if (!details) { + llvm::consumeError(details.takeError()); + return; + } + + // We assume that the stop description is currently: + // signal SIGSEGV: sync tag check fault (fault address: ) + // Remove the closing ) + m_stop_description.pop_back(); + + std::stringstream ss; + lldb::addr_t fault_addr = reinterpret_cast(info->si_addr); + std::unique_ptr manager(std::move(details->manager)); + + ss << " logical tag: 0x" << std::hex << manager->GetLogicalTag(fault_addr); + + std::vector allocation_tag_data; + // The fault address may not be granule aligned. ReadMemoryTags will granule + // align any range you give it, potentially making it larger. + // To prevent this set len to 1. This always results in a range that is at + // most 1 granule in size and includes fault_addr. + Status status = GetProcess().ReadMemoryTags(allocation_tag_type, fault_addr, + 1, allocation_tag_data); + + if (status.Success()) { + llvm::Expected> allocation_tag = + manager->UnpackTagsData(allocation_tag_data, 1); + if (allocation_tag) { + ss << " allocation tag: 0x" << std::hex << allocation_tag->front() << ")"; + } else { + llvm::consumeError(allocation_tag.takeError()); + ss << ")"; + } + } else + ss << ")"; + + m_stop_description += ss.str(); +} + +bool NativeThreadAIX::IsStopped(int *signo) { + if (!StateIsStoppedState(m_state, false)) + return false; + + // If we are stopped by a signal, return the signo. + if (signo && m_state == StateType::eStateStopped && + m_stop_info.reason == StopReason::eStopReasonSignal) { + *signo = m_stop_info.signo; + } + + // Regardless, we are stopped. + return true; +} + +void NativeThreadAIX::SetStopped() { + // On every stop, clear any cached register data structures + GetRegisterContext().InvalidateAllRegisters(); + + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange(new_state); + m_state = new_state; + m_stop_description.clear(); +} + +void NativeThreadAIX::SetStoppedByExec() { + Log *log = GetLog(LLDBLog::Thread); + LLDB_LOGF(log, "NativeThreadAIX::%s()", __FUNCTION__); + + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonExec; + m_stop_info.signo = SIGSTOP; +} + +void NativeThreadAIX::SetStoppedByBreakpoint() { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonBreakpoint; + m_stop_info.signo = SIGTRAP; + m_stop_description.clear(); +} + +void NativeThreadAIX::SetStoppedByWatchpoint(uint32_t wp_index) { + SetStopped(); + + lldbassert(wp_index != LLDB_INVALID_INDEX32 && "wp_index cannot be invalid"); + + std::ostringstream ostr; + ostr << m_reg_context_up->GetWatchpointAddress(wp_index) << " "; + ostr << wp_index; + + /* + * MIPS: Last 3bits of the watchpoint address are masked by the kernel. For + * example: + * 'n' is at 0x120010d00 and 'm' is 0x120010d04. When a watchpoint is set at + * 'm', then + * watch exception is generated even when 'n' is read/written. To handle this + * case, + * find the base address of the load/store instruction and append it in the + * stop-info + * packet. + */ + ostr << " " << m_reg_context_up->GetWatchpointHitAddress(wp_index); + + m_stop_description = ostr.str(); + + m_stop_info.reason = StopReason::eStopReasonWatchpoint; + m_stop_info.signo = SIGTRAP; +} + +bool NativeThreadAIX::IsStoppedAtBreakpoint() { + return GetState() == StateType::eStateStopped && + m_stop_info.reason == StopReason::eStopReasonBreakpoint; +} + +bool NativeThreadAIX::IsStoppedAtWatchpoint() { + return GetState() == StateType::eStateStopped && + m_stop_info.reason == StopReason::eStopReasonWatchpoint; +} + +void NativeThreadAIX::SetStoppedByTrace() { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonTrace; + m_stop_info.signo = SIGTRAP; +} + +void NativeThreadAIX::SetStoppedByFork(bool is_vfork, lldb::pid_t child_pid) { + SetStopped(); + + m_stop_info.reason = + is_vfork ? StopReason::eStopReasonVFork : StopReason::eStopReasonFork; + m_stop_info.details.fork.child_pid = child_pid; + m_stop_info.details.fork.child_tid = child_pid; +} + +void NativeThreadAIX::SetStoppedByVForkDone() { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonVForkDone; +} + +void NativeThreadAIX::SetStoppedWithNoReason() { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonNone; + m_stop_info.signo = 0; +} + +void NativeThreadAIX::SetStoppedByProcessorTrace( + llvm::StringRef description) { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonProcessorTrace; + m_stop_info.signo = 0; + m_stop_description = description.str(); +} + +void NativeThreadAIX::SetExited() { + const StateType new_state = StateType::eStateExited; + MaybeLogStateChange(new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonThreadExiting; +} + +Status NativeThreadAIX::RequestStop() { + Log *log = GetLog(LLDBLog::Thread); + + NativeProcessAIX &process = GetProcess(); + + lldb::pid_t pid = process.GetID(); + lldb::tid_t tid = GetID(); + + LLDB_LOGF(log, + "NativeThreadAIX::%s requesting thread stop(pid: %" PRIu64 + ", tid: %" PRIu64 ")", + __FUNCTION__, pid, tid); + + Status err; + errno = 0; + if (::kill(pid, SIGSTOP) != 0) { + err.SetErrorToErrno(); + LLDB_LOGF(log, + "NativeThreadAIX::%s kill(%" PRIu64 ", SIGSTOP) failed: %s", + __FUNCTION__, pid, err.AsCString()); + } + return err; +} + +void NativeThreadAIX::MaybeLogStateChange(lldb::StateType new_state) { + Log *log = GetLog(LLDBLog::Thread); + // If we're not logging, we're done. + if (!log) + return; + + // If this is a state change to the same state, we're done. + lldb::StateType old_state = m_state; + if (new_state == old_state) + return; + + LLDB_LOG(log, "pid={0}, tid={1}: changing from state {2} to {3}", + m_process.GetID(), GetID(), old_state, new_state); +} + +NativeProcessAIX &NativeThreadAIX::GetProcess() { + return static_cast(m_process); +} + +const NativeProcessAIX &NativeThreadAIX::GetProcess() const { + return static_cast(m_process); +} + +llvm::Expected> +NativeThreadAIX::GetSiginfo() const { + auto siginfo_buf = + llvm::WritableMemoryBuffer::getNewUninitMemBuffer(sizeof(siginfo_t)); +#if 0 + Status error = + GetProcess().GetSignalInfo(GetID(), siginfo_buf->getBufferStart()); + if (!error.Success()) + return error.ToError(); +#endif + return std::move(siginfo_buf); +} diff --git a/lldb/source/Plugins/Process/AIX/NativeThreadAIX.h b/lldb/source/Plugins/Process/AIX/NativeThreadAIX.h new file mode 100644 index 0000000000000..706a7ce69da8e --- /dev/null +++ b/lldb/source/Plugins/Process/AIX/NativeThreadAIX.h @@ -0,0 +1,126 @@ +//===-- NativeThreadAIX.h ----------------------------------- -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_NativeThreadAIX_H_ +#define liblldb_NativeThreadAIX_H_ + +#include "Plugins/Process/AIX/NativeRegisterContextAIX.h" +#include "lldb/Host/common/NativeThreadProtocol.h" +#include "lldb/lldb-private-forward.h" + +#include "llvm/ADT/StringRef.h" + +#include +#include +#include +#include + +namespace lldb_private { +namespace process_aix { + +class NativeProcessAIX; + +class NativeThreadAIX : public NativeThreadProtocol { + friend class NativeProcessAIX; + +public: + NativeThreadAIX(NativeProcessAIX &process, lldb::tid_t tid); + + // NativeThreadProtocol Interface + std::string GetName() override; + + lldb::StateType GetState() override; + + bool GetStopReason(ThreadStopInfo &stop_info, + std::string &description) override; + + NativeRegisterContextAIX &GetRegisterContext() override { + return *m_reg_context_up; + } + + Status SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags, + bool hardware) override; + + Status RemoveWatchpoint(lldb::addr_t addr) override; + + Status SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; + + Status RemoveHardwareBreakpoint(lldb::addr_t addr) override; + + NativeProcessAIX &GetProcess(); + + const NativeProcessAIX &GetProcess() const; + + llvm::Expected> + GetSiginfo() const override; + +private: + // Interface for friend classes + + /// Resumes the thread. If \p signo is anything but + /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. + Status Resume(uint32_t signo); + + /// Single steps the thread. If \p signo is anything but + /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. + Status SingleStep(uint32_t signo); + + void SetStoppedBySignal(uint32_t signo, const siginfo_t *info = nullptr); + + /// Return true if the thread is stopped. + /// If stopped by a signal, indicate the signo in the signo argument. + /// Otherwise, return LLDB_INVALID_SIGNAL_NUMBER. + bool IsStopped(int *signo); + + void SetStoppedByExec(); + + void SetStoppedByBreakpoint(); + + void SetStoppedByWatchpoint(uint32_t wp_index); + + bool IsStoppedAtBreakpoint(); + + bool IsStoppedAtWatchpoint(); + + void SetStoppedByTrace(); + + void SetStoppedByFork(bool is_vfork, lldb::pid_t child_pid); + + void SetStoppedByVForkDone(); + + void SetStoppedWithNoReason(); + + void SetStoppedByProcessorTrace(llvm::StringRef description); + + void SetExited(); + + Status RequestStop(); + + // Private interface + void MaybeLogStateChange(lldb::StateType new_state); + + void SetStopped(); + + /// Extend m_stop_description with logical and allocation tag values. + /// If there is an error along the way just add the information we were able + /// to get. + void AnnotateSyncTagCheckFault(const siginfo_t *info); + + // Member Variables + lldb::StateType m_state; + ThreadStopInfo m_stop_info; + std::unique_ptr m_reg_context_up; + std::string m_stop_description; + using WatchpointIndexMap = std::map; + WatchpointIndexMap m_watchpoint_index_map; + WatchpointIndexMap m_hw_break_index_map; +}; +} // namespace process_aix +} // namespace lldb_private + +#endif // #ifndef liblldb_NativeThreadAIX_H_ diff --git a/lldb/source/Plugins/Process/CMakeLists.txt b/lldb/source/Plugins/Process/CMakeLists.txt index a51d0f7afd175..01bb5f462eba4 100644 --- a/lldb/source/Plugins/Process/CMakeLists.txt +++ b/lldb/source/Plugins/Process/CMakeLists.txt @@ -7,6 +7,9 @@ elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") elseif (CMAKE_SYSTEM_NAME MATCHES "NetBSD") add_subdirectory(NetBSD) add_subdirectory(POSIX) +elseif (CMAKE_SYSTEM_NAME MATCHES "AIX") + add_subdirectory(AIX) + add_subdirectory(POSIX) elseif (CMAKE_SYSTEM_NAME MATCHES "Windows") add_subdirectory(Windows/Common) elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin") diff --git a/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp b/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp index 32c71d87c7f58..db271357d792a 100644 --- a/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp +++ b/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp @@ -46,8 +46,34 @@ bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr, function_options.include_inlines = false; SymbolContextList sc_list; +#if !defined(__AIX__) process->GetTarget().GetImages().FindFunctions( ConstString("mmap"), eFunctionNameTypeFull, function_options, sc_list); +#else + process->GetTarget().GetImages().FindFunctions( + ConstString("mmap64"), eFunctionNameTypeFull, function_options, sc_list); + SymbolContextList toc_list; + process->GetTarget().GetImages().FindSymbolsWithNameAndType( + ConstString("TOC"), lldb::eSymbolTypeAny, toc_list); + + AddressRange toc_range; + if (sc_list.GetSize() > 0) { + SymbolContext sc; + if (sc_list.GetContextAtIndex(0, sc)) { + for (int i = 0; i < toc_list.GetSize(); ++i) { + SymbolContext tocSC; + if (toc_list.GetContextAtIndex(i, tocSC)) { + if (tocSC.module_sp == sc.module_sp) { + if (tocSC.GetAddressRange(eSymbolContextSymbol, 0, false, + toc_range)) { + break; + } + } + } + } + } + } +#endif const uint32_t count = sc_list.GetSize(); if (count > 0) { SymbolContext sc; @@ -96,9 +122,16 @@ bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr, MmapArgList args = process->GetTarget().GetPlatform()->GetMmapArgumentList( arch, addr, length, prot_arg, flags, fd, offset); +#if defined(__AIX__) + lldb::ThreadPlanSP call_plan_sp( + new ThreadPlanCallFunction(*thread, mmap_range.GetBaseAddress(), + toc_range.GetBaseAddress(), + void_ptr_type, args, options)); +#else lldb::ThreadPlanSP call_plan_sp( new ThreadPlanCallFunction(*thread, mmap_range.GetBaseAddress(), void_ptr_type, args, options)); +#endif if (call_plan_sp) { DiagnosticManager diagnostics; diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.cpp b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.cpp index 159fd2856443c..d9b41d595147f 100644 --- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.cpp +++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.cpp @@ -23,6 +23,8 @@ static const lldb_private::RegisterInfo * GetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) { switch (target_arch.GetMachine()) { + //HH + case llvm::Triple::ppc64: case llvm::Triple::ppc64le: return g_register_infos_ppc64le; default: @@ -34,6 +36,8 @@ GetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) { static uint32_t GetRegisterInfoCount(const lldb_private::ArchSpec &target_arch) { switch (target_arch.GetMachine()) { + //HitchHike + case llvm::Triple::ppc64: case llvm::Triple::ppc64le: return static_cast(sizeof(g_register_infos_ppc64le) / sizeof(g_register_infos_ppc64le[0])); diff --git a/lldb/source/Plugins/Process/Utility/ThreadMemory.cpp b/lldb/source/Plugins/Process/Utility/ThreadMemory.cpp index 89ecc757a68f5..550b53688fd39 100644 --- a/lldb/source/Plugins/Process/Utility/ThreadMemory.cpp +++ b/lldb/source/Plugins/Process/Utility/ThreadMemory.cpp @@ -20,7 +20,7 @@ using namespace lldb; using namespace lldb_private; -ThreadMemory::ThreadMemory(Process &process, tid_t tid, +ThreadMemory::ThreadMemory(Process &process, lldb::tid_t tid, const ValueObjectSP &thread_info_valobj_sp) : Thread(process, tid), m_backing_thread_sp(), m_thread_info_valobj_sp(thread_info_valobj_sp), m_name(), m_queue(), diff --git a/lldb/source/Plugins/Process/gdb-remote/CMakeLists.txt b/lldb/source/Plugins/Process/gdb-remote/CMakeLists.txt index 6755999b18185..4eddbb5ec4cfd 100644 --- a/lldb/source/Plugins/Process/gdb-remote/CMakeLists.txt +++ b/lldb/source/Plugins/Process/gdb-remote/CMakeLists.txt @@ -6,6 +6,11 @@ lldb_tablegen(ProcessGDBRemotePropertiesEnum.inc -gen-lldb-property-enum-defs SOURCE ProcessGDBRemoteProperties.td TARGET LLDBPluginProcessGDBRemotePropertiesEnumGen) +if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX") + remove_definitions("-D_XOPEN_SOURCE=700") + add_definitions("-D_ALL_SOURCE") +endif() + set(LLDB_PLUGINS lldbPluginProcessUtility ) diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index 74e392249a94e..b7ecf7a5dc328 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -40,6 +40,10 @@ #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/JSON.h" +#if defined(__AIX__) +#include +#endif + #if defined(HAVE_LIBCOMPRESSION) #include #endif @@ -1710,6 +1714,32 @@ Status GDBRemoteCommunicationClient::GetMemoryRegionInfo( return error; } +#if defined(__AIX__) +Status GDBRemoteCommunicationClient::GetLDXINFO(struct ld_xinfo *info_ptr) +{ + Status error; + + char packet[64]; + const int packet_len = ::snprintf(packet, sizeof(packet), "qLDXINFO"); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, response) == + PacketResult::Success && + response.GetResponseType() == StringExtractorGDBRemote::eResponse) { + llvm::MutableArrayRef infoData((uint8_t *)info_ptr, sizeof(struct ld_xinfo)*64); + size_t got_bytes = response.GetHexBytesAvail(infoData); + if (got_bytes != sizeof(struct ld_xinfo)*64) { + error.SetErrorString("qLDXINFO ret bad size"); + return error; + } + } else { + error.SetErrorString("qLDXINFO is not supported"); + } + return error; +} +#endif + Status GDBRemoteCommunicationClient::GetQXferMemoryMapRegionInfo( lldb::addr_t addr, MemoryRegionInfo ®ion) { Status error = LoadQXferMemoryMap(); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h index 898d176abc346..520f37ac56716 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -32,6 +32,10 @@ #include "llvm/Support/VersionTuple.h" +#if defined(__AIX__) +struct ld_xinfo; +#endif + namespace lldb_private { namespace process_gdb_remote { @@ -196,6 +200,9 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase { Status GetMemoryRegionInfo(lldb::addr_t addr, MemoryRegionInfo &range_info); std::optional GetWatchpointSlotCount(); +#if defined(__AIX__) + Status GetLDXINFO(struct ld_xinfo *info_ptr); +#endif std::optional GetWatchpointReportedAfter(); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp index a0b08a219ae14..f019062986925 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -48,6 +48,9 @@ #include "ProcessGDBRemote.h" #include "ProcessGDBRemoteLog.h" #include "lldb/Utility/StringExtractorGDBRemote.h" +#if defined(__AIX__) +#include +#endif using namespace lldb; using namespace lldb_private; @@ -193,6 +196,8 @@ void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() { &GDBRemoteCommunicationServerLLGS::Handle_Z); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_z, &GDBRemoteCommunicationServerLLGS::Handle_z); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qLDXINFO, + &GDBRemoteCommunicationServerLLGS::Handle_qLDXINFO); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_QPassSignals, &GDBRemoteCommunicationServerLLGS::Handle_QPassSignals); @@ -3006,6 +3011,29 @@ GDBRemoteCommunicationServerLLGS::Handle_z(StringExtractorGDBRemote &packet) { } } +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qLDXINFO(StringExtractorGDBRemote &packet) { + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) { + Log *log = GetLog(LLDBLog::Process); + LLDB_LOG(log, "qLDXINFO failed, no process available"); + return SendErrorResponse(0xff); + } + +#if defined(__AIX__) + // FIXME: buffer size + struct ld_xinfo info[64]; + if (ptrace64(PT_LDXINFO, m_current_process->GetID(), (long long)&(info[0]), sizeof(info), nullptr) != 0) { + return SendErrorResponse(0xff); + } + StreamGDBRemote response; + response.PutBytesAsRawHex8(&(info[0]), sizeof(info)); + return SendPacketNoLock(response.GetString()); +#else + return SendErrorResponse(0xff); +#endif +} + GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_s(StringExtractorGDBRemote &packet) { Log *log = GetLog(LLDBLog::Process | LLDBLog::Thread); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h index 646b6a102abf6..a464479e178de 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h @@ -211,6 +211,8 @@ class GDBRemoteCommunicationServerLLGS PacketResult Handle_z(StringExtractorGDBRemote &packet); + PacketResult Handle_qLDXINFO(StringExtractorGDBRemote &packet); + PacketResult Handle_s(StringExtractorGDBRemote &packet); PacketResult Handle_qXfer(StringExtractorGDBRemote &packet); diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index 6f9c2cc1e4b4e..10fbaa2b3c837 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -92,6 +92,10 @@ #include "llvm/Support/Threading.h" #include "llvm/Support/raw_ostream.h" +#if defined(__AIX__) +#include +#endif + #define DEBUGSERVER_BASENAME "debugserver" using namespace lldb; using namespace lldb_private; @@ -1513,7 +1517,7 @@ bool ProcessGDBRemote::DoUpdateThreadList(ThreadList &old_thread_list, ThreadList old_thread_list_copy(old_thread_list); if (num_thread_ids > 0) { for (size_t i = 0; i < num_thread_ids; ++i) { - tid_t tid = m_thread_ids[i]; + lldb::tid_t tid = m_thread_ids[i]; ThreadSP thread_sp( old_thread_list_copy.RemoveThreadByProtocolID(tid, false)); if (!thread_sp) { @@ -2945,6 +2949,13 @@ Status ProcessGDBRemote::DoGetMemoryRegionInfo(addr_t load_addr, return error; } +#if defined(__AIX__) +Status ProcessGDBRemote::DoGetLDXINFO(struct ld_xinfo *info_ptr) { + Status error(m_gdb_comm.GetLDXINFO(info_ptr)); + return error; +} +#endif + std::optional ProcessGDBRemote::GetWatchpointSlotCount() { return m_gdb_comm.GetWatchpointSlotCount(); } diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index 2492795851388..82200fbea21cd 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -37,6 +37,10 @@ #include "GDBRemoteCommunicationClient.h" #include "GDBRemoteRegisterContext.h" +#if defined(__AIX__) +struct ld_xinfo; +#endif + #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringMap.h" @@ -423,6 +427,10 @@ class ProcessGDBRemote : public Process, Status DoGetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo ®ion_info) override; +#if defined(__AIX__) + Status DoGetLDXINFO(struct ld_xinfo *info_ptr) override; +#endif + private: // For ProcessGDBRemote only std::string m_partial_profile_data; diff --git a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp index 1da7696c9a352..930c707604bb3 100644 --- a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp +++ b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp @@ -593,19 +593,19 @@ bool ProcessMachCore::DoUpdateThreadList(ThreadList &old_thread_list, ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); if (core_objfile) { - std::set used_tids; + std::set used_tids; const uint32_t num_threads = core_objfile->GetNumThreadContexts(); - std::vector tids; + std::vector tids; if (core_objfile->GetCorefileThreadExtraInfos(tids)) { assert(tids.size() == num_threads); // Find highest tid value. - tid_t highest_tid = 0; + lldb::tid_t highest_tid = 0; for (uint32_t i = 0; i < num_threads; i++) { if (tids[i] != LLDB_INVALID_THREAD_ID && tids[i] > highest_tid) highest_tid = tids[i]; } - tid_t current_unused_tid = highest_tid + 1; + lldb::tid_t current_unused_tid = highest_tid + 1; for (uint32_t i = 0; i < num_threads; i++) { if (tids[i] == LLDB_INVALID_THREAD_ID) { tids[i] = current_unused_tid++; diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt b/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt index 7523d65abf0f8..1ce60a0b66154 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt +++ b/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt @@ -20,6 +20,11 @@ if (LLDB_ENABLE_LIBEDIT) endif() add_subdirectory(Interfaces) +if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX") + remove_definitions("-D_XOPEN_SOURCE=700") + add_definitions("-D_ALL_SOURCE") +endif() + add_lldb_library(lldbPluginScriptInterpreterPython PLUGIN PythonDataObjects.cpp diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp index e1f73f1997e36..92882cfc3da31 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp @@ -500,6 +500,8 @@ dw_addr_t DWARFFormValue::Address() const { &offset, index_size); } +bool UGLY_FLAG_FOR_AIX __attribute__((weak)) = false; + std::pair DWARFFormValue::ReferencedUnitAndOffset() const { uint64_t value = m_value.value.uval; @@ -512,6 +514,8 @@ DWARFFormValue::ReferencedUnitAndOffset() const { assert(m_unit); // Unit must be valid for DW_FORM_ref forms that are compile // unit relative or we will get this wrong value += m_unit->GetOffset(); + if (UGLY_FLAG_FOR_AIX) + value -= 8; if (!m_unit->ContainsDIEOffset(value)) { m_unit->GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError( "DW_FORM_ref* DIE reference {0:x16} is outside of its CU", value); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp index 66a762bf9b685..6721c1895a576 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp @@ -924,6 +924,12 @@ const DWARFDebugAranges &DWARFUnit::GetFunctionAranges() { return *m_func_aranges_up; } +/* AIX-NOTE - TODO: Removed conflicting code due to merge conflicts + * Refer Patches: 27,28,29,30,35 and 76 + * and modify the code accordingly. */ + +bool UGLY_FLAG_FOR_AIX __attribute__((weak)) = false; + llvm::Expected DWARFUnit::extract(SymbolFileDWARF &dwarf, user_id_t uid, const DWARFDataExtractor &debug_info, @@ -1002,6 +1008,10 @@ const lldb_private::DWARFDataExtractor &DWARFUnit::GetData() const { uint32_t DWARFUnit::GetHeaderByteSize() const { switch (m_header.getUnitType()) { case llvm::dwarf::DW_UT_compile: + if (UGLY_FLAG_FOR_AIX) + return 11 + 4/*GetDWARFSizeOfOffset*/; + else + return GetVersion() < 5 ? 11 : 12; case llvm::dwarf::DW_UT_partial: return GetVersion() < 5 ? 11 : 12; case llvm::dwarf::DW_UT_skeleton: @@ -1016,7 +1026,7 @@ uint32_t DWARFUnit::GetHeaderByteSize() const { std::optional DWARFUnit::GetStringOffsetSectionItem(uint32_t index) const { - offset_t offset = GetStrOffsetsBase() + index * 4; + lldb::offset_t offset = GetStrOffsetsBase() + index * 4; return m_dwarf.GetDWARFContext().getOrLoadStrOffsetsData().GetU32(&offset); } diff --git a/lldb/source/Plugins/SystemRuntime/MacOSX/AppleGetThreadItemInfoHandler.cpp b/lldb/source/Plugins/SystemRuntime/MacOSX/AppleGetThreadItemInfoHandler.cpp index 2064b73dc3ea5..824528fc3acfa 100644 --- a/lldb/source/Plugins/SystemRuntime/MacOSX/AppleGetThreadItemInfoHandler.cpp +++ b/lldb/source/Plugins/SystemRuntime/MacOSX/AppleGetThreadItemInfoHandler.cpp @@ -216,7 +216,7 @@ lldb::addr_t AppleGetThreadItemInfoHandler::SetupGetThreadItemInfoFunction( AppleGetThreadItemInfoHandler::GetThreadItemInfoReturnInfo AppleGetThreadItemInfoHandler::GetThreadItemInfo(Thread &thread, - tid_t thread_id, + lldb::tid_t thread_id, addr_t page_to_free, uint64_t page_to_free_size, Status &error) { diff --git a/lldb/source/Symbol/DWARFCallFrameInfo.cpp b/lldb/source/Symbol/DWARFCallFrameInfo.cpp index f3df8a2c27f5a..de244e372579d 100644 --- a/lldb/source/Symbol/DWARFCallFrameInfo.cpp +++ b/lldb/source/Symbol/DWARFCallFrameInfo.cpp @@ -33,7 +33,7 @@ using namespace lldb_private::dwarf; // Used for calls when the value type is specified by a DWARF EH Frame pointer // encoding. static uint64_t -GetGNUEHPointer(const DataExtractor &DE, offset_t *offset_ptr, +GetGNUEHPointer(const DataExtractor &DE, lldb::offset_t *offset_ptr, uint32_t eh_ptr_enc, addr_t pc_rel_addr, addr_t text_addr, addr_t data_addr) //, BSDRelocs *data_relocs) const { @@ -588,7 +588,7 @@ bool DWARFCallFrameInfo::FDEToUnwindPlan(dw_offset_t dwarf_offset, if (cie->augmentation[0] == 'z') { uint32_t aug_data_len = (uint32_t)m_cfi_data.GetULEB128(&offset); if (aug_data_len != 0 && cie->lsda_addr_encoding != DW_EH_PE_omit) { - offset_t saved_offset = offset; + lldb::offset_t saved_offset = offset; lsda_data_file_address = GetGNUEHPointer(m_cfi_data, &offset, cie->lsda_addr_encoding, pc_rel_addr, text_addr, data_addr); diff --git a/lldb/source/Target/ABI.cpp b/lldb/source/Target/ABI.cpp index 110b5c86fc425..6df03533cda29 100644 --- a/lldb/source/Target/ABI.cpp +++ b/lldb/source/Target/ABI.cpp @@ -208,6 +208,15 @@ bool ABI::PrepareTrivialCall(Thread &thread, lldb::addr_t sp, llvm_unreachable("Should never get here!"); } +bool ABI::PrepareTrivialCall(Thread &thread, lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t tocAddress, + lldb::addr_t returnAddress, + llvm::ArrayRef args) const { + // dummy prepare trivial call + llvm_unreachable("Should never get here!"); +} + bool ABI::GetFallbackRegisterLocation( const RegisterInfo *reg_info, UnwindPlan::Row::RegisterLocation &unwind_regloc) { diff --git a/lldb/source/Target/CMakeLists.txt b/lldb/source/Target/CMakeLists.txt index a42c44b761dc5..833489b16dfd7 100644 --- a/lldb/source/Target/CMakeLists.txt +++ b/lldb/source/Target/CMakeLists.txt @@ -1,3 +1,8 @@ +if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX") + remove_definitions("-D_XOPEN_SOURCE=700") + add_definitions("-D_ALL_SOURCE") +endif() + lldb_tablegen(TargetProperties.inc -gen-lldb-property-defs SOURCE TargetProperties.td TARGET LLDBTargetPropertiesGen) diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index e3c4f2ee398cc..e31245178b2f2 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -75,6 +75,10 @@ #include "lldb/Utility/State.h" #include "lldb/Utility/Timer.h" +#if defined(__AIX__) +#include +#endif + using namespace lldb; using namespace lldb_private; using namespace std::chrono; @@ -6206,6 +6210,12 @@ Status Process::GetMemoryRegionInfo(lldb::addr_t load_addr, return DoGetMemoryRegionInfo(load_addr, range_info); } +#if defined(__AIX__) +Status Process::GetLDXINFO(struct ld_xinfo *info_ptr) { + return DoGetLDXINFO(info_ptr); +} +#endif + Status Process::GetMemoryRegions(lldb_private::MemoryRegionInfos ®ion_list) { Status error; diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index a61228d092d89..57f42ea56cb18 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -40,6 +40,9 @@ #include #include +#ifdef __AIX__ +#include "Plugins/Process/Utility/lldb-ppc64le-register-enums.h" +#endif using namespace lldb; using namespace lldb_private; @@ -1257,6 +1260,10 @@ bool RegisterContextUnwind::IsTrapHandlerSymbol( // Answer the question: Where did THIS frame save the CALLER frame ("previous" // frame)'s register value? +#ifdef __AIX__ +extern bool UGLY_HACK_NULL_TOPFRAME; +#endif + enum UnwindLLDB::RegisterSearchResult RegisterContextUnwind::SavedLocationForRegister( uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation ®loc) { @@ -1517,6 +1524,11 @@ RegisterContextUnwind::SavedLocationForRegister( new_regloc.type = UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext; new_regloc.location.register_number = regnum.GetAsKind(eRegisterKindLLDB); +#ifdef __AIX__ + if (UGLY_HACK_NULL_TOPFRAME && new_regloc.location.register_number == 0x20) { + new_regloc.location.register_number = 0x24; + } +#endif m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = new_regloc; regloc = new_regloc; UnwindLogMsg("supplying caller's register %s (%d) from the live " @@ -2368,6 +2380,40 @@ bool RegisterContextUnwind::ReadPC(addr_t &pc) { } } +#ifdef __AIX__ +bool RegisterContextUnwind::ReadLR(addr_t &lr) { + if (!IsValid()) + return false; + + bool above_trap_handler = false; + if (GetNextFrame().get() && GetNextFrame()->IsValid() && + GetNextFrame()->IsTrapHandlerFrame()) + above_trap_handler = true; + + if (ReadGPRValue(eRegisterKindLLDB, gpr_lr_ppc64le, lr)) { + // A lr value of 0 or 1 is impossible in the middle of the stack -- it + // indicates the end of a stack walk. + // On the currently executing frame (or such a frame interrupted + // asynchronously by sigtramp et al) this may occur if code has jumped + // through a NULL pointer -- we want to be able to unwind past that frame + // to help find the bug. + + ProcessSP process_sp (m_thread.GetProcess()); + if (process_sp) + { + ABI *abi = process_sp->GetABI().get(); + if (abi) + lr = abi->FixCodeAddress(lr); + } + + return !(m_all_registers_available == false && + above_trap_handler == false && (lr == 0 || lr == 1)); + } else { + return false; + } +} +#endif + void RegisterContextUnwind::UnwindLogMsg(const char *fmt, ...) { Log *log = GetLog(LLDBLog::Unwind); if (!log) diff --git a/lldb/source/Target/ThreadPlanCallFunction.cpp b/lldb/source/Target/ThreadPlanCallFunction.cpp index 50dcb66b9719f..0926579ea2930 100644 --- a/lldb/source/Target/ThreadPlanCallFunction.cpp +++ b/lldb/source/Target/ThreadPlanCallFunction.cpp @@ -127,6 +127,40 @@ ThreadPlanCallFunction::ThreadPlanCallFunction( m_valid = true; } +ThreadPlanCallFunction::ThreadPlanCallFunction( + Thread &thread, const Address &function, const Address &toc, + const CompilerType &return_type, + llvm::ArrayRef args, const EvaluateExpressionOptions &options) + : ThreadPlan(ThreadPlan::eKindCallFunction, "Call function plan", thread, + eVoteNoOpinion, eVoteNoOpinion), + m_valid(false), m_stop_other_threads(options.GetStopOthers()), + m_unwind_on_error(options.DoesUnwindOnError()), + m_ignore_breakpoints(options.DoesIgnoreBreakpoints()), + m_debug_execution(options.GetDebug()), + m_trap_exceptions(options.GetTrapExceptions()), m_function_addr(function), + m_function_sp(0), m_takedown_done(false), + m_should_clear_objc_exception_bp(false), + m_should_clear_cxx_exception_bp(false), + m_stop_address(LLDB_INVALID_ADDRESS), m_return_type(return_type) { + lldb::addr_t start_load_addr = LLDB_INVALID_ADDRESS; + lldb::addr_t function_load_addr = LLDB_INVALID_ADDRESS; + lldb::addr_t toc_addr = LLDB_INVALID_ADDRESS; + ABI *abi = nullptr; + + if (!ConstructorSetup(thread, abi, start_load_addr, function_load_addr)) + return; + + toc_addr = toc.GetLoadAddress(&GetTarget()); + + if (!abi->PrepareTrivialCall(thread, m_function_sp, function_load_addr, + toc_addr, start_load_addr, args)) + return; + + ReportRegisterState("Function call was set up. Register state was:"); + + m_valid = true; +} + ThreadPlanCallFunction::ThreadPlanCallFunction( Thread &thread, const Address &function, const EvaluateExpressionOptions &options) diff --git a/lldb/source/Target/UnwindLLDB.cpp b/lldb/source/Target/UnwindLLDB.cpp index f43e940492b09..255b829738ba2 100644 --- a/lldb/source/Target/UnwindLLDB.cpp +++ b/lldb/source/Target/UnwindLLDB.cpp @@ -68,6 +68,10 @@ uint32_t UnwindLLDB::DoGetFrameCount() { return m_frames.size(); } +#ifdef __AIX__ +bool UGLY_HACK_NULL_TOPFRAME = false; +#endif + bool UnwindLLDB::AddFirstFrame() { if (m_frames.size() > 0) return true; @@ -91,6 +95,17 @@ bool UnwindLLDB::AddFirstFrame() { if (!reg_ctx_sp->ReadPC(first_cursor_sp->start_pc)) goto unwind_done; +#ifdef __AIX__ + lldb::addr_t lr; + if (!reg_ctx_sp->ReadLR(lr)) + goto unwind_done; + + if (first_cursor_sp->start_pc == 0) { + first_cursor_sp->start_pc = lr; + UGLY_HACK_NULL_TOPFRAME = true; + } +#endif + // Everything checks out, so release the auto pointer value and let the // cursor own it in its shared pointer first_cursor_sp->reg_ctx_lldb_sp = reg_ctx_sp; diff --git a/lldb/source/Utility/ArchSpec.cpp b/lldb/source/Utility/ArchSpec.cpp index 07ef435ef451d..3868f77169cc6 100644 --- a/lldb/source/Utility/ArchSpec.cpp +++ b/lldb/source/Utility/ArchSpec.cpp @@ -14,6 +14,7 @@ #include "lldb/lldb-defines.h" #include "llvm/ADT/STLExtras.h" #include "llvm/BinaryFormat/COFF.h" +#include "llvm/BinaryFormat/XCOFF.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/Support/Compiler.h" @@ -459,10 +460,22 @@ static const ArchDefinition g_coff_arch_def = { "pe-coff", }; +static const ArchDefinitionEntry g_xcoff_arch_entries[] = { + {ArchSpec::eCore_ppc_generic, llvm::XCOFF::TCPU_COM, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, + {ArchSpec::eCore_ppc64_generic, llvm::XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu} +}; + +static const ArchDefinition g_xcoff_arch_def = { + eArchTypeXCOFF, + std::size(g_xcoff_arch_entries), + g_xcoff_arch_entries, + "xcoff", +}; + //===----------------------------------------------------------------------===// // Table of all ArchDefinitions static const ArchDefinition *g_arch_definitions[] = { - &g_macho_arch_def, &g_elf_arch_def, &g_coff_arch_def}; + &g_macho_arch_def, &g_elf_arch_def, &g_coff_arch_def, &g_xcoff_arch_def}; //===----------------------------------------------------------------------===// // Static helper functions. @@ -903,6 +916,9 @@ bool ArchSpec::SetArchitecture(ArchitectureType arch_type, uint32_t cpu, } else if (arch_type == eArchTypeCOFF && os == llvm::Triple::Win32) { m_triple.setVendor(llvm::Triple::PC); m_triple.setOS(llvm::Triple::Win32); + } else if (arch_type == eArchTypeXCOFF && os == llvm::Triple::AIX) { + m_triple.setVendor(llvm::Triple::IBM); + m_triple.setOS(llvm::Triple::AIX); } else { m_triple.setVendor(llvm::Triple::UnknownVendor); m_triple.setOS(llvm::Triple::UnknownOS); diff --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp b/lldb/source/Utility/StringExtractorGDBRemote.cpp index 9f79d2271b1e6..dbd3236536f8c 100644 --- a/lldb/source/Utility/StringExtractorGDBRemote.cpp +++ b/lldb/source/Utility/StringExtractorGDBRemote.cpp @@ -227,6 +227,8 @@ StringExtractorGDBRemote::GetServerPacketType() const { return eServerPacketType_qLaunchGDBServer; if (PACKET_MATCHES("qLaunchSuccess")) return eServerPacketType_qLaunchSuccess; + if (PACKET_MATCHES("qLDXINFO")) + return eServerPacketType_qLDXINFO; break; case 'M': diff --git a/lldb/test/CMakeLists.txt b/lldb/test/CMakeLists.txt index 5ac474736eb63..413a1e5120288 100644 --- a/lldb/test/CMakeLists.txt +++ b/lldb/test/CMakeLists.txt @@ -155,7 +155,7 @@ if(TARGET clang) add_lldb_test_dependency(clang) # TestFullLtoStepping depends on LTO, and only runs when the compiler is clang. - add_lldb_test_dependency(LTO) + #add_lldb_test_dependency(LTO) if (TARGET libcxx OR ("libcxx" IN_LIST LLVM_ENABLE_RUNTIMES)) set(LLDB_HAS_LIBCXX ON) diff --git a/lldb/test/Shell/Expr/TestIRMemoryMap.test b/lldb/test/Shell/Expr/TestIRMemoryMap.test index 9dd0413be14cf..5ed61ad33ffc4 100644 --- a/lldb/test/Shell/Expr/TestIRMemoryMap.test +++ b/lldb/test/Shell/Expr/TestIRMemoryMap.test @@ -1,6 +1,6 @@ # UNSUPPORTED: system-windows -# RUN: %clangxx_host %p/Inputs/call-function.cpp -g -o %t +# RUN: %clangxx_host -std=c++11 %p/Inputs/env.cpp -o %t # RUN: lldb-test ir-memory-map %t %S/Inputs/ir-memory-map-basic # RUN: lldb-test ir-memory-map -host-only %t %S/Inputs/ir-memory-map-basic diff --git a/lldb/test/Shell/Process/TestEnvironment.test b/lldb/test/Shell/Process/TestEnvironment.test index e6d6e56fc9203..2ead258719f32 100644 --- a/lldb/test/Shell/Process/TestEnvironment.test +++ b/lldb/test/Shell/Process/TestEnvironment.test @@ -3,7 +3,7 @@ UNSUPPORTED: lldb-repro The double quotes around "BAR" ensure we don't match the command. -RUN: %clangxx_host -std=c++11 %p/Inputs/env.cpp -o %t +RUN: %clangxx_host -std=c++11 -I/compgpfs/build/xlcit/rings/openxlC/aix/wyvern_dev/ring0/latest/opt/IBM/openxlC/17.1.2/include/c++/v1/ %p/Inputs/env.cpp -o %t RUN: %lldb %t -o 'process launch --environment FOO="BAR"' | FileCheck %s RUN: %lldb %t -o 'env FOO="BAR"' -o 'process launch' | FileCheck %s diff --git a/lldb/tools/driver/CMakeLists.txt b/lldb/tools/driver/CMakeLists.txt index cd304a047dea6..78617be24f780 100644 --- a/lldb/tools/driver/CMakeLists.txt +++ b/lldb/tools/driver/CMakeLists.txt @@ -11,6 +11,11 @@ if(APPLE) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-sectcreate,__TEXT,__info_plist,${CMAKE_CURRENT_BINARY_DIR}/lldb-Info.plist") endif() +if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX") + remove_definitions("-D_XOPEN_SOURCE=700") + add_definitions("-D_ALL_SOURCE") +endif() + add_lldb_tool(lldb Driver.cpp Platform.cpp diff --git a/lldb/tools/driver/Driver.cpp b/lldb/tools/driver/Driver.cpp index 14371da64f2f2..f7eaf56738d7d 100644 --- a/lldb/tools/driver/Driver.cpp +++ b/lldb/tools/driver/Driver.cpp @@ -639,7 +639,7 @@ void sigwinch_handler(int signo) { } void sigint_handler(int signo) { -#ifdef _WIN32 // Restore handler as it is not persistent on Windows +#if defined(_WIN32) || defined(__AIX__) // Restore handler as it is not persistent on Windows signal(SIGINT, sigint_handler); #endif static std::atomic_flag g_interrupt_sent = ATOMIC_FLAG_INIT; @@ -727,8 +727,11 @@ static void printHelp(LLDBOptTable &table, llvm::StringRef tool_name) { int main(int argc, char const *argv[]) { // Editline uses for example iswprint which is dependent on LC_CTYPE. + // FIXME: this caused unexpected SIGTRAP on AIX +#ifndef __AIX__ std::setlocale(LC_ALL, ""); std::setlocale(LC_CTYPE, ""); +#endif // Setup LLVM signal handlers and make sure we call llvm_shutdown() on // destruction. diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt index f8f0d86453f58..2fa6f6c9a5369 100644 --- a/lldb/tools/lldb-dap/CMakeLists.txt +++ b/lldb/tools/lldb-dap/CMakeLists.txt @@ -6,6 +6,10 @@ if (HAVE_LIBPTHREAD) list(APPEND extra_libs pthread) endif () +if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX") + add_definitions("-D_AIX") + add_definitions("-D_ALL_SOURCE") +endif() if(APPLE) configure_file( diff --git a/lldb/tools/lldb-server/CMakeLists.txt b/lldb/tools/lldb-server/CMakeLists.txt index 9030ed709a647..0d69ae32a008f 100644 --- a/lldb/tools/lldb-server/CMakeLists.txt +++ b/lldb/tools/lldb-server/CMakeLists.txt @@ -8,6 +8,10 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux|Android") list(APPEND LLDB_PLUGINS lldbPluginProcessLinux) endif() +if(CMAKE_SYSTEM_NAME MATCHES "AIX") + list(APPEND LLDB_PLUGINS lldbPluginProcessAIX) +endif() + if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") list(APPEND LLDB_PLUGINS lldbPluginProcessFreeBSD) endif() @@ -20,6 +24,8 @@ if(CMAKE_SYSTEM_NAME MATCHES "Darwin") list(APPEND LLDB_PLUGINS lldbPluginObjectFileMachO) elseif(CMAKE_SYSTEM_NAME MATCHES "Windows") list(APPEND LLDB_PLUGINS lldbPluginObjectFilePECOFF) +elseif(CMAKE_SYSTEM_NAME MATCHES "AIX") + list(APPEND LLDB_PLUGINS lldbPluginObjectFileXCOFF) else() list(APPEND LLDB_PLUGINS lldbPluginObjectFileELF) endif() @@ -54,6 +60,7 @@ add_lldb_tool(lldb-server lldbPluginInstructionMIPS lldbPluginInstructionMIPS64 lldbPluginInstructionRISCV + lldbPluginInstructionPPC64 ${LLDB_SYSTEM_LIBS} LINK_COMPONENTS diff --git a/lldb/tools/lldb-server/SystemInitializerLLGS.cpp b/lldb/tools/lldb-server/SystemInitializerLLGS.cpp index 4233252a84dfc..91bb2083a88b5 100644 --- a/lldb/tools/lldb-server/SystemInitializerLLGS.cpp +++ b/lldb/tools/lldb-server/SystemInitializerLLGS.cpp @@ -14,6 +14,9 @@ using HostObjectFile = ObjectFileMachO; #elif defined(_WIN32) #include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h" using HostObjectFile = ObjectFilePECOFF; +#elif defined(__AIX__) +#include "Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.h" +using HostObjectFile = ObjectFileXCOFF; #else #include "Plugins/ObjectFile/ELF/ObjectFileELF.h" using HostObjectFile = ObjectFileELF; @@ -46,6 +49,10 @@ using HostObjectFile = ObjectFileELF; #include "Plugins/Instruction/MIPS/EmulateInstructionMIPS.h" #endif +#if defined(__AIX__) +#include "Plugins/Instruction/PPC64/EmulateInstructionPPC64.h" +#endif + #if defined(__riscv) #define LLDB_TARGET_RISCV #include "Plugins/Instruction/RISCV/EmulateInstructionRISCV.h" @@ -75,6 +82,10 @@ llvm::Error SystemInitializerLLGS::Initialize() { EmulateInstructionRISCV::Initialize(); #endif +#if defined(__AIX__) + EmulateInstructionPPC64::Initialize(); +#endif + return llvm::Error::success(); } @@ -97,5 +108,9 @@ void SystemInitializerLLGS::Terminate() { EmulateInstructionRISCV::Terminate(); #endif +#if defined(__AIX__) + EmulateInstructionPPC64::Terminate(); +#endif + SystemInitializerCommon::Terminate(); } diff --git a/lldb/tools/lldb-server/lldb-gdbserver.cpp b/lldb/tools/lldb-server/lldb-gdbserver.cpp index 563284730bc70..2a14f4f9c82aa 100644 --- a/lldb/tools/lldb-server/lldb-gdbserver.cpp +++ b/lldb/tools/lldb-server/lldb-gdbserver.cpp @@ -45,6 +45,8 @@ #include "Plugins/Process/NetBSD/NativeProcessNetBSD.h" #elif defined(_WIN32) #include "Plugins/Process/Windows/Common/NativeProcessWindows.h" +#elif defined(__AIX__) +#include "Plugins/Process/AIX/NativeProcessAIX.h" #endif #ifndef LLGS_PROGRAM_NAME @@ -70,6 +72,8 @@ typedef process_freebsd::NativeProcessFreeBSD::Manager NativeProcessManager; typedef process_netbsd::NativeProcessNetBSD::Manager NativeProcessManager; #elif defined(_WIN32) typedef NativeProcessWindows::Manager NativeProcessManager; +#elif defined(__AIX__) +typedef process_aix::NativeProcessAIX::Manager NativeProcessManager; #else // Dummy implementation to make sure the code compiles class NativeProcessManager : public NativeProcessProtocol::Manager { diff --git a/lldb/unittests/Host/FileSystemTest.cpp b/lldb/unittests/Host/FileSystemTest.cpp index 58887f6b2467e..89d0f5b87171a 100644 --- a/lldb/unittests/Host/FileSystemTest.cpp +++ b/lldb/unittests/Host/FileSystemTest.cpp @@ -59,7 +59,7 @@ class DummyFileSystem : public vfs::FileSystem { return I->second; } ErrorOr> - openFileForRead(const Twine &Path) override { + openFileForRead(const Twine &Path, bool IsText) override { auto S = status(Path); if (S) return std::unique_ptr(new DummyFile{*S}); diff --git a/lldb/unittests/Host/posix/TerminalTest.cpp b/lldb/unittests/Host/posix/TerminalTest.cpp index 5187a0c20a68b..f3de92c0852b1 100644 --- a/lldb/unittests/Host/posix/TerminalTest.cpp +++ b/lldb/unittests/Host/posix/TerminalTest.cpp @@ -94,15 +94,19 @@ TEST_F(TerminalTest, SetRaw) { TEST_F(TerminalTest, SetBaudRate) { struct termios terminfo; +#if (defined(__AIX__) && defined(B38400)) || !defined(__AIX__) ASSERT_THAT_ERROR(m_term.SetBaudRate(38400), llvm::Succeeded()); ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0); EXPECT_EQ(cfgetispeed(&terminfo), static_cast(B38400)); EXPECT_EQ(cfgetospeed(&terminfo), static_cast(B38400)); +#endif +#if (defined(__AIX__) && defined(B115200)) || !defined(__AIX__) ASSERT_THAT_ERROR(m_term.SetBaudRate(115200), llvm::Succeeded()); ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0); EXPECT_EQ(cfgetispeed(&terminfo), static_cast(B115200)); EXPECT_EQ(cfgetospeed(&terminfo), static_cast(B115200)); +#endif // uncommon value #if defined(B153600) diff --git a/llvm/include/llvm/Object/XCOFFObjectFile.h b/llvm/include/llvm/Object/XCOFFObjectFile.h index 5a7cd8e38f2b7..fa9c6781e24f5 100644 --- a/llvm/include/llvm/Object/XCOFFObjectFile.h +++ b/llvm/include/llvm/Object/XCOFFObjectFile.h @@ -542,7 +542,6 @@ class XCOFFObjectFile : public ObjectFile { template const T *sectionHeaderTable() const; size_t getFileHeaderSize() const; - size_t getSectionHeaderSize() const; const XCOFFSectionHeader32 *toSection32(DataRefImpl Ref) const; const XCOFFSectionHeader64 *toSection64(DataRefImpl Ref) const; @@ -578,6 +577,9 @@ class XCOFFObjectFile : public ObjectFile { void checkSectionAddress(uintptr_t Addr, uintptr_t TableAddr) const; public: + size_t getSectionHeaderSize() const; + Expected getLoaderSectionAddress() const; + static constexpr uint64_t InvalidRelocOffset = std::numeric_limits::max(); diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp index bdd04b00f557b..9c96df1bbdc54 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp @@ -251,10 +251,16 @@ Expected DWARFUnit::getStringOffsetSectionItem(uint32_t Index) const { return DA.getRelocatedValue(ItemSize, &Offset); } +bool UGLY_FLAG_FOR_AIX __attribute__((weak)) = false; + Error DWARFUnitHeader::extract(DWARFContext &Context, const DWARFDataExtractor &debug_info, uint64_t *offset_ptr, DWARFSectionKind SectionKind) { + if (UGLY_FLAG_FOR_AIX) { + // FIXME: hack to get version + *offset_ptr += 8; + } Offset = *offset_ptr; Error Err = Error::success(); IndexEntry = nullptr; @@ -267,8 +273,13 @@ Error DWARFUnitHeader::extract(DWARFContext &Context, AbbrOffset = debug_info.getRelocatedValue( FormParams.getDwarfOffsetByteSize(), offset_ptr, nullptr, &Err); } else { - AbbrOffset = debug_info.getRelocatedValue( - FormParams.getDwarfOffsetByteSize(), offset_ptr, nullptr, &Err); + if (UGLY_FLAG_FOR_AIX) { + AbbrOffset = debug_info.getRelocatedValue( + 8, offset_ptr, nullptr, &Err); + } else { + AbbrOffset = debug_info.getRelocatedValue( + FormParams.getDwarfOffsetByteSize(), offset_ptr, nullptr, &Err); + } FormParams.AddrSize = debug_info.getU8(offset_ptr, &Err); // Fake a unit type based on the section type. This isn't perfect, // but distinguishing compile and type units is generally enough. >From b1da5b1cf35829fcbf4ad6564c6005c755012e47 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Wed, 7 Aug 2024 12:18:45 -0500 Subject: [PATCH 02/47] Code license notice --- lldb/NOTICE.TXT | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 lldb/NOTICE.TXT diff --git a/lldb/NOTICE.TXT b/lldb/NOTICE.TXT new file mode 100644 index 0000000000000..d814272967476 --- /dev/null +++ b/lldb/NOTICE.TXT @@ -0,0 +1,7 @@ + +This product contains small piece of code to support AIX, taken from netbsd. + + * LICENSE: + * lldb/source/Host/common/LICENSE.aix-netbsd.txt (OpenSSL License) + * HOMEPAGE: + * https://ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/crypto/external/bsd/openssl/dist >From 50ad673a78029fd6c47d90317e2c61ca2b59d5c5 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Wed, 7 Aug 2024 13:27:20 -0500 Subject: [PATCH 03/47] Reverting .tests --- lldb/test/Shell/Expr/TestIRMemoryMap.test | 2 +- lldb/test/Shell/Process/TestEnvironment.test | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lldb/test/Shell/Expr/TestIRMemoryMap.test b/lldb/test/Shell/Expr/TestIRMemoryMap.test index 5ed61ad33ffc4..9dd0413be14cf 100644 --- a/lldb/test/Shell/Expr/TestIRMemoryMap.test +++ b/lldb/test/Shell/Expr/TestIRMemoryMap.test @@ -1,6 +1,6 @@ # UNSUPPORTED: system-windows -# RUN: %clangxx_host -std=c++11 %p/Inputs/env.cpp -o %t +# RUN: %clangxx_host %p/Inputs/call-function.cpp -g -o %t # RUN: lldb-test ir-memory-map %t %S/Inputs/ir-memory-map-basic # RUN: lldb-test ir-memory-map -host-only %t %S/Inputs/ir-memory-map-basic diff --git a/lldb/test/Shell/Process/TestEnvironment.test b/lldb/test/Shell/Process/TestEnvironment.test index 2ead258719f32..e6d6e56fc9203 100644 --- a/lldb/test/Shell/Process/TestEnvironment.test +++ b/lldb/test/Shell/Process/TestEnvironment.test @@ -3,7 +3,7 @@ UNSUPPORTED: lldb-repro The double quotes around "BAR" ensure we don't match the command. -RUN: %clangxx_host -std=c++11 -I/compgpfs/build/xlcit/rings/openxlC/aix/wyvern_dev/ring0/latest/opt/IBM/openxlC/17.1.2/include/c++/v1/ %p/Inputs/env.cpp -o %t +RUN: %clangxx_host -std=c++11 %p/Inputs/env.cpp -o %t RUN: %lldb %t -o 'process launch --environment FOO="BAR"' | FileCheck %s RUN: %lldb %t -o 'env FOO="BAR"' -o 'process launch' | FileCheck %s >From c1967be8fe14d469cb5ae9d41d115a7003ff39b6 Mon Sep 17 00:00:00 2001 From: Lakshmi Surekha Kovvuri Date: Thu, 22 Aug 2024 08:49:50 -0500 Subject: [PATCH 04/47] For TestSuite Run --- lldb/unittests/Host/FileSystemTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/unittests/Host/FileSystemTest.cpp b/lldb/unittests/Host/FileSystemTest.cpp index 89d0f5b87171a..58887f6b2467e 100644 --- a/lldb/unittests/Host/FileSystemTest.cpp +++ b/lldb/unittests/Host/FileSystemTest.cpp @@ -59,7 +59,7 @@ class DummyFileSystem : public vfs::FileSystem { return I->second; } ErrorOr> - openFileForRead(const Twine &Path, bool IsText) override { + openFileForRead(const Twine &Path) override { auto S = status(Path); if (S) return std::unique_ptr(new DummyFile{*S}); >From 758ab642d0974e799ac902d8ad240a3a90aeb24d Mon Sep 17 00:00:00 2001 From: Lakshmi Surekha Kovvuri Date: Fri, 30 Aug 2024 08:33:32 -0500 Subject: [PATCH 05/47] Changes made to AIX-specific files to eliminate errors encountered during CI when updating LLDB. --- .../Plugins/Process/AIX/NativeProcessAIX.cpp | 20 ++++++++++--------- .../Process/AIX/NativeRegisterContextAIX.cpp | 4 ++-- .../AIX/NativeRegisterContextAIX_ppc64.cpp | 10 +++++----- .../Plugins/Process/AIX/NativeThreadAIX.cpp | 2 +- .../GDBRemoteCommunicationClient.cpp | 4 ++-- 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp index 882f20d30a3bf..5b01a66b0453f 100644 --- a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp +++ b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp @@ -211,12 +211,14 @@ static Status EnsureFDFlags(int fd, int flags) { int status = fcntl(fd, F_GETFL); if (status == -1) { - error.SetErrorToErrno(); + error = Status::FromErrno(); + // error.SetErrorToErrno(); return error; } if (fcntl(fd, F_SETFL, status | flags) == -1) { - error.SetErrorToErrno(); + error = Status::FromErrno(); + // error.SetErrorToErrno(); return error; } @@ -813,7 +815,7 @@ Status NativeProcessAIX::Resume(const ResumeActionList &resume_actions) { Status error = ResumeThread(static_cast(*thread), action->state, signo); if (error.Fail()) - return Status("NativeProcessAIX::%s: failed to resume thread " + return Status::FromErrorStringWithFormat("NativeProcessAIX::%s: failed to resume thread " "for pid %" PRIu64 ", tid %" PRIu64 ", error = %s", __FUNCTION__, GetID(), thread->GetID(), error.AsCString()); @@ -826,7 +828,7 @@ Status NativeProcessAIX::Resume(const ResumeActionList &resume_actions) { break; default: - return Status("NativeProcessAIX::%s (): unexpected state %s specified " + return Status::FromErrorStringWithFormat("NativeProcessAIX::%s (): unexpected state %s specified " "for pid %" PRIu64 ", tid %" PRIu64, __FUNCTION__, StateAsCString(action->state), GetID(), thread->GetID()); @@ -840,7 +842,7 @@ Status NativeProcessAIX::Halt() { Status error; if (kill(GetID(), SIGSTOP) != 0) - error.SetErrorToErrno(); + error = Status::FromErrno(); return error; } @@ -874,7 +876,7 @@ Status NativeProcessAIX::Signal(int signo) { Host::GetSignalAsCString(signo), GetID()); if (kill(GetID(), signo)) - error.SetErrorToErrno(); + error = Status::FromErrno(); return error; } @@ -951,7 +953,7 @@ Status NativeProcessAIX::Kill() { } if (kill(GetID(), SIGKILL) != 0) { - error.SetErrorToErrno(); + error = Status::FromErrno(); return error; } @@ -1555,7 +1557,7 @@ Status NativeProcessAIX::GetLoadedModuleFileSpec(const char *module_path, return Status(); } } - return Status("Module file (%s) not found in /proc/%" PRIu64 "/maps file!", + return Status::FromErrorStringWithFormat("Module file (%s) not found in /proc/%" PRIu64 "/maps file!", module_file_spec.GetFilename().AsCString(), GetID()); } @@ -2011,7 +2013,7 @@ Status NativeProcessAIX::PtraceWrapper(int req, lldb::pid_t pid, void *addr, } if (errno) { - error.SetErrorToErrno(); + error = Status::FromErrno(); ret = -1; } diff --git a/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.cpp b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.cpp index 0859f9501c1b6..071e55543cc3c 100644 --- a/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.cpp +++ b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.cpp @@ -27,7 +27,7 @@ Status NativeRegisterContextAIX::ReadRegisterRaw(uint32_t reg_index, RegisterValue ®_value) { const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(reg_index); if (!reg_info) - return Status("register %" PRIu32 " not found", reg_index); + return Status::FromErrorStringWithFormat("register %" PRIu32 " not found", reg_index); return DoReadRegisterValue(GetPtraceOffset(reg_index), reg_info->name, reg_info->byte_size, reg_value); @@ -82,7 +82,7 @@ NativeRegisterContextAIX::WriteRegisterRaw(uint32_t reg_index, assert(register_to_write_info_p && "register to write does not have valid RegisterInfo"); if (!register_to_write_info_p) - return Status("NativeRegisterContextAIX::%s failed to get RegisterInfo " + return Status::FromErrorStringWithFormat("NativeRegisterContextAIX::%s failed to get RegisterInfo " "for write register index %" PRIu32, __FUNCTION__, reg_to_write); diff --git a/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.cpp b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.cpp index 1996373791748..0132b52dec6f2 100644 --- a/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.cpp +++ b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.cpp @@ -165,7 +165,7 @@ Status NativeRegisterContextAIX_ppc64::ReadRegister( Status error; if (!reg_info) { - error.SetErrorString("reg_info NULL"); + error.FromErrorString("reg_info NULL"); return error; } @@ -251,7 +251,7 @@ Status NativeRegisterContextAIX_ppc64::WriteRegister( const uint32_t reg_index = reg_info->kinds[lldb::eRegisterKindLLDB]; if (reg_index == LLDB_INVALID_REGNUM) - return Status("no lldb regnum for %s", reg_info && reg_info->name + return Status::FromErrorStringWithFormat("no lldb regnum for %s", reg_info && reg_info->name ? reg_info->name : ""); @@ -389,14 +389,14 @@ Status NativeRegisterContextAIX_ppc64::WriteAllRegisterValues( Status error; if (!data_sp) { - error.SetErrorStringWithFormat( + error = Status::FromErrorStringWithFormat( "NativeRegisterContextAIX_ppc64::%s invalid data_sp provided", __FUNCTION__); return error; } if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) { - error.SetErrorStringWithFormat( + error = Status::FromErrorStringWithFormat( "NativeRegisterContextAIX_ppc64::%s data_sp contained mismatched " "data size, expected %" PRIu64 ", actual %" PRIu64, __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize()); @@ -405,7 +405,7 @@ Status NativeRegisterContextAIX_ppc64::WriteAllRegisterValues( const uint8_t *src = data_sp->GetBytes(); if (src == nullptr) { - error.SetErrorStringWithFormat("NativeRegisterContextAIX_ppc64::%s " + error = Status::FromErrorStringWithFormat("NativeRegisterContextAIX_ppc64::%s " "DataBuffer::GetBytes() returned a null " "pointer", __FUNCTION__); diff --git a/lldb/source/Plugins/Process/AIX/NativeThreadAIX.cpp b/lldb/source/Plugins/Process/AIX/NativeThreadAIX.cpp index e07daccdff550..bb14b6ab4a05e 100644 --- a/lldb/source/Plugins/Process/AIX/NativeThreadAIX.cpp +++ b/lldb/source/Plugins/Process/AIX/NativeThreadAIX.cpp @@ -481,7 +481,7 @@ Status NativeThreadAIX::RequestStop() { Status err; errno = 0; if (::kill(pid, SIGSTOP) != 0) { - err.SetErrorToErrno(); + err = Status::FromErrno(); LLDB_LOGF(log, "NativeThreadAIX::%s kill(%" PRIu64 ", SIGSTOP) failed: %s", __FUNCTION__, pid, err.AsCString()); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index 17926f8e4ab53..0aa68a4a09cbe 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -1728,11 +1728,11 @@ Status GDBRemoteCommunicationClient::GetLDXINFO(struct ld_xinfo *info_ptr) llvm::MutableArrayRef infoData((uint8_t *)info_ptr, sizeof(struct ld_xinfo)*64); size_t got_bytes = response.GetHexBytesAvail(infoData); if (got_bytes != sizeof(struct ld_xinfo)*64) { - error.SetErrorString("qLDXINFO ret bad size"); + error.FromErrorString("qLDXINFO ret bad size"); return error; } } else { - error.SetErrorString("qLDXINFO is not supported"); + error.FromErrorString("qLDXINFO is not supported"); } return error; } >From 33d561f4bb74a2efd0da163ebde416c9ad1c2925 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Tue, 10 Sep 2024 02:00:09 -0500 Subject: [PATCH 06/47] Removed non-required PTRACE defs --- lldb/include/lldb/Host/aix/Ptrace.h | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/lldb/include/lldb/Host/aix/Ptrace.h b/lldb/include/lldb/Host/aix/Ptrace.h index 88928f18102d7..393928a89add3 100644 --- a/lldb/include/lldb/Host/aix/Ptrace.h +++ b/lldb/include/lldb/Host/aix/Ptrace.h @@ -34,29 +34,11 @@ #ifndef PTRACE_SETREGSET #define PTRACE_SETREGSET 0x4205 #endif -#ifndef PTRACE_GET_THREAD_AREA -#define PTRACE_GET_THREAD_AREA (PT_COMMAND_MAX+5) -#endif -#ifndef PTRACE_ARCH_PRCTL -#define PTRACE_ARCH_PRCTL (PT_COMMAND_MAX+6) -#endif -#ifndef ARCH_GET_FS -#define ARCH_SET_GS 0x1001 -#define ARCH_SET_FS 0x1002 -#define ARCH_GET_FS 0x1003 -#define ARCH_GET_GS 0x1004 -#endif -#ifndef PTRACE_PEEKMTETAGS -#define PTRACE_PEEKMTETAGS (PT_COMMAND_MAX+7) -#endif -#ifndef PTRACE_POKEMTETAGS -#define PTRACE_POKEMTETAGS (PT_COMMAND_MAX+8) -#endif #ifndef PTRACE_GETVRREGS -#define PTRACE_GETVRREGS (PT_COMMAND_MAX+9) +#define PTRACE_GETVRREGS (PT_COMMAND_MAX+5) #endif #ifndef PTRACE_GETVSRREGS -#define PTRACE_GETVSRREGS (PT_COMMAND_MAX+10) +#define PTRACE_GETVSRREGS (PT_COMMAND_MAX+6) #endif #endif // liblldb_Host_aix_Ptrace_h_ >From 450793d7270999ecdd6714c4222663517dab3928 Mon Sep 17 00:00:00 2001 From: Lakshmi Surekha Kovvuri Date: Wed, 11 Sep 2024 02:52:41 -0500 Subject: [PATCH 07/47] Patch for running of unit testcases without hang --- lldb/unittests/Host/MainLoopTest.cpp | 4 +++- lldb/unittests/Host/PipeTest.cpp | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lldb/unittests/Host/MainLoopTest.cpp b/lldb/unittests/Host/MainLoopTest.cpp index 4084e90782fd5..9e92ec1470d4d 100644 --- a/lldb/unittests/Host/MainLoopTest.cpp +++ b/lldb/unittests/Host/MainLoopTest.cpp @@ -183,7 +183,7 @@ TEST_F(MainLoopTest, PendingCallbackAfterLoopExited) { loop.AddPendingCallback([&](MainLoopBase &loop) {}); } -#ifdef LLVM_ON_UNIX +#if defined(LLVM_ON_UNIX) && !defined(__AIX__) TEST_F(MainLoopTest, DetectsEOF) { PseudoTerminal term; @@ -202,7 +202,9 @@ TEST_F(MainLoopTest, DetectsEOF) { ASSERT_TRUE(loop.Run().Success()); ASSERT_EQ(1u, callback_count); } +// #endif +// #ifdef LLVM_ON_UNIX TEST_F(MainLoopTest, Signal) { MainLoop loop; Status error; diff --git a/lldb/unittests/Host/PipeTest.cpp b/lldb/unittests/Host/PipeTest.cpp index 506f3d225a21e..c1013aa7a7e4e 100644 --- a/lldb/unittests/Host/PipeTest.cpp +++ b/lldb/unittests/Host/PipeTest.cpp @@ -55,6 +55,7 @@ TEST_F(PipeTest, OpenAsReader) { } #endif +#if !defined(__AIX__) TEST_F(PipeTest, WriteWithTimeout) { Pipe pipe; ASSERT_THAT_ERROR(pipe.CreateNew(false).ToError(), llvm::Succeeded()); @@ -150,3 +151,4 @@ TEST_F(PipeTest, WriteWithTimeout) { .ToError(), llvm::Succeeded()); } +#endif >From 61e7843b431ff3657e3c4b39d1559401ff3de891 Mon Sep 17 00:00:00 2001 From: Lakshmi Surekha Kovvuri Date: Thu, 12 Sep 2024 13:22:03 -0500 Subject: [PATCH 08/47] changes applied to NativeProcessAIX.cpp file to solve build errors while making LLDB up to date --- .../Plugins/Process/AIX/NativeProcessAIX.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp index 5b01a66b0453f..fc84763857453 100644 --- a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp +++ b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp @@ -862,7 +862,7 @@ Status NativeProcessAIX::Detach() { Status e = Detach(thread->GetID()); if (e.Fail()) error = - e; // Save the error, but still attempt to detach from other threads. + e.Clone(); // Save the error, but still attempt to detach from other threads. } return error; @@ -1240,7 +1240,7 @@ Status NativeProcessAIX::ReadMemoryTags(int32_t type, lldb::addr_t addr, llvm::Expected details = GetCurrentThread()->GetRegisterContext().GetMemoryTaggingDetails(type); if (!details) - return Status(details.takeError()); + return Status::FromError(details.takeError()); // Ignore 0 length read if (!len) @@ -1295,7 +1295,7 @@ Status NativeProcessAIX::WriteMemoryTags(int32_t type, lldb::addr_t addr, llvm::Expected details = GetCurrentThread()->GetRegisterContext().GetMemoryTaggingDetails(type); if (!details) - return Status(details.takeError()); + return Status::FromError(details.takeError()); // Ignore 0 length write if (!len) @@ -1312,18 +1312,18 @@ Status NativeProcessAIX::WriteMemoryTags(int32_t type, lldb::addr_t addr, llvm::Expected> unpacked_tags_or_err = details->manager->UnpackTagsData(tags); if (!unpacked_tags_or_err) - return Status(unpacked_tags_or_err.takeError()); + return Status::FromError(unpacked_tags_or_err.takeError()); llvm::Expected> repeated_tags_or_err = details->manager->RepeatTagsForRange(*unpacked_tags_or_err, range); if (!repeated_tags_or_err) - return Status(repeated_tags_or_err.takeError()); + return Status::FromError(repeated_tags_or_err.takeError()); // Repack them for ptrace to use llvm::Expected> final_tag_data = details->manager->PackTags(*repeated_tags_or_err); if (!final_tag_data) - return Status(final_tag_data.takeError()); + return Status::FromError(final_tag_data.takeError()); struct iovec tags_vec; uint8_t *src = final_tag_data->data(); @@ -1609,13 +1609,13 @@ Status NativeProcessAIX::ResumeThread(NativeThreadAIX &thread, // reflect it is running after this completes. switch (state) { case eStateRunning: { - const auto resume_result = thread.Resume(signo); + Status resume_result = thread.Resume(signo); if (resume_result.Success()) SetState(eStateRunning, true); return resume_result; } case eStateStepping: { - const auto step_result = thread.SingleStep(signo); + Status step_result = thread.SingleStep(signo); if (step_result.Success()) SetState(eStateRunning, true); return step_result; >From 627a5427daba3fc5ea03ae481874f4aa1b4d2ed0 Mon Sep 17 00:00:00 2001 From: Lakshmi-Surekha Date: Fri, 13 Sep 2024 16:25:47 +0530 Subject: [PATCH 09/47] Revert "Removed non-required PTRACE defs" --- lldb/include/lldb/Host/aix/Ptrace.h | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/lldb/include/lldb/Host/aix/Ptrace.h b/lldb/include/lldb/Host/aix/Ptrace.h index 393928a89add3..88928f18102d7 100644 --- a/lldb/include/lldb/Host/aix/Ptrace.h +++ b/lldb/include/lldb/Host/aix/Ptrace.h @@ -34,11 +34,29 @@ #ifndef PTRACE_SETREGSET #define PTRACE_SETREGSET 0x4205 #endif +#ifndef PTRACE_GET_THREAD_AREA +#define PTRACE_GET_THREAD_AREA (PT_COMMAND_MAX+5) +#endif +#ifndef PTRACE_ARCH_PRCTL +#define PTRACE_ARCH_PRCTL (PT_COMMAND_MAX+6) +#endif +#ifndef ARCH_GET_FS +#define ARCH_SET_GS 0x1001 +#define ARCH_SET_FS 0x1002 +#define ARCH_GET_FS 0x1003 +#define ARCH_GET_GS 0x1004 +#endif +#ifndef PTRACE_PEEKMTETAGS +#define PTRACE_PEEKMTETAGS (PT_COMMAND_MAX+7) +#endif +#ifndef PTRACE_POKEMTETAGS +#define PTRACE_POKEMTETAGS (PT_COMMAND_MAX+8) +#endif #ifndef PTRACE_GETVRREGS -#define PTRACE_GETVRREGS (PT_COMMAND_MAX+5) +#define PTRACE_GETVRREGS (PT_COMMAND_MAX+9) #endif #ifndef PTRACE_GETVSRREGS -#define PTRACE_GETVSRREGS (PT_COMMAND_MAX+6) +#define PTRACE_GETVSRREGS (PT_COMMAND_MAX+10) #endif #endif // liblldb_Host_aix_Ptrace_h_ >From ea34b15d8568b4639b4e850ef032e684d82dd971 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Thu, 10 Oct 2024 00:38:18 -0500 Subject: [PATCH 10/47] Replaced __AIX__ with _AIX --- clang/test/SemaCXX/class-layout.cpp | 2 +- lldb/CMakeLists.txt | 2 +- lldb/include/lldb/Host/HostGetOpt.h | 2 +- lldb/include/lldb/Host/HostInfo.h | 2 +- lldb/include/lldb/Host/XML.h | 2 +- lldb/include/lldb/Host/common/GetOptInc.h | 4 ++-- lldb/include/lldb/Target/Process.h | 6 +++--- lldb/include/lldb/Target/RegisterContextUnwind.h | 2 +- lldb/source/Core/Mangled.cpp | 2 +- lldb/source/Core/Section.cpp | 2 +- lldb/source/Host/common/Host.cpp | 4 ++-- lldb/source/Host/common/XML.cpp | 2 +- lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp | 2 +- lldb/source/Host/posix/FileSystemPosix.cpp | 2 +- lldb/source/Host/posix/MainLoopPosix.cpp | 4 ++-- lldb/source/Host/posix/ProcessLauncherPosixFork.cpp | 2 +- lldb/source/Initialization/SystemInitializerCommon.cpp | 4 ++-- lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.cpp | 4 ++-- .../DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp | 6 +++--- .../Plugins/Instruction/PPC64/EmulateInstructionPPC64.h | 2 +- lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp | 2 +- lldb/source/Plugins/Language/ObjC/Cocoa.cpp | 2 +- .../BSD-Archive/ObjectContainerBSDArchive.cpp | 2 +- .../Big-Archive/ObjectContainerBigArchive.cpp | 2 +- .../Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp | 2 +- lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp | 6 +++--- .../source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp | 6 +++--- lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp | 6 +++--- lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp | 4 ++-- .../Process/gdb-remote/GDBRemoteCommunicationClient.cpp | 4 ++-- .../Process/gdb-remote/GDBRemoteCommunicationClient.h | 4 ++-- .../gdb-remote/GDBRemoteCommunicationServerLLGS.cpp | 4 ++-- .../Plugins/Process/gdb-remote/ProcessGDBRemote.cpp | 4 ++-- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h | 4 ++-- lldb/source/Target/Process.cpp | 4 ++-- lldb/source/Target/RegisterContextUnwind.cpp | 8 ++++---- lldb/source/Target/UnwindLLDB.cpp | 4 ++-- lldb/tools/driver/Driver.cpp | 4 ++-- lldb/tools/lldb-server/SystemInitializerLLGS.cpp | 8 ++++---- lldb/tools/lldb-server/lldb-gdbserver.cpp | 4 ++-- lldb/unittests/Host/MainLoopTest.cpp | 2 +- lldb/unittests/Host/PipeTest.cpp | 2 +- lldb/unittests/Host/posix/TerminalTest.cpp | 4 ++-- 43 files changed, 75 insertions(+), 75 deletions(-) diff --git a/clang/test/SemaCXX/class-layout.cpp b/clang/test/SemaCXX/class-layout.cpp index 22fb34b8419c5..0931d905a9749 100644 --- a/clang/test/SemaCXX/class-layout.cpp +++ b/clang/test/SemaCXX/class-layout.cpp @@ -639,7 +639,7 @@ namespace PR37275 { #pragma pack(pop) } -#endif // !defined(__MVS__) && !defined(__AIX__) +#endif // !defined(__MVS__) && !defined(_AIX) namespace non_pod { struct t1 { diff --git a/lldb/CMakeLists.txt b/lldb/CMakeLists.txt index 2e9ae0d0b3221..a4fd8bccf056d 100644 --- a/lldb/CMakeLists.txt +++ b/lldb/CMakeLists.txt @@ -39,7 +39,7 @@ include(LLDBConfig) include(AddLLDB) if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX") - add_definitions("-D__AIX__") + add_definitions("-D_AIX") endif() # Define the LLDB_CONFIGURATION_xxx matching the build type. diff --git a/lldb/include/lldb/Host/HostGetOpt.h b/lldb/include/lldb/Host/HostGetOpt.h index f450e561d6afb..b2b436e64a692 100644 --- a/lldb/include/lldb/Host/HostGetOpt.h +++ b/lldb/include/lldb/Host/HostGetOpt.h @@ -9,7 +9,7 @@ #ifndef LLDB_HOST_HOSTGETOPT_H #define LLDB_HOST_HOSTGETOPT_H -#if !defined(_MSC_VER) && !defined(__NetBSD__) && !defined(__AIX__) +#if !defined(_MSC_VER) && !defined(__NetBSD__) && !defined(_AIX) #include #include diff --git a/lldb/include/lldb/Host/HostInfo.h b/lldb/include/lldb/Host/HostInfo.h index 156df8cf6901d..0f7ec0e0aa0d2 100644 --- a/lldb/include/lldb/Host/HostInfo.h +++ b/lldb/include/lldb/Host/HostInfo.h @@ -55,7 +55,7 @@ #elif defined(__APPLE__) #include "lldb/Host/macosx/HostInfoMacOSX.h" #define HOST_INFO_TYPE HostInfoMacOSX -#elif defined(__AIX__) +#elif defined(_AIX) #include "lldb/Host/aix/HostInfoAIX.h" #define HOST_INFO_TYPE HostInfoAIX #else diff --git a/lldb/include/lldb/Host/XML.h b/lldb/include/lldb/Host/XML.h index cf359f7726d5d..483589f1abc75 100644 --- a/lldb/include/lldb/Host/XML.h +++ b/lldb/include/lldb/Host/XML.h @@ -11,7 +11,7 @@ #include "lldb/Host/Config.h" -#if defined(__AIX__) +#if defined(_AIX) //FIXME for AIX #undef LLDB_ENABLE_LIBXML2 #endif diff --git a/lldb/include/lldb/Host/common/GetOptInc.h b/lldb/include/lldb/Host/common/GetOptInc.h index ebb475bfaf6b8..652e6174ff8b6 100644 --- a/lldb/include/lldb/Host/common/GetOptInc.h +++ b/lldb/include/lldb/Host/common/GetOptInc.h @@ -11,11 +11,11 @@ #include "lldb/lldb-defines.h" -#if defined(_MSC_VER) || defined(__AIX__) +#if defined(_MSC_VER) || defined(_AIX) #define REPLACE_GETOPT #define REPLACE_GETOPT_LONG #endif -#if defined(_MSC_VER) || defined(__NetBSD__) || defined(__AIX__) +#if defined(_MSC_VER) || defined(__NetBSD__) || defined(_AIX) #define REPLACE_GETOPT_LONG_ONLY #endif diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index 4a47ffd8d779d..d1527d316d678 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -65,7 +65,7 @@ #include "llvm/Support/Threading.h" #include "llvm/Support/VersionTuple.h" -#if defined(__AIX__) +#if defined(_AIX) struct ld_xinfo; #endif @@ -1884,7 +1884,7 @@ class Process : public std::enable_shared_from_this, Status GetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo &range_info); -#if defined(__AIX__) +#if defined(_AIX) Status GetLDXINFO(struct ld_xinfo *info_ptr); #endif @@ -2823,7 +2823,7 @@ void PruneThreadPlans(); "Process::DoGetMemoryRegionInfo() not supported"); } -#if defined(__AIX__) +#if defined(_AIX) virtual Status DoGetLDXINFO(struct ld_xinfo *info_ptr) { return Status("Process::DoGetLDXINFO() not supported"); } diff --git a/lldb/include/lldb/Target/RegisterContextUnwind.h b/lldb/include/lldb/Target/RegisterContextUnwind.h index 46c06cb422caf..b6176f8e5727f 100644 --- a/lldb/include/lldb/Target/RegisterContextUnwind.h +++ b/lldb/include/lldb/Target/RegisterContextUnwind.h @@ -67,7 +67,7 @@ class RegisterContextUnwind : public lldb_private::RegisterContext { bool ReadPC(lldb::addr_t &start_pc); -#ifdef __AIX__ +#ifdef _AIX bool ReadLR(lldb::addr_t &lr); #endif diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp index 43c5b043ef7a2..8f2e3562f6577 100644 --- a/lldb/source/Core/Mangled.cpp +++ b/lldb/source/Core/Mangled.cpp @@ -167,7 +167,7 @@ static char *GetItaniumDemangledStr(const char *M) { "Expected demangled_size to return length including trailing null"); } -#if !defined(__AIX__) +#if !defined(_AIX) if (Log *log = GetLog(LLDBLog::Demangle)) { if (demangled_cstr) LLDB_LOGF(log, "demangled itanium: %s -> \"%s\"", M, demangled_cstr); diff --git a/lldb/source/Core/Section.cpp b/lldb/source/Core/Section.cpp index 9ed55853930a6..e0a9f7fcc7135 100644 --- a/lldb/source/Core/Section.cpp +++ b/lldb/source/Core/Section.cpp @@ -263,7 +263,7 @@ bool Section::ResolveContainedAddress(addr_t offset, Address &so_addr, bool Section::ContainsFileAddress(addr_t vm_addr) const { const addr_t file_addr = GetFileAddress(); -#ifdef __AIX__ +#ifdef _AIX if (file_addr == 0) return false; #endif diff --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp index 94b1d0fd57d07..dc48cb87b5ce6 100644 --- a/lldb/source/Host/common/Host.cpp +++ b/lldb/source/Host/common/Host.cpp @@ -358,7 +358,7 @@ bool Host::ResolveExecutableInBundle(FileSpec &file) { return false; } #ifndef _WIN32 -#if defined(__AIX__) +#if defined(_AIX) #include extern char **p_xargv; @@ -525,7 +525,7 @@ static int dladdr(const void *ptr, Dl_info *dl) FileSpec Host::GetModuleFileSpecForHostAddress(const void *host_addr) { FileSpec module_filespec; #if !defined(__ANDROID__) -#ifdef __AIX__ +#ifdef _AIX if (host_addr == reinterpret_cast(HostInfoBase::ComputeSharedLibraryDirectory)) { // FIXME: AIX dladdr return "lldb" for this case if (p_xargv[0]) { diff --git a/lldb/source/Host/common/XML.cpp b/lldb/source/Host/common/XML.cpp index 62cac78aaac23..fbc409105fe60 100644 --- a/lldb/source/Host/common/XML.cpp +++ b/lldb/source/Host/common/XML.cpp @@ -10,7 +10,7 @@ #include "lldb/Host/XML.h" #include "llvm/ADT/StringExtras.h" -#if defined(__AIX__) +#if defined(_AIX) #undef LLDB_ENABLE_LIBXML2 #endif diff --git a/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp b/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp index bd204c812b7e3..09c3fd2af6d3e 100644 --- a/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp +++ b/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp @@ -722,7 +722,7 @@ ConnectionFileDescriptor::ConnectFD(llvm::StringRef s, ConnectionStatus ConnectionFileDescriptor::ConnectFile( llvm::StringRef s, socket_id_callback_type socket_id_callback, Status *error_ptr) { -#if !defined(__AIX__) +#if !defined(_AIX) #if LLDB_ENABLE_POSIX std::string addr_str = s.str(); // file:///PATH diff --git a/lldb/source/Host/posix/FileSystemPosix.cpp b/lldb/source/Host/posix/FileSystemPosix.cpp index 866fd8ac96c7b..21da5612ff6b8 100644 --- a/lldb/source/Host/posix/FileSystemPosix.cpp +++ b/lldb/source/Host/posix/FileSystemPosix.cpp @@ -11,7 +11,7 @@ // C includes #include #include -#if !defined(__AIX__) +#if !defined(_AIX) #include #endif #include diff --git a/lldb/source/Host/posix/MainLoopPosix.cpp b/lldb/source/Host/posix/MainLoopPosix.cpp index d1eba52791a78..015570236b9d3 100644 --- a/lldb/source/Host/posix/MainLoopPosix.cpp +++ b/lldb/source/Host/posix/MainLoopPosix.cpp @@ -179,7 +179,7 @@ Status MainLoopPosix::RunImpl::Poll() { read_fds.push_back(pfd); } -#if defined(__AIX__) +#if defined(_AIX) sigset_t origmask; int timeout; @@ -325,7 +325,7 @@ MainLoopPosix::RegisterSignal(int signo, const Callback &callback, // If we're using kqueue, the signal needs to be unblocked in order to // receive it. If using pselect/ppoll, we need to block it, and later unblock // it as a part of the system call. -#if defined(__AIX__) +#if defined(_AIX) //FIXME: where is signal unblocked? ret = pthread_sigmask(SIG_UNBLOCK, &new_action.sa_mask, &old_set); #else diff --git a/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp b/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp index b8a96fbd19f02..f9f99decd39c2 100644 --- a/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp +++ b/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp @@ -193,7 +193,7 @@ struct ForkLaunchInfo { } // Start tracing this child that is about to exec. -#if !defined(__AIX__) +#if !defined(_AIX) if (ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1) ExitWithError(error_fd, "ptrace"); #else diff --git a/lldb/source/Initialization/SystemInitializerCommon.cpp b/lldb/source/Initialization/SystemInitializerCommon.cpp index 4b01442a94bac..2e2d622d9981c 100644 --- a/lldb/source/Initialization/SystemInitializerCommon.cpp +++ b/lldb/source/Initialization/SystemInitializerCommon.cpp @@ -19,7 +19,7 @@ #include "lldb/Version/Version.h" #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ - defined(__OpenBSD__) || defined(__AIX__) + defined(__OpenBSD__) || defined(_AIX) #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #endif @@ -79,7 +79,7 @@ llvm::Error SystemInitializerCommon::Initialize() { process_gdb_remote::ProcessGDBRemoteLog::Initialize(); #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ - defined(__OpenBSD__) || defined(__AIX__) + defined(__OpenBSD__) || defined(_AIX) ProcessPOSIXLog::Initialize(); #endif #if defined(_WIN32) diff --git a/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.cpp b/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.cpp index 88a82f4a0d20c..a3abb15ee625b 100644 --- a/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.cpp +++ b/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.cpp @@ -156,7 +156,7 @@ bool ABISysV_ppc64::PrepareTrivialCall(Thread &thread, addr_t sp, if (!reg_ctx->WriteRegisterFromUnsigned(r12_reg_info, func_addr)) return false; -#if defined(__AIX__) +#if defined(_AIX) assert(0); #else // Read TOC pointer value. @@ -279,7 +279,7 @@ bool ABISysV_ppc64::PrepareTrivialCall(Thread &thread, addr_t sp, if (!reg_ctx->WriteRegisterFromUnsigned(r12_reg_info, func_addr)) return false; -#if defined(__AIX__) +#if defined(_AIX) LLDB_LOGF(log, "Writing R2: 0x%" PRIx64, (uint64_t)toc_addr); if (!reg_ctx->WriteRegisterFromUnsigned(r2_reg_info, toc_addr)) return false; diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp index 62663974134b0..7f3a638d5b028 100644 --- a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp @@ -18,7 +18,7 @@ #include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" -#if defined(__AIX__) +#if defined(_AIX) #include #endif @@ -160,7 +160,7 @@ void DynamicLoaderAIXDYLD::DidAttach() { auto error = m_process->LoadModules(); LLDB_LOG_ERROR(log, std::move(error), "failed to load modules: {0}"); -#if defined(__AIX__) +#if defined(_AIX) // Get struct ld_xinfo (FIXME) struct ld_xinfo ldinfo[64]; Status status = m_process->GetLDXINFO(&(ldinfo[0])); @@ -221,7 +221,7 @@ void DynamicLoaderAIXDYLD::DidLaunch() { LLDB_LOG_ERROR(log, std::move(error), "failed to load modules: {0}"); } -#if defined(__AIX__) +#if defined(_AIX) // Get struct ld_xinfo (FIXME) struct ld_xinfo ldinfo[64]; Status status = m_process->GetLDXINFO(&(ldinfo[0])); diff --git a/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h b/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h index 1576c9700e557..d98b2880ca3b4 100644 --- a/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h +++ b/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h @@ -39,7 +39,7 @@ class EmulateInstructionPPC64 : public EmulateInstruction { return true; case eInstructionTypePCModifying: -#if defined(__AIX__) +#if defined(_AIX) return true; #else return false; diff --git a/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp b/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp index 690fb0d60a09a..9a52fb2f2adc5 100644 --- a/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp +++ b/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp @@ -194,7 +194,7 @@ void JITLoaderGDB::SetJITBreakpoint(lldb_private::ModuleList &module_list) { if (jit_addr == LLDB_INVALID_ADDRESS) return; -#if defined(__AIX__) +#if defined(_AIX) return; #endif diff --git a/lldb/source/Plugins/Language/ObjC/Cocoa.cpp b/lldb/source/Plugins/Language/ObjC/Cocoa.cpp index fb5bc2c58e6fb..71f2b127afb12 100644 --- a/lldb/source/Plugins/Language/ObjC/Cocoa.cpp +++ b/lldb/source/Plugins/Language/ObjC/Cocoa.cpp @@ -1227,7 +1227,7 @@ bool lldb_private::formatters::ObjCSELSummaryProvider( time_t lldb_private::formatters::GetOSXEpoch() { static time_t epoch = 0; if (!epoch) { -#if !defined(__AIX__) +#if !defined(_AIX) #ifndef _WIN32 tzset(); tm tm_epoch; diff --git a/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp index 5ea55772c3aba..4f747ab20c9ef 100644 --- a/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp +++ b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp @@ -8,7 +8,7 @@ #include "ObjectContainerBSDArchive.h" -#if defined(_WIN32) || defined(__ANDROID__) || defined(__AIX__) +#if defined(_WIN32) || defined(__ANDROID__) || defined(_AIX) // Defines from ar, missing on Windows #define SARMAG 8 #define ARFMAG "`\n" diff --git a/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.cpp b/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.cpp index 050ad73f1d19a..38756a0dd2969 100644 --- a/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.cpp +++ b/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.cpp @@ -8,7 +8,7 @@ #include "ObjectContainerBigArchive.h" -#if defined(_WIN32) || defined(__ANDROID__) || defined(__AIX__) +#if defined(_WIN32) || defined(__ANDROID__) || defined(_AIX) // Defines from ar, missing on Windows #define ARMAG "!\n" #define SARMAG 8 diff --git a/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp b/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp index d8834af2c33ef..9aab76c6c48ba 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp +++ b/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp @@ -51,7 +51,7 @@ size_t ObjectFileMinidump::GetModuleSpecifications( const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp, lldb::offset_t data_offset, lldb::offset_t file_offset, lldb::offset_t length, lldb_private::ModuleSpecList &specs) { -#if !defined(__AIX__) +#if !defined(_AIX) specs.Clear(); #endif return 0; diff --git a/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp b/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp index 75cc54e4f0d48..d76d6adb1be2c 100644 --- a/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp +++ b/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp @@ -117,7 +117,7 @@ size_t ObjectFilePDB::GetModuleSpecifications( llvm::BumpPtrAllocator allocator; std::unique_ptr pdb_file = loadPDBFile(file.GetPath(), allocator); if (!pdb_file){ -#if !defined(__AIX__) +#if !defined(_AIX) return initial_count; #else return specs.GetSize() - initial_count; @@ -127,7 +127,7 @@ size_t ObjectFilePDB::GetModuleSpecifications( auto info_stream = pdb_file->getPDBInfoStream(); if (!info_stream) { llvm::consumeError(info_stream.takeError()); -#if !defined(__AIX__) +#if !defined(_AIX) return initial_count; #else return specs.GetSize() - initial_count; @@ -136,7 +136,7 @@ size_t ObjectFilePDB::GetModuleSpecifications( auto dbi_stream = pdb_file->getPDBDbiStream(); if (!dbi_stream) { llvm::consumeError(dbi_stream.takeError()); -#if !defined(__AIX__) +#if !defined(_AIX) return initial_count; #else return specs.GetSize() - initial_count; diff --git a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp index 519ce2ca4a0b2..02a86234bd363 100644 --- a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp @@ -254,7 +254,7 @@ size_t ObjectFilePECOFF::GetModuleSpecifications( lldb::offset_t length, lldb_private::ModuleSpecList &specs) { const size_t initial_count = specs.GetSize(); if (!data_sp || !ObjectFilePECOFF::MagicBytesMatch(data_sp)){ -#if !defined(__AIX__) +#if !defined(_AIX) return initial_count; #else return specs.GetSize() - initial_count; @@ -272,7 +272,7 @@ size_t ObjectFilePECOFF::GetModuleSpecifications( if (!binary) { LLDB_LOG_ERROR(log, binary.takeError(), "Failed to create binary for file ({1}): {0}", file); -#if !defined(__AIX__) +#if !defined(_AIX) return initial_count; #else return specs.GetSize() - initial_count; @@ -281,7 +281,7 @@ size_t ObjectFilePECOFF::GetModuleSpecifications( auto *COFFObj = llvm::dyn_cast(binary->get()); if (!COFFObj){ -#if !defined(__AIX__) +#if !defined(_AIX) return initial_count; #else return specs.GetSize() - initial_count; diff --git a/lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp b/lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp index b6b08b73bec41..5c94477002978 100644 --- a/lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp +++ b/lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp @@ -31,7 +31,7 @@ // Define these constants from AIX mman.h for use when targeting remote aix // systems even when host has different values. -#if defined(__AIX__) +#if defined(_AIX) #include #endif @@ -80,7 +80,7 @@ void PlatformAIX::Initialize() { PlatformPOSIX::Initialize(); if (g_initialize_count++ == 0) { -#if defined(__AIX__) +#if defined(_AIX) PlatformSP default_platform_sp(new PlatformAIX(true)); default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); Platform::SetHostPlatform(default_platform_sp); @@ -294,7 +294,7 @@ MmapArgList PlatformAIX::GetMmapArgumentList(const ArchSpec &arch, addr_t addr, addr_t length, unsigned prot, unsigned flags, addr_t fd, addr_t offset) { -#if defined(__AIX__) +#if defined(_AIX) unsigned flags_platform = MAP_VARIABLE | MAP_PRIVATE | MAP_ANONYMOUS; #else unsigned flags_platform = 0; diff --git a/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp b/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp index db271357d792a..ea758caa653a1 100644 --- a/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp +++ b/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp @@ -46,7 +46,7 @@ bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr, function_options.include_inlines = false; SymbolContextList sc_list; -#if !defined(__AIX__) +#if !defined(_AIX) process->GetTarget().GetImages().FindFunctions( ConstString("mmap"), eFunctionNameTypeFull, function_options, sc_list); #else @@ -122,7 +122,7 @@ bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr, MmapArgList args = process->GetTarget().GetPlatform()->GetMmapArgumentList( arch, addr, length, prot_arg, flags, fd, offset); -#if defined(__AIX__) +#if defined(_AIX) lldb::ThreadPlanSP call_plan_sp( new ThreadPlanCallFunction(*thread, mmap_range.GetBaseAddress(), toc_range.GetBaseAddress(), diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index 443b7c7b2c7fb..fa0a3b5d4dc38 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -41,7 +41,7 @@ #include "llvm/Config/llvm-config.h" // for LLVM_ENABLE_ZLIB #include "llvm/Support/JSON.h" -#if defined(__AIX__) +#if defined(_AIX) #include #endif @@ -1715,7 +1715,7 @@ Status GDBRemoteCommunicationClient::GetMemoryRegionInfo( return error; } -#if defined(__AIX__) +#if defined(_AIX) Status GDBRemoteCommunicationClient::GetLDXINFO(struct ld_xinfo *info_ptr) { Status error; diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h index 520f37ac56716..1812fc9b7ca65 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -32,7 +32,7 @@ #include "llvm/Support/VersionTuple.h" -#if defined(__AIX__) +#if defined(_AIX) struct ld_xinfo; #endif @@ -200,7 +200,7 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase { Status GetMemoryRegionInfo(lldb::addr_t addr, MemoryRegionInfo &range_info); std::optional GetWatchpointSlotCount(); -#if defined(__AIX__) +#if defined(_AIX) Status GetLDXINFO(struct ld_xinfo *info_ptr); #endif diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp index 4f1ef0898ba08..27be61a474238 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -48,7 +48,7 @@ #include "ProcessGDBRemote.h" #include "ProcessGDBRemoteLog.h" #include "lldb/Utility/StringExtractorGDBRemote.h" -#if defined(__AIX__) +#if defined(_AIX) #include #endif @@ -3011,7 +3011,7 @@ GDBRemoteCommunicationServerLLGS::Handle_qLDXINFO(StringExtractorGDBRemote &pack return SendErrorResponse(0xff); } -#if defined(__AIX__) +#if defined(_AIX) // FIXME: buffer size struct ld_xinfo info[64]; if (ptrace64(PT_LDXINFO, m_current_process->GetID(), (long long)&(info[0]), sizeof(info), nullptr) != 0) { diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index ca381290d0e9f..5b7ce5f1424d9 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -92,7 +92,7 @@ #include "llvm/Support/Threading.h" #include "llvm/Support/raw_ostream.h" -#if defined(__AIX__) +#if defined(_AIX) #include #endif @@ -2963,7 +2963,7 @@ Status ProcessGDBRemote::DoGetMemoryRegionInfo(addr_t load_addr, return error; } -#if defined(__AIX__) +#if defined(_AIX) Status ProcessGDBRemote::DoGetLDXINFO(struct ld_xinfo *info_ptr) { Status error(m_gdb_comm.GetLDXINFO(info_ptr)); return error; diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index 82200fbea21cd..2bf3a04d213d4 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -37,7 +37,7 @@ #include "GDBRemoteCommunicationClient.h" #include "GDBRemoteRegisterContext.h" -#if defined(__AIX__) +#if defined(_AIX) struct ld_xinfo; #endif @@ -427,7 +427,7 @@ class ProcessGDBRemote : public Process, Status DoGetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo ®ion_info) override; -#if defined(__AIX__) +#if defined(_AIX) Status DoGetLDXINFO(struct ld_xinfo *info_ptr) override; #endif diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index a9aef7ef21855..e6ae7fc559ef4 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -75,7 +75,7 @@ #include "lldb/Utility/State.h" #include "lldb/Utility/Timer.h" -#if defined(__AIX__) +#if defined(_AIX) #include #endif @@ -6188,7 +6188,7 @@ Status Process::GetMemoryRegionInfo(lldb::addr_t load_addr, return DoGetMemoryRegionInfo(load_addr, range_info); } -#if defined(__AIX__) +#if defined(_AIX) Status Process::GetLDXINFO(struct ld_xinfo *info_ptr) { return DoGetLDXINFO(info_ptr); } diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index fbdbc8c63a5d0..fdf269a3d3653 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -40,7 +40,7 @@ #include #include -#ifdef __AIX__ +#ifdef _AIX #include "Plugins/Process/Utility/lldb-ppc64le-register-enums.h" #endif @@ -1260,7 +1260,7 @@ bool RegisterContextUnwind::IsTrapHandlerSymbol( // Answer the question: Where did THIS frame save the CALLER frame ("previous" // frame)'s register value? -#ifdef __AIX__ +#ifdef _AIX extern bool UGLY_HACK_NULL_TOPFRAME; #endif @@ -1525,7 +1525,7 @@ RegisterContextUnwind::SavedLocationForRegister( new_regloc.type = UnwindLLDB::ConcreteRegisterLocation::eRegisterInLiveRegisterContext; new_regloc.location.register_number = regnum.GetAsKind(eRegisterKindLLDB); -#ifdef __AIX__ +#ifdef _AIX if (UGLY_HACK_NULL_TOPFRAME && new_regloc.location.register_number == 0x20) { new_regloc.location.register_number = 0x24; } @@ -2390,7 +2390,7 @@ bool RegisterContextUnwind::ReadPC(addr_t &pc) { } } -#ifdef __AIX__ +#ifdef _AIX bool RegisterContextUnwind::ReadLR(addr_t &lr) { if (!IsValid()) return false; diff --git a/lldb/source/Target/UnwindLLDB.cpp b/lldb/source/Target/UnwindLLDB.cpp index 8edf359cac497..764bea5bf86c6 100644 --- a/lldb/source/Target/UnwindLLDB.cpp +++ b/lldb/source/Target/UnwindLLDB.cpp @@ -68,7 +68,7 @@ uint32_t UnwindLLDB::DoGetFrameCount() { return m_frames.size(); } -#ifdef __AIX__ +#ifdef _AIX bool UGLY_HACK_NULL_TOPFRAME = false; #endif @@ -95,7 +95,7 @@ bool UnwindLLDB::AddFirstFrame() { if (!reg_ctx_sp->ReadPC(first_cursor_sp->start_pc)) goto unwind_done; -#ifdef __AIX__ +#ifdef _AIX lldb::addr_t lr; if (!reg_ctx_sp->ReadLR(lr)) goto unwind_done; diff --git a/lldb/tools/driver/Driver.cpp b/lldb/tools/driver/Driver.cpp index 45837503e8b73..d17ed77485d31 100644 --- a/lldb/tools/driver/Driver.cpp +++ b/lldb/tools/driver/Driver.cpp @@ -640,7 +640,7 @@ void sigwinch_handler(int signo) { } void sigint_handler(int signo) { -#if defined(_WIN32) || defined(__AIX__) // Restore handler as it is not persistent on Windows +#if defined(_WIN32) || defined(_AIX) // Restore handler as it is not persistent on Windows signal(SIGINT, sigint_handler); #endif static std::atomic_flag g_interrupt_sent = ATOMIC_FLAG_INIT; @@ -729,7 +729,7 @@ static void printHelp(LLDBOptTable &table, llvm::StringRef tool_name) { int main(int argc, char const *argv[]) { // Editline uses for example iswprint which is dependent on LC_CTYPE. // FIXME: this caused unexpected SIGTRAP on AIX -#ifndef __AIX__ +#ifndef _AIX std::setlocale(LC_ALL, ""); std::setlocale(LC_CTYPE, ""); #endif diff --git a/lldb/tools/lldb-server/SystemInitializerLLGS.cpp b/lldb/tools/lldb-server/SystemInitializerLLGS.cpp index 91bb2083a88b5..52c2eae0c9033 100644 --- a/lldb/tools/lldb-server/SystemInitializerLLGS.cpp +++ b/lldb/tools/lldb-server/SystemInitializerLLGS.cpp @@ -14,7 +14,7 @@ using HostObjectFile = ObjectFileMachO; #elif defined(_WIN32) #include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h" using HostObjectFile = ObjectFilePECOFF; -#elif defined(__AIX__) +#elif defined(_AIX) #include "Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.h" using HostObjectFile = ObjectFileXCOFF; #else @@ -49,7 +49,7 @@ using HostObjectFile = ObjectFileELF; #include "Plugins/Instruction/MIPS/EmulateInstructionMIPS.h" #endif -#if defined(__AIX__) +#if defined(_AIX) #include "Plugins/Instruction/PPC64/EmulateInstructionPPC64.h" #endif @@ -82,7 +82,7 @@ llvm::Error SystemInitializerLLGS::Initialize() { EmulateInstructionRISCV::Initialize(); #endif -#if defined(__AIX__) +#if defined(_AIX) EmulateInstructionPPC64::Initialize(); #endif @@ -108,7 +108,7 @@ void SystemInitializerLLGS::Terminate() { EmulateInstructionRISCV::Terminate(); #endif -#if defined(__AIX__) +#if defined(_AIX) EmulateInstructionPPC64::Terminate(); #endif diff --git a/lldb/tools/lldb-server/lldb-gdbserver.cpp b/lldb/tools/lldb-server/lldb-gdbserver.cpp index 844a6370bdb2e..dcbb421a73e25 100644 --- a/lldb/tools/lldb-server/lldb-gdbserver.cpp +++ b/lldb/tools/lldb-server/lldb-gdbserver.cpp @@ -45,7 +45,7 @@ #include "Plugins/Process/NetBSD/NativeProcessNetBSD.h" #elif defined(_WIN32) #include "Plugins/Process/Windows/Common/NativeProcessWindows.h" -#elif defined(__AIX__) +#elif defined(_AIX) #include "Plugins/Process/AIX/NativeProcessAIX.h" #endif @@ -72,7 +72,7 @@ typedef process_freebsd::NativeProcessFreeBSD::Manager NativeProcessManager; typedef process_netbsd::NativeProcessNetBSD::Manager NativeProcessManager; #elif defined(_WIN32) typedef NativeProcessWindows::Manager NativeProcessManager; -#elif defined(__AIX__) +#elif defined(_AIX) typedef process_aix::NativeProcessAIX::Manager NativeProcessManager; #else // Dummy implementation to make sure the code compiles diff --git a/lldb/unittests/Host/MainLoopTest.cpp b/lldb/unittests/Host/MainLoopTest.cpp index c76476c947054..5c042261b9ef2 100644 --- a/lldb/unittests/Host/MainLoopTest.cpp +++ b/lldb/unittests/Host/MainLoopTest.cpp @@ -223,7 +223,7 @@ TEST_F(MainLoopTest, PendingCallbackAfterLoopExited) { loop.AddPendingCallback([&](MainLoopBase &loop) {}); } -#if defined(LLVM_ON_UNIX) && !defined(__AIX__) +#if defined(LLVM_ON_UNIX) && !defined(_AIX) TEST_F(MainLoopTest, DetectsEOF) { PseudoTerminal term; diff --git a/lldb/unittests/Host/PipeTest.cpp b/lldb/unittests/Host/PipeTest.cpp index c1013aa7a7e4e..00ffd33d68f7a 100644 --- a/lldb/unittests/Host/PipeTest.cpp +++ b/lldb/unittests/Host/PipeTest.cpp @@ -55,7 +55,7 @@ TEST_F(PipeTest, OpenAsReader) { } #endif -#if !defined(__AIX__) +#if !defined(_AIX) TEST_F(PipeTest, WriteWithTimeout) { Pipe pipe; ASSERT_THAT_ERROR(pipe.CreateNew(false).ToError(), llvm::Succeeded()); diff --git a/lldb/unittests/Host/posix/TerminalTest.cpp b/lldb/unittests/Host/posix/TerminalTest.cpp index f3de92c0852b1..64e6be64db80c 100644 --- a/lldb/unittests/Host/posix/TerminalTest.cpp +++ b/lldb/unittests/Host/posix/TerminalTest.cpp @@ -94,14 +94,14 @@ TEST_F(TerminalTest, SetRaw) { TEST_F(TerminalTest, SetBaudRate) { struct termios terminfo; -#if (defined(__AIX__) && defined(B38400)) || !defined(__AIX__) +#if (defined(_AIX) && defined(B38400)) || !defined(_AIX) ASSERT_THAT_ERROR(m_term.SetBaudRate(38400), llvm::Succeeded()); ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0); EXPECT_EQ(cfgetispeed(&terminfo), static_cast(B38400)); EXPECT_EQ(cfgetospeed(&terminfo), static_cast(B38400)); #endif -#if (defined(__AIX__) && defined(B115200)) || !defined(__AIX__) +#if (defined(_AIX) && defined(B115200)) || !defined(_AIX) ASSERT_THAT_ERROR(m_term.SetBaudRate(115200), llvm::Succeeded()); ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0); EXPECT_EQ(cfgetispeed(&terminfo), static_cast(B115200)); >From a8020a6a8692f059679195ae1a0ef5e0eeee94c8 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Thu, 10 Oct 2024 04:52:08 -0500 Subject: [PATCH 11/47] Removed from lldb/CMakeLists --- lldb/CMakeLists.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lldb/CMakeLists.txt b/lldb/CMakeLists.txt index a4fd8bccf056d..59cdc4593463c 100644 --- a/lldb/CMakeLists.txt +++ b/lldb/CMakeLists.txt @@ -38,10 +38,6 @@ endif() include(LLDBConfig) include(AddLLDB) -if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX") - add_definitions("-D_AIX") -endif() - # Define the LLDB_CONFIGURATION_xxx matching the build type. if(uppercase_CMAKE_BUILD_TYPE STREQUAL "DEBUG" ) add_definitions(-DLLDB_CONFIGURATION_DEBUG) >From 7609ad339bfab48412221be54edc2d2d146279c3 Mon Sep 17 00:00:00 2001 From: Lakshmi-Surekha Date: Thu, 14 Nov 2024 13:23:59 -0600 Subject: [PATCH 12/47] Patch for the Merge conflict of xcoff first merge with llvm --- .../ObjectFile/XCOFF/ObjectFileXCOFF.cpp | 253 +++++++++++++++++- 1 file changed, 252 insertions(+), 1 deletion(-) diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp index 395a126a01fce..a4d9ea295b4c3 100644 --- a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp @@ -188,7 +188,258 @@ bool ObjectFileXCOFF::ParseHeader() { if (module_sp) { std::lock_guard guard(module_sp->GetMutex()); m_sect_headers.clear(); - lldb::offs + lldb::offset_t offset = 0; + + if (ParseXCOFFHeader(m_data, &offset, m_xcoff_header)) { + m_data.SetAddressByteSize(GetAddressByteSize()); + if (m_xcoff_header.auxhdrsize > 0) + ParseXCOFFOptionalHeader(m_data, &offset); + ParseSectionHeaders(offset); + } + return true; + } + + return false; +} + +bool ObjectFileXCOFF::ParseXCOFFHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset_ptr, + xcoff_header_t &xcoff_header) { + //FIXME: data.ValidOffsetForDataOfSize + xcoff_header.magic = data.GetU16(offset_ptr); + xcoff_header.nsects = data.GetU16(offset_ptr); + xcoff_header.modtime = data.GetU32(offset_ptr); + xcoff_header.symoff = data.GetU64(offset_ptr); + xcoff_header.auxhdrsize = data.GetU16(offset_ptr); + xcoff_header.flags = data.GetU16(offset_ptr); + xcoff_header.nsyms = data.GetU32(offset_ptr); + return true; +} + +bool ObjectFileXCOFF::ParseXCOFFOptionalHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset_ptr) { + lldb::offset_t init_offset = *offset_ptr; + //FIXME: data.ValidOffsetForDataOfSize + m_xcoff_aux_header.AuxMagic = data.GetU16(offset_ptr); + m_xcoff_aux_header.Version = data.GetU16(offset_ptr); + m_xcoff_aux_header.ReservedForDebugger = data.GetU32(offset_ptr); + m_xcoff_aux_header.TextStartAddr = data.GetU64(offset_ptr); + m_xcoff_aux_header.DataStartAddr = data.GetU64(offset_ptr); + m_xcoff_aux_header.TOCAnchorAddr = data.GetU64(offset_ptr); + m_xcoff_aux_header.SecNumOfEntryPoint = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfText = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfData = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfTOC = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfLoader = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfBSS = data.GetU16(offset_ptr); + m_xcoff_aux_header.MaxAlignOfText = data.GetU16(offset_ptr); + m_xcoff_aux_header.MaxAlignOfData = data.GetU16(offset_ptr); + m_xcoff_aux_header.ModuleType = data.GetU16(offset_ptr); + m_xcoff_aux_header.CpuFlag = data.GetU8(offset_ptr); + m_xcoff_aux_header.CpuType = data.GetU8(offset_ptr); + m_xcoff_aux_header.TextPageSize = data.GetU8(offset_ptr); + m_xcoff_aux_header.DataPageSize = data.GetU8(offset_ptr); + m_xcoff_aux_header.StackPageSize = data.GetU8(offset_ptr); + m_xcoff_aux_header.FlagAndTDataAlignment = data.GetU8(offset_ptr); + m_xcoff_aux_header.TextSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.InitDataSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.BssDataSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.EntryPointAddr = data.GetU64(offset_ptr); + m_xcoff_aux_header.MaxStackSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.MaxDataSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.SecNumOfTData = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfTBSS = data.GetU16(offset_ptr); + m_xcoff_aux_header.XCOFF64Flag = data.GetU16(offset_ptr); + lldb::offset_t last_offset = *offset_ptr; + if ((last_offset - init_offset) < m_xcoff_header.auxhdrsize) + *offset_ptr += (m_xcoff_header.auxhdrsize - (last_offset - init_offset)); + return true; +} + +bool ObjectFileXCOFF::ParseSectionHeaders( + uint32_t section_header_data_offset) { + const uint32_t nsects = m_xcoff_header.nsects; + m_sect_headers.clear(); + + if (nsects > 0) { + const size_t section_header_byte_size = nsects * m_binary->getSectionHeaderSize(); + lldb_private::DataExtractor section_header_data = + ReadImageData(section_header_data_offset, section_header_byte_size); + + lldb::offset_t offset = 0; + //FIXME: section_header_data.ValidOffsetForDataOfSize + m_sect_headers.resize(nsects); + + for (uint32_t idx = 0; idx < nsects; ++idx) { + const void *name_data = section_header_data.GetData(&offset, 8); + if (name_data) { + memcpy(m_sect_headers[idx].name, name_data, 8); + m_sect_headers[idx].phyaddr = section_header_data.GetU64(&offset); + m_sect_headers[idx].vmaddr = section_header_data.GetU64(&offset); + m_sect_headers[idx].size = section_header_data.GetU64(&offset); + m_sect_headers[idx].offset = section_header_data.GetU64(&offset); + m_sect_headers[idx].reloff = section_header_data.GetU64(&offset); + m_sect_headers[idx].lineoff = section_header_data.GetU64(&offset); + m_sect_headers[idx].nreloc = section_header_data.GetU32(&offset); + m_sect_headers[idx].nline = section_header_data.GetU32(&offset); + m_sect_headers[idx].flags = section_header_data.GetU32(&offset); + offset += 4; + } else { + offset += (m_binary->getSectionHeaderSize() - 8); + } + } + } + + return !m_sect_headers.empty(); +} + +lldb_private::DataExtractor ObjectFileXCOFF::ReadImageData(uint32_t offset, size_t size) { + if (!size) + return {}; + + if (m_data.ValidOffsetForDataOfSize(offset, size)) + return lldb_private::DataExtractor(m_data, offset, size); + + assert(0); + ProcessSP process_sp(m_process_wp.lock()); + lldb_private::DataExtractor data; + if (process_sp) { + auto data_up = std::make_unique(size, 0); + Status readmem_error; + size_t bytes_read = + process_sp->ReadMemory(offset, data_up->GetBytes(), + data_up->GetByteSize(), readmem_error); + if (bytes_read == size) { + DataBufferSP buffer_sp(data_up.release()); + data.SetData(buffer_sp, 0, buffer_sp->GetByteSize()); + } + } + return data; +} + +bool ObjectFileXCOFF::SetLoadAddress(Target &target, lldb::addr_t value, + bool value_is_offset) { + bool changed = false; + ModuleSP module_sp = GetModule(); + if (module_sp) { + size_t num_loaded_sections = 0; + SectionList *section_list = GetSectionList(); + if (section_list) { + const size_t num_sections = section_list->GetSize(); + size_t sect_idx = 0; + + for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) { + // Iterate through the object file sections to find all of the sections + // that have SHF_ALLOC in their flag bits. + SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); + if (section_sp && !section_sp->IsThreadSpecific()) { + bool use_offset = false; + if (strcmp(section_sp->GetName().AsCString(), ".text") == 0 || + strcmp(section_sp->GetName().AsCString(), ".data") == 0 || + strcmp(section_sp->GetName().AsCString(), ".bss") == 0) + use_offset = true; + + if (target.GetSectionLoadList().SetSectionLoadAddress( + section_sp, (use_offset ? + (section_sp->GetFileOffset() + value) : (section_sp->GetFileAddress() + value)))) + ++num_loaded_sections; + } + } + changed = num_loaded_sections > 0; + } + } + return changed; +} + +bool ObjectFileXCOFF::SetLoadAddressByType(Target &target, lldb::addr_t value, + bool value_is_offset, int type_id) { + bool changed = false; + ModuleSP module_sp = GetModule(); + if (module_sp) { + size_t num_loaded_sections = 0; + SectionList *section_list = GetSectionList(); + if (section_list) { + const size_t num_sections = section_list->GetSize(); + size_t sect_idx = 0; + + for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) { + // Iterate through the object file sections to find all of the sections + // that have SHF_ALLOC in their flag bits. + SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); + if (type_id == 1 && section_sp && strcmp(section_sp->GetName().AsCString(), ".text") == 0) { + if (!section_sp->IsThreadSpecific()) { + if (target.GetSectionLoadList().SetSectionLoadAddress( + section_sp, section_sp->GetFileOffset() + value)) + ++num_loaded_sections; + } + } else if (type_id == 2 && section_sp && strcmp(section_sp->GetName().AsCString(), ".data") == 0) { + if (!section_sp->IsThreadSpecific()) { + if (target.GetSectionLoadList().SetSectionLoadAddress( + section_sp, section_sp->GetFileAddress() + value)) + ++num_loaded_sections; + } + } + } + changed = num_loaded_sections > 0; + } + } + return changed; +} + +ByteOrder ObjectFileXCOFF::GetByteOrder() const { + return eByteOrderBig; +} + +bool ObjectFileXCOFF::IsExecutable() const { + return true; +} + +uint32_t ObjectFileXCOFF::GetAddressByteSize() const { + if (m_xcoff_header.magic == XCOFF::XCOFF64) + return 8; + else if (m_xcoff_header.magic == XCOFF::XCOFF32) + return 4; + return 4; +} + +AddressClass ObjectFileXCOFF::GetAddressClass(addr_t file_addr) { + return AddressClass::eUnknown; +} + +lldb::SymbolType ObjectFileXCOFF::MapSymbolType(llvm::object::SymbolRef::Type sym_type) { + if (sym_type == llvm::object::SymbolRef::ST_Function) + return lldb::eSymbolTypeCode; + else if (sym_type == llvm::object::SymbolRef::ST_Data) + return lldb::eSymbolTypeData; + return lldb::eSymbolTypeInvalid; +} + +void ObjectFileXCOFF::ParseSymtab(Symtab &lldb_symtab) { + SectionList *sect_list = GetSectionList(); + const uint32_t num_syms = m_xcoff_header.nsyms; + uint32_t sidx = 0; + if (num_syms > 0 && m_xcoff_header.symoff > 0) { + const uint32_t symbol_size = XCOFF::SymbolTableEntrySize; + const size_t symbol_data_size = num_syms * symbol_size; + lldb_private::DataExtractor symtab_data = + ReadImageData(m_xcoff_header.symoff, symbol_data_size); + + lldb::offset_t offset = 0; + std::string symbol_name; + Symbol *symbols = lldb_symtab.Resize(num_syms); + llvm::object::symbol_iterator SI = m_binary->symbol_begin(); + for (uint32_t i = 0; i < num_syms; ++i, ++SI) { + xcoff_symbol_t symbol; + const uint32_t symbol_offset = offset; + symbol.value = symtab_data.GetU64(&offset); + symbol.offset = symtab_data.GetU32(&offset); + Expected symbol_name_or_err = m_binary->getStringTableEntry(symbol.offset); + if (!symbol_name_or_err) { + consumeError(symbol_name_or_err.takeError()); + return; + } + StringRef symbol_name_str = symbol_name_or_err.get(); + symbol_name.assign(symbol_name_str.data()); symbol.sect = symtab_data.GetU16(&offset); symbol.type = symtab_data.GetU16(&offset); symbol.storage = symtab_data.GetU8(&offset); >From dd56fce276b60b40e1997292b3f554a20157661a Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Sun, 17 Nov 2024 00:15:01 -0600 Subject: [PATCH 13/47] Attach fix for AIX --- .../AIX-DYLD/DynamicLoaderAIXDYLD.cpp | 130 ++++++++++++++++++ .../AIX-DYLD/DynamicLoaderAIXDYLD.h | 5 +- 2 files changed, 134 insertions(+), 1 deletion(-) diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp index 7f3a638d5b028..acaa6a72edded 100644 --- a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp @@ -18,8 +18,13 @@ #include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" +#include "llvm/Support/FileSystem.h" #if defined(_AIX) #include +#include +#include +#include +#include #endif /*#include "llvm/ADT/Triple.h" @@ -131,14 +136,139 @@ bool DynamicLoaderAIXDYLD::NotifyBreakpointHit( lldb::user_id_t break_loc_id) { } + +void DynamicLoaderAIXDYLD::ResolveExecutableModule( + lldb::ModuleSP &module_sp) { + Log *log = GetLog(LLDBLog::DynamicLoader); + + if (m_process == nullptr) + return; + + auto &target = m_process->GetTarget(); + const auto platform_sp = target.GetPlatform(); + + ProcessInstanceInfo process_info; + if (!m_process->GetProcessInfo(process_info)) { + LLDB_LOGF(log, + "DynamicLoaderPOSIXDYLD::%s - failed to get process info for " + "pid %" PRIu64, + __FUNCTION__, m_process->GetID()); + return; + } + + char procinfo_path[64], exe_path[PATH_MAX], arg_buffer[8192]; + struct procsinfo64 procs_info; + int32long64_t pid = m_process->GetID(); + std::string proc_file = "/proc/" + std::to_string(pid) + "/psinfo"; + std::string cwd_link = "/proc/" + std::to_string(pid) + "/cwd"; + psinfo_t psinfo; + std::ifstream file(proc_file, std::ios::binary); + if(!file.is_open()) + { + LLDB_LOGF(log, "Error psinfo "); + } + file.read(reinterpret_cast(&psinfo), sizeof(psinfo_t)); + if(!file) + LLDB_LOGF(log, "Error psinfo: Failed to read "); + + std::string relative_path(psinfo.pr_fname); + LLDB_LOGF(log, "relative path %s",relative_path.c_str()); + + char cwd[PATH_MAX]; + char resolved_path[PATH_MAX]; + std::string executable_name; + bool found = 0; + if(readlink(cwd_link.c_str(), cwd, sizeof(cwd)) != -1){ + std::filesystem::path full_path = std::filesystem::path(cwd)/relative_path; + if(realpath(full_path.c_str(), resolved_path)) { + LLDB_LOGF(log, " RESOLVED PATH: %s", resolved_path); + found = 1; + } + else + perror("realpath error");} + + executable_name = resolved_path; + if(found == 0) { + std::string command_line(psinfo.pr_psargs); + LLDB_LOGF(log, "command line %s",command_line.c_str()); + if (!command_line.empty()) { + size_t space1 = command_line.find(' '); + executable_name = command_line.substr(0, space1); + LLDB_LOGF(log, "executable name %s",executable_name.c_str()); + } + } + + LLDB_LOGF(log, "executable name %s",executable_name.c_str()); + /*target.SetExecutableModule(target.GetOrCreateModule(lldb_private::FileSpec(resolved_path), + true),true);*/ + process_info.SetExecutableFile(lldb_private::FileSpec(executable_name), + true); + +/* snprintf(procinfo_path, sizeof(procinfo_path), "/proc/%d/object/a.out", pid); + ssize_t len = readlink(procinfo_path, exe_path, sizeof(exe_path) - 1); + exe_path[len] = '\0'; + int num_procs = getprocs64(&procs_info, sizeof(struct procsinfo64), NULL, 0, + &pid, + 1); + int result = getargs(pid, arg_buffer, sizeof(arg_buffer)); + std::vector args; + char *arg_start = arg_buffer; + while(*arg_start != '\0') { + args.emplace_back(arg_start); + arg_start += strlen(arg_start) + 1; + } + + LLDB_LOGF( + log, "1. DynamicLoaderPOSIXDYLD::%s - got executable by pid %" PRIu64 ": %s" + ", pid: %d, current_path: %s", + __FUNCTION__, m_process->GetID(), + args[0], pid, current_path.c_str()); + LLDB_LOGF( + log, "1. DynamicLoaderPOSIXDYLD::%s - got executable by pid %" PRIu64 ": %s" + "num_procs: %d, pid: %d", + __FUNCTION__, m_process->GetID(), + std::string(procs_info.pi_comm).c_str(), num_procs, pid); + if(num_procs <= 0) + perror("getprocs64 failed"); */ + + LLDB_LOGF( + log, "DynamicLoaderPOSIXDYLD::%s - got executable by pid %" PRIu64 ": %s", + __FUNCTION__, m_process->GetID(), + process_info.GetExecutableFile().GetPath().c_str()); + + ModuleSpec module_spec(process_info.GetExecutableFile(), + process_info.GetArchitecture()); + if (module_sp && module_sp->MatchesModuleSpec(module_spec)) + return; + + const auto executable_search_paths(Target::GetDefaultExecutableSearchPaths()); + auto error = platform_sp->ResolveExecutable( + module_spec, module_sp, + !executable_search_paths.IsEmpty() ? &executable_search_paths : nullptr); + if (error.Fail()) { + StreamString stream; + module_spec.Dump(stream); + + LLDB_LOGF(log, + "DynamicLoaderPOSIXDYLD::%s - failed to resolve executable " + "with module spec \"%s\": %s", + __FUNCTION__, stream.GetData(), error.AsCString()); + return; + } + + target.SetExecutableModule(module_sp, eLoadDependentsNo); +} + void DynamicLoaderAIXDYLD::DidAttach() { Log *log = GetLog(LLDBLog::DynamicLoader); LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); ModuleSP executable = GetTargetExecutable(); + ResolveExecutableModule(executable); if (!executable.get()) return; + LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); // Try to fetch the load address of the file from the process, since there // could be randomization of the load address. diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h index ae4b7aca66dcc..0ffbe688e0069 100644 --- a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h @@ -24,7 +24,7 @@ class DynamicLoaderAIXDYLD : public DynamicLoader { static void Initialize(); static void Terminate(); - static llvm::StringRef GetPluginNameStatic() { return "windows-dyld"; } + static llvm::StringRef GetPluginNameStatic() { return "aix-dyld"; } static llvm::StringRef GetPluginDescriptionStatic(); static DynamicLoader *CreateInstance(Process *process, bool force); @@ -46,6 +46,9 @@ class DynamicLoaderAIXDYLD : public DynamicLoader { protected: lldb::addr_t GetLoadAddress(lldb::ModuleSP executable); + /// Loads Module from inferior process. + void ResolveExecutableModule(lldb::ModuleSP &module_sp); + private: std::map m_loaded_modules; }; >From 48b8b1b6532181acab0ee1710d5f4ab92903ae78 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Mon, 18 Nov 2024 02:56:31 -0600 Subject: [PATCH 14/47] Cleanup --- .../AIX-DYLD/DynamicLoaderAIXDYLD.cpp | 65 +++++-------------- 1 file changed, 17 insertions(+), 48 deletions(-) diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp index acaa6a72edded..1a98bb9334043 100644 --- a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp @@ -156,81 +156,50 @@ void DynamicLoaderAIXDYLD::ResolveExecutableModule( return; } - char procinfo_path[64], exe_path[PATH_MAX], arg_buffer[8192]; - struct procsinfo64 procs_info; int32long64_t pid = m_process->GetID(); + char cwd[PATH_MAX], resolved_path[PATH_MAX]; + std::string executable_name; + bool path_resolved = false; + psinfo_t psinfo; + std::string proc_file = "/proc/" + std::to_string(pid) + "/psinfo"; std::string cwd_link = "/proc/" + std::to_string(pid) + "/cwd"; - psinfo_t psinfo; std::ifstream file(proc_file, std::ios::binary); if(!file.is_open()) - { - LLDB_LOGF(log, "Error psinfo "); - } + LLDB_LOGF(log, "Error: Unable to access process info "); + file.read(reinterpret_cast(&psinfo), sizeof(psinfo_t)); if(!file) - LLDB_LOGF(log, "Error psinfo: Failed to read "); + LLDB_LOGF(log, "Process info error: Failed to read "); std::string relative_path(psinfo.pr_fname); - LLDB_LOGF(log, "relative path %s",relative_path.c_str()); + LLDB_LOGF(log, "Relative path %s",relative_path.c_str()); - char cwd[PATH_MAX]; - char resolved_path[PATH_MAX]; - std::string executable_name; - bool found = 0; if(readlink(cwd_link.c_str(), cwd, sizeof(cwd)) != -1){ std::filesystem::path full_path = std::filesystem::path(cwd)/relative_path; if(realpath(full_path.c_str(), resolved_path)) { - LLDB_LOGF(log, " RESOLVED PATH: %s", resolved_path); - found = 1; + LLDB_LOGF(log, "Resolved Path using process info : %s", resolved_path); + path_resolved = true; } else - perror("realpath error");} + LLDB_LOGF(log, "Realpath error: Unable to resolve. "); + } executable_name = resolved_path; - if(found == 0) { + if(path_resolved == false) { std::string command_line(psinfo.pr_psargs); - LLDB_LOGF(log, "command line %s",command_line.c_str()); + LLDB_LOGF(log, "Command line: %s",command_line.c_str()); if (!command_line.empty()) { size_t space1 = command_line.find(' '); executable_name = command_line.substr(0, space1); - LLDB_LOGF(log, "executable name %s",executable_name.c_str()); + LLDB_LOGF(log, "Resolved path using command line arg %s",executable_name.c_str()); } } - LLDB_LOGF(log, "executable name %s",executable_name.c_str()); - /*target.SetExecutableModule(target.GetOrCreateModule(lldb_private::FileSpec(resolved_path), - true),true);*/ + LLDB_LOGF(log, "Executable Name %s",executable_name.c_str()); process_info.SetExecutableFile(lldb_private::FileSpec(executable_name), true); -/* snprintf(procinfo_path, sizeof(procinfo_path), "/proc/%d/object/a.out", pid); - ssize_t len = readlink(procinfo_path, exe_path, sizeof(exe_path) - 1); - exe_path[len] = '\0'; - int num_procs = getprocs64(&procs_info, sizeof(struct procsinfo64), NULL, 0, - &pid, - 1); - int result = getargs(pid, arg_buffer, sizeof(arg_buffer)); - std::vector args; - char *arg_start = arg_buffer; - while(*arg_start != '\0') { - args.emplace_back(arg_start); - arg_start += strlen(arg_start) + 1; - } - - LLDB_LOGF( - log, "1. DynamicLoaderPOSIXDYLD::%s - got executable by pid %" PRIu64 ": %s" - ", pid: %d, current_path: %s", - __FUNCTION__, m_process->GetID(), - args[0], pid, current_path.c_str()); - LLDB_LOGF( - log, "1. DynamicLoaderPOSIXDYLD::%s - got executable by pid %" PRIu64 ": %s" - "num_procs: %d, pid: %d", - __FUNCTION__, m_process->GetID(), - std::string(procs_info.pi_comm).c_str(), num_procs, pid); - if(num_procs <= 0) - perror("getprocs64 failed"); */ - LLDB_LOGF( log, "DynamicLoaderPOSIXDYLD::%s - got executable by pid %" PRIu64 ": %s", __FUNCTION__, m_process->GetID(), >From d410734184a681b3e95949d3953142995682d7f6 Mon Sep 17 00:00:00 2001 From: Lakshmi-Surekha Date: Tue, 19 Nov 2024 09:44:42 -0600 Subject: [PATCH 15/47] Patch in MainLoopPosix.cpp for runtime issue --- lldb/source/Host/posix/MainLoopPosix.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/source/Host/posix/MainLoopPosix.cpp b/lldb/source/Host/posix/MainLoopPosix.cpp index f68268f114075..4c617cdde67ba 100644 --- a/lldb/source/Host/posix/MainLoopPosix.cpp +++ b/lldb/source/Host/posix/MainLoopPosix.cpp @@ -149,7 +149,7 @@ Status MainLoopPosix::RunImpl::Poll() { int timeout; timeout = -1; - pthread_sigmask(SIG_SETMASK, &sigmask, &origmask); + pthread_sigmask(SIG_SETMASK, nullptr, &origmask); int ready = poll(read_fds.data(), read_fds.size(), timeout); pthread_sigmask(SIG_SETMASK, &origmask, nullptr); if (ready == -1 && errno != EINTR) >From 48f39dadbbdb4874fbd9b6350933dc67e8823339 Mon Sep 17 00:00:00 2001 From: Lakshmi-Surekha Date: Thu, 5 Dec 2024 05:13:14 -0600 Subject: [PATCH 16/47] Patch for compilation failure in DomainSocket.cpp, AbstractSocket.cpp and AbstractSocket.h --- lldb/include/lldb/Host/aix/AbstractSocket.h | 2 +- lldb/source/Host/aix/AbstractSocket.cpp | 3 +-- lldb/source/Host/posix/DomainSocket.cpp | 4 ++++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lldb/include/lldb/Host/aix/AbstractSocket.h b/lldb/include/lldb/Host/aix/AbstractSocket.h index 78a567a6b9095..accfd01457a5e 100644 --- a/lldb/include/lldb/Host/aix/AbstractSocket.h +++ b/lldb/include/lldb/Host/aix/AbstractSocket.h @@ -14,7 +14,7 @@ namespace lldb_private { class AbstractSocket : public DomainSocket { public: - AbstractSocket(bool child_processes_inherit); + AbstractSocket(); protected: size_t GetNameOffset() const override; diff --git a/lldb/source/Host/aix/AbstractSocket.cpp b/lldb/source/Host/aix/AbstractSocket.cpp index bfb67d452f7ec..fddf78f54f46d 100644 --- a/lldb/source/Host/aix/AbstractSocket.cpp +++ b/lldb/source/Host/aix/AbstractSocket.cpp @@ -13,8 +13,7 @@ using namespace lldb; using namespace lldb_private; -AbstractSocket::AbstractSocket(bool child_processes_inherit) - : DomainSocket(ProtocolUnixAbstract, child_processes_inherit) {} +AbstractSocket::AbstractSocket() : DomainSocket(ProtocolUnixAbstract) {} size_t AbstractSocket::GetNameOffset() const { return 1; } diff --git a/lldb/source/Host/posix/DomainSocket.cpp b/lldb/source/Host/posix/DomainSocket.cpp index 9a0b385d998bf..6cbffb2d9c4bd 100644 --- a/lldb/source/Host/posix/DomainSocket.cpp +++ b/lldb/source/Host/posix/DomainSocket.cpp @@ -17,6 +17,10 @@ #include #include +#if defined(_AIX) +#include +#endif + using namespace lldb; using namespace lldb_private; >From 97531f7bf6e385f0f51d860c6eea17aeb32f6594 Mon Sep 17 00:00:00 2001 From: Lakshmi-Surekha Date: Thu, 19 Dec 2024 06:38:36 -0600 Subject: [PATCH 17/47] Patch for merge conflict in ObjectFileXCOFF.cpp & ObjectFileXCOFF.h --- .../ObjectFile/XCOFF/ObjectFileXCOFF.cpp | 253 +++++++++++++++++- 1 file changed, 252 insertions(+), 1 deletion(-) diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp index 395a126a01fce..a4d9ea295b4c3 100644 --- a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp @@ -188,7 +188,258 @@ bool ObjectFileXCOFF::ParseHeader() { if (module_sp) { std::lock_guard guard(module_sp->GetMutex()); m_sect_headers.clear(); - lldb::offs + lldb::offset_t offset = 0; + + if (ParseXCOFFHeader(m_data, &offset, m_xcoff_header)) { + m_data.SetAddressByteSize(GetAddressByteSize()); + if (m_xcoff_header.auxhdrsize > 0) + ParseXCOFFOptionalHeader(m_data, &offset); + ParseSectionHeaders(offset); + } + return true; + } + + return false; +} + +bool ObjectFileXCOFF::ParseXCOFFHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset_ptr, + xcoff_header_t &xcoff_header) { + //FIXME: data.ValidOffsetForDataOfSize + xcoff_header.magic = data.GetU16(offset_ptr); + xcoff_header.nsects = data.GetU16(offset_ptr); + xcoff_header.modtime = data.GetU32(offset_ptr); + xcoff_header.symoff = data.GetU64(offset_ptr); + xcoff_header.auxhdrsize = data.GetU16(offset_ptr); + xcoff_header.flags = data.GetU16(offset_ptr); + xcoff_header.nsyms = data.GetU32(offset_ptr); + return true; +} + +bool ObjectFileXCOFF::ParseXCOFFOptionalHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset_ptr) { + lldb::offset_t init_offset = *offset_ptr; + //FIXME: data.ValidOffsetForDataOfSize + m_xcoff_aux_header.AuxMagic = data.GetU16(offset_ptr); + m_xcoff_aux_header.Version = data.GetU16(offset_ptr); + m_xcoff_aux_header.ReservedForDebugger = data.GetU32(offset_ptr); + m_xcoff_aux_header.TextStartAddr = data.GetU64(offset_ptr); + m_xcoff_aux_header.DataStartAddr = data.GetU64(offset_ptr); + m_xcoff_aux_header.TOCAnchorAddr = data.GetU64(offset_ptr); + m_xcoff_aux_header.SecNumOfEntryPoint = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfText = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfData = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfTOC = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfLoader = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfBSS = data.GetU16(offset_ptr); + m_xcoff_aux_header.MaxAlignOfText = data.GetU16(offset_ptr); + m_xcoff_aux_header.MaxAlignOfData = data.GetU16(offset_ptr); + m_xcoff_aux_header.ModuleType = data.GetU16(offset_ptr); + m_xcoff_aux_header.CpuFlag = data.GetU8(offset_ptr); + m_xcoff_aux_header.CpuType = data.GetU8(offset_ptr); + m_xcoff_aux_header.TextPageSize = data.GetU8(offset_ptr); + m_xcoff_aux_header.DataPageSize = data.GetU8(offset_ptr); + m_xcoff_aux_header.StackPageSize = data.GetU8(offset_ptr); + m_xcoff_aux_header.FlagAndTDataAlignment = data.GetU8(offset_ptr); + m_xcoff_aux_header.TextSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.InitDataSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.BssDataSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.EntryPointAddr = data.GetU64(offset_ptr); + m_xcoff_aux_header.MaxStackSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.MaxDataSize = data.GetU64(offset_ptr); + m_xcoff_aux_header.SecNumOfTData = data.GetU16(offset_ptr); + m_xcoff_aux_header.SecNumOfTBSS = data.GetU16(offset_ptr); + m_xcoff_aux_header.XCOFF64Flag = data.GetU16(offset_ptr); + lldb::offset_t last_offset = *offset_ptr; + if ((last_offset - init_offset) < m_xcoff_header.auxhdrsize) + *offset_ptr += (m_xcoff_header.auxhdrsize - (last_offset - init_offset)); + return true; +} + +bool ObjectFileXCOFF::ParseSectionHeaders( + uint32_t section_header_data_offset) { + const uint32_t nsects = m_xcoff_header.nsects; + m_sect_headers.clear(); + + if (nsects > 0) { + const size_t section_header_byte_size = nsects * m_binary->getSectionHeaderSize(); + lldb_private::DataExtractor section_header_data = + ReadImageData(section_header_data_offset, section_header_byte_size); + + lldb::offset_t offset = 0; + //FIXME: section_header_data.ValidOffsetForDataOfSize + m_sect_headers.resize(nsects); + + for (uint32_t idx = 0; idx < nsects; ++idx) { + const void *name_data = section_header_data.GetData(&offset, 8); + if (name_data) { + memcpy(m_sect_headers[idx].name, name_data, 8); + m_sect_headers[idx].phyaddr = section_header_data.GetU64(&offset); + m_sect_headers[idx].vmaddr = section_header_data.GetU64(&offset); + m_sect_headers[idx].size = section_header_data.GetU64(&offset); + m_sect_headers[idx].offset = section_header_data.GetU64(&offset); + m_sect_headers[idx].reloff = section_header_data.GetU64(&offset); + m_sect_headers[idx].lineoff = section_header_data.GetU64(&offset); + m_sect_headers[idx].nreloc = section_header_data.GetU32(&offset); + m_sect_headers[idx].nline = section_header_data.GetU32(&offset); + m_sect_headers[idx].flags = section_header_data.GetU32(&offset); + offset += 4; + } else { + offset += (m_binary->getSectionHeaderSize() - 8); + } + } + } + + return !m_sect_headers.empty(); +} + +lldb_private::DataExtractor ObjectFileXCOFF::ReadImageData(uint32_t offset, size_t size) { + if (!size) + return {}; + + if (m_data.ValidOffsetForDataOfSize(offset, size)) + return lldb_private::DataExtractor(m_data, offset, size); + + assert(0); + ProcessSP process_sp(m_process_wp.lock()); + lldb_private::DataExtractor data; + if (process_sp) { + auto data_up = std::make_unique(size, 0); + Status readmem_error; + size_t bytes_read = + process_sp->ReadMemory(offset, data_up->GetBytes(), + data_up->GetByteSize(), readmem_error); + if (bytes_read == size) { + DataBufferSP buffer_sp(data_up.release()); + data.SetData(buffer_sp, 0, buffer_sp->GetByteSize()); + } + } + return data; +} + +bool ObjectFileXCOFF::SetLoadAddress(Target &target, lldb::addr_t value, + bool value_is_offset) { + bool changed = false; + ModuleSP module_sp = GetModule(); + if (module_sp) { + size_t num_loaded_sections = 0; + SectionList *section_list = GetSectionList(); + if (section_list) { + const size_t num_sections = section_list->GetSize(); + size_t sect_idx = 0; + + for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) { + // Iterate through the object file sections to find all of the sections + // that have SHF_ALLOC in their flag bits. + SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); + if (section_sp && !section_sp->IsThreadSpecific()) { + bool use_offset = false; + if (strcmp(section_sp->GetName().AsCString(), ".text") == 0 || + strcmp(section_sp->GetName().AsCString(), ".data") == 0 || + strcmp(section_sp->GetName().AsCString(), ".bss") == 0) + use_offset = true; + + if (target.GetSectionLoadList().SetSectionLoadAddress( + section_sp, (use_offset ? + (section_sp->GetFileOffset() + value) : (section_sp->GetFileAddress() + value)))) + ++num_loaded_sections; + } + } + changed = num_loaded_sections > 0; + } + } + return changed; +} + +bool ObjectFileXCOFF::SetLoadAddressByType(Target &target, lldb::addr_t value, + bool value_is_offset, int type_id) { + bool changed = false; + ModuleSP module_sp = GetModule(); + if (module_sp) { + size_t num_loaded_sections = 0; + SectionList *section_list = GetSectionList(); + if (section_list) { + const size_t num_sections = section_list->GetSize(); + size_t sect_idx = 0; + + for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) { + // Iterate through the object file sections to find all of the sections + // that have SHF_ALLOC in their flag bits. + SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); + if (type_id == 1 && section_sp && strcmp(section_sp->GetName().AsCString(), ".text") == 0) { + if (!section_sp->IsThreadSpecific()) { + if (target.GetSectionLoadList().SetSectionLoadAddress( + section_sp, section_sp->GetFileOffset() + value)) + ++num_loaded_sections; + } + } else if (type_id == 2 && section_sp && strcmp(section_sp->GetName().AsCString(), ".data") == 0) { + if (!section_sp->IsThreadSpecific()) { + if (target.GetSectionLoadList().SetSectionLoadAddress( + section_sp, section_sp->GetFileAddress() + value)) + ++num_loaded_sections; + } + } + } + changed = num_loaded_sections > 0; + } + } + return changed; +} + +ByteOrder ObjectFileXCOFF::GetByteOrder() const { + return eByteOrderBig; +} + +bool ObjectFileXCOFF::IsExecutable() const { + return true; +} + +uint32_t ObjectFileXCOFF::GetAddressByteSize() const { + if (m_xcoff_header.magic == XCOFF::XCOFF64) + return 8; + else if (m_xcoff_header.magic == XCOFF::XCOFF32) + return 4; + return 4; +} + +AddressClass ObjectFileXCOFF::GetAddressClass(addr_t file_addr) { + return AddressClass::eUnknown; +} + +lldb::SymbolType ObjectFileXCOFF::MapSymbolType(llvm::object::SymbolRef::Type sym_type) { + if (sym_type == llvm::object::SymbolRef::ST_Function) + return lldb::eSymbolTypeCode; + else if (sym_type == llvm::object::SymbolRef::ST_Data) + return lldb::eSymbolTypeData; + return lldb::eSymbolTypeInvalid; +} + +void ObjectFileXCOFF::ParseSymtab(Symtab &lldb_symtab) { + SectionList *sect_list = GetSectionList(); + const uint32_t num_syms = m_xcoff_header.nsyms; + uint32_t sidx = 0; + if (num_syms > 0 && m_xcoff_header.symoff > 0) { + const uint32_t symbol_size = XCOFF::SymbolTableEntrySize; + const size_t symbol_data_size = num_syms * symbol_size; + lldb_private::DataExtractor symtab_data = + ReadImageData(m_xcoff_header.symoff, symbol_data_size); + + lldb::offset_t offset = 0; + std::string symbol_name; + Symbol *symbols = lldb_symtab.Resize(num_syms); + llvm::object::symbol_iterator SI = m_binary->symbol_begin(); + for (uint32_t i = 0; i < num_syms; ++i, ++SI) { + xcoff_symbol_t symbol; + const uint32_t symbol_offset = offset; + symbol.value = symtab_data.GetU64(&offset); + symbol.offset = symtab_data.GetU32(&offset); + Expected symbol_name_or_err = m_binary->getStringTableEntry(symbol.offset); + if (!symbol_name_or_err) { + consumeError(symbol_name_or_err.takeError()); + return; + } + StringRef symbol_name_str = symbol_name_or_err.get(); + symbol_name.assign(symbol_name_str.data()); symbol.sect = symtab_data.GetU16(&offset); symbol.type = symtab_data.GetU16(&offset); symbol.storage = symtab_data.GetU8(&offset); >From 71d2fcff8975831e7f0a657481220749b0a473dc Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Tue, 24 Dec 2024 02:05:35 -0600 Subject: [PATCH 18/47] Added upcoming clang-format and other merge changes --- .../posix/ConnectionFileDescriptorPosix.cpp | 9 ++-- lldb/source/Host/posix/DomainSocket.cpp | 5 ++- lldb/source/Host/posix/FileSystemPosix.cpp | 2 +- lldb/source/Host/posix/MainLoopPosix.cpp | 43 ++++++++----------- .../Host/posix/ProcessLauncherPosixFork.cpp | 2 +- lldb/source/Plugins/Language/ObjC/Cocoa.cpp | 17 +++----- .../BSD-Archive/ObjectContainerBSDArchive.cpp | 29 +++++++------ lldb/source/Utility/ArchSpec.cpp | 1 - 8 files changed, 49 insertions(+), 59 deletions(-) diff --git a/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp b/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp index 32d034e60d26c..e3d1300cf76ed 100644 --- a/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp +++ b/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp @@ -119,8 +119,7 @@ bool ConnectionFileDescriptor::IsConnected() const { ConnectionStatus ConnectionFileDescriptor::Connect(llvm::StringRef path, Status *error_ptr) { - return Connect( - path, [](llvm::StringRef) {}, error_ptr); + return Connect(path, [](llvm::StringRef) {}, error_ptr); } ConnectionStatus @@ -716,8 +715,7 @@ ConnectionFileDescriptor::ConnectFD(llvm::StringRef s, ConnectionStatus ConnectionFileDescriptor::ConnectFile( llvm::StringRef s, socket_id_callback_type socket_id_callback, Status *error_ptr) { -#if !defined(_AIX) -#if LLDB_ENABLE_POSIX +#if LLDB_ENABLE_POSIX && !defined(_AIX) std::string addr_str = s.str(); // file:///PATH int fd = FileSystem::Instance().Open(addr_str.c_str(), O_RDWR); @@ -748,8 +746,7 @@ ConnectionStatus ConnectionFileDescriptor::ConnectFile( m_io_sp = std::make_shared(fd, File::eOpenOptionReadWrite, true); return eConnectionStatusSuccess; -#endif // LLDB_ENABLE_POSIX -#endif +#endif // LLDB_ENABLE_POSIX && !defined(_AIX) llvm_unreachable("this function should be only called w/ LLDB_ENABLE_POSIX"); } diff --git a/lldb/source/Host/posix/DomainSocket.cpp b/lldb/source/Host/posix/DomainSocket.cpp index 6cbffb2d9c4bd..28db5964a5a8a 100644 --- a/lldb/source/Host/posix/DomainSocket.cpp +++ b/lldb/source/Host/posix/DomainSocket.cpp @@ -89,8 +89,9 @@ Status DomainSocket::Connect(llvm::StringRef name) { m_socket = CreateSocket(kDomain, kType, 0, error); if (error.Fail()) return error; - if (llvm::sys::RetryAfterSignal(-1, ::connect, GetNativeSocket(), - (struct sockaddr *)&saddr_un, saddr_un_len) < 0) + if (llvm::sys::RetryAfterSignal(-1, ::connect, GetNativeSocket(), + (struct sockaddr *)&saddr_un, + saddr_un_len) < 0) SetLastError(error); return error; diff --git a/lldb/source/Host/posix/FileSystemPosix.cpp b/lldb/source/Host/posix/FileSystemPosix.cpp index 21da5612ff6b8..1a84f550662d7 100644 --- a/lldb/source/Host/posix/FileSystemPosix.cpp +++ b/lldb/source/Host/posix/FileSystemPosix.cpp @@ -11,7 +11,7 @@ // C includes #include #include -#if !defined(_AIX) +#ifndef _AIX #include #endif #include diff --git a/lldb/source/Host/posix/MainLoopPosix.cpp b/lldb/source/Host/posix/MainLoopPosix.cpp index 125b954023dc0..e4ff928a58962 100644 --- a/lldb/source/Host/posix/MainLoopPosix.cpp +++ b/lldb/source/Host/posix/MainLoopPosix.cpp @@ -99,6 +99,7 @@ class MainLoopPosix::RunImpl { ~RunImpl() = default; Status Poll(); + int StartPoll(std::optional point); void ProcessReadEvents(); private: @@ -159,6 +160,22 @@ MainLoopPosix::RunImpl::RunImpl(MainLoopPosix &loop) : loop(loop) { read_fds.reserve(loop.m_read_fds.size()); } +int MainLoopPosix::RunImpl::StartPoll( + std::optional point) { +#if HAVE_PPOLL + return ppoll(read_fds.data(), read_fds.size(), ToTimeSpec(point), + /*sigmask=*/nullptr); +#else + using namespace std::chrono; + int timeout = -1; + if (point) { + nanoseconds dur = std::max(*point - steady_clock::now(), nanoseconds(0)); + timeout = ceil(dur).count(); + } + return poll(read_fds.data(), read_fds.size(), timeout); +#endif +} + Status MainLoopPosix::RunImpl::Poll() { read_fds.clear(); @@ -169,24 +186,10 @@ Status MainLoopPosix::RunImpl::Poll() { pfd.revents = 0; read_fds.push_back(pfd); } + int ready = StartPoll(loop.GetNextWakeupTime()); -#if defined(_AIX) - sigset_t origmask; - int timeout; - - timeout = -1; - pthread_sigmask(SIG_SETMASK, nullptr, &origmask); - int ready = poll(read_fds.data(), read_fds.size(), timeout); - pthread_sigmask(SIG_SETMASK, &origmask, nullptr); if (ready == -1 && errno != EINTR) return Status(errno, eErrorTypePOSIX); -#else - if (ppoll(read_fds.data(), read_fds.size(), - ToTimeSpec(loop.GetNextWakeupTime()), - /*sigmask=*/nullptr) == -1 && - errno != EINTR) - return Status(errno, eErrorTypePOSIX); -#endif return Status(); } @@ -291,16 +294,6 @@ MainLoopPosix::RegisterSignal(int signo, const Callback &callback, UNUSED_IF_ASSERT_DISABLED(ret); assert(ret == 0 && "sigaction failed"); -#if HAVE_SYS_EVENT_H - struct kevent ev; - EV_SET(&ev, signo, EVFILT_SIGNAL, EV_ADD, 0, 0, 0); - ret = kevent(m_kqueue, &ev, 1, nullptr, 0, nullptr); - assert(ret == 0); -#endif - - // If we're using kqueue, the signal needs to be unblocked in order to - // receive it. If using pselect/ppoll, we need to block it, and later unblock - // it as a part of the system call. ret = pthread_sigmask(SIG_UNBLOCK, &new_action.sa_mask, &old_set); assert(ret == 0 && "pthread_sigmask failed"); info.was_blocked = sigismember(&old_set, signo); diff --git a/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp b/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp index 52fc58aa21bf4..7b8b42a4b7fe0 100644 --- a/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp +++ b/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp @@ -197,7 +197,7 @@ struct ForkLaunchInfo { #else if (ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1) #endif - ExitWithError(error_fd, "ptrace"); + ExitWithError(error_fd, "ptrace"); } // Execute. We should never return... diff --git a/lldb/source/Plugins/Language/ObjC/Cocoa.cpp b/lldb/source/Plugins/Language/ObjC/Cocoa.cpp index 1d841a032aa6e..1d79edbede5d6 100644 --- a/lldb/source/Plugins/Language/ObjC/Cocoa.cpp +++ b/lldb/source/Plugins/Language/ObjC/Cocoa.cpp @@ -31,7 +31,6 @@ #include "llvm/ADT/APInt.h" #include "llvm/ADT/bit.h" - using namespace lldb; using namespace lldb_private; using namespace lldb_private::formatters; @@ -267,21 +266,21 @@ bool lldb_private::formatters::NSIndexSetSummaryProvider( if (class_name == "NSIndexSet" || class_name == "NSMutableIndexSet") { // Foundation version 2000 added a bitmask if the index set fit in 64 bits // and a Tagged Pointer version if the bitmask is small enough to fit in - // the tagged pointer payload. + // the tagged pointer payload. // It also changed the layout (but not the size) of the set descriptor. // First check whether this is a tagged pointer. The bitmask will be in // the payload of the tagged pointer. uint64_t payload; - if (runtime->GetFoundationVersion() >= 2000 - && descriptor->GetTaggedPointerInfo(nullptr, nullptr, &payload)) { + if (runtime->GetFoundationVersion() >= 2000 && + descriptor->GetTaggedPointerInfo(nullptr, nullptr, &payload)) { count = llvm::popcount(payload); break; } // The first 32 bits describe the index set in all cases: Status error; uint32_t mode = process_sp->ReadUnsignedIntegerFromMemory( - valobj_addr + ptr_size, 4, 0, error); + valobj_addr + ptr_size, 4, 0, error); if (error.Fail()) return false; // Now check if the index is held in a bitmask in the object: @@ -292,7 +291,7 @@ bool lldb_private::formatters::NSIndexSetSummaryProvider( if ((mode & 2) == 2) { // The bitfield is a 64 bit uint at the beginning of the data var. uint64_t bitfield = process_sp->ReadUnsignedIntegerFromMemory( - valobj_addr + 2 * ptr_size, 8, 0, error); + valobj_addr + 2 * ptr_size, 8, 0, error); if (error.Fail()) return false; count = llvm::popcount(bitfield); @@ -309,7 +308,7 @@ bool lldb_private::formatters::NSIndexSetSummaryProvider( count = 0; break; } - + if ((mode & 2) == 2) mode = 1; // this means the set only has one range else @@ -1227,8 +1226,7 @@ bool lldb_private::formatters::ObjCSELSummaryProvider( time_t lldb_private::formatters::GetOSXEpoch() { static time_t epoch = 0; if (!epoch) { -#if !defined(_AIX) -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(_AIX) tzset(); tm tm_epoch; tm_epoch.tm_sec = 0; @@ -1241,7 +1239,6 @@ time_t lldb_private::formatters::GetOSXEpoch() { tm_epoch.tm_gmtoff = 0; tm_epoch.tm_zone = nullptr; epoch = timegm(&tm_epoch); -#endif #endif } return epoch; diff --git a/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp index 4f747ab20c9ef..b202898ff438a 100644 --- a/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp +++ b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp @@ -81,10 +81,10 @@ size_t ObjectContainerBSDArchive::Archive::ParseObjects() { std::unique_ptr mem_buffer = llvm::MemoryBuffer::getMemBuffer( - llvm::StringRef((const char *)data.GetDataStart(), - data.GetByteSize()), - llvm::StringRef(), - /*RequiresNullTerminator=*/false); + llvm::StringRef((const char *)data.GetDataStart(), + data.GetByteSize()), + llvm::StringRef(), + /*RequiresNullTerminator=*/false); auto exp_ar = llvm::object::Archive::create(mem_buffer->getMemBufferRef()); if (!exp_ar) { @@ -95,7 +95,7 @@ size_t ObjectContainerBSDArchive::Archive::ParseObjects() { llvm::Error iter_err = llvm::Error::success(); Object obj; - for (const auto &child: llvm_archive->children(iter_err)) { + for (const auto &child : llvm_archive->children(iter_err)) { obj.Clear(); auto exp_name = child.getName(); if (exp_name) { @@ -111,7 +111,9 @@ size_t ObjectContainerBSDArchive::Archive::ParseObjects() { obj.modification_time = std::chrono::duration_cast( std::chrono::time_point_cast( - exp_mtime.get()).time_since_epoch()).count(); + exp_mtime.get()) + .time_since_epoch()) + .count(); } else { LLDB_LOG_ERROR(l, exp_mtime.takeError(), "failed to get archive object time: {0}"); @@ -331,21 +333,21 @@ ObjectContainer *ObjectContainerBSDArchive::CreateInstance( ArchiveType ObjectContainerBSDArchive::MagicBytesMatch(const DataExtractor &data) { uint32_t offset = 0; - const char *armag = (const char *)data.PeekData(offset, - sizeof(ar_hdr) + SARMAG); + const char *armag = + (const char *)data.PeekData(offset, sizeof(ar_hdr) + SARMAG); if (armag == nullptr) return ArchiveType::Invalid; ArchiveType result = ArchiveType::Invalid; if (strncmp(armag, ArchiveMagic, SARMAG) == 0) - result = ArchiveType::Archive; + result = ArchiveType::Archive; else if (strncmp(armag, ThinArchiveMagic, SARMAG) == 0) - result = ArchiveType::ThinArchive; + result = ArchiveType::ThinArchive; else - return ArchiveType::Invalid; + return ArchiveType::Invalid; armag += offsetof(struct ar_hdr, ar_fmag) + SARMAG; if (strncmp(armag, ARFMAG, 2) == 0) - return result; + return result; return ArchiveType::Invalid; } @@ -443,7 +445,8 @@ size_t ObjectContainerBSDArchive::GetModuleSpecifications( return 0; const size_t initial_count = specs.GetSize(); - llvm::sys::TimePoint<> file_mod_time = FileSystem::Instance().GetModificationTime(file); + llvm::sys::TimePoint<> file_mod_time = + FileSystem::Instance().GetModificationTime(file); Archive::shared_ptr archive_sp( Archive::FindCachedArchive(file, ArchSpec(), file_mod_time, file_offset)); bool set_archive_arch = false; diff --git a/lldb/source/Utility/ArchSpec.cpp b/lldb/source/Utility/ArchSpec.cpp index ac91183a271cc..85bb85044ec15 100644 --- a/lldb/source/Utility/ArchSpec.cpp +++ b/lldb/source/Utility/ArchSpec.cpp @@ -14,7 +14,6 @@ #include "lldb/lldb-defines.h" #include "llvm/ADT/STLExtras.h" #include "llvm/BinaryFormat/COFF.h" -#include "llvm/BinaryFormat/XCOFF.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/BinaryFormat/XCOFF.h" >From 8fcf69ed77148f8b339b87f75ed97e5ce719b4ba Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Fri, 27 Dec 2024 06:50:27 -0600 Subject: [PATCH 19/47] Some Updates --- lldb/include/lldb/Host/aix/HostInfoAIX.h | 11 +-- lldb/source/Host/aix/HostInfoAIX.cpp | 65 +------------- .../ObjectFile/XCOFF/ObjectFileXCOFF.cpp | 87 +++++++++---------- .../ObjectFile/XCOFF/ObjectFileXCOFF.h | 11 ++- 4 files changed, 50 insertions(+), 124 deletions(-) diff --git a/lldb/include/lldb/Host/aix/HostInfoAIX.h b/lldb/include/lldb/Host/aix/HostInfoAIX.h index ced4cf34d38a8..ba727e1d5f171 100644 --- a/lldb/include/lldb/Host/aix/HostInfoAIX.h +++ b/lldb/include/lldb/Host/aix/HostInfoAIX.h @@ -6,16 +6,14 @@ // //===----------------------------------------------------------------------===// -#ifndef lldb_Host_aix_HostInfoAIX_h_ -#define lldb_Host_aix_HostInfoAIX_h_ +#ifndef LLDB_HOST_AIX_HOSTINFOAIX_H_ +#define LLDB_HOST_AIX_HOSTINFOAIX_H_ #include "lldb/Host/posix/HostInfoPosix.h" #include "lldb/Utility/FileSpec.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/VersionTuple.h" -#include - namespace lldb_private { class HostInfoAIX : public HostInfoPosix { @@ -25,15 +23,10 @@ class HostInfoAIX : public HostInfoPosix { static void Initialize(SharedLibraryDirectoryHelper *helper = nullptr); static void Terminate(); - static llvm::VersionTuple GetOSVersion(); - static std::optional GetOSBuildString(); static llvm::StringRef GetDistributionId(); static FileSpec GetProgramFileSpec(); protected: - static bool ComputeSupportExeDirectory(FileSpec &file_spec); - static bool ComputeSystemPluginsDirectory(FileSpec &file_spec); - static bool ComputeUserPluginsDirectory(FileSpec &file_spec); static void ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64); }; diff --git a/lldb/source/Host/aix/HostInfoAIX.cpp b/lldb/source/Host/aix/HostInfoAIX.cpp index 8bda09e01741b..ef07b07c8cab2 100644 --- a/lldb/source/Host/aix/HostInfoAIX.cpp +++ b/lldb/source/Host/aix/HostInfoAIX.cpp @@ -29,8 +29,6 @@ namespace { struct HostInfoAIXFields { llvm::once_flag m_distribution_once_flag; std::string m_distribution_id; - llvm::once_flag m_os_version_once_flag; - llvm::VersionTuple m_os_version; }; } // namespace @@ -49,33 +47,6 @@ void HostInfoAIX::Terminate() { HostInfoBase::Terminate(); } -llvm::VersionTuple HostInfoAIX::GetOSVersion() { - assert(g_fields && "Missing call to Initialize?"); - llvm::call_once(g_fields->m_os_version_once_flag, []() { - struct utsname un; - if (uname(&un) != 0) - return; - - llvm::StringRef release = un.release; - // The kernel release string can include a lot of stuff (e.g. - // 4.9.0-6-amd64). We're only interested in the numbered prefix. - release = release.substr(0, release.find_first_not_of("0123456789.")); - g_fields->m_os_version.tryParse(release); - }); - - return g_fields->m_os_version; -} - -std::optional HostInfoAIX::GetOSBuildString() { - struct utsname un; - ::memset(&un, 0, sizeof(utsname)); - - if (uname(&un) < 0) - return std::nullopt; - - return std::string(un.release); -} - llvm::StringRef HostInfoAIX::GetDistributionId() { assert(g_fields && "Missing call to Initialize?"); // Try to run 'lbs_release -i', and use that response for the distribution @@ -122,8 +93,7 @@ llvm::StringRef HostInfoAIX::GetDistributionId() { if (strstr(distribution_id, distributor_id_key)) { // strip newlines std::string id_string(distribution_id + strlen(distributor_id_key)); - id_string.erase(std::remove(id_string.begin(), id_string.end(), '\n'), - id_string.end()); + llvm::erase(id_string, '\n'); // lower case it and convert whitespace to underscores std::transform( @@ -167,42 +137,11 @@ FileSpec HostInfoAIX::GetProgramFileSpec() { return g_program_filespec; } -bool HostInfoAIX::ComputeSupportExeDirectory(FileSpec &file_spec) { - if (HostInfoPosix::ComputeSupportExeDirectory(file_spec) && - file_spec.IsAbsolute() && FileSystem::Instance().Exists(file_spec)) - return true; - file_spec.SetDirectory(GetProgramFileSpec().GetDirectory()); - return !file_spec.GetDirectory().IsEmpty(); -} - -bool HostInfoAIX::ComputeSystemPluginsDirectory(FileSpec &file_spec) { - FileSpec temp_file("/usr/" LLDB_INSTALL_LIBDIR_BASENAME "/lldb/plugins"); - FileSystem::Instance().Resolve(temp_file); - file_spec.SetDirectory(temp_file.GetPath()); - return true; -} - -bool HostInfoAIX::ComputeUserPluginsDirectory(FileSpec &file_spec) { - // XDG Base Directory Specification - // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html If - // XDG_DATA_HOME exists, use that, otherwise use ~/.local/share/lldb. - const char *xdg_data_home = getenv("XDG_DATA_HOME"); - if (xdg_data_home && xdg_data_home[0]) { - std::string user_plugin_dir(xdg_data_home); - user_plugin_dir += "/lldb"; - file_spec.SetDirectory(user_plugin_dir.c_str()); - } else - file_spec.SetDirectory("~/.local/share/lldb"); - return true; -} - void HostInfoAIX::ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64) { HostInfoPosix::ComputeHostArchitectureSupport(arch_32, arch_64); - const char *distribution_id = GetDistributionId().data(); - - // On Linux, "unknown" in the vendor slot isn't what we want for the default + // "unknown" in the vendor slot isn't what we want for the default // triple. It's probably an artifact of config.guess. if (arch_32.IsValid()) { if (arch_32.GetTriple().getVendor() == llvm::Triple::UnknownVendor) diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp index a4d9ea295b4c3..afd8027bab06c 100644 --- a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp @@ -1,4 +1,5 @@ -//===-- ObjectFileXCOFF.cpp -------------------------------------------------===// +//===-- ObjectFileXCOFF.cpp +//-------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,13 +8,6 @@ //===----------------------------------------------------------------------===// #include "ObjectFileXCOFF.h" - -#include -#include -#include -#include - -#include "lldb/Utility/FileSpecList.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" @@ -28,6 +22,7 @@ #include "lldb/Target/Target.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/FileSpecList.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/RangeMap.h" @@ -38,12 +33,16 @@ #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/XCOFF.h" +#include "llvm/Object/XCOFFObjectFile.h" #include "llvm/Object/Decompressor.h" #include "llvm/Support/CRC.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/MemoryBuffer.h" -#include "llvm/Object/XCOFFObjectFile.h" +#include +#include +#include +#include using namespace llvm; using namespace lldb; @@ -69,21 +68,19 @@ void ObjectFileXCOFF::Terminate() { bool UGLY_FLAG_FOR_AIX __attribute__((weak)) = false; ObjectFile *ObjectFileXCOFF::CreateInstance(const lldb::ModuleSP &module_sp, - DataBufferSP data_sp, - lldb::offset_t data_offset, - const lldb_private::FileSpec *file, - lldb::offset_t file_offset, - lldb::offset_t length) { + DataBufferSP data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec *file, + lldb::offset_t file_offset, + lldb::offset_t length) { if (!data_sp) { data_sp = MapFileData(*file, length, file_offset); if (!data_sp) return nullptr; data_offset = 0; } - if (!ObjectFileXCOFF::MagicBytesMatch(data_sp, data_offset, length)) return nullptr; - // Update the data to contain the entire file if it doesn't already if (data_sp->GetByteSize() < length) { data_sp = MapFileData(*file, length, file_offset); @@ -114,15 +111,15 @@ bool ObjectFileXCOFF::CreateBinary() { Log *log = GetLog(LLDBLog::Object); - auto binary = llvm::object::XCOFFObjectFile::createObjectFile(llvm::MemoryBufferRef( - toStringRef(m_data.GetData()), m_file.GetFilename().GetStringRef()), - file_magic::xcoff_object_64); + auto binary = llvm::object::ObjectFile::createObjectFile( + llvm::MemoryBufferRef(toStringRef(m_data.GetData()), + m_file.GetFilename().GetStringRef()), + file_magic::xcoff_object_64); if (!binary) { LLDB_LOG_ERROR(log, binary.takeError(), "Failed to create binary for file ({1}): {0}", m_file); return false; } - // Make sure we only handle COFF format. m_binary = llvm::unique_dyn_cast(std::move(*binary)); @@ -132,6 +129,7 @@ bool ObjectFileXCOFF::CreateBinary() { LLDB_LOG(log, "this = {0}, module = {1} ({2}), file = {3}, binary = {4}", this, GetModule().get(), GetModule()->GetSpecificationDescription(), m_file.GetPath(), m_binary.get()); + return true; } @@ -148,9 +146,12 @@ size_t ObjectFileXCOFF::GetModuleSpecifications( const size_t initial_count = specs.GetSize(); if (ObjectFileXCOFF::MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) { - ArchSpec arch_spec = ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); + ArchSpec arch_spec = + ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); ModuleSpec spec(file, arch_spec); - spec.GetArchitecture().SetArchitecture(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE, llvm::Triple::AIX); + spec.GetArchitecture().SetArchitecture(eArchTypeXCOFF, XCOFF::TCPU_PPC64, + LLDB_INVALID_CPUTYPE, + llvm::Triple::AIX); specs.Append(spec); } return specs.GetSize() - initial_count; @@ -158,11 +159,9 @@ size_t ObjectFileXCOFF::GetModuleSpecifications( static uint32_t XCOFFHeaderSizeFromMagic(uint32_t magic) { switch (magic) { - /* TODO: 32bit not supported yet - case XCOFF::XCOFF32: - return sizeof(struct llvm::object::XCOFFFileHeader32); - */ - + // TODO: 32bit not supported. + // case XCOFF::XCOFF32: + // return sizeof(struct llvm::object::XCOFFFileHeader32); case XCOFF::XCOFF64: return sizeof(struct llvm::object::XCOFFFileHeader64); break; @@ -174,10 +173,12 @@ static uint32_t XCOFFHeaderSizeFromMagic(uint32_t magic) { } bool ObjectFileXCOFF::MagicBytesMatch(DataBufferSP &data_sp, - lldb::addr_t data_offset, - lldb::addr_t data_length) { - lldb_private::DataExtractor data; + lldb::addr_t data_offset, + lldb::addr_t data_length) { + lldb_private::DataExtractor data; data.SetData(data_sp, data_offset, data_length); + // Need to set this as XCOFF is only compatible with Big Endian + data.SetByteOrder(eByteOrderBig); lldb::offset_t offset = 0; uint16_t magic = data.GetU16(&offset); return XCOFFHeaderSizeFromMagic(magic) != 0; @@ -386,13 +387,10 @@ bool ObjectFileXCOFF::SetLoadAddressByType(Target &target, lldb::addr_t value, return changed; } -ByteOrder ObjectFileXCOFF::GetByteOrder() const { - return eByteOrderBig; -} -bool ObjectFileXCOFF::IsExecutable() const { - return true; -} +ByteOrder ObjectFileXCOFF::GetByteOrder() const { return eByteOrderBig; } + +bool ObjectFileXCOFF::IsExecutable() const { return true; } uint32_t ObjectFileXCOFF::GetAddressByteSize() const { if (m_xcoff_header.magic == XCOFF::XCOFF64) @@ -592,13 +590,12 @@ void ObjectFileXCOFF::Dump(Stream *s) { } ArchSpec ObjectFileXCOFF::GetArchitecture() { - ArchSpec arch_spec = ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); + ArchSpec arch_spec = + ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); return arch_spec; } -UUID ObjectFileXCOFF::GetUUID() { - return UUID(); -} +UUID ObjectFileXCOFF::GetUUID() { return UUID(); } std::optional ObjectFileXCOFF::GetDebugLink() { return std::nullopt; @@ -724,16 +721,14 @@ lldb_private::Address ObjectFileXCOFF::GetBaseAddress() { } ObjectFile::Type ObjectFileXCOFF::CalculateType() { - if (m_xcoff_header.flags & XCOFF::F_EXEC) + if (m_binary->fileHeader64()->Flags & XCOFF::F_EXEC) return eTypeExecutable; - else if (m_xcoff_header.flags & XCOFF::F_SHROBJ) + else if (m_binary->fileHeader64()->Flags & XCOFF::F_SHROBJ) return eTypeSharedLibrary; return eTypeUnknown; } -ObjectFile::Strata ObjectFileXCOFF::CalculateStrata() { - return eStrataUnknown; -} +ObjectFile::Strata ObjectFileXCOFF::CalculateStrata() { return eStrataUnknown; } llvm::StringRef ObjectFileXCOFF::StripLinkerSymbolAnnotations(llvm::StringRef symbol_name) const { @@ -752,7 +747,7 @@ ObjectFileXCOFF::GetLoadableData(Target &target) { lldb::WritableDataBufferSP ObjectFileXCOFF::MapFileDataWritable(const FileSpec &file, uint64_t Size, - uint64_t Offset) { + uint64_t Offset) { return FileSystem::Instance().CreateWritableDataBuffer(file.GetPath(), Size, Offset); } diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.h b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.h index 5a12d16886489..f827fca3932f4 100644 --- a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.h +++ b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.h @@ -1,4 +1,5 @@ -//===-- ObjectFileXCOFF.h --------------------------------------- -*- C++ -*-===// +//===-- ObjectFileXCOFF.h --------------------------------------- -*- C++ +//-*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,16 +10,14 @@ #ifndef LLDB_SOURCE_PLUGINS_OBJECTFILE_XCOFF_OBJECTFILEXCOFF_H #define LLDB_SOURCE_PLUGINS_OBJECTFILE_XCOFF_OBJECTFILEXCOFF_H -#include - -#include - #include "lldb/Symbol/ObjectFile.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/UUID.h" #include "lldb/lldb-private.h" #include "llvm/Object/XCOFFObjectFile.h" +#include +#include /// \class ObjectFileXCOFF /// Generic XCOFF object file reader. @@ -240,4 +239,4 @@ class ObjectFileXCOFF : public lldb_private::ObjectFile { std::map> m_deps_base_members; }; -#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_ELF_OBJECTFILEELF_H +#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_XCOFF_OBJECTFILE_H >From f8b05dfc9fc75177a63dfa2d6df4a9af143b09b8 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Sun, 5 Jan 2025 05:01:47 -0600 Subject: [PATCH 20/47] HostInfoAIX Cleanup --- lldb/include/lldb/Host/aix/HostInfoAIX.h | 4 - lldb/source/Host/aix/HostInfoAIX.cpp | 108 ----------------------- 2 files changed, 112 deletions(-) diff --git a/lldb/include/lldb/Host/aix/HostInfoAIX.h b/lldb/include/lldb/Host/aix/HostInfoAIX.h index ba727e1d5f171..5a52c42fa6199 100644 --- a/lldb/include/lldb/Host/aix/HostInfoAIX.h +++ b/lldb/include/lldb/Host/aix/HostInfoAIX.h @@ -23,12 +23,8 @@ class HostInfoAIX : public HostInfoPosix { static void Initialize(SharedLibraryDirectoryHelper *helper = nullptr); static void Terminate(); - static llvm::StringRef GetDistributionId(); static FileSpec GetProgramFileSpec(); -protected: - static void ComputeHostArchitectureSupport(ArchSpec &arch_32, - ArchSpec &arch_64); }; } diff --git a/lldb/source/Host/aix/HostInfoAIX.cpp b/lldb/source/Host/aix/HostInfoAIX.cpp index ef07b07c8cab2..2996fcb55f811 100644 --- a/lldb/source/Host/aix/HostInfoAIX.cpp +++ b/lldb/source/Host/aix/HostInfoAIX.cpp @@ -11,117 +11,25 @@ #include "lldb/Host/FileSystem.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" - #include "llvm/Support/Threading.h" - #include #include #include #include #include - #include #include using namespace lldb_private; -namespace { -struct HostInfoAIXFields { - llvm::once_flag m_distribution_once_flag; - std::string m_distribution_id; -}; -} // namespace - -static HostInfoAIXFields *g_fields = nullptr; - void HostInfoAIX::Initialize(SharedLibraryDirectoryHelper *helper) { HostInfoPosix::Initialize(helper); - - g_fields = new HostInfoAIXFields(); } void HostInfoAIX::Terminate() { - assert(g_fields && "Missing call to Initialize?"); - delete g_fields; - g_fields = nullptr; HostInfoBase::Terminate(); } -llvm::StringRef HostInfoAIX::GetDistributionId() { - assert(g_fields && "Missing call to Initialize?"); - // Try to run 'lbs_release -i', and use that response for the distribution - // id. - llvm::call_once(g_fields->m_distribution_once_flag, []() { - Log *log = GetLog(LLDBLog::Host); - LLDB_LOGF(log, "attempting to determine AIX distribution..."); - - // check if the lsb_release command exists at one of the following paths - const char *const exe_paths[] = {"/bin/lsb_release", - "/usr/bin/lsb_release"}; - - for (size_t exe_index = 0; - exe_index < sizeof(exe_paths) / sizeof(exe_paths[0]); ++exe_index) { - const char *const get_distribution_info_exe = exe_paths[exe_index]; - if (access(get_distribution_info_exe, F_OK)) { - // this exe doesn't exist, move on to next exe - LLDB_LOGF(log, "executable doesn't exist: %s", - get_distribution_info_exe); - continue; - } - - // execute the distribution-retrieval command, read output - std::string get_distribution_id_command(get_distribution_info_exe); - get_distribution_id_command += " -i"; - - FILE *file = popen(get_distribution_id_command.c_str(), "r"); - if (!file) { - LLDB_LOGF(log, - "failed to run command: \"%s\", cannot retrieve " - "platform information", - get_distribution_id_command.c_str()); - break; - } - - // retrieve the distribution id string. - char distribution_id[256] = {'\0'}; - if (fgets(distribution_id, sizeof(distribution_id) - 1, file) != - nullptr) { - LLDB_LOGF(log, "distribution id command returned \"%s\"", - distribution_id); - - const char *const distributor_id_key = "Distributor ID:\t"; - if (strstr(distribution_id, distributor_id_key)) { - // strip newlines - std::string id_string(distribution_id + strlen(distributor_id_key)); - llvm::erase(id_string, '\n'); - - // lower case it and convert whitespace to underscores - std::transform( - id_string.begin(), id_string.end(), id_string.begin(), - [](char ch) { return tolower(isspace(ch) ? '_' : ch); }); - - g_fields->m_distribution_id = id_string; - LLDB_LOGF(log, "distribution id set to \"%s\"", - g_fields->m_distribution_id.c_str()); - } else { - LLDB_LOGF(log, "failed to find \"%s\" field in \"%s\"", - distributor_id_key, distribution_id); - } - } else { - LLDB_LOGF(log, - "failed to retrieve distribution id, \"%s\" returned no" - " lines", - get_distribution_id_command.c_str()); - } - - // clean up the file - pclose(file); - } - }); - - return g_fields->m_distribution_id; -} - FileSpec HostInfoAIX::GetProgramFileSpec() { static FileSpec g_program_filespec; @@ -136,19 +44,3 @@ FileSpec HostInfoAIX::GetProgramFileSpec() { return g_program_filespec; } - -void HostInfoAIX::ComputeHostArchitectureSupport(ArchSpec &arch_32, - ArchSpec &arch_64) { - HostInfoPosix::ComputeHostArchitectureSupport(arch_32, arch_64); - - // "unknown" in the vendor slot isn't what we want for the default - // triple. It's probably an artifact of config.guess. - if (arch_32.IsValid()) { - if (arch_32.GetTriple().getVendor() == llvm::Triple::UnknownVendor) - arch_32.GetTriple().setVendorName(llvm::StringRef()); - } - if (arch_64.IsValid()) { - if (arch_64.GetTriple().getVendor() == llvm::Triple::UnknownVendor) - arch_64.GetTriple().setVendorName(llvm::StringRef()); - } -} >From 57d080e44e80203a6ab848c362469954a7c9f067 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Sun, 5 Jan 2025 05:49:40 -0600 Subject: [PATCH 21/47] Cleanup HostInfoAIX Including the previous commit, Removed: GetDistributionID, ComputeHostArchitectureSupport and Reduced GetProgramFileSpec as it was not needed --- lldb/source/Host/aix/HostInfoAIX.cpp | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/lldb/source/Host/aix/HostInfoAIX.cpp b/lldb/source/Host/aix/HostInfoAIX.cpp index 2996fcb55f811..d09b9052668af 100644 --- a/lldb/source/Host/aix/HostInfoAIX.cpp +++ b/lldb/source/Host/aix/HostInfoAIX.cpp @@ -26,21 +26,9 @@ void HostInfoAIX::Initialize(SharedLibraryDirectoryHelper *helper) { HostInfoPosix::Initialize(helper); } -void HostInfoAIX::Terminate() { - HostInfoBase::Terminate(); -} +void HostInfoAIX::Terminate() { HostInfoBase::Terminate(); } FileSpec HostInfoAIX::GetProgramFileSpec() { static FileSpec g_program_filespec; - - if (!g_program_filespec) { - char exe_path[PATH_MAX]; - ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1); - if (len > 0) { - exe_path[len] = 0; - g_program_filespec.SetFile(exe_path, FileSpec::Style::native); - } - } - return g_program_filespec; } >From 673713a9339de4e4ea395ee2e7f65dc1db43bcf9 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Sun, 5 Jan 2025 15:35:23 -0600 Subject: [PATCH 22/47] Removing headers --- lldb/include/lldb/Host/aix/HostInfoAIX.h | 2 -- lldb/source/Host/aix/HostInfoAIX.cpp | 12 ------------ 2 files changed, 14 deletions(-) diff --git a/lldb/include/lldb/Host/aix/HostInfoAIX.h b/lldb/include/lldb/Host/aix/HostInfoAIX.h index 5a52c42fa6199..331a274630850 100644 --- a/lldb/include/lldb/Host/aix/HostInfoAIX.h +++ b/lldb/include/lldb/Host/aix/HostInfoAIX.h @@ -11,8 +11,6 @@ #include "lldb/Host/posix/HostInfoPosix.h" #include "lldb/Utility/FileSpec.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/VersionTuple.h" namespace lldb_private { diff --git a/lldb/source/Host/aix/HostInfoAIX.cpp b/lldb/source/Host/aix/HostInfoAIX.cpp index d09b9052668af..61b47462dd647 100644 --- a/lldb/source/Host/aix/HostInfoAIX.cpp +++ b/lldb/source/Host/aix/HostInfoAIX.cpp @@ -7,18 +7,6 @@ //===----------------------------------------------------------------------===// #include "lldb/Host/aix/HostInfoAIX.h" -#include "lldb/Host/Config.h" -#include "lldb/Host/FileSystem.h" -#include "lldb/Utility/LLDBLog.h" -#include "lldb/Utility/Log.h" -#include "llvm/Support/Threading.h" -#include -#include -#include -#include -#include -#include -#include using namespace lldb_private; >From cdc31f3963365e4595247ff5a7155662df1ce1af Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Mon, 6 Jan 2025 09:28:56 -0600 Subject: [PATCH 23/47] Reverted merge blunder CMakeLists --- lldb/source/Host/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lldb/source/Host/CMakeLists.txt b/lldb/source/Host/CMakeLists.txt index f326bc07dc1f6..f4fca8acc4d83 100644 --- a/lldb/source/Host/CMakeLists.txt +++ b/lldb/source/Host/CMakeLists.txt @@ -141,7 +141,10 @@ else() elseif (CMAKE_SYSTEM_NAME MATCHES "AIX") add_host_subdirectory(aix + aix/AbstractSocket.cpp + aix/Host.cpp aix/HostInfoAIX.cpp + aix/Support.cpp ) endif() endif() >From 713a6cbbb97b9bc56b039ab050a1690eccc017e3 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Mon, 6 Jan 2025 09:39:36 -0600 Subject: [PATCH 24/47] Removed DomainSocket.cpp FileSystemPosix.cpp includes --- lldb/source/Host/posix/DomainSocket.cpp | 4 ---- lldb/source/Host/posix/FileSystemPosix.cpp | 3 --- 2 files changed, 7 deletions(-) diff --git a/lldb/source/Host/posix/DomainSocket.cpp b/lldb/source/Host/posix/DomainSocket.cpp index f30e84d83efca..be8fcdf2c8f2c 100644 --- a/lldb/source/Host/posix/DomainSocket.cpp +++ b/lldb/source/Host/posix/DomainSocket.cpp @@ -17,10 +17,6 @@ #include #include -#if defined(_AIX) -#include -#endif - using namespace lldb; using namespace lldb_private; diff --git a/lldb/source/Host/posix/FileSystemPosix.cpp b/lldb/source/Host/posix/FileSystemPosix.cpp index 1a84f550662d7..a631bb01209ec 100644 --- a/lldb/source/Host/posix/FileSystemPosix.cpp +++ b/lldb/source/Host/posix/FileSystemPosix.cpp @@ -11,9 +11,6 @@ // C includes #include #include -#ifndef _AIX -#include -#endif #include #include #include >From 0a706d29dabeefa62e354fc9358d650f89032048 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Mon, 6 Jan 2025 09:44:10 -0600 Subject: [PATCH 25/47] sys/mount.h --- lldb/source/Host/posix/FileSystemPosix.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lldb/source/Host/posix/FileSystemPosix.cpp b/lldb/source/Host/posix/FileSystemPosix.cpp index a631bb01209ec..945e2affc8371 100644 --- a/lldb/source/Host/posix/FileSystemPosix.cpp +++ b/lldb/source/Host/posix/FileSystemPosix.cpp @@ -11,6 +11,7 @@ // C includes #include #include +#include #include #include #include >From 84ebb4ec9b542c38fe1b60261a3253383e9fbf5d Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Mon, 6 Jan 2025 09:54:58 -0600 Subject: [PATCH 26/47] sys/mount.h --- lldb/source/Host/posix/FileSystemPosix.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lldb/source/Host/posix/FileSystemPosix.cpp b/lldb/source/Host/posix/FileSystemPosix.cpp index 945e2affc8371..1a84f550662d7 100644 --- a/lldb/source/Host/posix/FileSystemPosix.cpp +++ b/lldb/source/Host/posix/FileSystemPosix.cpp @@ -11,7 +11,9 @@ // C includes #include #include +#ifndef _AIX #include +#endif #include #include #include >From 844f7980040de9e13620e9d65a3fcaef56b6c8d6 Mon Sep 17 00:00:00 2001 From: Dhruv Srivastava Date: Thu, 9 Jan 2025 09:47:43 +0530 Subject: [PATCH 27/47] Removed _AIX from ConnectionFileDescriptorPosix.cpp --- lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp b/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp index 2530c8fa353ba..0ed2016667162 100644 --- a/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp +++ b/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp @@ -715,7 +715,7 @@ ConnectionFileDescriptor::ConnectFD(llvm::StringRef s, ConnectionStatus ConnectionFileDescriptor::ConnectFile( llvm::StringRef s, socket_id_callback_type socket_id_callback, Status *error_ptr) { -#if LLDB_ENABLE_POSIX && !defined(_AIX) +#if LLDB_ENABLE_POSIX std::string addr_str = s.str(); // file:///PATH int fd = FileSystem::Instance().Open(addr_str.c_str(), O_RDWR); @@ -756,7 +756,7 @@ ConnectionStatus ConnectionFileDescriptor::ConnectFile( m_io_sp = std::make_shared(fd, File::eOpenOptionReadWrite, true); return eConnectionStatusSuccess; -#endif // LLDB_ENABLE_POSIX && !defined(_AIX) +#endif // LLDB_ENABLE_POSIX llvm_unreachable("this function should be only called w/ LLDB_ENABLE_POSIX"); } >From 4fe42cda7c2f4990b18a39c1d6563094fb88775f Mon Sep 17 00:00:00 2001 From: ravindra shinde Date: Fri, 17 Jan 2025 13:58:13 +0530 Subject: [PATCH 28/47] [ObjectFileXCOFF] Fix access to protected member 'GetSectionLoadList' in Target - Added a public method to Target for accessing 'GetSectionLoadList' safely. - Updated ObjectFileXCOFF to use the new public method, ensuring compliance with encapsulation rules. This resolves the build error caused by direct access to the protected member. Signed-off-by: ravindra shinde --- lldb/include/lldb/Target/Target.h | 5 +++++ lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h index f31ac381391b4..75f9c9c2e999c 100644 --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -522,6 +522,7 @@ class Target : public std::enable_shared_from_this, eBroadcastBitSymbolsChanged = (1 << 5), }; + // These two functions fill out the Broadcaster interface: static llvm::StringRef GetStaticBroadcasterClass(); @@ -1644,6 +1645,10 @@ class Target : public std::enable_shared_from_this, TargetStats &GetStatistics() { return m_stats; } +public: + SectionLoadList &GetSectionLoadListPublic() { + return GetSectionLoadList(); + } protected: /// Construct with optional file and arch. /// diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp index afd8027bab06c..cf11e5fb8f5a3 100644 --- a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp @@ -340,7 +340,7 @@ bool ObjectFileXCOFF::SetLoadAddress(Target &target, lldb::addr_t value, strcmp(section_sp->GetName().AsCString(), ".bss") == 0) use_offset = true; - if (target.GetSectionLoadList().SetSectionLoadAddress( + if (target.GetSectionLoadListPublic().SetSectionLoadAddress( section_sp, (use_offset ? (section_sp->GetFileOffset() + value) : (section_sp->GetFileAddress() + value)))) ++num_loaded_sections; @@ -369,13 +369,13 @@ bool ObjectFileXCOFF::SetLoadAddressByType(Target &target, lldb::addr_t value, SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); if (type_id == 1 && section_sp && strcmp(section_sp->GetName().AsCString(), ".text") == 0) { if (!section_sp->IsThreadSpecific()) { - if (target.GetSectionLoadList().SetSectionLoadAddress( + if (target.GetSectionLoadListPublic().SetSectionLoadAddress( section_sp, section_sp->GetFileOffset() + value)) ++num_loaded_sections; } } else if (type_id == 2 && section_sp && strcmp(section_sp->GetName().AsCString(), ".data") == 0) { if (!section_sp->IsThreadSpecific()) { - if (target.GetSectionLoadList().SetSectionLoadAddress( + if (target.GetSectionLoadListPublic().SetSectionLoadAddress( section_sp, section_sp->GetFileAddress() + value)) ++num_loaded_sections; } >From e5ed4f21c5bbc709e5e2eff0f83995cc6d89de48 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Sun, 19 Jan 2025 03:43:11 -0600 Subject: [PATCH 29/47] Resolved cmake failure for SBProgress.cpp --- lldb/source/API/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/lldb/source/API/CMakeLists.txt b/lldb/source/API/CMakeLists.txt index eb348e2b97232..0a03e000c0cae 100644 --- a/lldb/source/API/CMakeLists.txt +++ b/lldb/source/API/CMakeLists.txt @@ -85,6 +85,7 @@ add_lldb_library(liblldb STATIC ${option_framework} SBModuleSpec.cpp SBPlatform.cpp SBProcess.cpp + SBProgress.cpp SBProcessInfo.cpp SBProcessInfoList.cpp SBQueue.cpp >From 82dbcb0e776c438e5f40c9f8d8c8e8eb81b7febd Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Mon, 27 Jan 2025 06:35:19 -0600 Subject: [PATCH 30/47] Host.cpp ANDROID --- lldb/source/Host/common/Host.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp index 758e9f49ade2c..adf74df8aa90b 100644 --- a/lldb/source/Host/common/Host.cpp +++ b/lldb/source/Host/common/Host.cpp @@ -1,4 +1,4 @@ -//===-- Host.cpp ----------------------------------------------------------===// + // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -516,7 +516,6 @@ static int dladdr(const void *ptr, Dl_info *dl) FileSpec Host::GetModuleFileSpecForHostAddress(const void *host_addr) { FileSpec module_filespec; -#if !defined(__ANDROID__) #ifdef _AIX if (host_addr == reinterpret_cast(HostInfoBase::ComputeSharedLibraryDirectory)) { // FIXME: AIX dladdr return "lldb" for this case @@ -527,6 +526,7 @@ FileSpec Host::GetModuleFileSpecForHostAddress(const void *host_addr) { } } #endif +#if !defined(__ANDROID__) Dl_info info; if (::dladdr(host_addr, &info)) { if (info.dli_fname) { @@ -534,6 +534,7 @@ FileSpec Host::GetModuleFileSpecForHostAddress(const void *host_addr) { FileSystem::Instance().Resolve(module_filespec); } } +#endif return module_filespec; } >From 60294eaa1611632afc94b9da503752e34ad2703d Mon Sep 17 00:00:00 2001 From: Ravindra Shinde Date: Tue, 4 Feb 2025 03:23:56 -0600 Subject: [PATCH 31/47] Resolving the fatal error while build Build is failing due to the fatal error: 'sys/syscall.h' file not found Signed-off-by: Ravindra Shinde --- lldb/source/Host/common/Host.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp index 40ce76d70d6e8..b5bd68b8539bd 100644 --- a/lldb/source/Host/common/Host.cpp +++ b/lldb/source/Host/common/Host.cpp @@ -18,8 +18,12 @@ #include #include #include + +#ifndef _AIX #include #include +#endif + #include #include #endif >From 2644be59b13a61c69cc635875c94b0b4645fe76c Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Mon, 10 Feb 2025 01:49:12 -0600 Subject: [PATCH 32/47] InferiorCallPOSIX.cpp --- lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp b/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp index d1f9fe851119e..8e74cce097894 100644 --- a/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp +++ b/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp @@ -120,12 +120,13 @@ bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr, arch, addr, length, prot_arg, flags, fd, offset); #if defined(_AIX) lldb::ThreadPlanSP call_plan_sp( - new ThreadPlanCallFunction(*thread, mmap_range.GetBaseAddress(), + new ThreadPlanCallFunction(*thread, mmap_addr, toc_range.GetBaseAddress(), void_ptr_type, args, options)); #else lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallFunction( *thread, mmap_addr, void_ptr_type, args, options)); +#endif if (call_plan_sp) { DiagnosticManager diagnostics; >From 4805b13cba964b58def39a66ad4c4309a9b2c501 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Wed, 12 Feb 2025 07:33:04 -0600 Subject: [PATCH 33/47] Merge branch gh-101657 --- .../AIX-DYLD/DynamicLoaderAIXDYLD.cpp | 97 ------------------- .../AIX-DYLD/DynamicLoaderAIXDYLD.h | 5 +- 2 files changed, 1 insertion(+), 101 deletions(-) diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp index 1a98bb9334043..375d879c7a995 100644 --- a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp @@ -21,10 +21,6 @@ #include "llvm/Support/FileSystem.h" #if defined(_AIX) #include -#include -#include -#include -#include #endif /*#include "llvm/ADT/Triple.h" @@ -137,107 +133,14 @@ bool DynamicLoaderAIXDYLD::NotifyBreakpointHit( } -void DynamicLoaderAIXDYLD::ResolveExecutableModule( - lldb::ModuleSP &module_sp) { - Log *log = GetLog(LLDBLog::DynamicLoader); - - if (m_process == nullptr) - return; - - auto &target = m_process->GetTarget(); - const auto platform_sp = target.GetPlatform(); - - ProcessInstanceInfo process_info; - if (!m_process->GetProcessInfo(process_info)) { - LLDB_LOGF(log, - "DynamicLoaderPOSIXDYLD::%s - failed to get process info for " - "pid %" PRIu64, - __FUNCTION__, m_process->GetID()); - return; - } - - int32long64_t pid = m_process->GetID(); - char cwd[PATH_MAX], resolved_path[PATH_MAX]; - std::string executable_name; - bool path_resolved = false; - psinfo_t psinfo; - - std::string proc_file = "/proc/" + std::to_string(pid) + "/psinfo"; - std::string cwd_link = "/proc/" + std::to_string(pid) + "/cwd"; - std::ifstream file(proc_file, std::ios::binary); - if(!file.is_open()) - LLDB_LOGF(log, "Error: Unable to access process info "); - - file.read(reinterpret_cast(&psinfo), sizeof(psinfo_t)); - if(!file) - LLDB_LOGF(log, "Process info error: Failed to read "); - - std::string relative_path(psinfo.pr_fname); - LLDB_LOGF(log, "Relative path %s",relative_path.c_str()); - - if(readlink(cwd_link.c_str(), cwd, sizeof(cwd)) != -1){ - std::filesystem::path full_path = std::filesystem::path(cwd)/relative_path; - if(realpath(full_path.c_str(), resolved_path)) { - LLDB_LOGF(log, "Resolved Path using process info : %s", resolved_path); - path_resolved = true; - } - else - LLDB_LOGF(log, "Realpath error: Unable to resolve. "); - } - - executable_name = resolved_path; - if(path_resolved == false) { - std::string command_line(psinfo.pr_psargs); - LLDB_LOGF(log, "Command line: %s",command_line.c_str()); - if (!command_line.empty()) { - size_t space1 = command_line.find(' '); - executable_name = command_line.substr(0, space1); - LLDB_LOGF(log, "Resolved path using command line arg %s",executable_name.c_str()); - } - } - - LLDB_LOGF(log, "Executable Name %s",executable_name.c_str()); - process_info.SetExecutableFile(lldb_private::FileSpec(executable_name), - true); - - LLDB_LOGF( - log, "DynamicLoaderPOSIXDYLD::%s - got executable by pid %" PRIu64 ": %s", - __FUNCTION__, m_process->GetID(), - process_info.GetExecutableFile().GetPath().c_str()); - - ModuleSpec module_spec(process_info.GetExecutableFile(), - process_info.GetArchitecture()); - if (module_sp && module_sp->MatchesModuleSpec(module_spec)) - return; - - const auto executable_search_paths(Target::GetDefaultExecutableSearchPaths()); - auto error = platform_sp->ResolveExecutable( - module_spec, module_sp, - !executable_search_paths.IsEmpty() ? &executable_search_paths : nullptr); - if (error.Fail()) { - StreamString stream; - module_spec.Dump(stream); - - LLDB_LOGF(log, - "DynamicLoaderPOSIXDYLD::%s - failed to resolve executable " - "with module spec \"%s\": %s", - __FUNCTION__, stream.GetData(), error.AsCString()); - return; - } - - target.SetExecutableModule(module_sp, eLoadDependentsNo); -} - void DynamicLoaderAIXDYLD::DidAttach() { Log *log = GetLog(LLDBLog::DynamicLoader); LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); ModuleSP executable = GetTargetExecutable(); - ResolveExecutableModule(executable); if (!executable.get()) return; - LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); // Try to fetch the load address of the file from the process, since there // could be randomization of the load address. diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h index 0ffbe688e0069..ae4b7aca66dcc 100644 --- a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h @@ -24,7 +24,7 @@ class DynamicLoaderAIXDYLD : public DynamicLoader { static void Initialize(); static void Terminate(); - static llvm::StringRef GetPluginNameStatic() { return "aix-dyld"; } + static llvm::StringRef GetPluginNameStatic() { return "windows-dyld"; } static llvm::StringRef GetPluginDescriptionStatic(); static DynamicLoader *CreateInstance(Process *process, bool force); @@ -46,9 +46,6 @@ class DynamicLoaderAIXDYLD : public DynamicLoader { protected: lldb::addr_t GetLoadAddress(lldb::ModuleSP executable); - /// Loads Module from inferior process. - void ResolveExecutableModule(lldb::ModuleSP &module_sp); - private: std::map m_loaded_modules; }; >From cff574b36903e12385e5d6cddf4532ba279f47de Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Wed, 12 Feb 2025 07:52:04 -0600 Subject: [PATCH 34/47] Fix for Debugging Attach to AIX Process --- .../AIX-DYLD/DynamicLoaderAIXDYLD.cpp | 97 +++++++++++++++++++ .../AIX-DYLD/DynamicLoaderAIXDYLD.h | 5 +- 2 files changed, 101 insertions(+), 1 deletion(-) diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp index 375d879c7a995..1a98bb9334043 100644 --- a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp @@ -21,6 +21,10 @@ #include "llvm/Support/FileSystem.h" #if defined(_AIX) #include +#include +#include +#include +#include #endif /*#include "llvm/ADT/Triple.h" @@ -133,14 +137,107 @@ bool DynamicLoaderAIXDYLD::NotifyBreakpointHit( } +void DynamicLoaderAIXDYLD::ResolveExecutableModule( + lldb::ModuleSP &module_sp) { + Log *log = GetLog(LLDBLog::DynamicLoader); + + if (m_process == nullptr) + return; + + auto &target = m_process->GetTarget(); + const auto platform_sp = target.GetPlatform(); + + ProcessInstanceInfo process_info; + if (!m_process->GetProcessInfo(process_info)) { + LLDB_LOGF(log, + "DynamicLoaderPOSIXDYLD::%s - failed to get process info for " + "pid %" PRIu64, + __FUNCTION__, m_process->GetID()); + return; + } + + int32long64_t pid = m_process->GetID(); + char cwd[PATH_MAX], resolved_path[PATH_MAX]; + std::string executable_name; + bool path_resolved = false; + psinfo_t psinfo; + + std::string proc_file = "/proc/" + std::to_string(pid) + "/psinfo"; + std::string cwd_link = "/proc/" + std::to_string(pid) + "/cwd"; + std::ifstream file(proc_file, std::ios::binary); + if(!file.is_open()) + LLDB_LOGF(log, "Error: Unable to access process info "); + + file.read(reinterpret_cast(&psinfo), sizeof(psinfo_t)); + if(!file) + LLDB_LOGF(log, "Process info error: Failed to read "); + + std::string relative_path(psinfo.pr_fname); + LLDB_LOGF(log, "Relative path %s",relative_path.c_str()); + + if(readlink(cwd_link.c_str(), cwd, sizeof(cwd)) != -1){ + std::filesystem::path full_path = std::filesystem::path(cwd)/relative_path; + if(realpath(full_path.c_str(), resolved_path)) { + LLDB_LOGF(log, "Resolved Path using process info : %s", resolved_path); + path_resolved = true; + } + else + LLDB_LOGF(log, "Realpath error: Unable to resolve. "); + } + + executable_name = resolved_path; + if(path_resolved == false) { + std::string command_line(psinfo.pr_psargs); + LLDB_LOGF(log, "Command line: %s",command_line.c_str()); + if (!command_line.empty()) { + size_t space1 = command_line.find(' '); + executable_name = command_line.substr(0, space1); + LLDB_LOGF(log, "Resolved path using command line arg %s",executable_name.c_str()); + } + } + + LLDB_LOGF(log, "Executable Name %s",executable_name.c_str()); + process_info.SetExecutableFile(lldb_private::FileSpec(executable_name), + true); + + LLDB_LOGF( + log, "DynamicLoaderPOSIXDYLD::%s - got executable by pid %" PRIu64 ": %s", + __FUNCTION__, m_process->GetID(), + process_info.GetExecutableFile().GetPath().c_str()); + + ModuleSpec module_spec(process_info.GetExecutableFile(), + process_info.GetArchitecture()); + if (module_sp && module_sp->MatchesModuleSpec(module_spec)) + return; + + const auto executable_search_paths(Target::GetDefaultExecutableSearchPaths()); + auto error = platform_sp->ResolveExecutable( + module_spec, module_sp, + !executable_search_paths.IsEmpty() ? &executable_search_paths : nullptr); + if (error.Fail()) { + StreamString stream; + module_spec.Dump(stream); + + LLDB_LOGF(log, + "DynamicLoaderPOSIXDYLD::%s - failed to resolve executable " + "with module spec \"%s\": %s", + __FUNCTION__, stream.GetData(), error.AsCString()); + return; + } + + target.SetExecutableModule(module_sp, eLoadDependentsNo); +} + void DynamicLoaderAIXDYLD::DidAttach() { Log *log = GetLog(LLDBLog::DynamicLoader); LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); ModuleSP executable = GetTargetExecutable(); + ResolveExecutableModule(executable); if (!executable.get()) return; + LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); // Try to fetch the load address of the file from the process, since there // could be randomization of the load address. diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h index ae4b7aca66dcc..0ffbe688e0069 100644 --- a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h @@ -24,7 +24,7 @@ class DynamicLoaderAIXDYLD : public DynamicLoader { static void Initialize(); static void Terminate(); - static llvm::StringRef GetPluginNameStatic() { return "windows-dyld"; } + static llvm::StringRef GetPluginNameStatic() { return "aix-dyld"; } static llvm::StringRef GetPluginDescriptionStatic(); static DynamicLoader *CreateInstance(Process *process, bool force); @@ -46,6 +46,9 @@ class DynamicLoaderAIXDYLD : public DynamicLoader { protected: lldb::addr_t GetLoadAddress(lldb::ModuleSP executable); + /// Loads Module from inferior process. + void ResolveExecutableModule(lldb::ModuleSP &module_sp); + private: std::map m_loaded_modules; }; >From 303fa3bc078d7572702fb302726d4dd1bd13ddb2 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Fri, 21 Feb 2025 06:18:33 -0600 Subject: [PATCH 35/47] Merge branch 'llvm:main' into gh-101657 --- lldb/source/Plugins/Platform/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/lldb/source/Plugins/Platform/CMakeLists.txt b/lldb/source/Plugins/Platform/CMakeLists.txt index f5b0dbdd5e0a9..0220e734b36d1 100644 --- a/lldb/source/Plugins/Platform/CMakeLists.txt +++ b/lldb/source/Plugins/Platform/CMakeLists.txt @@ -9,4 +9,3 @@ add_subdirectory(OpenBSD) add_subdirectory(POSIX) add_subdirectory(QemuUser) add_subdirectory(Windows) -add_subdirectory(AIX) >From 6947dec02bb527f710c1bea3827b2c16d89f308a Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Fri, 21 Feb 2025 07:02:53 -0600 Subject: [PATCH 36/47] Merge branch 'llvm:main' into gh-101657 --- .../Plugins/Platform/AIX/PlatformAIX.cpp | 171 +----------------- 1 file changed, 1 insertion(+), 170 deletions(-) diff --git a/lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp b/lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp index 0d66325d16267..21724d83133e9 100644 --- a/lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp +++ b/lldb/source/Plugins/Platform/AIX/PlatformAIX.cpp @@ -130,12 +130,6 @@ void PlatformAIX::GetStatus(Stream &strm) { #endif } -void PlatformAIX::CalculateTrapHandlerSymbolNames() { - m_trap_handlers.push_back(ConstString("_sigtramp")); - m_trap_handlers.push_back(ConstString("__kernel_rt_sigreturn")); - m_trap_handlers.push_back(ConstString("__restore_rt")); -} - void PlatformAIX::CalculateTrapHandlerSymbolNames() {} lldb::UnwindPlanSP @@ -160,168 +154,5 @@ MmapArgList PlatformAIX::GetMmapArgumentList(const ArchSpec &arch, addr_t addr, } CompilerType PlatformAIX::GetSiginfoType(const llvm::Triple &triple) { - if (!m_type_system_up) - m_type_system_up.reset(new TypeSystemClang("siginfo", triple)); - TypeSystemClang *ast = m_type_system_up.get(); - - bool si_errno_then_code = true; - - switch (triple.getArch()) { - case llvm::Triple::mips: - case llvm::Triple::mipsel: - case llvm::Triple::mips64: - case llvm::Triple::mips64el: - // mips has si_code and si_errno swapped - si_errno_then_code = false; - break; - default: - break; - } - - // generic types - CompilerType int_type = ast->GetBasicType(eBasicTypeInt); - CompilerType uint_type = ast->GetBasicType(eBasicTypeUnsignedInt); - CompilerType short_type = ast->GetBasicType(eBasicTypeShort); - CompilerType long_type = ast->GetBasicType(eBasicTypeLong); - CompilerType voidp_type = ast->GetBasicType(eBasicTypeVoid).GetPointerType(); - - // platform-specific types - CompilerType &pid_type = int_type; - CompilerType &uid_type = uint_type; - CompilerType &clock_type = long_type; - CompilerType &band_type = long_type; - - CompilerType sigval_type = ast->CreateRecordType( - nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "__lldb_sigval_t", - llvm::to_underlying(clang::TagTypeKind::Union), lldb::eLanguageTypeC); - ast->StartTagDeclarationDefinition(sigval_type); - ast->AddFieldToRecordType(sigval_type, "sival_int", int_type, - lldb::eAccessPublic, 0); - ast->AddFieldToRecordType(sigval_type, "sival_ptr", voidp_type, - lldb::eAccessPublic, 0); - ast->CompleteTagDeclarationDefinition(sigval_type); - - CompilerType sigfault_bounds_type = ast->CreateRecordType( - nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "", - llvm::to_underlying(clang::TagTypeKind::Union), lldb::eLanguageTypeC); - ast->StartTagDeclarationDefinition(sigfault_bounds_type); - ast->AddFieldToRecordType(sigfault_bounds_type, "_addr_bnd", - ast->CreateStructForIdentifier(ConstString(), - { - {"_lower", voidp_type}, - {"_upper", voidp_type}, - }), - lldb::eAccessPublic, 0); - ast->AddFieldToRecordType(sigfault_bounds_type, "_pkey", uint_type, - lldb::eAccessPublic, 0); - ast->CompleteTagDeclarationDefinition(sigfault_bounds_type); - - // siginfo_t - CompilerType siginfo_type = ast->CreateRecordType( - nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "__lldb_siginfo_t", - llvm::to_underlying(clang::TagTypeKind::Struct), lldb::eLanguageTypeC); - ast->StartTagDeclarationDefinition(siginfo_type); - ast->AddFieldToRecordType(siginfo_type, "si_signo", int_type, - lldb::eAccessPublic, 0); - - if (si_errno_then_code) { - ast->AddFieldToRecordType(siginfo_type, "si_errno", int_type, - lldb::eAccessPublic, 0); - ast->AddFieldToRecordType(siginfo_type, "si_code", int_type, - lldb::eAccessPublic, 0); - } else { - ast->AddFieldToRecordType(siginfo_type, "si_code", int_type, - lldb::eAccessPublic, 0); - ast->AddFieldToRecordType(siginfo_type, "si_errno", int_type, - lldb::eAccessPublic, 0); - } - - // the structure is padded on 64-bit arches to fix alignment - if (triple.isArch64Bit()) - ast->AddFieldToRecordType(siginfo_type, "__pad0", int_type, - lldb::eAccessPublic, 0); - - // union used to hold the signal data - CompilerType union_type = ast->CreateRecordType( - nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "", - llvm::to_underlying(clang::TagTypeKind::Union), lldb::eLanguageTypeC); - ast->StartTagDeclarationDefinition(union_type); - - ast->AddFieldToRecordType( - union_type, "_kill", - ast->CreateStructForIdentifier(ConstString(), - { - {"si_pid", pid_type}, - {"si_uid", uid_type}, - }), - lldb::eAccessPublic, 0); - - ast->AddFieldToRecordType( - union_type, "_timer", - ast->CreateStructForIdentifier(ConstString(), - { - {"si_tid", int_type}, - {"si_overrun", int_type}, - {"si_sigval", sigval_type}, - }), - lldb::eAccessPublic, 0); - - ast->AddFieldToRecordType( - union_type, "_rt", - ast->CreateStructForIdentifier(ConstString(), - { - {"si_pid", pid_type}, - {"si_uid", uid_type}, - {"si_sigval", sigval_type}, - }), - lldb::eAccessPublic, 0); - - ast->AddFieldToRecordType( - union_type, "_sigchld", - ast->CreateStructForIdentifier(ConstString(), - { - {"si_pid", pid_type}, - {"si_uid", uid_type}, - {"si_status", int_type}, - {"si_utime", clock_type}, - {"si_stime", clock_type}, - }), - lldb::eAccessPublic, 0); - - ast->AddFieldToRecordType( - union_type, "_sigfault", - ast->CreateStructForIdentifier(ConstString(), - { - {"si_addr", voidp_type}, - {"si_addr_lsb", short_type}, - {"_bounds", sigfault_bounds_type}, - }), - lldb::eAccessPublic, 0); - - ast->AddFieldToRecordType( - union_type, "_sigpoll", - ast->CreateStructForIdentifier(ConstString(), - { - {"si_band", band_type}, - {"si_fd", int_type}, - }), - lldb::eAccessPublic, 0); - - // NB: SIGSYS is not present on ia64 but we don't seem to support that - ast->AddFieldToRecordType( - union_type, "_sigsys", - ast->CreateStructForIdentifier(ConstString(), - { - {"_call_addr", voidp_type}, - {"_syscall", int_type}, - {"_arch", uint_type}, - }), - lldb::eAccessPublic, 0); - - ast->CompleteTagDeclarationDefinition(union_type); - ast->AddFieldToRecordType(siginfo_type, "_sifields", union_type, - lldb::eAccessPublic, 0); - - ast->CompleteTagDeclarationDefinition(siginfo_type); - return siginfo_type; + return CompilerType(); } >From 24615119addcdf9fe5a8c5b2ebd0c15d75651279 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Sat, 22 Feb 2025 03:42:26 -0600 Subject: [PATCH 37/47] Removed un-needed changes --- lldb/include/lldb/Host/aix/AbstractSocket.h | 25 --------------------- lldb/include/lldb/Host/aix/Uio.h | 23 ------------------- lldb/source/Host/CMakeLists.txt | 1 - lldb/source/Host/aix/AbstractSocket.cpp | 20 ----------------- 4 files changed, 69 deletions(-) delete mode 100644 lldb/include/lldb/Host/aix/AbstractSocket.h delete mode 100644 lldb/include/lldb/Host/aix/Uio.h delete mode 100644 lldb/source/Host/aix/AbstractSocket.cpp diff --git a/lldb/include/lldb/Host/aix/AbstractSocket.h b/lldb/include/lldb/Host/aix/AbstractSocket.h deleted file mode 100644 index accfd01457a5e..0000000000000 --- a/lldb/include/lldb/Host/aix/AbstractSocket.h +++ /dev/null @@ -1,25 +0,0 @@ -//===-- AbstractSocket.h ----------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef liblldb_AbstractSocket_h_ -#define liblldb_AbstractSocket_h_ - -#include "lldb/Host/posix/DomainSocket.h" - -namespace lldb_private { -class AbstractSocket : public DomainSocket { -public: - AbstractSocket(); - -protected: - size_t GetNameOffset() const override; - void DeleteSocketFile(llvm::StringRef name) override; -}; -} - -#endif // ifndef liblldb_AbstractSocket_h_ diff --git a/lldb/include/lldb/Host/aix/Uio.h b/lldb/include/lldb/Host/aix/Uio.h deleted file mode 100644 index acf79ecc6a1d0..0000000000000 --- a/lldb/include/lldb/Host/aix/Uio.h +++ /dev/null @@ -1,23 +0,0 @@ -//===-- Uio.h ---------------------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef liblldb_Host_aix_Uio_h_ -#define liblldb_Host_aix_Uio_h_ - -#include "lldb/Host/Config.h" -#include - -// We shall provide our own implementation of process_vm_readv if it is not -// present -#if !HAVE_PROCESS_VM_READV -ssize_t process_vm_readv(::pid_t pid, const struct iovec *local_iov, - unsigned long liovcnt, const struct iovec *remote_iov, - unsigned long riovcnt, unsigned long flags); -#endif - -#endif // liblldb_Host_aix_Uio_h_ diff --git a/lldb/source/Host/CMakeLists.txt b/lldb/source/Host/CMakeLists.txt index bb6b5befa16e4..5a14b4c629825 100644 --- a/lldb/source/Host/CMakeLists.txt +++ b/lldb/source/Host/CMakeLists.txt @@ -140,7 +140,6 @@ else() elseif (CMAKE_SYSTEM_NAME MATCHES "AIX") add_host_subdirectory(aix - aix/AbstractSocket.cpp aix/Host.cpp aix/HostInfoAIX.cpp aix/Support.cpp diff --git a/lldb/source/Host/aix/AbstractSocket.cpp b/lldb/source/Host/aix/AbstractSocket.cpp deleted file mode 100644 index fddf78f54f46d..0000000000000 --- a/lldb/source/Host/aix/AbstractSocket.cpp +++ /dev/null @@ -1,20 +0,0 @@ -//===-- AbstractSocket.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 "lldb/Host/aix/AbstractSocket.h" - -#include "llvm/ADT/StringRef.h" - -using namespace lldb; -using namespace lldb_private; - -AbstractSocket::AbstractSocket() : DomainSocket(ProtocolUnixAbstract) {} - -size_t AbstractSocket::GetNameOffset() const { return 1; } - -void AbstractSocket::DeleteSocketFile(llvm::StringRef name) {} >From 25bea9ca48dc458c1dddd72f10a483e76926a04e Mon Sep 17 00:00:00 2001 From: HemangGadhavi Date: Wed, 26 Feb 2025 01:10:15 -0600 Subject: [PATCH 38/47] Resolving coredump issue while attach with library calls --- lldb/include/lldb/Core/ModuleSpec.h | 2 ++ lldb/source/Core/ModuleList.cpp | 12 +++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lldb/include/lldb/Core/ModuleSpec.h b/lldb/include/lldb/Core/ModuleSpec.h index 4fe06412b6b0b..9d79992b48c7e 100644 --- a/lldb/include/lldb/Core/ModuleSpec.h +++ b/lldb/include/lldb/Core/ModuleSpec.h @@ -122,6 +122,8 @@ class ModuleSpec { ConstString &GetObjectName() { return m_object_name; } ConstString GetObjectName() const { return m_object_name; } + + void SetObjectName(ConstString objName) { m_object_name = objName; } uint64_t GetObjectOffset() const { return m_object_offset; } diff --git a/lldb/source/Core/ModuleList.cpp b/lldb/source/Core/ModuleList.cpp index 2b8ccab2406c6..862a2729c1afb 100644 --- a/lldb/source/Core/ModuleList.cpp +++ b/lldb/source/Core/ModuleList.cpp @@ -260,7 +260,17 @@ void ModuleList::ReplaceEquivalent( module_sp->GetArchitecture()); equivalent_module_spec.GetPlatformFileSpec() = module_sp->GetPlatformFileSpec(); - +#ifdef _AIX + // To remove the exact equivalent module, the object name must be + // specified. When the equivalent_module_spec object is created, its + // object name is initially set to NULL. This is because the module_sp's + // GetPath() returns a path in the format (/usr/ccs/libc.a), which does + // not include the object name. As a result, MatchesModuleSpec may return + // true even though the object name is NULL and doesn't match any loaded + // module. To fix this, set the object name of the equivalent_module_spec + // to be the same as the object name of the module_sp. */ + equivalent_module_spec.SetObjectName(module_sp->GetObjectName()); +#endif size_t idx = 0; while (idx < m_modules.size()) { ModuleSP test_module_sp(m_modules[idx]); >From 349ec0064668a0ee1d3af7c2f38fa7427b4b2d36 Mon Sep 17 00:00:00 2001 From: Dhruv Srivastava Date: Fri, 28 Feb 2025 23:44:40 +0530 Subject: [PATCH 39/47] [AIX][Coredump] AIX Coredump debugging Implementation (#25) Creates a general framework to be able to debug an AIX generated coredump file. At this point, we are only supporting 64-bit core files using this general framework. With this implementation, LLDB can recognise and debug any 64-bit AIX coredump file. Most of the generic debugging commands work after this: # bin/lldb --core /home/dhruv/LLDB/tests/core (lldb) target create --core "/home/dhruv/LLDB/tests/core" Core file '/home/dhruv/LLDB/tests/core' (powerpc64) was loaded. (lldb) bt * thread #1, stop reason = SIGSEGV * frame #0: 0x0000000100000940 coretest64`main at core.c:5 frame #1: 0x00000001000004ac coretest64`__start + 116 (lldb) process status Process 18446744071562067991 stopped * thread #1, stop reason = SIGSEGV frame #0: 0x0000000100000940 coretest64`main at core.c:5 2 char *str; 3 int a = 10; 4 str = "GfG"; -> 5 *(str+1) = 'n'; 6 return a; 7 } And others like memory read, image list, image dump sections, disassembly etc --- .../AIX-DYLD/DynamicLoaderAIXDYLD.cpp | 62 ++++- .../AIX-DYLD/DynamicLoaderAIXDYLD.h | 6 + .../Plugins/ObjectFile/AIXCore/CMakeLists.txt | 13 + .../ObjectFile/AIXCore/ObjectFileAIXCore.cpp | 254 ++++++++++++++++++ .../ObjectFile/AIXCore/ObjectFileAIXCore.h | 121 +++++++++ lldb/source/Plugins/ObjectFile/CMakeLists.txt | 1 + lldb/source/Plugins/Process/CMakeLists.txt | 1 + .../Plugins/Process/aix-core/AIXCore.cpp | 116 ++++++++ .../source/Plugins/Process/aix-core/AIXCore.h | 125 +++++++++ .../Plugins/Process/aix-core/CMakeLists.txt | 16 ++ .../Process/aix-core/ProcessAIXCore.cpp | 251 +++++++++++++++++ .../Plugins/Process/aix-core/ProcessAIXCore.h | 100 +++++++ .../aix-core/RegisterContextCoreAIX_ppc64.cpp | 136 ++++++++++ .../aix-core/RegisterContextCoreAIX_ppc64.h | 46 ++++ .../Process/aix-core/ThreadAIXCore.cpp | 127 +++++++++ .../Plugins/Process/aix-core/ThreadAIXCore.h | 110 ++++++++ 16 files changed, 1484 insertions(+), 1 deletion(-) create mode 100644 lldb/source/Plugins/ObjectFile/AIXCore/CMakeLists.txt create mode 100644 lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.cpp create mode 100644 lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.h create mode 100644 lldb/source/Plugins/Process/aix-core/AIXCore.cpp create mode 100644 lldb/source/Plugins/Process/aix-core/AIXCore.h create mode 100644 lldb/source/Plugins/Process/aix-core/CMakeLists.txt create mode 100644 lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp create mode 100644 lldb/source/Plugins/Process/aix-core/ProcessAIXCore.h create mode 100644 lldb/source/Plugins/Process/aix-core/RegisterContextCoreAIX_ppc64.cpp create mode 100644 lldb/source/Plugins/Process/aix-core/RegisterContextCoreAIX_ppc64.h create mode 100644 lldb/source/Plugins/Process/aix-core/ThreadAIXCore.cpp create mode 100644 lldb/source/Plugins/Process/aix-core/ThreadAIXCore.h diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp index 1a98bb9334043..7e44ffbb1c051 100644 --- a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp @@ -228,6 +228,65 @@ void DynamicLoaderAIXDYLD::ResolveExecutableModule( target.SetExecutableModule(module_sp, eLoadDependentsNo); } +bool DynamicLoaderAIXDYLD::IsCoreFile() const { + return !m_process->IsLiveDebugSession(); +} + +void DynamicLoaderAIXDYLD::FillCoreLoaderData(lldb_private::DataExtractor &data, + uint64_t loader_offset, uint64_t loader_size ) { + + static char *buffer = (char *)malloc(loader_size); + struct ld_info ldinfo[64]; + char *buffer_complete; + struct ld_info *ptr; + int i = 0; + + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); + ByteOrder byteorder = data.GetByteOrder(); + data.ExtractBytes(loader_offset, loader_size, eByteOrderBig, buffer); + buffer_complete = buffer + loader_size; + ldinfo[0].ldinfo_next = 1; + + while (ldinfo[i++].ldinfo_next != 0) { + + ptr = (struct ld_info *)buffer; + ldinfo[i].ldinfo_next = ptr->ldinfo_next; + ldinfo[i].ldinfo_flags = ptr->ldinfo_flags; + ldinfo[i].ldinfo_core = ptr->ldinfo_core; + ldinfo[i].ldinfo_textorg = ptr->ldinfo_textorg; + ldinfo[i].ldinfo_textsize = ptr->ldinfo_textsize; + ldinfo[i].ldinfo_dataorg = ptr->ldinfo_dataorg; + ldinfo[i].ldinfo_datasize = ptr->ldinfo_datasize; + + char *filename = &ptr->ldinfo_filename[0]; + char *membername = filename + (strlen(filename) + 1); + strcpy(ldinfo[i].ldinfo_filename, filename); + + buffer += ptr->ldinfo_next; + struct ld_info *ptr2 = &(ldinfo[i]); + char *pathName = ptr2->ldinfo_filename; + char pathWithMember[PATH_MAX] = {0}; + if (strlen(membername) > 0) { + sprintf(pathWithMember, "%s(%s)", pathName, membername); + } else { + sprintf(pathWithMember, "%s", pathName); + } + + FileSpec file(pathWithMember); + ModuleSpec module_spec(file, m_process->GetTarget().GetArchitecture()); + LLDB_LOGF(log, "Module :%s", pathWithMember); + if (ModuleSP module_sp = m_process->GetTarget().GetOrCreateModule(module_spec, true /* notify */)) { + UpdateLoadedSectionsByType(module_sp, LLDB_INVALID_ADDRESS, (lldb::addr_t)ptr2->ldinfo_textorg, false, 1); + UpdateLoadedSectionsByType(module_sp, LLDB_INVALID_ADDRESS, (lldb::addr_t)ptr2->ldinfo_dataorg, false, 2); + // FIXME: .tdata, .bss + } + if (ptr2->ldinfo_next == 0) { + ptr2 = nullptr; + } + } +} + void DynamicLoaderAIXDYLD::DidAttach() { Log *log = GetLog(LLDBLog::DynamicLoader); LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); @@ -361,7 +420,8 @@ void DynamicLoaderAIXDYLD::DidLaunch() { #endif } -Status DynamicLoaderAIXDYLD::CanLoadImage() { return Status(); } +Status DynamicLoaderAIXDYLD::CanLoadImage() { + return Status(); } ThreadPlanSP DynamicLoaderAIXDYLD::GetStepThroughTrampolinePlan(Thread &thread, diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h index 0ffbe688e0069..097f8d048b77f 100644 --- a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h @@ -33,6 +33,9 @@ class DynamicLoaderAIXDYLD : public DynamicLoader { lldb::addr_t module_addr); void OnUnloadModule(lldb::addr_t module_addr); + void FillCoreLoaderData(lldb_private::DataExtractor &data, + uint64_t loader_offset, uint64_t loader_size); + void DidAttach() override; void DidLaunch() override; Status CanLoadImage() override; @@ -49,6 +52,9 @@ class DynamicLoaderAIXDYLD : public DynamicLoader { /// Loads Module from inferior process. void ResolveExecutableModule(lldb::ModuleSP &module_sp); + /// Returns true if the process is for a core file. + bool IsCoreFile() const; + private: std::map m_loaded_modules; }; diff --git a/lldb/source/Plugins/ObjectFile/AIXCore/CMakeLists.txt b/lldb/source/Plugins/ObjectFile/AIXCore/CMakeLists.txt new file mode 100644 index 0000000000000..5656b33a61726 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/AIXCore/CMakeLists.txt @@ -0,0 +1,13 @@ +add_lldb_library(lldbPluginObjectFileAIXCore PLUGIN + ObjectFileAIXCore.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbSymbol + lldbTarget + lldbUtility + lldbPluginProcessUtility + LINK_COMPONENTS + Support + ) diff --git a/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.cpp b/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.cpp new file mode 100644 index 0000000000000..5158fa4e25077 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.cpp @@ -0,0 +1,254 @@ +//===-- ObjectFileAIXCore.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 "ObjectFileAIXCore.h" + +#include +#include +#include +#include + +#include "lldb/Utility/FileSpecList.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Stream.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/XCOFF.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Object/XCOFFObjectFile.h" + +using namespace llvm; +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(ObjectFileAIXCore) + +enum CoreVersion : uint64_t {AIXCORE32 = 0xFEEDDB1, AIXCORE64 = 0xFEEDDB2}; + +bool m_is_core = false; + +// Static methods. +void ObjectFileAIXCore::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + CreateMemoryInstance, GetModuleSpecifications); +} + +void ObjectFileAIXCore::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +ObjectFile *ObjectFileAIXCore::CreateInstance(const lldb::ModuleSP &module_sp, + DataBufferSP data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec *file, + lldb::offset_t file_offset, + lldb::offset_t length) { + + if(m_is_core) + { + + bool mapped_writable = false; + if (!data_sp) { + data_sp = MapFileDataWritable(*file, length, file_offset); + if (!data_sp) + return nullptr; + data_offset = 0; + mapped_writable = true; + } + + assert(data_sp); + + const uint8_t *magic = data_sp->GetBytes() + data_offset; + + // Update the data to contain the entire file if it doesn't already + if (data_sp->GetByteSize() < length) { + data_sp = MapFileDataWritable(*file, length, file_offset); + if (!data_sp) + return nullptr; + data_offset = 0; + mapped_writable = true; + magic = data_sp->GetBytes(); + } + + // If we didn't map the data as writable take ownership of the buffer. + if (!mapped_writable) { + data_sp = std::make_shared(data_sp->GetBytes(), + data_sp->GetByteSize()); + data_offset = 0; + magic = data_sp->GetBytes(); + } + + std::unique_ptr objfile_up(new ObjectFileAIXCore( + module_sp, data_sp, data_offset, file, file_offset, length)); + ArchSpec spec = objfile_up->GetArchitecture(); + objfile_up->SetModulesArchitecture(spec); + return objfile_up.release(); + + } +} + +ObjectFile *ObjectFileAIXCore::CreateMemoryInstance( + const lldb::ModuleSP &module_sp, WritableDataBufferSP data_sp, + const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) { + return nullptr; +} + +size_t ObjectFileAIXCore::GetModuleSpecifications( + const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, lldb::offset_t file_offset, + lldb::offset_t length, lldb_private::ModuleSpecList &specs) { + const size_t initial_count = specs.GetSize(); + + if (ObjectFileAIXCore::MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) { + // Need new ArchType??? + ArchSpec arch_spec = ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); + ModuleSpec spec(file, arch_spec); + spec.GetArchitecture().SetArchitecture(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE, llvm::Triple::AIX); + specs.Append(spec); + } + return specs.GetSize() - initial_count; +} + +static uint32_t AIXCoreHeaderCheckFromMagic(uint32_t magic) { + + Log *log = GetLog(LLDBLog::Modules); + switch (magic) { + case AIXCORE32: + LLDB_LOGF(log, "ObjectFileAIXCore: 32-bit not supported"); + break; + case AIXCORE64: + m_is_core = true; + return 1; + break; + } + return 0; +} + +bool ObjectFileAIXCore::MagicBytesMatch(DataBufferSP &data_sp, + lldb::addr_t data_offset, + lldb::addr_t data_length) { + lldb_private::DataExtractor data; + data.SetData(data_sp, data_offset, data_length); + lldb::offset_t offset = 0; + offset += 4; // Skipping to the coredump version + uint32_t magic = data.GetU32(&offset); + return AIXCoreHeaderCheckFromMagic(magic) != 0; +} + +bool ObjectFileAIXCore::ParseHeader() { + + return false; +} + +ByteOrder ObjectFileAIXCore::GetByteOrder() const { + return eByteOrderBig; +} + +bool ObjectFileAIXCore::IsExecutable() const { + return false; +} + +uint32_t ObjectFileAIXCore::GetAddressByteSize() const { + return 8; +} + +AddressClass ObjectFileAIXCore::GetAddressClass(addr_t file_addr) { + return AddressClass::eUnknown; +} + +lldb::SymbolType ObjectFileAIXCore::MapSymbolType(llvm::object::SymbolRef::Type sym_type) { + if (sym_type == llvm::object::SymbolRef::ST_Function) + return lldb::eSymbolTypeCode; + else if (sym_type == llvm::object::SymbolRef::ST_Data) + return lldb::eSymbolTypeData; + return lldb::eSymbolTypeInvalid; +} + +void ObjectFileAIXCore::ParseSymtab(Symtab &lldb_symtab) { +} + +bool ObjectFileAIXCore::IsStripped() { + return false; +} + +void ObjectFileAIXCore::CreateSections(SectionList &unified_section_list) { +} + +void ObjectFileAIXCore::Dump(Stream *s) { +} + +ArchSpec ObjectFileAIXCore::GetArchitecture() { + ArchSpec arch_spec = ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); + return arch_spec; +} + +UUID ObjectFileAIXCore::GetUUID() { + return UUID(); +} + +uint32_t ObjectFileAIXCore::GetDependentModules(FileSpecList &files) { + + auto original_size = files.GetSize(); + return files.GetSize() - original_size; +} + +Address ObjectFileAIXCore::GetImageInfoAddress(Target *target) { + return Address(); +} + +lldb_private::Address ObjectFileAIXCore::GetBaseAddress() { + return lldb_private::Address(); +} +ObjectFile::Type ObjectFileAIXCore::CalculateType() { + return eTypeCoreFile; +} + +ObjectFile::Strata ObjectFileAIXCore::CalculateStrata() { + return eStrataUnknown; +} + +std::vector +ObjectFileAIXCore::GetLoadableData(Target &target) { + std::vector loadables; + return loadables; +} + +lldb::WritableDataBufferSP +ObjectFileAIXCore::MapFileDataWritable(const FileSpec &file, uint64_t Size, + uint64_t Offset) { + return FileSystem::Instance().CreateWritableDataBuffer(file.GetPath(), Size, + Offset); +} + +ObjectFileAIXCore::ObjectFileAIXCore(const lldb::ModuleSP &module_sp, + DataBufferSP data_sp, lldb::offset_t data_offset, + const FileSpec *file, lldb::offset_t file_offset, + lldb::offset_t length) + : ObjectFile(module_sp, file, file_offset, length, data_sp, data_offset) + { + if (file) + m_file = *file; +} + +ObjectFileAIXCore::ObjectFileAIXCore(const lldb::ModuleSP &module_sp, + DataBufferSP header_data_sp, + const lldb::ProcessSP &process_sp, + addr_t header_addr) + : ObjectFile(module_sp, process_sp, header_addr, header_data_sp) + { +} diff --git a/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.h b/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.h new file mode 100644 index 0000000000000..5dbd78d919bb6 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.h @@ -0,0 +1,121 @@ +//===-- ObjectFileAIXCore.h --------------------------------------- -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_OBJECTFILE_AIXCORE_OBJECTFILEAIXCORE_H +#define LLDB_SOURCE_PLUGINS_OBJECTFILE_AIXCORE_OBJECTFILEAIXCORE_H + +#include + +#include + +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/UUID.h" +#include "lldb/lldb-private.h" +#include "llvm/Object/XCOFFObjectFile.h" + +/// \class ObjectFileAIXCore +/// Generic AIX CORE object file reader. +/// +/// This class provides a generic AIX Core (32/64 bit) reader plugin implementing +/// the ObjectFile protocol. +class ObjectFileAIXCore : public lldb_private::ObjectFile { +public: + // Static Functions + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "aix-core-obj"; } + + static llvm::StringRef GetPluginDescriptionStatic() { + return "AIX core object file reader."; + } + + static lldb_private::ObjectFile * + CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP data_sp, + lldb::offset_t data_offset, const lldb_private::FileSpec *file, + lldb::offset_t file_offset, lldb::offset_t length); + + static lldb_private::ObjectFile *CreateMemoryInstance( + const lldb::ModuleSP &module_sp, lldb::WritableDataBufferSP data_sp, + const lldb::ProcessSP &process_sp, lldb::addr_t header_addr); + + static size_t GetModuleSpecifications(const lldb_private::FileSpec &file, + lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb_private::ModuleSpecList &specs); + + static bool MagicBytesMatch(lldb::DataBufferSP &data_sp, lldb::addr_t offset, + lldb::addr_t length); + + static lldb::SymbolType MapSymbolType(llvm::object::SymbolRef::Type sym_type); + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + // ObjectFile Protocol. + bool ParseHeader() override; + + lldb::ByteOrder GetByteOrder() const override; + + bool IsExecutable() const override; + + uint32_t GetAddressByteSize() const override; + + lldb_private::AddressClass GetAddressClass(lldb::addr_t file_addr) override; + + void ParseSymtab(lldb_private::Symtab &symtab) override; + + bool IsStripped() override; + + void CreateSections(lldb_private::SectionList &unified_section_list) override; + + void Dump(lldb_private::Stream *s) override; + + lldb_private::ArchSpec GetArchitecture() override; + + lldb_private::UUID GetUUID() override; + + uint32_t GetDependentModules(lldb_private::FileSpecList &files) override; + + lldb_private::Address + GetImageInfoAddress(lldb_private::Target *target) override; + lldb_private::Address GetBaseAddress() override; + + ObjectFile::Type CalculateType() override; + + ObjectFile::Strata CalculateStrata() override; + + ObjectFileAIXCore(const lldb::ModuleSP &module_sp, lldb::DataBufferSP data_sp, + lldb::offset_t data_offset, const lldb_private::FileSpec *file, + lldb::offset_t offset, lldb::offset_t length); + + ObjectFileAIXCore(const lldb::ModuleSP &module_sp, + lldb::DataBufferSP header_data_sp, + const lldb::ProcessSP &process_sp, lldb::addr_t header_addr); + +protected: + + static bool ParseAIXCoreHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset_ptr + ); + + std::vector + GetLoadableData(lldb_private::Target &target) override; + + static lldb::WritableDataBufferSP + MapFileDataWritable(const lldb_private::FileSpec &file, uint64_t Size, + uint64_t Offset); + +}; + +#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_AIXCORE_OBJECTFILEAIXCORE_H diff --git a/lldb/source/Plugins/ObjectFile/CMakeLists.txt b/lldb/source/Plugins/ObjectFile/CMakeLists.txt index 7abd0c96f4fd7..1605356fdb7f1 100644 --- a/lldb/source/Plugins/ObjectFile/CMakeLists.txt +++ b/lldb/source/Plugins/ObjectFile/CMakeLists.txt @@ -9,3 +9,4 @@ add_subdirectory(PECOFF) add_subdirectory(XCOFF) add_subdirectory(Placeholder) add_subdirectory(wasm) +add_subdirectory(AIXCore) diff --git a/lldb/source/Plugins/Process/CMakeLists.txt b/lldb/source/Plugins/Process/CMakeLists.txt index 058b4b9ad2157..0b66ea18c82ce 100644 --- a/lldb/source/Plugins/Process/CMakeLists.txt +++ b/lldb/source/Plugins/Process/CMakeLists.txt @@ -24,3 +24,4 @@ add_subdirectory(elf-core) add_subdirectory(mach-core) add_subdirectory(minidump) add_subdirectory(FreeBSDKernel) +add_subdirectory(aix-core) diff --git a/lldb/source/Plugins/Process/aix-core/AIXCore.cpp b/lldb/source/Plugins/Process/aix-core/AIXCore.cpp new file mode 100644 index 0000000000000..95e47b4d8be53 --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/AIXCore.cpp @@ -0,0 +1,116 @@ +//===-- AIXCore.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 + +#include "lldb/Core/Section.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" + +#include "AIXCore.h" + +using namespace AIXCORE; +using namespace lldb; +using namespace lldb_private; + +AIXCore64Header::AIXCore64Header() { memset(this, 0, sizeof(AIXCore64Header)); } + + +bool AIXCore64Header::ParseRegisterContext(lldb_private::DataExtractor &data, + lldb::offset_t *offset) { + // The data is arranged in this order in this coredump file + // so we have to fetch in this exact order. But need to change + // the context structure order according to Infos_ppc64 + for(int i = 0; i < 32; i++) + Fault.context.gpr[i] = data.GetU64(offset); + Fault.context.msr = data.GetU64(offset); + Fault.context.pc = data.GetU64(offset); + Fault.context.lr = data.GetU64(offset); + Fault.context.ctr = data.GetU64(offset); + Fault.context.cr = data.GetU32(offset); + Fault.context.xer = data.GetU32(offset); + Fault.context.fpscr = data.GetU32(offset); + Fault.context.fpscrx = data.GetU32(offset); + Fault.context.except[0] = data.GetU64(offset); + for(int i = 0; i < 32; i++) + Fault.context.fpr[i] = data.GetU64(offset); + Fault.context.fpeu = data.GetU8(offset); + Fault.context.fpinfo = data.GetU8(offset); + Fault.context.fpscr24_31 = data.GetU8(offset); + Fault.context.pad[0] = data.GetU8(offset); + Fault.context.excp_type = data.GetU32(offset); + + return true; +} +bool AIXCore64Header::ParseThreadContext(lldb_private::DataExtractor &data, + lldb::offset_t *offset) { + + lldb::offset_t offset_to_regctx = *offset; + offset_to_regctx += sizeof(thrdentry64); + Fault.thread.ti_tid = data.GetU64(offset); + Fault.thread.ti_pid = data.GetU32(offset); + int ret = ParseRegisterContext(data, &offset_to_regctx); + return true; +} + +bool AIXCore64Header::ParseUserData(lldb_private::DataExtractor &data, + lldb::offset_t *offset) { + User.process.pi_pid = data.GetU32(offset); + User.process.pi_ppid = data.GetU32(offset); + User.process.pi_sid = data.GetU32(offset); + User.process.pi_pgrp = data.GetU32(offset); + User.process.pi_uid = data.GetU32(offset); + User.process.pi_suid = data.GetU32(offset); + + *offset += 76; + + ByteOrder byteorder = data.GetByteOrder(); + size_t size = 33; + data.ExtractBytes(*offset, size, byteorder, User.process.pi_comm); + offset += size; + + return true; +} + +bool AIXCore64Header::ParseCoreHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset) { + + SignalNum = data.GetU8(offset); + Flag = data.GetU8(offset); + Entries = data.GetU16(offset); + Version = data.GetU32(offset); + FDInfo = data.GetU64(offset); + + LoaderOffset = data.GetU64(offset); + LoaderSize = data.GetU64(offset); + NumberOfThreads = data.GetU32(offset); + Reserved0 = data.GetU32(offset); + ThreadContextOffset = data.GetU64(offset); + NumSegRegion = data.GetU64(offset); + SegRegionOffset = data.GetU64(offset); + StackOffset = data.GetU64(offset); + StackBaseAddr = data.GetU64(offset); + StackSize = data.GetU64(offset); + DataRegionOffset = data.GetU64(offset); + DataBaseAddr = data.GetU64(offset); + DataSize = data.GetU64(offset); + + *offset += 104; + lldb::offset_t offset_to_user = (*offset + sizeof(ThreadContext64)); + int ret = 0; + ret = ParseThreadContext(data, offset); + ret = ParseUserData(data, &offset_to_user); + + return true; + +} + diff --git a/lldb/source/Plugins/Process/aix-core/AIXCore.h b/lldb/source/Plugins/Process/aix-core/AIXCore.h new file mode 100644 index 0000000000000..3d78d5e92c7ab --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/AIXCore.h @@ -0,0 +1,125 @@ +//===-- AIXCore.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Notes about AIX Process core dumps: +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_AIXCORE_H +#define LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_AIXCORE_H + +#include "llvm/ADT/StringRef.h" +#include +#include +#include + +#include +#include + +namespace AIXCORE { + +struct RegContext { + // The data is arranged in order as filled by AIXCore.cpp in this coredump file + // so we have to fetch in that exact order, refer there. + // But need to change + // the context structure in order according to Infos_ppc64 + uint64_t gpr[32]; /* 64-bit gprs */ + unsigned long pc; /* msr */ + unsigned long msr; /* iar */ + unsigned long origr3; /* iar */ + unsigned long ctr; /* CTR */ + unsigned long lr; /* LR */ + unsigned long xer; /* XER */ + unsigned long cr; /* CR */ + unsigned long softe; /* CR */ + unsigned long trap; /* CR */ + unsigned int fpscr; /* floating pt status reg */ + unsigned int fpscrx; /* software ext to fpscr */ + unsigned long except[1]; /* exception address */ + double fpr[32]; /* floating pt regs */ + char fpeu; /* floating pt ever used */ + char fpinfo; /* floating pt info */ + char fpscr24_31; /* bits 24-31 of 64-bit FPSCR */ + char pad[1]; + int excp_type; /* exception type */ +}; + + struct ThreadContext64 { + struct thrdentry64 thread; + struct RegContext context; + }; + + struct UserData { + + struct procentry64 process; + unsigned long long reserved[16]; + }; + + struct AIXCore64Header { + + int8_t SignalNum; /* signal number (cause of error) */ + int8_t Flag; /* flag to describe core dump type */ + uint16_t Entries; /* number of core dump modules */ + uint32_t Version; /* core file format number */ + uint64_t FDInfo; /* offset to fd region in file */ + + uint64_t LoaderOffset; /* offset to loader region in file */ + uint64_t LoaderSize; /* size of loader region */ + + uint32_t NumberOfThreads ; /* number of elements in thread table */ + uint32_t Reserved0; /* Padding */ + uint64_t ThreadContextOffset; /* offset to thread context table */ + + uint64_t NumSegRegion; /* n of elements in segregion */ + uint64_t SegRegionOffset; /* offset to start of segregion table */ + + uint64_t StackOffset; /* offset of user stack in file */ + uint64_t StackBaseAddr; /* base address of user stack region */ + uint64_t StackSize; /* size of user stack region */ + + uint64_t DataRegionOffset; /* offset to user data region */ + uint64_t DataBaseAddr; /* base address of user data region */ + uint64_t DataSize; /* size of user data region */ + uint64_t SDataBase; /* base address of sdata region */ + uint64_t SDataSize; /* size of sdata region */ + + uint64_t NumVMRegions; /* number of anonymously mapped areas */ + uint64_t VMOffset; /* offset to start of vm_infox table */ + + int32_t ProcessorImplementation; /* processor implementation */ + uint32_t NumElementsCTX; /* n of elements in extended ctx table*/ + uint64_t CPRSOffset; /* Checkpoint/Restart offset */ + uint64_t ExtendedContextOffset; /* extended context offset */ + uint64_t OffsetUserKey; /* Offset to user-key exception data */ + uint64_t OffsetLoaderTLS; /* offset to the loader region in file + when a process uses TLS data */ + uint64_t TLSLoaderSize; /* size of the above loader region */ + uint64_t ExtendedProcEntry; /* Extended procentry64 information */ + uint64_t Reserved[2]; + + struct ThreadContext64 Fault; + + struct UserData User; + + AIXCore64Header(); + + bool ParseCoreHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset); + bool ParseThreadContext(lldb_private::DataExtractor &data, + lldb::offset_t *offset); + bool ParseUserData(lldb_private::DataExtractor &data, + lldb::offset_t *offset); + bool ParseRegisterContext(lldb_private::DataExtractor &data, + lldb::offset_t *offset); + bool ParseLoaderData(lldb_private::DataExtractor &data, + lldb::offset_t *offset); + + }; + + +} + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_AIXCORE_H diff --git a/lldb/source/Plugins/Process/aix-core/CMakeLists.txt b/lldb/source/Plugins/Process/aix-core/CMakeLists.txt new file mode 100644 index 0000000000000..347717a362491 --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/CMakeLists.txt @@ -0,0 +1,16 @@ +add_definitions("-D_ALL_SOURCE") + +add_lldb_library(lldbPluginProcessAIXCore PLUGIN + ProcessAIXCore.cpp + AIXCore.cpp + ThreadAIXCore.cpp + RegisterContextCoreAIX_ppc64.cpp + + LINK_LIBS + lldbCore + lldbTarget + lldbPluginProcessUtility + LINK_COMPONENTS + BinaryFormat + Support + ) diff --git a/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp b/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp new file mode 100644 index 0000000000000..9300aa14ac4db --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp @@ -0,0 +1,251 @@ +//===-- ProcessAIXCore.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 + +#include +#include + +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" + +#include "llvm/Support/Threading.h" +#include "Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h" + +#include "ProcessAIXCore.h" +#include "AIXCore.h" +#include "ThreadAIXCore.h" + +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(ProcessAIXCore) + +llvm::StringRef ProcessAIXCore::GetPluginDescriptionStatic() { + return "AIX core dump plug-in."; +} + +void ProcessAIXCore::Initialize() { + static llvm::once_flag g_once_flag; + + llvm::call_once(g_once_flag, []() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); + }); +} + +void ProcessAIXCore::Terminate() { + PluginManager::UnregisterPlugin(ProcessAIXCore::CreateInstance); +} + +lldb::ProcessSP ProcessAIXCore::CreateInstance(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec *crash_file, + bool can_connect) { + lldb::ProcessSP process_sp; + if (crash_file && !can_connect) { + const size_t header_size = sizeof(AIXCORE::AIXCore64Header); + + auto data_sp = FileSystem::Instance().CreateDataBuffer( + crash_file->GetPath(), header_size, 0); + + if (data_sp && data_sp->GetByteSize() == header_size) { + AIXCORE::AIXCore64Header aixcore_header; + DataExtractor data(data_sp, lldb::eByteOrderBig, 4); + lldb::offset_t data_offset = 0; + if(aixcore_header.ParseCoreHeader(data, &data_offset)) { + process_sp = std::make_shared(target_sp, listener_sp, + *crash_file); + } + } + + } + return process_sp; +} + +// ProcessAIXCore constructor +ProcessAIXCore::ProcessAIXCore(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec &core_file) + : PostMortemProcess(target_sp, listener_sp, core_file) {} + +// Destructor +ProcessAIXCore::~ProcessAIXCore() { + Clear(); + // We need to call finalize on the process before destroying ourselves to + // make sure all of the broadcaster cleanup goes as planned. If we destruct + // this class, then Process::~Process() might have problems trying to fully + // destroy the broadcaster. + Finalize(true /* destructing */); +} + +bool ProcessAIXCore::CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) { + + if (!m_core_module_sp && FileSystem::Instance().Exists(m_core_file)) { + ModuleSpec core_module_spec(m_core_file, target_sp->GetArchitecture()); + Status error(ModuleList::GetSharedModule(core_module_spec, m_core_module_sp, + nullptr, nullptr, nullptr)); + if (m_core_module_sp) { + ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); + if (core_objfile && core_objfile->GetType() == ObjectFile::eTypeCoreFile){ + return true; + } + } + } + return false; + +} + +ArchSpec ProcessAIXCore::GetArchitecture() { + + ArchSpec arch = m_core_module_sp->GetObjectFile()->GetArchitecture(); + + ArchSpec target_arch = GetTarget().GetArchitecture(); + arch.MergeFrom(target_arch); + + return arch; +} + +lldb_private::DynamicLoader *ProcessAIXCore::GetDynamicLoader() { + if (m_dyld_up.get() == nullptr) { + m_dyld_up.reset(DynamicLoader::FindPlugin( + this, DynamicLoaderAIXDYLD::GetPluginNameStatic())); + } + return m_dyld_up.get(); +} + +void ProcessAIXCore::ParseAIXCoreFile() { + + Log *log = GetLog(LLDBLog::Process); + AIXSigInfo siginfo; + ThreadData thread_data; + + const lldb_private::UnixSignals &unix_signals = *GetUnixSignals(); + const ArchSpec &arch = GetArchitecture(); + + siginfo.Parse(m_aixcore_header, arch, unix_signals); + thread_data.siginfo = siginfo; + SetID(m_aixcore_header.User.process.pi_pid); + + thread_data.name.assign (m_aixcore_header.User.process.pi_comm, + strnlen (m_aixcore_header.User.process.pi_comm, + sizeof (m_aixcore_header.User.process.pi_comm))); + + lldb::DataBufferSP data_buffer_sp(new lldb_private::DataBufferHeap(sizeof(m_aixcore_header.Fault.context), 0)); + + memcpy(static_cast(const_cast(data_buffer_sp->GetBytes())), + &m_aixcore_header.Fault.context, sizeof(m_aixcore_header.Fault.context)); + + lldb_private::DataExtractor data(data_buffer_sp, lldb::eByteOrderBig, 8); + + thread_data.gpregset = DataExtractor(data, 0, sizeof(m_aixcore_header.Fault.context)); + m_thread_data.push_back(thread_data); + LLDB_LOGF(log, "ProcessAIXCore: Parsing Complete!"); + +} + +// Process Control +Status ProcessAIXCore::DoLoadCore() { + + Status error; + if (!m_core_module_sp) { + error = Status::FromErrorString("invalid core module"); + return error; + } + + FileSpec file = m_core_module_sp->GetObjectFile()->GetFileSpec(); + + if (file) { + const size_t header_size = sizeof(AIXCORE::AIXCore64Header); + auto data_sp = FileSystem::Instance().CreateDataBuffer( + file.GetPath(), -1, 0); + if (data_sp && data_sp->GetByteSize() != 0) { + + DataExtractor data(data_sp, lldb::eByteOrderBig, 4); + lldb::offset_t data_offset = 0; + m_aixcore_header.ParseCoreHeader(data, &data_offset); + auto dyld = static_cast(GetDynamicLoader()); + dyld->FillCoreLoaderData(data, m_aixcore_header.LoaderOffset, + m_aixcore_header.LoaderSize); + + } else { + error = Status::FromErrorString("invalid data"); + return error; + } + } else { + error = Status::FromErrorString("invalid file"); + return error; + } + + m_thread_data_valid = true; + ParseAIXCoreFile(); + ArchSpec arch(m_core_module_sp->GetArchitecture()); + + ArchSpec target_arch = GetTarget().GetArchitecture(); + ArchSpec core_arch(m_core_module_sp->GetArchitecture()); + target_arch.MergeFrom(core_arch); + GetTarget().SetArchitecture(target_arch); + + lldb::ModuleSP exe_module_sp = GetTarget().GetExecutableModule(); + if (!exe_module_sp) { + ModuleSpec exe_module_spec; + exe_module_spec.GetArchitecture() = arch; + exe_module_spec.GetFileSpec().SetFile(m_aixcore_header.User.process.pi_comm, + FileSpec::Style::native); + exe_module_sp = GetTarget().GetOrCreateModule(exe_module_spec, true); + GetTarget().SetExecutableModule(exe_module_sp, eLoadDependentsNo); + } + + return error; +} + +bool ProcessAIXCore::DoUpdateThreadList(ThreadList &old_thread_list, + ThreadList &new_thread_list) +{ + const ThreadData &td = m_thread_data[0]; + + lldb::ThreadSP thread_sp = + std::make_shared(*this, td); + new_thread_list.AddThread(thread_sp); + + return true; +} + +void ProcessAIXCore::RefreshStateAfterStop() {} + +// Process Memory +size_t ProcessAIXCore::ReadMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) { + if (lldb::ABISP abi_sp = GetABI()) + addr = abi_sp->FixAnyAddress(addr); + + // Don't allow the caching that lldb_private::Process::ReadMemory does since + // in core files we have it all cached our our core file anyway. + return DoReadMemory(addr, buf, size, error); +} + +size_t ProcessAIXCore::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) { return 0; } + +Status ProcessAIXCore::DoGetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo ®ion_info) { + return Status(); +} + +Status ProcessAIXCore::DoDestroy() { return Status(); } diff --git a/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.h b/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.h new file mode 100644 index 0000000000000..9880c491689ca --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.h @@ -0,0 +1,100 @@ +//===-- ProcessAIXCore.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Notes about AIX Process core dumps: +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_PROCESSAIXCORE_H +#define LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_PROCESSAIXCORE_H + +#include +#include + +#include "lldb/Target/PostMortemProcess.h" +#include "lldb/Utility/Status.h" +#include "lldb/Target/Process.h" +#include "AIXCore.h" +#include "ThreadAIXCore.h" + +struct ThreadData; + +class ProcessAIXCore : public lldb_private::PostMortemProcess { +public: + // Constructors and Destructors + static lldb::ProcessSP + CreateInstance(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, + const lldb_private::FileSpec *crash_file_path, + bool can_connect); + + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "aix-core"; } + + static llvm::StringRef GetPluginDescriptionStatic(); + + // Constructors and Destructors + ProcessAIXCore(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, + const lldb_private::FileSpec &core_file); + + ~ProcessAIXCore() override; + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + // Process Control + lldb_private::Status DoDestroy() override; + + lldb_private::Status WillResume() override { + return lldb_private::Status::FromErrorStringWithFormatv( + "error: {0} does not support resuming processes", GetPluginName()); + } + + bool WarnBeforeDetach() const override { return false; } + + lldb_private::ArchSpec GetArchitecture(); + + bool CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) override; + + // Creating a new process, or attaching to an existing one + lldb_private::Status DoLoadCore() override; + + bool DoUpdateThreadList(lldb_private::ThreadList &old_thread_list, + lldb_private::ThreadList &new_thread_list) override; + + lldb_private::Status + DoGetMemoryRegionInfo(lldb::addr_t load_addr, + lldb_private::MemoryRegionInfo ®ion_info) override; + + void RefreshStateAfterStop() override; + + lldb_private::DynamicLoader *GetDynamicLoader() override; + + // Process Memory + size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size, + lldb_private::Status &error) override; + + size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + lldb_private::Status &error) override; + + void ParseAIXCoreFile(); + + +private: + lldb::ModuleSP m_core_module_sp; + std::string m_dyld_plugin_name; + + // True if m_thread_contexts contains valid entries + bool m_thread_data_valid = false; + AIXCORE::AIXCore64Header m_aixcore_header; + + std::vector m_thread_data; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_PROCESSAIXCORE_H diff --git a/lldb/source/Plugins/Process/aix-core/RegisterContextCoreAIX_ppc64.cpp b/lldb/source/Plugins/Process/aix-core/RegisterContextCoreAIX_ppc64.cpp new file mode 100644 index 0000000000000..b243017bf9a2a --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/RegisterContextCoreAIX_ppc64.cpp @@ -0,0 +1,136 @@ +//===-- RegisterContextCoreAIX_ppc64.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 "RegisterContextCoreAIX_ppc64.h" + +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/RegisterValue.h" + +#include "Plugins/Process/Utility/lldb-ppc64le-register-enums.h" +#include "Plugins/Process/elf-core/RegisterUtilities.h" + +#include + +using namespace lldb_private; + +RegisterContextCoreAIX_ppc64::RegisterContextCoreAIX_ppc64( + Thread &thread, RegisterInfoInterface *register_info, + const DataExtractor &gpregset) + : RegisterContextPOSIX_ppc64le(thread, 0, register_info) { + m_gpr_buffer = std::make_shared(gpregset.GetDataStart(), + gpregset.GetByteSize()); + m_gpr.SetData(m_gpr_buffer); + m_gpr.SetByteOrder(gpregset.GetByteOrder()); + + // This Code is for Registers like FPR, VSR, VMX and is disabled right now. + // It will be implemented as per need. + + /* ArchSpec arch = register_info->GetTargetArchitecture(); + DataExtractor fpregset;// = getRegset(notes, arch.GetTriple(), FPR_Desc); + m_fpr_buffer = std::make_shared(fpregset.GetDataStart(), + fpregset.GetByteSize()); + m_fpr.SetData(m_fpr_buffer); + m_fpr.SetByteOrder(fpregset.GetByteOrder()); + + DataExtractor vmxregset;// = getRegset(notes, arch.GetTriple(), PPC_VMX_Desc); + m_vmx_buffer = std::make_shared(vmxregset.GetDataStart(), + vmxregset.GetByteSize()); + m_vmx.SetData(m_vmx_buffer); + m_vmx.SetByteOrder(vmxregset.GetByteOrder()); + + DataExtractor vsxregset;// = getRegset(notes, arch.GetTriple(), PPC_VSX_Desc); + m_vsx_buffer = std::make_shared(vsxregset.GetDataStart(), + vsxregset.GetByteSize()); + m_vsx.SetData(m_vsx_buffer); + m_vsx.SetByteOrder(vsxregset.GetByteOrder());*/ +} + +size_t RegisterContextCoreAIX_ppc64::GetFPRSize() const { + return k_num_fpr_registers_ppc64le * sizeof(uint64_t); +} + +size_t RegisterContextCoreAIX_ppc64::GetVMXSize() const { + return (k_num_vmx_registers_ppc64le - 1) * sizeof(uint64_t) * 2 + + sizeof(uint32_t); +} + +size_t RegisterContextCoreAIX_ppc64::GetVSXSize() const { + return k_num_vsx_registers_ppc64le * sizeof(uint64_t) * 2; +} + +bool RegisterContextCoreAIX_ppc64::ReadRegister( + const RegisterInfo *reg_info, RegisterValue &value) { + lldb::offset_t offset = reg_info->byte_offset; + + if (IsFPR(reg_info->kinds[lldb::eRegisterKindLLDB])) { + uint64_t v; + offset -= GetGPRSize(); + offset = m_fpr.CopyData(offset, reg_info->byte_size, &v); + + if (offset == reg_info->byte_size) { + value.SetBytes(&v, reg_info->byte_size, m_fpr.GetByteOrder()); + return true; + } + } else if (IsVMX(reg_info->kinds[lldb::eRegisterKindLLDB])) { + uint32_t v[4]; + offset -= GetGPRSize() + GetFPRSize(); + offset = m_vmx.CopyData(offset, reg_info->byte_size, &v); + + if (offset == reg_info->byte_size) { + value.SetBytes(v, reg_info->byte_size, m_vmx.GetByteOrder()); + return true; + } + } else if (IsVSX(reg_info->kinds[lldb::eRegisterKindLLDB])) { + uint32_t v[4]; + lldb::offset_t tmp_offset; + offset -= GetGPRSize() + GetFPRSize() + GetVMXSize(); + + if (offset < GetVSXSize() / 2) { + tmp_offset = m_vsx.CopyData(offset / 2, reg_info->byte_size / 2, &v); + + if (tmp_offset != reg_info->byte_size / 2) { + return false; + } + + uint8_t *dst = (uint8_t *)&v + sizeof(uint64_t); + tmp_offset = m_fpr.CopyData(offset / 2, reg_info->byte_size / 2, dst); + + if (tmp_offset != reg_info->byte_size / 2) { + return false; + } + + value.SetBytes(&v, reg_info->byte_size, m_vsx.GetByteOrder()); + return true; + } else { + offset = + m_vmx.CopyData(offset - GetVSXSize() / 2, reg_info->byte_size, &v); + if (offset == reg_info->byte_size) { + value.SetBytes(v, reg_info->byte_size, m_vmx.GetByteOrder()); + return true; + } + } + } else { + uint64_t v = m_gpr.GetMaxU64(&offset, reg_info->byte_size); + + if (offset == reg_info->byte_offset + reg_info->byte_size) { + if (reg_info->byte_size < sizeof(v)) + value = (uint32_t)v; + else + value = v; + return true; + } + } + + return false; +} + +bool RegisterContextCoreAIX_ppc64::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue &value) { + return false; +} diff --git a/lldb/source/Plugins/Process/aix-core/RegisterContextCoreAIX_ppc64.h b/lldb/source/Plugins/Process/aix-core/RegisterContextCoreAIX_ppc64.h new file mode 100644 index 0000000000000..8f1f71ce8d884 --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/RegisterContextCoreAIX_ppc64.h @@ -0,0 +1,46 @@ +//===-- RegisterContextCoreAIX_ppc64.h ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_REGISTERCONTEXTAIXCORE_PPC64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_REGISTERCONTEXTAIXCORE_PPC64_H + +#include "Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.h" +#include "lldb/Utility/DataExtractor.h" + +class RegisterContextCoreAIX_ppc64 : public RegisterContextPOSIX_ppc64le { +public: + RegisterContextCoreAIX_ppc64( + lldb_private::Thread &thread, + lldb_private::RegisterInfoInterface *register_info, + const lldb_private::DataExtractor &gpregset); + + bool ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value) override; + + bool WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value) override; + +protected: + size_t GetFPRSize() const; + + size_t GetVMXSize() const; + + size_t GetVSXSize() const; + +private: + lldb::DataBufferSP m_gpr_buffer; + lldb::DataBufferSP m_fpr_buffer; + lldb::DataBufferSP m_vmx_buffer; + lldb::DataBufferSP m_vsx_buffer; + lldb_private::DataExtractor m_gpr; + lldb_private::DataExtractor m_fpr; + lldb_private::DataExtractor m_vmx; + lldb_private::DataExtractor m_vsx; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_REGISTERCONTEXTAIXCORE_PPC64_H diff --git a/lldb/source/Plugins/Process/aix-core/ThreadAIXCore.cpp b/lldb/source/Plugins/Process/aix-core/ThreadAIXCore.cpp new file mode 100644 index 0000000000000..979e5199fe24d --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/ThreadAIXCore.cpp @@ -0,0 +1,127 @@ +//===-- ThreadAIXCore.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 "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Target/Unwind.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/ProcessInfo.h" + +#include "Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h" +#include "Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.h" +#include "Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.h" +#include "Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.h" +#include "RegisterContextCoreAIX_ppc64.h" + +#include "ProcessAIXCore.h" +#include "AIXCore.h" +#include "ThreadAIXCore.h" + +#include +#include + +using namespace lldb; +using namespace lldb_private; + +// Construct a Thread object with given data +ThreadAIXCore::ThreadAIXCore(Process &process, const ThreadData &td) + : Thread(process, td.tid), m_thread_name(td.name), m_thread_reg_ctx_sp(), + m_gpregset_data(td.gpregset), + m_siginfo(std::move(td.siginfo)) {} + +ThreadAIXCore::~ThreadAIXCore() { DestroyThread(); } + +void ThreadAIXCore::RefreshStateAfterStop() { + GetRegisterContext()->InvalidateIfNeeded(false); +} + +RegisterContextSP ThreadAIXCore::GetRegisterContext() { + if (!m_reg_context_sp) { + m_reg_context_sp = CreateRegisterContextForFrame(nullptr); + } + return m_reg_context_sp; +} + +RegisterContextSP +ThreadAIXCore::CreateRegisterContextForFrame(StackFrame *frame) { + RegisterContextSP reg_ctx_sp; + uint32_t concrete_frame_idx = 0; + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex(); + + bool is_linux = false; + if (concrete_frame_idx == 0) { + if (m_thread_reg_ctx_sp) + return m_thread_reg_ctx_sp; + + ProcessAIXCore *process = static_cast(GetProcess().get()); + ArchSpec arch = process->GetArchitecture(); + RegisterInfoInterface *reg_interface = nullptr; + + switch (arch.GetMachine()) { + case llvm::Triple::ppc64: + reg_interface = new RegisterInfoPOSIX_ppc64le(arch); + m_thread_reg_ctx_sp = std::make_shared( + *this, reg_interface, m_gpregset_data); + break; + default: + break; + } + reg_ctx_sp = m_thread_reg_ctx_sp; + } else { + reg_ctx_sp = GetUnwinder().CreateRegisterContextForFrame(frame); + } + return reg_ctx_sp; +} + +bool ThreadAIXCore::CalculateStopInfo() { + ProcessSP process_sp(GetProcess()); + if (!process_sp) + return false; + + lldb::UnixSignalsSP unix_signals_sp(process_sp->GetUnixSignals()); + if (!unix_signals_sp) + return false; + + const char *sig_description; + std::string description = m_siginfo.GetDescription(*unix_signals_sp); + if (description.empty()) + sig_description = nullptr; + else + sig_description = description.c_str(); + + SetStopInfo(StopInfo::CreateStopReasonWithSignal( + *this, m_siginfo.si_signo, sig_description, m_siginfo.si_code)); + + SetStopInfo(m_stop_info_sp); + return true; +} + +void AIXSigInfo::Parse(const AIXCORE::AIXCore64Header data, const ArchSpec &arch, + const lldb_private::UnixSignals &unix_signals) { + si_signo = data.SignalNum; + sigfault.si_addr = data.Fault.context.pc; +} + +AIXSigInfo::AIXSigInfo() { memset(this, 0, sizeof(AIXSigInfo)); } + +size_t AIXSigInfo::GetSize(const lldb_private::ArchSpec &arch) { + return sizeof(AIXSigInfo); +} + +std::string AIXSigInfo::GetDescription( + const lldb_private::UnixSignals &unix_signals) const { + return unix_signals.GetSignalDescription(si_signo, 0, + sigfault.si_addr); + +} diff --git a/lldb/source/Plugins/Process/aix-core/ThreadAIXCore.h b/lldb/source/Plugins/Process/aix-core/ThreadAIXCore.h new file mode 100644 index 0000000000000..9ee157e9b2b9b --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/ThreadAIXCore.h @@ -0,0 +1,110 @@ +//===-- ThreadAIXCore.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_THREADAIXCORE_H +#define LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_THREADAIXCORE_H + +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataExtractor.h" +#include "llvm/ADT/DenseMap.h" +#include +#include +#include "ProcessAIXCore.h" +#include "AIXCore.h" +#include "ThreadAIXCore.h" + +namespace lldb_private { +class ProcessInstanceInfo; +} + +struct AIXSigInfo { + + //COPY siginfo_t correctly for AIX version + int32_t si_signo; // Order matters for the first 3. + int32_t si_errno; + int32_t si_code; + struct alignas(8) { + lldb::addr_t si_addr; + int16_t si_addr_lsb; + union { + struct { + lldb::addr_t _lower; + lldb::addr_t _upper; + } _addr_bnd; + uint32_t _pkey; + } bounds; + } sigfault; + + enum SigInfoNoteType : uint8_t { eUnspecified, eNT_SIGINFO }; + SigInfoNoteType note_type; + + AIXSigInfo(); + + void Parse(const AIXCORE::AIXCore64Header data, + const lldb_private::ArchSpec &arch, + const lldb_private::UnixSignals &unix_signals); + + std::string + GetDescription(const lldb_private::UnixSignals &unix_signals) const; + + static size_t GetSize(const lldb_private::ArchSpec &arch); +}; + +struct ThreadData { + lldb_private::DataExtractor gpregset; + std::vector notes; + lldb::tid_t tid; + std::string name; + AIXSigInfo siginfo; + int prstatus_sig = 0; +}; + +class ThreadAIXCore : public lldb_private::Thread { +public: + ThreadAIXCore(lldb_private::Process &process, const ThreadData &td); + + ~ThreadAIXCore() override; + + void RefreshStateAfterStop() override; + + lldb::RegisterContextSP GetRegisterContext() override; + + lldb::RegisterContextSP + CreateRegisterContextForFrame(lldb_private::StackFrame *frame) override; + + static bool ThreadIDIsValid(lldb::tid_t thread) { return thread != 0; } + + const char *GetName() override { + if (m_thread_name.empty()) + return nullptr; + return m_thread_name.c_str(); + } + + void SetName(const char *name) override { + if (name && name[0]) + m_thread_name.assign(name); + else + m_thread_name.clear(); + } + + void CreateStopFromSigInfo(const AIXSigInfo &siginfo, + const lldb_private::UnixSignals &unix_signals); + +protected: + // Member variables. + std::string m_thread_name; + lldb::RegisterContextSP m_thread_reg_ctx_sp; + + lldb_private::DataExtractor m_gpregset_data; + std::vector m_notes; + AIXSigInfo m_siginfo; + + bool CalculateStopInfo() override; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_THREADAIXCORE_H >From 57cb8058646103eeada1f92e039d9c54ccd4788f Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Mon, 17 Mar 2025 07:31:26 -0500 Subject: [PATCH 40/47] Merge branch 'llvm:main' into llvmgh-101657 --- lldb/cmake/modules/LLDBConfig.cmake | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lldb/cmake/modules/LLDBConfig.cmake b/lldb/cmake/modules/LLDBConfig.cmake index 312791ce36fc7..9df71edd8b359 100644 --- a/lldb/cmake/modules/LLDBConfig.cmake +++ b/lldb/cmake/modules/LLDBConfig.cmake @@ -292,11 +292,7 @@ endif() # Figure out if lldb could use lldb-server. If so, then we'll # ensure we build lldb-server when an lldb target is being built. -<<<<<<< HEAD -if (CMAKE_SYSTEM_NAME MATCHES "Android|Darwin|FreeBSD|Linux|NetBSD|OpenBSD|Windows|AIX") -======= if (CMAKE_SYSTEM_NAME MATCHES "AIX|Android|Darwin|FreeBSD|Linux|NetBSD|OpenBSD|Windows") ->>>>>>> upstream/main set(LLDB_CAN_USE_LLDB_SERVER ON) else() set(LLDB_CAN_USE_LLDB_SERVER OFF) >From 0767ef036d3f82d1429e04a319feb6627ea08158 Mon Sep 17 00:00:00 2001 From: Dhruv Srivastava Date: Tue, 18 Mar 2025 15:03:17 +0530 Subject: [PATCH 41/47] Error Handling (#32) * Error Handling for aix core module --- .../AIX-DYLD/DynamicLoaderAIXDYLD.cpp | 12 +++++++----- .../ObjectFile/AIXCore/ObjectFileAIXCore.cpp | 15 +++++++-------- lldb/source/Plugins/Process/aix-core/AIXCore.cpp | 2 +- .../Plugins/Process/aix-core/ProcessAIXCore.cpp | 11 ++++++++--- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp index 7e44ffbb1c051..12f24c049f373 100644 --- a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp @@ -235,17 +235,19 @@ bool DynamicLoaderAIXDYLD::IsCoreFile() const { void DynamicLoaderAIXDYLD::FillCoreLoaderData(lldb_private::DataExtractor &data, uint64_t loader_offset, uint64_t loader_size ) { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); static char *buffer = (char *)malloc(loader_size); - struct ld_info ldinfo[64]; - char *buffer_complete; + if (buffer == NULL) { + LLDB_LOG(log, "Buffer allocation failed error: {0}", std::strerror(errno)); + return; + } + struct ld_info ldinfo[64] = {}; struct ld_info *ptr; int i = 0; - Log *log = GetLog(LLDBLog::DynamicLoader); - LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); ByteOrder byteorder = data.GetByteOrder(); data.ExtractBytes(loader_offset, loader_size, eByteOrderBig, buffer); - buffer_complete = buffer + loader_size; ldinfo[0].ldinfo_next = 1; while (ldinfo[i++].ldinfo_next != 0) { diff --git a/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.cpp b/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.cpp index 5158fa4e25077..0ba1056866937 100644 --- a/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.cpp +++ b/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.cpp @@ -73,8 +73,6 @@ ObjectFile *ObjectFileAIXCore::CreateInstance(const lldb::ModuleSP &module_sp, assert(data_sp); - const uint8_t *magic = data_sp->GetBytes() + data_offset; - // Update the data to contain the entire file if it doesn't already if (data_sp->GetByteSize() < length) { data_sp = MapFileDataWritable(*file, length, file_offset); @@ -82,7 +80,6 @@ ObjectFile *ObjectFileAIXCore::CreateInstance(const lldb::ModuleSP &module_sp, return nullptr; data_offset = 0; mapped_writable = true; - magic = data_sp->GetBytes(); } // If we didn't map the data as writable take ownership of the buffer. @@ -90,7 +87,6 @@ ObjectFile *ObjectFileAIXCore::CreateInstance(const lldb::ModuleSP &module_sp, data_sp = std::make_shared(data_sp->GetBytes(), data_sp->GetByteSize()); data_offset = 0; - magic = data_sp->GetBytes(); } std::unique_ptr objfile_up(new ObjectFileAIXCore( @@ -124,19 +120,22 @@ size_t ObjectFileAIXCore::GetModuleSpecifications( return specs.GetSize() - initial_count; } -static uint32_t AIXCoreHeaderCheckFromMagic(uint32_t magic) { +static bool AIXCoreHeaderCheckFromMagic(uint32_t magic) { Log *log = GetLog(LLDBLog::Modules); + bool ret = false; switch (magic) { case AIXCORE32: LLDB_LOGF(log, "ObjectFileAIXCore: 32-bit not supported"); break; case AIXCORE64: m_is_core = true; - return 1; + ret = true; + break; + default: break; } - return 0; + return ret; } bool ObjectFileAIXCore::MagicBytesMatch(DataBufferSP &data_sp, @@ -147,7 +146,7 @@ bool ObjectFileAIXCore::MagicBytesMatch(DataBufferSP &data_sp, lldb::offset_t offset = 0; offset += 4; // Skipping to the coredump version uint32_t magic = data.GetU32(&offset); - return AIXCoreHeaderCheckFromMagic(magic) != 0; + return AIXCoreHeaderCheckFromMagic(magic); } bool ObjectFileAIXCore::ParseHeader() { diff --git a/lldb/source/Plugins/Process/aix-core/AIXCore.cpp b/lldb/source/Plugins/Process/aix-core/AIXCore.cpp index 95e47b4d8be53..bc496b5af273f 100644 --- a/lldb/source/Plugins/Process/aix-core/AIXCore.cpp +++ b/lldb/source/Plugins/Process/aix-core/AIXCore.cpp @@ -110,7 +110,7 @@ bool AIXCore64Header::ParseCoreHeader(lldb_private::DataExtractor &data, ret = ParseThreadContext(data, offset); ret = ParseUserData(data, &offset_to_user); - return true; + return ret; } diff --git a/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp b/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp index 9300aa14ac4db..cfcbe1a216116 100644 --- a/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp +++ b/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp @@ -208,8 +208,10 @@ Status ProcessAIXCore::DoLoadCore() { exe_module_spec.GetArchitecture() = arch; exe_module_spec.GetFileSpec().SetFile(m_aixcore_header.User.process.pi_comm, FileSpec::Style::native); - exe_module_sp = GetTarget().GetOrCreateModule(exe_module_spec, true); - GetTarget().SetExecutableModule(exe_module_sp, eLoadDependentsNo); + exe_module_sp = + GetTarget().GetOrCreateModule(exe_module_spec, true /* notify */); + if (exe_module_sp) + GetTarget().SetExecutableModule(exe_module_sp, eLoadDependentsNo); } return error; @@ -232,8 +234,11 @@ void ProcessAIXCore::RefreshStateAfterStop() {} // Process Memory size_t ProcessAIXCore::ReadMemory(lldb::addr_t addr, void *buf, size_t size, Status &error) { + if(addr == LLDB_INVALID_ADDRESS) + return 0; + if (lldb::ABISP abi_sp = GetABI()) - addr = abi_sp->FixAnyAddress(addr); + addr = abi_sp->FixAnyAddress(addr); // Don't allow the caching that lldb_private::Process::ReadMemory does since // in core files we have it all cached our our core file anyway. >From 8214e5d8cc52b486d3c3f5f4615848b1782cfcf8 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Wed, 26 Mar 2025 01:34:36 -0500 Subject: [PATCH 42/47] Merge branch 'llvm:main' into gh-101657 --- lldb/source/Host/common/Host.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp index 575bb46216d19..6cf1112511c3f 100644 --- a/lldb/source/Host/common/Host.cpp +++ b/lldb/source/Host/common/Host.cpp @@ -20,7 +20,6 @@ #include #include #include -#endif #include #endif >From d00d28ae68c731efe6f9392000be9822ee74ff0a Mon Sep 17 00:00:00 2001 From: HemangGadhavi Date: Wed, 26 Mar 2025 04:08:23 -0500 Subject: [PATCH 43/47] First time attach resolution --- .../Plugins/Process/AIX/NativeProcessAIX.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp index fc84763857453..83b8ae5eb3258 100644 --- a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp +++ b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp @@ -2000,7 +2000,21 @@ Status NativeProcessAIX::PtraceWrapper(int req, lldb::pid_t pid, void *addr, } else if (req == PT_WRITE_BLOCK) { ptrace64(req, pid, (long long)addr, (int)data_size, (int *)result); } else if (req == PT_ATTACH) { + // Block SIGCHLD signal during attach to the process, + // to prevent interruptions. + // The ptrace operation may send SIGCHLD signals in certain cases + // during the attach, which can interfere. + static sigset_t signal_set; + sigemptyset (&signal_set); + sigaddset (&signal_set, SIGCHLD); + if(!pthread_sigmask( SIG_BLOCK, &signal_set, NULL)) + LLDB_LOG(log,"NativeProcessAIX::pthread_sigmask(SIG_BLOCK) Failed"); + ptrace64(req, pid, 0, 0, nullptr); + + //Unblocking the SIGCHLD after attach work. + if(!pthread_sigmask( SIG_UNBLOCK, &signal_set, NULL )) + LLDB_LOG(log,"NativeProcessAIX::pthread_sigmask(SIG_UNBLOCK) Failed"); } else if (req == PT_WATCH) { ptrace64(req, pid, (long long)addr, (int)data_size, nullptr); } else if (req == PT_DETACH) { >From 9ff945e62a906e9711022bf8f77b86a0aa05c64e Mon Sep 17 00:00:00 2001 From: HemangGadhavi Date: Tue, 1 Apr 2025 04:54:51 -0500 Subject: [PATCH 44/47] Invalid DWARF rangelist --- lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp index cf11e5fb8f5a3..65bd1ee9781d8 100644 --- a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp @@ -578,6 +578,7 @@ SectionType ObjectFileXCOFF::GetSectionType(llvm::StringRef sect_name, .Case(".dwinfo", eSectionTypeDWARFDebugInfo) .Case(".dwline", eSectionTypeDWARFDebugLine) .Case(".dwabrev", eSectionTypeDWARFDebugAbbrev) + .Case(".dwrnges", eSectionTypeDWARFDebugRanges) .Default(eSectionTypeInvalid); if (section_type != eSectionTypeInvalid) >From b443dd5b26354da73cd4d785a093d9d1582b25a8 Mon Sep 17 00:00:00 2001 From: Dhruv Srivastava Date: Wed, 2 Apr 2025 11:57:28 +0530 Subject: [PATCH 45/47] Fix for stack memory access from core file (#40) * Fix for stack memory access in core file --- .../Process/aix-core/ProcessAIXCore.cpp | 74 ++++++++++++++++++- .../Plugins/Process/aix-core/ProcessAIXCore.h | 11 +++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp b/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp index cfcbe1a216116..bb2db66e2980e 100644 --- a/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp +++ b/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp @@ -94,6 +94,33 @@ ProcessAIXCore::~ProcessAIXCore() { Finalize(true /* destructing */); } +lldb::addr_t ProcessAIXCore::AddAddressRanges(AIXCORE::AIXCore64Header header) { + const lldb::addr_t addr = header.StackBaseAddr; + FileRange file_range(header.StackOffset, header.StackSize); + VMRangeToFileOffset::Entry range_entry(addr, header.StackSize, file_range); + + if (header.StackSize > 0) { + VMRangeToFileOffset::Entry *last_entry = m_core_aranges.Back(); + if (last_entry && + last_entry->GetRangeEnd() == range_entry.GetRangeBase() && + last_entry->data.GetRangeEnd() == range_entry.data.GetRangeBase() && + last_entry->GetByteSize() == last_entry->data.GetByteSize()) { + last_entry->SetRangeEnd(range_entry.GetRangeEnd()); + last_entry->data.SetRangeEnd(range_entry.data.GetRangeEnd()); + } else { + m_core_aranges.Append(range_entry); + } + } + + const uint32_t permissions = lldb::ePermissionsReadable | + lldb::ePermissionsWritable; + + m_core_range_infos.Append( + VMRangeToPermissions::Entry(addr, header.StackSize, permissions)); + + return addr; +} + bool ProcessAIXCore::CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) { @@ -170,6 +197,7 @@ Status ProcessAIXCore::DoLoadCore() { } FileSpec file = m_core_module_sp->GetObjectFile()->GetFileSpec(); + Log *log = GetLog(LLDBLog::Process); if (file) { const size_t header_size = sizeof(AIXCORE::AIXCore64Header); @@ -180,6 +208,9 @@ Status ProcessAIXCore::DoLoadCore() { DataExtractor data(data_sp, lldb::eByteOrderBig, 4); lldb::offset_t data_offset = 0; m_aixcore_header.ParseCoreHeader(data, &data_offset); + lldb::addr_t addr = AddAddressRanges(m_aixcore_header); + if (addr == LLDB_INVALID_ADDRESS) + LLDB_LOGF(log, "ProcessAIXCore: Invalid base address. Stack information will be limited"); auto dyld = static_cast(GetDynamicLoader()); dyld->FillCoreLoaderData(data, m_aixcore_header.LoaderOffset, m_aixcore_header.LoaderSize); @@ -246,7 +277,48 @@ size_t ProcessAIXCore::ReadMemory(lldb::addr_t addr, void *buf, size_t size, } size_t ProcessAIXCore::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, - Status &error) { return 0; } + Status &error) { + ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); + if (core_objfile == nullptr) + return 0; + // Get the address range + const VMRangeToFileOffset::Entry *address_range = + m_core_aranges.FindEntryThatContains(addr); + if (address_range == nullptr || address_range->GetRangeEnd() < addr) { + error = Status::FromErrorStringWithFormat( + "core file does not contain 0x%" PRIx64, addr); + return 0; + } + + // Convert the address into core file offset + const lldb::addr_t offset = addr - address_range->GetRangeBase(); + const lldb::addr_t file_start = address_range->data.GetRangeBase(); + const lldb::addr_t file_end = address_range->data.GetRangeEnd(); + size_t bytes_to_read = size; // Number of bytes to read from the core file + size_t bytes_copied = 0; // Number of bytes actually read from the core file + // Number of bytes available in the core file from the given address + lldb::addr_t bytes_left = 0; + + // Don't proceed if core file doesn't contain the actual data for this + // address range. + if (file_start == file_end) + return 0; + + // Figure out how many on-disk bytes remain in this segment starting at the + // given offset + if (file_end > file_start + offset) + bytes_left = file_end - (file_start + offset); + + if (bytes_to_read > bytes_left) + bytes_to_read = bytes_left; + + // If there is data available on the core file read it + if (bytes_to_read) + bytes_copied = + core_objfile->CopyData(offset + file_start, bytes_to_read, buf); + + return bytes_copied; +} Status ProcessAIXCore::DoGetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo ®ion_info) { diff --git a/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.h b/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.h index 9880c491689ca..ffd9e401ee192 100644 --- a/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.h +++ b/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.h @@ -85,11 +85,22 @@ class ProcessAIXCore : public lldb_private::PostMortemProcess { void ParseAIXCoreFile(); + lldb::addr_t AddAddressRanges(AIXCORE::AIXCore64Header header); private: lldb::ModuleSP m_core_module_sp; std::string m_dyld_plugin_name; + typedef lldb_private::Range FileRange; + typedef lldb_private::RangeDataVector + VMRangeToFileOffset; + typedef lldb_private::RangeDataVector + VMRangeToPermissions; + + // Address ranges found in the core + VMRangeToFileOffset m_core_aranges; + VMRangeToPermissions m_core_range_infos; + // True if m_thread_contexts contains valid entries bool m_thread_data_valid = false; AIXCORE::AIXCore64Header m_aixcore_header; >From 96db5e3257436a222ee38049528d682166421fa1 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Wed, 2 Apr 2025 01:46:46 -0500 Subject: [PATCH 46/47] Build fail: SBMutex --- lldb/source/API/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/lldb/source/API/CMakeLists.txt b/lldb/source/API/CMakeLists.txt index 0045edf6743d1..2c8f2d583c054 100644 --- a/lldb/source/API/CMakeLists.txt +++ b/lldb/source/API/CMakeLists.txt @@ -83,6 +83,7 @@ add_lldb_library(liblldb STATIC ${option_framework} SBMemoryRegionInfoList.cpp SBModule.cpp SBModuleSpec.cpp + SBMutex.cpp SBPlatform.cpp SBProcess.cpp SBProgress.cpp >From d7a892ef207cde5430021b8705279cc08dc4e0f7 Mon Sep 17 00:00:00 2001 From: HemangGadhavi Date: Wed, 30 Apr 2025 04:53:28 -0500 Subject: [PATCH 47/47] Added change for step command issue --- .../Plugins/Process/AIX/NativeProcessAIX.cpp | 87 +++++++++++++------ 1 file changed, 61 insertions(+), 26 deletions(-) diff --git a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp index 83b8ae5eb3258..ace9e11927bee 100644 --- a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp +++ b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp @@ -19,7 +19,7 @@ #include #include #include - +#include #include "NativeThreadAIX.h" #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" //#include "Plugins/Process/Utility/LinuxProcMaps.h" @@ -79,6 +79,7 @@ using namespace lldb_private; using namespace lldb_private::process_aix; using namespace llvm; +typedef std::function)> AIXMapCallback; // Private bits we only need internally. static bool ProcessVmReadvSupported() { @@ -988,13 +989,6 @@ Status NativeProcessAIX::GetMemoryRegionInfo(lldb::addr_t load_addr, for (auto it = m_mem_region_cache.begin(); it != m_mem_region_cache.end(); ++it) { MemoryRegionInfo &proc_entry_info = it->first; - - // Sanity check assumption that /proc/{pid}/maps entries are ascending. - assert((proc_entry_info.GetRange().GetRangeBase() >= prev_base_address) && - "descending /proc/pid/maps entries detected, unexpected"); - prev_base_address = proc_entry_info.GetRange().GetRangeBase(); - UNUSED_IF_ASSERT_DISABLED(prev_base_address); - // If the target address comes before this entry, indicate distance to next // region. if (load_addr < proc_entry_info.GetRange().GetRangeBase()) { @@ -1029,9 +1023,59 @@ Status NativeProcessAIX::GetMemoryRegionInfo(lldb::addr_t load_addr, return error; } +// Parsing the AIX map file /proc/PID/map +// The map file contains an array of prmap structures +// which has all the information like size, startaddress, object name, permissions +bool ParseAIXMapRegions(const char *aix_map, AIXMapCallback const &callback) { + MemoryRegionInfo region; + struct prmap *prmapData = (struct prmap *)aix_map; + struct prmap *entry; + uint32_t perm_flag; + + for(entry = prmapData;!(entry->pr_size == 0 && entry->pr_vaddr == NULL); entry++) { + const char *o_name = aix_map + entry->pr_pathoff; + lldb::addr_t start_address = (lldb::addr_t )entry->pr_vaddr; + lldb::addr_t end_address = start_address + entry->pr_size; + region.GetRange().SetRangeBase(start_address); + region.GetRange().SetRangeEnd(end_address); + region.SetMapped(MemoryRegionInfo::OptionalBool::eYes); + perm_flag = entry->pr_mflags; + + if(perm_flag & MA_READ) + region.SetReadable(MemoryRegionInfo::OptionalBool::eYes); + else + region.SetReadable(MemoryRegionInfo::OptionalBool::eNo); + + if(perm_flag & MA_WRITE) + region.SetWritable(MemoryRegionInfo::OptionalBool::eYes); + else + region.SetWritable(MemoryRegionInfo::OptionalBool::eNo); + + if(perm_flag & MA_EXEC) + region.SetExecutable(MemoryRegionInfo::OptionalBool::eYes); + else + region.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); + + if((perm_flag & MA_SLIBTEXT) || (perm_flag & MA_SLIBDATA)) + region.SetShared(MemoryRegionInfo::OptionalBool::eYes); + else if ((perm_flag & MA_PLIBTEXT) || (perm_flag & MA_PLIBDATA)) + region.SetShared(MemoryRegionInfo::OptionalBool::eNo); + else + region.SetShared(MemoryRegionInfo::OptionalBool::eDontKnow); + + if(o_name) + region.SetName(o_name); + + callback(region); + region.Clear(); + } + + return true; +} + + Status NativeProcessAIX::PopulateMemoryRegionCache() { Log *log = GetLog(POSIXLog::Process); - // If our cache is empty, pull the latest. There should always be at least // one memory region if memory region handling is supported. if (!m_mem_region_cache.empty()) { @@ -1041,7 +1085,7 @@ Status NativeProcessAIX::PopulateMemoryRegionCache() { } Status Result; -#if 0 + AIXMapCallback callback = [&](llvm::Expected Info) { if (Info) { FileSpec file_spec(Info->GetName().GetCString()); @@ -1050,26 +1094,17 @@ Status NativeProcessAIX::PopulateMemoryRegionCache() { return true; } - Result = Info.takeError(); + Result = Status::FromError(Info.takeError()); m_supports_mem_region = LazyBool::eLazyBoolNo; LLDB_LOG(log, "failed to parse proc maps: {0}", Result); return false; }; - // AIX kernel since 2.6.14 has /proc/{pid}/smaps - // if CONFIG_PROC_PAGE_MONITOR is enabled - auto BufferOrError = getProcFile(GetID(), GetCurrentThreadID(), "smaps"); - if (BufferOrError) - ParseAIXSMapRegions(BufferOrError.get()->getBuffer(), callback); - else { - BufferOrError = getProcFile(GetID(), GetCurrentThreadID(), "maps"); - if (!BufferOrError) { - m_supports_mem_region = LazyBool::eLazyBoolNo; - return BufferOrError.getError(); - } - - ParseAIXMapRegions(BufferOrError.get()->getBuffer(), callback); - } + auto BufferOrError = getProcFile(GetID(), "map"); + if (BufferOrError) { + std::unique_ptr MapBuffer = std::move(*BufferOrError); + ParseAIXMapRegions(MapBuffer->getBufferStart(), callback); + } if (Result.Fail()) return Result; @@ -1090,7 +1125,7 @@ Status NativeProcessAIX::PopulateMemoryRegionCache() { // We support memory retrieval, remember that. m_supports_mem_region = LazyBool::eLazyBoolYes; -#endif + return Status(); } From lldb-commits at lists.llvm.org Wed May 7 22:24:51 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Wed, 07 May 2025 22:24:51 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Change the statusline format to print "no target" (PR #139021) In-Reply-To: Message-ID: <681c4023.170a0220.4f475.33a5@mx.google.com> https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/139021 >From fcd590a31f7b490f5fbc5996d9c5f2836ff4e7ee Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Wed, 7 May 2025 21:20:47 -0700 Subject: [PATCH 1/2] [lldb] Change the statusline format to print "no target" Change the default statusline format to print "no target" when lldb is launched without a target. Currently, the statusline is empty, which looks rather odd. --- lldb/source/Core/CoreProperties.td | 2 +- .../statusline/TestStatusline.py | 62 ++++++++++--------- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/lldb/source/Core/CoreProperties.td b/lldb/source/Core/CoreProperties.td index 2498841b47d9f..78988ce5b732f 100644 --- a/lldb/source/Core/CoreProperties.td +++ b/lldb/source/Core/CoreProperties.td @@ -186,7 +186,7 @@ let Definition = "debugger" in { : Property<"statusline-format", "FormatEntity">, Global, DefaultStringValue< - "${ansi.negative}{${target.file.basename}}{ " + "${ansi.negative}{${target.file.basename}|no target}{ " "${separator}${line.file.basename}:${line.number}:${line.column}}{ " "${separator}${thread.stop-reason}}{ " "${separator}{${progress.count} }${progress.message}}">, diff --git a/lldb/test/API/functionalities/statusline/TestStatusline.py b/lldb/test/API/functionalities/statusline/TestStatusline.py index da6b4e7c8f320..f00413e878d26 100644 --- a/lldb/test/API/functionalities/statusline/TestStatusline.py +++ b/lldb/test/API/functionalities/statusline/TestStatusline.py @@ -6,7 +6,18 @@ from lldbsuite.test.lldbpexpect import PExpectTest +# PExpect uses many timeouts internally and doesn't play well +# under ASAN on a loaded machine.. + at skipIfAsan class TestStatusline(PExpectTest): + + # Change this value to something smaller to make debugging this test less + # tedious. + TIMEOUT = 60 + + TERMINAL_HEIGHT = 10 + TERMINAL_WIDTH = 60 + def do_setup(self): # Create a target and run to a breakpoint. exe = self.getBuildArtifact("a.out") @@ -15,36 +26,34 @@ def do_setup(self): ) self.expect('breakpoint set -p "Break here"', substrs=["Breakpoint 1"]) self.expect("run", substrs=["stop reason"]) + self.resize() + + def resize(self): + # Change the terminal dimensions. When we launch the tests, we reset + # all the settings, leaving the terminal dimensions unset. + self.child.setwinsize(self.TERMINAL_HEIGHT, self.TERMINAL_WIDTH) - # PExpect uses many timeouts internally and doesn't play well - # under ASAN on a loaded machine.. - @skipIfAsan def test(self): """Basic test for the statusline.""" self.build() - self.launch() + self.launch(timeout=self.TIMEOUT) self.do_setup() - # Change the terminal dimensions. - terminal_height = 10 - terminal_width = 60 - self.child.setwinsize(terminal_height, terminal_width) - # Enable the statusline and check for the control character and that we # can see the target, the location and the stop reason. self.expect('set set separator "| "') self.expect( "set set show-statusline true", [ - "\x1b[0;{}r".format(terminal_height - 1), + "\x1b[0;{}r".format(self.TERMINAL_HEIGHT - 1), "a.out | main.c:2:11 | breakpoint 1.1 ", ], ) # Change the terminal dimensions and make sure it's reflected immediately. - self.child.setwinsize(terminal_height, 25) + self.child.setwinsize(self.TERMINAL_HEIGHT, 25) self.child.expect(re.escape("a.out | main.c:2:11 | bre")) - self.child.setwinsize(terminal_height, terminal_width) + self.child.setwinsize(self.TERMINAL_HEIGHT, self.TERMINAL_WIDTH) # Change the separator. self.expect('set set separator "S "', ["a.out S main.c:2:11"]) @@ -58,23 +67,15 @@ def test(self): # Hide the statusline and check or the control character. self.expect( - "set set show-statusline false", ["\x1b[0;{}r".format(terminal_height)] + "set set show-statusline false", ["\x1b[0;{}r".format(self.TERMINAL_HEIGHT)] ) - # PExpect uses many timeouts internally and doesn't play well - # under ASAN on a loaded machine.. - @skipIfAsan def test_no_color(self): """Basic test for the statusline with colors disabled.""" self.build() - self.launch(use_colors=False) + self.launch(use_colors=False, timeout=self.TIMEOUT) self.do_setup() - # Change the terminal dimensions. - terminal_height = 10 - terminal_width = 60 - self.child.setwinsize(terminal_height, terminal_width) - # Enable the statusline and check for the "reverse video" control character. self.expect( "set set show-statusline true", @@ -87,15 +88,20 @@ def test_deadlock(self): """Regression test for lock inversion between the statusline mutex and the output mutex.""" self.build() - self.launch(extra_args=["-o", "settings set use-color false"]) + self.launch( + extra_args=["-o", "settings set use-color false"], timeout=self.TIMEOUT + ) self.child.expect("(lldb)") - - # Change the terminal dimensions. - terminal_height = 10 - terminal_width = 60 - self.child.setwinsize(terminal_height, terminal_width) + self.resize() exe = self.getBuildArtifact("a.out") self.expect("file {}".format(exe), ["Current executable"]) self.expect("help", ["Debugger commands"]) + + def test_no_target(self): + """Test that we print "no target" when launched without a target.""" + self.launch(timeout=self.TIMEOUT) + self.resize() + + self.expect("set set show-statusline true", ["no target"]) >From 547b9a3e41be42cf101614787c95c1137705ec2c Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Wed, 7 May 2025 22:24:36 -0700 Subject: [PATCH 2/2] Formatting --- lldb/test/API/functionalities/statusline/TestStatusline.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lldb/test/API/functionalities/statusline/TestStatusline.py b/lldb/test/API/functionalities/statusline/TestStatusline.py index f00413e878d26..53ac7432f4ba1 100644 --- a/lldb/test/API/functionalities/statusline/TestStatusline.py +++ b/lldb/test/API/functionalities/statusline/TestStatusline.py @@ -10,7 +10,6 @@ # under ASAN on a loaded machine.. @skipIfAsan class TestStatusline(PExpectTest): - # Change this value to something smaller to make debugging this test less # tedious. TIMEOUT = 60 From lldb-commits at lists.llvm.org Wed May 7 22:43:59 2025 From: lldb-commits at lists.llvm.org (Dhruv Srivastava via lldb-commits) Date: Wed, 07 May 2025 22:43:59 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][AIX] Support for XCOFF Sections (PR #131304) In-Reply-To: Message-ID: <681c449f.a70a0220.32933e.1c8d@mx.google.com> ================ @@ -190,8 +190,83 @@ void ObjectFileXCOFF::ParseSymtab(Symtab &lldb_symtab) {} bool ObjectFileXCOFF::IsStripped() { return false; } -void ObjectFileXCOFF::CreateSections(SectionList &unified_section_list) {} - +void ObjectFileXCOFF::CreateSections(SectionList &unified_section_list) { + + if (m_sections_up) + return; + m_sections_up = std::make_unique(); + ModuleSP module_sp(GetModule()); + if (module_sp) { + std::lock_guard guard(module_sp->GetMutex()); + + ModuleSP module_sp(GetModule()); + for (auto sIdx = m_binary->section_begin(); sIdx != m_binary->section_end(); + ++sIdx) { ---------------- DhruvSrivastavaX wrote: I was trying to use it this way, to make it more friendly for both 64 bit and upcoming 32 bit support. But yes, sections64() is much better for now. Maybe we can use a global template kind of approach later for 64/32 bit? https://github.com/llvm/llvm-project/pull/131304 From lldb-commits at lists.llvm.org Wed May 7 22:44:38 2025 From: lldb-commits at lists.llvm.org (Dhruv Srivastava via lldb-commits) Date: Wed, 07 May 2025 22:44:38 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][AIX] Support for XCOFF Sections (PR #131304) In-Reply-To: Message-ID: <681c44c6.170a0220.248d48.1cc8@mx.google.com> ================ @@ -7,6 +7,9 @@ # CHECK: Stripped: false # CHECK: Type: executable # CHECK: Strata: unknown +# CHECK: Name: .text +# CHECK-NEXT: code +# CHECK-NEXT: r-x ---------------- DhruvSrivastavaX wrote: Yes, I was a little hesitant to make it larger as other platform test cases are much smaller. Sure, I will update this https://github.com/llvm/llvm-project/pull/131304 From lldb-commits at lists.llvm.org Wed May 7 23:08:55 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Wed, 07 May 2025 23:08:55 -0700 (PDT) Subject: [Lldb-commits] [lldb] df4eac2 - [lldb-dap] Temporarily disable the breakpoint tests Message-ID: <681c4a77.170a0220.ffaf7.2c7d@mx.google.com> Author: Jonas Devlieghere Date: 2025-05-07T23:08:51-07:00 New Revision: df4eac2f8b6d32772953d3d8063568fe4c0314c1 URL: https://github.com/llvm/llvm-project/commit/df4eac2f8b6d32772953d3d8063568fe4c0314c1 DIFF: https://github.com/llvm/llvm-project/commit/df4eac2f8b6d32772953d3d8063568fe4c0314c1.diff LOG: [lldb-dap] Temporarily disable the breakpoint tests At least one of these tests is failing every run on GreenDragon: https://ci.swift.org/view/all/job/llvm.org/view/LLDB/job/as-lldb-cmake/ Added: Modified: lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setExceptionBreakpoints.py lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setFunctionBreakpoints.py Removed: ################################################################################ diff --git a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py index 26df2573555df..aae1251b17c93 100644 --- a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py +++ b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py @@ -12,6 +12,7 @@ import os + at skip("Temporarily disable the breakpoint tests") class TestDAP_setBreakpoints(lldbdap_testcase.DAPTestCaseBase): def setUp(self): lldbdap_testcase.DAPTestCaseBase.setUp(self) diff --git a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setExceptionBreakpoints.py b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setExceptionBreakpoints.py index 92ac66cd44c5d..4dc8c5b3c7ded 100644 --- a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setExceptionBreakpoints.py +++ b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setExceptionBreakpoints.py @@ -10,6 +10,7 @@ import lldbdap_testcase + at skip("Temporarily disable the breakpoint tests") class TestDAP_setExceptionBreakpoints(lldbdap_testcase.DAPTestCaseBase): @skipIfWindows def test_functionality(self): diff --git a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setFunctionBreakpoints.py b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setFunctionBreakpoints.py index 946595f639edc..baaca4d974d5d 100644 --- a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setFunctionBreakpoints.py +++ b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setFunctionBreakpoints.py @@ -10,6 +10,7 @@ import lldbdap_testcase + at skip("Temporarily disable the breakpoint tests") class TestDAP_setFunctionBreakpoints(lldbdap_testcase.DAPTestCaseBase): @skipIfWindows def test_set_and_clear(self): From lldb-commits at lists.llvm.org Wed May 7 23:09:27 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 23:09:27 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add field member operators to DIL (PR #138093) In-Reply-To: Message-ID: <681c4a97.170a0220.16da84.3387@mx.google.com> https://github.com/cmtice updated https://github.com/llvm/llvm-project/pull/138093 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Wed May 7 23:10:29 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 23:10:29 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add field member operators to DIL (PR #138093) In-Reply-To: Message-ID: <681c4ad5.050a0220.efbb2.6d4d@mx.google.com> ================ @@ -272,4 +272,204 @@ Interpreter::Visit(const UnaryOpNode *node) { m_expr, "invalid ast: unexpected binary operator", node->GetLocation()); } +lldb::ValueObjectSP +Interpreter::EvaluateMemberOf(lldb::ValueObjectSP value, + const std::vector &path, + bool use_synthetic, bool is_dynamic) { ---------------- cmtice wrote: Removed this function so comment doesn't matter now. https://github.com/llvm/llvm-project/pull/138093 From lldb-commits at lists.llvm.org Wed May 7 23:10:42 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 23:10:42 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add field member operators to DIL (PR #138093) In-Reply-To: Message-ID: <681c4ae2.170a0220.aef1e.c70b@mx.google.com> ================ @@ -88,6 +89,29 @@ class IdentifierNode : public ASTNode { std::string m_name; }; +class MemberOfNode : public ASTNode { +public: + MemberOfNode(uint32_t location, ASTNodeUP base, bool is_arrow, + ConstString name) + : ASTNode(location, NodeKind::eMemberOfNode), m_base(std::move(base)), + m_is_arrow(is_arrow), m_field_name(name) { } + + llvm::Expected Accept(Visitor *v) const override; + + ASTNode *base() const { return m_base.get(); } + bool IsArrow() const { return m_is_arrow; } + ConstString FieldName() const { return m_field_name; } ---------------- cmtice wrote: Done. https://github.com/llvm/llvm-project/pull/138093 From lldb-commits at lists.llvm.org Wed May 7 23:10:48 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 23:10:48 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add field member operators to DIL (PR #138093) In-Reply-To: Message-ID: <681c4ae8.170a0220.1b1bd1.fc69@mx.google.com> ================ @@ -88,6 +89,29 @@ class IdentifierNode : public ASTNode { std::string m_name; }; +class MemberOfNode : public ASTNode { +public: + MemberOfNode(uint32_t location, ASTNodeUP base, bool is_arrow, + ConstString name) + : ASTNode(location, NodeKind::eMemberOfNode), m_base(std::move(base)), + m_is_arrow(is_arrow), m_field_name(name) { } + + llvm::Expected Accept(Visitor *v) const override; + + ASTNode *base() const { return m_base.get(); } + bool IsArrow() const { return m_is_arrow; } ---------------- cmtice wrote: Done. https://github.com/llvm/llvm-project/pull/138093 From lldb-commits at lists.llvm.org Wed May 7 23:10:58 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Wed, 07 May 2025 23:10:58 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Re-enable the lldb-dap tests (PR #138791) In-Reply-To: Message-ID: <681c4af2.170a0220.2db2b7.5349@mx.google.com> JDevlieghere wrote: All the Linux and Windows buildbots are green, but GreenDragon is consistently failing with one of the breakpoint tests and it's not immediately obvious what the problem is. If I rerun the tests enough times, I can reproduce a failure locally. I've disabled those tests in df4eac2f8b6d32772953d3d8063568fe4c0314c1. https://github.com/llvm/llvm-project/pull/138791 From lldb-commits at lists.llvm.org Wed May 7 23:13:56 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Wed, 07 May 2025 23:13:56 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add field member operators to DIL (PR #138093) In-Reply-To: Message-ID: <681c4ba4.170a0220.24e1bd.cbb1@mx.google.com> cmtice wrote: > A very predictable question. Why is the implementation of this more complicated than `base->GetChildMemberWithName(name)`. I think it shouldn't be. > > To avoid repeating the same points over and over, I suggest going through the discussion on the dereference/addressof patch. If you still have questions after that, let me know. GetChildMemberWithName didn't handle everything correctly. I've fixed part of that now. There's still a bug in the code that searches for fields in base classes, which I want to try to find & fix. I've removed all the special handling code from this PR. Please review again. Thanks! https://github.com/llvm/llvm-project/pull/138093 From lldb-commits at lists.llvm.org Wed May 7 23:26:18 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Wed, 07 May 2025 23:26:18 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681c4e8a.a70a0220.a1062.69cd@mx.google.com> https://github.com/jasonmolenda updated https://github.com/llvm/llvm-project/pull/138805 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Wed May 7 23:28:57 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Wed, 07 May 2025 23:28:57 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681c4f29.170a0220.1ac9a8.5a60@mx.google.com> https://github.com/jasonmolenda updated https://github.com/llvm/llvm-project/pull/138805 >From 4fc9acd03a58a3617b113352c48e5dd2fdb58eda Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Tue, 6 May 2025 22:37:17 -0700 Subject: [PATCH 1/4] [lldb] Provide lr value in faulting frame on arm64 When a frameless function faults or is interrupted asynchronously, the UnwindPlan MAY have no register location rule for the return address register (lr on arm64); the value is simply live in the lr register when it was interrupted, and the frame below this on the stack -- e.g. sigtramp on a Unix system -- has the full register context, including that register. RegisterContextUnwind::SavedLocationForRegister, when asked to find the caller's pc value, will first see if there is a pc register location. If there isn't, on a Return Address Register architecture like arm/mips/riscv, we rewrite the register request from "pc" to "RA register", and search for a location. On frame 0 (the live frame) and an interrupted frame, the UnwindPlan may have no register location rule for the RA Reg, that is valid. A frameless function that never calls another may simply keep the return address in the live register the whole way. Our instruction emulation unwind plans explicitly add a rule (see Pavel's May 2024 change https://github.com/llvm/llvm-project/pull/91321 ), but an UnwindPlan sourced from debug_frame may not. I've got a case where this exactly happens - clang debug_frame for arm64 where there is no register location for the lr in a frameless function. There is a fault in the middle of this frameless function and we only get the lr value from the fault handler below this frame if lr has a register location of `IsSame`, in line with Pavel's 2024 change. Similar to how we see a request of the RA Reg from frame 0 after failing to find an unwind location for the pc register, the same style of special casing is needed when this is a function that was interrupted. Without this change, we can find the pc of the frame that was executing when it was interrupted, but we need $lr to find its caller, and we don't descend down to the trap handler to get that value, truncating the stack. rdar://145614545 --- lldb/source/Target/RegisterContextUnwind.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 3ed49e12476dd..23a86bee2518b 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -1377,6 +1377,7 @@ RegisterContextUnwind::SavedLocationForRegister( } } + // Check if the active_row has a register location listed. if (regnum.IsValid() && active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind), unwindplan_regloc)) { @@ -1390,11 +1391,10 @@ RegisterContextUnwind::SavedLocationForRegister( // This is frame 0 and we're retrieving the PC and it's saved in a Return // Address register and it hasn't been saved anywhere yet -- that is, // it's still live in the actual register. Handle this specially. - if (!have_unwindplan_regloc && return_address_reg.IsValid() && - IsFrameZero()) { - if (return_address_reg.GetAsKind(eRegisterKindLLDB) != - LLDB_INVALID_REGNUM) { + return_address_reg.GetAsKind(eRegisterKindLLDB) != + LLDB_INVALID_REGNUM) { + if (IsFrameZero()) { lldb_private::UnwindLLDB::ConcreteRegisterLocation new_regloc; new_regloc.type = UnwindLLDB::ConcreteRegisterLocation:: eRegisterInLiveRegisterContext; @@ -1408,6 +1408,17 @@ RegisterContextUnwind::SavedLocationForRegister( return_address_reg.GetAsKind(eRegisterKindLLDB), return_address_reg.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } else if (BehavesLikeZerothFrame()) { + // This function was interrupted asynchronously -- it faulted, + // an async interrupt, a timer fired, a debugger expression etc. + // The caller's pc is in the Return Address register, but the + // UnwindPlan for this function may have no location rule for + // the RA reg. + // This means that the caller's return address is in the RA reg + // when the function was interrupted--descend down one stack frame + // to retrieve it from the trap handler's saved context. + unwindplan_regloc.SetSame(); + have_unwindplan_regloc = true; } } >From b10162deb49ecddca6439665c2b8ea1995fdd81f Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:24:17 -0700 Subject: [PATCH 2/4] Add the sources for an API test case to be written --- .../interrupt-and-trap-funcs.s | 92 +++++++++++++++++++ .../macosx/unwind-frameless-faulted/main.c | 6 ++ 2 files changed, 98 insertions(+) create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/main.c diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s new file mode 100644 index 0000000000000..23eb35c2b31ca --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -0,0 +1,92 @@ +#define DW_CFA_register 0x9 +#define ehframe_x22 22 +#define ehframe_x23 23 +#define ehframe_pc 32 + + .section __TEXT,__text,regular,pure_instructions + +//-------------------------------------- +// to_be_interrupted() a frameless function that does a non-ABI +// function call ("is interrupted/traps" simulated) to trap(). +// Before it branches to trap(), it puts its return address in +// x23. trap() knows to branch back to $x23 when it has finished. +//-------------------------------------- + .globl _to_be_interrupted + .p2align 2 +_to_be_interrupted: + .cfi_startproc + + // This is a garbage entry to ensure that eh_frame is emitted, + // it isn't used for anything. If there's no eh_frame, lldb + // can do an assembly emulation scan and add a rule for $lr + // which won't expose the issue at hand. + .cfi_escape DW_CFA_register, ehframe_x22, ehframe_x23 + mov x24, x0 + add x24, x24, #1 + + adrp x23, L_.return at PAGE ; put return address in x4 + add x23, x23, L_.return at PAGEOFF + + b _trap ; branch to trap handler, fake async interrupt + +L_.return: + mov x0, x24 + ret + .cfi_endproc + + + +//-------------------------------------- +// trap() trap handler function, sets up stack frame +// with special unwind rule for the pc value of the +// "interrupted" stack frame (it's in x23), then calls +// break_to_debugger(). +//-------------------------------------- + .globl _trap + .p2align 2 +_trap: + .cfi_startproc + .cfi_signal_frame + + // The pc value when we were interrupted is in x23 + .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 + + // standard prologue save of fp & lr so we can call puts() + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 + .cfi_def_cfa w29, 16 + .cfi_offset w30, -8 + .cfi_offset w29, -16 + + bl _break_to_debugger + + ldp x29, x30, [sp, #16] + add sp, sp, #32 + + // jump back to $x23 to resume execution of to_be_interrupted + br x23 + .cfi_endproc + +//-------------------------------------- +// break_to_debugger() executes a BRK instruction +//-------------------------------------- + .globl _break_to_debugger + .p2align 2 +_break_to_debugger: + .cfi_startproc + + // standard prologue save of fp & lr so we can call puts() + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 + .cfi_def_cfa w29, 16 + .cfi_offset w30, -8 + .cfi_offset w29, -16 + + brk #0xf000 ;; __builtin_debugtrap aarch64 instruction + + ldp x29, x30, [sp, #16] + add sp, sp, #32 + ret + .cfi_endproc diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c b/lldb/test/API/macosx/unwind-frameless-faulted/main.c new file mode 100644 index 0000000000000..e121809f25478 --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/main.c @@ -0,0 +1,6 @@ +int to_be_interrupted(int); +int main() { + int c = 10; + c = to_be_interrupted(c); + return c; +} >From 508b9cb404072263a6ed52a75b3b7aad5d949510 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:24:58 -0700 Subject: [PATCH 3/4] Add a log msg when a frame is marked as a trap handler via the UnwindPlan. And use the trap handler flag for zeroth frame so we can properly unwind a frameless function that was interrupted while in the trap handler function. --- lldb/source/Target/RegisterContextUnwind.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 51c0f56e9bc1e..cf4b96c6eda9f 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -248,6 +248,7 @@ void RegisterContextUnwind::InitializeZerothFrame() { active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); row_register_kind = m_full_unwind_plan_sp->GetRegisterKind(); + PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp); if (active_row && log) { StreamString active_row_strm; active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, @@ -1933,6 +1934,7 @@ void RegisterContextUnwind::PropagateTrapHandlerFlagFromUnwindPlan( } m_frame_type = eTrapHandlerFrame; + UnwindLogMsg("This frame is marked as a trap handler via its UnwindPlan"); if (m_current_offset_backed_up_one != m_current_offset) { // We backed up the pc by 1 to compute the symbol context, but >From d8843fe86c0eaaa1483bed0317f832b7630d8548 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:28:40 -0700 Subject: [PATCH 4/4] Update assembly a bit. --- .../interrupt-and-trap-funcs.s | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 23eb35c2b31ca..708ad9ed56a1c 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -51,7 +51,8 @@ _trap: // The pc value when we were interrupted is in x23 .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 - // standard prologue save of fp & lr so we can call puts() + // standard prologue save of fp & lr so we can call + // break_to_debugger() sub sp, sp, #32 stp x29, x30, [sp, #16] add x29, sp, #16 @@ -76,17 +77,7 @@ _trap: _break_to_debugger: .cfi_startproc - // standard prologue save of fp & lr so we can call puts() - sub sp, sp, #32 - stp x29, x30, [sp, #16] - add x29, sp, #16 - .cfi_def_cfa w29, 16 - .cfi_offset w30, -8 - .cfi_offset w29, -16 - brk #0xf000 ;; __builtin_debugtrap aarch64 instruction - ldp x29, x30, [sp, #16] - add sp, sp, #32 ret .cfi_endproc From lldb-commits at lists.llvm.org Wed May 7 23:32:41 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Wed, 07 May 2025 23:32:41 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681c5009.170a0220.ed7b3.2774@mx.google.com> jasonmolenda wrote: I'm thinking I'll write an API test that instruction steps through the whole thing and backtraces at each point, making sure it can get every function between the current one & main at all points, just for fun extra stress testing. https://github.com/llvm/llvm-project/pull/138805 From lldb-commits at lists.llvm.org Thu May 8 00:20:33 2025 From: lldb-commits at lists.llvm.org (Ely Ronnen via lldb-commits) Date: Thu, 08 May 2025 00:20:33 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Migrate 'continue' request to new RequestHandler. (PR #138987) In-Reply-To: Message-ID: <681c5b41.a70a0220.2f8c81.7556@mx.google.com> https://github.com/eronnen approved this pull request. https://github.com/llvm/llvm-project/pull/138987 From lldb-commits at lists.llvm.org Thu May 8 00:22:41 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 00:22:41 -0700 (PDT) Subject: [Lldb-commits] [lldb] e18f248 - [lldb][test] Disable flaky test_qThreadInfo_matches_qC_attach test on AArch64 Linux (#138940) Message-ID: <681c5bc1.050a0220.15a3dd.e8fe@mx.google.com> Author: Dmitry Vasilyev Date: 2025-05-08T11:22:37+04:00 New Revision: e18f248956b317f06f7822920c72d7a2eebcd267 URL: https://github.com/llvm/llvm-project/commit/e18f248956b317f06f7822920c72d7a2eebcd267 DIFF: https://github.com/llvm/llvm-project/commit/e18f248956b317f06f7822920c72d7a2eebcd267.diff LOG: [lldb][test] Disable flaky test_qThreadInfo_matches_qC_attach test on AArch64 Linux (#138940) See #138085 for details. https://lab.llvm.org/buildbot/#/builders/59/builds/16937 https://lab.llvm.org/buildbot/#/builders/59/builds/17224 Added: Modified: lldb/test/API/tools/lldb-server/TestLldbGdbServer.py Removed: ################################################################################ diff --git a/lldb/test/API/tools/lldb-server/TestLldbGdbServer.py b/lldb/test/API/tools/lldb-server/TestLldbGdbServer.py index 2c328125e3058..67690a275f0da 100644 --- a/lldb/test/API/tools/lldb-server/TestLldbGdbServer.py +++ b/lldb/test/API/tools/lldb-server/TestLldbGdbServer.py @@ -202,6 +202,13 @@ def test_qThreadInfo_matches_qC_launch(self): self.set_inferior_startup_launch() self.qThreadInfo_matches_qC() + # This test is flaky on AArch64 Linux. Sometimes it causes an unhandled Error: + # Operation not permitted in lldb_private::process_linux::NativeProcessLinux::Attach(int). + @skipIf( + oslist=["linux"], + archs=["aarch64"], + bugnumber="github.com/llvm/llvm-project/issues/138085", + ) @expectedFailureAll(oslist=["windows"]) # expect one more thread stopped def test_qThreadInfo_matches_qC_attach(self): self.build() From lldb-commits at lists.llvm.org Thu May 8 00:22:43 2025 From: lldb-commits at lists.llvm.org (Dmitry Vasilyev via lldb-commits) Date: Thu, 08 May 2025 00:22:43 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][test] Disable flaky test_qThreadInfo_matches_qC_attach test on AArch64 Linux (PR #138940) In-Reply-To: Message-ID: <681c5bc3.050a0220.2f1ad4.6d9e@mx.google.com> https://github.com/slydiman closed https://github.com/llvm/llvm-project/pull/138940 From lldb-commits at lists.llvm.org Thu May 8 00:35:53 2025 From: lldb-commits at lists.llvm.org (Ely Ronnen via lldb-commits) Date: Thu, 08 May 2025 00:35:53 -0700 (PDT) Subject: [Lldb-commits] [lldb] [llvm] [lldb-dap] Migrating breakpointLocations request to use typed RequestHandler (PR #137426) In-Reply-To: Message-ID: <681c5ed9.170a0220.d302d.e778@mx.google.com> https://github.com/eronnen updated https://github.com/llvm/llvm-project/pull/137426 >From 84638973a3592411025d5294d1f0e93dddba3bf7 Mon Sep 17 00:00:00 2001 From: Ely Ronnen Date: Sat, 26 Apr 2025 01:22:05 +0200 Subject: [PATCH] Migrating breakpointLocations request to use typed RequestHandler --- .../Handler/BreakpointLocationsHandler.cpp | 156 +++--------------- lldb/tools/lldb-dap/Handler/RequestHandler.h | 10 +- .../lldb-dap/Protocol/ProtocolRequests.cpp | 17 ++ .../lldb-dap/Protocol/ProtocolRequests.h | 38 +++++ .../tools/lldb-dap/Protocol/ProtocolTypes.cpp | 14 ++ lldb/tools/lldb-dap/Protocol/ProtocolTypes.h | 21 +++ llvm/include/llvm/Support/JSON.h | 8 + 7 files changed, 125 insertions(+), 139 deletions(-) diff --git a/lldb/tools/lldb-dap/Handler/BreakpointLocationsHandler.cpp b/lldb/tools/lldb-dap/Handler/BreakpointLocationsHandler.cpp index 7a477f3e97875..2ac886c3a5d2c 100644 --- a/lldb/tools/lldb-dap/Handler/BreakpointLocationsHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/BreakpointLocationsHandler.cpp @@ -9,136 +9,22 @@ #include "DAP.h" #include "JSONUtils.h" #include "RequestHandler.h" +#include namespace lldb_dap { -// "BreakpointLocationsRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "The `breakpointLocations` request returns all possible -// locations for source breakpoints in a given range.\nClients should only -// call this request if the corresponding capability -// `supportsBreakpointLocationsRequest` is true.", -// "properties": { -// "command": { -// "type": "string", -// "enum": [ "breakpointLocations" ] -// }, -// "arguments": { -// "$ref": "#/definitions/BreakpointLocationsArguments" -// } -// }, -// "required": [ "command" ] -// }] -// }, -// "BreakpointLocationsArguments": { -// "type": "object", -// "description": "Arguments for `breakpointLocations` request.", -// "properties": { -// "source": { -// "$ref": "#/definitions/Source", -// "description": "The source location of the breakpoints; either -// `source.path` or `source.sourceReference` must be specified." -// }, -// "line": { -// "type": "integer", -// "description": "Start line of range to search possible breakpoint -// locations in. If only the line is specified, the request returns all -// possible locations in that line." -// }, -// "column": { -// "type": "integer", -// "description": "Start position within `line` to search possible -// breakpoint locations in. It is measured in UTF-16 code units and the -// client capability `columnsStartAt1` determines whether it is 0- or -// 1-based. If no column is given, the first position in the start line is -// assumed." -// }, -// "endLine": { -// "type": "integer", -// "description": "End line of range to search possible breakpoint -// locations in. If no end line is given, then the end line is assumed to -// be the start line." -// }, -// "endColumn": { -// "type": "integer", -// "description": "End position within `endLine` to search possible -// breakpoint locations in. It is measured in UTF-16 code units and the -// client capability `columnsStartAt1` determines whether it is 0- or -// 1-based. If no end column is given, the last position in the end line -// is assumed." -// } -// }, -// "required": [ "source", "line" ] -// }, -// "BreakpointLocationsResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to `breakpointLocations` request.\nContains -// possible locations for source breakpoints.", -// "properties": { -// "body": { -// "type": "object", -// "properties": { -// "breakpoints": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/BreakpointLocation" -// }, -// "description": "Sorted set of possible breakpoint locations." -// } -// }, -// "required": [ "breakpoints" ] -// } -// }, -// "required": [ "body" ] -// }] -// }, -// "BreakpointLocation": { -// "type": "object", -// "description": "Properties of a breakpoint location returned from the -// `breakpointLocations` request.", -// "properties": { -// "line": { -// "type": "integer", -// "description": "Start line of breakpoint location." -// }, -// "column": { -// "type": "integer", -// "description": "The start position of a breakpoint location. Position -// is measured in UTF-16 code units and the client capability -// `columnsStartAt1` determines whether it is 0- or 1-based." -// }, -// "endLine": { -// "type": "integer", -// "description": "The end line of breakpoint location if the location -// covers a range." -// }, -// "endColumn": { -// "type": "integer", -// "description": "The end position of a breakpoint location (if the -// location covers a range). Position is measured in UTF-16 code units and -// the client capability `columnsStartAt1` determines whether it is 0- or -// 1-based." -// } -// }, -// "required": [ "line" ] -// }, -void BreakpointLocationsRequestHandler::operator()( - const llvm::json::Object &request) const { - llvm::json::Object response; - FillResponse(request, response); - auto *arguments = request.getObject("arguments"); - auto *source = arguments->getObject("source"); - std::string path = GetString(source, "path").value_or("").str(); - const auto start_line = GetInteger(arguments, "line") - .value_or(LLDB_INVALID_LINE_NUMBER); - const auto start_column = GetInteger(arguments, "column") - .value_or(LLDB_INVALID_COLUMN_NUMBER); - const auto end_line = - GetInteger(arguments, "endLine").value_or(start_line); - const auto end_column = GetInteger(arguments, "endColumn") - .value_or(std::numeric_limits::max()); +/// The `breakpointLocations` request returns all possible locations for source +/// breakpoints in a given range. Clients should only call this request if the +/// corresponding capability `supportsBreakpointLocationsRequest` is true. +llvm::Expected +BreakpointLocationsRequestHandler::Run( + const protocol::BreakpointLocationsArguments &args) const { + std::string path = args.source.path.value_or(""); + uint32_t start_line = args.line; + uint32_t start_column = args.column.value_or(LLDB_INVALID_COLUMN_NUMBER); + uint32_t end_line = args.endLine.value_or(start_line); + uint32_t end_column = + args.endColumn.value_or(std::numeric_limits::max()); lldb::SBFileSpec file_spec(path.c_str(), true); lldb::SBSymbolContextList compile_units = @@ -191,18 +77,16 @@ void BreakpointLocationsRequestHandler::operator()( std::sort(locations.begin(), locations.end()); locations.erase(llvm::unique(locations), locations.end()); - llvm::json::Array locations_json; + std::vector breakpoint_locations; for (auto &l : locations) { - llvm::json::Object location; - location.try_emplace("line", l.first); - location.try_emplace("column", l.second); - locations_json.emplace_back(std::move(location)); + protocol::BreakpointLocation lc; + lc.line = l.first; + lc.column = l.second; + breakpoint_locations.push_back(std::move(lc)); } - llvm::json::Object body; - body.try_emplace("breakpoints", std::move(locations_json)); - response.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(response))); + return protocol::BreakpointLocationsResponseBody{ + /*breakpoints=*/std::move(breakpoint_locations)}; } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index 9e9cfb13d77b8..8fb1a250e0e8e 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.h +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h @@ -204,14 +204,18 @@ class AttachRequestHandler : public LegacyRequestHandler { void operator()(const llvm::json::Object &request) const override; }; -class BreakpointLocationsRequestHandler : public LegacyRequestHandler { +class BreakpointLocationsRequestHandler + : public RequestHandler< + protocol::BreakpointLocationsArguments, + llvm::Expected> { public: - using LegacyRequestHandler::LegacyRequestHandler; + using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "breakpointLocations"; } FeatureSet GetSupportedFeatures() const override { return {protocol::eAdapterFeatureBreakpointLocationsRequest}; } - void operator()(const llvm::json::Object &request) const override; + llvm::Expected + Run(const protocol::BreakpointLocationsArguments &args) const override; }; class CompletionsRequestHandler : public LegacyRequestHandler { diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp index d9ffc0c04e134..8702c2faabdf8 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp @@ -245,6 +245,23 @@ bool fromJSON(const json::Value &Params, Configuration &C, json::Path P) { parseSourceMap(Params, C.sourceMap, P); } +bool fromJSON(const json::Value &Params, BreakpointLocationsArguments &BLA, + json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("source", BLA.source) && O.map("line", BLA.line) && + O.mapOptional("column", BLA.column) && + O.mapOptional("endLine", BLA.endLine) && + O.mapOptional("endColumn", BLA.endColumn); +} + +llvm::json::Value toJSON(const BreakpointLocationsResponseBody &BLRB) { + llvm::json::Array breakpoints_json; + for (const auto &breakpoint : BLRB.breakpoints) { + breakpoints_json.push_back(toJSON(breakpoint)); + } + return llvm::json::Object{{"breakpoints", std::move(breakpoints_json)}}; +} + bool fromJSON(const json::Value &Params, LaunchRequestArguments &LRA, json::Path P) { json::ObjectMapper O(Params, P); diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h index 110e90837c0cd..f92bf9124ae1c 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h @@ -30,6 +30,7 @@ #include #include #include +#include namespace lldb_dap::protocol { @@ -446,6 +447,43 @@ bool fromJSON(const llvm::json::Value &, StepOutArguments &, llvm::json::Path); /// body field is required. using StepOutResponse = VoidResponse; +/// Arguments for `breakpointLocations` request. +struct BreakpointLocationsArguments { + /// The source location of the breakpoints; either `source.path` or + /// `source.sourceReference` must be specified. + Source source; + + /// Start line of range to search possible breakpoint locations in. If only + /// the line is specified, the request returns all possible locations in that + /// line. + uint32_t line; + + /// Start position within `line` to search possible breakpoint locations in. + /// It is measured in UTF-16 code units and the client capability + /// `columnsStartAt1` determines whether it is 0- or 1-based. If no column is + /// given, the first position in the start line is assumed. + std::optional column; + + /// End line of range to search possible breakpoint locations in. If no end + /// line is given, then the end line is assumed to be the start line. + std::optional endLine; + + /// End position within `endLine` to search possible breakpoint locations in. + /// It is measured in UTF-16 code units and the client capability + /// `columnsStartAt1` determines whether it is 0- or 1-based. If no end column + /// is given, the last position in the end line is assumed. + std::optional endColumn; +}; +bool fromJSON(const llvm::json::Value &, BreakpointLocationsArguments &, + llvm::json::Path); + +/// Response to `breakpointLocations` request. +struct BreakpointLocationsResponseBody { + /// Content of the source reference. + std::vector breakpoints; +}; +llvm::json::Value toJSON(const BreakpointLocationsResponseBody &); + } // namespace lldb_dap::protocol #endif diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp index 8f4defb6fd2ea..967c1d2321502 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp @@ -260,4 +260,18 @@ bool fromJSON(const llvm::json::Value &Params, ValueFormat &VF, return O && O.mapOptional("hex", VF.hex); } +json::Value toJSON(const BreakpointLocation &B) { + json::Object result; + + result.insert({"line", B.line}); + if (B.column) + result.insert({"column", *B.column}); + if (B.endLine) + result.insert({"endLine", *B.endLine}); + if (B.endColumn) + result.insert({"endColumn", *B.endColumn}); + + return result; +} + } // namespace lldb_dap::protocol diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h index ae1ba90d1666a..8ea483d810e02 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h @@ -329,6 +329,27 @@ struct ValueFormat { }; bool fromJSON(const llvm::json::Value &, ValueFormat &, llvm::json::Path); +/// Properties of a breakpoint location returned from the `breakpointLocations` +/// request. +struct BreakpointLocation { + /// Start line of breakpoint location. + uint32_t line; + + /// The start position of a breakpoint location. Position is measured in + /// UTF-16 code units and the client capability `columnsStartAt1` determines + /// whether it is 0- or 1-based. + std::optional column; + + /// The end line of breakpoint location if the location covers a range. + std::optional endLine; + + /// The end position of a breakpoint location (if the location covers a + /// range). Position is measured in UTF-16 code units and the client + /// capability `columnsStartAt1` determines whether it is 0- or 1-based. + std::optional endColumn; +}; +llvm::json::Value toJSON(const BreakpointLocation &); + } // namespace lldb_dap::protocol #endif diff --git a/llvm/include/llvm/Support/JSON.h b/llvm/include/llvm/Support/JSON.h index 7f7f5f6228763..f1f4f4db709dd 100644 --- a/llvm/include/llvm/Support/JSON.h +++ b/llvm/include/llvm/Support/JSON.h @@ -776,6 +776,14 @@ inline bool fromJSON(const Value &E, bool &Out, Path P) { P.report("expected boolean"); return false; } +inline bool fromJSON(const Value &E, unsigned int &Out, Path P) { + if (auto S = E.getAsInteger()) { + Out = *S; + return true; + } + P.report("expected integer"); + return false; +} inline bool fromJSON(const Value &E, uint64_t &Out, Path P) { if (auto S = E.getAsUINT64()) { Out = *S; From lldb-commits at lists.llvm.org Thu May 8 00:59:38 2025 From: lldb-commits at lists.llvm.org (Hu Jialun via lldb-commits) Date: Thu, 08 May 2025 00:59:38 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Implement `runInTerminal` for Windows (PR #121269) In-Reply-To: Message-ID: <681c646a.170a0220.3bb871.4fe3@mx.google.com> ================ @@ -24,41 +31,105 @@ using namespace llvm; namespace lldb_dap { -FifoFile::FifoFile(StringRef path) : m_path(path) {} +FifoFile::FifoFile(std::string path, FILE *f) : m_path(path), m_file(f) {} + +Expected FifoFile::create(StringRef path) { + auto file = fopen(path.data(), "r+"); + if (file == nullptr) + return createStringError(inconvertibleErrorCode(), + "Failed to open fifo file " + path); + if (setvbuf(file, NULL, _IONBF, 0)) + return createStringError(inconvertibleErrorCode(), + "Failed to setvbuf on fifo file " + path); + return FifoFile(path, file); +} + +FifoFile::FifoFile(StringRef path, FILE *f) : m_path(path), m_file(f) {} +FifoFile::FifoFile(FifoFile &&other) + : m_path(other.m_path), m_file(other.m_file) { + other.m_path.clear(); + other.m_file = nullptr; +} FifoFile::~FifoFile() { + if (m_file) + fclose(m_file); #if !defined(_WIN32) + // Unreferenced named pipes are deleted automatically on Win32 unlink(m_path.c_str()); #endif } -Expected> CreateFifoFile(StringRef path) { -#if defined(_WIN32) - return createStringError(inconvertibleErrorCode(), "Unimplemented"); +// This probably belongs to llvm::sys::fs as another FSEntity type +Error createUniqueNamedPipe(const Twine &prefix, StringRef suffix, + int &result_fd, + SmallVectorImpl &result_path) { + std::error_code EC; +#ifdef _WIN32 + const char *middle = suffix.empty() ? "-%%%%%%" : "-%%%%%%."; + EC = sys::fs::getPotentiallyUniqueFileName( + "\\\\.\\pipe\\LOCAL\\" + prefix + middle + suffix, result_path); +#else + EC = sys::fs::getPotentiallyUniqueTempFileName(prefix, suffix, result_path); +#endif + if (EC) + return errorCodeToError(EC); + result_path.push_back(0); + const char *path = result_path.data(); +#ifdef _WIN32 + HANDLE h = ::CreateNamedPipeA( + path, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, ---------------- SuibianP wrote: > when its opened for reading it blocks until the file is also opened for writing and vice versa This is implemented with [`ConnectNamedPipe`](https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-connectnamedpipe) in `FifoFileIO::WaitForPeer` after creation. `WaitNamedPipe` was not needed as the server side would already be around by the time the client tries to connect. > deadlock the process by having it wait on itself accidentally Intra-process deadlock (waiting on its own pending output) should not be possible, since on Windows a process cannot even read back what it wrote into a named pipe from the same handle. The data flow is bidirectional, but can only be consumed by the other end. Inter-process deadlock, with the exception of buffering, is also rather unlikely. Current protocol over the pipe is strictly synchronous and sequential, or "half-duplex" in some sense -- there are no overlapping transmissions; in other words, the process will not transmit before expected response is fully received, and will not block on read before it sends out the message. Buffering can still cause deadlocks. Without the `rewind`, the process proceeds to wait for the response after write, but the payload has somehow not fully arrived at the other end, and the peer as a result does not respond yet. I speculated it to be due to libc buffering, but did not manage to penetrate through MS CRT code to verify. > Should we instead switch to a communication channel that supports reading and writing for all platforms? Using sockets feels a bit like overkill, but would definitely make code more uniform across platforms I suppose. I can give it a try if you deem it more maintainable. > we have some existing helpers for supporting sockets already. As an aside, I remember I did also attempt to retrofit the use case into `lldb_private::Pipe`, but gave up because duplex was too hard to shoehorn into the existing abstraction, and would have very limited utility even if implemented. > Is that something you might have a chance to look into? I am not too familiar with that so unable to give guarantees, but let me give it a try. Also unsure if the socket rewrite is going to make it for LLVM 21 feature freeze in July. https://github.com/llvm/llvm-project/pull/121269 From lldb-commits at lists.llvm.org Thu May 8 01:03:38 2025 From: lldb-commits at lists.llvm.org (Michael Buch via lldb-commits) Date: Thu, 08 May 2025 01:03:38 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Change the statusline format to print "no target" (PR #139021) In-Reply-To: Message-ID: <681c655a.170a0220.3079f9.5418@mx.google.com> https://github.com/Michael137 approved this pull request. https://github.com/llvm/llvm-project/pull/139021 From lldb-commits at lists.llvm.org Thu May 8 01:48:40 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Thu, 08 May 2025 01:48:40 -0700 (PDT) Subject: [Lldb-commits] [lldb] 2668167 - [lldb] Disable some lldb-dap tests on Windows Message-ID: <681c6fe8.050a0220.1cddbb.6b8b@mx.google.com> Author: David Spickett Date: 2025-05-08T08:47:12Z New Revision: 2668167e2cf935528f7d93cb3b12a651a29e52f6 URL: https://github.com/llvm/llvm-project/commit/2668167e2cf935528f7d93cb3b12a651a29e52f6 DIFF: https://github.com/llvm/llvm-project/commit/2668167e2cf935528f7d93cb3b12a651a29e52f6.diff LOG: [lldb] Disable some lldb-dap tests on Windows Since https://github.com/llvm/llvm-project/pull/138981 / https://github.com/llvm/llvm-project/commit/aeeb9a3c09f40f42a1e8e5e3c8dbde3b260744bd were landed and tests re-enabled, these tests have been failing on our Windows on Arm bot: https://lab.llvm.org/buildbot/#/builders/141/builds/8523 ******************** Unresolved Tests (1): lldb-api :: tools/lldb-dap/send-event/TestDAP_sendEvent.py ******************** Failed Tests (2): lldb-api :: tools/lldb-dap/launch/TestDAP_launch.py lldb-api :: tools/lldb-dap/stackTrace/TestDAP_stackTrace.py Added: Modified: lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py Removed: ################################################################################ diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py index e8e9181f8da8d..acbe0366d1ecc 100644 --- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py +++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py @@ -546,6 +546,7 @@ def test_terminate_commands(self): ) self.verify_commands("terminateCommands", output, terminateCommands) + @skipIfWindows def test_version(self): """ Tests that "initialize" response contains the "version" string the same diff --git a/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py b/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py index 64cec70aa923b..3e015186d4b81 100644 --- a/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py +++ b/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py @@ -10,6 +10,7 @@ class TestDAP_sendEvent(lldbdap_testcase.DAPTestCaseBase): + @skipIfWindows def test_send_event(self): """ Test sending a custom event. @@ -42,6 +43,7 @@ def test_send_event(self): self.assertEqual(custom_event["event"], "my-custom-event") self.assertEqual(custom_event["body"], custom_event_body) + @skipIfWindows def test_send_internal_event(self): """ Test sending an internal event produces an error. diff --git a/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py b/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py index edf4adae14a3b..9c6f1d42feda2 100644 --- a/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py +++ b/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py @@ -201,6 +201,7 @@ def test_stackTrace(self): 0, len(stackFrames), "verify zero frames with startFrame out of bounds" ) + @skipIfWindows def test_functionNameWithArgs(self): """ Test that the stack frame without a function name is given its pc in the response. @@ -215,6 +216,7 @@ def test_functionNameWithArgs(self): frame = self.get_stackFrames()[0] self.assertEqual(frame["name"], "recurse(x=1)") + @skipIfWindows def test_StackFrameFormat(self): """ Test the StackFrameFormat. From lldb-commits at lists.llvm.org Thu May 8 01:52:19 2025 From: lldb-commits at lists.llvm.org (Dhruv Srivastava via lldb-commits) Date: Thu, 08 May 2025 01:52:19 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][AIX] Support for XCOFF Sections (PR #131304) In-Reply-To: Message-ID: <681c70c3.050a0220.b8c2e.576b@mx.google.com> https://github.com/DhruvSrivastavaX updated https://github.com/llvm/llvm-project/pull/131304 >From 106e137fea7d4b420ce3d97a8df16c3a91400997 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Fri, 14 Mar 2025 02:51:21 -0500 Subject: [PATCH 1/4] Support for XCOFF Sections --- .../ObjectFile/XCOFF/ObjectFileXCOFF.cpp | 153 +++++++++++++----- 1 file changed, 114 insertions(+), 39 deletions(-) diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp index b54d43c5dd737..0dd9126468923 100644 --- a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp @@ -190,50 +190,125 @@ void ObjectFileXCOFF::ParseSymtab(Symtab &lldb_symtab) {} bool ObjectFileXCOFF::IsStripped() { return false; } -void ObjectFileXCOFF::CreateSections(SectionList &unified_section_list) {} - -void ObjectFileXCOFF::Dump(Stream *s) {} - -ArchSpec ObjectFileXCOFF::GetArchitecture() { - ArchSpec arch_spec = - ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); - return arch_spec; +void ObjectFileXCOFF::CreateSections(SectionList &unified_section_list) { + + if (m_sections_up) + return; + m_sections_up = std::make_unique(); + ModuleSP module_sp(GetModule()); + if (module_sp) { + std::lock_guard guard(module_sp->GetMutex()); + + ModuleSP module_sp(GetModule()); + for (auto sIdx = m_binary->section_begin(); sIdx != m_binary->section_end(); + ++sIdx) { + llvm::Expected name = + m_binary->getSectionName(sIdx->getRawDataRefImpl()); + if (!name) { + llvm::Error err = name.takeError(); + } + llvm::StringRef sect_name = *name; + ConstString const_sect_name(sect_name); + int sect_index = sIdx->getIndex(), idx = 1; + llvm::Expected section = + m_binary->getSectionByNum(sect_index); + if (!section) { + llvm::Error err = section.takeError(); + } + llvm::object::DataRefImpl dataref = section.get(); + const llvm::object::XCOFFSectionHeader64 *sectionPtr = + reinterpret_cast( + dataref.p); + + SectionType section_type = lldb::eSectionTypeOther; + if (sectionPtr->Flags & XCOFF::STYP_TEXT) + section_type = eSectionTypeCode; + if (sectionPtr->Flags & XCOFF::STYP_DATA) + section_type = eSectionTypeData; + if (sectionPtr->Flags & XCOFF::STYP_BSS) + section_type = eSectionTypeZeroFill; + if (sectionPtr->Flags & XCOFF::STYP_DWARF) { + SectionType section_type = + llvm::StringSwitch(sect_name) + .Case(".dwinfo", eSectionTypeDWARFDebugInfo) + .Case(".dwline", eSectionTypeDWARFDebugLine) + .Case(".dwabrev", eSectionTypeDWARFDebugAbbrev) + .Default(eSectionTypeInvalid); + + if (section_type == eSectionTypeInvalid) + section_type = lldb::eSectionTypeOther; + } + SectionSP section_sp(new Section( + module_sp, // Module to which this section belongs + this, // Object file to which this section belongs + idx++, // Section ID is the 1 based section index. + const_sect_name, // Name of this section + section_type, + sectionPtr->VirtualAddress, // File VM address == addresses as + // they are found in the object file + sectionPtr->SectionSize, // VM size in bytes of this section + sectionPtr->FileOffsetToRawData, // Offset to the data for this + // section in the file + sectionPtr->SectionSize, // Size in bytes of this section as found in + // the file + 0, // FIXME: alignment + sectionPtr->Flags)); // Flags for this section + + uint32_t permissions = 0; + permissions |= ePermissionsReadable; + if (sectionPtr->Flags & (XCOFF::STYP_DATA | XCOFF::STYP_BSS)) + permissions |= ePermissionsWritable; + if (sectionPtr->Flags & XCOFF::STYP_TEXT) + permissions |= ePermissionsExecutable; + section_sp->SetPermissions(permissions); + + m_sections_up->AddSection(section_sp); + unified_section_list.AddSection(section_sp); + } + } } + void ObjectFileXCOFF::Dump(Stream * s) {} -UUID ObjectFileXCOFF::GetUUID() { return UUID(); } + ArchSpec ObjectFileXCOFF::GetArchitecture() { + ArchSpec arch_spec = + ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); + return arch_spec; + } -uint32_t ObjectFileXCOFF::GetDependentModules(FileSpecList &files) { return 0; } + UUID ObjectFileXCOFF::GetUUID() { return UUID(); } -ObjectFile::Type ObjectFileXCOFF::CalculateType() { - if (m_binary->fileHeader64()->Flags & XCOFF::F_EXEC) - return eTypeExecutable; - else if (m_binary->fileHeader64()->Flags & XCOFF::F_SHROBJ) - return eTypeSharedLibrary; - return eTypeUnknown; -} + uint32_t ObjectFileXCOFF::GetDependentModules(FileSpecList & files) { + return 0; + } -ObjectFile::Strata ObjectFileXCOFF::CalculateStrata() { return eStrataUnknown; } + ObjectFile::Type ObjectFileXCOFF::CalculateType() { + if (m_binary->fileHeader64()->Flags & XCOFF::F_EXEC) + return eTypeExecutable; + else if (m_binary->fileHeader64()->Flags & XCOFF::F_SHROBJ) + return eTypeSharedLibrary; + return eTypeUnknown; + } -lldb::WritableDataBufferSP -ObjectFileXCOFF::MapFileDataWritable(const FileSpec &file, uint64_t Size, - uint64_t Offset) { - return FileSystem::Instance().CreateWritableDataBuffer(file.GetPath(), Size, - Offset); -} + ObjectFile::Strata ObjectFileXCOFF::CalculateStrata() { + return eStrataUnknown; + } -ObjectFileXCOFF::ObjectFileXCOFF(const lldb::ModuleSP &module_sp, - DataBufferSP data_sp, - lldb::offset_t data_offset, - const FileSpec *file, - lldb::offset_t file_offset, - lldb::offset_t length) - : ObjectFile(module_sp, file, file_offset, length, data_sp, data_offset) { - if (file) - m_file = *file; -} + lldb::WritableDataBufferSP ObjectFileXCOFF::MapFileDataWritable( + const FileSpec &file, uint64_t Size, uint64_t Offset) { + return FileSystem::Instance().CreateWritableDataBuffer(file.GetPath(), Size, + Offset); + } + + ObjectFileXCOFF::ObjectFileXCOFF( + const lldb::ModuleSP &module_sp, DataBufferSP data_sp, + lldb::offset_t data_offset, const FileSpec *file, + lldb::offset_t file_offset, lldb::offset_t length) + : ObjectFile(module_sp, file, file_offset, length, data_sp, data_offset) { + if (file) + m_file = *file; + } -ObjectFileXCOFF::ObjectFileXCOFF(const lldb::ModuleSP &module_sp, - DataBufferSP header_data_sp, - const lldb::ProcessSP &process_sp, - addr_t header_addr) - : ObjectFile(module_sp, process_sp, header_addr, header_data_sp) {} + ObjectFileXCOFF::ObjectFileXCOFF( + const lldb::ModuleSP &module_sp, DataBufferSP header_data_sp, + const lldb::ProcessSP &process_sp, addr_t header_addr) + : ObjectFile(module_sp, process_sp, header_addr, header_data_sp) {} >From 9c06cd5204c3a78a74947fd8c18c741d701e5d8d Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Fri, 14 Mar 2025 06:15:58 -0500 Subject: [PATCH 2/4] format fix --- .../ObjectFile/XCOFF/ObjectFileXCOFF.cpp | 74 +++++++++---------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp index 0dd9126468923..eebf6a3a657a5 100644 --- a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp @@ -267,48 +267,48 @@ void ObjectFileXCOFF::CreateSections(SectionList &unified_section_list) { } } } - void ObjectFileXCOFF::Dump(Stream * s) {} +void ObjectFileXCOFF::Dump(Stream *s) {} - ArchSpec ObjectFileXCOFF::GetArchitecture() { - ArchSpec arch_spec = - ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); - return arch_spec; - } +ArchSpec ObjectFileXCOFF::GetArchitecture() { + ArchSpec arch_spec = + ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); + return arch_spec; +} - UUID ObjectFileXCOFF::GetUUID() { return UUID(); } +UUID ObjectFileXCOFF::GetUUID() { return UUID(); } - uint32_t ObjectFileXCOFF::GetDependentModules(FileSpecList & files) { - return 0; - } +uint32_t ObjectFileXCOFF::GetDependentModules(FileSpecList &files) { return 0; } - ObjectFile::Type ObjectFileXCOFF::CalculateType() { - if (m_binary->fileHeader64()->Flags & XCOFF::F_EXEC) - return eTypeExecutable; - else if (m_binary->fileHeader64()->Flags & XCOFF::F_SHROBJ) - return eTypeSharedLibrary; - return eTypeUnknown; - } +ObjectFile::Type ObjectFileXCOFF::CalculateType() { + if (m_binary->fileHeader64()->Flags & XCOFF::F_EXEC) + return eTypeExecutable; + else if (m_binary->fileHeader64()->Flags & XCOFF::F_SHROBJ) + return eTypeSharedLibrary; + return eTypeUnknown; +} - ObjectFile::Strata ObjectFileXCOFF::CalculateStrata() { - return eStrataUnknown; - } +ObjectFile::Strata ObjectFileXCOFF::CalculateStrata() { return eStrataUnknown; } - lldb::WritableDataBufferSP ObjectFileXCOFF::MapFileDataWritable( - const FileSpec &file, uint64_t Size, uint64_t Offset) { - return FileSystem::Instance().CreateWritableDataBuffer(file.GetPath(), Size, - Offset); - } +lldb::WritableDataBufferSP +ObjectFileXCOFF::MapFileDataWritable(const FileSpec &file, uint64_t Size, + uint64_t Offset) { + return FileSystem::Instance().CreateWritableDataBuffer(file.GetPath(), Size, + Offset); +} - ObjectFileXCOFF::ObjectFileXCOFF( - const lldb::ModuleSP &module_sp, DataBufferSP data_sp, - lldb::offset_t data_offset, const FileSpec *file, - lldb::offset_t file_offset, lldb::offset_t length) - : ObjectFile(module_sp, file, file_offset, length, data_sp, data_offset) { - if (file) - m_file = *file; - } +ObjectFileXCOFF::ObjectFileXCOFF(const lldb::ModuleSP &module_sp, + DataBufferSP data_sp, + lldb::offset_t data_offset, + const FileSpec *file, + lldb::offset_t file_offset, + lldb::offset_t length) + : ObjectFile(module_sp, file, file_offset, length, data_sp, data_offset) { + if (file) + m_file = *file; +} - ObjectFileXCOFF::ObjectFileXCOFF( - const lldb::ModuleSP &module_sp, DataBufferSP header_data_sp, - const lldb::ProcessSP &process_sp, addr_t header_addr) - : ObjectFile(module_sp, process_sp, header_addr, header_data_sp) {} +ObjectFileXCOFF::ObjectFileXCOFF(const lldb::ModuleSP &module_sp, + DataBufferSP header_data_sp, + const lldb::ProcessSP &process_sp, + addr_t header_addr) + : ObjectFile(module_sp, process_sp, header_addr, header_data_sp) {} >From edb54c3ff6a63c97b4c1d46b046abde981109f40 Mon Sep 17 00:00:00 2001 From: Dhruv-Srivastava Date: Thu, 3 Apr 2025 01:56:56 -0500 Subject: [PATCH 3/4] Test case modification --- lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp | 5 +++-- lldb/test/Shell/ObjectFile/XCOFF/basic-info.yaml | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp index eebf6a3a657a5..179b26df612a0 100644 --- a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp @@ -200,6 +200,7 @@ void ObjectFileXCOFF::CreateSections(SectionList &unified_section_list) { std::lock_guard guard(module_sp->GetMutex()); ModuleSP module_sp(GetModule()); + int idx = 0; for (auto sIdx = m_binary->section_begin(); sIdx != m_binary->section_end(); ++sIdx) { llvm::Expected name = @@ -209,7 +210,7 @@ void ObjectFileXCOFF::CreateSections(SectionList &unified_section_list) { } llvm::StringRef sect_name = *name; ConstString const_sect_name(sect_name); - int sect_index = sIdx->getIndex(), idx = 1; + int sect_index = sIdx->getIndex(); llvm::Expected section = m_binary->getSectionByNum(sect_index); if (!section) { @@ -241,7 +242,7 @@ void ObjectFileXCOFF::CreateSections(SectionList &unified_section_list) { SectionSP section_sp(new Section( module_sp, // Module to which this section belongs this, // Object file to which this section belongs - idx++, // Section ID is the 1 based section index. + ++idx, // Section ID is the 1 based section index. const_sect_name, // Name of this section section_type, sectionPtr->VirtualAddress, // File VM address == addresses as diff --git a/lldb/test/Shell/ObjectFile/XCOFF/basic-info.yaml b/lldb/test/Shell/ObjectFile/XCOFF/basic-info.yaml index 3c0037db36dbb..e5cc9d0bc5063 100644 --- a/lldb/test/Shell/ObjectFile/XCOFF/basic-info.yaml +++ b/lldb/test/Shell/ObjectFile/XCOFF/basic-info.yaml @@ -7,6 +7,9 @@ # CHECK: Stripped: false # CHECK: Type: executable # CHECK: Strata: unknown +# CHECK: Name: .text +# CHECK-NEXT: code +# CHECK-NEXT: r-x --- !XCOFF FileHeader: >From a480048b60c4bf8510fdd28821d4d8146515bf61 Mon Sep 17 00:00:00 2001 From: DhruvSrivastavaX Date: Thu, 8 May 2025 03:51:32 -0500 Subject: [PATCH 4/4] Modified to sections64 and test case --- .../ObjectFile/XCOFF/ObjectFileXCOFF.cpp | 116 +++++++----------- .../Shell/ObjectFile/XCOFF/basic-info.yaml | 86 ++++++++++++- 2 files changed, 128 insertions(+), 74 deletions(-) diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp index 179b26df612a0..59156bda948c6 100644 --- a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp @@ -191,83 +191,59 @@ void ObjectFileXCOFF::ParseSymtab(Symtab &lldb_symtab) {} bool ObjectFileXCOFF::IsStripped() { return false; } void ObjectFileXCOFF::CreateSections(SectionList &unified_section_list) { - if (m_sections_up) return; + m_sections_up = std::make_unique(); ModuleSP module_sp(GetModule()); - if (module_sp) { - std::lock_guard guard(module_sp->GetMutex()); - - ModuleSP module_sp(GetModule()); - int idx = 0; - for (auto sIdx = m_binary->section_begin(); sIdx != m_binary->section_end(); - ++sIdx) { - llvm::Expected name = - m_binary->getSectionName(sIdx->getRawDataRefImpl()); - if (!name) { - llvm::Error err = name.takeError(); - } - llvm::StringRef sect_name = *name; - ConstString const_sect_name(sect_name); - int sect_index = sIdx->getIndex(); - llvm::Expected section = - m_binary->getSectionByNum(sect_index); - if (!section) { - llvm::Error err = section.takeError(); - } - llvm::object::DataRefImpl dataref = section.get(); - const llvm::object::XCOFFSectionHeader64 *sectionPtr = - reinterpret_cast( - dataref.p); - - SectionType section_type = lldb::eSectionTypeOther; - if (sectionPtr->Flags & XCOFF::STYP_TEXT) - section_type = eSectionTypeCode; - if (sectionPtr->Flags & XCOFF::STYP_DATA) - section_type = eSectionTypeData; - if (sectionPtr->Flags & XCOFF::STYP_BSS) - section_type = eSectionTypeZeroFill; - if (sectionPtr->Flags & XCOFF::STYP_DWARF) { - SectionType section_type = - llvm::StringSwitch(sect_name) - .Case(".dwinfo", eSectionTypeDWARFDebugInfo) - .Case(".dwline", eSectionTypeDWARFDebugLine) - .Case(".dwabrev", eSectionTypeDWARFDebugAbbrev) - .Default(eSectionTypeInvalid); - - if (section_type == eSectionTypeInvalid) - section_type = lldb::eSectionTypeOther; - } - SectionSP section_sp(new Section( - module_sp, // Module to which this section belongs - this, // Object file to which this section belongs - ++idx, // Section ID is the 1 based section index. - const_sect_name, // Name of this section - section_type, - sectionPtr->VirtualAddress, // File VM address == addresses as - // they are found in the object file - sectionPtr->SectionSize, // VM size in bytes of this section - sectionPtr->FileOffsetToRawData, // Offset to the data for this - // section in the file - sectionPtr->SectionSize, // Size in bytes of this section as found in - // the file - 0, // FIXME: alignment - sectionPtr->Flags)); // Flags for this section - - uint32_t permissions = 0; - permissions |= ePermissionsReadable; - if (sectionPtr->Flags & (XCOFF::STYP_DATA | XCOFF::STYP_BSS)) - permissions |= ePermissionsWritable; - if (sectionPtr->Flags & XCOFF::STYP_TEXT) - permissions |= ePermissionsExecutable; - section_sp->SetPermissions(permissions); - - m_sections_up->AddSection(section_sp); - unified_section_list.AddSection(section_sp); + + if (!module_sp) + return; + + std::lock_guard guard(module_sp->GetMutex()); + + const auto §ions = m_binary->sections64(); + int idx = 0; + for (size_t i = 0; i < sections.size(); ++i) { + const llvm::object::XCOFFSectionHeader64 §ion = sections[i]; + + ConstString const_sect_name(section.Name); + + SectionType section_type = lldb::eSectionTypeOther; + if (section.Flags & XCOFF::STYP_TEXT) + section_type = eSectionTypeCode; + else if (section.Flags & XCOFF::STYP_DATA) + section_type = eSectionTypeData; + else if (section.Flags & XCOFF::STYP_BSS) + section_type = eSectionTypeZeroFill; + else if (section.Flags & XCOFF::STYP_DWARF) { + section_type = llvm::StringSwitch(section.Name) + .Case(".dwinfo", eSectionTypeDWARFDebugInfo) + .Case(".dwline", eSectionTypeDWARFDebugLine) + .Case(".dwabrev", eSectionTypeDWARFDebugAbbrev) + .Default(eSectionTypeInvalid); + + if (section_type == eSectionTypeInvalid) + section_type = lldb::eSectionTypeOther; } + + SectionSP section_sp(new Section( + module_sp, this, ++idx, const_sect_name, section_type, + section.VirtualAddress, section.SectionSize, + section.FileOffsetToRawData, section.SectionSize, 0, section.Flags)); + + uint32_t permissions = ePermissionsReadable; + if (section.Flags & (XCOFF::STYP_DATA | XCOFF::STYP_BSS)) + permissions |= ePermissionsWritable; + if (section.Flags & XCOFF::STYP_TEXT) + permissions |= ePermissionsExecutable; + + section_sp->SetPermissions(permissions); + m_sections_up->AddSection(section_sp); + unified_section_list.AddSection(section_sp); } } + void ObjectFileXCOFF::Dump(Stream *s) {} ArchSpec ObjectFileXCOFF::GetArchitecture() { diff --git a/lldb/test/Shell/ObjectFile/XCOFF/basic-info.yaml b/lldb/test/Shell/ObjectFile/XCOFF/basic-info.yaml index e5cc9d0bc5063..17ff2f31c2fff 100644 --- a/lldb/test/Shell/ObjectFile/XCOFF/basic-info.yaml +++ b/lldb/test/Shell/ObjectFile/XCOFF/basic-info.yaml @@ -8,13 +8,31 @@ # CHECK: Type: executable # CHECK: Strata: unknown # CHECK: Name: .text -# CHECK-NEXT: code -# CHECK-NEXT: r-x +# CHECK-NEXT: Type: code +# CHECK-NEXT: Permissions: r-x +# CHECK: Name: .data +# CHECK-NEXT: Type: data +# CHECK-NEXT: Permissions: rw- +# CHECK: Name: .bss +# CHECK-NEXT: Type: zero-fill +# CHECK-NEXT: Permissions: rw- +# CHECK: Name: .loader +# CHECK-NEXT: Type: regular +# CHECK-NEXT: Permissions: r-- +# CHECK: Name: .dwline +# CHECK-NEXT: Type: dwarf-line +# CHECK-NEXT: Permissions: r-- +# CHECK: Name: .dwinfo +# CHECK-NEXT: Type: dwarf-info +# CHECK-NEXT: Permissions: r-- +# CHECK: Name: .dwabrev +# CHECK-NEXT: Type: dwarf-abbrev +# CHECK-NEXT: Permissions: r-- --- !XCOFF FileHeader: MagicNumber: 0x1F7 - NumberOfSections: 1 + NumberOfSections: 7 CreationTime: 000000000 Flags: 0x0002 Sections: @@ -25,6 +43,66 @@ Sections: FileOffsetToLineNumbers: 0x0 NumberOfLineNumbers: 0x0 Flags: [ STYP_TEXT ] - SectionData: E8C20000E94204 + SectionData: E8C20000 + - Name: .data + Address: 0x1100008D2 + Size: 0x2AE + FileOffsetToData: 0x8D2 + FileOffsetToRelocations: 0x132E + FileOffsetToLineNumbers: 0x0 + NumberOfRelocations: 0x22 + NumberOfLineNumbers: 0x0 + Flags: [ STYP_DATA ] + SectionData: '' + - Name: .bss + Address: 0x110000B80 + Size: 0x28 + FileOffsetToData: 0x0 + FileOffsetToRelocations: 0x0 + FileOffsetToLineNumbers: 0x0 + NumberOfRelocations: 0x0 + NumberOfLineNumbers: 0x0 + Flags: [ STYP_BSS ] + SectionData: '' + - Name: .loader + Address: 0x0 + Size: 0x413 + FileOffsetToData: 0xB80 + FileOffsetToRelocations: 0x0 + FileOffsetToLineNumbers: 0x0 + NumberOfRelocations: 0x0 + NumberOfLineNumbers: 0x0 + Flags: [ STYP_LOADER ] + SectionData: 00000001 + - Name: .dwline + Address: 0x0 + Size: 0x9C + FileOffsetToData: 0xF94 + FileOffsetToRelocations: 0x150A + FileOffsetToLineNumbers: 0x0 + NumberOfRelocations: 0x5 + NumberOfLineNumbers: 0x0 + Flags: [ STYP_DWARF ] + SectionData: FFFFFFFF + - Name: .dwinfo + Address: 0x0 + Size: 0xDD + FileOffsetToData: 0x1030 + FileOffsetToRelocations: 0x1550 + FileOffsetToLineNumbers: 0x0 + NumberOfRelocations: 0x6 + NumberOfLineNumbers: 0x0 + Flags: [ STYP_DWARF ] + SectionData: FFFFFFFF + - Name: .dwabrev + Address: 0x0 + Size: 0x43 + FileOffsetToData: 0x110E + FileOffsetToRelocations: 0x0 + FileOffsetToLineNumbers: 0x0 + NumberOfRelocations: 0x0 + NumberOfLineNumbers: 0x0 + Flags: [ STYP_DWARF ] + SectionData: 01110125 StringTable: {} ... From lldb-commits at lists.llvm.org Thu May 8 01:53:43 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Thu, 08 May 2025 01:53:43 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Change the launch sequence (reland) (PR #138981) In-Reply-To: Message-ID: <681c7117.170a0220.366d18.624c@mx.google.com> DavidSpickett wrote: A few tests still failing on Windows - https://lab.llvm.org/buildbot/#/builders/141/builds/8523 ``` ******************** Unresolved Tests (1): lldb-api :: tools/lldb-dap/send-event/TestDAP_sendEvent.py ******************** Failed Tests (2): lldb-api :: tools/lldb-dap/launch/TestDAP_launch.py lldb-api :: tools/lldb-dap/stackTrace/TestDAP_stackTrace.py ``` ``` FAIL: test_version (TestDAP_launch.TestDAP_launch.test_version) Tests that "initialize" response contains the "version" string the same ---------------------------------------------------------------------- ========= END ========= Traceback (most recent call last): File "C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\test\API\tools\lldb-dap\launch\TestDAP_launch.py", line 562, in test_version self.continue_to_breakpoints(breakpoint_ids) File "C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\packages\Python\lldbsuite\test\tools\lldb-dap\lldbdap_testcase.py", line 294, in continue_to_breakpoints self.verify_breakpoint_hit(breakpoint_ids) File "C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\packages\Python\lldbsuite\test\tools\lldb-dap\lldbdap_testcase.py", line 113, in verify_breakpoint_hit self.assertTrue(False, "breakpoint not hit") AssertionError: False is not true : breakpoint not hit ``` Looks like your previous theory, does the test need updating somehow? ``` ERROR: test_send_internal_event (TestDAP_sendEvent.TestDAP_sendEvent.test_send_internal_event) Test sending an internal event produces an error. ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\test\API\tools\lldb-dap\send-event\TestDAP_sendEvent.py", line 62, in test_send_internal_event resp["body"]["result"], ~~~~^^^^^^^^ TypeError: list indices must be integers or slices, not str Config=aarch64-C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\build\bin\clang.exe ``` Seems like it was expecting one kind of response, but got a different one. ``` ====================================================================== FAIL: test_StackFrameFormat (TestDAP_stackTrace.TestDAP_stackTrace.test_StackFrameFormat) Test the StackFrameFormat. ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\test\API\tools\lldb-dap\stackTrace\TestDAP_stackTrace.py", line 229, in test_StackFrameFormat frame = self.get_stackFrames(format={"parameters": True})[0] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\packages\Python\lldbsuite\test\tools\lldb-dap\lldbdap_testcase.py", line 194, in get_stackFrames (stackFrames, totalFrames) = self.get_stackFrames_and_totalFramesCount( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\packages\Python\lldbsuite\test\tools\lldb-dap\lldbdap_testcase.py", line 182, in get_stackFrames_and_totalFramesCount totalFrames = self.get_dict_value(response, ["body", "totalFrames"]) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\packages\Python\lldbsuite\test\tools\lldb-dap\lldbdap_testcase.py", line 164, in get_dict_value self.assertTrue( AssertionError: False is not true : key "totalFrames" from key_path "['body', 'totalFrames']" not in "{'body': {'stackFrames': []}, 'command': 'stackTrace', 'request_seq': 7, 'seq': 0, 'success': True, 'type': 'response'}" Config=aarch64-C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\build\bin\clang.exe ``` Again could be what you said, if the program never stopped that explains why there are no stack frames here. https://github.com/llvm/llvm-project/pull/138981 From lldb-commits at lists.llvm.org Thu May 8 02:01:20 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Thu, 08 May 2025 02:01:20 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Re-enable the lldb-dap tests (PR #138791) In-Reply-To: Message-ID: <681c72e0.170a0220.2c734d.e8ff@mx.google.com> DavidSpickett wrote: We have a few failures on Windows on Arm too: https://github.com/llvm/llvm-project/commit/2668167e2cf935528f7d93cb3b12a651a29e52f6 I've skipped those tests for now. https://github.com/llvm/llvm-project/pull/138791 From lldb-commits at lists.llvm.org Thu May 8 02:37:10 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Thu, 08 May 2025 02:37:10 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fixed TestProcessModificationIdOnExpr to work on both x86 and x64 architectures (PR #138941) In-Reply-To: Message-ID: <681c7b46.050a0220.367aba.a294@mx.google.com> https://github.com/DavidSpickett commented: Looks fine just one question. https://github.com/llvm/llvm-project/pull/138941 From lldb-commits at lists.llvm.org Thu May 8 02:37:10 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Thu, 08 May 2025 02:37:10 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fixed TestProcessModificationIdOnExpr to work on both x86 and x64 architectures (PR #138941) In-Reply-To: Message-ID: <681c7b46.050a0220.1137bf.6d58@mx.google.com> https://github.com/DavidSpickett edited https://github.com/llvm/llvm-project/pull/138941 From lldb-commits at lists.llvm.org Thu May 8 02:37:11 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Thu, 08 May 2025 02:37:11 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fixed TestProcessModificationIdOnExpr to work on both x86 and x64 architectures (PR #138941) In-Reply-To: Message-ID: <681c7b47.170a0220.239b15.157d@mx.google.com> ================ @@ -36,34 +39,49 @@ int main() { } // CHECK-LABEL: process status -d -// CHECK: m_stop_id: 2 -// CHECK: m_memory_id: 0 +// CHECK: m_stop_id: [[#STOP_ID:]] +// CHECK: m_memory_id: [[#MEMORY_ID:]] // CHECK-LABEL: expr x.i != 42 // IDs are not changed when executing simple expressions // CHECK-LABEL: process status -d -// CHECK: m_stop_id: 2 -// CHECK: m_memory_id: 0 +// CHECK: m_stop_id: [[#STOP_ID]] +// CHECK: m_memory_id: [[#MEMORY_ID]] + +// CHECK-LABEL: process status -d +// Remember new values ---------------- DavidSpickett wrote: If the IDs are not changed, why do you need to capture them again here? https://github.com/llvm/llvm-project/pull/138941 From lldb-commits at lists.llvm.org Thu May 8 02:59:46 2025 From: lldb-commits at lists.llvm.org (Mikhail Zakharov via lldb-commits) Date: Thu, 08 May 2025 02:59:46 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fixed TestProcessModificationIdOnExpr to work on both x86 and x64 architectures (PR #138941) In-Reply-To: Message-ID: <681c8092.050a0220.284945.a434@mx.google.com> ================ @@ -36,34 +39,49 @@ int main() { } // CHECK-LABEL: process status -d -// CHECK: m_stop_id: 2 -// CHECK: m_memory_id: 0 +// CHECK: m_stop_id: [[#STOP_ID:]] +// CHECK: m_memory_id: [[#MEMORY_ID:]] // CHECK-LABEL: expr x.i != 42 // IDs are not changed when executing simple expressions // CHECK-LABEL: process status -d -// CHECK: m_stop_id: 2 -// CHECK: m_memory_id: 0 +// CHECK: m_stop_id: [[#STOP_ID]] +// CHECK: m_memory_id: [[#MEMORY_ID]] + +// CHECK-LABEL: process status -d +// Remember new values ---------------- real-mikhail wrote: Sure, we can avoid recapturing them but I decided that it is more readable to use the same pattern (all 4 times): 1. Capture variables; 2. Do some "test" command; 3. Check the variables. Otherwise the fact whether we need to capture variable for "testing" next command depends on what was done in the previous command. And some commands change one value but not the other and it becomes messy so I decided that it is simpler to always recapture them. I can change that and avoid unnecessary recaptures if you think that it is bad idea. https://github.com/llvm/llvm-project/pull/138941 From lldb-commits at lists.llvm.org Thu May 8 03:16:56 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Thu, 08 May 2025 03:16:56 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fixed TestProcessModificationIdOnExpr to work on both x86 and x64 architectures (PR #138941) In-Reply-To: Message-ID: <681c8498.050a0220.2847f4.c105@mx.google.com> ================ @@ -36,34 +39,49 @@ int main() { } // CHECK-LABEL: process status -d -// CHECK: m_stop_id: 2 -// CHECK: m_memory_id: 0 +// CHECK: m_stop_id: [[#STOP_ID:]] +// CHECK: m_memory_id: [[#MEMORY_ID:]] // CHECK-LABEL: expr x.i != 42 // IDs are not changed when executing simple expressions // CHECK-LABEL: process status -d -// CHECK: m_stop_id: 2 -// CHECK: m_memory_id: 0 +// CHECK: m_stop_id: [[#STOP_ID]] +// CHECK: m_memory_id: [[#MEMORY_ID]] + +// CHECK-LABEL: process status -d +// Remember new values ---------------- DavidSpickett wrote: Oh I see what you mean. Otherwise you have to unravel the sequence to add anything new and we're liable to make a mistake. https://github.com/llvm/llvm-project/pull/138941 From lldb-commits at lists.llvm.org Thu May 8 03:17:07 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Thu, 08 May 2025 03:17:07 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fixed TestProcessModificationIdOnExpr to work on both x86 and x64 architectures (PR #138941) In-Reply-To: Message-ID: <681c84a3.170a0220.5f78b.5990@mx.google.com> https://github.com/DavidSpickett approved this pull request. LGTM, thanks! https://github.com/llvm/llvm-project/pull/138941 From lldb-commits at lists.llvm.org Thu May 8 04:11:11 2025 From: lldb-commits at lists.llvm.org (Mikhail Zakharov via lldb-commits) Date: Thu, 08 May 2025 04:11:11 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fixed TestProcessModificationIdOnExpr to work on both x86 and x64 architectures (PR #138941) In-Reply-To: Message-ID: <681c914f.170a0220.2fbbe3.75a0@mx.google.com> real-mikhail wrote: Thanks for checking @DavidSpickett. Could you please merge this PR for me? https://github.com/llvm/llvm-project/pull/138941 From lldb-commits at lists.llvm.org Thu May 8 04:21:30 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 04:21:30 -0700 (PDT) Subject: [Lldb-commits] [lldb] 0d47a45 - [lldb][DataFormatters] Change ExtractIndexFromString to return std::optional (#138297) Message-ID: <681c93ba.630a0220.3c0424.13be@mx.google.com> Author: Charles Zablit Date: 2025-05-08T12:21:26+01:00 New Revision: 0d47a4548c17b320e02e33a1e250792626652e59 URL: https://github.com/llvm/llvm-project/commit/0d47a4548c17b320e02e33a1e250792626652e59 DIFF: https://github.com/llvm/llvm-project/commit/0d47a4548c17b320e02e33a1e250792626652e59.diff LOG: [lldb][DataFormatters] Change ExtractIndexFromString to return std::optional (#138297) This PR is in continuation of https://github.com/llvm/llvm-project/pull/136693. Added: Modified: lldb/include/lldb/DataFormatters/FormattersHelpers.h lldb/source/DataFormatters/FormattersHelpers.cpp lldb/source/DataFormatters/VectorType.cpp lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp lldb/source/Plugins/Language/CPlusPlus/LibCxxProxyArray.cpp lldb/source/Plugins/Language/CPlusPlus/LibCxxSliceArray.cpp lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp lldb/source/Plugins/Language/CPlusPlus/LibCxxValarray.cpp lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp lldb/source/Plugins/Language/ObjC/NSArray.cpp lldb/source/Plugins/Language/ObjC/NSDictionary.cpp lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp lldb/source/Plugins/Language/ObjC/NSSet.cpp Removed: ################################################################################ diff --git a/lldb/include/lldb/DataFormatters/FormattersHelpers.h b/lldb/include/lldb/DataFormatters/FormattersHelpers.h index a98042fd40f93..42699d0a0b1b1 100644 --- a/lldb/include/lldb/DataFormatters/FormattersHelpers.h +++ b/lldb/include/lldb/DataFormatters/FormattersHelpers.h @@ -53,7 +53,7 @@ void AddFilter(TypeCategoryImpl::SharedPointer category_sp, llvm::StringRef type_name, ScriptedSyntheticChildren::Flags flags, bool regex = false); -size_t ExtractIndexFromString(const char *item_name); +std::optional ExtractIndexFromString(const char *item_name); Address GetArrayAddressOrPointerValue(ValueObject &valobj); diff --git a/lldb/source/DataFormatters/FormattersHelpers.cpp b/lldb/source/DataFormatters/FormattersHelpers.cpp index 085ed3d0a2f29..5f5541c352623 100644 --- a/lldb/source/DataFormatters/FormattersHelpers.cpp +++ b/lldb/source/DataFormatters/FormattersHelpers.cpp @@ -97,18 +97,17 @@ void lldb_private::formatters::AddFilter( category_sp->AddTypeFilter(type_name, match_type, filter_sp); } -size_t lldb_private::formatters::ExtractIndexFromString(const char *item_name) { +std::optional +lldb_private::formatters::ExtractIndexFromString(const char *item_name) { if (!item_name || !*item_name) - return UINT32_MAX; + return std::nullopt; if (*item_name != '[') - return UINT32_MAX; + return std::nullopt; item_name++; char *endptr = nullptr; unsigned long int idx = ::strtoul(item_name, &endptr, 0); - if (idx == 0 && endptr == item_name) - return UINT32_MAX; - if (idx == ULONG_MAX) - return UINT32_MAX; + if ((idx == 0 && endptr == item_name) || idx == ULONG_MAX) + return std::nullopt; return idx; } diff --git a/lldb/source/DataFormatters/VectorType.cpp b/lldb/source/DataFormatters/VectorType.cpp index eab2612d1e941..8a842b8675f57 100644 --- a/lldb/source/DataFormatters/VectorType.cpp +++ b/lldb/source/DataFormatters/VectorType.cpp @@ -270,10 +270,13 @@ class VectorTypeSyntheticFrontEnd : public SyntheticChildrenFrontEnd { } llvm::Expected GetIndexOfChildWithName(ConstString name) override { - const char *item_name = name.GetCString(); - uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *optional_idx; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); return idx; diff --git a/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp b/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp index 234471d5ba518..f2521ec750875 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp @@ -29,11 +29,12 @@ class GenericBitsetFrontEnd : public SyntheticChildrenFrontEnd { GenericBitsetFrontEnd(ValueObject &valobj, StdLib stdlib); llvm::Expected GetIndexOfChildWithName(ConstString name) override { - size_t idx = formatters::ExtractIndexFromString(name.GetCString()); - if (idx == UINT32_MAX) + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - return idx; + } + return *optional_idx; } lldb::ChildCacheState Update() override; diff --git a/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp b/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp index 6f1b2ee3fd9e3..b1fdc0fe37763 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp @@ -39,11 +39,12 @@ class GenericOptionalFrontend : public SyntheticChildrenFrontEnd { llvm::Expected GetIndexOfChildWithName(ConstString name) override { if (name == "$$dereference$$") return 0; - size_t idx = formatters::ExtractIndexFromString(name.GetCString()); - if (idx == UINT32_MAX) + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - return idx; + } + return *optional_idx; } llvm::Expected CalculateNumChildren() override { diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp index e8a886dd71821..d952688f381f5 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp @@ -108,12 +108,12 @@ lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd:: return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - size_t idx = ExtractIndexFromString(name.GetCString()); - if (idx == UINT32_MAX) { + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return idx; + return *optional_idx; } lldb_private::SyntheticChildrenFrontEnd * diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp index ea18c02ee6591..30db5f15c388f 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp @@ -107,11 +107,12 @@ class ListIterator { class AbstractListFrontEnd : public SyntheticChildrenFrontEnd { public: llvm::Expected GetIndexOfChildWithName(ConstString name) override { - size_t idx = ExtractIndexFromString(name.GetCString()); - if (idx == UINT32_MAX) + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - return idx; + } + return *optional_idx; } lldb::ChildCacheState Update() override; diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp index fec2d6f29ca54..41441dfbc7180 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp @@ -395,12 +395,12 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() { llvm::Expected lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: GetIndexOfChildWithName(ConstString name) { - size_t idx = ExtractIndexFromString(name.GetCString()); - if (idx == UINT32_MAX) { + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return idx; + return *optional_idx; } SyntheticChildrenFrontEnd * diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxProxyArray.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxProxyArray.cpp index 41fc704d5886f..a3d34c6e76d1e 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxProxyArray.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxProxyArray.cpp @@ -179,12 +179,12 @@ lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEnd:: if (!m_base) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - size_t idx = ExtractIndexFromString(name.GetCString()); - if (idx == UINT32_MAX) { + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return idx; + return *optional_idx; } lldb_private::SyntheticChildrenFrontEnd * diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxSliceArray.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxSliceArray.cpp index 1c4d8509374f1..3bdb099d21012 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxSliceArray.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxSliceArray.cpp @@ -150,11 +150,12 @@ lldb_private::formatters::LibcxxStdSliceArraySyntheticFrontEnd:: if (!m_start) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - size_t idx = ExtractIndexFromString(name.GetCString()); - if (idx == UINT32_MAX) + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - return idx; + } + return *optional_idx; } lldb_private::SyntheticChildrenFrontEnd * diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp index 43721e4b41fb8..496bbd07304d6 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp @@ -133,12 +133,12 @@ llvm::Expected lldb_private::formatters:: if (!m_start) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - size_t idx = ExtractIndexFromString(name.GetCString()); - if (idx == UINT32_MAX) { + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return idx; + return *optional_idx; } lldb_private::SyntheticChildrenFrontEnd * diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp index 270aad5ea9310..ebc6d92aabe05 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp @@ -21,11 +21,12 @@ class TupleFrontEnd: public SyntheticChildrenFrontEnd { } llvm::Expected GetIndexOfChildWithName(ConstString name) override { - size_t idx = formatters::ExtractIndexFromString(name.GetCString()); - if (idx == UINT32_MAX) + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - return idx; + } + return *optional_idx; } lldb::ChildCacheState Update() override; diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp index 313664e691c9f..aad387137ea50 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp @@ -294,12 +294,12 @@ lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::Update() { llvm::Expected lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: GetIndexOfChildWithName(ConstString name) { - size_t idx = ExtractIndexFromString(name.GetCString()); - if (idx == UINT32_MAX) { + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return idx; + return *optional_idx; } SyntheticChildrenFrontEnd * diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxValarray.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxValarray.cpp index 172df6358789e..3a8bf3a45ce54 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxValarray.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxValarray.cpp @@ -129,12 +129,12 @@ lldb_private::formatters::LibcxxStdValarraySyntheticFrontEnd:: if (!m_start || !m_finish) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - size_t idx = ExtractIndexFromString(name.GetCString()); - if (idx == UINT32_MAX) { + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return idx; + return *optional_idx; } lldb_private::SyntheticChildrenFrontEnd * diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp index 2419683a4d713..30fec4e2dde0f 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp @@ -203,11 +203,12 @@ class VariantFrontEnd : public SyntheticChildrenFrontEnd { } llvm::Expected GetIndexOfChildWithName(ConstString name) override { - size_t index = formatters::ExtractIndexFromString(name.GetCString()); - if (index == UINT32_MAX) + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - return index; + } + return *optional_idx; } lldb::ChildCacheState Update() override; diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp index abbf1ed935b6b..4bcdf01c221a3 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp @@ -170,12 +170,12 @@ lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd:: if (!m_start || !m_finish) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - size_t index = formatters::ExtractIndexFromString(name.GetCString()); - if (index == UINT32_MAX) { + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return index; + return *optional_idx; } lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd:: @@ -273,10 +273,13 @@ lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd:: if (!m_count || !m_base_data_address) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); - const char *item_name = name.GetCString(); - uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *optional_idx; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); return idx; diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp index cae5aa379b245..cf72265bfbad3 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp @@ -98,12 +98,12 @@ LibStdcppTupleSyntheticFrontEnd::CalculateNumChildren() { llvm::Expected LibStdcppTupleSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) { - size_t index = formatters::ExtractIndexFromString(name.GetCString()); - if (index == UINT32_MAX) { + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { return llvm::createStringError("Type has no child named '%s'", name.AsCString()); } - return index; + return *optional_idx; } SyntheticChildrenFrontEnd * diff --git a/lldb/source/Plugins/Language/ObjC/NSArray.cpp b/lldb/source/Plugins/Language/ObjC/NSArray.cpp index 5e72bd5a6bab6..25376e064879d 100644 --- a/lldb/source/Plugins/Language/ObjC/NSArray.cpp +++ b/lldb/source/Plugins/Language/ObjC/NSArray.cpp @@ -528,10 +528,13 @@ lldb_private::formatters::GenericNSArrayMSyntheticFrontEnd::Update() { llvm::Expected lldb_private::formatters::NSArrayMSyntheticFrontEndBase:: GetIndexOfChildWithName(ConstString name) { - const char *item_name = name.GetCString(); - size_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *optional_idx; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); return idx; @@ -616,10 +619,13 @@ template llvm::Expected lldb_private::formatters::GenericNSArrayISyntheticFrontEnd< D32, D64, Inline>::GetIndexOfChildWithName(ConstString name) { - const char *item_name = name.GetCString(); - uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *optional_idx; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); return idx; diff --git a/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp b/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp index b04b4610a0bf4..ef1c2c89fe125 100644 --- a/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp +++ b/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp @@ -587,10 +587,13 @@ lldb_private::formatters::NSDictionaryISyntheticFrontEnd:: llvm::Expected lldb_private::formatters:: NSDictionaryISyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) { - const char *item_name = name.GetCString(); - uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *optional_idx; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); return idx; @@ -722,12 +725,15 @@ lldb_private::formatters::NSCFDictionarySyntheticFrontEnd:: llvm::Expected lldb_private::formatters:: NSCFDictionarySyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) { - const char *item_name = name.GetCString(); - const uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *optional_idx; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", - name.AsCString(), idx); + name.AsCString()); return idx; } @@ -856,10 +862,13 @@ lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd:: llvm::Expected lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd:: GetIndexOfChildWithName(ConstString name) { - const char *item_name = name.GetCString(); - uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *optional_idx; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); return idx; @@ -1058,10 +1067,13 @@ template llvm::Expected lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd< D32, D64>::GetIndexOfChildWithName(ConstString name) { - const char *item_name = name.GetCString(); - uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *optional_idx; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); return idx; @@ -1217,10 +1229,13 @@ lldb_private::formatters::Foundation1100:: llvm::Expected lldb_private::formatters::Foundation1100:: NSDictionaryMSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) { - const char *item_name = name.GetCString(); - uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *optional_idx; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); return idx; diff --git a/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp b/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp index 93d852b7e748c..b5360195e91d2 100644 --- a/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp +++ b/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp @@ -127,10 +127,13 @@ class NSIndexPathSyntheticFrontEnd : public SyntheticChildrenFrontEnd { bool MightHaveChildren() override { return m_impl.m_mode != Mode::Invalid; } llvm::Expected GetIndexOfChildWithName(ConstString name) override { - const char *item_name = name.GetCString(); - uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *optional_idx; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); return idx; diff --git a/lldb/source/Plugins/Language/ObjC/NSSet.cpp b/lldb/source/Plugins/Language/ObjC/NSSet.cpp index 71674ea63f4ca..7d814e656dc5f 100644 --- a/lldb/source/Plugins/Language/ObjC/NSSet.cpp +++ b/lldb/source/Plugins/Language/ObjC/NSSet.cpp @@ -389,10 +389,13 @@ lldb_private::formatters::NSSetISyntheticFrontEnd::~NSSetISyntheticFrontEnd() { llvm::Expected lldb_private::formatters::NSSetISyntheticFrontEnd::GetIndexOfChildWithName( ConstString name) { - const char *item_name = name.GetCString(); - uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *optional_idx; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); return idx; @@ -524,10 +527,13 @@ lldb_private::formatters::NSCFSetSyntheticFrontEnd::NSCFSetSyntheticFrontEnd( llvm::Expected lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetIndexOfChildWithName( ConstString name) { - const char *item_name = name.GetCString(); - const uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *optional_idx; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); return idx; @@ -660,10 +666,13 @@ lldb_private::formatters::GenericNSSetMSyntheticFrontEnd:: template llvm::Expected lldb_private::formatters::GenericNSSetMSyntheticFrontEnd< D32, D64>::GetIndexOfChildWithName(ConstString name) { - const char *item_name = name.GetCString(); - uint32_t idx = ExtractIndexFromString(item_name); - if (idx == UINT32_MAX || - (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())) + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *optional_idx; + if (idx >= CalculateNumChildrenIgnoringErrors()) return llvm::createStringError("Type has no child named '%s'", name.AsCString()); return idx; From lldb-commits at lists.llvm.org Thu May 8 04:21:33 2025 From: lldb-commits at lists.llvm.org (Michael Buch via lldb-commits) Date: Thu, 08 May 2025 04:21:33 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][DataFormatters] Change ExtractIndexFromString to return std::optional (PR #138297) In-Reply-To: Message-ID: <681c93bd.050a0220.20b192.443c@mx.google.com> https://github.com/Michael137 closed https://github.com/llvm/llvm-project/pull/138297 From lldb-commits at lists.llvm.org Thu May 8 05:01:45 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 05:01:45 -0700 (PDT) Subject: [Lldb-commits] [lldb] 4132141 - [lldb] Expose QueueThreadPlanForStepSingleInstruction function to SBThreadPlan (#137904) Message-ID: <681c9d29.630a0220.7c9e3.14a4@mx.google.com> Author: Ely Ronnen Date: 2025-05-08T14:01:42+02:00 New Revision: 41321416815d74a4a7fd15c78fcfa5af457625bb URL: https://github.com/llvm/llvm-project/commit/41321416815d74a4a7fd15c78fcfa5af457625bb DIFF: https://github.com/llvm/llvm-project/commit/41321416815d74a4a7fd15c78fcfa5af457625bb.diff LOG: [lldb] Expose QueueThreadPlanForStepSingleInstruction function to SBThreadPlan (#137904) Expose `QueueThreadPlanForStepSingleInstruction` function to SBThreadPlan Added: Modified: lldb/include/lldb/API/SBThreadPlan.h lldb/source/API/SBThreadPlan.cpp lldb/test/API/functionalities/step_scripted/Steps.py lldb/test/API/functionalities/step_scripted/TestStepScripted.py lldb/test/API/functionalities/step_scripted/main.c Removed: ################################################################################ diff --git a/lldb/include/lldb/API/SBThreadPlan.h b/lldb/include/lldb/API/SBThreadPlan.h index d02ab80f76a76..1f0164efcfb98 100644 --- a/lldb/include/lldb/API/SBThreadPlan.h +++ b/lldb/include/lldb/API/SBThreadPlan.h @@ -105,6 +105,9 @@ class LLDB_API SBThreadPlan { SBThreadPlan QueueThreadPlanForStepOut(uint32_t frame_idx_to_step_to, bool first_insn, SBError &error); + SBThreadPlan QueueThreadPlanForStepSingleInstruction(bool step_over, + SBError &error); + SBThreadPlan QueueThreadPlanForRunToAddress(SBAddress address); SBThreadPlan QueueThreadPlanForRunToAddress(SBAddress address, SBError &error); diff --git a/lldb/source/API/SBThreadPlan.cpp b/lldb/source/API/SBThreadPlan.cpp index 083a050de8a38..c8ca6c81a3efb 100644 --- a/lldb/source/API/SBThreadPlan.cpp +++ b/lldb/source/API/SBThreadPlan.cpp @@ -325,6 +325,29 @@ SBThreadPlan::QueueThreadPlanForStepOut(uint32_t frame_idx_to_step_to, return SBThreadPlan(); } +SBThreadPlan +SBThreadPlan::QueueThreadPlanForStepSingleInstruction(bool step_over, + SBError &error) { + LLDB_INSTRUMENT_VA(this, step_over, error); + + ThreadPlanSP thread_plan_sp(GetSP()); + if (thread_plan_sp) { + Status plan_status; + SBThreadPlan plan( + thread_plan_sp->GetThread().QueueThreadPlanForStepSingleInstruction( + step_over, false, false, plan_status)); + + if (plan_status.Fail()) + error.SetErrorString(plan_status.AsCString()); + else + plan.GetSP()->SetPrivate(true); + + return plan; + } + + return SBThreadPlan(); +} + SBThreadPlan SBThreadPlan::QueueThreadPlanForRunToAddress(SBAddress sb_address) { LLDB_INSTRUMENT_VA(this, sb_address); diff --git a/lldb/test/API/functionalities/step_scripted/Steps.py b/lldb/test/API/functionalities/step_scripted/Steps.py index 3325dba753657..e2a03c9988111 100644 --- a/lldb/test/API/functionalities/step_scripted/Steps.py +++ b/lldb/test/API/functionalities/step_scripted/Steps.py @@ -45,6 +45,26 @@ def queue_child_thread_plan(self): return self.thread_plan.QueueThreadPlanForStepScripted("Steps.StepOut") +class StepSingleInstruction(StepWithChild): + def __init__(self, thread_plan, dict): + super().__init__(thread_plan) + + def queue_child_thread_plan(self): + return self.thread_plan.QueueThreadPlanForStepSingleInstruction( + False, lldb.SBError() + ) + + +class StepSingleInstructionWithStepOver(StepWithChild): + def __init__(self, thread_plan, dict): + super().__init__(thread_plan) + + def queue_child_thread_plan(self): + return self.thread_plan.QueueThreadPlanForStepSingleInstruction( + True, lldb.SBError() + ) + + # This plan does a step-over until a variable changes value. class StepUntil(StepWithChild): def __init__(self, thread_plan, args_data): diff --git a/lldb/test/API/functionalities/step_scripted/TestStepScripted.py b/lldb/test/API/functionalities/step_scripted/TestStepScripted.py index 53901718019f9..54bc154590ed0 100644 --- a/lldb/test/API/functionalities/step_scripted/TestStepScripted.py +++ b/lldb/test/API/functionalities/step_scripted/TestStepScripted.py @@ -44,6 +44,48 @@ def step_out_with_scripted_plan(self, name): stop_desc = thread.GetStopDescription(1000) self.assertIn("Stepping out from", stop_desc, "Got right description") + def run_until_branch_instruction(self): + self.build() + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "Break on branch instruction", self.main_source_file + ) + + # Check that we landed in a call instruction + frame = thread.GetFrameAtIndex(0) + current_instruction = target.ReadInstructions(frame.GetPCAddress(), 1)[0] + self.assertEqual( + lldb.eInstructionControlFlowKindCall, + current_instruction.GetControlFlowKind(target), + ) + return (target, process, thread, bkpt) + + def test_step_single_instruction(self): + (target, process, thread, bkpt) = self.run_until_branch_instruction() + + err = thread.StepUsingScriptedThreadPlan("Steps.StepSingleInstruction") + self.assertSuccess(err) + + # Verify that stepping a single instruction after "foo();" steps into `foo` + frame = thread.GetFrameAtIndex(0) + self.assertEqual("foo", frame.GetFunctionName()) + + def test_step_single_instruction_with_step_over(self): + (target, process, thread, bkpt) = self.run_until_branch_instruction() + + frame = thread.GetFrameAtIndex(0) + next_instruction = target.ReadInstructions(frame.GetPCAddress(), 2)[1] + next_instruction_address = next_instruction.GetAddress() + + err = thread.StepUsingScriptedThreadPlan( + "Steps.StepSingleInstructionWithStepOver" + ) + self.assertSuccess(err) + + # Verify that stepping over an instruction doesn't step into `foo` + frame = thread.GetFrameAtIndex(0) + self.assertEqual("main", frame.GetFunctionName()) + self.assertEqual(next_instruction_address, frame.GetPCAddress()) + def test_misspelled_plan_name(self): """Test that we get a useful error if we misspell the plan class name""" self.build() diff --git a/lldb/test/API/functionalities/step_scripted/main.c b/lldb/test/API/functionalities/step_scripted/main.c index bfd8a35d55626..9023120c44312 100644 --- a/lldb/test/API/functionalities/step_scripted/main.c +++ b/lldb/test/API/functionalities/step_scripted/main.c @@ -8,6 +8,6 @@ void foo() { } int main() { - foo(); + foo(); // Break on branch instruction. return 0; } From lldb-commits at lists.llvm.org Thu May 8 05:01:50 2025 From: lldb-commits at lists.llvm.org (Ely Ronnen via lldb-commits) Date: Thu, 08 May 2025 05:01:50 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Expose QueueThreadPlanForStepSingleInstruction function to SBThreadPlan (PR #137904) In-Reply-To: Message-ID: <681c9d2e.050a0220.2b88d3.baf4@mx.google.com> https://github.com/eronnen closed https://github.com/llvm/llvm-project/pull/137904 From lldb-commits at lists.llvm.org Thu May 8 06:17:19 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 06:17:19 -0700 (PDT) Subject: [Lldb-commits] =?utf-8?q?=5Blldb=5D_=5Blldb=5D_print_a_notice_whe?= =?utf-8?q?n_=60source_list=60_paging_reaches_the_end_of_th=E2=80=A6_=28PR?= =?utf-8?b?ICMxMzc1MTUp?= In-Reply-To: Message-ID: <681caedf.170a0220.2e20fd.68be@mx.google.com> hapeeeeee wrote: Hi @JDevlieghere @jinmingjian , all required checks have passed and the PR has received approvals. Would you mind helping merge this PR? Thanks! https://github.com/llvm/llvm-project/pull/137515 From lldb-commits at lists.llvm.org Thu May 8 07:01:20 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 07:01:20 -0700 (PDT) Subject: [Lldb-commits] =?utf-8?q?=5Blldb=5D_b5674cb_-_=5Blldb=5D_print_a_?= =?utf-8?q?notice_when_=60source_list=60_paging_reaches_the_end_of_th?= =?utf-8?b?4oCmICgjMTM3NTE1KQ==?= Message-ID: <681cb930.050a0220.1ea59f.484d@mx.google.com> Author: Zax Date: 2025-05-08T07:01:16-07:00 New Revision: b5674cb7be1b010be181883601a3674ceef38683 URL: https://github.com/llvm/llvm-project/commit/b5674cb7be1b010be181883601a3674ceef38683 DIFF: https://github.com/llvm/llvm-project/commit/b5674cb7be1b010be181883601a3674ceef38683.diff LOG: [lldb] print a notice when `source list` paging reaches the end of th… (#137515) Added: lldb/test/Shell/Commands/command-list-reach-beginning-of-file.test lldb/test/Shell/Commands/command-list-reach-end-of-file.test Modified: lldb/include/lldb/Core/SourceManager.h lldb/source/Commands/CommandObjectSource.cpp lldb/source/Core/SourceManager.cpp Removed: ################################################################################ diff --git a/lldb/include/lldb/Core/SourceManager.h b/lldb/include/lldb/Core/SourceManager.h index d929f7bd9bf22..1244291596b73 100644 --- a/lldb/include/lldb/Core/SourceManager.h +++ b/lldb/include/lldb/Core/SourceManager.h @@ -155,6 +155,9 @@ class SourceManager { ~SourceManager(); FileSP GetLastFile() { return GetFile(m_last_support_file_sp); } + bool AtLastLine(bool reverse) { + return m_last_line == UINT32_MAX || (reverse && m_last_line == 1); + } size_t DisplaySourceLinesWithLineNumbers( lldb::SupportFileSP support_file_sp, uint32_t line, uint32_t column, diff --git a/lldb/source/Commands/CommandObjectSource.cpp b/lldb/source/Commands/CommandObjectSource.cpp index c205813565d52..8c87af590a372 100644 --- a/lldb/source/Commands/CommandObjectSource.cpp +++ b/lldb/source/Commands/CommandObjectSource.cpp @@ -1067,7 +1067,16 @@ class CommandObjectSourceList : public CommandObjectParsed { &result.GetOutputStream(), m_options.num_lines, m_options.reverse, GetBreakpointLocations())) { result.SetStatus(eReturnStatusSuccessFinishResult); + } else { + if (target.GetSourceManager().AtLastLine(m_options.reverse)) { + result.AppendNoteWithFormatv( + "Reached {0} of the file, no more to page", + m_options.reverse ? "beginning" : "end"); + } else { + result.AppendNote("No source available"); + } } + } else { if (m_options.num_lines == 0) m_options.num_lines = 10; diff --git a/lldb/source/Core/SourceManager.cpp b/lldb/source/Core/SourceManager.cpp index d63d42de14e80..f786866a18137 100644 --- a/lldb/source/Core/SourceManager.cpp +++ b/lldb/source/Core/SourceManager.cpp @@ -360,10 +360,7 @@ size_t SourceManager::DisplayMoreWithLineNumbers( GetDefaultFileAndLine(); if (last_file_sp) { - if (m_last_line == UINT32_MAX) - return 0; - - if (reverse && m_last_line == 1) + if (AtLastLine(reverse)) return 0; if (count > 0) diff --git a/lldb/test/Shell/Commands/command-list-reach-beginning-of-file.test b/lldb/test/Shell/Commands/command-list-reach-beginning-of-file.test new file mode 100644 index 0000000000000..5ca1b5c2306a7 --- /dev/null +++ b/lldb/test/Shell/Commands/command-list-reach-beginning-of-file.test @@ -0,0 +1,29 @@ +# RUN: %clang_host -g -O0 %S/Inputs/sigchld.c -o %t.out +# RUN: %lldb %t.out -b -s %s 2>&1 | FileCheck %s + +list +# CHECK: note: No source available + +b main +# CHECK: Breakpoint 1: + +r +# CHECK: int main() + +list +# CHECK: if (child_pid == 0) + +list - +# CHECK: int main() + +list -10 +# CHECK: #include + +list - +# CHECK: note: Reached beginning of the file, no more to page + +list - +# CHECK: note: Reached beginning of the file, no more to page + +list +# CHECK: int main() diff --git a/lldb/test/Shell/Commands/command-list-reach-end-of-file.test b/lldb/test/Shell/Commands/command-list-reach-end-of-file.test new file mode 100644 index 0000000000000..c5e9c8169e7d9 --- /dev/null +++ b/lldb/test/Shell/Commands/command-list-reach-end-of-file.test @@ -0,0 +1,26 @@ +# RUN: %clang_host -g -O0 %S/Inputs/sigchld.c -o %t.out +# RUN: %lldb %t.out -b -s %s 2>&1 | FileCheck %s + +list +# CHECK: note: No source available + +b main +# CHECK: Breakpoint 1: + +r +# CHECK: int main() + +list +# CHECK: if (child_pid == 0) + +list +# CHECK: printf("signo = %d\n", SIGCHLD); + +list +# CHECK: return 0; + +list +# CHECK: note: Reached end of the file, no more to page + +list +# CHECK: note: Reached end of the file, no more to page \ No newline at end of file From lldb-commits at lists.llvm.org Thu May 8 07:01:23 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Thu, 08 May 2025 07:01:23 -0700 (PDT) Subject: [Lldb-commits] =?utf-8?q?=5Blldb=5D_=5Blldb=5D_print_a_notice_whe?= =?utf-8?q?n_=60source_list=60_paging_reaches_the_end_of_th=E2=80=A6_=28PR?= =?utf-8?b?ICMxMzc1MTUp?= In-Reply-To: Message-ID: <681cb933.170a0220.22506c.729a@mx.google.com> https://github.com/JDevlieghere closed https://github.com/llvm/llvm-project/pull/137515 From lldb-commits at lists.llvm.org Thu May 8 07:01:41 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 07:01:41 -0700 (PDT) Subject: [Lldb-commits] =?utf-8?q?=5Blldb=5D_=5Blldb=5D_print_a_notice_whe?= =?utf-8?q?n_=60source_list=60_paging_reaches_the_end_of_th=E2=80=A6_=28PR?= =?utf-8?b?ICMxMzc1MTUp?= In-Reply-To: Message-ID: <681cb945.050a0220.f11e8.5f20@mx.google.com> github-actions[bot] wrote: @hapeeeeee Congratulations on having your first Pull Request (PR) merged into the LLVM Project! Your changes will be combined with recent changes from other authors, then tested by our [build bots](https://lab.llvm.org/buildbot/). If there is a problem with a build, you may receive a report in an email or a comment on this PR. Please check whether problems have been caused by your change specifically, as the builds can include changes from many authors. It is not uncommon for your change to be included in a build that fails due to someone else's changes, or infrastructure issues. How to do this, and the rest of the post-merge process, is covered in detail [here](https://llvm.org/docs/MyFirstTypoFix.html#myfirsttypofix-issues-after-landing-your-pr). If your change does cause a problem, it may be reverted, or you can revert it yourself. This is a normal part of [LLVM development](https://llvm.org/docs/DeveloperPolicy.html#patch-reversion-policy). You can fix your changes and open a new PR to merge them again. If you don't get any reports, no action is required from you. Your changes are working as expected, well done! https://github.com/llvm/llvm-project/pull/137515 From lldb-commits at lists.llvm.org Thu May 8 07:11:10 2025 From: lldb-commits at lists.llvm.org (Vy Nguyen via lldb-commits) Date: Thu, 08 May 2025 07:11:10 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb]Make `list` command work with headers when possible. (PR #139002) In-Reply-To: Message-ID: <681cbb7e.170a0220.28f8e3.3187@mx.google.com> https://github.com/oontvoo updated https://github.com/llvm/llvm-project/pull/139002 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Thu May 8 07:16:24 2025 From: lldb-commits at lists.llvm.org (Vy Nguyen via lldb-commits) Date: Thu, 08 May 2025 07:16:24 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb]Make `list` command work with headers when possible. (PR #139002) In-Reply-To: Message-ID: <681cbcb8.170a0220.39873b.2f11@mx.google.com> https://github.com/oontvoo edited https://github.com/llvm/llvm-project/pull/139002 From lldb-commits at lists.llvm.org Thu May 8 07:27:50 2025 From: lldb-commits at lists.llvm.org (Felipe de Azevedo Piovezan via lldb-commits) Date: Thu, 08 May 2025 07:27:50 -0700 (PDT) Subject: [Lldb-commits] [lldb] bbafa52 - [lldb] Fix asan failure in MinidumpFileBuilder Message-ID: <681cbf66.a70a0220.e4811.0f47@mx.google.com> Author: Felipe de Azevedo Piovezan Date: 2025-05-08T07:27:09-07:00 New Revision: bbafa5214e8d5d5daf7cf428780500b13a7d6cbb URL: https://github.com/llvm/llvm-project/commit/bbafa5214e8d5d5daf7cf428780500b13a7d6cbb DIFF: https://github.com/llvm/llvm-project/commit/bbafa5214e8d5d5daf7cf428780500b13a7d6cbb.diff LOG: [lldb] Fix asan failure in MinidumpFileBuilder As per comment in https://github.com/llvm/llvm-project/pull/138698#issuecomment-2860369432 Added: Modified: lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp Removed: ################################################################################ diff --git a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp index d2ca5b26c9ec9..2818d31eb2301 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp +++ b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp @@ -718,7 +718,7 @@ Status MinidumpFileBuilder::AddExceptions() { // We have 120 bytes to work with and it's unlikely description will // overflow, but we gotta check. memcpy(&exp_record.ExceptionInformation, description.c_str(), - std::max(description.size(), Exception::MaxParameterBytes)); + std::min(description.size(), Exception::MaxParameterBytes)); exp_record.UnusedAlignment = static_cast(0); ExceptionStream exp_stream; exp_stream.ThreadId = From lldb-commits at lists.llvm.org Thu May 8 07:28:25 2025 From: lldb-commits at lists.llvm.org (Felipe de Azevedo Piovezan via lldb-commits) Date: Thu, 08 May 2025 07:28:25 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fix dynamic type resolutions for core files (PR #138698) In-Reply-To: Message-ID: <681cbf89.170a0220.13fd87.531c@mx.google.com> felipepiovezan wrote: Pushed your suggestion > Lol, I think its this: > > https://github.com/llvm/llvm-project/blob/6babd63a4bbc094bee4ef8e75f95dccd32325c15/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp#L718-L721 > > I'm done for this week, but could someone try if changing that to std::min fixes this? https://github.com/llvm/llvm-project/pull/138698 From lldb-commits at lists.llvm.org Thu May 8 07:32:24 2025 From: lldb-commits at lists.llvm.org (Felipe de Azevedo Piovezan via lldb-commits) Date: Thu, 08 May 2025 07:32:24 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Expose QueueThreadPlanForStepSingleInstruction function to SBThreadPlan (PR #137904) In-Reply-To: Message-ID: <681cc078.170a0220.260633.3bdb@mx.google.com> felipepiovezan wrote: @eronnen this is breaking the incremental green dragon bots. Could you revert while you look at this please? https://ci.swift.org/view/all/job/llvm.org/job/as-lldb-cmake/25425/changes#41321416815d74a4a7fd15c78fcfa5af457625bb ``` [2025-05-08T12:15:32.021Z] ====================================================================== [2025-05-08T12:15:32.021Z] FAIL: test_step_single_instruction (TestStepScripted.StepScriptedTestCase) [2025-05-08T12:15:32.021Z] ---------------------------------------------------------------------- [2025-05-08T12:15:32.021Z] Traceback (most recent call last): [2025-05-08T12:15:32.021Z] File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/step_scripted/TestStepScripted.py", line 63, in test_step_single_instruction [2025-05-08T12:15:32.021Z] (target, process, thread, bkpt) = self.run_until_branch_instruction() [2025-05-08T12:15:32.021Z] File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/step_scripted/TestStepScripted.py", line 56, in run_until_branch_instruction [2025-05-08T12:15:32.021Z] self.assertEqual( [2025-05-08T12:15:32.021Z] AssertionError: 2 != 0 [2025-05-08T12:15:32.021Z] Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang ``` https://github.com/llvm/llvm-project/pull/137904 From lldb-commits at lists.llvm.org Thu May 8 07:50:45 2025 From: lldb-commits at lists.llvm.org (Vy Nguyen via lldb-commits) Date: Thu, 08 May 2025 07:50:45 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb]Make `list` command work with headers when possible. (PR #139002) In-Reply-To: Message-ID: <681cc4c5.170a0220.1de534.5310@mx.google.com> ================ @@ -1170,8 +1171,37 @@ class CommandObjectSourceList : public CommandObjectParsed { if (m_options.num_lines == 0) m_options.num_lines = 10; const uint32_t column = 0; + + // Headers aren't always in the DWARF but if they have + // executable code (eg., inlined-functions) then the callsite's file(s) + // will be found. + // So if a header was requested and we got a primary file, then look + // thru its support file(s) for the header. + lldb::SupportFileSP actual_file_sp = + sc.comp_unit->GetPrimarySupportFile(); + if (llvm::StringRef(m_options.file_name).ends_with(".h")) { ---------------- oontvoo wrote: How about we change to check to `if the file that was found has different extension from the requested`? (That should cover most cases?) https://github.com/llvm/llvm-project/pull/139002 From lldb-commits at lists.llvm.org Thu May 8 07:51:52 2025 From: lldb-commits at lists.llvm.org (Felipe de Azevedo Piovezan via lldb-commits) Date: Thu, 08 May 2025 07:51:52 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Expose QueueThreadPlanForStepSingleInstruction function to SBThreadPlan (PR #137904) In-Reply-To: Message-ID: <681cc508.170a0220.12c517.5b71@mx.google.com> ================ @@ -44,6 +44,48 @@ def step_out_with_scripted_plan(self, name): stop_desc = thread.GetStopDescription(1000) self.assertIn("Stepping out from", stop_desc, "Got right description") + def run_until_branch_instruction(self): + self.build() + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "Break on branch instruction", self.main_source_file + ) + + # Check that we landed in a call instruction + frame = thread.GetFrameAtIndex(0) + current_instruction = target.ReadInstructions(frame.GetPCAddress(), 1)[0] + self.assertEqual( + lldb.eInstructionControlFlowKindCall, + current_instruction.GetControlFlowKind(target), ---------------- felipepiovezan wrote: FWIW I don't think is implemented for arm targets https://github.com/llvm/llvm-project/pull/137904 From lldb-commits at lists.llvm.org Thu May 8 07:53:34 2025 From: lldb-commits at lists.llvm.org (Vy Nguyen via lldb-commits) Date: Thu, 08 May 2025 07:53:34 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb]Make `list` command work with headers when possible. (PR #139002) In-Reply-To: Message-ID: <681cc56e.630a0220.216826.65bc@mx.google.com> https://github.com/oontvoo edited https://github.com/llvm/llvm-project/pull/139002 From lldb-commits at lists.llvm.org Thu May 8 07:53:51 2025 From: lldb-commits at lists.llvm.org (Vy Nguyen via lldb-commits) Date: Thu, 08 May 2025 07:53:51 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb]Make `list` command work with headers when possible. (PR #139002) In-Reply-To: Message-ID: <681cc57f.050a0220.13ccdb.2445@mx.google.com> https://github.com/oontvoo edited https://github.com/llvm/llvm-project/pull/139002 From lldb-commits at lists.llvm.org Thu May 8 08:11:53 2025 From: lldb-commits at lists.llvm.org (Felipe de Azevedo Piovezan via lldb-commits) Date: Thu, 08 May 2025 08:11:53 -0700 (PDT) Subject: [Lldb-commits] [lldb] cb0b961 - [lldb] Disable test using GetControlFlowKind on arm Message-ID: <681cc9b9.170a0220.1b5a32.1aef@mx.google.com> Author: Felipe de Azevedo Piovezan Date: 2025-05-08T08:11:17-07:00 New Revision: cb0b9614f8ca7ffcd5f091b1c9990adfd6cb7e33 URL: https://github.com/llvm/llvm-project/commit/cb0b9614f8ca7ffcd5f091b1c9990adfd6cb7e33 DIFF: https://github.com/llvm/llvm-project/commit/cb0b9614f8ca7ffcd5f091b1c9990adfd6cb7e33.diff LOG: [lldb] Disable test using GetControlFlowKind on arm This is only implemented for x86. Originally introduced in: https://github.com/llvm/llvm-project/pull/137904 Added: Modified: lldb/test/API/functionalities/step_scripted/TestStepScripted.py Removed: ################################################################################ diff --git a/lldb/test/API/functionalities/step_scripted/TestStepScripted.py b/lldb/test/API/functionalities/step_scripted/TestStepScripted.py index 54bc154590ed0..cec2901aa0369 100644 --- a/lldb/test/API/functionalities/step_scripted/TestStepScripted.py +++ b/lldb/test/API/functionalities/step_scripted/TestStepScripted.py @@ -59,6 +59,7 @@ def run_until_branch_instruction(self): ) return (target, process, thread, bkpt) + @skipIf(archs=no_match(["x86_64"])) def test_step_single_instruction(self): (target, process, thread, bkpt) = self.run_until_branch_instruction() From lldb-commits at lists.llvm.org Thu May 8 08:12:44 2025 From: lldb-commits at lists.llvm.org (Felipe de Azevedo Piovezan via lldb-commits) Date: Thu, 08 May 2025 08:12:44 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Expose QueueThreadPlanForStepSingleInstruction function to SBThreadPlan (PR #137904) In-Reply-To: Message-ID: <681cc9ec.630a0220.16a024.8812@mx.google.com> felipepiovezan wrote: To fix the bots, I've disabled your new test in all non x86 archs, feel free to update if you want to do something else https://github.com/llvm/llvm-project/pull/137904 From lldb-commits at lists.llvm.org Thu May 8 08:43:44 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 08:43:44 -0700 (PDT) Subject: [Lldb-commits] [lldb] 155bf37 - [lldb-dap] Migrate 'continue' request to new RequestHandler. (#138987) Message-ID: <681cd130.050a0220.23be6a.5b18@mx.google.com> Author: John Harrison Date: 2025-05-08T08:43:41-07:00 New Revision: 155bf37ad995fa07baf99ad59294ec5fe2777635 URL: https://github.com/llvm/llvm-project/commit/155bf37ad995fa07baf99ad59294ec5fe2777635 DIFF: https://github.com/llvm/llvm-project/commit/155bf37ad995fa07baf99ad59294ec5fe2777635.diff LOG: [lldb-dap] Migrate 'continue' request to new RequestHandler. (#138987) This adds types for the 'continue' request and updates the existing handler to the new base class. Added: Modified: lldb/tools/lldb-dap/Handler/ContinueRequestHandler.cpp lldb/tools/lldb-dap/Handler/RequestHandler.h lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp lldb/tools/lldb-dap/Protocol/ProtocolRequests.h Removed: ################################################################################ diff --git a/lldb/tools/lldb-dap/Handler/ContinueRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ContinueRequestHandler.cpp index 214e3c59c594c..ca4c9141eca38 100644 --- a/lldb/tools/lldb-dap/Handler/ContinueRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/ContinueRequestHandler.cpp @@ -7,74 +7,41 @@ //===----------------------------------------------------------------------===// #include "DAP.h" -#include "JSONUtils.h" -#include "RequestHandler.h" +#include "Handler/RequestHandler.h" +#include "LLDBUtils.h" +#include "Protocol/ProtocolRequests.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBProcess.h" +#include "llvm/Support/Error.h" + +using namespace llvm; +using namespace lldb; +using namespace lldb_dap::protocol; namespace lldb_dap { -// "ContinueRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "Continue request; value of command field is 'continue'. -// The request starts the debuggee to run again.", -// "properties": { -// "command": { -// "type": "string", -// "enum": [ "continue" ] -// }, -// "arguments": { -// "$ref": "#/definitions/ContinueArguments" -// } -// }, -// "required": [ "command", "arguments" ] -// }] -// }, -// "ContinueArguments": { -// "type": "object", -// "description": "Arguments for 'continue' request.", -// "properties": { -// "threadId": { -// "type": "integer", -// "description": "Continue execution for the specified thread (if -// possible). If the backend cannot continue on a single -// thread but will continue on all threads, it should -// set the allThreadsContinued attribute in the response -// to true." -// } -// }, -// "required": [ "threadId" ] -// }, -// "ContinueResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to 'continue' request.", -// "properties": { -// "body": { -// "type": "object", -// "properties": { -// "allThreadsContinued": { -// "type": "boolean", -// "description": "If true, the continue request has ignored the -// specified thread and continued all threads -// instead. If this attribute is missing a value -// of 'true' is assumed for backward -// compatibility." -// } -// } -// } -// }, -// "required": [ "body" ] -// }] -// } -void ContinueRequestHandler::operator()( - const llvm::json::Object &request) const { - llvm::json::Object response; - FillResponse(request, response); - lldb::SBProcess process = dap.target.GetProcess(); - lldb::SBError error = process.Continue(); - llvm::json::Object body; - body.try_emplace("allThreadsContinued", true); - response.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(response))); +/// The request resumes execution of all threads. If the debug adapter supports +/// single thread execution (see capability +/// `supportsSingleThreadExecutionRequests`), setting the `singleThread` +/// argument to true resumes only the specified thread. If not all threads were +/// resumed, the `allThreadsContinued` attribute of the response should be set +/// to false. +Expected +ContinueRequestHandler::Run(const ContinueArguments &args) const { + SBProcess process = dap.target.GetProcess(); + SBError error; + + if (args.singleThread) + dap.GetLLDBThread(args.threadId).Resume(error); + else + error = process.Continue(); + + if (error.Fail()) + return ToError(error); + + ContinueResponseBody body; + body.allThreadsContinued = args.singleThread; + return body; } + } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index 9e9cfb13d77b8..25534b5675e45 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.h +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h @@ -224,11 +224,14 @@ class CompletionsRequestHandler : public LegacyRequestHandler { void operator()(const llvm::json::Object &request) const override; }; -class ContinueRequestHandler : public LegacyRequestHandler { +class ContinueRequestHandler + : public RequestHandler> { public: - using LegacyRequestHandler::LegacyRequestHandler; + using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "continue"; } - void operator()(const llvm::json::Object &request) const override; + llvm::Expected + Run(const protocol::ContinueArguments &args) const override; }; class ConfigurationDoneRequestHandler : public LegacyRequestHandler { diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp index d9ffc0c04e134..950e8d17e3489 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp @@ -261,6 +261,18 @@ bool fromJSON(const json::Value &Params, LaunchRequestArguments &LRA, parseEnv(Params, LRA.env, P) && parseTimeout(Params, LRA.timeout, P); } +bool fromJSON(const llvm::json::Value &Params, ContinueArguments &CA, + llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("threadId", CA.threadId) && + O.mapOptional("singleThread", CA.singleThread); +} + +llvm::json::Value toJSON(const ContinueResponseBody &CRB) { + json::Object Body{{"allThreadsContinued", CRB.allThreadsContinued}}; + return std::move(Body); +} + bool fromJSON(const llvm::json::Value &Params, SetVariableArguments &SVA, llvm::json::Path P) { json::ObjectMapper O(Params, P); diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h index 110e90837c0cd..18222d61f9a14 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h @@ -294,6 +294,28 @@ bool fromJSON(const llvm::json::Value &, LaunchRequestArguments &, /// field is required. using LaunchResponseBody = VoidResponse; +/// Arguments for `continue` request. +struct ContinueArguments { + /// Specifies the active thread. If the debug adapter supports single thread + /// execution (see `supportsSingleThreadExecutionRequests`) and the argument + /// `singleThread` is true, only the thread with this ID is resumed. + lldb::tid_t threadId = LLDB_INVALID_THREAD_ID; + + /// If this flag is true, execution is resumed only for the thread with given + /// `threadId`. + bool singleThread = false; +}; +bool fromJSON(const llvm::json::Value &, ContinueArguments &, llvm::json::Path); + +/// Response to `continue` request. +struct ContinueResponseBody { + // If omitted or set to `true`, this response signals to the client that all + // threads have been resumed. The value `false` indicates that not all threads + // were resumed. + bool allThreadsContinued = true; +}; +llvm::json::Value toJSON(const ContinueResponseBody &); + /// Arguments for `setVariable` request. struct SetVariableArguments { /// The reference of the variable container. The `variablesReference` must @@ -390,7 +412,7 @@ llvm::json::Value toJSON(const SourceResponseBody &); struct NextArguments { /// Specifies the thread for which to resume execution for one step (of the /// given granularity). - uint64_t threadId = LLDB_INVALID_THREAD_ID; + lldb::tid_t threadId = LLDB_INVALID_THREAD_ID; /// If this flag is true, all other suspended threads are not resumed. bool singleThread = false; @@ -409,7 +431,7 @@ using NextResponse = VoidResponse; struct StepInArguments { /// Specifies the thread for which to resume execution for one step-into (of /// the given granularity). - uint64_t threadId = LLDB_INVALID_THREAD_ID; + lldb::tid_t threadId = LLDB_INVALID_THREAD_ID; /// If this flag is true, all other suspended threads are not resumed. bool singleThread = false; @@ -431,7 +453,7 @@ using StepInResponse = VoidResponse; struct StepOutArguments { /// Specifies the thread for which to resume execution for one step-out (of /// the given granularity). - uint64_t threadId = LLDB_INVALID_THREAD_ID; + lldb::tid_t threadId = LLDB_INVALID_THREAD_ID; /// If this flag is true, all other suspended threads are not resumed. std::optional singleThread; From lldb-commits at lists.llvm.org Thu May 8 08:43:47 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Thu, 08 May 2025 08:43:47 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Migrate 'continue' request to new RequestHandler. (PR #138987) In-Reply-To: Message-ID: <681cd133.170a0220.215e02.3646@mx.google.com> https://github.com/ashgti closed https://github.com/llvm/llvm-project/pull/138987 From lldb-commits at lists.llvm.org Thu May 8 09:09:49 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 09:09:49 -0700 (PDT) Subject: [Lldb-commits] [lldb] 45cd708 - [lldb] Change the statusline format to print "no target" (#139021) Message-ID: <681cd74d.170a0220.3580a1.35ca@mx.google.com> Author: Jonas Devlieghere Date: 2025-05-08T09:09:46-07:00 New Revision: 45cd708184e114bb771330d51ec552f7f674ffa0 URL: https://github.com/llvm/llvm-project/commit/45cd708184e114bb771330d51ec552f7f674ffa0 DIFF: https://github.com/llvm/llvm-project/commit/45cd708184e114bb771330d51ec552f7f674ffa0.diff LOG: [lldb] Change the statusline format to print "no target" (#139021) Change the default statusline format to print "no target" when lldb is launched without a target. Currently, the statusline is empty, which looks rather odd. Added: Modified: lldb/source/Core/CoreProperties.td lldb/test/API/functionalities/statusline/TestStatusline.py Removed: ################################################################################ diff --git a/lldb/source/Core/CoreProperties.td b/lldb/source/Core/CoreProperties.td index 2498841b47d9f..78988ce5b732f 100644 --- a/lldb/source/Core/CoreProperties.td +++ b/lldb/source/Core/CoreProperties.td @@ -186,7 +186,7 @@ let Definition = "debugger" in { : Property<"statusline-format", "FormatEntity">, Global, DefaultStringValue< - "${ansi.negative}{${target.file.basename}}{ " + "${ansi.negative}{${target.file.basename}|no target}{ " "${separator}${line.file.basename}:${line.number}:${line.column}}{ " "${separator}${thread.stop-reason}}{ " "${separator}{${progress.count} }${progress.message}}">, diff --git a/lldb/test/API/functionalities/statusline/TestStatusline.py b/lldb/test/API/functionalities/statusline/TestStatusline.py index da6b4e7c8f320..53ac7432f4ba1 100644 --- a/lldb/test/API/functionalities/statusline/TestStatusline.py +++ b/lldb/test/API/functionalities/statusline/TestStatusline.py @@ -6,7 +6,17 @@ from lldbsuite.test.lldbpexpect import PExpectTest +# PExpect uses many timeouts internally and doesn't play well +# under ASAN on a loaded machine.. + at skipIfAsan class TestStatusline(PExpectTest): + # Change this value to something smaller to make debugging this test less + # tedious. + TIMEOUT = 60 + + TERMINAL_HEIGHT = 10 + TERMINAL_WIDTH = 60 + def do_setup(self): # Create a target and run to a breakpoint. exe = self.getBuildArtifact("a.out") @@ -15,36 +25,34 @@ def do_setup(self): ) self.expect('breakpoint set -p "Break here"', substrs=["Breakpoint 1"]) self.expect("run", substrs=["stop reason"]) + self.resize() + + def resize(self): + # Change the terminal dimensions. When we launch the tests, we reset + # all the settings, leaving the terminal dimensions unset. + self.child.setwinsize(self.TERMINAL_HEIGHT, self.TERMINAL_WIDTH) - # PExpect uses many timeouts internally and doesn't play well - # under ASAN on a loaded machine.. - @skipIfAsan def test(self): """Basic test for the statusline.""" self.build() - self.launch() + self.launch(timeout=self.TIMEOUT) self.do_setup() - # Change the terminal dimensions. - terminal_height = 10 - terminal_width = 60 - self.child.setwinsize(terminal_height, terminal_width) - # Enable the statusline and check for the control character and that we # can see the target, the location and the stop reason. self.expect('set set separator "| "') self.expect( "set set show-statusline true", [ - "\x1b[0;{}r".format(terminal_height - 1), + "\x1b[0;{}r".format(self.TERMINAL_HEIGHT - 1), "a.out | main.c:2:11 | breakpoint 1.1 ", ], ) # Change the terminal dimensions and make sure it's reflected immediately. - self.child.setwinsize(terminal_height, 25) + self.child.setwinsize(self.TERMINAL_HEIGHT, 25) self.child.expect(re.escape("a.out | main.c:2:11 | bre")) - self.child.setwinsize(terminal_height, terminal_width) + self.child.setwinsize(self.TERMINAL_HEIGHT, self.TERMINAL_WIDTH) # Change the separator. self.expect('set set separator "S "', ["a.out S main.c:2:11"]) @@ -58,23 +66,15 @@ def test(self): # Hide the statusline and check or the control character. self.expect( - "set set show-statusline false", ["\x1b[0;{}r".format(terminal_height)] + "set set show-statusline false", ["\x1b[0;{}r".format(self.TERMINAL_HEIGHT)] ) - # PExpect uses many timeouts internally and doesn't play well - # under ASAN on a loaded machine.. - @skipIfAsan def test_no_color(self): """Basic test for the statusline with colors disabled.""" self.build() - self.launch(use_colors=False) + self.launch(use_colors=False, timeout=self.TIMEOUT) self.do_setup() - # Change the terminal dimensions. - terminal_height = 10 - terminal_width = 60 - self.child.setwinsize(terminal_height, terminal_width) - # Enable the statusline and check for the "reverse video" control character. self.expect( "set set show-statusline true", @@ -87,15 +87,20 @@ def test_deadlock(self): """Regression test for lock inversion between the statusline mutex and the output mutex.""" self.build() - self.launch(extra_args=["-o", "settings set use-color false"]) + self.launch( + extra_args=["-o", "settings set use-color false"], timeout=self.TIMEOUT + ) self.child.expect("(lldb)") - - # Change the terminal dimensions. - terminal_height = 10 - terminal_width = 60 - self.child.setwinsize(terminal_height, terminal_width) + self.resize() exe = self.getBuildArtifact("a.out") self.expect("file {}".format(exe), ["Current executable"]) self.expect("help", ["Debugger commands"]) + + def test_no_target(self): + """Test that we print "no target" when launched without a target.""" + self.launch(timeout=self.TIMEOUT) + self.resize() + + self.expect("set set show-statusline true", ["no target"]) From lldb-commits at lists.llvm.org Thu May 8 09:09:53 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Thu, 08 May 2025 09:09:53 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Change the statusline format to print "no target" (PR #139021) In-Reply-To: Message-ID: <681cd751.170a0220.24dd0f.652a@mx.google.com> https://github.com/JDevlieghere closed https://github.com/llvm/llvm-project/pull/139021 From lldb-commits at lists.llvm.org Thu May 8 09:14:27 2025 From: lldb-commits at lists.llvm.org (Dmitry Vasilyev via lldb-commits) Date: Thu, 08 May 2025 09:14:27 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Expose QueueThreadPlanForStepSingleInstruction function to SBThreadPlan (PR #137904) In-Reply-To: Message-ID: <681cd863.050a0220.e0544.79a0@mx.google.com> slydiman wrote: The following buildbots are broken by this patch too https://lab.llvm.org/buildbot/#/builders/195/builds/8715 https://lab.llvm.org/buildbot/#/builders/197/builds/5054 Please fix ASAP. https://github.com/llvm/llvm-project/pull/137904 From lldb-commits at lists.llvm.org Thu May 8 09:20:32 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Thu, 08 May 2025 09:20:32 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB][SBSaveCoreOptions] Add new API to expose the expected core size in bytes (PR #138169) In-Reply-To: Message-ID: <681cd9d0.170a0220.2299f9.522f@mx.google.com> Jlalond wrote: @clayborg, @dmpots bump if you have time https://github.com/llvm/llvm-project/pull/138169 From lldb-commits at lists.llvm.org Thu May 8 09:31:56 2025 From: lldb-commits at lists.llvm.org (Ely Ronnen via lldb-commits) Date: Thu, 08 May 2025 09:31:56 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Expose QueueThreadPlanForStepSingleInstruction function to SBThreadPlan (PR #137904) In-Reply-To: Message-ID: <681cdc7c.050a0220.240307.0976@mx.google.com> eronnen wrote: @felipepiovezan thanks for the temp fix, I'll try to understand the problem soon https://github.com/llvm/llvm-project/pull/137904 From lldb-commits at lists.llvm.org Thu May 8 09:32:59 2025 From: lldb-commits at lists.llvm.org (Ely Ronnen via lldb-commits) Date: Thu, 08 May 2025 09:32:59 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Expose QueueThreadPlanForStepSingleInstruction function to SBThreadPlan (PR #137904) In-Reply-To: Message-ID: <681cdcbb.170a0220.9a25a.534d@mx.google.com> eronnen wrote: @slydiman Seems this worker is `aarch64` so this test should be disabled after @felipepiovezan 's fix. https://github.com/llvm/llvm-project/pull/137904 From lldb-commits at lists.llvm.org Thu May 8 09:46:16 2025 From: lldb-commits at lists.llvm.org (Ilia Kuklin via lldb-commits) Date: Thu, 08 May 2025 09:46:16 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <681cdfd8.630a0220.b012b.1b29@mx.google.com> https://github.com/kuilpd updated https://github.com/llvm/llvm-project/pull/138551 >From cfe7359bd16c1e87932e2ebb8bcdfc88130e9729 Mon Sep 17 00:00:00 2001 From: Ilia Kuklin Date: Wed, 30 Apr 2025 22:03:50 +0500 Subject: [PATCH 1/4] [LLDB] Add array subscription and integer parsing to DIL --- lldb/docs/dil-expr-lang.ebnf | 12 +- lldb/include/lldb/ValueObject/DILAST.h | 46 +++++ lldb/include/lldb/ValueObject/DILEval.h | 6 + lldb/include/lldb/ValueObject/DILLexer.h | 3 + lldb/include/lldb/ValueObject/DILParser.h | 3 + lldb/source/ValueObject/DILAST.cpp | 10 ++ lldb/source/ValueObject/DILEval.cpp | 159 ++++++++++++++++++ lldb/source/ValueObject/DILLexer.cpp | 43 ++++- lldb/source/ValueObject/DILParser.cpp | 79 ++++++++- .../var-dil/basics/ArraySubscript/Makefile | 3 + .../TestFrameVarDILArraySubscript.py | 88 ++++++++++ .../var-dil/basics/ArraySubscript/main.cpp | 31 ++++ lldb/unittests/ValueObject/DILLexerTests.cpp | 34 +++- 13 files changed, 506 insertions(+), 11 deletions(-) create mode 100644 lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/Makefile create mode 100644 lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py create mode 100644 lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf index c8bf4231b3e4a..0cbb5403785db 100644 --- a/lldb/docs/dil-expr-lang.ebnf +++ b/lldb/docs/dil-expr-lang.ebnf @@ -6,16 +6,20 @@ expression = unary_expression ; unary_expression = unary_operator expression - | primary_expression ; + | postfix_expression ; unary_operator = "*" | "&" ; -primary_expression = id_expression +postfix_expression = primary_expression + | postfix_expression "[" expression "]"; + +primary_expression = numeric_literal + | id_expression | "(" expression ")"; id_expression = unqualified_id | qualified_id - | register ; + | register ; unqualified_id = identifier ; @@ -24,6 +28,8 @@ qualified_id = ["::"] [nested_name_specifier] unqualified_id identifier = ? C99 Identifier ? ; +numeric_literal = ? C99 Integer constant ? ; + register = "$" ? Register name ? ; nested_name_specifier = type_name "::" diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h index fe3827ef0516a..6908deed7aee3 100644 --- a/lldb/include/lldb/ValueObject/DILAST.h +++ b/lldb/include/lldb/ValueObject/DILAST.h @@ -18,8 +18,10 @@ namespace lldb_private::dil { /// The various types DIL AST nodes (used by the DIL parser). enum class NodeKind { + eArraySubscriptNode, eErrorNode, eIdentifierNode, + eScalarLiteralNode, eUnaryOpNode, }; @@ -71,6 +73,26 @@ class ErrorNode : public ASTNode { } }; +class ScalarLiteralNode : public ASTNode { +public: + ScalarLiteralNode(uint32_t location, lldb::BasicType type, Scalar value) + : ASTNode(location, NodeKind::eScalarLiteralNode), m_type(type), + m_value(value) {} + + llvm::Expected Accept(Visitor *v) const override; + + lldb::BasicType GetType() const { return m_type; } + Scalar GetValue() const & { return m_value; } + + static bool classof(const ASTNode *node) { + return node->GetKind() == NodeKind::eScalarLiteralNode; + } + +private: + lldb::BasicType m_type; + Scalar m_value; +}; + class IdentifierNode : public ASTNode { public: IdentifierNode(uint32_t location, std::string name) @@ -108,6 +130,26 @@ class UnaryOpNode : public ASTNode { ASTNodeUP m_operand; }; +class ArraySubscriptNode : public ASTNode { +public: + ArraySubscriptNode(uint32_t location, ASTNodeUP lhs, ASTNodeUP rhs) + : ASTNode(location, NodeKind::eArraySubscriptNode), m_lhs(std::move(lhs)), + m_rhs(std::move(rhs)) {} + + llvm::Expected Accept(Visitor *v) const override; + + ASTNode *lhs() const { return m_lhs.get(); } + ASTNode *rhs() const { return m_rhs.get(); } + + static bool classof(const ASTNode *node) { + return node->GetKind() == NodeKind::eArraySubscriptNode; + } + +private: + ASTNodeUP m_lhs; + ASTNodeUP m_rhs; +}; + /// 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 @@ -116,9 +158,13 @@ class Visitor { public: virtual ~Visitor() = default; virtual llvm::Expected + Visit(const ScalarLiteralNode *node) = 0; + virtual llvm::Expected Visit(const IdentifierNode *node) = 0; virtual llvm::Expected Visit(const UnaryOpNode *node) = 0; + virtual llvm::Expected + Visit(const ArraySubscriptNode *node) = 0; }; } // namespace lldb_private::dil diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h index b1dd3fdb49739..e3df80862b082 100644 --- a/lldb/include/lldb/ValueObject/DILEval.h +++ b/lldb/include/lldb/ValueObject/DILEval.h @@ -47,9 +47,15 @@ class Interpreter : Visitor { llvm::Expected Evaluate(const ASTNode *node); private: + llvm::Expected + Visit(const ScalarLiteralNode *node) override; llvm::Expected Visit(const IdentifierNode *node) override; llvm::Expected Visit(const UnaryOpNode *node) override; + llvm::Expected + Visit(const ArraySubscriptNode *node) override; + + lldb::ValueObjectSP PointerAdd(lldb::ValueObjectSP lhs, int64_t offset); // 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 3508b8b7a85c6..0176db73835e9 100644 --- a/lldb/include/lldb/ValueObject/DILLexer.h +++ b/lldb/include/lldb/ValueObject/DILLexer.h @@ -29,7 +29,10 @@ class Token { eof, identifier, l_paren, + l_square, + numeric_constant, r_paren, + r_square, star, }; diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h index f5c00b1040ef4..af237ece0228d 100644 --- a/lldb/include/lldb/ValueObject/DILParser.h +++ b/lldb/include/lldb/ValueObject/DILParser.h @@ -84,12 +84,15 @@ class DILParser { ASTNodeUP ParseExpression(); ASTNodeUP ParseUnaryExpression(); + ASTNodeUP ParsePostfixExpression(); ASTNodeUP ParsePrimaryExpression(); std::string ParseNestedNameSpecifier(); std::string ParseIdExpression(); std::string ParseUnqualifiedId(); + ASTNodeUP ParseNumericLiteral(); + ASTNodeUP ParseNumericConstant(); void BailOut(const std::string &error, uint32_t loc, uint16_t err_len); diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp index ea847587501ee..ceb4a4aa99c4f 100644 --- a/lldb/source/ValueObject/DILAST.cpp +++ b/lldb/source/ValueObject/DILAST.cpp @@ -15,6 +15,11 @@ llvm::Expected ErrorNode::Accept(Visitor *v) const { llvm_unreachable("Attempting to Visit a DIL ErrorNode."); } +llvm::Expected +ScalarLiteralNode::Accept(Visitor *v) const { + return v->Visit(this); +} + llvm::Expected IdentifierNode::Accept(Visitor *v) const { return v->Visit(this); } @@ -23,4 +28,9 @@ llvm::Expected UnaryOpNode::Accept(Visitor *v) const { return v->Visit(this); } +llvm::Expected +ArraySubscriptNode::Accept(Visitor *v) const { + return v->Visit(this); +} + } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index 15a66d4866305..527017da7c019 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -18,6 +18,22 @@ namespace lldb_private::dil { +static lldb::ValueObjectSP +ArrayToPointerConversion(lldb::ValueObjectSP valobj, + std::shared_ptr ctx) { + assert(valobj->IsArrayType() && + "an argument to array-to-pointer conversion must be an array"); + + uint64_t addr = valobj->GetLoadAddress(); + llvm::StringRef name = "result"; + ExecutionContext exe_ctx; + ctx->CalculateExecutionContext(exe_ctx); + return ValueObject::CreateValueObjectFromAddress( + name, addr, exe_ctx, + valobj->GetCompilerType().GetArrayElementType(ctx.get()).GetPointerType(), + /* do_deref */ false); +} + static lldb::ValueObjectSP LookupStaticIdentifier( VariableList &variable_list, std::shared_ptr exe_scope, llvm::StringRef name_ref, llvm::StringRef unqualified_name) { @@ -214,6 +230,45 @@ llvm::Expected Interpreter::Evaluate(const ASTNode *node) { return value_or_error; } +static CompilerType GetBasicType(std::shared_ptr ctx, + lldb::BasicType basic_type) { + static std::unordered_map basic_types; + auto type = basic_types.find(basic_type); + if (type != basic_types.end()) { + std::string type_name((type->second).GetTypeName().AsCString()); + // Only return the found type if it's valid. + if (type_name != "") + return type->second; + } + + lldb::TargetSP target_sp = ctx->CalculateTarget(); + if (target_sp) { + for (auto type_system_sp : target_sp->GetScratchTypeSystems()) + if (auto compiler_type = + type_system_sp->GetBasicTypeFromAST(basic_type)) { + basic_types.insert({basic_type, compiler_type}); + return compiler_type; + } + } + CompilerType empty_type; + return empty_type; +} + +llvm::Expected +Interpreter::Visit(const ScalarLiteralNode *node) { + CompilerType result_type = GetBasicType(m_exe_ctx_scope, node->GetType()); + Scalar value = node->GetValue(); + + if (result_type.IsInteger() || result_type.IsNullPtrType() || + result_type.IsPointerType()) { + llvm::APInt val = value.GetAPSInt(); + return ValueObject::CreateValueObjectFromAPInt(m_target, val, result_type, + "result"); + } + + return lldb::ValueObjectSP(); +} + llvm::Expected Interpreter::Visit(const IdentifierNode *node) { lldb::DynamicValueType use_dynamic = m_default_dynamic; @@ -272,4 +327,108 @@ Interpreter::Visit(const UnaryOpNode *node) { m_expr, "invalid ast: unexpected binary operator", node->GetLocation()); } +lldb::ValueObjectSP Interpreter::PointerAdd(lldb::ValueObjectSP lhs, + int64_t offset) { + uint64_t byte_size = 0; + if (auto temp = lhs->GetCompilerType().GetPointeeType().GetByteSize( + lhs->GetTargetSP().get())) + byte_size = *temp; + uintptr_t addr = lhs->GetValueAsUnsigned(0) + offset * byte_size; + + llvm::StringRef name = "result"; + ExecutionContext exe_ctx(m_target.get(), false); + return ValueObject::CreateValueObjectFromAddress(name, addr, exe_ctx, + lhs->GetCompilerType(), + /* do_deref */ false); +} + +llvm::Expected +Interpreter::Visit(const ArraySubscriptNode *node) { + auto lhs_or_err = Evaluate(node->lhs()); + if (!lhs_or_err) { + return lhs_or_err; + } + lldb::ValueObjectSP base = *lhs_or_err; + auto rhs_or_err = Evaluate(node->rhs()); + if (!rhs_or_err) { + return rhs_or_err; + } + lldb::ValueObjectSP index = *rhs_or_err; + + Status error; + if (base->GetCompilerType().IsReferenceType()) { + base = base->Dereference(error); + if (error.Fail()) + return error.ToError(); + } + if (index->GetCompilerType().IsReferenceType()) { + index = index->Dereference(error); + if (error.Fail()) + return error.ToError(); + } + + auto index_type = index->GetCompilerType(); + if (!index_type.IsIntegerOrUnscopedEnumerationType()) + return llvm::make_error( + m_expr, "array subscript is not an integer", node->GetLocation()); + + // Check to see if 'base' has a synthetic value; if so, try using that. + if (base->HasSyntheticValue()) { + lldb::ValueObjectSP synthetic = base->GetSyntheticValue(); + if (synthetic && synthetic != base) { + uint32_t num_children = synthetic->GetNumChildrenIgnoringErrors(); + // Verify that the 'index' is not out-of-range for the declared type. + if (index->GetValueAsSigned(0) >= num_children) { + auto message = + llvm::formatv("array index {0} is not valid for \"({1}) {2}\"", + index->GetValueAsSigned(0), + base->GetTypeName().AsCString(""), + base->GetName().AsCString()); + return llvm::make_error(m_expr, message, + node->GetLocation()); + } + + uint64_t child_idx = index->GetValueAsUnsigned(0); + if (static_cast(child_idx) < + synthetic->GetNumChildrenIgnoringErrors()) { + lldb::ValueObjectSP child_valobj_sp = + synthetic->GetChildAtIndex(child_idx); + if (child_valobj_sp) { + return child_valobj_sp; + } + } + } + } + + auto base_type = base->GetCompilerType(); + if (!base_type.IsPointerType() && !base_type.IsArrayType()) + return llvm::make_error( + m_expr, "subscripted value is not an array or pointer", + node->GetLocation()); + if (base_type.IsPointerToVoid()) + return llvm::make_error( + m_expr, "subscript of pointer to incomplete type 'void'", + node->GetLocation()); + + if (base_type.IsArrayType()) + base = ArrayToPointerConversion(base, m_exe_ctx_scope); + + CompilerType item_type = base->GetCompilerType().GetPointeeType(); + lldb::addr_t base_addr = base->GetValueAsUnsigned(0); + + llvm::StringRef name = "result"; + ExecutionContext exe_ctx(m_target.get(), false); + // Create a pointer and add the index, i.e. "base + index". + lldb::ValueObjectSP value = + PointerAdd(ValueObject::CreateValueObjectFromAddress( + name, base_addr, exe_ctx, item_type.GetPointerType(), + /*do_deref=*/false), + index->GetValueAsSigned(0)); + + lldb::ValueObjectSP val2 = value->Dereference(error); + if (error.Fail()) + return error.ToError(); + return val2; +} + } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp index b9c2e7971e3b4..3222032feef19 100644 --- a/lldb/source/ValueObject/DILLexer.cpp +++ b/lldb/source/ValueObject/DILLexer.cpp @@ -13,6 +13,7 @@ #include "lldb/ValueObject/DILLexer.h" #include "lldb/Utility/Status.h" +#include "lldb/ValueObject/DILParser.h" #include "llvm/ADT/StringSwitch.h" namespace lldb_private::dil { @@ -29,8 +30,14 @@ llvm::StringRef Token::GetTokenName(Kind kind) { return "identifier"; case Kind::l_paren: return "l_paren"; + case Kind::l_square: + return "l_square"; + case Kind::numeric_constant: + return "numeric_constant"; case Kind::r_paren: return "r_paren"; + case Kind::r_square: + return "r_square"; case Token::star: return "star"; } @@ -57,6 +64,29 @@ static std::optional IsWord(llvm::StringRef expr, return candidate; } +static void ConsumeNumberBody(char &prev_ch, llvm::StringRef::iterator &cur_pos, + llvm::StringRef expr) { + while (cur_pos != expr.end() && + (IsDigit(*cur_pos) || IsLetter(*cur_pos) || *cur_pos == '_')) { + prev_ch = *cur_pos; + cur_pos++; + } +} + +static std::optional IsNumber(llvm::StringRef expr, + llvm::StringRef &remainder) { + llvm::StringRef::iterator cur_pos = remainder.begin(); + llvm::StringRef::iterator start = cur_pos; + char prev_ch = 0; + if (IsDigit(*start)) { + ConsumeNumberBody(prev_ch, cur_pos, expr); + llvm::StringRef number = expr.substr(start - expr.begin(), cur_pos - start); + if (remainder.consume_front(number)) + return number; + } + return std::nullopt; +} + llvm::Expected DILLexer::Create(llvm::StringRef expr) { std::vector tokens; llvm::StringRef remainder = expr; @@ -81,13 +111,19 @@ llvm::Expected DILLexer::Lex(llvm::StringRef expr, return Token(Token::eof, "", (uint32_t)expr.size()); uint32_t position = cur_pos - expr.begin(); + std::optional maybe_number = IsNumber(expr, remainder); + if (maybe_number) { + std::string number = (*maybe_number).str(); + return Token(Token::numeric_constant, number, position); + } std::optional maybe_word = IsWord(expr, remainder); if (maybe_word) return Token(Token::identifier, maybe_word->str(), position); constexpr std::pair operators[] = { - {Token::amp, "&"}, {Token::coloncolon, "::"}, {Token::l_paren, "("}, - {Token::r_paren, ")"}, {Token::star, "*"}, + {Token::amp, "&"}, {Token::coloncolon, "::"}, {Token::l_paren, "("}, + {Token::l_square, "["}, {Token::r_paren, ")"}, {Token::r_square, "]"}, + {Token::star, "*"}, }; for (auto [kind, str] : operators) { if (remainder.consume_front(str)) @@ -95,7 +131,8 @@ llvm::Expected DILLexer::Lex(llvm::StringRef expr, } // Unrecognized character(s) in string; unable to lex it. - return llvm::createStringError("Unable to lex input string"); + return llvm::make_error(expr, "unrecognized token", + position); } } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp index 2c78eae8cf6bf..24a6f0c909a0a 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -111,7 +111,36 @@ ASTNodeUP DILParser::ParseUnaryExpression() { llvm_unreachable("invalid token kind"); } } - return ParsePrimaryExpression(); + return ParsePostfixExpression(); +} + +// Parse a postfix_expression. +// +// postfix_expression: +// primary_expression +// postfix_expression "[" expression "]" +// +ASTNodeUP DILParser::ParsePostfixExpression() { + ASTNodeUP lhs = ParsePrimaryExpression(); + while (CurToken().Is(Token::l_square)) { + uint32_t loc = CurToken().GetLocation(); + Token token = CurToken(); + switch (token.GetKind()) { + case Token::l_square: { + m_dil_lexer.Advance(); + auto rhs = ParseExpression(); + Expect(Token::r_square); + m_dil_lexer.Advance(); + lhs = std::make_unique(loc, std::move(lhs), + std::move(rhs)); + break; + } + default: + llvm_unreachable("invalid token"); + } + } + + return lhs; } // Parse a primary_expression. @@ -121,6 +150,8 @@ ASTNodeUP DILParser::ParseUnaryExpression() { // "(" expression ")" // ASTNodeUP DILParser::ParsePrimaryExpression() { + if (CurToken().Is(Token::numeric_constant)) + return ParseNumericLiteral(); if (CurToken().IsOneOf({Token::coloncolon, Token::identifier})) { // Save the source location for the diagnostics message. uint32_t loc = CurToken().GetLocation(); @@ -280,6 +311,52 @@ void DILParser::BailOut(const std::string &error, uint32_t loc, m_dil_lexer.ResetTokenIdx(m_dil_lexer.NumLexedTokens() - 1); } +// Parse a numeric_literal. +// +// numeric_literal: +// ? Token::numeric_constant ? +// +ASTNodeUP DILParser::ParseNumericLiteral() { + Expect(Token::numeric_constant); + ASTNodeUP numeric_constant = ParseNumericConstant(); + if (numeric_constant->GetKind() == NodeKind::eErrorNode) { + BailOut(llvm::formatv("Failed to parse token as numeric-constant: {0}", + CurToken()), + CurToken().GetLocation(), CurToken().GetSpelling().length()); + return std::make_unique(); + } + m_dil_lexer.Advance(); + return numeric_constant; +} + +static constexpr std::pair type_suffixes[] = { + {"ull", lldb::eBasicTypeUnsignedLongLong}, + {"ul", lldb::eBasicTypeUnsignedLong}, + {"u", lldb::eBasicTypeUnsignedInt}, + {"ll", lldb::eBasicTypeLongLong}, + {"l", lldb::eBasicTypeLong}, +}; + +ASTNodeUP DILParser::ParseNumericConstant() { + Token token = CurToken(); + auto spelling = token.GetSpelling(); + llvm::StringRef spelling_ref = spelling; + lldb::BasicType type = lldb::eBasicTypeInt; + for (auto [suffix, t] : type_suffixes) { + if (spelling_ref.consume_back_insensitive(suffix)) { + type = t; + break; + } + } + llvm::APInt raw_value; + if (!spelling_ref.getAsInteger(0, raw_value)) { + Scalar scalar_value(raw_value); + return std::make_unique(token.GetLocation(), type, + scalar_value); + } + return std::make_unique(); +} + void DILParser::Expect(Token::Kind kind) { if (CurToken().IsNot(kind)) { BailOut(llvm::formatv("expected {0}, got: {1}", kind, CurToken()), diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/Makefile b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py new file mode 100644 index 0000000000000..e142889124613 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py @@ -0,0 +1,88 @@ +""" +Test DIL array subscript. +""" + +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +from lldbsuite.test import lldbutil + + +class TestFrameVarDILGlobalVariableLookup(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def expect_var_path(self, expr, compare_to_framevar=False, value=None, type=None): + value_dil = super().expect_var_path(expr, value=value, type=type) + if compare_to_framevar: + self.runCmd("settings set target.experimental.use-DIL false") + value_frv = super().expect_var_path(expr, value=value, type=type) + self.runCmd("settings set target.experimental.use-DIL true") + self.assertEqual(value_dil.GetValue(), value_frv.GetValue()) + + def test_dereference(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp") + ) + + self.runCmd("settings set target.experimental.use-DIL true") + + # Test int[] and int* + self.expect_var_path("int_arr[0]", True, value="1") + self.expect_var_path("int_ptr[1]", True, value="2") + self.expect_var_path("int_arr[enum_one]", value="2") + + # Test when base and index are references. + self.expect_var_path("int_arr[0]", True, value="1") + self.expect_var_path("int_arr[idx_1_ref]", value="2") + self.expect_var_path("int_arr[enum_ref]", value="2") + self.expect_var_path("int_arr_ref[0]", value="1") + self.expect_var_path("int_arr_ref[idx_1_ref]", value="2") + self.expect_var_path("int_arr_ref[enum_ref]", value="2") + + # Test when base and index are typedefs. + self.expect_var_path("td_int_arr[0]", True, value="1") + self.expect_var_path("td_int_arr[td_int_idx_1]", value="2") + self.expect_var_path("td_int_arr[td_td_int_idx_2]", value="3") + self.expect_var_path("td_int_ptr[0]", True, value="1") + self.expect_var_path("td_int_ptr[td_int_idx_1]", value="2") + self.expect_var_path("td_int_ptr[td_td_int_idx_2]", value="3") + + # Both typedefs and refs + self.expect_var_path("td_int_arr_ref[td_int_idx_1_ref]", value="2") + + # Test for index out of bounds. + self.expect_var_path("int_arr[42]", True, type="int") + self.expect_var_path("int_arr[100]", True, type="int") + + # Test address-of of the subscripted value. + self.expect_var_path("*(&int_arr[1])", value="2") + + # Test synthetic value subscription + self.expect_var_path("vector[1]", value="2") + + # Test for negative index. + self.expect( + "frame var 'int_arr[-1]'", + error=True, + substrs=["unrecognized token"], + ) + + # Test for floating point index + self.expect( + "frame var 'int_arr[1.0]'", + error=True, + substrs=["unrecognized token"], + ) + + # Base should be a "pointer to T" and index should be of an integral type. + self.expect( + "frame var 'int_arr[int_ptr]'", + error=True, + substrs=["array subscript is not an integer"], + ) + self.expect( + "frame var '1[2]'", + error=True, + substrs=["subscripted value is not an array or pointer"], + ) diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp new file mode 100644 index 0000000000000..b34e4670b9db6 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp @@ -0,0 +1,31 @@ +#include + +int main(int argc, char **argv) { + int int_arr[] = {1, 2, 3}; + int *int_ptr = int_arr; + int(&int_arr_ref)[3] = int_arr; + + int idx_1 = 1; + const int &idx_1_ref = idx_1; + + typedef int td_int_t; + typedef td_int_t td_td_int_t; + typedef int *td_int_ptr_t; + typedef int &td_int_ref_t; + + td_int_t td_int_idx_1 = 1; + td_td_int_t td_td_int_idx_2 = 2; + + td_int_t td_int_arr[3] = {1, 2, 3}; + td_int_ptr_t td_int_ptr = td_int_arr; + + td_int_ref_t td_int_idx_1_ref = td_int_idx_1; + td_int_t(&td_int_arr_ref)[3] = td_int_arr; + + enum Enum { kZero, kOne } enum_one = kOne; + Enum &enum_ref = enum_one; + + std::vector vector = {1, 2, 3}; + + return 0; // Set a breakpoint here +} diff --git a/lldb/unittests/ValueObject/DILLexerTests.cpp b/lldb/unittests/ValueObject/DILLexerTests.cpp index 9afa957901ae7..203763b91afc4 100644 --- a/lldb/unittests/ValueObject/DILLexerTests.cpp +++ b/lldb/unittests/ValueObject/DILLexerTests.cpp @@ -121,11 +121,11 @@ TEST(DILLexerTests, IdentifiersTest) { "a_b", "this", "self", "a", "MyName", "namespace"}; // The lexer can lex these strings, but they should not be identifiers. - std::vector invalid_identifiers = {"", "::", "(", ")"}; + std::vector invalid_identifiers = {"", "::", "(", ")", "0abc"}; // The lexer is expected to fail attempting to lex these strings (it cannot // create valid tokens out of them). - std::vector invalid_tok_strings = {"234", "2a", "2", "1MyName"}; + std::vector invalid_tok_strings = {"#include", "a at a"}; // Verify that all of the valid identifiers come out as identifier tokens. for (auto &str : valid_identifiers) { @@ -150,7 +150,33 @@ TEST(DILLexerTests, IdentifiersTest) { DILLexer lexer(*maybe_lexer); Token token = lexer.GetCurrentToken(); EXPECT_TRUE(token.IsNot(Token::identifier)); - EXPECT_TRUE(token.IsOneOf( - {Token::eof, Token::coloncolon, Token::l_paren, Token::r_paren})); + EXPECT_TRUE(token.IsOneOf({Token::eof, Token::coloncolon, Token::l_paren, + Token::r_paren, Token::numeric_constant})); + } +} + +TEST(DILLexerTests, NumbersTest) { + // These strings should lex into number tokens. + std::vector valid_numbers = {"123", "0x123", "0123", "0b101", + "1_000"}; + + // The lexer can lex these strings, but they should not be numbers. + std::vector invalid_numbers = {"", "x123", "b123"}; + + for (auto &str : valid_numbers) { + SCOPED_TRACE(str); + EXPECT_THAT_EXPECTED(ExtractTokenData(str), + llvm::HasValue(testing::ElementsAre( + testing::Pair(Token::numeric_constant, str)))); + } + // Verify that none of the invalid numbers come out as numeric tokens. + for (auto &str : invalid_numbers) { + SCOPED_TRACE(str); + llvm::Expected maybe_lexer = DILLexer::Create(str); + EXPECT_THAT_EXPECTED(maybe_lexer, llvm::Succeeded()); + DILLexer lexer(*maybe_lexer); + Token token = lexer.GetCurrentToken(); + EXPECT_TRUE(token.IsNot(Token::numeric_constant)); + EXPECT_TRUE(token.IsOneOf({Token::eof, Token::identifier})); } } >From df1ef931315375ba9e8c15c092f8548f24afc1ce Mon Sep 17 00:00:00 2001 From: Ilia Kuklin Date: Tue, 6 May 2025 23:07:12 +0500 Subject: [PATCH 2/4] Use `GetSyntheticArrayMember` to subscript pointers and out of bounds index from arrays --- lldb/include/lldb/ValueObject/DILEval.h | 2 - lldb/source/ValueObject/DILEval.cpp | 71 +++++-------------------- 2 files changed, 14 insertions(+), 59 deletions(-) diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h index e3df80862b082..a03b1a138798f 100644 --- a/lldb/include/lldb/ValueObject/DILEval.h +++ b/lldb/include/lldb/ValueObject/DILEval.h @@ -55,8 +55,6 @@ class Interpreter : Visitor { llvm::Expected Visit(const ArraySubscriptNode *node) override; - lldb::ValueObjectSP PointerAdd(lldb::ValueObjectSP lhs, int64_t offset); - // Used by the interpreter to create objects, perform casts, etc. lldb::TargetSP m_target; llvm::StringRef m_expr; diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index 527017da7c019..a3e1380ea1de4 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -18,22 +18,6 @@ namespace lldb_private::dil { -static lldb::ValueObjectSP -ArrayToPointerConversion(lldb::ValueObjectSP valobj, - std::shared_ptr ctx) { - assert(valobj->IsArrayType() && - "an argument to array-to-pointer conversion must be an array"); - - uint64_t addr = valobj->GetLoadAddress(); - llvm::StringRef name = "result"; - ExecutionContext exe_ctx; - ctx->CalculateExecutionContext(exe_ctx); - return ValueObject::CreateValueObjectFromAddress( - name, addr, exe_ctx, - valobj->GetCompilerType().GetArrayElementType(ctx.get()).GetPointerType(), - /* do_deref */ false); -} - static lldb::ValueObjectSP LookupStaticIdentifier( VariableList &variable_list, std::shared_ptr exe_scope, llvm::StringRef name_ref, llvm::StringRef unqualified_name) { @@ -327,21 +311,6 @@ Interpreter::Visit(const UnaryOpNode *node) { m_expr, "invalid ast: unexpected binary operator", node->GetLocation()); } -lldb::ValueObjectSP Interpreter::PointerAdd(lldb::ValueObjectSP lhs, - int64_t offset) { - uint64_t byte_size = 0; - if (auto temp = lhs->GetCompilerType().GetPointeeType().GetByteSize( - lhs->GetTargetSP().get())) - byte_size = *temp; - uintptr_t addr = lhs->GetValueAsUnsigned(0) + offset * byte_size; - - llvm::StringRef name = "result"; - ExecutionContext exe_ctx(m_target.get(), false); - return ValueObject::CreateValueObjectFromAddress(name, addr, exe_ctx, - lhs->GetCompilerType(), - /* do_deref */ false); -} - llvm::Expected Interpreter::Visit(const ArraySubscriptNode *node) { auto lhs_or_err = Evaluate(node->lhs()); @@ -373,22 +342,21 @@ Interpreter::Visit(const ArraySubscriptNode *node) { m_expr, "array subscript is not an integer", node->GetLocation()); // Check to see if 'base' has a synthetic value; if so, try using that. + uint64_t child_idx = index->GetValueAsUnsigned(0); if (base->HasSyntheticValue()) { lldb::ValueObjectSP synthetic = base->GetSyntheticValue(); if (synthetic && synthetic != base) { uint32_t num_children = synthetic->GetNumChildrenIgnoringErrors(); // Verify that the 'index' is not out-of-range for the declared type. - if (index->GetValueAsSigned(0) >= num_children) { - auto message = - llvm::formatv("array index {0} is not valid for \"({1}) {2}\"", - index->GetValueAsSigned(0), - base->GetTypeName().AsCString(""), - base->GetName().AsCString()); + if (child_idx >= num_children) { + auto message = llvm::formatv( + "array index {0} is not valid for \"({1}) {2}\"", child_idx, + base->GetTypeName().AsCString(""), + base->GetName().AsCString()); return llvm::make_error(m_expr, message, node->GetLocation()); } - uint64_t child_idx = index->GetValueAsUnsigned(0); if (static_cast(child_idx) < synthetic->GetNumChildrenIgnoringErrors()) { lldb::ValueObjectSP child_valobj_sp = @@ -410,25 +378,14 @@ Interpreter::Visit(const ArraySubscriptNode *node) { m_expr, "subscript of pointer to incomplete type 'void'", node->GetLocation()); - if (base_type.IsArrayType()) - base = ArrayToPointerConversion(base, m_exe_ctx_scope); - - CompilerType item_type = base->GetCompilerType().GetPointeeType(); - lldb::addr_t base_addr = base->GetValueAsUnsigned(0); - - llvm::StringRef name = "result"; - ExecutionContext exe_ctx(m_target.get(), false); - // Create a pointer and add the index, i.e. "base + index". - lldb::ValueObjectSP value = - PointerAdd(ValueObject::CreateValueObjectFromAddress( - name, base_addr, exe_ctx, item_type.GetPointerType(), - /*do_deref=*/false), - index->GetValueAsSigned(0)); - - lldb::ValueObjectSP val2 = value->Dereference(error); - if (error.Fail()) - return error.ToError(); - return val2; + if (base_type.IsArrayType()) { + uint32_t num_children = base->GetNumChildrenIgnoringErrors(); + if (child_idx < num_children) + return base->GetChildAtIndex(child_idx); + } + + int64_t signed_child_idx = index->GetValueAsSigned(0); + return base->GetSyntheticArrayMember(signed_child_idx, true); } } // namespace lldb_private::dil >From 05da91b5a9fa77570c5157068bc858f4359e6d3a Mon Sep 17 00:00:00 2001 From: Ilia Kuklin Date: Tue, 6 May 2025 23:22:47 +0500 Subject: [PATCH 3/4] Rename getters, refactor number lexing and remove '_' --- lldb/include/lldb/ValueObject/DILAST.h | 8 ++++---- lldb/source/ValueObject/DILEval.cpp | 8 ++++---- lldb/source/ValueObject/DILLexer.cpp | 21 +++++--------------- lldb/unittests/ValueObject/DILLexerTests.cpp | 3 +-- 4 files changed, 14 insertions(+), 26 deletions(-) diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h index 6908deed7aee3..b6deff19a2f8b 100644 --- a/lldb/include/lldb/ValueObject/DILAST.h +++ b/lldb/include/lldb/ValueObject/DILAST.h @@ -118,8 +118,8 @@ class UnaryOpNode : public ASTNode { llvm::Expected Accept(Visitor *v) const override; - UnaryOpKind kind() const { return m_kind; } - ASTNode *operand() const { return m_operand.get(); } + UnaryOpKind GetKind() const { return m_kind; } + ASTNode *GetOperand() const { return m_operand.get(); } static bool classof(const ASTNode *node) { return node->GetKind() == NodeKind::eUnaryOpNode; @@ -138,8 +138,8 @@ class ArraySubscriptNode : public ASTNode { llvm::Expected Accept(Visitor *v) const override; - ASTNode *lhs() const { return m_lhs.get(); } - ASTNode *rhs() const { return m_rhs.get(); } + ASTNode *GetLHS() const { return m_lhs.get(); } + ASTNode *GetRHS() const { return m_rhs.get(); } static bool classof(const ASTNode *node) { return node->GetKind() == NodeKind::eArraySubscriptNode; diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index a3e1380ea1de4..604f9da777223 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -276,13 +276,13 @@ Interpreter::Visit(const IdentifierNode *node) { llvm::Expected Interpreter::Visit(const UnaryOpNode *node) { Status error; - auto rhs_or_err = Evaluate(node->operand()); + auto rhs_or_err = Evaluate(node->GetOperand()); if (!rhs_or_err) return rhs_or_err; lldb::ValueObjectSP rhs = *rhs_or_err; - switch (node->kind()) { + switch (node->GetKind()) { case UnaryOpKind::Deref: { lldb::ValueObjectSP dynamic_rhs = rhs->GetDynamicValue(m_default_dynamic); if (dynamic_rhs) @@ -313,12 +313,12 @@ Interpreter::Visit(const UnaryOpNode *node) { llvm::Expected Interpreter::Visit(const ArraySubscriptNode *node) { - auto lhs_or_err = Evaluate(node->lhs()); + auto lhs_or_err = Evaluate(node->GetLHS()); if (!lhs_or_err) { return lhs_or_err; } lldb::ValueObjectSP base = *lhs_or_err; - auto rhs_or_err = Evaluate(node->rhs()); + auto rhs_or_err = Evaluate(node->GetRHS()); if (!rhs_or_err) { return rhs_or_err; } diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp index 3222032feef19..6e41ff50bd571 100644 --- a/lldb/source/ValueObject/DILLexer.cpp +++ b/lldb/source/ValueObject/DILLexer.cpp @@ -64,25 +64,14 @@ static std::optional IsWord(llvm::StringRef expr, return candidate; } -static void ConsumeNumberBody(char &prev_ch, llvm::StringRef::iterator &cur_pos, - llvm::StringRef expr) { - while (cur_pos != expr.end() && - (IsDigit(*cur_pos) || IsLetter(*cur_pos) || *cur_pos == '_')) { - prev_ch = *cur_pos; - cur_pos++; - } -} +static bool IsNumberBodyChar(char ch) { return IsDigit(ch) || IsLetter(ch); } static std::optional IsNumber(llvm::StringRef expr, llvm::StringRef &remainder) { - llvm::StringRef::iterator cur_pos = remainder.begin(); - llvm::StringRef::iterator start = cur_pos; - char prev_ch = 0; - if (IsDigit(*start)) { - ConsumeNumberBody(prev_ch, cur_pos, expr); - llvm::StringRef number = expr.substr(start - expr.begin(), cur_pos - start); - if (remainder.consume_front(number)) - return number; + if (IsDigit(remainder[0])) { + llvm::StringRef number = remainder.take_while(IsNumberBodyChar); + remainder = remainder.drop_front(number.size()); + return number; } return std::nullopt; } diff --git a/lldb/unittests/ValueObject/DILLexerTests.cpp b/lldb/unittests/ValueObject/DILLexerTests.cpp index 203763b91afc4..f65034c1dbea3 100644 --- a/lldb/unittests/ValueObject/DILLexerTests.cpp +++ b/lldb/unittests/ValueObject/DILLexerTests.cpp @@ -157,8 +157,7 @@ TEST(DILLexerTests, IdentifiersTest) { TEST(DILLexerTests, NumbersTest) { // These strings should lex into number tokens. - std::vector valid_numbers = {"123", "0x123", "0123", "0b101", - "1_000"}; + std::vector valid_numbers = {"123", "0x123", "0123", "0b101"}; // The lexer can lex these strings, but they should not be numbers. std::vector invalid_numbers = {"", "x123", "b123"}; >From b80eb87a21839da927806d0423ec15a9932f5367 Mon Sep 17 00:00:00 2001 From: Ilia Kuklin Date: Thu, 8 May 2025 20:46:41 +0500 Subject: [PATCH 4/4] Remove scalar literal node, store subscript index as APInt --- lldb/docs/dil-expr-lang.ebnf | 7 +-- lldb/include/lldb/ValueObject/DILAST.h | 36 +++-------- lldb/include/lldb/ValueObject/DILEval.h | 2 - lldb/include/lldb/ValueObject/DILParser.h | 3 +- lldb/source/ValueObject/DILAST.cpp | 5 -- lldb/source/ValueObject/DILEval.cpp | 61 ++----------------- lldb/source/ValueObject/DILParser.cpp | 61 ++++++------------- .../TestFrameVarDILArraySubscript.py | 24 ++++---- 8 files changed, 45 insertions(+), 154 deletions(-) diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf index 0cbb5403785db..d54f65df15865 100644 --- a/lldb/docs/dil-expr-lang.ebnf +++ b/lldb/docs/dil-expr-lang.ebnf @@ -11,10 +11,9 @@ unary_expression = unary_operator expression unary_operator = "*" | "&" ; postfix_expression = primary_expression - | postfix_expression "[" expression "]"; + | postfix_expression "[" integer_literal "]"; -primary_expression = numeric_literal - | id_expression +primary_expression = id_expression | "(" expression ")"; id_expression = unqualified_id @@ -28,7 +27,7 @@ qualified_id = ["::"] [nested_name_specifier] unqualified_id identifier = ? C99 Identifier ? ; -numeric_literal = ? C99 Integer constant ? ; +integer_literal = ? Integer constant: hexademical, decimal, octal, binary ? ; register = "$" ? Register name ? ; diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h index b6deff19a2f8b..7c8dfbfafdccc 100644 --- a/lldb/include/lldb/ValueObject/DILAST.h +++ b/lldb/include/lldb/ValueObject/DILAST.h @@ -73,26 +73,6 @@ class ErrorNode : public ASTNode { } }; -class ScalarLiteralNode : public ASTNode { -public: - ScalarLiteralNode(uint32_t location, lldb::BasicType type, Scalar value) - : ASTNode(location, NodeKind::eScalarLiteralNode), m_type(type), - m_value(value) {} - - llvm::Expected Accept(Visitor *v) const override; - - lldb::BasicType GetType() const { return m_type; } - Scalar GetValue() const & { return m_value; } - - static bool classof(const ASTNode *node) { - return node->GetKind() == NodeKind::eScalarLiteralNode; - } - -private: - lldb::BasicType m_type; - Scalar m_value; -}; - class IdentifierNode : public ASTNode { public: IdentifierNode(uint32_t location, std::string name) @@ -132,22 +112,22 @@ class UnaryOpNode : public ASTNode { class ArraySubscriptNode : public ASTNode { public: - ArraySubscriptNode(uint32_t location, ASTNodeUP lhs, ASTNodeUP rhs) - : ASTNode(location, NodeKind::eArraySubscriptNode), m_lhs(std::move(lhs)), - m_rhs(std::move(rhs)) {} + ArraySubscriptNode(uint32_t location, ASTNodeUP base, llvm::APInt index) + : ASTNode(location, NodeKind::eArraySubscriptNode), + m_base(std::move(base)), m_index(std::move(index)) {} llvm::Expected Accept(Visitor *v) const override; - ASTNode *GetLHS() const { return m_lhs.get(); } - ASTNode *GetRHS() const { return m_rhs.get(); } + ASTNode *GetBase() const { return m_base.get(); } + const llvm::APInt *GetIndex() const { return &m_index; } static bool classof(const ASTNode *node) { return node->GetKind() == NodeKind::eArraySubscriptNode; } private: - ASTNodeUP m_lhs; - ASTNodeUP m_rhs; + ASTNodeUP m_base; + llvm::APInt m_index; }; /// This class contains one Visit method for each specialized type of @@ -158,8 +138,6 @@ class Visitor { public: virtual ~Visitor() = default; virtual llvm::Expected - Visit(const ScalarLiteralNode *node) = 0; - virtual llvm::Expected Visit(const IdentifierNode *node) = 0; virtual llvm::Expected Visit(const UnaryOpNode *node) = 0; diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h index a03b1a138798f..03f869297c18f 100644 --- a/lldb/include/lldb/ValueObject/DILEval.h +++ b/lldb/include/lldb/ValueObject/DILEval.h @@ -47,8 +47,6 @@ class Interpreter : Visitor { llvm::Expected Evaluate(const ASTNode *node); private: - llvm::Expected - Visit(const ScalarLiteralNode *node) override; llvm::Expected Visit(const IdentifierNode *node) override; llvm::Expected Visit(const UnaryOpNode *node) override; diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h index af237ece0228d..50680b4c6abb5 100644 --- a/lldb/include/lldb/ValueObject/DILParser.h +++ b/lldb/include/lldb/ValueObject/DILParser.h @@ -91,8 +91,7 @@ class DILParser { std::string ParseIdExpression(); std::string ParseUnqualifiedId(); - ASTNodeUP ParseNumericLiteral(); - ASTNodeUP ParseNumericConstant(); + std::optional ParseIntegerConstant(); void BailOut(const std::string &error, uint32_t loc, uint16_t err_len); diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp index ceb4a4aa99c4f..330b5a3f3c586 100644 --- a/lldb/source/ValueObject/DILAST.cpp +++ b/lldb/source/ValueObject/DILAST.cpp @@ -15,11 +15,6 @@ llvm::Expected ErrorNode::Accept(Visitor *v) const { llvm_unreachable("Attempting to Visit a DIL ErrorNode."); } -llvm::Expected -ScalarLiteralNode::Accept(Visitor *v) const { - return v->Visit(this); -} - llvm::Expected IdentifierNode::Accept(Visitor *v) const { return v->Visit(this); } diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index 604f9da777223..76ca6723c36fe 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -214,45 +214,6 @@ llvm::Expected Interpreter::Evaluate(const ASTNode *node) { return value_or_error; } -static CompilerType GetBasicType(std::shared_ptr ctx, - lldb::BasicType basic_type) { - static std::unordered_map basic_types; - auto type = basic_types.find(basic_type); - if (type != basic_types.end()) { - std::string type_name((type->second).GetTypeName().AsCString()); - // Only return the found type if it's valid. - if (type_name != "") - return type->second; - } - - lldb::TargetSP target_sp = ctx->CalculateTarget(); - if (target_sp) { - for (auto type_system_sp : target_sp->GetScratchTypeSystems()) - if (auto compiler_type = - type_system_sp->GetBasicTypeFromAST(basic_type)) { - basic_types.insert({basic_type, compiler_type}); - return compiler_type; - } - } - CompilerType empty_type; - return empty_type; -} - -llvm::Expected -Interpreter::Visit(const ScalarLiteralNode *node) { - CompilerType result_type = GetBasicType(m_exe_ctx_scope, node->GetType()); - Scalar value = node->GetValue(); - - if (result_type.IsInteger() || result_type.IsNullPtrType() || - result_type.IsPointerType()) { - llvm::APInt val = value.GetAPSInt(); - return ValueObject::CreateValueObjectFromAPInt(m_target, val, result_type, - "result"); - } - - return lldb::ValueObjectSP(); -} - llvm::Expected Interpreter::Visit(const IdentifierNode *node) { lldb::DynamicValueType use_dynamic = m_default_dynamic; @@ -313,16 +274,12 @@ Interpreter::Visit(const UnaryOpNode *node) { llvm::Expected Interpreter::Visit(const ArraySubscriptNode *node) { - auto lhs_or_err = Evaluate(node->GetLHS()); + auto lhs_or_err = Evaluate(node->GetBase()); if (!lhs_or_err) { return lhs_or_err; } lldb::ValueObjectSP base = *lhs_or_err; - auto rhs_or_err = Evaluate(node->GetRHS()); - if (!rhs_or_err) { - return rhs_or_err; - } - lldb::ValueObjectSP index = *rhs_or_err; + const llvm::APInt *index = node->GetIndex(); Status error; if (base->GetCompilerType().IsReferenceType()) { @@ -330,19 +287,9 @@ Interpreter::Visit(const ArraySubscriptNode *node) { if (error.Fail()) return error.ToError(); } - if (index->GetCompilerType().IsReferenceType()) { - index = index->Dereference(error); - if (error.Fail()) - return error.ToError(); - } - - auto index_type = index->GetCompilerType(); - if (!index_type.IsIntegerOrUnscopedEnumerationType()) - return llvm::make_error( - m_expr, "array subscript is not an integer", node->GetLocation()); // Check to see if 'base' has a synthetic value; if so, try using that. - uint64_t child_idx = index->GetValueAsUnsigned(0); + uint64_t child_idx = index->getZExtValue(); if (base->HasSyntheticValue()) { lldb::ValueObjectSP synthetic = base->GetSyntheticValue(); if (synthetic && synthetic != base) { @@ -384,7 +331,7 @@ Interpreter::Visit(const ArraySubscriptNode *node) { return base->GetChildAtIndex(child_idx); } - int64_t signed_child_idx = index->GetValueAsSigned(0); + int64_t signed_child_idx = index->getSExtValue(); return base->GetSyntheticArrayMember(signed_child_idx, true); } diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp index 24a6f0c909a0a..cca2b483cb57e 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -118,7 +118,7 @@ ASTNodeUP DILParser::ParseUnaryExpression() { // // postfix_expression: // primary_expression -// postfix_expression "[" expression "]" +// postfix_expression "[" integer_literal "]" // ASTNodeUP DILParser::ParsePostfixExpression() { ASTNodeUP lhs = ParsePrimaryExpression(); @@ -128,11 +128,17 @@ ASTNodeUP DILParser::ParsePostfixExpression() { switch (token.GetKind()) { case Token::l_square: { m_dil_lexer.Advance(); - auto rhs = ParseExpression(); + auto rhs = ParseIntegerConstant(); + if (!rhs) { + BailOut( + llvm::formatv("failed to parse integer constant: {0}", CurToken()), + CurToken().GetLocation(), CurToken().GetSpelling().length()); + return std::make_unique(); + } Expect(Token::r_square); m_dil_lexer.Advance(); lhs = std::make_unique(loc, std::move(lhs), - std::move(rhs)); + std::move(*rhs)); break; } default: @@ -150,8 +156,6 @@ ASTNodeUP DILParser::ParsePostfixExpression() { // "(" expression ")" // ASTNodeUP DILParser::ParsePrimaryExpression() { - if (CurToken().Is(Token::numeric_constant)) - return ParseNumericLiteral(); if (CurToken().IsOneOf({Token::coloncolon, Token::identifier})) { // Save the source location for the diagnostics message. uint32_t loc = CurToken().GetLocation(); @@ -311,50 +315,21 @@ void DILParser::BailOut(const std::string &error, uint32_t loc, m_dil_lexer.ResetTokenIdx(m_dil_lexer.NumLexedTokens() - 1); } -// Parse a numeric_literal. +// Parse a integer_literal. // -// numeric_literal: -// ? Token::numeric_constant ? +// integer_literal: +// ? Integer constant ? // -ASTNodeUP DILParser::ParseNumericLiteral() { - Expect(Token::numeric_constant); - ASTNodeUP numeric_constant = ParseNumericConstant(); - if (numeric_constant->GetKind() == NodeKind::eErrorNode) { - BailOut(llvm::formatv("Failed to parse token as numeric-constant: {0}", - CurToken()), - CurToken().GetLocation(), CurToken().GetSpelling().length()); - return std::make_unique(); - } - m_dil_lexer.Advance(); - return numeric_constant; -} - -static constexpr std::pair type_suffixes[] = { - {"ull", lldb::eBasicTypeUnsignedLongLong}, - {"ul", lldb::eBasicTypeUnsignedLong}, - {"u", lldb::eBasicTypeUnsignedInt}, - {"ll", lldb::eBasicTypeLongLong}, - {"l", lldb::eBasicTypeLong}, -}; - -ASTNodeUP DILParser::ParseNumericConstant() { - Token token = CurToken(); - auto spelling = token.GetSpelling(); +std::optional DILParser::ParseIntegerConstant() { + auto spelling = CurToken().GetSpelling(); llvm::StringRef spelling_ref = spelling; - lldb::BasicType type = lldb::eBasicTypeInt; - for (auto [suffix, t] : type_suffixes) { - if (spelling_ref.consume_back_insensitive(suffix)) { - type = t; - break; - } - } llvm::APInt raw_value; if (!spelling_ref.getAsInteger(0, raw_value)) { - Scalar scalar_value(raw_value); - return std::make_unique(token.GetLocation(), type, - scalar_value); + m_dil_lexer.Advance(); + return raw_value; } - return std::make_unique(); + + return std::nullopt; } void DILParser::Expect(Token::Kind kind) { diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py index e142889124613..42e9e1de9f6f3 100644 --- a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py +++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py @@ -30,26 +30,26 @@ def test_dereference(self): # Test int[] and int* self.expect_var_path("int_arr[0]", True, value="1") self.expect_var_path("int_ptr[1]", True, value="2") - self.expect_var_path("int_arr[enum_one]", value="2") + self.expect("frame var 'int_arr[enum_one]'", error=True) # Test when base and index are references. self.expect_var_path("int_arr[0]", True, value="1") - self.expect_var_path("int_arr[idx_1_ref]", value="2") - self.expect_var_path("int_arr[enum_ref]", value="2") + self.expect("frame var 'int_arr[idx_1_ref]'", error=True) + self.expect("frame var 'int_arr[enum_ref]'", error=True) self.expect_var_path("int_arr_ref[0]", value="1") - self.expect_var_path("int_arr_ref[idx_1_ref]", value="2") - self.expect_var_path("int_arr_ref[enum_ref]", value="2") + self.expect("frame var 'int_arr_ref[idx_1_ref]'", error=True) + self.expect("frame var 'int_arr_ref[enum_ref]'", error=True) # Test when base and index are typedefs. self.expect_var_path("td_int_arr[0]", True, value="1") - self.expect_var_path("td_int_arr[td_int_idx_1]", value="2") - self.expect_var_path("td_int_arr[td_td_int_idx_2]", value="3") + self.expect("frame var 'td_int_arr[td_int_idx_1]'", error=True) + self.expect("frame var 'td_int_arr[td_td_int_idx_2]'", error=True) self.expect_var_path("td_int_ptr[0]", True, value="1") - self.expect_var_path("td_int_ptr[td_int_idx_1]", value="2") - self.expect_var_path("td_int_ptr[td_td_int_idx_2]", value="3") + self.expect("frame var 'td_int_ptr[td_int_idx_1]'", error=True) + self.expect("frame var 'td_int_ptr[td_td_int_idx_2]'", error=True) # Both typedefs and refs - self.expect_var_path("td_int_arr_ref[td_int_idx_1_ref]", value="2") + self.expect("frame var 'td_int_arr_ref[td_int_idx_1_ref]'", error=True) # Test for index out of bounds. self.expect_var_path("int_arr[42]", True, type="int") @@ -79,10 +79,10 @@ def test_dereference(self): self.expect( "frame var 'int_arr[int_ptr]'", error=True, - substrs=["array subscript is not an integer"], + substrs=["failed to parse integer constant"], ) self.expect( "frame var '1[2]'", error=True, - substrs=["subscripted value is not an array or pointer"], + substrs=["Unexpected token"], ) From lldb-commits at lists.llvm.org Thu May 8 09:46:36 2025 From: lldb-commits at lists.llvm.org (Ilia Kuklin via lldb-commits) Date: Thu, 08 May 2025 09:46:36 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551) In-Reply-To: Message-ID: <681cdfec.170a0220.17629f.4720@mx.google.com> kuilpd wrote: > Because I wanted to avoid this getting bogged down in the discussion about the types of numbers. I don't really mind the "extra capability" of indexing an array with a another variable. The part I have problem with is the precedent it sets about the representation of numbers. > > You're right that we can't do math operations without (implicitly or explicitly) assigning them some type, but that's exactly the part I think needs more discussion. Right now, you're assigning the type based on the first type system you find (which is likely going to be C), but I think that's not a good choice. If you're debugging some swift code, I think you'd be surprised if `47` resolves to a C type. Okay, I see the problem, I didn't really think that retrieving the type system when debugging Swift code would return C type system. Why is it like this though? Is there a reliable way to get a type system of the main language? I also found now that the function `TypeSystem::GetBasicTypeFromAST` that the code in Eval relies on is not even implemented in [`TypeSystemSwift`](https://github.com/swiftlang/llvm-project/blob/a5b0b3daf26fd41b2caf61551b72f74b0e2a4ab7/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwift.h#L296). I don't have any other suggestion how to implement this, so I guess I'll stick to having an integer within the subscript node until we have a better idea. https://github.com/llvm/llvm-project/pull/138551 From lldb-commits at lists.llvm.org Thu May 8 10:30:44 2025 From: lldb-commits at lists.llvm.org (Vy Nguyen via lldb-commits) Date: Thu, 08 May 2025 10:30:44 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb]Make `list` command work with headers when possible. (PR #139002) In-Reply-To: Message-ID: <681cea44.050a0220.164b8f.9389@mx.google.com> https://github.com/oontvoo updated https://github.com/llvm/llvm-project/pull/139002 >From 5746e997eea55c05cbbb63ad6f78ca225c9ac855 Mon Sep 17 00:00:00 2001 From: Vy Nguyen Date: Wed, 7 May 2025 21:37:31 -0400 Subject: [PATCH 1/4] [lldb]Make `list` command work with headers when possible. --- lldb/source/Commands/CommandObjectSource.cpp | 36 +++++++++++- lldb/source/Core/ModuleList.cpp | 2 + lldb/test/Shell/Commands/list-header.test | 59 ++++++++++++++++++++ 3 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 lldb/test/Shell/Commands/list-header.test diff --git a/lldb/source/Commands/CommandObjectSource.cpp b/lldb/source/Commands/CommandObjectSource.cpp index c205813565d52..475317021255c 100644 --- a/lldb/source/Commands/CommandObjectSource.cpp +++ b/lldb/source/Commands/CommandObjectSource.cpp @@ -1104,6 +1104,7 @@ class CommandObjectSourceList : public CommandObjectParsed { bool check_inlines = false; SymbolContextList sc_list; size_t num_matches = 0; + uint32_t start_line = m_options.start_line; if (!m_options.modules.empty()) { ModuleList matching_modules; @@ -1114,7 +1115,7 @@ class CommandObjectSourceList : public CommandObjectParsed { matching_modules.Clear(); target.GetImages().FindModules(module_spec, matching_modules); num_matches += matching_modules.ResolveSymbolContextForFilePath( - filename, 0, check_inlines, + filename, start_line, check_inlines, SymbolContextItem(eSymbolContextModule | eSymbolContextCompUnit), sc_list); @@ -1122,7 +1123,7 @@ class CommandObjectSourceList : public CommandObjectParsed { } } else { num_matches = target.GetImages().ResolveSymbolContextForFilePath( - filename, 0, check_inlines, + filename, start_line, check_inlines, eSymbolContextModule | eSymbolContextCompUnit, sc_list); } @@ -1170,8 +1171,37 @@ class CommandObjectSourceList : public CommandObjectParsed { if (m_options.num_lines == 0) m_options.num_lines = 10; const uint32_t column = 0; + + // Headers aren't always in the DWARF but if they have + // executable code (eg., inlined-functions) then the callsite's file(s) + // will be found. + // So if a header was requested and we got a primary file, then look + // thru its support file(s) for the header. + lldb::SupportFileSP actual_file_sp = + sc.comp_unit->GetPrimarySupportFile(); + if (llvm::StringRef(m_options.file_name).ends_with(".h")) { + int support_matches_count = 0; + for (auto &file : sc.comp_unit->GetSupportFiles()) { + if (llvm::StringRef(file->GetSpecOnly().GetPath()).ends_with(filename)) { + actual_file_sp = file; + ++support_matches_count; + } + } + if (support_matches_count == 0) { + result.AppendErrorWithFormat( + "No file found for requested header: \"%s.\"\n", + m_options.file_name.c_str()); + return; + } else if (support_matches_count > 1) { + result.AppendErrorWithFormat( + "Multiple files found for requested header: \"%s.\"\n", + m_options.file_name.c_str()); + return; + } + } + target.GetSourceManager().DisplaySourceLinesWithLineNumbers( - sc.comp_unit->GetPrimarySupportFile(), + actual_file_sp, m_options.start_line, column, 0, m_options.num_lines, "", &result.GetOutputStream(), GetBreakpointLocations()); diff --git a/lldb/source/Core/ModuleList.cpp b/lldb/source/Core/ModuleList.cpp index d5ddf6e846112..90c6a62727734 100644 --- a/lldb/source/Core/ModuleList.cpp +++ b/lldb/source/Core/ModuleList.cpp @@ -714,6 +714,8 @@ uint32_t ModuleList::ResolveSymbolContextsForFileSpec( const FileSpec &file_spec, uint32_t line, bool check_inlines, SymbolContextItem resolve_scope, SymbolContextList &sc_list) const { std::lock_guard guard(m_modules_mutex); + // If we're looking for a header (not source), then need to check inline. + check_inlines = check_inlines || !file_spec.IsSourceImplementationFile(); for (const ModuleSP &module_sp : m_modules) { module_sp->ResolveSymbolContextsForFileSpec(file_spec, line, check_inlines, resolve_scope, sc_list); diff --git a/lldb/test/Shell/Commands/list-header.test b/lldb/test/Shell/Commands/list-header.test new file mode 100644 index 0000000000000..ca700cd2b2fb1 --- /dev/null +++ b/lldb/test/Shell/Commands/list-header.test @@ -0,0 +1,59 @@ +## Test that `list header.h:` works correctly when header is available. +## +# REQUIRES: x86 +# RUN: split-file %s %t + +# RUN: %clang_host -g %t/main_with_inlined.cc %t/foo.cc -o %t/main_with_inlined.out +# RUN: %clang_host -g %t/main_no_inlined.cc %t/foo.cc -o %t/main_no_inlined.out + +# RUN: %lldb %t/main_with_inlined.out -o "list foo.h:2" -o "exit" 2>&1 \ +# RUN: | FileCheck %s --check-prefix=CHECK-INLINED + +## Would be nice if this listed the header too - but probably not something +## we want to support right now. +# RUN: echo quit | %lldb %t/main_no_inlined.out -o "list foo.h:2" -o "exit" 2>&1 \ +# RUN: | FileCheck %s --check-prefix=CHECK-NO-INLINED + +# CHECK-INLINED: 2 extern int* ptr; +# CHECK-INLINED: 3 void f(int x); +# CHECK-INLINED: 4 +# CHECK-INLINED: 5 inline void g(int x) { +# CHECK-INLINED: 6 *ptr = x; // should raise a SIGILL +# CHECK-INLINED: 7 } + +# CHECK-NO-INLINED: error: Could not find source file "foo.h". + +#--- foo.h +// foo.h +extern int* ptr; +void f(int x); + +inline void g(int x) { + *ptr = x; // should raise a SIGILL +} + +#--- foo.cc +#include "foo.h" + +int* ptr; + +void f(int x) { + *ptr = x; +} + +#--- main_with_inlined.cc +#include "foo.h" + +int main() { + f(234); + g(123); // Call the inlined function + return 0; +} + +#--- main_no_inlined.cc +#include "foo.h" + +int main() { + f(234); + return 0; +} >From b0af06e7ea2f408691c25aab25dc3951f3ddd403 Mon Sep 17 00:00:00 2001 From: Vy Nguyen Date: Wed, 7 May 2025 21:42:26 -0400 Subject: [PATCH 2/4] formatting --- lldb/source/Commands/CommandObjectSource.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lldb/source/Commands/CommandObjectSource.cpp b/lldb/source/Commands/CommandObjectSource.cpp index 475317021255c..4b0e47f4cc83f 100644 --- a/lldb/source/Commands/CommandObjectSource.cpp +++ b/lldb/source/Commands/CommandObjectSource.cpp @@ -1173,16 +1173,16 @@ class CommandObjectSourceList : public CommandObjectParsed { const uint32_t column = 0; // Headers aren't always in the DWARF but if they have - // executable code (eg., inlined-functions) then the callsite's file(s) - // will be found. - // So if a header was requested and we got a primary file, then look - // thru its support file(s) for the header. + // executable code (eg., inlined-functions) then the callsite's + // file(s) will be found. So if a header was requested and we got a + // primary file, then look thru its support file(s) for the header. lldb::SupportFileSP actual_file_sp = sc.comp_unit->GetPrimarySupportFile(); if (llvm::StringRef(m_options.file_name).ends_with(".h")) { int support_matches_count = 0; for (auto &file : sc.comp_unit->GetSupportFiles()) { - if (llvm::StringRef(file->GetSpecOnly().GetPath()).ends_with(filename)) { + if (llvm::StringRef(file->GetSpecOnly().GetPath()) + .ends_with(filename)) { actual_file_sp = file; ++support_matches_count; } @@ -1201,9 +1201,9 @@ class CommandObjectSourceList : public CommandObjectParsed { } target.GetSourceManager().DisplaySourceLinesWithLineNumbers( - actual_file_sp, - m_options.start_line, column, 0, m_options.num_lines, "", - &result.GetOutputStream(), GetBreakpointLocations()); + actual_file_sp, m_options.start_line, column, 0, + m_options.num_lines, "", &result.GetOutputStream(), + GetBreakpointLocations()); result.SetStatus(eReturnStatusSuccessFinishResult); } else { >From 0e823400669138776ed55f441ca8cb03e262f6e3 Mon Sep 17 00:00:00 2001 From: Vy Nguyen Date: Thu, 8 May 2025 10:10:58 -0400 Subject: [PATCH 3/4] update test --- lldb/test/Shell/Commands/list-header.test | 1 - 1 file changed, 1 deletion(-) diff --git a/lldb/test/Shell/Commands/list-header.test b/lldb/test/Shell/Commands/list-header.test index ca700cd2b2fb1..204b704ebe2a2 100644 --- a/lldb/test/Shell/Commands/list-header.test +++ b/lldb/test/Shell/Commands/list-header.test @@ -45,7 +45,6 @@ void f(int x) { #include "foo.h" int main() { - f(234); g(123); // Call the inlined function return 0; } >From 692a387f56715e99fe33f6b5a840b2fa9f92d8c7 Mon Sep 17 00:00:00 2001 From: Vy Nguyen Date: Thu, 8 May 2025 13:30:31 -0400 Subject: [PATCH 4/4] rework the logic to be more general --- lldb/source/Commands/CommandObjectSource.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lldb/source/Commands/CommandObjectSource.cpp b/lldb/source/Commands/CommandObjectSource.cpp index 4b0e47f4cc83f..c4c46bb33d0fc 100644 --- a/lldb/source/Commands/CommandObjectSource.cpp +++ b/lldb/source/Commands/CommandObjectSource.cpp @@ -1175,33 +1175,35 @@ class CommandObjectSourceList : public CommandObjectParsed { // Headers aren't always in the DWARF but if they have // executable code (eg., inlined-functions) then the callsite's // file(s) will be found. So if a header was requested and we got a - // primary file, then look thru its support file(s) for the header. - lldb::SupportFileSP actual_file_sp = + // primary file (ie., something with a different name), then look thru + // its support file(s) for the header. + lldb::SupportFileSP found_file_sp = sc.comp_unit->GetPrimarySupportFile(); - if (llvm::StringRef(m_options.file_name).ends_with(".h")) { + + if (!llvm::StringRef(found_file_sp->GetSpecOnly().GetPath()) + .ends_with(filename)) { int support_matches_count = 0; for (auto &file : sc.comp_unit->GetSupportFiles()) { if (llvm::StringRef(file->GetSpecOnly().GetPath()) .ends_with(filename)) { - actual_file_sp = file; + found_file_sp = file; ++support_matches_count; } } if (support_matches_count == 0) { result.AppendErrorWithFormat( - "No file found for requested header: \"%s.\"\n", - m_options.file_name.c_str()); + "No file found for requested header: \"%s.\"\n", filename); return; } else if (support_matches_count > 1) { result.AppendErrorWithFormat( "Multiple files found for requested header: \"%s.\"\n", - m_options.file_name.c_str()); + filename); return; } } target.GetSourceManager().DisplaySourceLinesWithLineNumbers( - actual_file_sp, m_options.start_line, column, 0, + found_file_sp, m_options.start_line, column, 0, m_options.num_lines, "", &result.GetOutputStream(), GetBreakpointLocations()); From lldb-commits at lists.llvm.org Thu May 8 10:31:00 2025 From: lldb-commits at lists.llvm.org (Vy Nguyen via lldb-commits) Date: Thu, 08 May 2025 10:31:00 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb]Make `list` command work with headers when possible. (PR #139002) In-Reply-To: Message-ID: <681cea54.a70a0220.3995f1.9256@mx.google.com> ================ @@ -1170,8 +1171,37 @@ class CommandObjectSourceList : public CommandObjectParsed { if (m_options.num_lines == 0) m_options.num_lines = 10; const uint32_t column = 0; + + // Headers aren't always in the DWARF but if they have + // executable code (eg., inlined-functions) then the callsite's file(s) + // will be found. + // So if a header was requested and we got a primary file, then look + // thru its support file(s) for the header. + lldb::SupportFileSP actual_file_sp = + sc.comp_unit->GetPrimarySupportFile(); + if (llvm::StringRef(m_options.file_name).ends_with(".h")) { ---------------- oontvoo wrote: done! https://github.com/llvm/llvm-project/pull/139002 From lldb-commits at lists.llvm.org Thu May 8 10:45:56 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Thu, 08 May 2025 10:45:56 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Migrate attach to typed RequestHandler. (PR #137911) In-Reply-To: Message-ID: <681cedd4.170a0220.6aab2.61be@mx.google.com> ================ @@ -10,183 +10,133 @@ #include "EventHelper.h" #include "JSONUtils.h" #include "LLDBUtils.h" +#include "Protocol/ProtocolRequests.h" #include "RequestHandler.h" +#include "lldb/API/SBAttachInfo.h" #include "lldb/API/SBListener.h" +#include "lldb/lldb-defines.h" +#include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" +using namespace llvm; +using namespace lldb_dap::protocol; + namespace lldb_dap { -// "AttachRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "Attach request; value of command field is 'attach'.", -// "properties": { -// "command": { -// "type": "string", -// "enum": [ "attach" ] -// }, -// "arguments": { -// "$ref": "#/definitions/AttachRequestArguments" -// } -// }, -// "required": [ "command", "arguments" ] -// }] -// }, -// "AttachRequestArguments": { -// "type": "object", -// "description": "Arguments for 'attach' request.\nThe attach request has no -// standardized attributes." -// }, -// "AttachResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to 'attach' request. This is just an -// acknowledgement, so no body field is required." -// }] -// } -void AttachRequestHandler::operator()(const llvm::json::Object &request) const { - dap.is_attach = true; - llvm::json::Object response; - lldb::SBError error; - FillResponse(request, response); - const int invalid_port = 0; - const auto *arguments = request.getObject("arguments"); - const lldb::pid_t pid = - GetInteger(arguments, "pid").value_or(LLDB_INVALID_PROCESS_ID); - const auto gdb_remote_port = - GetInteger(arguments, "gdb-remote-port").value_or(invalid_port); - const auto gdb_remote_hostname = - GetString(arguments, "gdb-remote-hostname").value_or("localhost"); - const auto wait_for = GetBoolean(arguments, "waitFor").value_or(false); - dap.configuration.initCommands = GetStrings(arguments, "initCommands"); - dap.configuration.preRunCommands = GetStrings(arguments, "preRunCommands"); - dap.configuration.postRunCommands = GetStrings(arguments, "postRunCommands"); - dap.configuration.stopCommands = GetStrings(arguments, "stopCommands"); - dap.configuration.exitCommands = GetStrings(arguments, "exitCommands"); - dap.configuration.terminateCommands = - GetStrings(arguments, "terminateCommands"); - auto attachCommands = GetStrings(arguments, "attachCommands"); - llvm::StringRef core_file = GetString(arguments, "coreFile").value_or(""); - const uint64_t timeout_seconds = - GetInteger(arguments, "timeout").value_or(30); - dap.stop_at_entry = core_file.empty() - ? GetBoolean(arguments, "stopOnEntry").value_or(false) - : true; - const llvm::StringRef debuggerRoot = - GetString(arguments, "debuggerRoot").value_or(""); - dap.configuration.enableAutoVariableSummaries = - GetBoolean(arguments, "enableAutoVariableSummaries").value_or(false); - dap.configuration.enableSyntheticChildDebugging = - GetBoolean(arguments, "enableSyntheticChildDebugging").value_or(false); - dap.configuration.displayExtendedBacktrace = - GetBoolean(arguments, "displayExtendedBacktrace").value_or(false); - dap.configuration.commandEscapePrefix = - GetString(arguments, "commandEscapePrefix").value_or("`"); - dap.configuration.program = GetString(arguments, "program"); - dap.configuration.targetTriple = GetString(arguments, "targetTriple"); - dap.configuration.platformName = GetString(arguments, "platformName"); - dap.SetFrameFormat(GetString(arguments, "customFrameFormat").value_or("")); - dap.SetThreadFormat(GetString(arguments, "customThreadFormat").value_or("")); +/// The `attach` request is sent from the client to the debug adapter to attach +/// to a debuggee that is already running. +/// +/// Since attaching is debugger/runtime specific, the arguments for this request +/// are not part of this specification. +Error AttachRequestHandler::Run(const AttachRequestArguments &args) const { + // Validate that we have a well formed attach request. + if (args.attachCommands.empty() && args.coreFile.empty() && + args.configuration.program.empty() && + args.pid == LLDB_INVALID_PROCESS_ID && + args.gdbRemotePort == LLDB_DAP_INVALID_PORT) + return make_error( + "expected one of 'pid', 'program', 'attachCommands', " + "'coreFile' or 'gdb-remote-port' to be specified"); + + // Check if we have mutually exclusive arguments. + if ((args.pid != LLDB_INVALID_PROCESS_ID) && + (args.gdbRemotePort != LLDB_DAP_INVALID_PORT)) + return make_error( + "'pid' and 'gdb-remote-port' are mutually exclusive"); + + dap.SetConfiguration(args.configuration, /*is_attach=*/true); + if (!args.coreFile.empty()) + dap.stop_at_entry = true; PrintWelcomeMessage(); // This is a hack for loading DWARF in .o files on Mac where the .o files - // in the debug map of the main executable have relative paths which require - // the lldb-dap binary to have its working directory set to that relative - // root for the .o files in order to be able to load debug info. - if (!debuggerRoot.empty()) - llvm::sys::fs::set_current_path(debuggerRoot); + // in the debug map of the main executable have relative paths which + // require the lldb-dap binary to have its working directory set to that + // relative root for the .o files in order to be able to load debug info. + if (!dap.configuration.debuggerRoot.empty()) + sys::fs::set_current_path(dap.configuration.debuggerRoot); // Run any initialize LLDB commands the user specified in the launch.json - if (llvm::Error err = dap.RunInitCommands()) { - response["success"] = false; - EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; - } + if (llvm::Error err = dap.RunInitCommands()) + return err; - SetSourceMapFromArguments(*arguments); + dap.ConfigureSourceMaps(); - lldb::SBError status; - dap.SetTarget(dap.CreateTarget(status)); - if (status.Fail()) { - response["success"] = llvm::json::Value(false); - EmplaceSafeString(response, "message", status.GetCString()); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; - } + lldb::SBError error; + lldb::SBTarget target = dap.CreateTarget(error); + if (error.Fail()) + return ToError(error); + + dap.SetTarget(target); // Run any pre run LLDB commands the user specified in the launch.json - if (llvm::Error err = dap.RunPreRunCommands()) { - response["success"] = false; - EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; - } + if (Error err = dap.RunPreRunCommands()) + return err; - if ((pid == LLDB_INVALID_PROCESS_ID || gdb_remote_port == invalid_port) && - wait_for) { + if ((args.pid == LLDB_INVALID_PROCESS_ID || + args.gdbRemotePort == LLDB_DAP_INVALID_PORT) && + args.waitFor) { char attach_msg[256]; auto attach_msg_len = snprintf(attach_msg, sizeof(attach_msg), "Waiting to attach to \"%s\"...", dap.target.GetExecutable().GetFilename()); - dap.SendOutput(OutputType::Console, - llvm::StringRef(attach_msg, attach_msg_len)); + dap.SendOutput(OutputType::Console, StringRef(attach_msg, attach_msg_len)); ---------------- ashgti wrote: Done. https://github.com/llvm/llvm-project/pull/137911 From lldb-commits at lists.llvm.org Thu May 8 10:46:25 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Thu, 08 May 2025 10:46:25 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Migrate attach to typed RequestHandler. (PR #137911) In-Reply-To: Message-ID: <681cedf1.170a0220.37f32a.77e8@mx.google.com> ================ @@ -183,7 +194,7 @@ struct Configuration { /// Specify a source path to remap "./" to allow full paths to be used when /// setting breakpoints in binaries that have relative source paths. - std::optional sourcePath; + std::string sourcePath = ""; ---------------- ashgti wrote: Removed the `= ""` part here and below. https://github.com/llvm/llvm-project/pull/137911 From lldb-commits at lists.llvm.org Thu May 8 10:50:48 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Thu, 08 May 2025 10:50:48 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb]Make `list` command work with headers when possible. (PR #139002) In-Reply-To: Message-ID: <681ceef8.a70a0220.1fe7d.9957@mx.google.com> ================ @@ -0,0 +1,58 @@ +## Test that `list header.h:` works correctly when header is available. +## +# REQUIRES: x86 ---------------- bulbazord wrote: Why does this test require x86? https://github.com/llvm/llvm-project/pull/139002 From lldb-commits at lists.llvm.org Thu May 8 10:50:49 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Thu, 08 May 2025 10:50:49 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb]Make `list` command work with headers when possible. (PR #139002) In-Reply-To: Message-ID: <681ceef9.170a0220.51cec.90de@mx.google.com> ================ @@ -1170,10 +1171,41 @@ class CommandObjectSourceList : public CommandObjectParsed { if (m_options.num_lines == 0) m_options.num_lines = 10; const uint32_t column = 0; + + // Headers aren't always in the DWARF but if they have + // executable code (eg., inlined-functions) then the callsite's + // file(s) will be found. So if a header was requested and we got a + // primary file (ie., something with a different name), then look thru + // its support file(s) for the header. + lldb::SupportFileSP found_file_sp = + sc.comp_unit->GetPrimarySupportFile(); + + if (!llvm::StringRef(found_file_sp->GetSpecOnly().GetPath()) + .ends_with(filename)) { + int support_matches_count = 0; + for (auto &file : sc.comp_unit->GetSupportFiles()) { + if (llvm::StringRef(file->GetSpecOnly().GetPath()) + .ends_with(filename)) { + found_file_sp = file; + ++support_matches_count; + } + } + if (support_matches_count == 0) { + result.AppendErrorWithFormat( + "No file found for requested header: \"%s.\"\n", filename); ---------------- bulbazord wrote: The error message implies that you're looking for a header, but maybe somebody's not looking for a header per se. You could do something like `#include "foo.inc"` and maybe a developer wouldn't conceptualize that as including a header. Maybe a more generic message like `Failed to find requested file`? https://github.com/llvm/llvm-project/pull/139002 From lldb-commits at lists.llvm.org Thu May 8 10:53:48 2025 From: lldb-commits at lists.llvm.org (Vy Nguyen via lldb-commits) Date: Thu, 08 May 2025 10:53:48 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb]Make `list` command work with headers when possible. (PR #139002) In-Reply-To: Message-ID: <681cefac.170a0220.b6a2b.606e@mx.google.com> ================ @@ -0,0 +1,58 @@ +## Test that `list header.h:` works correctly when header is available. +## +# REQUIRES: x86 ---------------- oontvoo wrote: removed https://github.com/llvm/llvm-project/pull/139002 From lldb-commits at lists.llvm.org Thu May 8 10:53:50 2025 From: lldb-commits at lists.llvm.org (Vy Nguyen via lldb-commits) Date: Thu, 08 May 2025 10:53:50 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb]Make `list` command work with headers when possible. (PR #139002) In-Reply-To: Message-ID: <681cefae.170a0220.1b2979.6164@mx.google.com> ================ @@ -1170,10 +1171,41 @@ class CommandObjectSourceList : public CommandObjectParsed { if (m_options.num_lines == 0) m_options.num_lines = 10; const uint32_t column = 0; + + // Headers aren't always in the DWARF but if they have + // executable code (eg., inlined-functions) then the callsite's + // file(s) will be found. So if a header was requested and we got a + // primary file (ie., something with a different name), then look thru + // its support file(s) for the header. + lldb::SupportFileSP found_file_sp = + sc.comp_unit->GetPrimarySupportFile(); + + if (!llvm::StringRef(found_file_sp->GetSpecOnly().GetPath()) + .ends_with(filename)) { + int support_matches_count = 0; + for (auto &file : sc.comp_unit->GetSupportFiles()) { + if (llvm::StringRef(file->GetSpecOnly().GetPath()) + .ends_with(filename)) { + found_file_sp = file; + ++support_matches_count; + } + } + if (support_matches_count == 0) { + result.AppendErrorWithFormat( + "No file found for requested header: \"%s.\"\n", filename); ---------------- oontvoo wrote: done https://github.com/llvm/llvm-project/pull/139002 From lldb-commits at lists.llvm.org Thu May 8 10:54:10 2025 From: lldb-commits at lists.llvm.org (Vy Nguyen via lldb-commits) Date: Thu, 08 May 2025 10:54:10 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb]Make `list` command work with headers when possible. (PR #139002) In-Reply-To: Message-ID: <681cefc2.050a0220.30beba.2be1@mx.google.com> https://github.com/oontvoo updated https://github.com/llvm/llvm-project/pull/139002 >From 5746e997eea55c05cbbb63ad6f78ca225c9ac855 Mon Sep 17 00:00:00 2001 From: Vy Nguyen Date: Wed, 7 May 2025 21:37:31 -0400 Subject: [PATCH 1/5] [lldb]Make `list` command work with headers when possible. --- lldb/source/Commands/CommandObjectSource.cpp | 36 +++++++++++- lldb/source/Core/ModuleList.cpp | 2 + lldb/test/Shell/Commands/list-header.test | 59 ++++++++++++++++++++ 3 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 lldb/test/Shell/Commands/list-header.test diff --git a/lldb/source/Commands/CommandObjectSource.cpp b/lldb/source/Commands/CommandObjectSource.cpp index c205813565d52..475317021255c 100644 --- a/lldb/source/Commands/CommandObjectSource.cpp +++ b/lldb/source/Commands/CommandObjectSource.cpp @@ -1104,6 +1104,7 @@ class CommandObjectSourceList : public CommandObjectParsed { bool check_inlines = false; SymbolContextList sc_list; size_t num_matches = 0; + uint32_t start_line = m_options.start_line; if (!m_options.modules.empty()) { ModuleList matching_modules; @@ -1114,7 +1115,7 @@ class CommandObjectSourceList : public CommandObjectParsed { matching_modules.Clear(); target.GetImages().FindModules(module_spec, matching_modules); num_matches += matching_modules.ResolveSymbolContextForFilePath( - filename, 0, check_inlines, + filename, start_line, check_inlines, SymbolContextItem(eSymbolContextModule | eSymbolContextCompUnit), sc_list); @@ -1122,7 +1123,7 @@ class CommandObjectSourceList : public CommandObjectParsed { } } else { num_matches = target.GetImages().ResolveSymbolContextForFilePath( - filename, 0, check_inlines, + filename, start_line, check_inlines, eSymbolContextModule | eSymbolContextCompUnit, sc_list); } @@ -1170,8 +1171,37 @@ class CommandObjectSourceList : public CommandObjectParsed { if (m_options.num_lines == 0) m_options.num_lines = 10; const uint32_t column = 0; + + // Headers aren't always in the DWARF but if they have + // executable code (eg., inlined-functions) then the callsite's file(s) + // will be found. + // So if a header was requested and we got a primary file, then look + // thru its support file(s) for the header. + lldb::SupportFileSP actual_file_sp = + sc.comp_unit->GetPrimarySupportFile(); + if (llvm::StringRef(m_options.file_name).ends_with(".h")) { + int support_matches_count = 0; + for (auto &file : sc.comp_unit->GetSupportFiles()) { + if (llvm::StringRef(file->GetSpecOnly().GetPath()).ends_with(filename)) { + actual_file_sp = file; + ++support_matches_count; + } + } + if (support_matches_count == 0) { + result.AppendErrorWithFormat( + "No file found for requested header: \"%s.\"\n", + m_options.file_name.c_str()); + return; + } else if (support_matches_count > 1) { + result.AppendErrorWithFormat( + "Multiple files found for requested header: \"%s.\"\n", + m_options.file_name.c_str()); + return; + } + } + target.GetSourceManager().DisplaySourceLinesWithLineNumbers( - sc.comp_unit->GetPrimarySupportFile(), + actual_file_sp, m_options.start_line, column, 0, m_options.num_lines, "", &result.GetOutputStream(), GetBreakpointLocations()); diff --git a/lldb/source/Core/ModuleList.cpp b/lldb/source/Core/ModuleList.cpp index d5ddf6e846112..90c6a62727734 100644 --- a/lldb/source/Core/ModuleList.cpp +++ b/lldb/source/Core/ModuleList.cpp @@ -714,6 +714,8 @@ uint32_t ModuleList::ResolveSymbolContextsForFileSpec( const FileSpec &file_spec, uint32_t line, bool check_inlines, SymbolContextItem resolve_scope, SymbolContextList &sc_list) const { std::lock_guard guard(m_modules_mutex); + // If we're looking for a header (not source), then need to check inline. + check_inlines = check_inlines || !file_spec.IsSourceImplementationFile(); for (const ModuleSP &module_sp : m_modules) { module_sp->ResolveSymbolContextsForFileSpec(file_spec, line, check_inlines, resolve_scope, sc_list); diff --git a/lldb/test/Shell/Commands/list-header.test b/lldb/test/Shell/Commands/list-header.test new file mode 100644 index 0000000000000..ca700cd2b2fb1 --- /dev/null +++ b/lldb/test/Shell/Commands/list-header.test @@ -0,0 +1,59 @@ +## Test that `list header.h:` works correctly when header is available. +## +# REQUIRES: x86 +# RUN: split-file %s %t + +# RUN: %clang_host -g %t/main_with_inlined.cc %t/foo.cc -o %t/main_with_inlined.out +# RUN: %clang_host -g %t/main_no_inlined.cc %t/foo.cc -o %t/main_no_inlined.out + +# RUN: %lldb %t/main_with_inlined.out -o "list foo.h:2" -o "exit" 2>&1 \ +# RUN: | FileCheck %s --check-prefix=CHECK-INLINED + +## Would be nice if this listed the header too - but probably not something +## we want to support right now. +# RUN: echo quit | %lldb %t/main_no_inlined.out -o "list foo.h:2" -o "exit" 2>&1 \ +# RUN: | FileCheck %s --check-prefix=CHECK-NO-INLINED + +# CHECK-INLINED: 2 extern int* ptr; +# CHECK-INLINED: 3 void f(int x); +# CHECK-INLINED: 4 +# CHECK-INLINED: 5 inline void g(int x) { +# CHECK-INLINED: 6 *ptr = x; // should raise a SIGILL +# CHECK-INLINED: 7 } + +# CHECK-NO-INLINED: error: Could not find source file "foo.h". + +#--- foo.h +// foo.h +extern int* ptr; +void f(int x); + +inline void g(int x) { + *ptr = x; // should raise a SIGILL +} + +#--- foo.cc +#include "foo.h" + +int* ptr; + +void f(int x) { + *ptr = x; +} + +#--- main_with_inlined.cc +#include "foo.h" + +int main() { + f(234); + g(123); // Call the inlined function + return 0; +} + +#--- main_no_inlined.cc +#include "foo.h" + +int main() { + f(234); + return 0; +} >From b0af06e7ea2f408691c25aab25dc3951f3ddd403 Mon Sep 17 00:00:00 2001 From: Vy Nguyen Date: Wed, 7 May 2025 21:42:26 -0400 Subject: [PATCH 2/5] formatting --- lldb/source/Commands/CommandObjectSource.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lldb/source/Commands/CommandObjectSource.cpp b/lldb/source/Commands/CommandObjectSource.cpp index 475317021255c..4b0e47f4cc83f 100644 --- a/lldb/source/Commands/CommandObjectSource.cpp +++ b/lldb/source/Commands/CommandObjectSource.cpp @@ -1173,16 +1173,16 @@ class CommandObjectSourceList : public CommandObjectParsed { const uint32_t column = 0; // Headers aren't always in the DWARF but if they have - // executable code (eg., inlined-functions) then the callsite's file(s) - // will be found. - // So if a header was requested and we got a primary file, then look - // thru its support file(s) for the header. + // executable code (eg., inlined-functions) then the callsite's + // file(s) will be found. So if a header was requested and we got a + // primary file, then look thru its support file(s) for the header. lldb::SupportFileSP actual_file_sp = sc.comp_unit->GetPrimarySupportFile(); if (llvm::StringRef(m_options.file_name).ends_with(".h")) { int support_matches_count = 0; for (auto &file : sc.comp_unit->GetSupportFiles()) { - if (llvm::StringRef(file->GetSpecOnly().GetPath()).ends_with(filename)) { + if (llvm::StringRef(file->GetSpecOnly().GetPath()) + .ends_with(filename)) { actual_file_sp = file; ++support_matches_count; } @@ -1201,9 +1201,9 @@ class CommandObjectSourceList : public CommandObjectParsed { } target.GetSourceManager().DisplaySourceLinesWithLineNumbers( - actual_file_sp, - m_options.start_line, column, 0, m_options.num_lines, "", - &result.GetOutputStream(), GetBreakpointLocations()); + actual_file_sp, m_options.start_line, column, 0, + m_options.num_lines, "", &result.GetOutputStream(), + GetBreakpointLocations()); result.SetStatus(eReturnStatusSuccessFinishResult); } else { >From 0e823400669138776ed55f441ca8cb03e262f6e3 Mon Sep 17 00:00:00 2001 From: Vy Nguyen Date: Thu, 8 May 2025 10:10:58 -0400 Subject: [PATCH 3/5] update test --- lldb/test/Shell/Commands/list-header.test | 1 - 1 file changed, 1 deletion(-) diff --git a/lldb/test/Shell/Commands/list-header.test b/lldb/test/Shell/Commands/list-header.test index ca700cd2b2fb1..204b704ebe2a2 100644 --- a/lldb/test/Shell/Commands/list-header.test +++ b/lldb/test/Shell/Commands/list-header.test @@ -45,7 +45,6 @@ void f(int x) { #include "foo.h" int main() { - f(234); g(123); // Call the inlined function return 0; } >From 692a387f56715e99fe33f6b5a840b2fa9f92d8c7 Mon Sep 17 00:00:00 2001 From: Vy Nguyen Date: Thu, 8 May 2025 13:30:31 -0400 Subject: [PATCH 4/5] rework the logic to be more general --- lldb/source/Commands/CommandObjectSource.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lldb/source/Commands/CommandObjectSource.cpp b/lldb/source/Commands/CommandObjectSource.cpp index 4b0e47f4cc83f..c4c46bb33d0fc 100644 --- a/lldb/source/Commands/CommandObjectSource.cpp +++ b/lldb/source/Commands/CommandObjectSource.cpp @@ -1175,33 +1175,35 @@ class CommandObjectSourceList : public CommandObjectParsed { // Headers aren't always in the DWARF but if they have // executable code (eg., inlined-functions) then the callsite's // file(s) will be found. So if a header was requested and we got a - // primary file, then look thru its support file(s) for the header. - lldb::SupportFileSP actual_file_sp = + // primary file (ie., something with a different name), then look thru + // its support file(s) for the header. + lldb::SupportFileSP found_file_sp = sc.comp_unit->GetPrimarySupportFile(); - if (llvm::StringRef(m_options.file_name).ends_with(".h")) { + + if (!llvm::StringRef(found_file_sp->GetSpecOnly().GetPath()) + .ends_with(filename)) { int support_matches_count = 0; for (auto &file : sc.comp_unit->GetSupportFiles()) { if (llvm::StringRef(file->GetSpecOnly().GetPath()) .ends_with(filename)) { - actual_file_sp = file; + found_file_sp = file; ++support_matches_count; } } if (support_matches_count == 0) { result.AppendErrorWithFormat( - "No file found for requested header: \"%s.\"\n", - m_options.file_name.c_str()); + "No file found for requested header: \"%s.\"\n", filename); return; } else if (support_matches_count > 1) { result.AppendErrorWithFormat( "Multiple files found for requested header: \"%s.\"\n", - m_options.file_name.c_str()); + filename); return; } } target.GetSourceManager().DisplaySourceLinesWithLineNumbers( - actual_file_sp, m_options.start_line, column, 0, + found_file_sp, m_options.start_line, column, 0, m_options.num_lines, "", &result.GetOutputStream(), GetBreakpointLocations()); >From 11ef8875774418dd4122ebf5779dcd5ef6f26689 Mon Sep 17 00:00:00 2001 From: Vy Nguyen Date: Thu, 8 May 2025 13:53:57 -0400 Subject: [PATCH 5/5] address review comments --- lldb/source/Commands/CommandObjectSource.cpp | 4 ++-- lldb/test/Shell/Commands/list-header.test | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lldb/source/Commands/CommandObjectSource.cpp b/lldb/source/Commands/CommandObjectSource.cpp index c4c46bb33d0fc..1f147fb08d5fa 100644 --- a/lldb/source/Commands/CommandObjectSource.cpp +++ b/lldb/source/Commands/CommandObjectSource.cpp @@ -1192,11 +1192,11 @@ class CommandObjectSourceList : public CommandObjectParsed { } if (support_matches_count == 0) { result.AppendErrorWithFormat( - "No file found for requested header: \"%s.\"\n", filename); + "No file found for requested file: \"%s.\"\n", filename); return; } else if (support_matches_count > 1) { result.AppendErrorWithFormat( - "Multiple files found for requested header: \"%s.\"\n", + "Multiple files found for requested file: \"%s.\"\n", filename); return; } diff --git a/lldb/test/Shell/Commands/list-header.test b/lldb/test/Shell/Commands/list-header.test index 204b704ebe2a2..d1dc261e8575e 100644 --- a/lldb/test/Shell/Commands/list-header.test +++ b/lldb/test/Shell/Commands/list-header.test @@ -1,6 +1,5 @@ ## Test that `list header.h:` works correctly when header is available. ## -# REQUIRES: x86 # RUN: split-file %s %t # RUN: %clang_host -g %t/main_with_inlined.cc %t/foo.cc -o %t/main_with_inlined.out From lldb-commits at lists.llvm.org Thu May 8 13:02:22 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Thu, 08 May 2025 13:02:22 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Ptrace seize dead process (PR #137041) In-Reply-To: Message-ID: <681d0dce.620a0220.2f5848.a528@mx.google.com> https://github.com/Jlalond updated https://github.com/llvm/llvm-project/pull/137041 >From fa08811c7a90b9fd1c644b051ed3d7d1157243fe Mon Sep 17 00:00:00 2001 From: Jacob Lalonde Date: Thu, 8 May 2025 12:56:14 -0700 Subject: [PATCH] Implement PTRACE_SEIZE when a process is coredumping --- .../lldb/Host/common/NativeProcessProtocol.h | 2 + .../Process/Linux/NativeProcessLinux.cpp | 120 +++++++++++++++++- .../Process/Linux/NativeProcessLinux.h | 5 +- 3 files changed, 120 insertions(+), 7 deletions(-) diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h b/lldb/include/lldb/Host/common/NativeProcessProtocol.h index 1d5fecfcd5c27..f50e9da0957f5 100644 --- a/lldb/include/lldb/Host/common/NativeProcessProtocol.h +++ b/lldb/include/lldb/Host/common/NativeProcessProtocol.h @@ -189,6 +189,8 @@ class NativeProcessProtocol { bool CanResume() const { return m_state == lldb::eStateStopped; } + bool IsStopped() const { return m_state == lldb::eStateStopped; } + lldb::ByteOrder GetByteOrder() const { return GetArchitecture().GetByteOrder(); } diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp index 7f2aba0e4eb2c..c8e8f47524fb6 100644 --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -312,10 +312,26 @@ NativeProcessLinux::Manager::Attach( Log *log = GetLog(POSIXLog::Process); LLDB_LOG(log, "pid = {0:x}", pid); - auto tids_or = NativeProcessLinux::Attach(pid); - if (!tids_or) - return tids_or.takeError(); - ArrayRef<::pid_t> tids = *tids_or; + // This safety check lets us decide if we should + // seize or attach. + ProcessInstanceInfo process_info; + if (!Host::GetProcessInfo(pid, process_info)) + return llvm::make_error("Unable to read process info", + llvm::inconvertibleErrorCode()); + + std::vector<::pid_t> tids; + if (process_info.IsCoreDumping()) { + auto attached_or = NativeProcessLinux::Seize(pid); + if (!attached_or) + return attached_or.takeError(); + tids = std::move(*attached_or); + } else { + auto attached_or = NativeProcessLinux::Attach(pid); + if (!attached_or) + return attached_or.takeError(); + tids = std::move(*attached_or); + } + llvm::Expected arch_or = NativeRegisterContextLinux::DetermineArchitecture(tids[0]); if (!arch_or) @@ -444,6 +460,93 @@ NativeProcessLinux::NativeProcessLinux(::pid_t pid, int terminal_fd, SetState(StateType::eStateStopped, false); } +llvm::Expected> NativeProcessLinux::Seize(::pid_t pid) { + // TODO: Because the Seize during coredumping change introduces the + // concept of a non resumable stop, we should also check for + // PTRACE_O_TRACEEXIT, which per the man page the status will equal + // status >> 8 == (SIGTRAP | (PTRACE_EVENT_EXEC<<8)) + // and if this is true, we should say we can't resume. + Log *log = GetLog(POSIXLog::Process); + + uint64_t options = GetDefaultPtraceOpts(); + Status status; + // Use a map to keep track of the threads which we have attached/need to + // attach. + Host::TidMap tids_to_attach; + while (Host::FindProcessThreads(pid, tids_to_attach)) { + for (Host::TidMap::iterator it = tids_to_attach.begin(); + it != tids_to_attach.end();) { + if (it->second == true) { + continue; + } + lldb::tid_t tid = it->first; + if ((status = PtraceWrapper(PTRACE_SEIZE, tid, nullptr, (void *)options)) + .Fail()) { + // No such thread. The thread may have exited. More error handling + // may be needed. + if (status.GetError() == ESRCH) { + it = tids_to_attach.erase(it); + continue; + } + if (status.GetError() == EPERM) { + // Depending on the value of ptrace_scope, we can return a + // different error that suggests how to fix it. + return AddPtraceScopeNote(status.ToError()); + } + return status.ToError(); + } + + if ((status = PtraceWrapper(PTRACE_INTERRUPT, tid)).Fail()) { + // No such thread. The thread may have exited. More error handling + // may be needed. + if (status.GetError() == ESRCH) { + it = tids_to_attach.erase(it); + continue; + } + if (status.GetError() == EPERM) { + // Depending on the value of ptrace_scope, we can return a + // different error that suggests how to fix it. + return AddPtraceScopeNote(status.ToError()); + } + return status.ToError(); + } + + int wpid = + llvm::sys::RetryAfterSignal(-1, ::waitpid, tid, nullptr, __WALL); + // Need to use __WALL otherwise we receive an error with errno=ECHLD At + // this point we should have a thread stopped if waitpid succeeds. + if (wpid < 0) { + // No such thread. The thread may have exited. More error handling + // may be needed. + if (errno == ESRCH) { + it = tids_to_attach.erase(it); + continue; + } + return llvm::errorCodeToError( + std::error_code(errno, std::generic_category())); + } + + LLDB_LOG(log, "adding tid = {0}", tid); + it->second = true; + + // move the loop forward + ++it; + } + } + + size_t tid_count = tids_to_attach.size(); + if (tid_count == 0) + return llvm::make_error("No such process", + llvm::inconvertibleErrorCode()); + + std::vector<::pid_t> tids; + tids.reserve(tid_count); + for (const auto &p : tids_to_attach) + tids.push_back(p.first); + + return std::move(tids); +} + llvm::Expected> NativeProcessLinux::Attach(::pid_t pid) { Log *log = GetLog(POSIXLog::Process); @@ -513,8 +616,8 @@ llvm::Expected> NativeProcessLinux::Attach(::pid_t pid) { return std::move(tids); } -Status NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid) { - long ptrace_opts = 0; +uint64_t NativeProcessLinux::GetDefaultPtraceOpts() { + uint64_t ptrace_opts = 0; // Have the child raise an event on exit. This is used to keep the child in // limbo until it is destroyed. @@ -537,6 +640,11 @@ Status NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid) { // the child finishes sharing memory. ptrace_opts |= PTRACE_O_TRACEVFORKDONE; + return ptrace_opts; +} + +Status NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid) { + uint64_t ptrace_opts = GetDefaultPtraceOpts(); return PtraceWrapper(PTRACE_SETOPTIONS, pid, nullptr, (void *)ptrace_opts); } diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h index d345f165a75d8..9ae4e57e74add 100644 --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h +++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h @@ -175,7 +175,6 @@ class NativeProcessLinux : public NativeProcessELF, private: Manager &m_manager; ArchSpec m_arch; - LazyBool m_supports_mem_region = eLazyBoolCalculate; std::vector> m_mem_region_cache; @@ -191,9 +190,13 @@ class NativeProcessLinux : public NativeProcessELF, // Returns a list of process threads that we have attached to. static llvm::Expected> Attach(::pid_t pid); + // Returns a list of process threads that we have seized and interrupted. + static llvm::Expected> Seize(::pid_t pid); static Status SetDefaultPtraceOpts(const lldb::pid_t); + static uint64_t GetDefaultPtraceOpts(); + bool TryHandleWaitStatus(lldb::pid_t pid, WaitStatus status); void MonitorCallback(NativeThreadLinux &thread, WaitStatus status); From lldb-commits at lists.llvm.org Thu May 8 13:04:47 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Thu, 08 May 2025 13:04:47 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Ptrace seize dead process (PR #137041) In-Reply-To: Message-ID: <681d0e5f.630a0220.2e524d.43d4@mx.google.com> https://github.com/Jlalond updated https://github.com/llvm/llvm-project/pull/137041 >From 42365065305d190aec3206f5d3eb35095050fb69 Mon Sep 17 00:00:00 2001 From: Jacob Lalonde Date: Thu, 8 May 2025 12:56:14 -0700 Subject: [PATCH] Implement PTRACE_SEIZE when a process is coredumping --- .../Process/Linux/NativeProcessLinux.cpp | 120 +++++++++++++++++- .../Process/Linux/NativeProcessLinux.h | 5 +- 2 files changed, 118 insertions(+), 7 deletions(-) diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp index 7f2aba0e4eb2c..c8e8f47524fb6 100644 --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -312,10 +312,26 @@ NativeProcessLinux::Manager::Attach( Log *log = GetLog(POSIXLog::Process); LLDB_LOG(log, "pid = {0:x}", pid); - auto tids_or = NativeProcessLinux::Attach(pid); - if (!tids_or) - return tids_or.takeError(); - ArrayRef<::pid_t> tids = *tids_or; + // This safety check lets us decide if we should + // seize or attach. + ProcessInstanceInfo process_info; + if (!Host::GetProcessInfo(pid, process_info)) + return llvm::make_error("Unable to read process info", + llvm::inconvertibleErrorCode()); + + std::vector<::pid_t> tids; + if (process_info.IsCoreDumping()) { + auto attached_or = NativeProcessLinux::Seize(pid); + if (!attached_or) + return attached_or.takeError(); + tids = std::move(*attached_or); + } else { + auto attached_or = NativeProcessLinux::Attach(pid); + if (!attached_or) + return attached_or.takeError(); + tids = std::move(*attached_or); + } + llvm::Expected arch_or = NativeRegisterContextLinux::DetermineArchitecture(tids[0]); if (!arch_or) @@ -444,6 +460,93 @@ NativeProcessLinux::NativeProcessLinux(::pid_t pid, int terminal_fd, SetState(StateType::eStateStopped, false); } +llvm::Expected> NativeProcessLinux::Seize(::pid_t pid) { + // TODO: Because the Seize during coredumping change introduces the + // concept of a non resumable stop, we should also check for + // PTRACE_O_TRACEEXIT, which per the man page the status will equal + // status >> 8 == (SIGTRAP | (PTRACE_EVENT_EXEC<<8)) + // and if this is true, we should say we can't resume. + Log *log = GetLog(POSIXLog::Process); + + uint64_t options = GetDefaultPtraceOpts(); + Status status; + // Use a map to keep track of the threads which we have attached/need to + // attach. + Host::TidMap tids_to_attach; + while (Host::FindProcessThreads(pid, tids_to_attach)) { + for (Host::TidMap::iterator it = tids_to_attach.begin(); + it != tids_to_attach.end();) { + if (it->second == true) { + continue; + } + lldb::tid_t tid = it->first; + if ((status = PtraceWrapper(PTRACE_SEIZE, tid, nullptr, (void *)options)) + .Fail()) { + // No such thread. The thread may have exited. More error handling + // may be needed. + if (status.GetError() == ESRCH) { + it = tids_to_attach.erase(it); + continue; + } + if (status.GetError() == EPERM) { + // Depending on the value of ptrace_scope, we can return a + // different error that suggests how to fix it. + return AddPtraceScopeNote(status.ToError()); + } + return status.ToError(); + } + + if ((status = PtraceWrapper(PTRACE_INTERRUPT, tid)).Fail()) { + // No such thread. The thread may have exited. More error handling + // may be needed. + if (status.GetError() == ESRCH) { + it = tids_to_attach.erase(it); + continue; + } + if (status.GetError() == EPERM) { + // Depending on the value of ptrace_scope, we can return a + // different error that suggests how to fix it. + return AddPtraceScopeNote(status.ToError()); + } + return status.ToError(); + } + + int wpid = + llvm::sys::RetryAfterSignal(-1, ::waitpid, tid, nullptr, __WALL); + // Need to use __WALL otherwise we receive an error with errno=ECHLD At + // this point we should have a thread stopped if waitpid succeeds. + if (wpid < 0) { + // No such thread. The thread may have exited. More error handling + // may be needed. + if (errno == ESRCH) { + it = tids_to_attach.erase(it); + continue; + } + return llvm::errorCodeToError( + std::error_code(errno, std::generic_category())); + } + + LLDB_LOG(log, "adding tid = {0}", tid); + it->second = true; + + // move the loop forward + ++it; + } + } + + size_t tid_count = tids_to_attach.size(); + if (tid_count == 0) + return llvm::make_error("No such process", + llvm::inconvertibleErrorCode()); + + std::vector<::pid_t> tids; + tids.reserve(tid_count); + for (const auto &p : tids_to_attach) + tids.push_back(p.first); + + return std::move(tids); +} + llvm::Expected> NativeProcessLinux::Attach(::pid_t pid) { Log *log = GetLog(POSIXLog::Process); @@ -513,8 +616,8 @@ llvm::Expected> NativeProcessLinux::Attach(::pid_t pid) { return std::move(tids); } -Status NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid) { - long ptrace_opts = 0; +uint64_t NativeProcessLinux::GetDefaultPtraceOpts() { + uint64_t ptrace_opts = 0; // Have the child raise an event on exit. This is used to keep the child in // limbo until it is destroyed. @@ -537,6 +640,11 @@ Status NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid) { // the child finishes sharing memory. ptrace_opts |= PTRACE_O_TRACEVFORKDONE; + return ptrace_opts; +} + +Status NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid) { + uint64_t ptrace_opts = GetDefaultPtraceOpts(); return PtraceWrapper(PTRACE_SETOPTIONS, pid, nullptr, (void *)ptrace_opts); } diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h index d345f165a75d8..9ae4e57e74add 100644 --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h +++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h @@ -175,7 +175,6 @@ class NativeProcessLinux : public NativeProcessELF, private: Manager &m_manager; ArchSpec m_arch; - LazyBool m_supports_mem_region = eLazyBoolCalculate; std::vector> m_mem_region_cache; @@ -191,9 +190,13 @@ class NativeProcessLinux : public NativeProcessELF, // Returns a list of process threads that we have attached to. static llvm::Expected> Attach(::pid_t pid); + // Returns a list of process threads that we have seized and interrupted. + static llvm::Expected> Seize(::pid_t pid); static Status SetDefaultPtraceOpts(const lldb::pid_t); + static uint64_t GetDefaultPtraceOpts(); + bool TryHandleWaitStatus(lldb::pid_t pid, WaitStatus status); void MonitorCallback(NativeThreadLinux &thread, WaitStatus status); From lldb-commits at lists.llvm.org Thu May 8 13:04:57 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Thu, 08 May 2025 13:04:57 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Ptrace seize dead process (PR #137041) In-Reply-To: Message-ID: <681d0e69.170a0220.1125e5.6c6c@mx.google.com> https://github.com/Jlalond updated https://github.com/llvm/llvm-project/pull/137041 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Thu May 8 13:05:27 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Thu, 08 May 2025 13:05:27 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Ptrace seize dead process (PR #137041) In-Reply-To: Message-ID: <681d0e87.a70a0220.f24c.a6b0@mx.google.com> https://github.com/Jlalond updated https://github.com/llvm/llvm-project/pull/137041 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Thu May 8 13:06:05 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Thu, 08 May 2025 13:06:05 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Ptrace seize dead process (PR #137041) In-Reply-To: Message-ID: <681d0ead.050a0220.1ed5ad.35f9@mx.google.com> https://github.com/Jlalond edited https://github.com/llvm/llvm-project/pull/137041 From lldb-commits at lists.llvm.org Thu May 8 13:06:51 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Thu, 08 May 2025 13:06:51 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Ptrace seize dead process (PR #137041) In-Reply-To: Message-ID: <681d0edb.a70a0220.145309.a389@mx.google.com> https://github.com/Jlalond ready_for_review https://github.com/llvm/llvm-project/pull/137041 From lldb-commits at lists.llvm.org Thu May 8 13:07:26 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 13:07:26 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Ptrace seize dead process (PR #137041) In-Reply-To: Message-ID: <681d0efe.a70a0220.2d17f9.ae6e@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Jacob Lalonde (Jlalond)
Changes This the actual PR to my [SEIZE RFC](https://discourse.llvm.org/t/rfc-ptrace-seize-when-attaching-to-dead-processes/85825/8). This is currently the bare bones on seizing a dead process, and being able to attach and introspect with LLDB. Additionally, right now I only check proc status before seize, and we should double check after seize that the process has not changed. Worth noting is once you seize a coredumping process (and it hits trace stop), Coredumping in status will now report 0. This is pretty complicated to test because it requires integration with the Kernel, thankfully the setup only involves some very simple toy programs, which I have outlined with instructions [in this gist](https://gist.github.com/Jlalond/a81a995dd14ff5d88b7f21f00879ce83) --- Full diff: https://github.com/llvm/llvm-project/pull/137041.diff 2 Files Affected: - (modified) lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp (+109-6) - (modified) lldb/source/Plugins/Process/Linux/NativeProcessLinux.h (+4-1) ``````````diff diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp index 7f2aba0e4eb2c..141e49d8a0b7e 100644 --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -312,10 +312,26 @@ NativeProcessLinux::Manager::Attach( Log *log = GetLog(POSIXLog::Process); LLDB_LOG(log, "pid = {0:x}", pid); - auto tids_or = NativeProcessLinux::Attach(pid); - if (!tids_or) - return tids_or.takeError(); - ArrayRef<::pid_t> tids = *tids_or; + // This safety check lets us decide if we should + // seize or attach. + ProcessInstanceInfo process_info; + if (!Host::GetProcessInfo(pid, process_info)) + return llvm::make_error("Unable to read process info", + llvm::inconvertibleErrorCode()); + + std::vector<::pid_t> tids; + if (process_info.IsCoreDumping()) { + auto attached_or = NativeProcessLinux::Seize(pid); + if (!attached_or) + return attached_or.takeError(); + tids = std::move(*attached_or); + } else { + auto attached_or = NativeProcessLinux::Attach(pid); + if (!attached_or) + return attached_or.takeError(); + tids = std::move(*attached_or); + } + llvm::Expected arch_or = NativeRegisterContextLinux::DetermineArchitecture(tids[0]); if (!arch_or) @@ -444,6 +460,88 @@ NativeProcessLinux::NativeProcessLinux(::pid_t pid, int terminal_fd, SetState(StateType::eStateStopped, false); } +llvm::Expected> NativeProcessLinux::Seize(::pid_t pid) { + Log *log = GetLog(POSIXLog::Process); + + uint64_t options = GetDefaultPtraceOpts(); + Status status; + // Use a map to keep track of the threads which we have attached/need to + // attach. + Host::TidMap tids_to_attach; + while (Host::FindProcessThreads(pid, tids_to_attach)) { + for (Host::TidMap::iterator it = tids_to_attach.begin(); + it != tids_to_attach.end();) { + if (it->second == true) { + continue; + } + lldb::tid_t tid = it->first; + if ((status = PtraceWrapper(PTRACE_SEIZE, tid, nullptr, (void *)options)) + .Fail()) { + // No such thread. The thread may have exited. More error handling + // may be needed. + if (status.GetError() == ESRCH) { + it = tids_to_attach.erase(it); + continue; + } + if (status.GetError() == EPERM) { + // Depending on the value of ptrace_scope, we can return a + // different error that suggests how to fix it. + return AddPtraceScopeNote(status.ToError()); + } + return status.ToError(); + } + + if ((status = PtraceWrapper(PTRACE_INTERRUPT, tid)).Fail()) { + // No such thread. The thread may have exited. More error handling + // may be needed. + if (status.GetError() == ESRCH) { + it = tids_to_attach.erase(it); + continue; + } + if (status.GetError() == EPERM) { + // Depending on the value of ptrace_scope, we can return a + // different error that suggests how to fix it. + return AddPtraceScopeNote(status.ToError()); + } + return status.ToError(); + } + + int wpid = + llvm::sys::RetryAfterSignal(-1, ::waitpid, tid, nullptr, __WALL); + // Need to use __WALL otherwise we receive an error with errno=ECHLD At + // this point we should have a thread stopped if waitpid succeeds. + if (wpid < 0) { + // No such thread. The thread may have exited. More error handling + // may be needed. + if (errno == ESRCH) { + it = tids_to_attach.erase(it); + continue; + } + return llvm::errorCodeToError( + std::error_code(errno, std::generic_category())); + } + + LLDB_LOG(log, "adding tid = {0}", tid); + it->second = true; + + // move the loop forward + ++it; + } + } + + size_t tid_count = tids_to_attach.size(); + if (tid_count == 0) + return llvm::make_error("No such process", + llvm::inconvertibleErrorCode()); + + std::vector<::pid_t> tids; + tids.reserve(tid_count); + for (const auto &p : tids_to_attach) + tids.push_back(p.first); + + return std::move(tids); +} + llvm::Expected> NativeProcessLinux::Attach(::pid_t pid) { Log *log = GetLog(POSIXLog::Process); @@ -513,8 +611,8 @@ llvm::Expected> NativeProcessLinux::Attach(::pid_t pid) { return std::move(tids); } -Status NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid) { - long ptrace_opts = 0; +uint64_t NativeProcessLinux::GetDefaultPtraceOpts() { + uint64_t ptrace_opts = 0; // Have the child raise an event on exit. This is used to keep the child in // limbo until it is destroyed. @@ -537,6 +635,11 @@ Status NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid) { // the child finishes sharing memory. ptrace_opts |= PTRACE_O_TRACEVFORKDONE; + return ptrace_opts; +} + +Status NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid) { + uint64_t ptrace_opts = GetDefaultPtraceOpts(); return PtraceWrapper(PTRACE_SETOPTIONS, pid, nullptr, (void *)ptrace_opts); } diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h index d345f165a75d8..9ae4e57e74add 100644 --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h +++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h @@ -175,7 +175,6 @@ class NativeProcessLinux : public NativeProcessELF, private: Manager &m_manager; ArchSpec m_arch; - LazyBool m_supports_mem_region = eLazyBoolCalculate; std::vector> m_mem_region_cache; @@ -191,9 +190,13 @@ class NativeProcessLinux : public NativeProcessELF, // Returns a list of process threads that we have attached to. static llvm::Expected> Attach(::pid_t pid); + // Returns a list of process threads that we have seized and interrupted. + static llvm::Expected> Seize(::pid_t pid); static Status SetDefaultPtraceOpts(const lldb::pid_t); + static uint64_t GetDefaultPtraceOpts(); + bool TryHandleWaitStatus(lldb::pid_t pid, WaitStatus status); void MonitorCallback(NativeThreadLinux &thread, WaitStatus status); ``````````
https://github.com/llvm/llvm-project/pull/137041 From lldb-commits at lists.llvm.org Thu May 8 13:08:09 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Thu, 08 May 2025 13:08:09 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Ptrace seize dead process (PR #137041) In-Reply-To: Message-ID: <681d0f29.a70a0220.19c00a.b0ba@mx.google.com> Jlalond wrote: @labath @DavidSpickett Thanks for the patience. I've broken down my prototype, and this is now patch 2, where we implement the SEIZE functionality. I will follow up with making it so we can't resume the process when we're in this state. @DavidSpickett you mentioned you wanted me to include my gist, to my knowledge github will include my summary by default. Did you want me to check in the toy program as an example? https://github.com/llvm/llvm-project/pull/137041 From lldb-commits at lists.llvm.org Thu May 8 13:09:49 2025 From: lldb-commits at lists.llvm.org (Felipe de Azevedo Piovezan via lldb-commits) Date: Thu, 08 May 2025 13:09:49 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Expose QueueThreadPlanForStepSingleInstruction function to SBThreadPlan (PR #137904) In-Reply-To: Message-ID: <681d0f8d.170a0220.3d8973.5b3e@mx.google.com> felipepiovezan wrote: @eronnen my fix does not seem to have worked https://github.com/llvm/llvm-project/pull/137904 From lldb-commits at lists.llvm.org Thu May 8 13:15:21 2025 From: lldb-commits at lists.llvm.org (Felipe de Azevedo Piovezan via lldb-commits) Date: Thu, 08 May 2025 13:15:21 -0700 (PDT) Subject: [Lldb-commits] [lldb] 2815653 - [lldb] Disable test using GetControlFlowKind on arm Message-ID: <681d10d9.a70a0220.267ef6.ac3c@mx.google.com> Author: Felipe de Azevedo Piovezan Date: 2025-05-08T13:14:40-07:00 New Revision: 28156539a9df3fa0d9db47c405c0006fcee9f77f URL: https://github.com/llvm/llvm-project/commit/28156539a9df3fa0d9db47c405c0006fcee9f77f DIFF: https://github.com/llvm/llvm-project/commit/28156539a9df3fa0d9db47c405c0006fcee9f77f.diff LOG: [lldb] Disable test using GetControlFlowKind on arm Added: Modified: lldb/test/API/functionalities/step_scripted/TestStepScripted.py Removed: ################################################################################ diff --git a/lldb/test/API/functionalities/step_scripted/TestStepScripted.py b/lldb/test/API/functionalities/step_scripted/TestStepScripted.py index cec2901aa0369..52763694541ec 100644 --- a/lldb/test/API/functionalities/step_scripted/TestStepScripted.py +++ b/lldb/test/API/functionalities/step_scripted/TestStepScripted.py @@ -70,6 +70,7 @@ def test_step_single_instruction(self): frame = thread.GetFrameAtIndex(0) self.assertEqual("foo", frame.GetFunctionName()) + @skipIf(archs=no_match(["x86_64"])) def test_step_single_instruction_with_step_over(self): (target, process, thread, bkpt) = self.run_until_branch_instruction() From lldb-commits at lists.llvm.org Thu May 8 13:15:25 2025 From: lldb-commits at lists.llvm.org (Felipe de Azevedo Piovezan via lldb-commits) Date: Thu, 08 May 2025 13:15:25 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Expose QueueThreadPlanForStepSingleInstruction function to SBThreadPlan (PR #137904) In-Reply-To: Message-ID: <681d10dd.050a0220.2b5cfa.b067@mx.google.com> felipepiovezan wrote: Ah there were _two_ tests here. I've disabled the other one too. https://github.com/llvm/llvm-project/pull/137904 From lldb-commits at lists.llvm.org Thu May 8 13:15:49 2025 From: lldb-commits at lists.llvm.org (Felipe de Azevedo Piovezan via lldb-commits) Date: Thu, 08 May 2025 13:15:49 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Expose QueueThreadPlanForStepSingleInstruction function to SBThreadPlan (PR #137904) In-Reply-To: Message-ID: <681d10f5.050a0220.10ef79.b3ef@mx.google.com> felipepiovezan wrote: 28156539a9df 20250508 fpiove.. [lldb] Disable test using GetControlFlowKind on arm (HEAD, llvm/main) https://github.com/llvm/llvm-project/pull/137904 From lldb-commits at lists.llvm.org Thu May 8 13:24:48 2025 From: lldb-commits at lists.llvm.org (Ely Ronnen via lldb-commits) Date: Thu, 08 May 2025 13:24:48 -0700 (PDT) Subject: [Lldb-commits] [lldb] [llvm] [lldb-dap] Migrating breakpointLocations request to use typed RequestHandler (PR #137426) In-Reply-To: Message-ID: <681d1310.170a0220.e5829.6ce7@mx.google.com> https://github.com/eronnen updated https://github.com/llvm/llvm-project/pull/137426 >From da044f5d5391f679cfbba4afc69b46dc30dc8fff Mon Sep 17 00:00:00 2001 From: Ely Ronnen Date: Sat, 26 Apr 2025 01:22:05 +0200 Subject: [PATCH] Migrating breakpointLocations request to use typed RequestHandler --- .../Handler/BreakpointLocationsHandler.cpp | 156 +++--------------- lldb/tools/lldb-dap/Handler/RequestHandler.h | 10 +- .../lldb-dap/Protocol/ProtocolRequests.cpp | 17 ++ .../lldb-dap/Protocol/ProtocolRequests.h | 38 +++++ .../tools/lldb-dap/Protocol/ProtocolTypes.cpp | 14 ++ lldb/tools/lldb-dap/Protocol/ProtocolTypes.h | 21 +++ llvm/include/llvm/Support/JSON.h | 8 + 7 files changed, 125 insertions(+), 139 deletions(-) diff --git a/lldb/tools/lldb-dap/Handler/BreakpointLocationsHandler.cpp b/lldb/tools/lldb-dap/Handler/BreakpointLocationsHandler.cpp index 7a477f3e97875..2ac886c3a5d2c 100644 --- a/lldb/tools/lldb-dap/Handler/BreakpointLocationsHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/BreakpointLocationsHandler.cpp @@ -9,136 +9,22 @@ #include "DAP.h" #include "JSONUtils.h" #include "RequestHandler.h" +#include namespace lldb_dap { -// "BreakpointLocationsRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "The `breakpointLocations` request returns all possible -// locations for source breakpoints in a given range.\nClients should only -// call this request if the corresponding capability -// `supportsBreakpointLocationsRequest` is true.", -// "properties": { -// "command": { -// "type": "string", -// "enum": [ "breakpointLocations" ] -// }, -// "arguments": { -// "$ref": "#/definitions/BreakpointLocationsArguments" -// } -// }, -// "required": [ "command" ] -// }] -// }, -// "BreakpointLocationsArguments": { -// "type": "object", -// "description": "Arguments for `breakpointLocations` request.", -// "properties": { -// "source": { -// "$ref": "#/definitions/Source", -// "description": "The source location of the breakpoints; either -// `source.path` or `source.sourceReference` must be specified." -// }, -// "line": { -// "type": "integer", -// "description": "Start line of range to search possible breakpoint -// locations in. If only the line is specified, the request returns all -// possible locations in that line." -// }, -// "column": { -// "type": "integer", -// "description": "Start position within `line` to search possible -// breakpoint locations in. It is measured in UTF-16 code units and the -// client capability `columnsStartAt1` determines whether it is 0- or -// 1-based. If no column is given, the first position in the start line is -// assumed." -// }, -// "endLine": { -// "type": "integer", -// "description": "End line of range to search possible breakpoint -// locations in. If no end line is given, then the end line is assumed to -// be the start line." -// }, -// "endColumn": { -// "type": "integer", -// "description": "End position within `endLine` to search possible -// breakpoint locations in. It is measured in UTF-16 code units and the -// client capability `columnsStartAt1` determines whether it is 0- or -// 1-based. If no end column is given, the last position in the end line -// is assumed." -// } -// }, -// "required": [ "source", "line" ] -// }, -// "BreakpointLocationsResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to `breakpointLocations` request.\nContains -// possible locations for source breakpoints.", -// "properties": { -// "body": { -// "type": "object", -// "properties": { -// "breakpoints": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/BreakpointLocation" -// }, -// "description": "Sorted set of possible breakpoint locations." -// } -// }, -// "required": [ "breakpoints" ] -// } -// }, -// "required": [ "body" ] -// }] -// }, -// "BreakpointLocation": { -// "type": "object", -// "description": "Properties of a breakpoint location returned from the -// `breakpointLocations` request.", -// "properties": { -// "line": { -// "type": "integer", -// "description": "Start line of breakpoint location." -// }, -// "column": { -// "type": "integer", -// "description": "The start position of a breakpoint location. Position -// is measured in UTF-16 code units and the client capability -// `columnsStartAt1` determines whether it is 0- or 1-based." -// }, -// "endLine": { -// "type": "integer", -// "description": "The end line of breakpoint location if the location -// covers a range." -// }, -// "endColumn": { -// "type": "integer", -// "description": "The end position of a breakpoint location (if the -// location covers a range). Position is measured in UTF-16 code units and -// the client capability `columnsStartAt1` determines whether it is 0- or -// 1-based." -// } -// }, -// "required": [ "line" ] -// }, -void BreakpointLocationsRequestHandler::operator()( - const llvm::json::Object &request) const { - llvm::json::Object response; - FillResponse(request, response); - auto *arguments = request.getObject("arguments"); - auto *source = arguments->getObject("source"); - std::string path = GetString(source, "path").value_or("").str(); - const auto start_line = GetInteger(arguments, "line") - .value_or(LLDB_INVALID_LINE_NUMBER); - const auto start_column = GetInteger(arguments, "column") - .value_or(LLDB_INVALID_COLUMN_NUMBER); - const auto end_line = - GetInteger(arguments, "endLine").value_or(start_line); - const auto end_column = GetInteger(arguments, "endColumn") - .value_or(std::numeric_limits::max()); +/// The `breakpointLocations` request returns all possible locations for source +/// breakpoints in a given range. Clients should only call this request if the +/// corresponding capability `supportsBreakpointLocationsRequest` is true. +llvm::Expected +BreakpointLocationsRequestHandler::Run( + const protocol::BreakpointLocationsArguments &args) const { + std::string path = args.source.path.value_or(""); + uint32_t start_line = args.line; + uint32_t start_column = args.column.value_or(LLDB_INVALID_COLUMN_NUMBER); + uint32_t end_line = args.endLine.value_or(start_line); + uint32_t end_column = + args.endColumn.value_or(std::numeric_limits::max()); lldb::SBFileSpec file_spec(path.c_str(), true); lldb::SBSymbolContextList compile_units = @@ -191,18 +77,16 @@ void BreakpointLocationsRequestHandler::operator()( std::sort(locations.begin(), locations.end()); locations.erase(llvm::unique(locations), locations.end()); - llvm::json::Array locations_json; + std::vector breakpoint_locations; for (auto &l : locations) { - llvm::json::Object location; - location.try_emplace("line", l.first); - location.try_emplace("column", l.second); - locations_json.emplace_back(std::move(location)); + protocol::BreakpointLocation lc; + lc.line = l.first; + lc.column = l.second; + breakpoint_locations.push_back(std::move(lc)); } - llvm::json::Object body; - body.try_emplace("breakpoints", std::move(locations_json)); - response.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(response))); + return protocol::BreakpointLocationsResponseBody{ + /*breakpoints=*/std::move(breakpoint_locations)}; } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index 25534b5675e45..841857375759a 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.h +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h @@ -204,14 +204,18 @@ class AttachRequestHandler : public LegacyRequestHandler { void operator()(const llvm::json::Object &request) const override; }; -class BreakpointLocationsRequestHandler : public LegacyRequestHandler { +class BreakpointLocationsRequestHandler + : public RequestHandler< + protocol::BreakpointLocationsArguments, + llvm::Expected> { public: - using LegacyRequestHandler::LegacyRequestHandler; + using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "breakpointLocations"; } FeatureSet GetSupportedFeatures() const override { return {protocol::eAdapterFeatureBreakpointLocationsRequest}; } - void operator()(const llvm::json::Object &request) const override; + llvm::Expected + Run(const protocol::BreakpointLocationsArguments &args) const override; }; class CompletionsRequestHandler : public LegacyRequestHandler { diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp index 950e8d17e3489..4e471be2876c4 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp @@ -245,6 +245,23 @@ bool fromJSON(const json::Value &Params, Configuration &C, json::Path P) { parseSourceMap(Params, C.sourceMap, P); } +bool fromJSON(const json::Value &Params, BreakpointLocationsArguments &BLA, + json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("source", BLA.source) && O.map("line", BLA.line) && + O.mapOptional("column", BLA.column) && + O.mapOptional("endLine", BLA.endLine) && + O.mapOptional("endColumn", BLA.endColumn); +} + +llvm::json::Value toJSON(const BreakpointLocationsResponseBody &BLRB) { + llvm::json::Array breakpoints_json; + for (const auto &breakpoint : BLRB.breakpoints) { + breakpoints_json.push_back(toJSON(breakpoint)); + } + return llvm::json::Object{{"breakpoints", std::move(breakpoints_json)}}; +} + bool fromJSON(const json::Value &Params, LaunchRequestArguments &LRA, json::Path P) { json::ObjectMapper O(Params, P); diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h index 18222d61f9a14..ee83891c0a616 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h @@ -30,6 +30,7 @@ #include #include #include +#include namespace lldb_dap::protocol { @@ -468,6 +469,43 @@ bool fromJSON(const llvm::json::Value &, StepOutArguments &, llvm::json::Path); /// body field is required. using StepOutResponse = VoidResponse; +/// Arguments for `breakpointLocations` request. +struct BreakpointLocationsArguments { + /// The source location of the breakpoints; either `source.path` or + /// `source.sourceReference` must be specified. + Source source; + + /// Start line of range to search possible breakpoint locations in. If only + /// the line is specified, the request returns all possible locations in that + /// line. + uint32_t line; + + /// Start position within `line` to search possible breakpoint locations in. + /// It is measured in UTF-16 code units and the client capability + /// `columnsStartAt1` determines whether it is 0- or 1-based. If no column is + /// given, the first position in the start line is assumed. + std::optional column; + + /// End line of range to search possible breakpoint locations in. If no end + /// line is given, then the end line is assumed to be the start line. + std::optional endLine; + + /// End position within `endLine` to search possible breakpoint locations in. + /// It is measured in UTF-16 code units and the client capability + /// `columnsStartAt1` determines whether it is 0- or 1-based. If no end column + /// is given, the last position in the end line is assumed. + std::optional endColumn; +}; +bool fromJSON(const llvm::json::Value &, BreakpointLocationsArguments &, + llvm::json::Path); + +/// Response to `breakpointLocations` request. +struct BreakpointLocationsResponseBody { + /// Content of the source reference. + std::vector breakpoints; +}; +llvm::json::Value toJSON(const BreakpointLocationsResponseBody &); + } // namespace lldb_dap::protocol #endif diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp index 8f4defb6fd2ea..967c1d2321502 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp @@ -260,4 +260,18 @@ bool fromJSON(const llvm::json::Value &Params, ValueFormat &VF, return O && O.mapOptional("hex", VF.hex); } +json::Value toJSON(const BreakpointLocation &B) { + json::Object result; + + result.insert({"line", B.line}); + if (B.column) + result.insert({"column", *B.column}); + if (B.endLine) + result.insert({"endLine", *B.endLine}); + if (B.endColumn) + result.insert({"endColumn", *B.endColumn}); + + return result; +} + } // namespace lldb_dap::protocol diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h index ae1ba90d1666a..8ea483d810e02 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h @@ -329,6 +329,27 @@ struct ValueFormat { }; bool fromJSON(const llvm::json::Value &, ValueFormat &, llvm::json::Path); +/// Properties of a breakpoint location returned from the `breakpointLocations` +/// request. +struct BreakpointLocation { + /// Start line of breakpoint location. + uint32_t line; + + /// The start position of a breakpoint location. Position is measured in + /// UTF-16 code units and the client capability `columnsStartAt1` determines + /// whether it is 0- or 1-based. + std::optional column; + + /// The end line of breakpoint location if the location covers a range. + std::optional endLine; + + /// The end position of a breakpoint location (if the location covers a + /// range). Position is measured in UTF-16 code units and the client + /// capability `columnsStartAt1` determines whether it is 0- or 1-based. + std::optional endColumn; +}; +llvm::json::Value toJSON(const BreakpointLocation &); + } // namespace lldb_dap::protocol #endif diff --git a/llvm/include/llvm/Support/JSON.h b/llvm/include/llvm/Support/JSON.h index 7f7f5f6228763..f1f4f4db709dd 100644 --- a/llvm/include/llvm/Support/JSON.h +++ b/llvm/include/llvm/Support/JSON.h @@ -776,6 +776,14 @@ inline bool fromJSON(const Value &E, bool &Out, Path P) { P.report("expected boolean"); return false; } +inline bool fromJSON(const Value &E, unsigned int &Out, Path P) { + if (auto S = E.getAsInteger()) { + Out = *S; + return true; + } + P.report("expected integer"); + return false; +} inline bool fromJSON(const Value &E, uint64_t &Out, Path P) { if (auto S = E.getAsUINT64()) { Out = *S; From lldb-commits at lists.llvm.org Thu May 8 13:26:56 2025 From: lldb-commits at lists.llvm.org (Ely Ronnen via lldb-commits) Date: Thu, 08 May 2025 13:26:56 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Expose QueueThreadPlanForStepSingleInstruction function to SBThreadPlan (PR #137904) In-Reply-To: Message-ID: <681d1390.170a0220.3dc054.88fe@mx.google.com> eronnen wrote: @felipepiovezan Sorry for the trouble, do you know if there's a way for me to run the CI on arm/aarch64 before merging the PR next time? https://github.com/llvm/llvm-project/pull/137904 From lldb-commits at lists.llvm.org Thu May 8 14:12:52 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 14:12:52 -0700 (PDT) Subject: [Lldb-commits] [lldb] 0389640 - [lldb-dap] Migrate attach to typed RequestHandler. (#137911) Message-ID: <681d1e54.a70a0220.145002.b473@mx.google.com> Author: John Harrison Date: 2025-05-08T14:12:49-07:00 New Revision: 03896403d3bf330c8163aa9ae3fe2aa284e273be URL: https://github.com/llvm/llvm-project/commit/03896403d3bf330c8163aa9ae3fe2aa284e273be DIFF: https://github.com/llvm/llvm-project/commit/03896403d3bf330c8163aa9ae3fe2aa284e273be.diff LOG: [lldb-dap] Migrate attach to typed RequestHandler. (#137911) This updates the `attach` request to the typed `RequestHandler`. Added a few more overlapping configurations to `lldb_dap::protocol::Configuration` that are shared between launching and attaching. There may be some additional code we could clean-up that is no longer referenced now that this has migrated to use well defined types. Added: Modified: lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py lldb/tools/lldb-dap/DAP.cpp lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp lldb/tools/lldb-dap/Handler/RequestHandler.cpp lldb/tools/lldb-dap/Handler/RequestHandler.h lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp lldb/tools/lldb-dap/Protocol/ProtocolRequests.h lldb/tools/lldb-dap/package.json Removed: ################################################################################ diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py index b5569642f9d34..a9218d3c3dde3 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py @@ -78,7 +78,7 @@ def test_by_name(self): def test_by_name_waitFor(self): """ Tests attaching to a process by process name and waiting for the - next instance of a process to be launched, ingoring all current + next instance of a process to be launched, ignoring all current ones. """ program = self.build_and_create_debug_adapter_for_attach() @@ -101,7 +101,7 @@ def test_commands(self): that can be passed during attach. "initCommands" are a list of LLDB commands that get executed - before the targt is created. + before the target is created. "preRunCommands" are a list of LLDB commands that get executed after the target has been created and before the launch. "stopCommands" are a list of LLDB commands that get executed each @@ -179,6 +179,24 @@ def test_commands(self): self.verify_commands("exitCommands", output, exitCommands) self.verify_commands("terminateCommands", output, terminateCommands) + def test_attach_command_process_failures(self): + """ + Tests that a 'attachCommands' is expected to leave the debugger's + selected target with a valid process. + """ + program = self.build_and_create_debug_adapter_for_attach() + attachCommands = ['script print("oops, forgot to attach to a process...")'] + resp = self.attach( + program=program, + attachCommands=attachCommands, + expectFailure=True, + ) + self.assertFalse(resp["success"]) + self.assertIn( + "attachCommands failed to attach to a process", + resp["body"]["error"]["format"], + ) + @skipIfNetBSD # Hangs on NetBSD as well @skipIf( archs=["arm", "aarch64"] diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py index acbe0366d1ecc..7c85f05c1ba45 100644 --- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py +++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py @@ -56,7 +56,7 @@ def test_failing_launch_commands_and_run_in_terminal(self): self.assertFalse(response["success"]) self.assertTrue(self.get_dict_value(response, ["body", "error", "showUser"])) self.assertEqual( - "launchCommands and runInTerminal are mutually exclusive", + "'launchCommands' and 'runInTerminal' are mutually exclusive", self.get_dict_value(response, ["body", "error", "format"]), ) diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 62c60cc3a9b3b..1b118bb8c6a2b 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -676,10 +676,10 @@ lldb::SBTarget DAP::CreateTarget(lldb::SBError &error) { // omitted at all), so it is good to leave the user an opportunity to specify // those. Any of those three can be left empty. auto target = this->debugger.CreateTarget( - configuration.program.value_or("").data(), - configuration.targetTriple.value_or("").data(), - configuration.platformName.value_or("").data(), - true, // Add dependent modules. + /*filename=*/configuration.program.data(), + /*target_triple=*/configuration.targetTriple.data(), + /*platform_name=*/configuration.platformName.data(), + /*add_dependent_modules=*/true, // Add dependent modules. error); return target; @@ -1203,7 +1203,7 @@ bool SendEventRequestHandler::DoExecute(lldb::SBDebugger debugger, } void DAP::ConfigureSourceMaps() { - if (configuration.sourceMap.empty() && !configuration.sourcePath) + if (configuration.sourceMap.empty() && configuration.sourcePath.empty()) return; std::string sourceMapCommand; @@ -1214,8 +1214,8 @@ void DAP::ConfigureSourceMaps() { for (const auto &kv : configuration.sourceMap) { strm << "\"" << kv.first << "\" \"" << kv.second << "\" "; } - } else if (configuration.sourcePath) { - strm << "\".\" \"" << *configuration.sourcePath << "\""; + } else if (!configuration.sourcePath.empty()) { + strm << "\".\" \"" << configuration.sourcePath << "\""; } RunLLDBCommands("Setting source map:", {sourceMapCommand}); @@ -1224,6 +1224,7 @@ void DAP::ConfigureSourceMaps() { void DAP::SetConfiguration(const protocol::Configuration &config, bool is_attach) { configuration = config; + stop_at_entry = config.stopOnEntry; this->is_attach = is_attach; if (configuration.customFrameFormat) @@ -1243,8 +1244,6 @@ void DAP::SetConfigurationDone() { } void DAP::SetFrameFormat(llvm::StringRef format) { - if (format.empty()) - return; lldb::SBError error; frame_format = lldb::SBFormat(format.str().c_str(), error); if (error.Fail()) { @@ -1257,8 +1256,6 @@ void DAP::SetFrameFormat(llvm::StringRef format) { } void DAP::SetThreadFormat(llvm::StringRef format) { - if (format.empty()) - return; lldb::SBError error; thread_format = lldb::SBFormat(format.str().c_str(), error); if (error.Fail()) { diff --git a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp index 5dc9c3f9772e3..0293ffbd0c922 100644 --- a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp @@ -10,183 +10,138 @@ #include "EventHelper.h" #include "JSONUtils.h" #include "LLDBUtils.h" +#include "Protocol/ProtocolRequests.h" #include "RequestHandler.h" +#include "lldb/API/SBAttachInfo.h" #include "lldb/API/SBListener.h" +#include "lldb/lldb-defines.h" +#include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" +using namespace llvm; +using namespace lldb_dap::protocol; + namespace lldb_dap { -// "AttachRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "Attach request; value of command field is 'attach'.", -// "properties": { -// "command": { -// "type": "string", -// "enum": [ "attach" ] -// }, -// "arguments": { -// "$ref": "#/definitions/AttachRequestArguments" -// } -// }, -// "required": [ "command", "arguments" ] -// }] -// }, -// "AttachRequestArguments": { -// "type": "object", -// "description": "Arguments for 'attach' request.\nThe attach request has no -// standardized attributes." -// }, -// "AttachResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to 'attach' request. This is just an -// acknowledgement, so no body field is required." -// }] -// } -void AttachRequestHandler::operator()(const llvm::json::Object &request) const { - dap.is_attach = true; - llvm::json::Object response; - lldb::SBError error; - FillResponse(request, response); - const int invalid_port = 0; - const auto *arguments = request.getObject("arguments"); - const lldb::pid_t pid = - GetInteger(arguments, "pid").value_or(LLDB_INVALID_PROCESS_ID); - const auto gdb_remote_port = - GetInteger(arguments, "gdb-remote-port").value_or(invalid_port); - const auto gdb_remote_hostname = - GetString(arguments, "gdb-remote-hostname").value_or("localhost"); - const auto wait_for = GetBoolean(arguments, "waitFor").value_or(false); - dap.configuration.initCommands = GetStrings(arguments, "initCommands"); - dap.configuration.preRunCommands = GetStrings(arguments, "preRunCommands"); - dap.configuration.postRunCommands = GetStrings(arguments, "postRunCommands"); - dap.configuration.stopCommands = GetStrings(arguments, "stopCommands"); - dap.configuration.exitCommands = GetStrings(arguments, "exitCommands"); - dap.configuration.terminateCommands = - GetStrings(arguments, "terminateCommands"); - auto attachCommands = GetStrings(arguments, "attachCommands"); - llvm::StringRef core_file = GetString(arguments, "coreFile").value_or(""); - const uint64_t timeout_seconds = - GetInteger(arguments, "timeout").value_or(30); - dap.stop_at_entry = core_file.empty() - ? GetBoolean(arguments, "stopOnEntry").value_or(false) - : true; - const llvm::StringRef debuggerRoot = - GetString(arguments, "debuggerRoot").value_or(""); - dap.configuration.enableAutoVariableSummaries = - GetBoolean(arguments, "enableAutoVariableSummaries").value_or(false); - dap.configuration.enableSyntheticChildDebugging = - GetBoolean(arguments, "enableSyntheticChildDebugging").value_or(false); - dap.configuration.displayExtendedBacktrace = - GetBoolean(arguments, "displayExtendedBacktrace").value_or(false); - dap.configuration.commandEscapePrefix = - GetString(arguments, "commandEscapePrefix").value_or("`"); - dap.configuration.program = GetString(arguments, "program"); - dap.configuration.targetTriple = GetString(arguments, "targetTriple"); - dap.configuration.platformName = GetString(arguments, "platformName"); - dap.SetFrameFormat(GetString(arguments, "customFrameFormat").value_or("")); - dap.SetThreadFormat(GetString(arguments, "customThreadFormat").value_or("")); +/// The `attach` request is sent from the client to the debug adapter to attach +/// to a debuggee that is already running. +/// +/// Since attaching is debugger/runtime specific, the arguments for this request +/// are not part of this specification. +Error AttachRequestHandler::Run(const AttachRequestArguments &args) const { + // Validate that we have a well formed attach request. + if (args.attachCommands.empty() && args.coreFile.empty() && + args.configuration.program.empty() && + args.pid == LLDB_INVALID_PROCESS_ID && + args.gdbRemotePort == LLDB_DAP_INVALID_PORT) + return make_error( + "expected one of 'pid', 'program', 'attachCommands', " + "'coreFile' or 'gdb-remote-port' to be specified"); + + // Check if we have mutually exclusive arguments. + if ((args.pid != LLDB_INVALID_PROCESS_ID) && + (args.gdbRemotePort != LLDB_DAP_INVALID_PORT)) + return make_error( + "'pid' and 'gdb-remote-port' are mutually exclusive"); + + dap.SetConfiguration(args.configuration, /*is_attach=*/true); + if (!args.coreFile.empty()) + dap.stop_at_entry = true; PrintWelcomeMessage(); // This is a hack for loading DWARF in .o files on Mac where the .o files - // in the debug map of the main executable have relative paths which require - // the lldb-dap binary to have its working directory set to that relative - // root for the .o files in order to be able to load debug info. - if (!debuggerRoot.empty()) - llvm::sys::fs::set_current_path(debuggerRoot); + // in the debug map of the main executable have relative paths which + // require the lldb-dap binary to have its working directory set to that + // relative root for the .o files in order to be able to load debug info. + if (!dap.configuration.debuggerRoot.empty()) + sys::fs::set_current_path(dap.configuration.debuggerRoot); // Run any initialize LLDB commands the user specified in the launch.json - if (llvm::Error err = dap.RunInitCommands()) { - response["success"] = false; - EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; - } + if (llvm::Error err = dap.RunInitCommands()) + return err; - SetSourceMapFromArguments(*arguments); + dap.ConfigureSourceMaps(); - lldb::SBError status; - dap.SetTarget(dap.CreateTarget(status)); - if (status.Fail()) { - response["success"] = llvm::json::Value(false); - EmplaceSafeString(response, "message", status.GetCString()); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; - } + lldb::SBError error; + lldb::SBTarget target = dap.CreateTarget(error); + if (error.Fail()) + return ToError(error); + + dap.SetTarget(target); // Run any pre run LLDB commands the user specified in the launch.json - if (llvm::Error err = dap.RunPreRunCommands()) { - response["success"] = false; - EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; - } + if (Error err = dap.RunPreRunCommands()) + return err; - if ((pid == LLDB_INVALID_PROCESS_ID || gdb_remote_port == invalid_port) && - wait_for) { - char attach_msg[256]; - auto attach_msg_len = snprintf(attach_msg, sizeof(attach_msg), - "Waiting to attach to \"%s\"...", - dap.target.GetExecutable().GetFilename()); + if ((args.pid == LLDB_INVALID_PROCESS_ID || + args.gdbRemotePort == LLDB_DAP_INVALID_PORT) && + args.waitFor) { dap.SendOutput(OutputType::Console, - llvm::StringRef(attach_msg, attach_msg_len)); + llvm::formatv("Waiting to attach to \"{0}\"...", + dap.target.GetExecutable().GetFilename()) + .str()); } { // Perform the launch in synchronous mode so that we don't have to worry // about process state changes during the launch. ScopeSyncMode scope_sync_mode(dap.debugger); - if (attachCommands.empty()) { - // No "attachCommands", just attach normally. - if (core_file.empty()) { - if ((pid != LLDB_INVALID_PROCESS_ID) && - (gdb_remote_port != invalid_port)) { - // If both pid and port numbers are specified. - error.SetErrorString("The user can't specify both pid and port"); - } else if (gdb_remote_port != invalid_port) { - // If port is specified and pid is not. - lldb::SBListener listener = dap.debugger.GetListener(); - - // If the user hasn't provided the hostname property, default - // localhost being used. - std::string connect_url = - llvm::formatv("connect://{0}:", gdb_remote_hostname); - connect_url += std::to_string(gdb_remote_port); - dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", - error); - } else { - // Attach by pid or process name. - lldb::SBAttachInfo attach_info; - if (pid != LLDB_INVALID_PROCESS_ID) - attach_info.SetProcessID(pid); - else if (dap.configuration.program.has_value()) - attach_info.SetExecutable(dap.configuration.program->data()); - attach_info.SetWaitForLaunch(wait_for, false /*async*/); - dap.target.Attach(attach_info, error); - } - } else { - dap.target.LoadCore(core_file.data(), error); - } - } else { - // We have "attachCommands" that are a set of commands that are expected - // to execute the commands after which a process should be created. If - // there is no valid process after running these commands, we have failed. - if (llvm::Error err = dap.RunAttachCommands(attachCommands)) { - response["success"] = false; - EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; - } - // The custom commands might have created a new target so we should use - // the selected target after these commands are run. + + if (!args.attachCommands.empty()) { + // Run the attach commands, after which we expect the debugger's selected + // target to contain a valid and stopped process. Otherwise inform the + // user that their command failed or the debugger is in an unexpected + // state. + if (llvm::Error err = dap.RunAttachCommands(args.attachCommands)) + return err; + dap.target = dap.debugger.GetSelectedTarget(); + + // Validate the attachCommand results. + if (!dap.target.GetProcess().IsValid()) + return make_error( + "attachCommands failed to attach to a process"); + } else if (!args.coreFile.empty()) { + dap.target.LoadCore(args.coreFile.data(), error); + } else if (args.gdbRemotePort != LLDB_DAP_INVALID_PORT) { + lldb::SBListener listener = dap.debugger.GetListener(); + + // If the user hasn't provided the hostname property, default + // localhost being used. + std::string connect_url = + llvm::formatv("connect://{0}:", args.gdbRemoteHostname); + connect_url += std::to_string(args.gdbRemotePort); + dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", + error); + } else { + // Attach by pid or process name. + lldb::SBAttachInfo attach_info; + if (args.pid != LLDB_INVALID_PROCESS_ID) + attach_info.SetProcessID(args.pid); + else if (!dap.configuration.program.empty()) + attach_info.SetExecutable(dap.configuration.program.data()); + attach_info.SetWaitForLaunch(args.waitFor, /*async=*/false); + dap.target.Attach(attach_info, error); } } // Make sure the process is attached and stopped. - error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds)); + error = dap.WaitForProcessToStop(args.configuration.timeout); + if (error.Fail()) + return ToError(error); + + if (args.coreFile.empty() && !dap.target.GetProcess().IsValid()) + return make_error("failed to attach to process"); + + dap.RunPostRunCommands(); + + return Error::success(); +} + +void AttachRequestHandler::PostRun() const { + if (!dap.target.GetProcess().IsValid()) + return; // Clients can request a baseline of currently existing threads after // we acknowledge the configurationDone request. @@ -197,36 +152,12 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { dap.initial_thread_list = GetThreads(dap.target.GetProcess(), dap.thread_format); - if (error.Success() && core_file.empty()) { - auto attached_pid = dap.target.GetProcess().GetProcessID(); - if (attached_pid == LLDB_INVALID_PROCESS_ID) { - if (attachCommands.empty()) - error.SetErrorString("failed to attach to a process"); - else - error.SetErrorString("attachCommands failed to attach to a process"); - } - } - - if (error.Fail()) { - response["success"] = llvm::json::Value(false); - EmplaceSafeString(response, "message", std::string(error.GetCString())); - } else { - dap.RunPostRunCommands(); - } - - dap.SendJSON(llvm::json::Value(std::move(response))); + SendProcessEvent(dap, Attach); - // FIXME: Move this into PostRun. - if (error.Success()) { - if (dap.target.GetProcess().IsValid()) { - SendProcessEvent(dap, Attach); - - if (dap.stop_at_entry) - SendThreadStoppedEvent(dap); - else - dap.target.GetProcess().Continue(); - } - } + if (dap.stop_at_entry) + SendThreadStoppedEvent(dap); + else + dap.target.GetProcess().Continue(); } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp index aa947d3cb5ab9..6d1e2c7402f4c 100644 --- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp @@ -262,7 +262,7 @@ static void EventThreadFunction(DAP &dap) { } /// Initialize request; value of command field is 'initialize'. -llvm::Expected InitializeRequestHandler::Run( +llvm::Expected InitializeRequestHandler::Run( const InitializeRequestArguments &arguments) const { dap.clientFeatures = arguments.supportedFeatures; diff --git a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp index 7e0e76935dd02..22d1a090187d8 100644 --- a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp @@ -22,13 +22,13 @@ namespace lldb_dap { /// Launch request; value of command field is 'launch'. Error LaunchRequestHandler::Run(const LaunchRequestArguments &arguments) const { - dap.SetConfiguration(arguments.configuration, /*is_attach=*/false); - dap.last_launch_request = arguments; - dap.stop_at_entry = arguments.stopOnEntry; - + // Validate that we have a well formed launch request. if (!arguments.launchCommands.empty() && arguments.runInTerminal) return make_error( - "launchCommands and runInTerminal are mutually exclusive"); + "'launchCommands' and 'runInTerminal' are mutually exclusive"); + + dap.SetConfiguration(arguments.configuration, /*is_attach=*/false); + dap.last_launch_request = arguments; PrintWelcomeMessage(); @@ -36,9 +36,8 @@ Error LaunchRequestHandler::Run(const LaunchRequestArguments &arguments) const { // in the debug map of the main executable have relative paths which // require the lldb-dap binary to have its working directory set to that // relative root for the .o files in order to be able to load debug info. - const std::string debugger_root = dap.configuration.debuggerRoot.value_or(""); - if (!debugger_root.empty()) - sys::fs::set_current_path(debugger_root); + if (!dap.configuration.debuggerRoot.empty()) + sys::fs::set_current_path(dap.configuration.debuggerRoot); // Run any initialize LLDB commands the user specified in the launch.json. // This is run before target is created, so commands can't do anything with @@ -68,15 +67,25 @@ Error LaunchRequestHandler::Run(const LaunchRequestArguments &arguments) const { } void LaunchRequestHandler::PostRun() const { - if (dap.target.GetProcess().IsValid()) { - // Attach happens when launching with runInTerminal. - SendProcessEvent(dap, dap.is_attach ? Attach : Launch); - - if (dap.stop_at_entry) - SendThreadStoppedEvent(dap); - else - dap.target.GetProcess().Continue(); - } + if (!dap.target.GetProcess().IsValid()) + return; + + // Clients can request a baseline of currently existing threads after + // we acknowledge the configurationDone request. + // Client requests the baseline of currently existing threads after + // a successful or attach by sending a 'threads' request + // right after receiving the configurationDone response. + // Obtain the list of threads before we resume the process + dap.initial_thread_list = + GetThreads(dap.target.GetProcess(), dap.thread_format); + + // Attach happens when launching with runInTerminal. + SendProcessEvent(dap, dap.is_attach ? Attach : Launch); + + if (dap.stop_at_entry) + SendThreadStoppedEvent(dap); + else + dap.target.GetProcess().Continue(); } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp index 282c5f4ab15a5..93bc80a38e29d 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp @@ -51,56 +51,6 @@ static uint32_t SetLaunchFlag(uint32_t flags, bool flag, return flags; } -// Both attach and launch take either a sourcePath or a sourceMap -// argument (or neither), from which we need to set the target.source-map. -void BaseRequestHandler::SetSourceMapFromArguments( - const llvm::json::Object &arguments) const { - const char *sourceMapHelp = - "source must be be an array of two-element arrays, " - "each containing a source and replacement path string.\n"; - - std::string sourceMapCommand; - llvm::raw_string_ostream strm(sourceMapCommand); - strm << "settings set target.source-map "; - const auto sourcePath = GetString(arguments, "sourcePath").value_or(""); - - // sourceMap is the new, more general form of sourcePath and overrides it. - constexpr llvm::StringRef sourceMapKey = "sourceMap"; - - if (const auto *sourceMapArray = arguments.getArray(sourceMapKey)) { - for (const auto &value : *sourceMapArray) { - const auto *mapping = value.getAsArray(); - if (mapping == nullptr || mapping->size() != 2 || - (*mapping)[0].kind() != llvm::json::Value::String || - (*mapping)[1].kind() != llvm::json::Value::String) { - dap.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp)); - return; - } - const auto mapFrom = GetAsString((*mapping)[0]); - const auto mapTo = GetAsString((*mapping)[1]); - strm << "\"" << mapFrom << "\" \"" << mapTo << "\" "; - } - } else if (const auto *sourceMapObj = arguments.getObject(sourceMapKey)) { - for (const auto &[key, value] : *sourceMapObj) { - if (value.kind() == llvm::json::Value::String) { - strm << "\"" << key.str() << "\" \"" << GetAsString(value) << "\" "; - } - } - } else { - if (ObjectContainsKey(arguments, sourceMapKey)) { - dap.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp)); - return; - } - if (sourcePath.empty()) - return; - // Do any source remapping needed before we create our targets - strm << "\".\" \"" << sourcePath << "\""; - } - if (!sourceMapCommand.empty()) { - dap.RunLLDBCommands("Setting source map:", {sourceMapCommand}); - } -} - static llvm::Error RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) { if (!dap.clientFeatures.contains( @@ -108,8 +58,7 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) { return llvm::make_error("Cannot use runInTerminal, feature is " "not supported by the connected client"); - if (!arguments.configuration.program || - arguments.configuration.program->empty()) + if (arguments.configuration.program.empty()) return llvm::make_error( "program must be set to when using runInTerminal"); @@ -130,8 +79,8 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) { #endif llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest( - *arguments.configuration.program, arguments.args, arguments.env, - arguments.cwd.value_or(""), comm_file.m_path, debugger_pid); + arguments.configuration.program, arguments.args, arguments.env, + arguments.cwd, comm_file.m_path, debugger_pid); dap.SendReverseRequest("runInTerminal", std::move(reverse_request)); @@ -211,9 +160,8 @@ llvm::Error BaseRequestHandler::LaunchProcess( // Grab the current working directory if there is one and set it in the // launch info. - const auto cwd = arguments.cwd.value_or(""); - if (!cwd.empty()) - launch_info.SetWorkingDirectory(cwd.data()); + if (!arguments.cwd.empty()) + launch_info.SetWorkingDirectory(arguments.cwd.data()); // Extract any extra arguments and append them to our program arguments for // when we launch @@ -251,7 +199,7 @@ llvm::Error BaseRequestHandler::LaunchProcess( lldb::SBError error; dap.target.Launch(launch_info, error); if (error.Fail()) - return llvm::make_error(error.GetCString()); + return ToError(error); } else { // Set the launch info so that run commands can access the configured // launch details. @@ -267,18 +215,10 @@ llvm::Error BaseRequestHandler::LaunchProcess( // Make sure the process is launched and stopped at the entry point before // proceeding. - lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout); + lldb::SBError error = + dap.WaitForProcessToStop(arguments.configuration.timeout); if (error.Fail()) - return llvm::make_error(error.GetCString()); - - // Clients can request a baseline of currently existing threads after - // we acknowledge the configurationDone request. - // Client requests the baseline of currently existing threads after - // a successful or attach by sending a 'threads' request - // right after receiving the configurationDone response. - // Obtain the list of threads before we resume the process - dap.initial_thread_list = - GetThreads(dap.target.GetProcess(), dap.thread_format); + return ToError(error); return llvm::Error::success(); } diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index 25534b5675e45..948a29a005aa7 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.h +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h @@ -59,10 +59,6 @@ class BaseRequestHandler { /// FIXME: Move these into the DAP class? /// @{ - /// Both attach and launch take a either a sourcePath or sourceMap - /// argument (or neither), from which we need to set the target.source-map. - void SetSourceMapFromArguments(const llvm::json::Object &arguments) const; - /// Prints a welcome message on the editor if the preprocessor variable /// LLDB_DAP_WELCOME_MESSAGE is defined. void PrintWelcomeMessage() const; @@ -172,6 +168,9 @@ class RequestHandler : public BaseRequestHandler { /// A hook for a request handler to run additional operations after the /// request response is sent but before the next request handler. + /// + /// *NOTE*: PostRun will be invoked even if the `Run` operation returned an + /// error. virtual void PostRun() const {}; protocol::ErrorResponseBody ToResponse(llvm::Error err) const { @@ -197,11 +196,14 @@ class RequestHandler : public BaseRequestHandler { } }; -class AttachRequestHandler : public LegacyRequestHandler { +class AttachRequestHandler + : public RequestHandler { public: - using LegacyRequestHandler::LegacyRequestHandler; + using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "attach"; } - void operator()(const llvm::json::Object &request) const override; + llvm::Error Run(const protocol::AttachRequestArguments &args) const override; + void PostRun() const override; }; class BreakpointLocationsRequestHandler : public LegacyRequestHandler { @@ -279,18 +281,18 @@ class ExceptionInfoRequestHandler : public LegacyRequestHandler { class InitializeRequestHandler : public RequestHandler> { + llvm::Expected> { public: using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "initialize"; } - llvm::Expected + llvm::Expected Run(const protocol::InitializeRequestArguments &args) const override; void PostRun() const override; }; class LaunchRequestHandler : public RequestHandler { + protocol::LaunchResponse> { public: using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "launch"; } @@ -508,9 +510,8 @@ class ReadMemoryRequestHandler : public LegacyRequestHandler { void operator()(const llvm::json::Object &request) const override; }; -class CancelRequestHandler - : public RequestHandler { +class CancelRequestHandler : public RequestHandler { public: using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "cancel"; } diff --git a/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp index 47fedf9dd779c..58091b622f7e5 100644 --- a/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp @@ -109,7 +109,6 @@ void RestartRequestHandler::operator()( // Update DAP configuration based on the latest copy of the launch // arguments. dap.SetConfiguration(updated_arguments.configuration, false); - dap.stop_at_entry = updated_arguments.stopOnEntry; dap.ConfigureSourceMaps(); } } diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp index 950e8d17e3489..cd507692f0c21 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp @@ -223,26 +223,29 @@ bool fromJSON(const json::Value &Params, InitializeRequestArguments &IRA, bool fromJSON(const json::Value &Params, Configuration &C, json::Path P) { json::ObjectMapper O(Params, P); - return O.map("debuggerRoot", C.debuggerRoot) && + return O.mapOptional("debuggerRoot", C.debuggerRoot) && O.mapOptional("enableAutoVariableSummaries", C.enableAutoVariableSummaries) && O.mapOptional("enableSyntheticChildDebugging", C.enableSyntheticChildDebugging) && O.mapOptional("displayExtendedBacktrace", C.displayExtendedBacktrace) && + O.mapOptional("stopOnEntry", C.stopOnEntry) && O.mapOptional("commandEscapePrefix", C.commandEscapePrefix) && - O.map("customFrameFormat", C.customFrameFormat) && - O.map("customThreadFormat", C.customThreadFormat) && - O.map("sourcePath", C.sourcePath) && + O.mapOptional("customFrameFormat", C.customFrameFormat) && + O.mapOptional("customThreadFormat", C.customThreadFormat) && + O.mapOptional("sourcePath", C.sourcePath) && O.mapOptional("initCommands", C.initCommands) && O.mapOptional("preRunCommands", C.preRunCommands) && O.mapOptional("postRunCommands", C.postRunCommands) && O.mapOptional("stopCommands", C.stopCommands) && O.mapOptional("exitCommands", C.exitCommands) && O.mapOptional("terminateCommands", C.terminateCommands) && - O.map("program", C.program) && O.map("targetTriple", C.targetTriple) && - O.map("platformName", C.platformName) && - parseSourceMap(Params, C.sourceMap, P); + O.mapOptional("program", C.program) && + O.mapOptional("targetTriple", C.targetTriple) && + O.mapOptional("platformName", C.platformName) && + parseSourceMap(Params, C.sourceMap, P) && + parseTimeout(Params, C.timeout, P); } bool fromJSON(const json::Value &Params, LaunchRequestArguments &LRA, @@ -251,14 +254,26 @@ bool fromJSON(const json::Value &Params, LaunchRequestArguments &LRA, return O && fromJSON(Params, LRA.configuration, P) && O.mapOptional("noDebug", LRA.noDebug) && O.mapOptional("launchCommands", LRA.launchCommands) && - O.map("cwd", LRA.cwd) && O.mapOptional("args", LRA.args) && + O.mapOptional("cwd", LRA.cwd) && O.mapOptional("args", LRA.args) && O.mapOptional("detachOnError", LRA.detachOnError) && O.mapOptional("disableASLR", LRA.disableASLR) && O.mapOptional("disableSTDIO", LRA.disableSTDIO) && O.mapOptional("shellExpandArguments", LRA.shellExpandArguments) && - O.mapOptional("stopOnEntry", LRA.stopOnEntry) && + O.mapOptional("runInTerminal", LRA.runInTerminal) && - parseEnv(Params, LRA.env, P) && parseTimeout(Params, LRA.timeout, P); + parseEnv(Params, LRA.env, P); +} + +bool fromJSON(const json::Value &Params, AttachRequestArguments &ARA, + json::Path P) { + json::ObjectMapper O(Params, P); + return O && fromJSON(Params, ARA.configuration, P) && + O.mapOptional("attachCommands", ARA.attachCommands) && + O.mapOptional("pid", ARA.pid) && + O.mapOptional("waitFor", ARA.waitFor) && + O.mapOptional("gdb-remote-port", ARA.gdbRemotePort) && + O.mapOptional("gdb-remote-hostname", ARA.gdbRemoteHostname) && + O.mapOptional("coreFile", ARA.coreFile); } bool fromJSON(const llvm::json::Value &Params, ContinueArguments &CA, diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h index 18222d61f9a14..acc3358736e70 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h @@ -23,6 +23,7 @@ #include "Protocol/ProtocolBase.h" #include "Protocol/ProtocolTypes.h" #include "lldb/lldb-defines.h" +#include "lldb/lldb-types.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/JSON.h" @@ -51,7 +52,7 @@ bool fromJSON(const llvm::json::Value &, CancelArguments &, llvm::json::Path); /// Response to `cancel` request. This is just an acknowledgement, so no body /// field is required. -using CancelResponseBody = VoidResponse; +using CancelResponse = VoidResponse; /// Arguments for `disconnect` request. struct DisconnectArguments { @@ -139,15 +140,18 @@ bool fromJSON(const llvm::json::Value &, InitializeRequestArguments &, llvm::json::Path); /// Response to `initialize` request. The capabilities of this debug adapter. -using InitializeResponseBody = std::optional; +using InitializeResponse = std::optional; /// DAP Launch and Attach common configurations. +/// +/// See package.json debuggers > configurationAttributes > launch or attach > +/// properties for common configurations. struct Configuration { /// Specify a working directory to use when launching `lldb-dap`. If the debug /// information in your executable contains relative paths, this option can be /// used so that `lldb-dap` can find source files and object files that have /// relative paths. - std::optional debuggerRoot; + std::string debuggerRoot; /// Enable auto generated summaries for variables when no summaries exist for /// a given type. This feature can cause performance delays in large projects @@ -156,13 +160,20 @@ struct Configuration { /// If a variable is displayed using a synthetic children, also display the /// actual contents of the variable at the end under a [raw] entry. This is - /// useful when creating sythetic child plug-ins as it lets you see the actual - /// contents of the variable. + /// useful when creating synthetic child plug-ins as it lets you see the + /// actual contents of the variable. bool enableSyntheticChildDebugging = false; /// Enable language specific extended backtraces. bool displayExtendedBacktrace = false; + /// Stop at the entry point of the program when launching or attaching. + bool stopOnEntry = false; + + /// Optional timeout when waiting for the program to `runInTerminal` or + /// attach. + std::chrono::seconds timeout = std::chrono::seconds(30); + /// The escape prefix to use for executing regular LLDB commands in the Debug /// Console, instead of printing variables. Defaults to a backtick. If it's an /// empty string, then all expression in the Debug Console are treated as @@ -183,7 +194,7 @@ struct Configuration { /// Specify a source path to remap "./" to allow full paths to be used when /// setting breakpoints in binaries that have relative source paths. - std::optional sourcePath; + std::string sourcePath; /// Specify an array of path re-mappings. Each element in the array must be a /// two element array containing a source and destination pathname. Overrides @@ -219,15 +230,15 @@ struct Configuration { /// /// *NOTE:* When launching, either `launchCommands` or `program` must be /// configured. If both are configured then `launchCommands` takes priority. - std::optional program; + std::string program; /// Target triple for the program (arch-vendor-os). If not set, inferred from /// the binary. - std::optional targetTriple; + std::string targetTriple; /// Specify name of the platform to use for this target, creating the platform /// if necessary. - std::optional platformName; + std::string platformName; }; /// lldb-dap specific launch arguments. @@ -240,6 +251,9 @@ struct LaunchRequestArguments { bool noDebug = false; /// Launch specific operations. + /// + /// See package.json debuggers > configurationAttributes > launch > + /// properties. /// @{ /// LLDB commands executed to launch the program. @@ -250,7 +264,7 @@ struct LaunchRequestArguments { std::vector launchCommands; /// The program working directory. - std::optional cwd; + std::string cwd; /// An array of command line argument strings to be passed to the program /// being launched. @@ -261,8 +275,8 @@ struct LaunchRequestArguments { /// with values or just "VAR" for environment variables with no values. llvm::StringMap env; - /// If set, then the client stub should detach rather than killing the debugee - /// if it loses connection with lldb. + /// If set, then the client stub should detach rather than killing the + /// debuggee if it loses connection with lldb. bool detachOnError = false; /// Disable ASLR (Address Space Layout Randomization) when launching the @@ -275,16 +289,10 @@ struct LaunchRequestArguments { /// Set whether to shell expand arguments to the process when launching. bool shellExpandArguments = false; - /// Stop at the entry point of the program when launching a process. - bool stopOnEntry = false; - /// Launch the program inside an integrated terminal in the IDE. Useful for /// debugging interactive command line programs. bool runInTerminal = false; - /// Optional timeout for `runInTerminal` requests. - std::chrono::seconds timeout = std::chrono::seconds(30); - /// @} }; bool fromJSON(const llvm::json::Value &, LaunchRequestArguments &, @@ -292,7 +300,52 @@ bool fromJSON(const llvm::json::Value &, LaunchRequestArguments &, /// Response to `launch` request. This is just an acknowledgement, so no body /// field is required. -using LaunchResponseBody = VoidResponse; +using LaunchResponse = VoidResponse; + +#define LLDB_DAP_INVALID_PORT -1 + +/// lldb-dap specific attach arguments. +struct AttachRequestArguments { + /// Common lldb-dap configuration values for launching/attaching operations. + Configuration configuration; + + /// Attach specific operations. + /// + /// See package.json debuggers > configurationAttributes > attach > + /// properties. + /// @{ + + /// Custom commands that are executed instead of attaching to a process ID or + /// to a process by name. These commands may optionally create a new target + /// and must perform an attach. A valid process must exist after these + /// commands complete or the `"attach"` will fail. + std::vector attachCommands; + + /// System process ID to attach to. + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + + /// Wait for the process to launch. + bool waitFor = false; + + /// TCP/IP port to attach to a remote system. Specifying both pid and port is + /// an error. + int32_t gdbRemotePort = LLDB_DAP_INVALID_PORT; + + /// The hostname to connect to a remote system. The default hostname being + /// used `localhost`. + std::string gdbRemoteHostname = "localhost"; + + /// Path to the core file to debug. + std::string coreFile; + + /// @} +}; +bool fromJSON(const llvm::json::Value &, AttachRequestArguments &, + llvm::json::Path); + +/// Response to `attach` request. This is just an acknowledgement, so no body +/// field is required. +using AttachResponse = VoidResponse; /// Arguments for `continue` request. struct ContinueArguments { diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json index f66badc2a930f..a7631464d236a 100644 --- a/lldb/tools/lldb-dap/package.json +++ b/lldb/tools/lldb-dap/package.json @@ -552,10 +552,7 @@ "description": "Path to the program to attach to." }, "pid": { - "type": [ - "number", - "string" - ], + "type": "number", "description": "System process ID to attach to." }, "waitFor": { From lldb-commits at lists.llvm.org Thu May 8 14:12:59 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Thu, 08 May 2025 14:12:59 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Migrate attach to typed RequestHandler. (PR #137911) In-Reply-To: Message-ID: <681d1e5b.050a0220.2ad712.a7e5@mx.google.com> https://github.com/ashgti closed https://github.com/llvm/llvm-project/pull/137911 From lldb-commits at lists.llvm.org Thu May 8 14:47:09 2025 From: lldb-commits at lists.llvm.org (Ebuka Ezike via lldb-commits) Date: Thu, 08 May 2025 14:47:09 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Adding a modules explorer to lldb-dap ext. (PR #138977) In-Reply-To: Message-ID: <681d265d.a70a0220.d74e1.a954@mx.google.com> da-viper wrote: LGTM, Except the load address seems to be in decimal. It does not specify in the dap specification if it is decimal or hex. I assume it is implementation based. if it is the case we should show it as hex as that is default format for address in lldb. We could later extend it to show children instead of using the tool-tip. https://github.com/llvm/llvm-project/pull/138977 From lldb-commits at lists.llvm.org Thu May 8 14:47:27 2025 From: lldb-commits at lists.llvm.org (Ebuka Ezike via lldb-commits) Date: Thu, 08 May 2025 14:47:27 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Adding a modules explorer to lldb-dap ext. (PR #138977) In-Reply-To: Message-ID: <681d266f.170a0220.8f373.8932@mx.google.com> https://github.com/da-viper approved this pull request. https://github.com/llvm/llvm-project/pull/138977 From lldb-commits at lists.llvm.org Thu May 8 14:57:50 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 14:57:50 -0700 (PDT) Subject: [Lldb-commits] [lldb] Branch island debug (PR #139166) Message-ID: https://github.com/jimingham created https://github.com/llvm/llvm-project/pull/139166 This patch allows lldb to step in across "branch islands" which is the Darwin linker's way of dealing with immediate branches to targets that are too far away for the immediate slot to make the jump. I submitted this a couple days ago and it failed on the arm64 bot. I was able to match the bot OS and Tool versions (they are a bit old at this point) and ran the test there but sadly it succeeded. The x86_64 bot also failed but that was my bad, I did @skipUnlessDarwin when I should have done @skipUnlessAppleSilicon. So this resubmission is with the proper decoration for the test, and with a bunch of debug output printed in case of failure. With any luck, if this resubmission fails again I'll be able to see what's going on. Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Thu May 8 14:58:23 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 14:58:23 -0700 (PDT) Subject: [Lldb-commits] [lldb] Branch island debug (PR #139166) In-Reply-To: Message-ID: <681d28ff.170a0220.1762c6.7541@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: None (jimingham)
Changes This patch allows lldb to step in across "branch islands" which is the Darwin linker's way of dealing with immediate branches to targets that are too far away for the immediate slot to make the jump. I submitted this a couple days ago and it failed on the arm64 bot. I was able to match the bot OS and Tool versions (they are a bit old at this point) and ran the test there but sadly it succeeded. The x86_64 bot also failed but that was my bad, I did @skipUnlessDarwin when I should have done @skipUnlessAppleSilicon. So this resubmission is with the proper decoration for the test, and with a bunch of debug output printed in case of failure. With any luck, if this resubmission fails again I'll be able to see what's going on. --- Full diff: https://github.com/llvm/llvm-project/pull/139166.diff 9 Files Affected: - (modified) lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp (+25-7) - (added) lldb/test/API/macosx/branch-islands/Makefile (+16) - (added) lldb/test/API/macosx/branch-islands/TestBranchIslands.py (+61) - (added) lldb/test/API/macosx/branch-islands/foo.c (+6) - (added) lldb/test/API/macosx/branch-islands/main.c (+6) - (added) lldb/test/API/macosx/branch-islands/padding1.s (+3) - (added) lldb/test/API/macosx/branch-islands/padding2.s (+3) - (added) lldb/test/API/macosx/branch-islands/padding3.s (+3) - (added) lldb/test/API/macosx/branch-islands/padding4.s (+3) ``````````diff diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index e25c4ff55e408..578ab12268ea3 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -26,6 +26,7 @@ #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Utility/DataBuffer.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/LLDBLog.h" @@ -923,15 +924,15 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, if (current_symbol != nullptr) { std::vector
addresses; + ConstString current_name = + current_symbol->GetMangled().GetName(Mangled::ePreferMangled); if (current_symbol->IsTrampoline()) { - ConstString trampoline_name = - current_symbol->GetMangled().GetName(Mangled::ePreferMangled); - if (trampoline_name) { + if (current_name) { const ModuleList &images = target_sp->GetImages(); SymbolContextList code_symbols; - images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeCode, + images.FindSymbolsWithNameAndType(current_name, eSymbolTypeCode, code_symbols); for (const SymbolContext &context : code_symbols) { Address addr = context.GetFunctionOrSymbolAddress(); @@ -945,8 +946,8 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, } SymbolContextList reexported_symbols; - images.FindSymbolsWithNameAndType( - trampoline_name, eSymbolTypeReExported, reexported_symbols); + images.FindSymbolsWithNameAndType(current_name, eSymbolTypeReExported, + reexported_symbols); for (const SymbolContext &context : reexported_symbols) { if (context.symbol) { Symbol *actual_symbol = @@ -968,7 +969,7 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, } SymbolContextList indirect_symbols; - images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeResolver, + images.FindSymbolsWithNameAndType(current_name, eSymbolTypeResolver, indirect_symbols); for (const SymbolContext &context : indirect_symbols) { @@ -1028,6 +1029,23 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, thread_plan_sp = std::make_shared( thread, load_addrs, stop_others); } + // One more case we have to consider is "branch islands". These are regular + // TEXT symbols but their names end in .island plus maybe a .digit suffix. + // They are to allow arm64 code to branch further than the size of the + // address slot allows. We just need to single-instruction step in that + // case. + static const char *g_branch_island_pattern = "\\.island\\.?[0-9]*$"; + static RegularExpression g_branch_island_regex(g_branch_island_pattern); + + bool is_branch_island = g_branch_island_regex.Execute(current_name); + if (!thread_plan_sp && is_branch_island) { + thread_plan_sp = std::make_shared( + thread, + /* step_over= */ false, /* stop_others */ false, eVoteNoOpinion, + eVoteNoOpinion); + LLDB_LOG(log, "Stepping one instruction over branch island: '{0}'.", + current_name); + } } else { LLDB_LOGF(log, "Could not find symbol for step through."); } diff --git a/lldb/test/API/macosx/branch-islands/Makefile b/lldb/test/API/macosx/branch-islands/Makefile new file mode 100644 index 0000000000000..062e947f6d6ee --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/Makefile @@ -0,0 +1,16 @@ +C_SOURCES := main.c foo.c +CFLAGS_EXTRAS := -std=c99 + +include Makefile.rules + +a.out: main.o padding1.o padding2.o padding3.o padding4.o foo.o + ${CC} ${LDFLAGS} foo.o padding1.o padding2.o padding3.o padding4.o main.o -o a.out + +%.o: $(SRCDIR)/%.s + ${CC} -c $< + +#padding1.o: padding1.s +# ${CC} -c $(SRCDIR)/padding1.s + +#padding2.o: padding2.s +# ${CC} -c $(SRCDIR)/padding2.s diff --git a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py new file mode 100644 index 0000000000000..2c15c159e7da9 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py @@ -0,0 +1,61 @@ +""" +Make sure that we can step in across an arm64 branch island +""" + + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * + + +class TestBranchIslandStepping(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + @skipUnlessAppleSilicon + def test_step_in_branch_island(self): + """Make sure we can step in across a branch island""" + self.build() + self.main_source_file = lldb.SBFileSpec("main.c") + self.do_test() + + def do_test(self): + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "Set a breakpoint here", self.main_source_file + ) + + # Make sure that we did manage to generate a branch island for foo: + syms = target.FindSymbols("foo.island", lldb.eSymbolTypeCode) + self.assertEqual(len(syms), 1, "We did generate an island for foo") + + # Gathering some info to dump in case of failure: + trace_before = lldbutil.print_stacktrace(thread, True) + func_before = thread.frames[0].function + + thread.StepInto() + stop_frame = thread.frames[0] + # This is failing on the bot, but I can't reproduce the failure + # locally. Let's see if we can dump some more info here to help + # figure out what went wrong... + if stop_frame.name.find("foo") == -1: + stream = lldb.SBStream() + print("Branch island symbols: ") + syms[0].GetDescription(stream) + for i in range (0,6): + for sep in ["", "."]: + syms = target.FindSymbols(f"foo.island{sep}{i}", lldb.eSymbolTypeCode) + if len(syms) > 0: + stream.Print("\n") + syms[0].GetDescription(stream) + + print(stream.GetData()) + print(f"Start backtrace:") + print(trace_before) + print(f"\n'main' disassembly:\n{lldbutil.disassemble(target, func_before)}") + print("\nEnd backtrace:\n") + lldbutil.print_stacktrace(thread) + print(f"\nStop disassembly:\n {lldbutil.disassemble(target, stop_frame.function)}") + + self.assertIn("foo", stop_frame.name, "Stepped into foo") + var = stop_frame.FindVariable("a_variable_in_foo") + self.assertTrue(var.IsValid(), "Found the variable in foo") diff --git a/lldb/test/API/macosx/branch-islands/foo.c b/lldb/test/API/macosx/branch-islands/foo.c new file mode 100644 index 0000000000000..a5dd2e59e1d82 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/foo.c @@ -0,0 +1,6 @@ +#include + +void foo() { + int a_variable_in_foo = 10; + printf("I am foo: %d.\n", a_variable_in_foo); +} diff --git a/lldb/test/API/macosx/branch-islands/main.c b/lldb/test/API/macosx/branch-islands/main.c new file mode 100644 index 0000000000000..b5578bdd715df --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/main.c @@ -0,0 +1,6 @@ +extern void foo(); + +int main() { + foo(); // Set a breakpoint here + return 0; +} diff --git a/lldb/test/API/macosx/branch-islands/padding1.s b/lldb/test/API/macosx/branch-islands/padding1.s new file mode 100644 index 0000000000000..4911e53b0240d --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding1.s @@ -0,0 +1,3 @@ +.text +_padding1: +.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding2.s b/lldb/test/API/macosx/branch-islands/padding2.s new file mode 100644 index 0000000000000..5ad1bad11263b --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding2.s @@ -0,0 +1,3 @@ +.text +_padding2: +.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding3.s b/lldb/test/API/macosx/branch-islands/padding3.s new file mode 100644 index 0000000000000..9f614eecf56d9 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding3.s @@ -0,0 +1,3 @@ +.text +_padding3: +.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding4.s b/lldb/test/API/macosx/branch-islands/padding4.s new file mode 100644 index 0000000000000..12896cf5e5b8e --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding4.s @@ -0,0 +1,3 @@ +.text +_padding4: +.space 120*1024*1024 ``````````
https://github.com/llvm/llvm-project/pull/139166 From lldb-commits at lists.llvm.org Thu May 8 15:00:17 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 15:00:17 -0700 (PDT) Subject: [Lldb-commits] [lldb] Branch island debug (PR #139166) In-Reply-To: Message-ID: <681d2971.a70a0220.267ef6.b19c@mx.google.com> github-actions[bot] wrote: :warning: Python code formatter, darker found issues in your code. :warning:
You can test this locally with the following command: ``````````bash darker --check --diff -r HEAD~1...HEAD lldb/test/API/macosx/branch-islands/TestBranchIslands.py ``````````
View the diff from darker here. ``````````diff --- TestBranchIslands.py 2025-05-08 21:41:17.000000 +0000 +++ TestBranchIslands.py 2025-05-08 21:59:48.164599 +0000 @@ -39,23 +39,27 @@ # figure out what went wrong... if stop_frame.name.find("foo") == -1: stream = lldb.SBStream() print("Branch island symbols: ") syms[0].GetDescription(stream) - for i in range (0,6): + for i in range(0, 6): for sep in ["", "."]: - syms = target.FindSymbols(f"foo.island{sep}{i}", lldb.eSymbolTypeCode) + syms = target.FindSymbols( + f"foo.island{sep}{i}", lldb.eSymbolTypeCode + ) if len(syms) > 0: stream.Print("\n") syms[0].GetDescription(stream) - + print(stream.GetData()) print(f"Start backtrace:") print(trace_before) print(f"\n'main' disassembly:\n{lldbutil.disassemble(target, func_before)}") print("\nEnd backtrace:\n") lldbutil.print_stacktrace(thread) - print(f"\nStop disassembly:\n {lldbutil.disassemble(target, stop_frame.function)}") - + print( + f"\nStop disassembly:\n {lldbutil.disassemble(target, stop_frame.function)}" + ) + self.assertIn("foo", stop_frame.name, "Stepped into foo") var = stop_frame.FindVariable("a_variable_in_foo") self.assertTrue(var.IsValid(), "Found the variable in foo") ``````````
https://github.com/llvm/llvm-project/pull/139166 From lldb-commits at lists.llvm.org Thu May 8 15:01:34 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Thu, 08 May 2025 15:01:34 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Move the event and progress event threads into DAP (NFC) (PR #139167) Message-ID: https://github.com/JDevlieghere created https://github.com/llvm/llvm-project/pull/139167 Move the event and progress event threads into the DAP class. Currently both are implemented in the InitializeRequestHandler, but it doesn't really belong there because the threads are not tied to that request, they just launch them. >From de9861531afa2fe9a9e7aa1b7669c4d1bd28d43c Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Thu, 8 May 2025 14:59:00 -0700 Subject: [PATCH] [lldb-dap] Move the event and progress event threads into DAP (NFC) Move the event and progress event threads into the DAP class. Currently both are implemented in the InitializeRequestHandler, but it doesn't really belong there because the threads are not tied to that request, they just launch them. --- lldb/tools/lldb-dap/DAP.cpp | 261 +++++++++++++++++- lldb/tools/lldb-dap/DAP.h | 14 +- .../Handler/InitializeRequestHandler.cpp | 253 +---------------- 3 files changed, 269 insertions(+), 259 deletions(-) diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 62c60cc3a9b3b..7aa2f03894504 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -8,6 +8,7 @@ #include "DAP.h" #include "DAPLog.h" +#include "EventHelper.h" #include "Handler/RequestHandler.h" #include "Handler/ResponseHandler.h" #include "JSONUtils.h" @@ -20,6 +21,7 @@ #include "lldb/API/SBBreakpoint.h" #include "lldb/API/SBCommandInterpreter.h" #include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBEvent.h" #include "lldb/API/SBLanguageRuntime.h" #include "lldb/API/SBListener.h" #include "lldb/API/SBProcess.h" @@ -52,6 +54,7 @@ #include #include #include +#include #include #include @@ -77,6 +80,48 @@ const char DEV_NULL[] = "/dev/null"; namespace lldb_dap { +static std::string GetStringFromStructuredData(lldb::SBStructuredData &data, + const char *key) { + lldb::SBStructuredData keyValue = data.GetValueForKey(key); + if (!keyValue) + return std::string(); + + const size_t length = keyValue.GetStringValue(nullptr, 0); + + if (length == 0) + return std::string(); + + std::string str(length + 1, 0); + keyValue.GetStringValue(&str[0], length + 1); + return str; +} + +static uint64_t GetUintFromStructuredData(lldb::SBStructuredData &data, + const char *key) { + lldb::SBStructuredData keyValue = data.GetValueForKey(key); + + if (!keyValue.IsValid()) + return 0; + return keyValue.GetUnsignedIntegerValue(); +} + +static llvm::StringRef GetModuleEventReason(uint32_t event_mask) { + if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded) + return "new"; + if (event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded) + return "removed"; + assert(event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded || + event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged); + return "changed"; +} + +/// Return string with first character capitalized. +static std::string capitalize(llvm::StringRef str) { + if (str.empty()) + return ""; + return ((llvm::Twine)llvm::toUpper(str[0]) + str.drop_front()).str(); +} + llvm::StringRef DAP::debug_adapter_path = ""; DAP::DAP(Log *log, const ReplMode default_repl_mode, @@ -94,13 +139,6 @@ DAP::DAP(Log *log, const ReplMode default_repl_mode, DAP::~DAP() = default; -/// Return string with first character capitalized. -static std::string capitalize(llvm::StringRef str) { - if (str.empty()) - return ""; - return ((llvm::Twine)llvm::toUpper(str[0]) + str.drop_front()).str(); -} - void DAP::PopulateExceptionBreakpoints() { llvm::call_once(init_exception_breakpoints_flag, [this]() { exception_breakpoints = std::vector{}; @@ -1390,4 +1428,213 @@ protocol::Capabilities DAP::GetCapabilities() { return capabilities; } +void DAP::StartEventThread() { + event_thread = std::thread(&DAP::EventThread, this); +} + +void DAP::StartProgressEventThread() { + progress_event_thread = std::thread(&DAP::ProgressEventThread, this); +} + +void DAP::ProgressEventThread() { + lldb::SBListener listener("lldb-dap.progress.listener"); + debugger.GetBroadcaster().AddListener( + listener, lldb::SBDebugger::eBroadcastBitProgress | + lldb::SBDebugger::eBroadcastBitExternalProgress); + broadcaster.AddListener(listener, eBroadcastBitStopProgressThread); + lldb::SBEvent event; + bool done = false; + while (!done) { + if (listener.WaitForEvent(1, event)) { + const auto event_mask = event.GetType(); + if (event.BroadcasterMatchesRef(broadcaster)) { + if (event_mask & eBroadcastBitStopProgressThread) { + done = true; + } + } else { + lldb::SBStructuredData data = + lldb::SBDebugger::GetProgressDataFromEvent(event); + + const uint64_t progress_id = + GetUintFromStructuredData(data, "progress_id"); + const uint64_t completed = GetUintFromStructuredData(data, "completed"); + const uint64_t total = GetUintFromStructuredData(data, "total"); + const std::string details = + GetStringFromStructuredData(data, "details"); + + if (completed == 0) { + if (total == UINT64_MAX) { + // This progress is non deterministic and won't get updated until it + // is completed. Send the "message" which will be the combined title + // and detail. The only other progress event for thus + // non-deterministic progress will be the completed event So there + // will be no need to update the detail. + const std::string message = + GetStringFromStructuredData(data, "message"); + SendProgressEvent(progress_id, message.c_str(), completed, total); + } else { + // This progress is deterministic and will receive updates, + // on the progress creation event VSCode will save the message in + // the create packet and use that as the title, so we send just the + // title in the progressCreate packet followed immediately by a + // detail packet, if there is any detail. + const std::string title = + GetStringFromStructuredData(data, "title"); + SendProgressEvent(progress_id, title.c_str(), completed, total); + if (!details.empty()) + SendProgressEvent(progress_id, details.c_str(), completed, total); + } + } else { + // This progress event is either the end of the progress dialog, or an + // update with possible detail. The "detail" string we send to VS Code + // will be appended to the progress dialog's initial text from when it + // was created. + SendProgressEvent(progress_id, details.c_str(), completed, total); + } + } + } + } +} + +// All events from the debugger, target, process, thread and frames are +// received in this function that runs in its own thread. We are using a +// "FILE *" to output packets back to VS Code and they have mutexes in them +// them prevent multiple threads from writing simultaneously so no locking +// is required. +void DAP::EventThread() { + llvm::set_thread_name(transport.GetClientName() + ".event_handler"); + lldb::SBEvent event; + lldb::SBListener listener = debugger.GetListener(); + broadcaster.AddListener(listener, eBroadcastBitStopEventThread); + debugger.GetBroadcaster().AddListener( + listener, lldb::eBroadcastBitError | lldb::eBroadcastBitWarning); + bool done = false; + while (!done) { + if (listener.WaitForEvent(1, event)) { + const auto event_mask = event.GetType(); + if (lldb::SBProcess::EventIsProcessEvent(event)) { + lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event); + if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) { + auto state = lldb::SBProcess::GetStateFromEvent(event); + switch (state) { + case lldb::eStateConnected: + case lldb::eStateDetached: + case lldb::eStateInvalid: + case lldb::eStateUnloaded: + break; + case lldb::eStateAttaching: + case lldb::eStateCrashed: + case lldb::eStateLaunching: + case lldb::eStateStopped: + case lldb::eStateSuspended: + // Only report a stopped event if the process was not + // automatically restarted. + if (!lldb::SBProcess::GetRestartedFromEvent(event)) { + SendStdOutStdErr(*this, process); + SendThreadStoppedEvent(*this); + } + break; + case lldb::eStateRunning: + case lldb::eStateStepping: + WillContinue(); + SendContinuedEvent(*this); + break; + case lldb::eStateExited: + lldb::SBStream stream; + process.GetStatus(stream); + SendOutput(OutputType::Console, stream.GetData()); + + // When restarting, we can get an "exited" event for the process we + // just killed with the old PID, or even with no PID. In that case + // we don't have to terminate the session. + if (process.GetProcessID() == LLDB_INVALID_PROCESS_ID || + process.GetProcessID() == restarting_process_id) { + restarting_process_id = LLDB_INVALID_PROCESS_ID; + } else { + // Run any exit LLDB commands the user specified in the + // launch.json + RunExitCommands(); + SendProcessExitedEvent(*this, process); + SendTerminatedEvent(); + done = true; + } + break; + } + } else if ((event_mask & lldb::SBProcess::eBroadcastBitSTDOUT) || + (event_mask & lldb::SBProcess::eBroadcastBitSTDERR)) { + SendStdOutStdErr(*this, process); + } + } else if (lldb::SBTarget::EventIsTargetEvent(event)) { + if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded || + event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded || + event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded || + event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged) { + llvm::StringRef reason = GetModuleEventReason(event_mask); + const uint32_t num_modules = + lldb::SBTarget::GetNumModulesFromEvent(event); + for (uint32_t i = 0; i < num_modules; ++i) { + lldb::SBModule module = + lldb::SBTarget::GetModuleAtIndexFromEvent(i, event); + if (!module.IsValid()) + continue; + + llvm::json::Object body; + body.try_emplace("reason", reason); + body.try_emplace("module", CreateModule(target, module)); + llvm::json::Object module_event = CreateEventObject("module"); + module_event.try_emplace("body", std::move(body)); + SendJSON(llvm::json::Value(std::move(module_event))); + } + } + } else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) { + if (event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged) { + auto event_type = + lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event); + auto bp = Breakpoint( + *this, lldb::SBBreakpoint::GetBreakpointFromEvent(event)); + // If the breakpoint was set through DAP, it will have the + // BreakpointBase::kDAPBreakpointLabel. Regardless of whether + // locations were added, removed, or resolved, the breakpoint isn't + // going away and the reason is always "changed". + if ((event_type & lldb::eBreakpointEventTypeLocationsAdded || + event_type & lldb::eBreakpointEventTypeLocationsRemoved || + event_type & lldb::eBreakpointEventTypeLocationsResolved) && + bp.MatchesName(BreakpointBase::kDAPBreakpointLabel)) { + // As the DAP client already knows the path of this breakpoint, we + // don't need to send it back as part of the "changed" event. This + // avoids sending paths that should be source mapped. Note that + // CreateBreakpoint doesn't apply source mapping and certain + // implementation ignore the source part of this event anyway. + llvm::json::Value source_bp = CreateBreakpoint(&bp); + source_bp.getAsObject()->erase("source"); + + llvm::json::Object body; + body.try_emplace("breakpoint", source_bp); + body.try_emplace("reason", "changed"); + + llvm::json::Object bp_event = CreateEventObject("breakpoint"); + bp_event.try_emplace("body", std::move(body)); + + SendJSON(llvm::json::Value(std::move(bp_event))); + } + } + } else if (event_mask & lldb::eBroadcastBitError || + event_mask & lldb::eBroadcastBitWarning) { + lldb::SBStructuredData data = + lldb::SBDebugger::GetDiagnosticFromEvent(event); + if (!data.IsValid()) + continue; + std::string type = GetStringValue(data.GetValueForKey("type")); + std::string message = GetStringValue(data.GetValueForKey("message")); + SendOutput(OutputType::Important, + llvm::formatv("{0}: {1}", type, message).str()); + } else if (event.BroadcasterMatchesRef(broadcaster)) { + if (event_mask & eBroadcastBitStopEventThread) { + done = true; + } + } + } + } +} + } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index b581ae759b1bc..afeda8d81efb0 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -167,8 +167,6 @@ struct DAP { lldb::SBTarget target; Variables variables; lldb::SBBroadcaster broadcaster; - std::thread event_thread; - std::thread progress_event_thread; llvm::StringMap source_breakpoints; FunctionBreakpointMap function_breakpoints; InstructionBreakpointMap instruction_breakpoints; @@ -418,7 +416,19 @@ struct DAP { lldb::SBMutex GetAPIMutex() const { return target.GetAPIMutex(); } + void StartEventThread(); + void StartProgressEventThread(); + private: + /// Event threads. + /// @{ + void EventThread(); + void ProgressEventThread(); + + std::thread event_thread; + std::thread progress_event_thread; + /// @} + /// Queue for all incoming messages. std::deque m_queue; std::deque m_pending_queue; diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp index aa947d3cb5ab9..cdcbdfa7f48e3 100644 --- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp @@ -12,255 +12,11 @@ #include "LLDBUtils.h" #include "Protocol/ProtocolRequests.h" #include "RequestHandler.h" -#include "lldb/API/SBEvent.h" -#include "lldb/API/SBListener.h" -#include "lldb/API/SBStream.h" #include "lldb/API/SBTarget.h" -#include -using namespace lldb; +using namespace lldb_dap; using namespace lldb_dap::protocol; -namespace lldb_dap { - -static std::string GetStringFromStructuredData(lldb::SBStructuredData &data, - const char *key) { - lldb::SBStructuredData keyValue = data.GetValueForKey(key); - if (!keyValue) - return std::string(); - - const size_t length = keyValue.GetStringValue(nullptr, 0); - - if (length == 0) - return std::string(); - - std::string str(length + 1, 0); - keyValue.GetStringValue(&str[0], length + 1); - return str; -} - -static uint64_t GetUintFromStructuredData(lldb::SBStructuredData &data, - const char *key) { - lldb::SBStructuredData keyValue = data.GetValueForKey(key); - - if (!keyValue.IsValid()) - return 0; - return keyValue.GetUnsignedIntegerValue(); -} - -void ProgressEventThreadFunction(DAP &dap) { - lldb::SBListener listener("lldb-dap.progress.listener"); - dap.debugger.GetBroadcaster().AddListener( - listener, lldb::SBDebugger::eBroadcastBitProgress | - lldb::SBDebugger::eBroadcastBitExternalProgress); - dap.broadcaster.AddListener(listener, eBroadcastBitStopProgressThread); - lldb::SBEvent event; - bool done = false; - while (!done) { - if (listener.WaitForEvent(1, event)) { - const auto event_mask = event.GetType(); - if (event.BroadcasterMatchesRef(dap.broadcaster)) { - if (event_mask & eBroadcastBitStopProgressThread) { - done = true; - } - } else { - lldb::SBStructuredData data = - lldb::SBDebugger::GetProgressDataFromEvent(event); - - const uint64_t progress_id = - GetUintFromStructuredData(data, "progress_id"); - const uint64_t completed = GetUintFromStructuredData(data, "completed"); - const uint64_t total = GetUintFromStructuredData(data, "total"); - const std::string details = - GetStringFromStructuredData(data, "details"); - - if (completed == 0) { - if (total == UINT64_MAX) { - // This progress is non deterministic and won't get updated until it - // is completed. Send the "message" which will be the combined title - // and detail. The only other progress event for thus - // non-deterministic progress will be the completed event So there - // will be no need to update the detail. - const std::string message = - GetStringFromStructuredData(data, "message"); - dap.SendProgressEvent(progress_id, message.c_str(), completed, - total); - } else { - // This progress is deterministic and will receive updates, - // on the progress creation event VSCode will save the message in - // the create packet and use that as the title, so we send just the - // title in the progressCreate packet followed immediately by a - // detail packet, if there is any detail. - const std::string title = - GetStringFromStructuredData(data, "title"); - dap.SendProgressEvent(progress_id, title.c_str(), completed, total); - if (!details.empty()) - dap.SendProgressEvent(progress_id, details.c_str(), completed, - total); - } - } else { - // This progress event is either the end of the progress dialog, or an - // update with possible detail. The "detail" string we send to VS Code - // will be appended to the progress dialog's initial text from when it - // was created. - dap.SendProgressEvent(progress_id, details.c_str(), completed, total); - } - } - } - } -} - -static llvm::StringRef GetModuleEventReason(uint32_t event_mask) { - if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded) - return "new"; - if (event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded) - return "removed"; - assert(event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded || - event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged); - return "changed"; -} - -// All events from the debugger, target, process, thread and frames are -// received in this function that runs in its own thread. We are using a -// "FILE *" to output packets back to VS Code and they have mutexes in them -// them prevent multiple threads from writing simultaneously so no locking -// is required. -static void EventThreadFunction(DAP &dap) { - llvm::set_thread_name(dap.transport.GetClientName() + ".event_handler"); - lldb::SBEvent event; - lldb::SBListener listener = dap.debugger.GetListener(); - dap.broadcaster.AddListener(listener, eBroadcastBitStopEventThread); - dap.debugger.GetBroadcaster().AddListener(listener, eBroadcastBitError | - eBroadcastBitWarning); - bool done = false; - while (!done) { - if (listener.WaitForEvent(1, event)) { - const auto event_mask = event.GetType(); - if (lldb::SBProcess::EventIsProcessEvent(event)) { - lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event); - if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) { - auto state = lldb::SBProcess::GetStateFromEvent(event); - - DAP_LOG(dap.log, "State = {0}", state); - switch (state) { - case lldb::eStateConnected: - case lldb::eStateDetached: - case lldb::eStateInvalid: - case lldb::eStateUnloaded: - break; - case lldb::eStateAttaching: - case lldb::eStateCrashed: - case lldb::eStateLaunching: - case lldb::eStateStopped: - case lldb::eStateSuspended: - // Only report a stopped event if the process was not - // automatically restarted. - if (!lldb::SBProcess::GetRestartedFromEvent(event)) { - SendStdOutStdErr(dap, process); - SendThreadStoppedEvent(dap); - } - break; - case lldb::eStateRunning: - case lldb::eStateStepping: - dap.WillContinue(); - SendContinuedEvent(dap); - break; - case lldb::eStateExited: - lldb::SBStream stream; - process.GetStatus(stream); - dap.SendOutput(OutputType::Console, stream.GetData()); - - // When restarting, we can get an "exited" event for the process we - // just killed with the old PID, or even with no PID. In that case - // we don't have to terminate the session. - if (process.GetProcessID() == LLDB_INVALID_PROCESS_ID || - process.GetProcessID() == dap.restarting_process_id) { - dap.restarting_process_id = LLDB_INVALID_PROCESS_ID; - } else { - // Run any exit LLDB commands the user specified in the - // launch.json - dap.RunExitCommands(); - SendProcessExitedEvent(dap, process); - dap.SendTerminatedEvent(); - done = true; - } - break; - } - } else if ((event_mask & lldb::SBProcess::eBroadcastBitSTDOUT) || - (event_mask & lldb::SBProcess::eBroadcastBitSTDERR)) { - SendStdOutStdErr(dap, process); - } - } else if (lldb::SBTarget::EventIsTargetEvent(event)) { - if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded || - event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded || - event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded || - event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged) { - llvm::StringRef reason = GetModuleEventReason(event_mask); - const uint32_t num_modules = SBTarget::GetNumModulesFromEvent(event); - for (uint32_t i = 0; i < num_modules; ++i) { - lldb::SBModule module = - SBTarget::GetModuleAtIndexFromEvent(i, event); - if (!module.IsValid()) - continue; - - llvm::json::Object body; - body.try_emplace("reason", reason); - body.try_emplace("module", CreateModule(dap.target, module)); - llvm::json::Object module_event = CreateEventObject("module"); - module_event.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(module_event))); - } - } - } else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) { - if (event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged) { - auto event_type = - lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event); - auto bp = Breakpoint( - dap, lldb::SBBreakpoint::GetBreakpointFromEvent(event)); - // If the breakpoint was set through DAP, it will have the - // BreakpointBase::kDAPBreakpointLabel. Regardless of whether - // locations were added, removed, or resolved, the breakpoint isn't - // going away and the reason is always "changed". - if ((event_type & lldb::eBreakpointEventTypeLocationsAdded || - event_type & lldb::eBreakpointEventTypeLocationsRemoved || - event_type & lldb::eBreakpointEventTypeLocationsResolved) && - bp.MatchesName(BreakpointBase::kDAPBreakpointLabel)) { - // As the DAP client already knows the path of this breakpoint, we - // don't need to send it back as part of the "changed" event. This - // avoids sending paths that should be source mapped. Note that - // CreateBreakpoint doesn't apply source mapping and certain - // implementation ignore the source part of this event anyway. - llvm::json::Value source_bp = CreateBreakpoint(&bp); - source_bp.getAsObject()->erase("source"); - - llvm::json::Object body; - body.try_emplace("breakpoint", source_bp); - body.try_emplace("reason", "changed"); - - llvm::json::Object bp_event = CreateEventObject("breakpoint"); - bp_event.try_emplace("body", std::move(body)); - - dap.SendJSON(llvm::json::Value(std::move(bp_event))); - } - } - } else if (event_mask & eBroadcastBitError || - event_mask & eBroadcastBitWarning) { - SBStructuredData data = SBDebugger::GetDiagnosticFromEvent(event); - if (!data.IsValid()) - continue; - std::string type = GetStringValue(data.GetValueForKey("type")); - std::string message = GetStringValue(data.GetValueForKey("message")); - dap.SendOutput(OutputType::Important, - llvm::formatv("{0}: {1}", type, message).str()); - } else if (event.BroadcasterMatchesRef(dap.broadcaster)) { - if (event_mask & eBroadcastBitStopEventThread) { - done = true; - } - } - } - } -} - /// Initialize request; value of command field is 'initialize'. llvm::Expected InitializeRequestHandler::Run( const InitializeRequestArguments &arguments) const { @@ -314,12 +70,11 @@ llvm::Expected InitializeRequestHandler::Run( "Sends an DAP event to the client."); if (arguments.supportedFeatures.contains(eClientFeatureProgressReporting)) - dap.progress_event_thread = - std::thread(ProgressEventThreadFunction, std::ref(dap)); + dap.StartProgressEventThread(); // Start our event thread so we can receive events from the debugger, target, // process and more. - dap.event_thread = std::thread(EventThreadFunction, std::ref(dap)); + dap.StartEventThread(); return dap.GetCapabilities(); } @@ -327,5 +82,3 @@ llvm::Expected InitializeRequestHandler::Run( void InitializeRequestHandler::PostRun() const { dap.SendJSON(CreateEventObject("initialized")); } - -} // namespace lldb_dap From lldb-commits at lists.llvm.org Thu May 8 15:02:09 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 15:02:09 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Move the event and progress event threads into DAP (NFC) (PR #139167) In-Reply-To: Message-ID: <681d29e1.170a0220.111eaf.9039@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Jonas Devlieghere (JDevlieghere)
Changes Move the event and progress event threads into the DAP class. Currently both are implemented in the InitializeRequestHandler, but it doesn't really belong there because the threads are not tied to that request, they just launch them. --- Patch is 25.83 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/139167.diff 3 Files Affected: - (modified) lldb/tools/lldb-dap/DAP.cpp (+254-7) - (modified) lldb/tools/lldb-dap/DAP.h (+12-2) - (modified) lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp (+3-250) ``````````diff diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 62c60cc3a9b3b..7aa2f03894504 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -8,6 +8,7 @@ #include "DAP.h" #include "DAPLog.h" +#include "EventHelper.h" #include "Handler/RequestHandler.h" #include "Handler/ResponseHandler.h" #include "JSONUtils.h" @@ -20,6 +21,7 @@ #include "lldb/API/SBBreakpoint.h" #include "lldb/API/SBCommandInterpreter.h" #include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBEvent.h" #include "lldb/API/SBLanguageRuntime.h" #include "lldb/API/SBListener.h" #include "lldb/API/SBProcess.h" @@ -52,6 +54,7 @@ #include #include #include +#include #include #include @@ -77,6 +80,48 @@ const char DEV_NULL[] = "/dev/null"; namespace lldb_dap { +static std::string GetStringFromStructuredData(lldb::SBStructuredData &data, + const char *key) { + lldb::SBStructuredData keyValue = data.GetValueForKey(key); + if (!keyValue) + return std::string(); + + const size_t length = keyValue.GetStringValue(nullptr, 0); + + if (length == 0) + return std::string(); + + std::string str(length + 1, 0); + keyValue.GetStringValue(&str[0], length + 1); + return str; +} + +static uint64_t GetUintFromStructuredData(lldb::SBStructuredData &data, + const char *key) { + lldb::SBStructuredData keyValue = data.GetValueForKey(key); + + if (!keyValue.IsValid()) + return 0; + return keyValue.GetUnsignedIntegerValue(); +} + +static llvm::StringRef GetModuleEventReason(uint32_t event_mask) { + if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded) + return "new"; + if (event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded) + return "removed"; + assert(event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded || + event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged); + return "changed"; +} + +/// Return string with first character capitalized. +static std::string capitalize(llvm::StringRef str) { + if (str.empty()) + return ""; + return ((llvm::Twine)llvm::toUpper(str[0]) + str.drop_front()).str(); +} + llvm::StringRef DAP::debug_adapter_path = ""; DAP::DAP(Log *log, const ReplMode default_repl_mode, @@ -94,13 +139,6 @@ DAP::DAP(Log *log, const ReplMode default_repl_mode, DAP::~DAP() = default; -/// Return string with first character capitalized. -static std::string capitalize(llvm::StringRef str) { - if (str.empty()) - return ""; - return ((llvm::Twine)llvm::toUpper(str[0]) + str.drop_front()).str(); -} - void DAP::PopulateExceptionBreakpoints() { llvm::call_once(init_exception_breakpoints_flag, [this]() { exception_breakpoints = std::vector{}; @@ -1390,4 +1428,213 @@ protocol::Capabilities DAP::GetCapabilities() { return capabilities; } +void DAP::StartEventThread() { + event_thread = std::thread(&DAP::EventThread, this); +} + +void DAP::StartProgressEventThread() { + progress_event_thread = std::thread(&DAP::ProgressEventThread, this); +} + +void DAP::ProgressEventThread() { + lldb::SBListener listener("lldb-dap.progress.listener"); + debugger.GetBroadcaster().AddListener( + listener, lldb::SBDebugger::eBroadcastBitProgress | + lldb::SBDebugger::eBroadcastBitExternalProgress); + broadcaster.AddListener(listener, eBroadcastBitStopProgressThread); + lldb::SBEvent event; + bool done = false; + while (!done) { + if (listener.WaitForEvent(1, event)) { + const auto event_mask = event.GetType(); + if (event.BroadcasterMatchesRef(broadcaster)) { + if (event_mask & eBroadcastBitStopProgressThread) { + done = true; + } + } else { + lldb::SBStructuredData data = + lldb::SBDebugger::GetProgressDataFromEvent(event); + + const uint64_t progress_id = + GetUintFromStructuredData(data, "progress_id"); + const uint64_t completed = GetUintFromStructuredData(data, "completed"); + const uint64_t total = GetUintFromStructuredData(data, "total"); + const std::string details = + GetStringFromStructuredData(data, "details"); + + if (completed == 0) { + if (total == UINT64_MAX) { + // This progress is non deterministic and won't get updated until it + // is completed. Send the "message" which will be the combined title + // and detail. The only other progress event for thus + // non-deterministic progress will be the completed event So there + // will be no need to update the detail. + const std::string message = + GetStringFromStructuredData(data, "message"); + SendProgressEvent(progress_id, message.c_str(), completed, total); + } else { + // This progress is deterministic and will receive updates, + // on the progress creation event VSCode will save the message in + // the create packet and use that as the title, so we send just the + // title in the progressCreate packet followed immediately by a + // detail packet, if there is any detail. + const std::string title = + GetStringFromStructuredData(data, "title"); + SendProgressEvent(progress_id, title.c_str(), completed, total); + if (!details.empty()) + SendProgressEvent(progress_id, details.c_str(), completed, total); + } + } else { + // This progress event is either the end of the progress dialog, or an + // update with possible detail. The "detail" string we send to VS Code + // will be appended to the progress dialog's initial text from when it + // was created. + SendProgressEvent(progress_id, details.c_str(), completed, total); + } + } + } + } +} + +// All events from the debugger, target, process, thread and frames are +// received in this function that runs in its own thread. We are using a +// "FILE *" to output packets back to VS Code and they have mutexes in them +// them prevent multiple threads from writing simultaneously so no locking +// is required. +void DAP::EventThread() { + llvm::set_thread_name(transport.GetClientName() + ".event_handler"); + lldb::SBEvent event; + lldb::SBListener listener = debugger.GetListener(); + broadcaster.AddListener(listener, eBroadcastBitStopEventThread); + debugger.GetBroadcaster().AddListener( + listener, lldb::eBroadcastBitError | lldb::eBroadcastBitWarning); + bool done = false; + while (!done) { + if (listener.WaitForEvent(1, event)) { + const auto event_mask = event.GetType(); + if (lldb::SBProcess::EventIsProcessEvent(event)) { + lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event); + if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) { + auto state = lldb::SBProcess::GetStateFromEvent(event); + switch (state) { + case lldb::eStateConnected: + case lldb::eStateDetached: + case lldb::eStateInvalid: + case lldb::eStateUnloaded: + break; + case lldb::eStateAttaching: + case lldb::eStateCrashed: + case lldb::eStateLaunching: + case lldb::eStateStopped: + case lldb::eStateSuspended: + // Only report a stopped event if the process was not + // automatically restarted. + if (!lldb::SBProcess::GetRestartedFromEvent(event)) { + SendStdOutStdErr(*this, process); + SendThreadStoppedEvent(*this); + } + break; + case lldb::eStateRunning: + case lldb::eStateStepping: + WillContinue(); + SendContinuedEvent(*this); + break; + case lldb::eStateExited: + lldb::SBStream stream; + process.GetStatus(stream); + SendOutput(OutputType::Console, stream.GetData()); + + // When restarting, we can get an "exited" event for the process we + // just killed with the old PID, or even with no PID. In that case + // we don't have to terminate the session. + if (process.GetProcessID() == LLDB_INVALID_PROCESS_ID || + process.GetProcessID() == restarting_process_id) { + restarting_process_id = LLDB_INVALID_PROCESS_ID; + } else { + // Run any exit LLDB commands the user specified in the + // launch.json + RunExitCommands(); + SendProcessExitedEvent(*this, process); + SendTerminatedEvent(); + done = true; + } + break; + } + } else if ((event_mask & lldb::SBProcess::eBroadcastBitSTDOUT) || + (event_mask & lldb::SBProcess::eBroadcastBitSTDERR)) { + SendStdOutStdErr(*this, process); + } + } else if (lldb::SBTarget::EventIsTargetEvent(event)) { + if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded || + event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded || + event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded || + event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged) { + llvm::StringRef reason = GetModuleEventReason(event_mask); + const uint32_t num_modules = + lldb::SBTarget::GetNumModulesFromEvent(event); + for (uint32_t i = 0; i < num_modules; ++i) { + lldb::SBModule module = + lldb::SBTarget::GetModuleAtIndexFromEvent(i, event); + if (!module.IsValid()) + continue; + + llvm::json::Object body; + body.try_emplace("reason", reason); + body.try_emplace("module", CreateModule(target, module)); + llvm::json::Object module_event = CreateEventObject("module"); + module_event.try_emplace("body", std::move(body)); + SendJSON(llvm::json::Value(std::move(module_event))); + } + } + } else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) { + if (event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged) { + auto event_type = + lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event); + auto bp = Breakpoint( + *this, lldb::SBBreakpoint::GetBreakpointFromEvent(event)); + // If the breakpoint was set through DAP, it will have the + // BreakpointBase::kDAPBreakpointLabel. Regardless of whether + // locations were added, removed, or resolved, the breakpoint isn't + // going away and the reason is always "changed". + if ((event_type & lldb::eBreakpointEventTypeLocationsAdded || + event_type & lldb::eBreakpointEventTypeLocationsRemoved || + event_type & lldb::eBreakpointEventTypeLocationsResolved) && + bp.MatchesName(BreakpointBase::kDAPBreakpointLabel)) { + // As the DAP client already knows the path of this breakpoint, we + // don't need to send it back as part of the "changed" event. This + // avoids sending paths that should be source mapped. Note that + // CreateBreakpoint doesn't apply source mapping and certain + // implementation ignore the source part of this event anyway. + llvm::json::Value source_bp = CreateBreakpoint(&bp); + source_bp.getAsObject()->erase("source"); + + llvm::json::Object body; + body.try_emplace("breakpoint", source_bp); + body.try_emplace("reason", "changed"); + + llvm::json::Object bp_event = CreateEventObject("breakpoint"); + bp_event.try_emplace("body", std::move(body)); + + SendJSON(llvm::json::Value(std::move(bp_event))); + } + } + } else if (event_mask & lldb::eBroadcastBitError || + event_mask & lldb::eBroadcastBitWarning) { + lldb::SBStructuredData data = + lldb::SBDebugger::GetDiagnosticFromEvent(event); + if (!data.IsValid()) + continue; + std::string type = GetStringValue(data.GetValueForKey("type")); + std::string message = GetStringValue(data.GetValueForKey("message")); + SendOutput(OutputType::Important, + llvm::formatv("{0}: {1}", type, message).str()); + } else if (event.BroadcasterMatchesRef(broadcaster)) { + if (event_mask & eBroadcastBitStopEventThread) { + done = true; + } + } + } + } +} + } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index b581ae759b1bc..afeda8d81efb0 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -167,8 +167,6 @@ struct DAP { lldb::SBTarget target; Variables variables; lldb::SBBroadcaster broadcaster; - std::thread event_thread; - std::thread progress_event_thread; llvm::StringMap source_breakpoints; FunctionBreakpointMap function_breakpoints; InstructionBreakpointMap instruction_breakpoints; @@ -418,7 +416,19 @@ struct DAP { lldb::SBMutex GetAPIMutex() const { return target.GetAPIMutex(); } + void StartEventThread(); + void StartProgressEventThread(); + private: + /// Event threads. + /// @{ + void EventThread(); + void ProgressEventThread(); + + std::thread event_thread; + std::thread progress_event_thread; + /// @} + /// Queue for all incoming messages. std::deque m_queue; std::deque m_pending_queue; diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp index aa947d3cb5ab9..cdcbdfa7f48e3 100644 --- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp @@ -12,255 +12,11 @@ #include "LLDBUtils.h" #include "Protocol/ProtocolRequests.h" #include "RequestHandler.h" -#include "lldb/API/SBEvent.h" -#include "lldb/API/SBListener.h" -#include "lldb/API/SBStream.h" #include "lldb/API/SBTarget.h" -#include -using namespace lldb; +using namespace lldb_dap; using namespace lldb_dap::protocol; -namespace lldb_dap { - -static std::string GetStringFromStructuredData(lldb::SBStructuredData &data, - const char *key) { - lldb::SBStructuredData keyValue = data.GetValueForKey(key); - if (!keyValue) - return std::string(); - - const size_t length = keyValue.GetStringValue(nullptr, 0); - - if (length == 0) - return std::string(); - - std::string str(length + 1, 0); - keyValue.GetStringValue(&str[0], length + 1); - return str; -} - -static uint64_t GetUintFromStructuredData(lldb::SBStructuredData &data, - const char *key) { - lldb::SBStructuredData keyValue = data.GetValueForKey(key); - - if (!keyValue.IsValid()) - return 0; - return keyValue.GetUnsignedIntegerValue(); -} - -void ProgressEventThreadFunction(DAP &dap) { - lldb::SBListener listener("lldb-dap.progress.listener"); - dap.debugger.GetBroadcaster().AddListener( - listener, lldb::SBDebugger::eBroadcastBitProgress | - lldb::SBDebugger::eBroadcastBitExternalProgress); - dap.broadcaster.AddListener(listener, eBroadcastBitStopProgressThread); - lldb::SBEvent event; - bool done = false; - while (!done) { - if (listener.WaitForEvent(1, event)) { - const auto event_mask = event.GetType(); - if (event.BroadcasterMatchesRef(dap.broadcaster)) { - if (event_mask & eBroadcastBitStopProgressThread) { - done = true; - } - } else { - lldb::SBStructuredData data = - lldb::SBDebugger::GetProgressDataFromEvent(event); - - const uint64_t progress_id = - GetUintFromStructuredData(data, "progress_id"); - const uint64_t completed = GetUintFromStructuredData(data, "completed"); - const uint64_t total = GetUintFromStructuredData(data, "total"); - const std::string details = - GetStringFromStructuredData(data, "details"); - - if (completed == 0) { - if (total == UINT64_MAX) { - // This progress is non deterministic and won't get updated until it - // is completed. Send the "message" which will be the combined title - // and detail. The only other progress event for thus - // non-deterministic progress will be the completed event So there - // will be no need to update the detail. - const std::string message = - GetStringFromStructuredData(data, "message"); - dap.SendProgressEvent(progress_id, message.c_str(), completed, - total); - } else { - // This progress is deterministic and will receive updates, - // on the progress creation event VSCode will save the message in - // the create packet and use that as the title, so we send just the - // title in the progressCreate packet followed immediately by a - // detail packet, if there is any detail. - const std::string title = - GetStringFromStructuredData(data, "title"); - dap.SendProgressEvent(progress_id, title.c_str(), completed, total); - if (!details.empty()) - dap.SendProgressEvent(progress_id, details.c_str(), completed, - total); - } - } else { - // This progress event is either the end of the progress dialog, or an - // update with possible detail. The "detail" string we send to VS Code - // will be appended to the progress dialog's initial text from when it - // was created. - dap.SendProgressEvent(progress_id, details.c_str(), completed, total); - } - } - } - } -} - -static llvm::StringRef GetModuleEventReason(uint32_t event_mask) { - if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded) - return "new"; - if (event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded) - return "removed"; - assert(event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded || - event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged); - return "changed"; -} - -// All events from the debugger, target, process, thread and frames are -// received in this function that runs in its own thread. We are using a -// "FILE *" to output packets back to VS Code and they have mutexes in them -// them prevent multiple threads from writing simultaneously so no locking -// is required. -static void EventThreadFunction(DAP &dap) { - llvm::set_thread_name(dap.transport.GetClientName() + ".event_handler"); - lldb::SBEvent event; - lldb::SBListener listener = dap.debugger.GetListener(); - dap.broadcaster.AddListener(listener, eBroadcastBitStopEventThread); - dap.debugger.GetBroadcaster().AddListener(listener, eBroadcastBitError | - eBroadcastBitWarning); - bool done = false; - while (!done) { - if (listener.WaitForEvent(1, event)) { - const auto event_mask = event.GetType(); - if (lldb::SBProcess::EventIsProcessEvent(event)) { - lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event); - if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) { - auto state = lldb::SBProcess::GetStateFromEvent(event); - - DAP_LOG(dap.log, "State = {0}", state); - switch (state) { - case lldb::eStateConnected: - case lldb::eStateDetached: - case lldb::eStateInvalid: - case lldb::eStateUnloaded: - break; - case lldb::eStateAttaching: - case lldb::eStateCrashed: - case lldb::eStateLaunching: - case lldb::eStateStopped: - case lldb::eStateSuspended: - // Only report a stopped event if the process was not - // automatically restarted. - if (!lldb::SBProcess::GetRestartedFromEvent(event)) { - SendStdOutStdErr(dap, process); - SendThreadStoppedEvent(dap); - } - br... [truncated] ``````````
https://github.com/llvm/llvm-project/pull/139167 From lldb-commits at lists.llvm.org Thu May 8 15:03:41 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Thu, 08 May 2025 15:03:41 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Re-enable the lldb-dap tests (PR #138791) In-Reply-To: Message-ID: <681d2a3d.050a0220.2ad712.aac8@mx.google.com> JDevlieghere wrote: Attaching the bot output before it gets recycled: [stdio-3.txt](https://github.com/user-attachments/files/20111424/stdio-3.txt) https://github.com/llvm/llvm-project/pull/138791 From lldb-commits at lists.llvm.org Thu May 8 15:03:43 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 15:03:43 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Move the event and progress event threads into DAP (NFC) (PR #139167) In-Reply-To: Message-ID: <681d2a3f.050a0220.122171.b1bd@mx.google.com> github-actions[bot] wrote: :warning: C/C++ code formatter, clang-format found issues in your code. :warning:
You can test this locally with the following command: ``````````bash git-clang-format --diff HEAD~1 HEAD --extensions h,cpp -- lldb/tools/lldb-dap/DAP.cpp lldb/tools/lldb-dap/DAP.h lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp ``````````
View the diff from clang-format here. ``````````diff diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 169afd258..f6754b1f8 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -1624,7 +1624,7 @@ void DAP::EventThread() { std::string type = GetStringValue(data.GetValueForKey("type")); std::string message = GetStringValue(data.GetValueForKey("message")); SendOutput(OutputType::Important, - llvm::formatv("{0}: {1}", type, message).str()); + llvm::formatv("{0}: {1}", type, message).str()); } else if (event.BroadcasterMatchesRef(broadcaster)) { if (event_mask & eBroadcastBitStopEventThread) { done = true; ``````````
https://github.com/llvm/llvm-project/pull/139167 From lldb-commits at lists.llvm.org Thu May 8 15:15:37 2025 From: lldb-commits at lists.llvm.org (Felipe de Azevedo Piovezan via lldb-commits) Date: Thu, 08 May 2025 15:15:37 -0700 (PDT) Subject: [Lldb-commits] [lldb] Branch island debug (PR #139166) In-Reply-To: Message-ID: <681d2d09.050a0220.1cdd73.b335@mx.google.com> https://github.com/felipepiovezan approved this pull request. Fingers crossed this will give us enough info! https://github.com/llvm/llvm-project/pull/139166 From lldb-commits at lists.llvm.org Thu May 8 15:23:16 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Thu, 08 May 2025 15:23:16 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Adding a modules explorer to lldb-dap ext. (PR #138977) In-Reply-To: Message-ID: <681d2ed4.170a0220.142a41.8e72@mx.google.com> ashgti wrote: Yea, the address is being encoded as an plain integer not a hex string here https://github.com/llvm/llvm-project/blob/fa43e8f7de534984bc56b6319b72fd8ad422cbdf/lldb/tools/lldb-dap/JSONUtils.cpp#L492-L494 We can follow up with other adjustments to either the data. https://github.com/llvm/llvm-project/pull/138977 From lldb-commits at lists.llvm.org Thu May 8 15:25:32 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 15:25:32 -0700 (PDT) Subject: [Lldb-commits] [lldb] 611d81b - [lldb-dap] Adding a modules explorer to lldb-dap ext. (#138977) Message-ID: <681d2f5c.170a0220.b6a2b.7a88@mx.google.com> Author: John Harrison Date: 2025-05-08T15:25:28-07:00 New Revision: 611d81bd9304768f3cdb101d37c81d36b9762723 URL: https://github.com/llvm/llvm-project/commit/611d81bd9304768f3cdb101d37c81d36b9762723 DIFF: https://github.com/llvm/llvm-project/commit/611d81bd9304768f3cdb101d37c81d36b9762723.diff LOG: [lldb-dap] Adding a modules explorer to lldb-dap ext. (#138977) This creates a very basic module explorer for tracking and displaying loaded modules, reported by lldb-dap for the active debug session. This includes a basic session tracker that we can use to observe the debug session and collect specific information for additional visualizations in the lldb-dap ext. Here is a screenshot of the current visualization in the tree view. There is some unfortunate wrapping of the path, but it shows the basic support that could be extended in the future. Screenshot 2025-05-07 at 2 52 50 PM Added: lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts Modified: lldb/tools/lldb-dap/package-lock.json lldb/tools/lldb-dap/package.json lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts lldb/tools/lldb-dap/src-ts/disposable-context.ts lldb/tools/lldb-dap/src-ts/extension.ts Removed: ################################################################################ diff --git a/lldb/tools/lldb-dap/package-lock.json b/lldb/tools/lldb-dap/package-lock.json index ab5c7dc33a8e5..0a2b9e764067e 100644 --- a/lldb/tools/lldb-dap/package-lock.json +++ b/lldb/tools/lldb-dap/package-lock.json @@ -1,16 +1,17 @@ { "name": "lldb-dap", - "version": "0.2.10", + "version": "0.2.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "lldb-dap", - "version": "0.2.10", + "version": "0.2.13", "license": "Apache 2.0 License with LLVM exceptions", "devDependencies": { "@types/node": "^18.19.41", "@types/vscode": "1.75.0", + "@vscode/debugprotocol": "^1.68.0", "@vscode/vsce": "^3.2.2", "prettier": "^3.4.2", "prettier-plugin-curly": "^0.3.1", @@ -405,6 +406,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@vscode/debugprotocol": { + "version": "1.68.0", + "resolved": "https://registry.npmjs.org/@vscode/debugprotocol/-/debugprotocol-1.68.0.tgz", + "integrity": "sha512-2J27dysaXmvnfuhFGhfeuxfHRXunqNPxtBoR3koiTOA9rdxWNDTa1zIFLCFMSHJ9MPTPKFcBeblsyaCJCIlQxg==", + "dev": true, + "license": "MIT" + }, "node_modules/@vscode/vsce": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.2.2.tgz", diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json index a7631464d236a..e3e46526f379f 100644 --- a/lldb/tools/lldb-dap/package.json +++ b/lldb/tools/lldb-dap/package.json @@ -30,9 +30,10 @@ "devDependencies": { "@types/node": "^18.19.41", "@types/vscode": "1.75.0", + "@vscode/debugprotocol": "^1.68.0", "@vscode/vsce": "^3.2.2", - "prettier-plugin-curly": "^0.3.1", "prettier": "^3.4.2", + "prettier-plugin-curly": "^0.3.1", "typescript": "^5.7.3" }, "activationEvents": [ @@ -760,6 +761,16 @@ } ] } - ] + ], + "views": { + "debug": [ + { + "id": "lldb-dap.modules", + "name": "Modules", + "when": "inDebugMode && debugType == 'lldb-dap'", + "icon": "$(symbol-module)" + } + ] + } } } diff --git a/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts b/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts index c91b101f4a9ba..957bc5e1eb956 100644 --- a/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts +++ b/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts @@ -78,7 +78,7 @@ export class LLDBDapConfigurationProvider debugConfiguration: vscode.DebugConfiguration, token?: vscode.CancellationToken, ): Promise { - let config = vscode.workspace.getConfiguration("lldb-dap.defaults"); + let config = vscode.workspace.getConfiguration("lldb-dap"); for (const [key, cfg] of Object.entries(configurations)) { if (Reflect.has(debugConfiguration, key)) { continue; diff --git a/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts b/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts new file mode 100644 index 0000000000000..1ce190938d9c7 --- /dev/null +++ b/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts @@ -0,0 +1,109 @@ +import { DebugProtocol } from "@vscode/debugprotocol"; +import * as vscode from "vscode"; + +/** A helper type for mapping event types to their corresponding data type. */ +// prettier-ignore +interface EventMap { + "module": DebugProtocol.ModuleEvent; +} + +/** A type assertion to check if a ProtocolMessage is an event or if it is a specific event. */ +function isEvent( + message: DebugProtocol.ProtocolMessage, +): message is DebugProtocol.Event; +function isEvent( + message: DebugProtocol.ProtocolMessage, + event: K, +): message is EventMap[K]; +function isEvent( + message: DebugProtocol.ProtocolMessage, + event?: string, +): boolean { + return ( + message.type === "event" && + (!event || (message as DebugProtocol.Event).event === event) + ); +} + +/** Tracks lldb-dap sessions for data visualizers. */ +export class DebugSessionTracker + implements vscode.DebugAdapterTrackerFactory, vscode.Disposable +{ + /** + * Tracks active modules for each debug sessions. + * + * The modules are kept in an array to maintain the load order of the modules. + */ + private modules = new Map(); + private modulesChanged = new vscode.EventEmitter(); + + /** + * Fired when modules are changed for any active debug session. + * + * Use `debugSessionModules` to retieve the active modules for a given debug session. + */ + onDidChangeModules: vscode.Event = this.modulesChanged.event; + + dispose() { + this.modules.clear(); + this.modulesChanged.dispose(); + } + + createDebugAdapterTracker( + session: vscode.DebugSession, + ): vscode.ProviderResult { + return { + onDidSendMessage: (message) => this.onDidSendMessage(session, message), + onExit: () => this.onExit(session), + }; + } + + /** + * Retrieves the modules for the given debug session. + * + * Modules are returned in load order. + */ + debugSessionModules(session: vscode.DebugSession): DebugProtocol.Module[] { + return this.modules.get(session) ?? []; + } + + /** Clear information from the active session. */ + private onExit(session: vscode.DebugSession) { + this.modules.delete(session); + this.modulesChanged.fire(); + } + + private onDidSendMessage( + session: vscode.DebugSession, + message: DebugProtocol.ProtocolMessage, + ) { + if (isEvent(message, "module")) { + const { module, reason } = message.body; + const modules = this.modules.get(session) ?? []; + switch (reason) { + case "new": + case "changed": { + const index = modules.findIndex((m) => m.id === module.id); + if (index !== -1) { + modules[index] = module; + } else { + modules.push(module); + } + break; + } + case "removed": { + const index = modules.findIndex((m) => m.id === module.id); + if (index !== -1) { + modules.splice(index, 1); + } + break; + } + default: + console.error("unexpected module event reason"); + break; + } + this.modules.set(session, modules); + this.modulesChanged.fire(); + } + } +} diff --git a/lldb/tools/lldb-dap/src-ts/disposable-context.ts b/lldb/tools/lldb-dap/src-ts/disposable-context.ts index 39d9f18d2d85f..42ece763d247f 100644 --- a/lldb/tools/lldb-dap/src-ts/disposable-context.ts +++ b/lldb/tools/lldb-dap/src-ts/disposable-context.ts @@ -21,7 +21,7 @@ export class DisposableContext implements vscode.Disposable { * * @param disposable The disposable to register. */ - public pushSubscription(disposable: vscode.Disposable) { - this._disposables.push(disposable); + public pushSubscription(...disposable: vscode.Disposable[]) { + this._disposables.push(...disposable); } } diff --git a/lldb/tools/lldb-dap/src-ts/extension.ts b/lldb/tools/lldb-dap/src-ts/extension.ts index 0b014f953d5ba..a5c0a09ae60cf 100644 --- a/lldb/tools/lldb-dap/src-ts/extension.ts +++ b/lldb/tools/lldb-dap/src-ts/extension.ts @@ -5,6 +5,8 @@ import { DisposableContext } from "./disposable-context"; import { LaunchUriHandler } from "./uri-launch-handler"; import { LLDBDapConfigurationProvider } from "./debug-configuration-provider"; import { LLDBDapServer } from "./lldb-dap-server"; +import { DebugSessionTracker } from "./debug-session-tracker"; +import { ModulesDataProvider } from "./ui/modules-data-provider"; /** * This class represents the extension and manages its life cycle. Other extensions @@ -15,23 +17,27 @@ export class LLDBDapExtension extends DisposableContext { super(); const lldbDapServer = new LLDBDapServer(); - this.pushSubscription(lldbDapServer); + const sessionTracker = new DebugSessionTracker(); this.pushSubscription( + lldbDapServer, + sessionTracker, vscode.debug.registerDebugConfigurationProvider( "lldb-dap", new LLDBDapConfigurationProvider(lldbDapServer), ), - ); - - this.pushSubscription( vscode.debug.registerDebugAdapterDescriptorFactory( "lldb-dap", new LLDBDapDescriptorFactory(), ), - ); - - this.pushSubscription( + vscode.debug.registerDebugAdapterTrackerFactory( + "lldb-dap", + sessionTracker, + ), + vscode.window.registerTreeDataProvider( + "lldb-dap.modules", + new ModulesDataProvider(sessionTracker), + ), vscode.window.registerUriHandler(new LaunchUriHandler()), ); } diff --git a/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts b/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts new file mode 100644 index 0000000000000..5af3d52e9870c --- /dev/null +++ b/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts @@ -0,0 +1,59 @@ +import * as vscode from "vscode"; +import { DebugProtocol } from "@vscode/debugprotocol"; +import { DebugSessionTracker } from "../debug-session-tracker"; + +/** A tree data provider for listing loaded modules for the active debug session. */ +export class ModulesDataProvider + implements vscode.TreeDataProvider +{ + private changeTreeData = new vscode.EventEmitter(); + readonly onDidChangeTreeData = this.changeTreeData.event; + + constructor(private readonly tracker: DebugSessionTracker) { + tracker.onDidChangeModules(() => this.changeTreeData.fire()); + vscode.debug.onDidChangeActiveDebugSession(() => + this.changeTreeData.fire(), + ); + } + + getTreeItem(module: DebugProtocol.Module): vscode.TreeItem { + let treeItem = new vscode.TreeItem(/*label=*/ module.name); + if (module.path) { + treeItem.description = `${module.id} -- ${module.path}`; + } else { + treeItem.description = `${module.id}`; + } + + const tooltip = new vscode.MarkdownString(); + tooltip.appendMarkdown(`# Module '${module.name}'\n\n`); + tooltip.appendMarkdown(`- **id** : ${module.id}\n`); + if (module.addressRange) { + tooltip.appendMarkdown(`- **load address** : ${module.addressRange}\n`); + } + if (module.path) { + tooltip.appendMarkdown(`- **path** : ${module.path}\n`); + } + if (module.version) { + tooltip.appendMarkdown(`- **version** : ${module.version}\n`); + } + if (module.symbolStatus) { + tooltip.appendMarkdown(`- **symbol status** : ${module.symbolStatus}\n`); + } + if (module.symbolFilePath) { + tooltip.appendMarkdown( + `- **symbol file path** : ${module.symbolFilePath}\n`, + ); + } + + treeItem.tooltip = tooltip; + return treeItem; + } + + getChildren(): DebugProtocol.Module[] { + if (!vscode.debug.activeDebugSession) { + return []; + } + + return this.tracker.debugSessionModules(vscode.debug.activeDebugSession); + } +} From lldb-commits at lists.llvm.org Thu May 8 15:25:35 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Thu, 08 May 2025 15:25:35 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Adding a modules explorer to lldb-dap ext. (PR #138977) In-Reply-To: Message-ID: <681d2f5f.170a0220.2683be.90ad@mx.google.com> https://github.com/ashgti closed https://github.com/llvm/llvm-project/pull/138977 From lldb-commits at lists.llvm.org Thu May 8 15:47:40 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Thu, 08 May 2025 15:47:40 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Move the event and progress event threads into DAP (NFC) (PR #139167) In-Reply-To: Message-ID: <681d348c.170a0220.24dd0f.acae@mx.google.com> https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/139167 >From 724a9e9a13de896e43935b2c77e8398a3ed318c5 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Thu, 8 May 2025 15:47:22 -0700 Subject: [PATCH] [lldb-dap] Move the event and progress event threads into DAP (NFC) Move the event and progress event threads into the DAP class. Currently both are implemented in the InitializeRequestHandler, but it doesn't really belong there because the threads are not tied to that request, they just launch them. --- lldb/tools/lldb-dap/DAP.cpp | 261 +++++++++++++++++- lldb/tools/lldb-dap/DAP.h | 14 +- .../Handler/InitializeRequestHandler.cpp | 253 +---------------- 3 files changed, 269 insertions(+), 259 deletions(-) diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 62c60cc3a9b3b..552904fe95672 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -8,6 +8,7 @@ #include "DAP.h" #include "DAPLog.h" +#include "EventHelper.h" #include "Handler/RequestHandler.h" #include "Handler/ResponseHandler.h" #include "JSONUtils.h" @@ -20,6 +21,7 @@ #include "lldb/API/SBBreakpoint.h" #include "lldb/API/SBCommandInterpreter.h" #include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBEvent.h" #include "lldb/API/SBLanguageRuntime.h" #include "lldb/API/SBListener.h" #include "lldb/API/SBProcess.h" @@ -52,6 +54,7 @@ #include #include #include +#include #include #include @@ -77,6 +80,48 @@ const char DEV_NULL[] = "/dev/null"; namespace lldb_dap { +static std::string GetStringFromStructuredData(lldb::SBStructuredData &data, + const char *key) { + lldb::SBStructuredData keyValue = data.GetValueForKey(key); + if (!keyValue) + return std::string(); + + const size_t length = keyValue.GetStringValue(nullptr, 0); + + if (length == 0) + return std::string(); + + std::string str(length + 1, 0); + keyValue.GetStringValue(&str[0], length + 1); + return str; +} + +static uint64_t GetUintFromStructuredData(lldb::SBStructuredData &data, + const char *key) { + lldb::SBStructuredData keyValue = data.GetValueForKey(key); + + if (!keyValue.IsValid()) + return 0; + return keyValue.GetUnsignedIntegerValue(); +} + +static llvm::StringRef GetModuleEventReason(uint32_t event_mask) { + if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded) + return "new"; + if (event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded) + return "removed"; + assert(event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded || + event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged); + return "changed"; +} + +/// Return string with first character capitalized. +static std::string capitalize(llvm::StringRef str) { + if (str.empty()) + return ""; + return ((llvm::Twine)llvm::toUpper(str[0]) + str.drop_front()).str(); +} + llvm::StringRef DAP::debug_adapter_path = ""; DAP::DAP(Log *log, const ReplMode default_repl_mode, @@ -94,13 +139,6 @@ DAP::DAP(Log *log, const ReplMode default_repl_mode, DAP::~DAP() = default; -/// Return string with first character capitalized. -static std::string capitalize(llvm::StringRef str) { - if (str.empty()) - return ""; - return ((llvm::Twine)llvm::toUpper(str[0]) + str.drop_front()).str(); -} - void DAP::PopulateExceptionBreakpoints() { llvm::call_once(init_exception_breakpoints_flag, [this]() { exception_breakpoints = std::vector{}; @@ -1390,4 +1428,213 @@ protocol::Capabilities DAP::GetCapabilities() { return capabilities; } +void DAP::StartEventThread() { + event_thread = std::thread(&DAP::EventThread, this); +} + +void DAP::StartProgressEventThread() { + progress_event_thread = std::thread(&DAP::ProgressEventThread, this); +} + +void DAP::ProgressEventThread() { + lldb::SBListener listener("lldb-dap.progress.listener"); + debugger.GetBroadcaster().AddListener( + listener, lldb::SBDebugger::eBroadcastBitProgress | + lldb::SBDebugger::eBroadcastBitExternalProgress); + broadcaster.AddListener(listener, eBroadcastBitStopProgressThread); + lldb::SBEvent event; + bool done = false; + while (!done) { + if (listener.WaitForEvent(1, event)) { + const auto event_mask = event.GetType(); + if (event.BroadcasterMatchesRef(broadcaster)) { + if (event_mask & eBroadcastBitStopProgressThread) { + done = true; + } + } else { + lldb::SBStructuredData data = + lldb::SBDebugger::GetProgressDataFromEvent(event); + + const uint64_t progress_id = + GetUintFromStructuredData(data, "progress_id"); + const uint64_t completed = GetUintFromStructuredData(data, "completed"); + const uint64_t total = GetUintFromStructuredData(data, "total"); + const std::string details = + GetStringFromStructuredData(data, "details"); + + if (completed == 0) { + if (total == UINT64_MAX) { + // This progress is non deterministic and won't get updated until it + // is completed. Send the "message" which will be the combined title + // and detail. The only other progress event for thus + // non-deterministic progress will be the completed event So there + // will be no need to update the detail. + const std::string message = + GetStringFromStructuredData(data, "message"); + SendProgressEvent(progress_id, message.c_str(), completed, total); + } else { + // This progress is deterministic and will receive updates, + // on the progress creation event VSCode will save the message in + // the create packet and use that as the title, so we send just the + // title in the progressCreate packet followed immediately by a + // detail packet, if there is any detail. + const std::string title = + GetStringFromStructuredData(data, "title"); + SendProgressEvent(progress_id, title.c_str(), completed, total); + if (!details.empty()) + SendProgressEvent(progress_id, details.c_str(), completed, total); + } + } else { + // This progress event is either the end of the progress dialog, or an + // update with possible detail. The "detail" string we send to VS Code + // will be appended to the progress dialog's initial text from when it + // was created. + SendProgressEvent(progress_id, details.c_str(), completed, total); + } + } + } + } +} + +// All events from the debugger, target, process, thread and frames are +// received in this function that runs in its own thread. We are using a +// "FILE *" to output packets back to VS Code and they have mutexes in them +// them prevent multiple threads from writing simultaneously so no locking +// is required. +void DAP::EventThread() { + llvm::set_thread_name(transport.GetClientName() + ".event_handler"); + lldb::SBEvent event; + lldb::SBListener listener = debugger.GetListener(); + broadcaster.AddListener(listener, eBroadcastBitStopEventThread); + debugger.GetBroadcaster().AddListener( + listener, lldb::eBroadcastBitError | lldb::eBroadcastBitWarning); + bool done = false; + while (!done) { + if (listener.WaitForEvent(1, event)) { + const auto event_mask = event.GetType(); + if (lldb::SBProcess::EventIsProcessEvent(event)) { + lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event); + if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) { + auto state = lldb::SBProcess::GetStateFromEvent(event); + switch (state) { + case lldb::eStateConnected: + case lldb::eStateDetached: + case lldb::eStateInvalid: + case lldb::eStateUnloaded: + break; + case lldb::eStateAttaching: + case lldb::eStateCrashed: + case lldb::eStateLaunching: + case lldb::eStateStopped: + case lldb::eStateSuspended: + // Only report a stopped event if the process was not + // automatically restarted. + if (!lldb::SBProcess::GetRestartedFromEvent(event)) { + SendStdOutStdErr(*this, process); + SendThreadStoppedEvent(*this); + } + break; + case lldb::eStateRunning: + case lldb::eStateStepping: + WillContinue(); + SendContinuedEvent(*this); + break; + case lldb::eStateExited: + lldb::SBStream stream; + process.GetStatus(stream); + SendOutput(OutputType::Console, stream.GetData()); + + // When restarting, we can get an "exited" event for the process we + // just killed with the old PID, or even with no PID. In that case + // we don't have to terminate the session. + if (process.GetProcessID() == LLDB_INVALID_PROCESS_ID || + process.GetProcessID() == restarting_process_id) { + restarting_process_id = LLDB_INVALID_PROCESS_ID; + } else { + // Run any exit LLDB commands the user specified in the + // launch.json + RunExitCommands(); + SendProcessExitedEvent(*this, process); + SendTerminatedEvent(); + done = true; + } + break; + } + } else if ((event_mask & lldb::SBProcess::eBroadcastBitSTDOUT) || + (event_mask & lldb::SBProcess::eBroadcastBitSTDERR)) { + SendStdOutStdErr(*this, process); + } + } else if (lldb::SBTarget::EventIsTargetEvent(event)) { + if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded || + event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded || + event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded || + event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged) { + llvm::StringRef reason = GetModuleEventReason(event_mask); + const uint32_t num_modules = + lldb::SBTarget::GetNumModulesFromEvent(event); + for (uint32_t i = 0; i < num_modules; ++i) { + lldb::SBModule module = + lldb::SBTarget::GetModuleAtIndexFromEvent(i, event); + if (!module.IsValid()) + continue; + + llvm::json::Object body; + body.try_emplace("reason", reason); + body.try_emplace("module", CreateModule(target, module)); + llvm::json::Object module_event = CreateEventObject("module"); + module_event.try_emplace("body", std::move(body)); + SendJSON(llvm::json::Value(std::move(module_event))); + } + } + } else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) { + if (event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged) { + auto event_type = + lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event); + auto bp = Breakpoint( + *this, lldb::SBBreakpoint::GetBreakpointFromEvent(event)); + // If the breakpoint was set through DAP, it will have the + // BreakpointBase::kDAPBreakpointLabel. Regardless of whether + // locations were added, removed, or resolved, the breakpoint isn't + // going away and the reason is always "changed". + if ((event_type & lldb::eBreakpointEventTypeLocationsAdded || + event_type & lldb::eBreakpointEventTypeLocationsRemoved || + event_type & lldb::eBreakpointEventTypeLocationsResolved) && + bp.MatchesName(BreakpointBase::kDAPBreakpointLabel)) { + // As the DAP client already knows the path of this breakpoint, we + // don't need to send it back as part of the "changed" event. This + // avoids sending paths that should be source mapped. Note that + // CreateBreakpoint doesn't apply source mapping and certain + // implementation ignore the source part of this event anyway. + llvm::json::Value source_bp = CreateBreakpoint(&bp); + source_bp.getAsObject()->erase("source"); + + llvm::json::Object body; + body.try_emplace("breakpoint", source_bp); + body.try_emplace("reason", "changed"); + + llvm::json::Object bp_event = CreateEventObject("breakpoint"); + bp_event.try_emplace("body", std::move(body)); + + SendJSON(llvm::json::Value(std::move(bp_event))); + } + } + } else if (event_mask & lldb::eBroadcastBitError || + event_mask & lldb::eBroadcastBitWarning) { + lldb::SBStructuredData data = + lldb::SBDebugger::GetDiagnosticFromEvent(event); + if (!data.IsValid()) + continue; + std::string type = GetStringValue(data.GetValueForKey("type")); + std::string message = GetStringValue(data.GetValueForKey("message")); + SendOutput(OutputType::Important, + llvm::formatv("{0}: {1}", type, message).str()); + } else if (event.BroadcasterMatchesRef(broadcaster)) { + if (event_mask & eBroadcastBitStopEventThread) { + done = true; + } + } + } + } +} + } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index b581ae759b1bc..afeda8d81efb0 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -167,8 +167,6 @@ struct DAP { lldb::SBTarget target; Variables variables; lldb::SBBroadcaster broadcaster; - std::thread event_thread; - std::thread progress_event_thread; llvm::StringMap source_breakpoints; FunctionBreakpointMap function_breakpoints; InstructionBreakpointMap instruction_breakpoints; @@ -418,7 +416,19 @@ struct DAP { lldb::SBMutex GetAPIMutex() const { return target.GetAPIMutex(); } + void StartEventThread(); + void StartProgressEventThread(); + private: + /// Event threads. + /// @{ + void EventThread(); + void ProgressEventThread(); + + std::thread event_thread; + std::thread progress_event_thread; + /// @} + /// Queue for all incoming messages. std::deque m_queue; std::deque m_pending_queue; diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp index aa947d3cb5ab9..cdcbdfa7f48e3 100644 --- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp @@ -12,255 +12,11 @@ #include "LLDBUtils.h" #include "Protocol/ProtocolRequests.h" #include "RequestHandler.h" -#include "lldb/API/SBEvent.h" -#include "lldb/API/SBListener.h" -#include "lldb/API/SBStream.h" #include "lldb/API/SBTarget.h" -#include -using namespace lldb; +using namespace lldb_dap; using namespace lldb_dap::protocol; -namespace lldb_dap { - -static std::string GetStringFromStructuredData(lldb::SBStructuredData &data, - const char *key) { - lldb::SBStructuredData keyValue = data.GetValueForKey(key); - if (!keyValue) - return std::string(); - - const size_t length = keyValue.GetStringValue(nullptr, 0); - - if (length == 0) - return std::string(); - - std::string str(length + 1, 0); - keyValue.GetStringValue(&str[0], length + 1); - return str; -} - -static uint64_t GetUintFromStructuredData(lldb::SBStructuredData &data, - const char *key) { - lldb::SBStructuredData keyValue = data.GetValueForKey(key); - - if (!keyValue.IsValid()) - return 0; - return keyValue.GetUnsignedIntegerValue(); -} - -void ProgressEventThreadFunction(DAP &dap) { - lldb::SBListener listener("lldb-dap.progress.listener"); - dap.debugger.GetBroadcaster().AddListener( - listener, lldb::SBDebugger::eBroadcastBitProgress | - lldb::SBDebugger::eBroadcastBitExternalProgress); - dap.broadcaster.AddListener(listener, eBroadcastBitStopProgressThread); - lldb::SBEvent event; - bool done = false; - while (!done) { - if (listener.WaitForEvent(1, event)) { - const auto event_mask = event.GetType(); - if (event.BroadcasterMatchesRef(dap.broadcaster)) { - if (event_mask & eBroadcastBitStopProgressThread) { - done = true; - } - } else { - lldb::SBStructuredData data = - lldb::SBDebugger::GetProgressDataFromEvent(event); - - const uint64_t progress_id = - GetUintFromStructuredData(data, "progress_id"); - const uint64_t completed = GetUintFromStructuredData(data, "completed"); - const uint64_t total = GetUintFromStructuredData(data, "total"); - const std::string details = - GetStringFromStructuredData(data, "details"); - - if (completed == 0) { - if (total == UINT64_MAX) { - // This progress is non deterministic and won't get updated until it - // is completed. Send the "message" which will be the combined title - // and detail. The only other progress event for thus - // non-deterministic progress will be the completed event So there - // will be no need to update the detail. - const std::string message = - GetStringFromStructuredData(data, "message"); - dap.SendProgressEvent(progress_id, message.c_str(), completed, - total); - } else { - // This progress is deterministic and will receive updates, - // on the progress creation event VSCode will save the message in - // the create packet and use that as the title, so we send just the - // title in the progressCreate packet followed immediately by a - // detail packet, if there is any detail. - const std::string title = - GetStringFromStructuredData(data, "title"); - dap.SendProgressEvent(progress_id, title.c_str(), completed, total); - if (!details.empty()) - dap.SendProgressEvent(progress_id, details.c_str(), completed, - total); - } - } else { - // This progress event is either the end of the progress dialog, or an - // update with possible detail. The "detail" string we send to VS Code - // will be appended to the progress dialog's initial text from when it - // was created. - dap.SendProgressEvent(progress_id, details.c_str(), completed, total); - } - } - } - } -} - -static llvm::StringRef GetModuleEventReason(uint32_t event_mask) { - if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded) - return "new"; - if (event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded) - return "removed"; - assert(event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded || - event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged); - return "changed"; -} - -// All events from the debugger, target, process, thread and frames are -// received in this function that runs in its own thread. We are using a -// "FILE *" to output packets back to VS Code and they have mutexes in them -// them prevent multiple threads from writing simultaneously so no locking -// is required. -static void EventThreadFunction(DAP &dap) { - llvm::set_thread_name(dap.transport.GetClientName() + ".event_handler"); - lldb::SBEvent event; - lldb::SBListener listener = dap.debugger.GetListener(); - dap.broadcaster.AddListener(listener, eBroadcastBitStopEventThread); - dap.debugger.GetBroadcaster().AddListener(listener, eBroadcastBitError | - eBroadcastBitWarning); - bool done = false; - while (!done) { - if (listener.WaitForEvent(1, event)) { - const auto event_mask = event.GetType(); - if (lldb::SBProcess::EventIsProcessEvent(event)) { - lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event); - if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) { - auto state = lldb::SBProcess::GetStateFromEvent(event); - - DAP_LOG(dap.log, "State = {0}", state); - switch (state) { - case lldb::eStateConnected: - case lldb::eStateDetached: - case lldb::eStateInvalid: - case lldb::eStateUnloaded: - break; - case lldb::eStateAttaching: - case lldb::eStateCrashed: - case lldb::eStateLaunching: - case lldb::eStateStopped: - case lldb::eStateSuspended: - // Only report a stopped event if the process was not - // automatically restarted. - if (!lldb::SBProcess::GetRestartedFromEvent(event)) { - SendStdOutStdErr(dap, process); - SendThreadStoppedEvent(dap); - } - break; - case lldb::eStateRunning: - case lldb::eStateStepping: - dap.WillContinue(); - SendContinuedEvent(dap); - break; - case lldb::eStateExited: - lldb::SBStream stream; - process.GetStatus(stream); - dap.SendOutput(OutputType::Console, stream.GetData()); - - // When restarting, we can get an "exited" event for the process we - // just killed with the old PID, or even with no PID. In that case - // we don't have to terminate the session. - if (process.GetProcessID() == LLDB_INVALID_PROCESS_ID || - process.GetProcessID() == dap.restarting_process_id) { - dap.restarting_process_id = LLDB_INVALID_PROCESS_ID; - } else { - // Run any exit LLDB commands the user specified in the - // launch.json - dap.RunExitCommands(); - SendProcessExitedEvent(dap, process); - dap.SendTerminatedEvent(); - done = true; - } - break; - } - } else if ((event_mask & lldb::SBProcess::eBroadcastBitSTDOUT) || - (event_mask & lldb::SBProcess::eBroadcastBitSTDERR)) { - SendStdOutStdErr(dap, process); - } - } else if (lldb::SBTarget::EventIsTargetEvent(event)) { - if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded || - event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded || - event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded || - event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged) { - llvm::StringRef reason = GetModuleEventReason(event_mask); - const uint32_t num_modules = SBTarget::GetNumModulesFromEvent(event); - for (uint32_t i = 0; i < num_modules; ++i) { - lldb::SBModule module = - SBTarget::GetModuleAtIndexFromEvent(i, event); - if (!module.IsValid()) - continue; - - llvm::json::Object body; - body.try_emplace("reason", reason); - body.try_emplace("module", CreateModule(dap.target, module)); - llvm::json::Object module_event = CreateEventObject("module"); - module_event.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(module_event))); - } - } - } else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) { - if (event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged) { - auto event_type = - lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event); - auto bp = Breakpoint( - dap, lldb::SBBreakpoint::GetBreakpointFromEvent(event)); - // If the breakpoint was set through DAP, it will have the - // BreakpointBase::kDAPBreakpointLabel. Regardless of whether - // locations were added, removed, or resolved, the breakpoint isn't - // going away and the reason is always "changed". - if ((event_type & lldb::eBreakpointEventTypeLocationsAdded || - event_type & lldb::eBreakpointEventTypeLocationsRemoved || - event_type & lldb::eBreakpointEventTypeLocationsResolved) && - bp.MatchesName(BreakpointBase::kDAPBreakpointLabel)) { - // As the DAP client already knows the path of this breakpoint, we - // don't need to send it back as part of the "changed" event. This - // avoids sending paths that should be source mapped. Note that - // CreateBreakpoint doesn't apply source mapping and certain - // implementation ignore the source part of this event anyway. - llvm::json::Value source_bp = CreateBreakpoint(&bp); - source_bp.getAsObject()->erase("source"); - - llvm::json::Object body; - body.try_emplace("breakpoint", source_bp); - body.try_emplace("reason", "changed"); - - llvm::json::Object bp_event = CreateEventObject("breakpoint"); - bp_event.try_emplace("body", std::move(body)); - - dap.SendJSON(llvm::json::Value(std::move(bp_event))); - } - } - } else if (event_mask & eBroadcastBitError || - event_mask & eBroadcastBitWarning) { - SBStructuredData data = SBDebugger::GetDiagnosticFromEvent(event); - if (!data.IsValid()) - continue; - std::string type = GetStringValue(data.GetValueForKey("type")); - std::string message = GetStringValue(data.GetValueForKey("message")); - dap.SendOutput(OutputType::Important, - llvm::formatv("{0}: {1}", type, message).str()); - } else if (event.BroadcasterMatchesRef(dap.broadcaster)) { - if (event_mask & eBroadcastBitStopEventThread) { - done = true; - } - } - } - } -} - /// Initialize request; value of command field is 'initialize'. llvm::Expected InitializeRequestHandler::Run( const InitializeRequestArguments &arguments) const { @@ -314,12 +70,11 @@ llvm::Expected InitializeRequestHandler::Run( "Sends an DAP event to the client."); if (arguments.supportedFeatures.contains(eClientFeatureProgressReporting)) - dap.progress_event_thread = - std::thread(ProgressEventThreadFunction, std::ref(dap)); + dap.StartProgressEventThread(); // Start our event thread so we can receive events from the debugger, target, // process and more. - dap.event_thread = std::thread(EventThreadFunction, std::ref(dap)); + dap.StartEventThread(); return dap.GetCapabilities(); } @@ -327,5 +82,3 @@ llvm::Expected InitializeRequestHandler::Run( void InitializeRequestHandler::PostRun() const { dap.SendJSON(CreateEventObject("initialized")); } - -} // namespace lldb_dap From lldb-commits at lists.llvm.org Thu May 8 15:59:43 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 15:59:43 -0700 (PDT) Subject: [Lldb-commits] [lldb] Don't create instance of `SymbolFileDWARFDebugMap` for non-Mach-O files (PR #139170) Message-ID: https://github.com/royitaqi created https://github.com/llvm/llvm-project/pull/139170 # Change `SymbolFileDWARFDebugMap::CreateInstance()` will return `nullptr` if the file is not a Mach-O. # Benefit This may improve **Linux** debugger launch time by skipping the creation of `SymbolFileDWARFDebugMap` during the [`SymbolFile::FindPlugin()` call](https://fburl.com/hi1w8dil), which loops through a list of `SymbolFile` plugins and tries to find the one that provides the best abilities. If the `SymbolFileDWARFDebugMap` is created during this loop, it will load the symbol table of the file in question and loop through all the compile units in the debug map (the OSO entries) to calculate the abilities. # Tests Two tests are added to verify the creation and the absence for Mach-O and non-Mach-O files. Ran `ninja check-lldb` on both Darwin and Linux. No regression spotted. See results compared for [mac](https://pastebin.com/NdqTK4HC) and [linux](https://pastebin.com/bvjVJbi1). >From c7432d04896c1eea67ed6a6d5f4b4c28941e5a90 Mon Sep 17 00:00:00 2001 From: Roy Shi Date: Thu, 8 May 2025 07:38:20 -0700 Subject: [PATCH 1/3] [lldb] Do not create SymbolFileDWARFDebugMap for non-Mach-O files --- .../Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp index 961c212e2e6dc..f94c756868953 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp @@ -10,7 +10,6 @@ #include "DWARFCompileUnit.h" #include "DWARFDebugAranges.h" #include "DWARFDebugInfo.h" - #include "lldb/Core/Module.h" #include "lldb/Core/ModuleList.h" #include "lldb/Core/PluginManager.h" @@ -34,12 +33,12 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/ScopedPrinter.h" - #include "lldb/Target/StackFrame.h" #include "LogChannelDWARF.h" #include "SymbolFileDWARF.h" #include "lldb/lldb-private-enumerations.h" +#include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h" #include #include @@ -246,6 +245,8 @@ llvm::StringRef SymbolFileDWARFDebugMap::GetPluginDescriptionStatic() { } SymbolFile *SymbolFileDWARFDebugMap::CreateInstance(ObjectFileSP objfile_sp) { + if (objfile_sp->GetPluginName() != ObjectFileMachO::GetPluginNameStatic()) + return nullptr; return new SymbolFileDWARFDebugMap(std::move(objfile_sp)); } >From 6c1ce45355ed1f955727e830019e7cafec4b0070 Mon Sep 17 00:00:00 2001 From: Roy Shi Date: Thu, 8 May 2025 10:22:25 -0700 Subject: [PATCH 2/3] Add test, but failing --- .../unittests/SymbolFile/DWARF/CMakeLists.txt | 1 + .../DWARF/SymbolFileDWARFDebugMapTests.cpp | 84 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 lldb/unittests/SymbolFile/DWARF/SymbolFileDWARFDebugMapTests.cpp diff --git a/lldb/unittests/SymbolFile/DWARF/CMakeLists.txt b/lldb/unittests/SymbolFile/DWARF/CMakeLists.txt index d5b0be7ea2a28..5aacb24fc5206 100644 --- a/lldb/unittests/SymbolFile/DWARF/CMakeLists.txt +++ b/lldb/unittests/SymbolFile/DWARF/CMakeLists.txt @@ -4,6 +4,7 @@ add_lldb_unittest(SymbolFileDWARFTests DWARFDIETest.cpp DWARFIndexCachingTest.cpp DWARFUnitTest.cpp + SymbolFileDWARFDebugMapTests.cpp SymbolFileDWARFTests.cpp XcodeSDKModuleTests.cpp diff --git a/lldb/unittests/SymbolFile/DWARF/SymbolFileDWARFDebugMapTests.cpp b/lldb/unittests/SymbolFile/DWARF/SymbolFileDWARFDebugMapTests.cpp new file mode 100644 index 0000000000000..399510e519521 --- /dev/null +++ b/lldb/unittests/SymbolFile/DWARF/SymbolFileDWARFDebugMapTests.cpp @@ -0,0 +1,84 @@ +//===-- SymbolFileDWARFDebugMapTests.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 "Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h" +#include "TestingSupport/TestUtilities.h" + +#include "lldb/Core/Module.h" +#include "llvm/Testing/Support/Error.h" + +#include "gtest/gtest.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::plugin::dwarf; + +TEST(SymbolFileDWARFDebugMapTests, CreateInstanceReturnNullForNonMachOFile) { + // Make sure we don't crash parsing a null unit DIE. + const char *yamldata = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_386 +DWARF: + debug_abbrev: + - Table: + - Code: 0x00000001 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_addr_base + Form: DW_FORM_sec_offset + debug_info: + - Version: 5 + AddrSize: 4 + UnitType: DW_UT_compile + Entries: + - AbbrCode: 0x00000001 + Values: + - Value: 0x8 # Offset of the first Address past the header + - AbbrCode: 0x0 + debug_addr: + - Version: 5 + AddressSize: 4 + Entries: + - Address: 0x1234 + - Address: 0x5678 + debug_line: + - Length: 42 + Version: 2 + PrologueLength: 36 + MinInstLength: 1 + DefaultIsStmt: 1 + LineBase: 251 + LineRange: 14 + OpcodeBase: 13 + StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ] + IncludeDirs: + - '/tmp' + Files: + - Name: main.cpp + DirIdx: 1 + ModTime: 0 + Length: 0 +)"; + + // bool waiting = true; + // while (waiting) {} + + llvm::Expected file = TestFile::fromYaml(yamldata); + EXPECT_THAT_EXPECTED(file, llvm::Succeeded()); + auto module_sp = std::make_shared(file->moduleSpec()); + auto object_file = module_sp->GetObjectFile(); + ASSERT_NE(object_file, nullptr); + + auto debug_map = SymbolFileDWARFDebugMap::CreateInstance(object_file->shared_from_this()); + ASSERT_EQ(debug_map, nullptr); +} >From 8bf38e24f941b431b90dd7ff60a5ff4514410020 Mon Sep 17 00:00:00 2001 From: Roy Shi Date: Thu, 8 May 2025 15:32:37 -0700 Subject: [PATCH 3/3] Add/fix test --- .../DWARF/SymbolFileDWARFDebugMap.cpp | 4 +- .../DWARF/SymbolFileDWARFDebugMapTests.cpp | 70 +++++++++++++++++-- 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp index f94c756868953..1793c23950d55 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp @@ -10,6 +10,7 @@ #include "DWARFCompileUnit.h" #include "DWARFDebugAranges.h" #include "DWARFDebugInfo.h" + #include "lldb/Core/Module.h" #include "lldb/Core/ModuleList.h" #include "lldb/Core/PluginManager.h" @@ -33,12 +34,13 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/ScopedPrinter.h" + #include "lldb/Target/StackFrame.h" #include "LogChannelDWARF.h" +#include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h" #include "SymbolFileDWARF.h" #include "lldb/lldb-private-enumerations.h" -#include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h" #include #include diff --git a/lldb/unittests/SymbolFile/DWARF/SymbolFileDWARFDebugMapTests.cpp b/lldb/unittests/SymbolFile/DWARF/SymbolFileDWARFDebugMapTests.cpp index 399510e519521..8c65fca889a40 100644 --- a/lldb/unittests/SymbolFile/DWARF/SymbolFileDWARFDebugMapTests.cpp +++ b/lldb/unittests/SymbolFile/DWARF/SymbolFileDWARFDebugMapTests.cpp @@ -6,7 +6,10 @@ // //===----------------------------------------------------------------------===// +#include "Plugins/ObjectFile/ELF/ObjectFileELF.h" +#include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h" #include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h" +#include "TestingSupport/SubsystemRAII.h" #include "TestingSupport/TestUtilities.h" #include "lldb/Core/Module.h" @@ -18,7 +21,64 @@ using namespace lldb; using namespace lldb_private; using namespace lldb_private::plugin::dwarf; -TEST(SymbolFileDWARFDebugMapTests, CreateInstanceReturnNullForNonMachOFile) { +class SymbolFileDWARFDebugMapTests : public testing::Test { + SubsystemRAII subsystems; +}; + +TEST_F(SymbolFileDWARFDebugMapTests, CreateInstanceReturnNonNullForMachOFile) { + // Make sure we don't crash parsing a null unit DIE. + const char *yamldata = R"( +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x01000007 + cpusubtype: 0x80000003 + filetype: 0x00000001 + ncmds: 1 + sizeofcmds: 152 + flags: 0x00002000 + reserved: 0x00000000 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 152 + segname: __TEXT + vmaddr: 0 + vmsize: 4 + fileoff: 184 + filesize: 4 + maxprot: 7 + initprot: 7 + nsects: 1 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x0000000000000000 + content: 'AABBCCDD' + size: 4 + offset: 184 + align: 0 + reloff: 0x00000000 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 +... +)"; + + llvm::Expected file = TestFile::fromYaml(yamldata); + EXPECT_THAT_EXPECTED(file, llvm::Succeeded()); + auto module_sp = std::make_shared(file->moduleSpec()); + ASSERT_NE(module_sp, nullptr); + auto object_file = module_sp->GetObjectFile(); + ASSERT_NE(object_file, nullptr); + auto debug_map = + SymbolFileDWARFDebugMap::CreateInstance(object_file->shared_from_this()); + ASSERT_NE(debug_map, nullptr); +} + +TEST_F(SymbolFileDWARFDebugMapTests, CreateInstanceReturnNullForNonMachOFile) { // Make sure we don't crash parsing a null unit DIE. const char *yamldata = R"( --- !ELF @@ -70,15 +130,13 @@ TEST(SymbolFileDWARFDebugMapTests, CreateInstanceReturnNullForNonMachOFile) { Length: 0 )"; - // bool waiting = true; - // while (waiting) {} - llvm::Expected file = TestFile::fromYaml(yamldata); EXPECT_THAT_EXPECTED(file, llvm::Succeeded()); auto module_sp = std::make_shared(file->moduleSpec()); + ASSERT_NE(module_sp, nullptr); auto object_file = module_sp->GetObjectFile(); ASSERT_NE(object_file, nullptr); - - auto debug_map = SymbolFileDWARFDebugMap::CreateInstance(object_file->shared_from_this()); + auto debug_map = + SymbolFileDWARFDebugMap::CreateInstance(object_file->shared_from_this()); ASSERT_EQ(debug_map, nullptr); } From lldb-commits at lists.llvm.org Thu May 8 16:00:09 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 16:00:09 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Don't create instance of `SymbolFileDWARFDebugMap` for non-Mach-O files (PR #139170) In-Reply-To: Message-ID: <681d3779.170a0220.c58c2.7e69@mx.google.com> https://github.com/royitaqi edited https://github.com/llvm/llvm-project/pull/139170 From lldb-commits at lists.llvm.org Thu May 8 16:00:15 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 16:00:15 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Don't create instance of `SymbolFileDWARFDebugMap` for non-Mach-O files (PR #139170) In-Reply-To: Message-ID: <681d377f.050a0220.2a492b.b1b2@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: None (royitaqi)
Changes # Change `SymbolFileDWARFDebugMap::CreateInstance()` will return `nullptr` if the file is not a Mach-O. # Benefit This may improve **Linux** debugger launch time by skipping the creation of `SymbolFileDWARFDebugMap` during the [`SymbolFile::FindPlugin()` call](https://fburl.com/hi1w8dil), which loops through a list of `SymbolFile` plugins and tries to find the one that provides the best abilities. If the `SymbolFileDWARFDebugMap` is created during this loop, it will load the symbol table of the file in question and loop through all the compile units in the debug map (the OSO entries) to calculate the abilities. # Tests Two tests are added to verify the creation and the absence for Mach-O and non-Mach-O files. Ran `ninja check-lldb` on both Darwin and Linux. No regression spotted. See results compared for [mac](https://pastebin.com/NdqTK4HC) and [linux](https://pastebin.com/bvjVJbi1). --- Full diff: https://github.com/llvm/llvm-project/pull/139170.diff 3 Files Affected: - (modified) lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp (+3) - (modified) lldb/unittests/SymbolFile/DWARF/CMakeLists.txt (+1) - (added) lldb/unittests/SymbolFile/DWARF/SymbolFileDWARFDebugMapTests.cpp (+142) ``````````diff diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp index 961c212e2e6dc..1793c23950d55 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp @@ -38,6 +38,7 @@ #include "lldb/Target/StackFrame.h" #include "LogChannelDWARF.h" +#include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h" #include "SymbolFileDWARF.h" #include "lldb/lldb-private-enumerations.h" @@ -246,6 +247,8 @@ llvm::StringRef SymbolFileDWARFDebugMap::GetPluginDescriptionStatic() { } SymbolFile *SymbolFileDWARFDebugMap::CreateInstance(ObjectFileSP objfile_sp) { + if (objfile_sp->GetPluginName() != ObjectFileMachO::GetPluginNameStatic()) + return nullptr; return new SymbolFileDWARFDebugMap(std::move(objfile_sp)); } diff --git a/lldb/unittests/SymbolFile/DWARF/CMakeLists.txt b/lldb/unittests/SymbolFile/DWARF/CMakeLists.txt index d5b0be7ea2a28..5aacb24fc5206 100644 --- a/lldb/unittests/SymbolFile/DWARF/CMakeLists.txt +++ b/lldb/unittests/SymbolFile/DWARF/CMakeLists.txt @@ -4,6 +4,7 @@ add_lldb_unittest(SymbolFileDWARFTests DWARFDIETest.cpp DWARFIndexCachingTest.cpp DWARFUnitTest.cpp + SymbolFileDWARFDebugMapTests.cpp SymbolFileDWARFTests.cpp XcodeSDKModuleTests.cpp diff --git a/lldb/unittests/SymbolFile/DWARF/SymbolFileDWARFDebugMapTests.cpp b/lldb/unittests/SymbolFile/DWARF/SymbolFileDWARFDebugMapTests.cpp new file mode 100644 index 0000000000000..8c65fca889a40 --- /dev/null +++ b/lldb/unittests/SymbolFile/DWARF/SymbolFileDWARFDebugMapTests.cpp @@ -0,0 +1,142 @@ +//===-- SymbolFileDWARFDebugMapTests.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 "Plugins/ObjectFile/ELF/ObjectFileELF.h" +#include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h" +#include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h" +#include "TestingSupport/SubsystemRAII.h" +#include "TestingSupport/TestUtilities.h" + +#include "lldb/Core/Module.h" +#include "llvm/Testing/Support/Error.h" + +#include "gtest/gtest.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::plugin::dwarf; + +class SymbolFileDWARFDebugMapTests : public testing::Test { + SubsystemRAII subsystems; +}; + +TEST_F(SymbolFileDWARFDebugMapTests, CreateInstanceReturnNonNullForMachOFile) { + // Make sure we don't crash parsing a null unit DIE. + const char *yamldata = R"( +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x01000007 + cpusubtype: 0x80000003 + filetype: 0x00000001 + ncmds: 1 + sizeofcmds: 152 + flags: 0x00002000 + reserved: 0x00000000 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 152 + segname: __TEXT + vmaddr: 0 + vmsize: 4 + fileoff: 184 + filesize: 4 + maxprot: 7 + initprot: 7 + nsects: 1 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x0000000000000000 + content: 'AABBCCDD' + size: 4 + offset: 184 + align: 0 + reloff: 0x00000000 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 +... +)"; + + llvm::Expected file = TestFile::fromYaml(yamldata); + EXPECT_THAT_EXPECTED(file, llvm::Succeeded()); + auto module_sp = std::make_shared(file->moduleSpec()); + ASSERT_NE(module_sp, nullptr); + auto object_file = module_sp->GetObjectFile(); + ASSERT_NE(object_file, nullptr); + auto debug_map = + SymbolFileDWARFDebugMap::CreateInstance(object_file->shared_from_this()); + ASSERT_NE(debug_map, nullptr); +} + +TEST_F(SymbolFileDWARFDebugMapTests, CreateInstanceReturnNullForNonMachOFile) { + // Make sure we don't crash parsing a null unit DIE. + const char *yamldata = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_386 +DWARF: + debug_abbrev: + - Table: + - Code: 0x00000001 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_addr_base + Form: DW_FORM_sec_offset + debug_info: + - Version: 5 + AddrSize: 4 + UnitType: DW_UT_compile + Entries: + - AbbrCode: 0x00000001 + Values: + - Value: 0x8 # Offset of the first Address past the header + - AbbrCode: 0x0 + debug_addr: + - Version: 5 + AddressSize: 4 + Entries: + - Address: 0x1234 + - Address: 0x5678 + debug_line: + - Length: 42 + Version: 2 + PrologueLength: 36 + MinInstLength: 1 + DefaultIsStmt: 1 + LineBase: 251 + LineRange: 14 + OpcodeBase: 13 + StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ] + IncludeDirs: + - '/tmp' + Files: + - Name: main.cpp + DirIdx: 1 + ModTime: 0 + Length: 0 +)"; + + llvm::Expected file = TestFile::fromYaml(yamldata); + EXPECT_THAT_EXPECTED(file, llvm::Succeeded()); + auto module_sp = std::make_shared(file->moduleSpec()); + ASSERT_NE(module_sp, nullptr); + auto object_file = module_sp->GetObjectFile(); + ASSERT_NE(object_file, nullptr); + auto debug_map = + SymbolFileDWARFDebugMap::CreateInstance(object_file->shared_from_this()); + ASSERT_EQ(debug_map, nullptr); +} ``````````
https://github.com/llvm/llvm-project/pull/139170 From lldb-commits at lists.llvm.org Thu May 8 16:01:35 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 16:01:35 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Don't create instance of `SymbolFileDWARFDebugMap` for non-Mach-O files (PR #139170) In-Reply-To: Message-ID: <681d37cf.170a0220.199d49.609a@mx.google.com> https://github.com/royitaqi edited https://github.com/llvm/llvm-project/pull/139170 From lldb-commits at lists.llvm.org Thu May 8 16:04:09 2025 From: lldb-commits at lists.llvm.org (Adrian Prantl via lldb-commits) Date: Thu, 08 May 2025 16:04:09 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Don't create instance of `SymbolFileDWARFDebugMap` for non-Mach-O files (PR #139170) In-Reply-To: Message-ID: <681d3869.170a0220.59850.8c10@mx.google.com> ================ @@ -246,6 +247,8 @@ llvm::StringRef SymbolFileDWARFDebugMap::GetPluginDescriptionStatic() { } SymbolFile *SymbolFileDWARFDebugMap::CreateInstance(ObjectFileSP objfile_sp) { + if (objfile_sp->GetPluginName() != ObjectFileMachO::GetPluginNameStatic()) + return nullptr; ---------------- adrian-prantl wrote: Who is calling this? I'm wondering if the check might make more sense at the call site? https://github.com/llvm/llvm-project/pull/139170 From lldb-commits at lists.llvm.org Thu May 8 16:06:50 2025 From: lldb-commits at lists.llvm.org (Felipe de Azevedo Piovezan via lldb-commits) Date: Thu, 08 May 2025 16:06:50 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Adding a modules explorer to lldb-dap ext. (PR #138977) In-Reply-To: Message-ID: <681d390a.170a0220.d4a0d.68bc@mx.google.com> felipepiovezan wrote: It may be a coincidence, but a DAP test failed in green dragon right after this patch was applied: https://ci.swift.org/view/all/job/llvm.org/job/as-lldb-cmake/25465/console TestDAP_repl_mode_detection.py DAP tasks have been flaky though, so maybe unrelated, I don't fully understand how this patch works. https://github.com/llvm/llvm-project/pull/138977 From lldb-commits at lists.llvm.org Thu May 8 16:07:53 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Thu, 08 May 2025 16:07:53 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Don't create instance of `SymbolFileDWARFDebugMap` for non-Mach-O files (PR #139170) In-Reply-To: Message-ID: <681d3949.630a0220.1dc5d7.4f0e@mx.google.com> https://github.com/JDevlieghere requested changes to this pull request. Plugins shouldn't depend on each other. The correct way to do this would be to have a method in ObjectFile, something like `ObjectFile::SupportsDebugMap()` which returns false for all implementations except the `ObjectFileMachO` one. https://github.com/llvm/llvm-project/pull/139170 From lldb-commits at lists.llvm.org Thu May 8 16:07:56 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Thu, 08 May 2025 16:07:56 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][lldb-dap] Migrate ScopesRequest to structured types (PR #138116) In-Reply-To: Message-ID: <681d394c.a70a0220.3a79da.b879@mx.google.com> ================ @@ -302,6 +302,74 @@ struct Source { // unsupported keys: origin, sources, adapterData, checksums }; bool fromJSON(const llvm::json::Value &, Source &, llvm::json::Path); +llvm::json::Value toJSON(const Source &); + +/// A `Scope` is a named container for variables. Optionally a scope can map to +/// a source or a range within a source. +struct Scope { + enum PresentationHint : unsigned { + ePresentationHintArguments, + ePresentationHintLocals, + ePresentationHintRegisters, + ePresentationHintReturnValue + }; + /// Name of the scope such as 'Arguments', 'Locals', or 'Registers'. This + /// string is shown in the UI as is and can be translated. + //// + std::string name; + + /// A hint for how to present this scope in the UI. If this attribute is + /// missing, the scope is shown with a generic UI. + /// Values: + /// 'arguments': Scope contains method arguments. + /// 'locals': Scope contains local variables. + /// 'registers': Scope contains registers. Only a single `registers` scope + /// should be returned from a `scopes` request. + /// 'returnValue': Scope contains one or more return values. + /// etc. + std::optional presentationHint; + + /// The variables of this scope can be retrieved by passing the value of + /// `variablesReference` to the `variables` request as long as execution + /// remains suspended. See 'Lifetime of Object References' in the Overview + /// section for details. + //// + uint64_t variablesReference; ---------------- ashgti wrote: Can we also have a default value for this one as well? Maybe: ``` #define LLDB_DAP_INVALID_VARIABLE_REF UINT64_MAX ``` https://github.com/llvm/llvm-project/pull/138116 From lldb-commits at lists.llvm.org Thu May 8 16:07:56 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Thu, 08 May 2025 16:07:56 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][lldb-dap] Migrate ScopesRequest to structured types (PR #138116) In-Reply-To: Message-ID: <681d394c.170a0220.3a1a59.7a67@mx.google.com> ================ @@ -302,6 +302,74 @@ struct Source { // unsupported keys: origin, sources, adapterData, checksums }; bool fromJSON(const llvm::json::Value &, Source &, llvm::json::Path); +llvm::json::Value toJSON(const Source &); + +/// A `Scope` is a named container for variables. Optionally a scope can map to +/// a source or a range within a source. +struct Scope { + enum PresentationHint : unsigned { + ePresentationHintArguments, + ePresentationHintLocals, + ePresentationHintRegisters, + ePresentationHintReturnValue + }; + /// Name of the scope such as 'Arguments', 'Locals', or 'Registers'. This + /// string is shown in the UI as is and can be translated. + //// + std::string name; + + /// A hint for how to present this scope in the UI. If this attribute is + /// missing, the scope is shown with a generic UI. + /// Values: + /// 'arguments': Scope contains method arguments. + /// 'locals': Scope contains local variables. + /// 'registers': Scope contains registers. Only a single `registers` scope + /// should be returned from a `scopes` request. + /// 'returnValue': Scope contains one or more return values. + /// etc. + std::optional presentationHint; + + /// The variables of this scope can be retrieved by passing the value of + /// `variablesReference` to the `variables` request as long as execution + /// remains suspended. See 'Lifetime of Object References' in the Overview + /// section for details. + //// + uint64_t variablesReference; + + /// The number of named variables in this scope. + /// The client can use this information to present the variables in a paged UI + /// and fetch them in chunks. + std::optional namedVariables; + + /// The number of indexed variables in this scope. + /// The client can use this information to present the variables in a paged UI + /// and fetch them in chunks. + std::optional indexedVariables; + + /// The source for this scope. + std::optional source; + + /// If true, the number of variables in this scope is large or expensive to + /// retrieve. + bool expensive; ---------------- ashgti wrote: Default to false? https://github.com/llvm/llvm-project/pull/138116 From lldb-commits at lists.llvm.org Thu May 8 16:07:56 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Thu, 08 May 2025 16:07:56 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][lldb-dap] Migrate ScopesRequest to structured types (PR #138116) In-Reply-To: Message-ID: <681d394c.a70a0220.d2712.be41@mx.google.com> ================ @@ -275,9 +275,7 @@ struct DAP { lldb::SBThread GetLLDBThread(lldb::tid_t id); lldb::SBThread GetLLDBThread(const llvm::json::Object &arguments); - lldb::SBFrame GetLLDBFrame(const llvm::json::Object &arguments); ---------------- ashgti wrote: We may want to leave this helper in for now until we've finished migrating to new types. https://github.com/llvm/llvm-project/pull/138116 From lldb-commits at lists.llvm.org Thu May 8 16:07:56 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Thu, 08 May 2025 16:07:56 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][lldb-dap] Migrate ScopesRequest to structured types (PR #138116) In-Reply-To: Message-ID: <681d394c.170a0220.1d536f.aa1a@mx.google.com> ================ @@ -98,9 +83,15 @@ void ScopesRequestHandler::operator()(const llvm::json::Object &request) const { /*statics=*/true, /*in_scope_only=*/true); dap.variables.registers = frame.GetRegisters(); - body.try_emplace("scopes", dap.CreateTopLevelScopes()); - response.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(response))); + + std::vector scopes = {CreateScope("Locals", VARREF_LOCALS, + dap.variables.locals.GetSize(), false), + CreateScope("Globals", VARREF_GLOBALS, + dap.variables.globals.GetSize(), false), + CreateScope("Registers", VARREF_REGS, + dap.variables.registers.GetSize(), false)}; + + return ScopesResponseBody{std::move(scopes)}; ---------------- ashgti wrote: I almost wonder if it would make more sense for some of this to move into the `Variables` helper? Maybe `llvm::Expected> Variables::SetFrame(SBFrame frame);` or a `SetFrame` and `GetScopes` helper separately. I think the intention is for the `Variables` helper to be able to handle caching and managing variable lookups. We can make it a bit more aware of its own state by moving this over there. How does that sound to you? https://github.com/llvm/llvm-project/pull/138116 From lldb-commits at lists.llvm.org Thu May 8 16:07:57 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Thu, 08 May 2025 16:07:57 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][lldb-dap] Migrate ScopesRequest to structured types (PR #138116) In-Reply-To: Message-ID: <681d394d.170a0220.a7366.65ef@mx.google.com> ================ @@ -294,6 +294,19 @@ bool fromJSON(const llvm::json::Value &, LaunchRequestArguments &, /// field is required. using LaunchResponseBody = VoidResponse; +struct ScopesArguments { + /// Retrieve the scopes for the stack frame identified by `frameId`. The + /// `frameId` must have been obtained in the current suspended state. See + /// 'Lifetime of Object References' in the Overview section for details. + uint64_t frameId; ---------------- ashgti wrote: Could we default this to a value like `UINT64_MAX` so we can use that as a hint for distinguishing an uninitialized `ScopesArguments args;` vs one we've parsed? https://github.com/llvm/llvm-project/pull/138116 From lldb-commits at lists.llvm.org Thu May 8 16:16:37 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Thu, 08 May 2025 16:16:37 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Move the event and progress event threads into DAP (NFC) (PR #139167) In-Reply-To: Message-ID: <681d3b55.630a0220.9e92c.45dc@mx.google.com> https://github.com/ashgti approved this pull request. Makes sense to me https://github.com/llvm/llvm-project/pull/139167 From lldb-commits at lists.llvm.org Thu May 8 16:22:42 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 16:22:42 -0700 (PDT) Subject: [Lldb-commits] [lldb] 6bb3019 - Branch island debug (#139166) Message-ID: <681d3cc2.630a0220.1d31d0.4f14@mx.google.com> Author: jimingham Date: 2025-05-08T16:22:39-07:00 New Revision: 6bb30196912daeaa92babc39519b2ae0bfce9771 URL: https://github.com/llvm/llvm-project/commit/6bb30196912daeaa92babc39519b2ae0bfce9771 DIFF: https://github.com/llvm/llvm-project/commit/6bb30196912daeaa92babc39519b2ae0bfce9771.diff LOG: Branch island debug (#139166) This patch allows lldb to step in across "branch islands" which is the Darwin linker's way of dealing with immediate branches to targets that are too far away for the immediate slot to make the jump. I submitted this a couple days ago and it failed on the arm64 bot. I was able to match the bot OS and Tool versions (they are a bit old at this point) and ran the test there but sadly it succeeded. The x86_64 bot also failed but that was my bad, I did @skipUnlessDarwin when I should have done @skipUnlessAppleSilicon. So this resubmission is with the proper decoration for the test, and with a bunch of debug output printed in case of failure. With any luck, if this resubmission fails again I'll be able to see what's going on. Added: lldb/test/API/macosx/branch-islands/Makefile lldb/test/API/macosx/branch-islands/TestBranchIslands.py lldb/test/API/macosx/branch-islands/foo.c lldb/test/API/macosx/branch-islands/main.c lldb/test/API/macosx/branch-islands/padding1.s lldb/test/API/macosx/branch-islands/padding2.s lldb/test/API/macosx/branch-islands/padding3.s lldb/test/API/macosx/branch-islands/padding4.s Modified: lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp Removed: ################################################################################ diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index e25c4ff55e408..578ab12268ea3 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -26,6 +26,7 @@ #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Utility/DataBuffer.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/LLDBLog.h" @@ -923,15 +924,15 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, if (current_symbol != nullptr) { std::vector
addresses; + ConstString current_name = + current_symbol->GetMangled().GetName(Mangled::ePreferMangled); if (current_symbol->IsTrampoline()) { - ConstString trampoline_name = - current_symbol->GetMangled().GetName(Mangled::ePreferMangled); - if (trampoline_name) { + if (current_name) { const ModuleList &images = target_sp->GetImages(); SymbolContextList code_symbols; - images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeCode, + images.FindSymbolsWithNameAndType(current_name, eSymbolTypeCode, code_symbols); for (const SymbolContext &context : code_symbols) { Address addr = context.GetFunctionOrSymbolAddress(); @@ -945,8 +946,8 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, } SymbolContextList reexported_symbols; - images.FindSymbolsWithNameAndType( - trampoline_name, eSymbolTypeReExported, reexported_symbols); + images.FindSymbolsWithNameAndType(current_name, eSymbolTypeReExported, + reexported_symbols); for (const SymbolContext &context : reexported_symbols) { if (context.symbol) { Symbol *actual_symbol = @@ -968,7 +969,7 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, } SymbolContextList indirect_symbols; - images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeResolver, + images.FindSymbolsWithNameAndType(current_name, eSymbolTypeResolver, indirect_symbols); for (const SymbolContext &context : indirect_symbols) { @@ -1028,6 +1029,23 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, thread_plan_sp = std::make_shared( thread, load_addrs, stop_others); } + // One more case we have to consider is "branch islands". These are regular + // TEXT symbols but their names end in .island plus maybe a .digit suffix. + // They are to allow arm64 code to branch further than the size of the + // address slot allows. We just need to single-instruction step in that + // case. + static const char *g_branch_island_pattern = "\\.island\\.?[0-9]*$"; + static RegularExpression g_branch_island_regex(g_branch_island_pattern); + + bool is_branch_island = g_branch_island_regex.Execute(current_name); + if (!thread_plan_sp && is_branch_island) { + thread_plan_sp = std::make_shared( + thread, + /* step_over= */ false, /* stop_others */ false, eVoteNoOpinion, + eVoteNoOpinion); + LLDB_LOG(log, "Stepping one instruction over branch island: '{0}'.", + current_name); + } } else { LLDB_LOGF(log, "Could not find symbol for step through."); } diff --git a/lldb/test/API/macosx/branch-islands/Makefile b/lldb/test/API/macosx/branch-islands/Makefile new file mode 100644 index 0000000000000..062e947f6d6ee --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/Makefile @@ -0,0 +1,16 @@ +C_SOURCES := main.c foo.c +CFLAGS_EXTRAS := -std=c99 + +include Makefile.rules + +a.out: main.o padding1.o padding2.o padding3.o padding4.o foo.o + ${CC} ${LDFLAGS} foo.o padding1.o padding2.o padding3.o padding4.o main.o -o a.out + +%.o: $(SRCDIR)/%.s + ${CC} -c $< + +#padding1.o: padding1.s +# ${CC} -c $(SRCDIR)/padding1.s + +#padding2.o: padding2.s +# ${CC} -c $(SRCDIR)/padding2.s diff --git a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py new file mode 100644 index 0000000000000..c79840b400432 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py @@ -0,0 +1,65 @@ +""" +Make sure that we can step in across an arm64 branch island +""" + + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * + + +class TestBranchIslandStepping(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + @skipUnlessAppleSilicon + def test_step_in_branch_island(self): + """Make sure we can step in across a branch island""" + self.build() + self.main_source_file = lldb.SBFileSpec("main.c") + self.do_test() + + def do_test(self): + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "Set a breakpoint here", self.main_source_file + ) + + # Make sure that we did manage to generate a branch island for foo: + syms = target.FindSymbols("foo.island", lldb.eSymbolTypeCode) + self.assertEqual(len(syms), 1, "We did generate an island for foo") + + # Gathering some info to dump in case of failure: + trace_before = lldbutil.print_stacktrace(thread, True) + func_before = thread.frames[0].function + + thread.StepInto() + stop_frame = thread.frames[0] + # This is failing on the bot, but I can't reproduce the failure + # locally. Let's see if we can dump some more info here to help + # figure out what went wrong... + if stop_frame.name.find("foo") == -1: + stream = lldb.SBStream() + print("Branch island symbols: ") + syms[0].GetDescription(stream) + for i in range(0, 6): + for sep in ["", "."]: + syms = target.FindSymbols( + f"foo.island{sep}{i}", lldb.eSymbolTypeCode + ) + if len(syms) > 0: + stream.Print("\n") + syms[0].GetDescription(stream) + + print(stream.GetData()) + print(f"Start backtrace:") + print(trace_before) + print(f"\n'main' disassembly:\n{lldbutil.disassemble(target, func_before)}") + print("\nEnd backtrace:\n") + lldbutil.print_stacktrace(thread) + print( + f"\nStop disassembly:\n {lldbutil.disassemble(target, stop_frame.function)}" + ) + + self.assertIn("foo", stop_frame.name, "Stepped into foo") + var = stop_frame.FindVariable("a_variable_in_foo") + self.assertTrue(var.IsValid(), "Found the variable in foo") diff --git a/lldb/test/API/macosx/branch-islands/foo.c b/lldb/test/API/macosx/branch-islands/foo.c new file mode 100644 index 0000000000000..a5dd2e59e1d82 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/foo.c @@ -0,0 +1,6 @@ +#include + +void foo() { + int a_variable_in_foo = 10; + printf("I am foo: %d.\n", a_variable_in_foo); +} diff --git a/lldb/test/API/macosx/branch-islands/main.c b/lldb/test/API/macosx/branch-islands/main.c new file mode 100644 index 0000000000000..b5578bdd715df --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/main.c @@ -0,0 +1,6 @@ +extern void foo(); + +int main() { + foo(); // Set a breakpoint here + return 0; +} diff --git a/lldb/test/API/macosx/branch-islands/padding1.s b/lldb/test/API/macosx/branch-islands/padding1.s new file mode 100644 index 0000000000000..4911e53b0240d --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding1.s @@ -0,0 +1,3 @@ +.text +_padding1: +.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding2.s b/lldb/test/API/macosx/branch-islands/padding2.s new file mode 100644 index 0000000000000..5ad1bad11263b --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding2.s @@ -0,0 +1,3 @@ +.text +_padding2: +.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding3.s b/lldb/test/API/macosx/branch-islands/padding3.s new file mode 100644 index 0000000000000..9f614eecf56d9 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding3.s @@ -0,0 +1,3 @@ +.text +_padding3: +.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding4.s b/lldb/test/API/macosx/branch-islands/padding4.s new file mode 100644 index 0000000000000..12896cf5e5b8e --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding4.s @@ -0,0 +1,3 @@ +.text +_padding4: +.space 120*1024*1024 From lldb-commits at lists.llvm.org Thu May 8 16:22:46 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 16:22:46 -0700 (PDT) Subject: [Lldb-commits] [lldb] Branch island debug (PR #139166) In-Reply-To: Message-ID: <681d3cc6.050a0220.8b715.b788@mx.google.com> https://github.com/jimingham closed https://github.com/llvm/llvm-project/pull/139166 From lldb-commits at lists.llvm.org Thu May 8 16:24:05 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Thu, 08 May 2025 16:24:05 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Adding a modules explorer to lldb-dap ext. (PR #138977) In-Reply-To: Message-ID: <681d3d15.630a0220.9bdbe.34ec@mx.google.com> ashgti wrote: The failure seems to be that `stackTrace` returned an empty list: ``` 15:36:48 1746743807.491051912 --> (stdin/stdout) {"command":"stackTrace","type":"request","arguments":{"threadId":1744343,"startFrame":0,"levels":1},"seq":13} 15:36:48 1746743807.491221905 <-- (stdin/stdout) {"body":{"stackFrames":[]},"command":"stackTrace","request_seq":13,"seq":0,"success":true,"type":"response"} ``` This should just be code in the lldb-dap VSCode extension, not in the lldb-dap executable, so this is likely a flaky test. It looks like that was running on macOS and I've been running those tests locally. I'll see if I can reproduce the flake by running it a few times. https://github.com/llvm/llvm-project/pull/138977 From lldb-commits at lists.llvm.org Thu May 8 16:48:03 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 16:48:03 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fix FindProcessImpl() for iOS simulators (PR #139174) Message-ID: https://github.com/royitaqi created https://github.com/llvm/llvm-project/pull/139174 # Benefit This patch fixes: 1. After `platform select ios-simulator`, `platform process list` will now print processes which are running in the iOS simulator. Previously, no process will be listed. 2. After `platform select ios-simulator`, `platform attach --name ` will succeed. Previously, it will error out saying no process is found. # Changes 1. During the process listing, add `aarch64` to the list of CPU types for which iOS simulators are checked for. 2. Fix a bug in the code which checks for simulators, where for a process running on simulator, the original code will find the desired environment variable and set the OS to iOS, but then the immediate next environment variable will set it back to macOS. 3. For processes running on simulator, set the triple's `Environment` to `Simulator`, so that such processes can pass the filtering [in this line](https://fburl.com/8nivnrjx). The original code leave it as the default `UnknownEnvironment`. # Manual test **With this patch:** ``` royshi-mac-home ~/public_llvm/build % bin/lldb (lldb) platform select ios-simulator (lldb) platform process list 240 matching processes were found on "ios-simulator" PID PARENT USER TRIPLE NAME ====== ====== ========== ============================== ============================ 40511 28844 royshi arm64-apple-ios-simulator FocusPlayground // my toy iOS app running on simulator ... // omit 28844 1 royshi arm64-apple-ios-simulator launchd_sim (lldb) process attach --name FocusPlayground Process 40511 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP frame #0: 0x0000000104e3cb70 libsystem_kernel.dylib`mach_msg2_trap + 8 libsystem_kernel.dylib`mach_msg2_trap: -> 0x104e3cb70 <+8>: ret ... // omit ``` **Without this patch:** ``` royshi-mac-home ~/public_llvm/build % bin/lldb (lldb) platform select ios-simulator (lldb) platform process list error: no processes were found on the "ios-simulator" platform (lldb) process attach --name FocusPlayground error: attach failed: could not find a process named FocusPlayground ``` # Unittest I did a couple of code searches ([1](https://github.com/search?q=repo%3Allvm%2Fllvm-project+FindProcessesImpl+test&type=code), [2](https://github.com/search?q=repo%3Allvm%2Fllvm-project+GetMacOSXProcessArgs+test&type=code)) and didn't seem to find any existing tests. Any suggestion about how to test this? >From d98210b81f7b49f5384e968b1a18e00e432aaf2c Mon Sep 17 00:00:00 2001 From: Roy Shi Date: Thu, 8 May 2025 16:21:53 -0700 Subject: [PATCH] Fix macOS FindProcessImpl() --- lldb/source/Host/macosx/objcxx/Host.mm | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/lldb/source/Host/macosx/objcxx/Host.mm b/lldb/source/Host/macosx/objcxx/Host.mm index e187bf98188ae..e8a1c597eea53 100644 --- a/lldb/source/Host/macosx/objcxx/Host.mm +++ b/lldb/source/Host/macosx/objcxx/Host.mm @@ -595,7 +595,9 @@ DataExtractor data(arg_data.GetBytes(), arg_data_size, const llvm::Triple::ArchType triple_arch = triple.getArch(); const bool check_for_ios_simulator = (triple_arch == llvm::Triple::x86 || - triple_arch == llvm::Triple::x86_64); + triple_arch == llvm::Triple::x86_64 || + triple_arch == llvm::Triple::aarch64); + const char *cstr = data.GetCStr(&offset); if (cstr) { process_info.GetExecutableFile().SetFile(cstr, FileSpec::Style::native); @@ -621,22 +623,25 @@ DataExtractor data(arg_data.GetBytes(), arg_data_size, } Environment &proc_env = process_info.GetEnvironment(); + bool is_simulator = false; while ((cstr = data.GetCStr(&offset))) { if (cstr[0] == '\0') break; - if (check_for_ios_simulator) { - if (strncmp(cstr, "SIMULATOR_UDID=", strlen("SIMULATOR_UDID=")) == - 0) - process_info.GetArchitecture().GetTriple().setOS( - llvm::Triple::IOS); - else - process_info.GetArchitecture().GetTriple().setOS( - llvm::Triple::MacOSX); - } + if (check_for_ios_simulator && + strncmp(cstr, "SIMULATOR_UDID=", strlen("SIMULATOR_UDID=")) == + 0) + is_simulator = true; proc_env.insert(cstr); } + llvm::Triple &triple = process_info.GetArchitecture().GetTriple(); + if (is_simulator) { + triple.setOS(llvm::Triple::IOS); + triple.setEnvironment(llvm::Triple::Simulator); + } else { + triple.setOS(llvm::Triple::MacOSX); + } return true; } } From lldb-commits at lists.llvm.org Thu May 8 16:48:39 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 16:48:39 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fix FindProcessImpl() for iOS simulators (PR #139174) In-Reply-To: Message-ID: <681d42d7.050a0220.bfee9.36a9@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: None (royitaqi)
Changes # Benefit This patch fixes: 1. After `platform select ios-simulator`, `platform process list` will now print processes which are running in the iOS simulator. Previously, no process will be listed. 2. After `platform select ios-simulator`, `platform attach --name <name>` will succeed. Previously, it will error out saying no process is found. # Changes 1. During the process listing, add `aarch64` to the list of CPU types for which iOS simulators are checked for. 2. Fix a bug in the code which checks for simulators, where for a process running on simulator, the original code will find the desired environment variable and set the OS to iOS, but then the immediate next environment variable will set it back to macOS. 3. For processes running on simulator, set the triple's `Environment` to `Simulator`, so that such processes can pass the filtering [in this line](https://fburl.com/8nivnrjx). The original code leave it as the default `UnknownEnvironment`. # Manual test **With this patch:** ``` royshi-mac-home ~/public_llvm/build % bin/lldb (lldb) platform select ios-simulator (lldb) platform process list 240 matching processes were found on "ios-simulator" PID PARENT USER TRIPLE NAME ====== ====== ========== ============================== ============================ 40511 28844 royshi arm64-apple-ios-simulator FocusPlayground // my toy iOS app running on simulator ... // omit 28844 1 royshi arm64-apple-ios-simulator launchd_sim (lldb) process attach --name FocusPlayground Process 40511 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP frame #0: 0x0000000104e3cb70 libsystem_kernel.dylib`mach_msg2_trap + 8 libsystem_kernel.dylib`mach_msg2_trap: -> 0x104e3cb70 <+8>: ret ... // omit ``` **Without this patch:** ``` royshi-mac-home ~/public_llvm/build % bin/lldb (lldb) platform select ios-simulator (lldb) platform process list error: no processes were found on the "ios-simulator" platform (lldb) process attach --name FocusPlayground error: attach failed: could not find a process named FocusPlayground ``` # Unittest I did a couple of code searches ([1](https://github.com/search?q=repo%3Allvm%2Fllvm-project+FindProcessesImpl+test&type=code), [2](https://github.com/search?q=repo%3Allvm%2Fllvm-project+GetMacOSXProcessArgs+test&type=code)) and didn't seem to find any existing tests. Any suggestion about how to test this? --- Full diff: https://github.com/llvm/llvm-project/pull/139174.diff 1 Files Affected: - (modified) lldb/source/Host/macosx/objcxx/Host.mm (+15-10) ``````````diff diff --git a/lldb/source/Host/macosx/objcxx/Host.mm b/lldb/source/Host/macosx/objcxx/Host.mm index e187bf98188ae..e8a1c597eea53 100644 --- a/lldb/source/Host/macosx/objcxx/Host.mm +++ b/lldb/source/Host/macosx/objcxx/Host.mm @@ -595,7 +595,9 @@ DataExtractor data(arg_data.GetBytes(), arg_data_size, const llvm::Triple::ArchType triple_arch = triple.getArch(); const bool check_for_ios_simulator = (triple_arch == llvm::Triple::x86 || - triple_arch == llvm::Triple::x86_64); + triple_arch == llvm::Triple::x86_64 || + triple_arch == llvm::Triple::aarch64); + const char *cstr = data.GetCStr(&offset); if (cstr) { process_info.GetExecutableFile().SetFile(cstr, FileSpec::Style::native); @@ -621,22 +623,25 @@ DataExtractor data(arg_data.GetBytes(), arg_data_size, } Environment &proc_env = process_info.GetEnvironment(); + bool is_simulator = false; while ((cstr = data.GetCStr(&offset))) { if (cstr[0] == '\0') break; - if (check_for_ios_simulator) { - if (strncmp(cstr, "SIMULATOR_UDID=", strlen("SIMULATOR_UDID=")) == - 0) - process_info.GetArchitecture().GetTriple().setOS( - llvm::Triple::IOS); - else - process_info.GetArchitecture().GetTriple().setOS( - llvm::Triple::MacOSX); - } + if (check_for_ios_simulator && + strncmp(cstr, "SIMULATOR_UDID=", strlen("SIMULATOR_UDID=")) == + 0) + is_simulator = true; proc_env.insert(cstr); } + llvm::Triple &triple = process_info.GetArchitecture().GetTriple(); + if (is_simulator) { + triple.setOS(llvm::Triple::IOS); + triple.setEnvironment(llvm::Triple::Simulator); + } else { + triple.setOS(llvm::Triple::MacOSX); + } return true; } } ``````````
https://github.com/llvm/llvm-project/pull/139174 From lldb-commits at lists.llvm.org Thu May 8 16:54:04 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Thu, 08 May 2025 16:54:04 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Don't create instance of `SymbolFileDWARFDebugMap` for non-Mach-O files (PR #139170) In-Reply-To: Message-ID: <681d441c.170a0220.247d49.6909@mx.google.com> bulbazord wrote: +1 to Jonas's comment. Plugins ideally don't depend on each other and they should strive to fail gracefully if they depend on a specific plugin instance that's not available. I recognize that many plugins depend on each other today, but I don't think we should be adding more direct dependencies if we can help it. https://github.com/llvm/llvm-project/pull/139170 From lldb-commits at lists.llvm.org Thu May 8 17:00:56 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 17:00:56 -0700 (PDT) Subject: [Lldb-commits] [lldb] Add more logging so I can figure out why TestBranchIslands.py is (PR #139178) Message-ID: https://github.com/jimingham created https://github.com/llvm/llvm-project/pull/139178 failing but only on the bot. >From 3198c53fbf642694fde0a99a29ad779208ea8b08 Mon Sep 17 00:00:00 2001 From: Jim Ingham Date: Thu, 8 May 2025 16:59:10 -0700 Subject: [PATCH] Add more logging so I can figure out why TestBranchIslands.py is failing but only on the bot. --- .../DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp | 9 +++++++++ lldb/test/API/macosx/branch-islands/TestBranchIslands.py | 9 ++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index 578ab12268ea3..6c3040ef1a1da 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -1038,6 +1038,15 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, static RegularExpression g_branch_island_regex(g_branch_island_pattern); bool is_branch_island = g_branch_island_regex.Execute(current_name); + // FIXME: this is extra logging so I can figure out why this test is failing + // on the bot but not locally with all the same tools, etc... + if (thread_plan_sp && is_branch_island) { + if (log) { + StreamString s; + thread_plan_sp->GetDescription(&s, eDescriptionLevelVerbose); + LLDB_LOGF(log, "Am at a branch island, but already had plan: \n\t%s", s.GetData()); + } + } if (!thread_plan_sp && is_branch_island) { thread_plan_sp = std::make_shared( thread, diff --git a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py index c79840b400432..a8dd1886d5568 100644 --- a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py +++ b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py @@ -2,7 +2,7 @@ Make sure that we can step in across an arm64 branch island """ - +import os import lldb import lldbsuite.test.lldbutil as lldbutil from lldbsuite.test.lldbtest import * @@ -32,6 +32,9 @@ def do_test(self): trace_before = lldbutil.print_stacktrace(thread, True) func_before = thread.frames[0].function + log_file_path = os.path.join(self.getBuildDir(), "step-log.txt") + self.runCmd(f"log enable -f {log_file_path} lldb step") + thread.StepInto() stop_frame = thread.frames[0] # This is failing on the bot, but I can't reproduce the failure @@ -59,6 +62,10 @@ def do_test(self): print( f"\nStop disassembly:\n {lldbutil.disassemble(target, stop_frame.function)}" ) + with open(log_file_path, "r") as f: + data = f.read() + print("Step Log:") + print(data) self.assertIn("foo", stop_frame.name, "Stepped into foo") var = stop_frame.FindVariable("a_variable_in_foo") From lldb-commits at lists.llvm.org Thu May 8 17:01:26 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 17:01:26 -0700 (PDT) Subject: [Lldb-commits] [lldb] Add more logging so I can figure out why TestBranchIslands.py is (PR #139178) In-Reply-To: Message-ID: <681d45d6.170a0220.4ef56.97c9@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: None (jimingham)
Changes failing but only on the bot. --- Full diff: https://github.com/llvm/llvm-project/pull/139178.diff 2 Files Affected: - (modified) lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp (+9) - (modified) lldb/test/API/macosx/branch-islands/TestBranchIslands.py (+8-1) ``````````diff diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index 578ab12268ea3..6c3040ef1a1da 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -1038,6 +1038,15 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, static RegularExpression g_branch_island_regex(g_branch_island_pattern); bool is_branch_island = g_branch_island_regex.Execute(current_name); + // FIXME: this is extra logging so I can figure out why this test is failing + // on the bot but not locally with all the same tools, etc... + if (thread_plan_sp && is_branch_island) { + if (log) { + StreamString s; + thread_plan_sp->GetDescription(&s, eDescriptionLevelVerbose); + LLDB_LOGF(log, "Am at a branch island, but already had plan: \n\t%s", s.GetData()); + } + } if (!thread_plan_sp && is_branch_island) { thread_plan_sp = std::make_shared( thread, diff --git a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py index c79840b400432..a8dd1886d5568 100644 --- a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py +++ b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py @@ -2,7 +2,7 @@ Make sure that we can step in across an arm64 branch island """ - +import os import lldb import lldbsuite.test.lldbutil as lldbutil from lldbsuite.test.lldbtest import * @@ -32,6 +32,9 @@ def do_test(self): trace_before = lldbutil.print_stacktrace(thread, True) func_before = thread.frames[0].function + log_file_path = os.path.join(self.getBuildDir(), "step-log.txt") + self.runCmd(f"log enable -f {log_file_path} lldb step") + thread.StepInto() stop_frame = thread.frames[0] # This is failing on the bot, but I can't reproduce the failure @@ -59,6 +62,10 @@ def do_test(self): print( f"\nStop disassembly:\n {lldbutil.disassemble(target, stop_frame.function)}" ) + with open(log_file_path, "r") as f: + data = f.read() + print("Step Log:") + print(data) self.assertIn("foo", stop_frame.name, "Stepped into foo") var = stop_frame.FindVariable("a_variable_in_foo") ``````````
https://github.com/llvm/llvm-project/pull/139178 From lldb-commits at lists.llvm.org Thu May 8 17:03:09 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 17:03:09 -0700 (PDT) Subject: [Lldb-commits] [lldb] Add more logging so I can figure out why TestBranchIslands.py is (PR #139178) In-Reply-To: Message-ID: <681d463d.170a0220.274bf7.ab0a@mx.google.com> github-actions[bot] wrote: :warning: Python code formatter, darker found issues in your code. :warning:
You can test this locally with the following command: ``````````bash darker --check --diff -r HEAD~1...HEAD lldb/test/API/macosx/branch-islands/TestBranchIslands.py ``````````
View the diff from darker here. ``````````diff --- TestBranchIslands.py 2025-05-08 23:59:10.000000 +0000 +++ TestBranchIslands.py 2025-05-09 00:02:44.868955 +0000 @@ -32,11 +32,11 @@ trace_before = lldbutil.print_stacktrace(thread, True) func_before = thread.frames[0].function log_file_path = os.path.join(self.getBuildDir(), "step-log.txt") self.runCmd(f"log enable -f {log_file_path} lldb step") - + thread.StepInto() stop_frame = thread.frames[0] # This is failing on the bot, but I can't reproduce the failure # locally. Let's see if we can dump some more info here to help # figure out what went wrong... ``````````
https://github.com/llvm/llvm-project/pull/139178 From lldb-commits at lists.llvm.org Thu May 8 17:03:10 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 17:03:10 -0700 (PDT) Subject: [Lldb-commits] [lldb] Add more logging so I can figure out why TestBranchIslands.py is (PR #139178) In-Reply-To: Message-ID: <681d463e.050a0220.1c974c.b400@mx.google.com> github-actions[bot] wrote: :warning: C/C++ code formatter, clang-format found issues in your code. :warning:
You can test this locally with the following command: ``````````bash git-clang-format --diff HEAD~1 HEAD --extensions cpp -- lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp ``````````
View the diff from clang-format here. ``````````diff diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index 6c3040ef1..def40205f 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -1044,7 +1044,8 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, if (log) { StreamString s; thread_plan_sp->GetDescription(&s, eDescriptionLevelVerbose); - LLDB_LOGF(log, "Am at a branch island, but already had plan: \n\t%s", s.GetData()); + LLDB_LOGF(log, "Am at a branch island, but already had plan: \n\t%s", + s.GetData()); } } if (!thread_plan_sp && is_branch_island) { ``````````
https://github.com/llvm/llvm-project/pull/139178 From lldb-commits at lists.llvm.org Thu May 8 17:03:25 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 17:03:25 -0700 (PDT) Subject: [Lldb-commits] [lldb] b6922b7 - Add more logging so I can figure out why TestBranchIslands.py is (#139178) Message-ID: <681d464d.170a0220.1454c2.806e@mx.google.com> Author: jimingham Date: 2025-05-08T17:03:21-07:00 New Revision: b6922b717045d3d1bd136b96f672533a498fd5aa URL: https://github.com/llvm/llvm-project/commit/b6922b717045d3d1bd136b96f672533a498fd5aa DIFF: https://github.com/llvm/llvm-project/commit/b6922b717045d3d1bd136b96f672533a498fd5aa.diff LOG: Add more logging so I can figure out why TestBranchIslands.py is (#139178) failing but only on the bot. Added: Modified: lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp lldb/test/API/macosx/branch-islands/TestBranchIslands.py Removed: ################################################################################ diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index 578ab12268ea3..6c3040ef1a1da 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -1038,6 +1038,15 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, static RegularExpression g_branch_island_regex(g_branch_island_pattern); bool is_branch_island = g_branch_island_regex.Execute(current_name); + // FIXME: this is extra logging so I can figure out why this test is failing + // on the bot but not locally with all the same tools, etc... + if (thread_plan_sp && is_branch_island) { + if (log) { + StreamString s; + thread_plan_sp->GetDescription(&s, eDescriptionLevelVerbose); + LLDB_LOGF(log, "Am at a branch island, but already had plan: \n\t%s", s.GetData()); + } + } if (!thread_plan_sp && is_branch_island) { thread_plan_sp = std::make_shared( thread, diff --git a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py index c79840b400432..a8dd1886d5568 100644 --- a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py +++ b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py @@ -2,7 +2,7 @@ Make sure that we can step in across an arm64 branch island """ - +import os import lldb import lldbsuite.test.lldbutil as lldbutil from lldbsuite.test.lldbtest import * @@ -32,6 +32,9 @@ def do_test(self): trace_before = lldbutil.print_stacktrace(thread, True) func_before = thread.frames[0].function + log_file_path = os.path.join(self.getBuildDir(), "step-log.txt") + self.runCmd(f"log enable -f {log_file_path} lldb step") + thread.StepInto() stop_frame = thread.frames[0] # This is failing on the bot, but I can't reproduce the failure @@ -59,6 +62,10 @@ def do_test(self): print( f"\nStop disassembly:\n {lldbutil.disassemble(target, stop_frame.function)}" ) + with open(log_file_path, "r") as f: + data = f.read() + print("Step Log:") + print(data) self.assertIn("foo", stop_frame.name, "Stepped into foo") var = stop_frame.FindVariable("a_variable_in_foo") From lldb-commits at lists.llvm.org Thu May 8 17:03:27 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 17:03:27 -0700 (PDT) Subject: [Lldb-commits] [lldb] Add more logging so I can figure out why TestBranchIslands.py is (PR #139178) In-Reply-To: Message-ID: <681d464f.050a0220.1cee34.bcbf@mx.google.com> https://github.com/jimingham closed https://github.com/llvm/llvm-project/pull/139178 From lldb-commits at lists.llvm.org Thu May 8 18:00:49 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 18:00:49 -0700 (PDT) Subject: [Lldb-commits] [lldb] Branch no lld (PR #139187) Message-ID: https://github.com/jimingham created https://github.com/llvm/llvm-project/pull/139187 I suspect the test may be failing because lld doesn't behave the same way the native Darwin linker does. Trying that theory here... >From 3198c53fbf642694fde0a99a29ad779208ea8b08 Mon Sep 17 00:00:00 2001 From: Jim Ingham Date: Thu, 8 May 2025 16:59:10 -0700 Subject: [PATCH 1/2] Add more logging so I can figure out why TestBranchIslands.py is failing but only on the bot. --- .../DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp | 9 +++++++++ lldb/test/API/macosx/branch-islands/TestBranchIslands.py | 9 ++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index 578ab12268ea3..6c3040ef1a1da 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -1038,6 +1038,15 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, static RegularExpression g_branch_island_regex(g_branch_island_pattern); bool is_branch_island = g_branch_island_regex.Execute(current_name); + // FIXME: this is extra logging so I can figure out why this test is failing + // on the bot but not locally with all the same tools, etc... + if (thread_plan_sp && is_branch_island) { + if (log) { + StreamString s; + thread_plan_sp->GetDescription(&s, eDescriptionLevelVerbose); + LLDB_LOGF(log, "Am at a branch island, but already had plan: \n\t%s", s.GetData()); + } + } if (!thread_plan_sp && is_branch_island) { thread_plan_sp = std::make_shared( thread, diff --git a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py index c79840b400432..a8dd1886d5568 100644 --- a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py +++ b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py @@ -2,7 +2,7 @@ Make sure that we can step in across an arm64 branch island """ - +import os import lldb import lldbsuite.test.lldbutil as lldbutil from lldbsuite.test.lldbtest import * @@ -32,6 +32,9 @@ def do_test(self): trace_before = lldbutil.print_stacktrace(thread, True) func_before = thread.frames[0].function + log_file_path = os.path.join(self.getBuildDir(), "step-log.txt") + self.runCmd(f"log enable -f {log_file_path} lldb step") + thread.StepInto() stop_frame = thread.frames[0] # This is failing on the bot, but I can't reproduce the failure @@ -59,6 +62,10 @@ def do_test(self): print( f"\nStop disassembly:\n {lldbutil.disassemble(target, stop_frame.function)}" ) + with open(log_file_path, "r") as f: + data = f.read() + print("Step Log:") + print(data) self.assertIn("foo", stop_frame.name, "Stepped into foo") var = stop_frame.FindVariable("a_variable_in_foo") >From 97524326b677db911969170fbeb74a40665fb3eb Mon Sep 17 00:00:00 2001 From: Jim Ingham Date: Thu, 8 May 2025 17:58:18 -0700 Subject: [PATCH 2/2] Use the system linker for this test, I'm guessing lld isn't handling this correctly. --- lldb/test/API/macosx/branch-islands/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/test/API/macosx/branch-islands/Makefile b/lldb/test/API/macosx/branch-islands/Makefile index 062e947f6d6ee..ff341522e15de 100644 --- a/lldb/test/API/macosx/branch-islands/Makefile +++ b/lldb/test/API/macosx/branch-islands/Makefile @@ -4,7 +4,7 @@ CFLAGS_EXTRAS := -std=c99 include Makefile.rules a.out: main.o padding1.o padding2.o padding3.o padding4.o foo.o - ${CC} ${LDFLAGS} foo.o padding1.o padding2.o padding3.o padding4.o main.o -o a.out + ${CC} ${LDFLAGS} -fuse-ld=/usr/bin/ld foo.o padding1.o padding2.o padding3.o padding4.o main.o -o a.out %.o: $(SRCDIR)/%.s ${CC} -c $< From lldb-commits at lists.llvm.org Thu May 8 18:00:55 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 18:00:55 -0700 (PDT) Subject: [Lldb-commits] [lldb] b80c3c5 - Branch no lld (#139187) Message-ID: <681d53c7.170a0220.5727b.82ca@mx.google.com> Author: jimingham Date: 2025-05-08T18:00:52-07:00 New Revision: b80c3c576f169326f55956985706816cf7b170eb URL: https://github.com/llvm/llvm-project/commit/b80c3c576f169326f55956985706816cf7b170eb DIFF: https://github.com/llvm/llvm-project/commit/b80c3c576f169326f55956985706816cf7b170eb.diff LOG: Branch no lld (#139187) I suspect the test may be failing because lld doesn't behave the same way the native Darwin linker does. Trying that theory here... Added: Modified: lldb/test/API/macosx/branch-islands/Makefile Removed: ################################################################################ diff --git a/lldb/test/API/macosx/branch-islands/Makefile b/lldb/test/API/macosx/branch-islands/Makefile index 062e947f6d6ee..ff341522e15de 100644 --- a/lldb/test/API/macosx/branch-islands/Makefile +++ b/lldb/test/API/macosx/branch-islands/Makefile @@ -4,7 +4,7 @@ CFLAGS_EXTRAS := -std=c99 include Makefile.rules a.out: main.o padding1.o padding2.o padding3.o padding4.o foo.o - ${CC} ${LDFLAGS} foo.o padding1.o padding2.o padding3.o padding4.o main.o -o a.out + ${CC} ${LDFLAGS} -fuse-ld=/usr/bin/ld foo.o padding1.o padding2.o padding3.o padding4.o main.o -o a.out %.o: $(SRCDIR)/%.s ${CC} -c $< From lldb-commits at lists.llvm.org Thu May 8 18:00:58 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 18:00:58 -0700 (PDT) Subject: [Lldb-commits] [lldb] Branch no lld (PR #139187) In-Reply-To: Message-ID: <681d53ca.630a0220.8fc15.53ce@mx.google.com> https://github.com/jimingham closed https://github.com/llvm/llvm-project/pull/139187 From lldb-commits at lists.llvm.org Thu May 8 18:01:20 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 18:01:20 -0700 (PDT) Subject: [Lldb-commits] [lldb] Branch no lld (PR #139187) In-Reply-To: Message-ID: <681d53e0.170a0220.57336.84f6@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: None (jimingham)
Changes I suspect the test may be failing because lld doesn't behave the same way the native Darwin linker does. Trying that theory here... --- Full diff: https://github.com/llvm/llvm-project/pull/139187.diff 3 Files Affected: - (modified) lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp (+9) - (modified) lldb/test/API/macosx/branch-islands/Makefile (+1-1) - (modified) lldb/test/API/macosx/branch-islands/TestBranchIslands.py (+8-1) ``````````diff diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index 578ab12268ea3..6c3040ef1a1da 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -1038,6 +1038,15 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, static RegularExpression g_branch_island_regex(g_branch_island_pattern); bool is_branch_island = g_branch_island_regex.Execute(current_name); + // FIXME: this is extra logging so I can figure out why this test is failing + // on the bot but not locally with all the same tools, etc... + if (thread_plan_sp && is_branch_island) { + if (log) { + StreamString s; + thread_plan_sp->GetDescription(&s, eDescriptionLevelVerbose); + LLDB_LOGF(log, "Am at a branch island, but already had plan: \n\t%s", s.GetData()); + } + } if (!thread_plan_sp && is_branch_island) { thread_plan_sp = std::make_shared( thread, diff --git a/lldb/test/API/macosx/branch-islands/Makefile b/lldb/test/API/macosx/branch-islands/Makefile index 062e947f6d6ee..ff341522e15de 100644 --- a/lldb/test/API/macosx/branch-islands/Makefile +++ b/lldb/test/API/macosx/branch-islands/Makefile @@ -4,7 +4,7 @@ CFLAGS_EXTRAS := -std=c99 include Makefile.rules a.out: main.o padding1.o padding2.o padding3.o padding4.o foo.o - ${CC} ${LDFLAGS} foo.o padding1.o padding2.o padding3.o padding4.o main.o -o a.out + ${CC} ${LDFLAGS} -fuse-ld=/usr/bin/ld foo.o padding1.o padding2.o padding3.o padding4.o main.o -o a.out %.o: $(SRCDIR)/%.s ${CC} -c $< diff --git a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py index c79840b400432..a8dd1886d5568 100644 --- a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py +++ b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py @@ -2,7 +2,7 @@ Make sure that we can step in across an arm64 branch island """ - +import os import lldb import lldbsuite.test.lldbutil as lldbutil from lldbsuite.test.lldbtest import * @@ -32,6 +32,9 @@ def do_test(self): trace_before = lldbutil.print_stacktrace(thread, True) func_before = thread.frames[0].function + log_file_path = os.path.join(self.getBuildDir(), "step-log.txt") + self.runCmd(f"log enable -f {log_file_path} lldb step") + thread.StepInto() stop_frame = thread.frames[0] # This is failing on the bot, but I can't reproduce the failure @@ -59,6 +62,10 @@ def do_test(self): print( f"\nStop disassembly:\n {lldbutil.disassemble(target, stop_frame.function)}" ) + with open(log_file_path, "r") as f: + data = f.read() + print("Step Log:") + print(data) self.assertIn("foo", stop_frame.name, "Stepped into foo") var = stop_frame.FindVariable("a_variable_in_foo") ``````````
https://github.com/llvm/llvm-project/pull/139187 From lldb-commits at lists.llvm.org Thu May 8 18:21:42 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Thu, 08 May 2025 18:21:42 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681d58a6.170a0220.d7156.8283@mx.google.com> https://github.com/jasonmolenda updated https://github.com/llvm/llvm-project/pull/138805 >From 4fc9acd03a58a3617b113352c48e5dd2fdb58eda Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Tue, 6 May 2025 22:37:17 -0700 Subject: [PATCH 1/5] [lldb] Provide lr value in faulting frame on arm64 When a frameless function faults or is interrupted asynchronously, the UnwindPlan MAY have no register location rule for the return address register (lr on arm64); the value is simply live in the lr register when it was interrupted, and the frame below this on the stack -- e.g. sigtramp on a Unix system -- has the full register context, including that register. RegisterContextUnwind::SavedLocationForRegister, when asked to find the caller's pc value, will first see if there is a pc register location. If there isn't, on a Return Address Register architecture like arm/mips/riscv, we rewrite the register request from "pc" to "RA register", and search for a location. On frame 0 (the live frame) and an interrupted frame, the UnwindPlan may have no register location rule for the RA Reg, that is valid. A frameless function that never calls another may simply keep the return address in the live register the whole way. Our instruction emulation unwind plans explicitly add a rule (see Pavel's May 2024 change https://github.com/llvm/llvm-project/pull/91321 ), but an UnwindPlan sourced from debug_frame may not. I've got a case where this exactly happens - clang debug_frame for arm64 where there is no register location for the lr in a frameless function. There is a fault in the middle of this frameless function and we only get the lr value from the fault handler below this frame if lr has a register location of `IsSame`, in line with Pavel's 2024 change. Similar to how we see a request of the RA Reg from frame 0 after failing to find an unwind location for the pc register, the same style of special casing is needed when this is a function that was interrupted. Without this change, we can find the pc of the frame that was executing when it was interrupted, but we need $lr to find its caller, and we don't descend down to the trap handler to get that value, truncating the stack. rdar://145614545 --- lldb/source/Target/RegisterContextUnwind.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 3ed49e12476dd..23a86bee2518b 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -1377,6 +1377,7 @@ RegisterContextUnwind::SavedLocationForRegister( } } + // Check if the active_row has a register location listed. if (regnum.IsValid() && active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind), unwindplan_regloc)) { @@ -1390,11 +1391,10 @@ RegisterContextUnwind::SavedLocationForRegister( // This is frame 0 and we're retrieving the PC and it's saved in a Return // Address register and it hasn't been saved anywhere yet -- that is, // it's still live in the actual register. Handle this specially. - if (!have_unwindplan_regloc && return_address_reg.IsValid() && - IsFrameZero()) { - if (return_address_reg.GetAsKind(eRegisterKindLLDB) != - LLDB_INVALID_REGNUM) { + return_address_reg.GetAsKind(eRegisterKindLLDB) != + LLDB_INVALID_REGNUM) { + if (IsFrameZero()) { lldb_private::UnwindLLDB::ConcreteRegisterLocation new_regloc; new_regloc.type = UnwindLLDB::ConcreteRegisterLocation:: eRegisterInLiveRegisterContext; @@ -1408,6 +1408,17 @@ RegisterContextUnwind::SavedLocationForRegister( return_address_reg.GetAsKind(eRegisterKindLLDB), return_address_reg.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } else if (BehavesLikeZerothFrame()) { + // This function was interrupted asynchronously -- it faulted, + // an async interrupt, a timer fired, a debugger expression etc. + // The caller's pc is in the Return Address register, but the + // UnwindPlan for this function may have no location rule for + // the RA reg. + // This means that the caller's return address is in the RA reg + // when the function was interrupted--descend down one stack frame + // to retrieve it from the trap handler's saved context. + unwindplan_regloc.SetSame(); + have_unwindplan_regloc = true; } } >From b10162deb49ecddca6439665c2b8ea1995fdd81f Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:24:17 -0700 Subject: [PATCH 2/5] Add the sources for an API test case to be written --- .../interrupt-and-trap-funcs.s | 92 +++++++++++++++++++ .../macosx/unwind-frameless-faulted/main.c | 6 ++ 2 files changed, 98 insertions(+) create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/main.c diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s new file mode 100644 index 0000000000000..23eb35c2b31ca --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -0,0 +1,92 @@ +#define DW_CFA_register 0x9 +#define ehframe_x22 22 +#define ehframe_x23 23 +#define ehframe_pc 32 + + .section __TEXT,__text,regular,pure_instructions + +//-------------------------------------- +// to_be_interrupted() a frameless function that does a non-ABI +// function call ("is interrupted/traps" simulated) to trap(). +// Before it branches to trap(), it puts its return address in +// x23. trap() knows to branch back to $x23 when it has finished. +//-------------------------------------- + .globl _to_be_interrupted + .p2align 2 +_to_be_interrupted: + .cfi_startproc + + // This is a garbage entry to ensure that eh_frame is emitted, + // it isn't used for anything. If there's no eh_frame, lldb + // can do an assembly emulation scan and add a rule for $lr + // which won't expose the issue at hand. + .cfi_escape DW_CFA_register, ehframe_x22, ehframe_x23 + mov x24, x0 + add x24, x24, #1 + + adrp x23, L_.return at PAGE ; put return address in x4 + add x23, x23, L_.return at PAGEOFF + + b _trap ; branch to trap handler, fake async interrupt + +L_.return: + mov x0, x24 + ret + .cfi_endproc + + + +//-------------------------------------- +// trap() trap handler function, sets up stack frame +// with special unwind rule for the pc value of the +// "interrupted" stack frame (it's in x23), then calls +// break_to_debugger(). +//-------------------------------------- + .globl _trap + .p2align 2 +_trap: + .cfi_startproc + .cfi_signal_frame + + // The pc value when we were interrupted is in x23 + .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 + + // standard prologue save of fp & lr so we can call puts() + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 + .cfi_def_cfa w29, 16 + .cfi_offset w30, -8 + .cfi_offset w29, -16 + + bl _break_to_debugger + + ldp x29, x30, [sp, #16] + add sp, sp, #32 + + // jump back to $x23 to resume execution of to_be_interrupted + br x23 + .cfi_endproc + +//-------------------------------------- +// break_to_debugger() executes a BRK instruction +//-------------------------------------- + .globl _break_to_debugger + .p2align 2 +_break_to_debugger: + .cfi_startproc + + // standard prologue save of fp & lr so we can call puts() + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 + .cfi_def_cfa w29, 16 + .cfi_offset w30, -8 + .cfi_offset w29, -16 + + brk #0xf000 ;; __builtin_debugtrap aarch64 instruction + + ldp x29, x30, [sp, #16] + add sp, sp, #32 + ret + .cfi_endproc diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c b/lldb/test/API/macosx/unwind-frameless-faulted/main.c new file mode 100644 index 0000000000000..e121809f25478 --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/main.c @@ -0,0 +1,6 @@ +int to_be_interrupted(int); +int main() { + int c = 10; + c = to_be_interrupted(c); + return c; +} >From 508b9cb404072263a6ed52a75b3b7aad5d949510 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:24:58 -0700 Subject: [PATCH 3/5] Add a log msg when a frame is marked as a trap handler via the UnwindPlan. And use the trap handler flag for zeroth frame so we can properly unwind a frameless function that was interrupted while in the trap handler function. --- lldb/source/Target/RegisterContextUnwind.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 51c0f56e9bc1e..cf4b96c6eda9f 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -248,6 +248,7 @@ void RegisterContextUnwind::InitializeZerothFrame() { active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); row_register_kind = m_full_unwind_plan_sp->GetRegisterKind(); + PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp); if (active_row && log) { StreamString active_row_strm; active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, @@ -1933,6 +1934,7 @@ void RegisterContextUnwind::PropagateTrapHandlerFlagFromUnwindPlan( } m_frame_type = eTrapHandlerFrame; + UnwindLogMsg("This frame is marked as a trap handler via its UnwindPlan"); if (m_current_offset_backed_up_one != m_current_offset) { // We backed up the pc by 1 to compute the symbol context, but >From d8843fe86c0eaaa1483bed0317f832b7630d8548 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:28:40 -0700 Subject: [PATCH 4/5] Update assembly a bit. --- .../interrupt-and-trap-funcs.s | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 23eb35c2b31ca..708ad9ed56a1c 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -51,7 +51,8 @@ _trap: // The pc value when we were interrupted is in x23 .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 - // standard prologue save of fp & lr so we can call puts() + // standard prologue save of fp & lr so we can call + // break_to_debugger() sub sp, sp, #32 stp x29, x30, [sp, #16] add x29, sp, #16 @@ -76,17 +77,7 @@ _trap: _break_to_debugger: .cfi_startproc - // standard prologue save of fp & lr so we can call puts() - sub sp, sp, #32 - stp x29, x30, [sp, #16] - add x29, sp, #16 - .cfi_def_cfa w29, 16 - .cfi_offset w30, -8 - .cfi_offset w29, -16 - brk #0xf000 ;; __builtin_debugtrap aarch64 instruction - ldp x29, x30, [sp, #16] - add sp, sp, #32 ret .cfi_endproc >From 1d90aa48c92d9f8ef43c889223c0804ac1491d53 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 18:18:14 -0700 Subject: [PATCH 5/5] Add unwind rules for x0 (volatile) and x20 (non-voltile) to trap() and break_to_debugger() for fun, and add unwind instructions for the epilogue of trap(). When stopped in `break_to_debugger`, ``` * frame #0: 0x00000001000003e8 a.out`break_to_debugger + 4 frame #1: 0x00000001000003d8 a.out`trap + 16 frame #2: 0x00000001000003c0 a.out`to_be_interrupted + 20 frame #3: 0x0000000100000398 a.out`main + 32 ``` Normally we can't provide a volatile register (e.g. x0) up the stack. And we can provide a non-volatile register (e.g. x20) up the stack. I added an IsSame rule for trap() and break_to_debugger() for x0, so it CAN be passed up the stack. And I added an Undefined rule for x20 to trap() so it CAN'T be provided up the stack. If a user selects `to_be_interrupted` and does `register read`, they'll get x0 and they won't get x20. From `main`, they will not get x0 or x20 (x0 because `to_be_interrupted` doesn't have an IsSame rule). --- .../interrupt-and-trap-funcs.s | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 708ad9ed56a1c..b50c4734f18f3 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -1,4 +1,6 @@ #define DW_CFA_register 0x9 +#define ehframe_x0 0 +#define ehframe_x20 20 #define ehframe_x22 22 #define ehframe_x23 23 #define ehframe_pc 32 @@ -7,9 +9,9 @@ //-------------------------------------- // to_be_interrupted() a frameless function that does a non-ABI -// function call ("is interrupted/traps" simulated) to trap(). -// Before it branches to trap(), it puts its return address in -// x23. trap() knows to branch back to $x23 when it has finished. +// function call to trap(), simulating an async signal/interrup/exception/fault. +// Before it branches to trap(), put the return address in x23. +// trap() knows to branch back to $x23 when it has finished. //-------------------------------------- .globl _to_be_interrupted .p2align 2 @@ -51,6 +53,15 @@ _trap: // The pc value when we were interrupted is in x23 .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 + // For fun, mark x0 as unmodified so the caller can + // retrieve the value if it wants. + .cfi_same_value ehframe_x0 + + // Mark x20 as undefined. This is a callee-preserved + // (non-volatile) register by the SysV AArch64 ABI, but + // it's be fun to see lldb not passing a value past this. + .cfi_undefined ehframe_x20 + // standard prologue save of fp & lr so we can call // break_to_debugger() sub sp, sp, #32 @@ -63,7 +74,11 @@ _trap: bl _break_to_debugger ldp x29, x30, [sp, #16] + .cfi_same_value x29 + .cfi_same_value x30 + .cfi_def_cfa sp, 32 add sp, sp, #32 + .cfi_same_value sp // jump back to $x23 to resume execution of to_be_interrupted br x23 @@ -77,6 +92,10 @@ _trap: _break_to_debugger: .cfi_startproc + // For fun, mark x0 as unmodified so the caller can + // retrieve the value if it wants. + .cfi_same_value ehframe_x0 + brk #0xf000 ;; __builtin_debugtrap aarch64 instruction ret From lldb-commits at lists.llvm.org Thu May 8 18:22:03 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 18:22:03 -0700 (PDT) Subject: [Lldb-commits] [lldb] Branch island no dsym (PR #139191) Message-ID: https://github.com/jimingham created https://github.com/llvm/llvm-project/pull/139191 When we get to the branch island, we don't see the symbol for it. The only other thing I can think of that would be a dsymutil bug? Let's try this just with dwarf, and then I'll have to revert all this and see if I can reproduce this locally somehow. Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Thu May 8 18:22:16 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 18:22:16 -0700 (PDT) Subject: [Lldb-commits] [lldb] Branch island no dsym (PR #139191) In-Reply-To: Message-ID: <681d58c8.170a0220.741af.9687@mx.google.com> https://github.com/jimingham closed https://github.com/llvm/llvm-project/pull/139191 From lldb-commits at lists.llvm.org Thu May 8 18:22:38 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 18:22:38 -0700 (PDT) Subject: [Lldb-commits] [lldb] Branch island no dsym (PR #139191) In-Reply-To: Message-ID: <681d58de.170a0220.1fdb38.981c@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: None (jimingham)
Changes When we get to the branch island, we don't see the symbol for it. The only other thing I can think of that would be a dsymutil bug? Let's try this just with dwarf, and then I'll have to revert all this and see if I can reproduce this locally somehow. --- Full diff: https://github.com/llvm/llvm-project/pull/139191.diff 2 Files Affected: - (modified) lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp (+9) - (modified) lldb/test/API/macosx/branch-islands/TestBranchIslands.py (+9-2) ``````````diff diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index 578ab12268ea3..6c3040ef1a1da 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -1038,6 +1038,15 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, static RegularExpression g_branch_island_regex(g_branch_island_pattern); bool is_branch_island = g_branch_island_regex.Execute(current_name); + // FIXME: this is extra logging so I can figure out why this test is failing + // on the bot but not locally with all the same tools, etc... + if (thread_plan_sp && is_branch_island) { + if (log) { + StreamString s; + thread_plan_sp->GetDescription(&s, eDescriptionLevelVerbose); + LLDB_LOGF(log, "Am at a branch island, but already had plan: \n\t%s", s.GetData()); + } + } if (!thread_plan_sp && is_branch_island) { thread_plan_sp = std::make_shared( thread, diff --git a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py index c79840b400432..2d768d35aad03 100644 --- a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py +++ b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py @@ -2,7 +2,7 @@ Make sure that we can step in across an arm64 branch island """ - +import os import lldb import lldbsuite.test.lldbutil as lldbutil from lldbsuite.test.lldbtest import * @@ -15,7 +15,7 @@ class TestBranchIslandStepping(TestBase): @skipUnlessAppleSilicon def test_step_in_branch_island(self): """Make sure we can step in across a branch island""" - self.build() + self.build(debug_info="dwarf") self.main_source_file = lldb.SBFileSpec("main.c") self.do_test() @@ -32,6 +32,9 @@ def do_test(self): trace_before = lldbutil.print_stacktrace(thread, True) func_before = thread.frames[0].function + log_file_path = os.path.join(self.getBuildDir(), "step-log.txt") + self.runCmd(f"log enable -f {log_file_path} lldb step") + thread.StepInto() stop_frame = thread.frames[0] # This is failing on the bot, but I can't reproduce the failure @@ -59,6 +62,10 @@ def do_test(self): print( f"\nStop disassembly:\n {lldbutil.disassemble(target, stop_frame.function)}" ) + with open(log_file_path, "r") as f: + data = f.read() + print("Step Log:") + print(data) self.assertIn("foo", stop_frame.name, "Stepped into foo") var = stop_frame.FindVariable("a_variable_in_foo") ``````````
https://github.com/llvm/llvm-project/pull/139191 From lldb-commits at lists.llvm.org Thu May 8 18:23:32 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 18:23:32 -0700 (PDT) Subject: [Lldb-commits] [lldb] 803fd73 - Branch island no dsym (#139191) Message-ID: <681d5914.170a0220.809e7.b4ff@mx.google.com> Author: jimingham Date: 2025-05-08T18:22:10-07:00 New Revision: 803fd732ae634b49c308e88e9b508fdbff664034 URL: https://github.com/llvm/llvm-project/commit/803fd732ae634b49c308e88e9b508fdbff664034 DIFF: https://github.com/llvm/llvm-project/commit/803fd732ae634b49c308e88e9b508fdbff664034.diff LOG: Branch island no dsym (#139191) When we get to the branch island, we don't see the symbol for it. The only other thing I can think of that would be a dsymutil bug? Let's try this just with dwarf, and then I'll have to revert all this and see if I can reproduce this locally somehow. Added: Modified: lldb/test/API/macosx/branch-islands/TestBranchIslands.py Removed: ################################################################################ diff --git a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py index a8dd1886d5568..2d768d35aad03 100644 --- a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py +++ b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py @@ -15,7 +15,7 @@ class TestBranchIslandStepping(TestBase): @skipUnlessAppleSilicon def test_step_in_branch_island(self): """Make sure we can step in across a branch island""" - self.build() + self.build(debug_info="dwarf") self.main_source_file = lldb.SBFileSpec("main.c") self.do_test() From lldb-commits at lists.llvm.org Thu May 8 18:37:24 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 18:37:24 -0700 (PDT) Subject: [Lldb-commits] [lldb] Revert branch island experiments (PR #139192) Message-ID: https://github.com/jimingham created https://github.com/llvm/llvm-project/pull/139192 This test is failing because when we step to what is the branch island address and ask for its symbol, we can't resolve the symbol, and just call it the last padding symbol plus a bajillion. That has nothing to do with the changes in this patch, but I'll revert this and keep trying to figure out why symbol reading on this bot is wrong. Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Thu May 8 18:37:47 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 18:37:47 -0700 (PDT) Subject: [Lldb-commits] [lldb] 74120d0 - Revert branch island experiments (#139192) Message-ID: <681d5c6b.170a0220.27a782.8341@mx.google.com> Author: jimingham Date: 2025-05-08T18:37:43-07:00 New Revision: 74120d0a389584bd8d74073fb0c0b80af29f0a4c URL: https://github.com/llvm/llvm-project/commit/74120d0a389584bd8d74073fb0c0b80af29f0a4c DIFF: https://github.com/llvm/llvm-project/commit/74120d0a389584bd8d74073fb0c0b80af29f0a4c.diff LOG: Revert branch island experiments (#139192) This test is failing because when we step to what is the branch island address and ask for its symbol, we can't resolve the symbol, and just call it the last padding symbol plus a bajillion. That has nothing to do with the changes in this patch, but I'll revert this and keep trying to figure out why symbol reading on this bot is wrong. Added: Modified: lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp Removed: lldb/test/API/macosx/branch-islands/Makefile lldb/test/API/macosx/branch-islands/TestBranchIslands.py lldb/test/API/macosx/branch-islands/foo.c lldb/test/API/macosx/branch-islands/main.c lldb/test/API/macosx/branch-islands/padding1.s lldb/test/API/macosx/branch-islands/padding2.s lldb/test/API/macosx/branch-islands/padding3.s lldb/test/API/macosx/branch-islands/padding4.s ################################################################################ diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index 6c3040ef1a1da..e25c4ff55e408 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -26,7 +26,6 @@ #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Target/ThreadPlanRunToAddress.h" -#include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Utility/DataBuffer.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/LLDBLog.h" @@ -924,15 +923,15 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, if (current_symbol != nullptr) { std::vector
addresses; - ConstString current_name = - current_symbol->GetMangled().GetName(Mangled::ePreferMangled); if (current_symbol->IsTrampoline()) { + ConstString trampoline_name = + current_symbol->GetMangled().GetName(Mangled::ePreferMangled); - if (current_name) { + if (trampoline_name) { const ModuleList &images = target_sp->GetImages(); SymbolContextList code_symbols; - images.FindSymbolsWithNameAndType(current_name, eSymbolTypeCode, + images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeCode, code_symbols); for (const SymbolContext &context : code_symbols) { Address addr = context.GetFunctionOrSymbolAddress(); @@ -946,8 +945,8 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, } SymbolContextList reexported_symbols; - images.FindSymbolsWithNameAndType(current_name, eSymbolTypeReExported, - reexported_symbols); + images.FindSymbolsWithNameAndType( + trampoline_name, eSymbolTypeReExported, reexported_symbols); for (const SymbolContext &context : reexported_symbols) { if (context.symbol) { Symbol *actual_symbol = @@ -969,7 +968,7 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, } SymbolContextList indirect_symbols; - images.FindSymbolsWithNameAndType(current_name, eSymbolTypeResolver, + images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeResolver, indirect_symbols); for (const SymbolContext &context : indirect_symbols) { @@ -1029,32 +1028,6 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, thread_plan_sp = std::make_shared( thread, load_addrs, stop_others); } - // One more case we have to consider is "branch islands". These are regular - // TEXT symbols but their names end in .island plus maybe a .digit suffix. - // They are to allow arm64 code to branch further than the size of the - // address slot allows. We just need to single-instruction step in that - // case. - static const char *g_branch_island_pattern = "\\.island\\.?[0-9]*$"; - static RegularExpression g_branch_island_regex(g_branch_island_pattern); - - bool is_branch_island = g_branch_island_regex.Execute(current_name); - // FIXME: this is extra logging so I can figure out why this test is failing - // on the bot but not locally with all the same tools, etc... - if (thread_plan_sp && is_branch_island) { - if (log) { - StreamString s; - thread_plan_sp->GetDescription(&s, eDescriptionLevelVerbose); - LLDB_LOGF(log, "Am at a branch island, but already had plan: \n\t%s", s.GetData()); - } - } - if (!thread_plan_sp && is_branch_island) { - thread_plan_sp = std::make_shared( - thread, - /* step_over= */ false, /* stop_others */ false, eVoteNoOpinion, - eVoteNoOpinion); - LLDB_LOG(log, "Stepping one instruction over branch island: '{0}'.", - current_name); - } } else { LLDB_LOGF(log, "Could not find symbol for step through."); } diff --git a/lldb/test/API/macosx/branch-islands/Makefile b/lldb/test/API/macosx/branch-islands/Makefile deleted file mode 100644 index ff341522e15de..0000000000000 --- a/lldb/test/API/macosx/branch-islands/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -C_SOURCES := main.c foo.c -CFLAGS_EXTRAS := -std=c99 - -include Makefile.rules - -a.out: main.o padding1.o padding2.o padding3.o padding4.o foo.o - ${CC} ${LDFLAGS} -fuse-ld=/usr/bin/ld foo.o padding1.o padding2.o padding3.o padding4.o main.o -o a.out - -%.o: $(SRCDIR)/%.s - ${CC} -c $< - -#padding1.o: padding1.s -# ${CC} -c $(SRCDIR)/padding1.s - -#padding2.o: padding2.s -# ${CC} -c $(SRCDIR)/padding2.s diff --git a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py deleted file mode 100644 index 2d768d35aad03..0000000000000 --- a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Make sure that we can step in across an arm64 branch island -""" - -import os -import lldb -import lldbsuite.test.lldbutil as lldbutil -from lldbsuite.test.lldbtest import * -from lldbsuite.test.decorators import * - - -class TestBranchIslandStepping(TestBase): - NO_DEBUG_INFO_TESTCASE = True - - @skipUnlessAppleSilicon - def test_step_in_branch_island(self): - """Make sure we can step in across a branch island""" - self.build(debug_info="dwarf") - self.main_source_file = lldb.SBFileSpec("main.c") - self.do_test() - - def do_test(self): - (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( - self, "Set a breakpoint here", self.main_source_file - ) - - # Make sure that we did manage to generate a branch island for foo: - syms = target.FindSymbols("foo.island", lldb.eSymbolTypeCode) - self.assertEqual(len(syms), 1, "We did generate an island for foo") - - # Gathering some info to dump in case of failure: - trace_before = lldbutil.print_stacktrace(thread, True) - func_before = thread.frames[0].function - - log_file_path = os.path.join(self.getBuildDir(), "step-log.txt") - self.runCmd(f"log enable -f {log_file_path} lldb step") - - thread.StepInto() - stop_frame = thread.frames[0] - # This is failing on the bot, but I can't reproduce the failure - # locally. Let's see if we can dump some more info here to help - # figure out what went wrong... - if stop_frame.name.find("foo") == -1: - stream = lldb.SBStream() - print("Branch island symbols: ") - syms[0].GetDescription(stream) - for i in range(0, 6): - for sep in ["", "."]: - syms = target.FindSymbols( - f"foo.island{sep}{i}", lldb.eSymbolTypeCode - ) - if len(syms) > 0: - stream.Print("\n") - syms[0].GetDescription(stream) - - print(stream.GetData()) - print(f"Start backtrace:") - print(trace_before) - print(f"\n'main' disassembly:\n{lldbutil.disassemble(target, func_before)}") - print("\nEnd backtrace:\n") - lldbutil.print_stacktrace(thread) - print( - f"\nStop disassembly:\n {lldbutil.disassemble(target, stop_frame.function)}" - ) - with open(log_file_path, "r") as f: - data = f.read() - print("Step Log:") - print(data) - - self.assertIn("foo", stop_frame.name, "Stepped into foo") - var = stop_frame.FindVariable("a_variable_in_foo") - self.assertTrue(var.IsValid(), "Found the variable in foo") diff --git a/lldb/test/API/macosx/branch-islands/foo.c b/lldb/test/API/macosx/branch-islands/foo.c deleted file mode 100644 index a5dd2e59e1d82..0000000000000 --- a/lldb/test/API/macosx/branch-islands/foo.c +++ /dev/null @@ -1,6 +0,0 @@ -#include - -void foo() { - int a_variable_in_foo = 10; - printf("I am foo: %d.\n", a_variable_in_foo); -} diff --git a/lldb/test/API/macosx/branch-islands/main.c b/lldb/test/API/macosx/branch-islands/main.c deleted file mode 100644 index b5578bdd715df..0000000000000 --- a/lldb/test/API/macosx/branch-islands/main.c +++ /dev/null @@ -1,6 +0,0 @@ -extern void foo(); - -int main() { - foo(); // Set a breakpoint here - return 0; -} diff --git a/lldb/test/API/macosx/branch-islands/padding1.s b/lldb/test/API/macosx/branch-islands/padding1.s deleted file mode 100644 index 4911e53b0240d..0000000000000 --- a/lldb/test/API/macosx/branch-islands/padding1.s +++ /dev/null @@ -1,3 +0,0 @@ -.text -_padding1: -.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding2.s b/lldb/test/API/macosx/branch-islands/padding2.s deleted file mode 100644 index 5ad1bad11263b..0000000000000 --- a/lldb/test/API/macosx/branch-islands/padding2.s +++ /dev/null @@ -1,3 +0,0 @@ -.text -_padding2: -.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding3.s b/lldb/test/API/macosx/branch-islands/padding3.s deleted file mode 100644 index 9f614eecf56d9..0000000000000 --- a/lldb/test/API/macosx/branch-islands/padding3.s +++ /dev/null @@ -1,3 +0,0 @@ -.text -_padding3: -.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding4.s b/lldb/test/API/macosx/branch-islands/padding4.s deleted file mode 100644 index 12896cf5e5b8e..0000000000000 --- a/lldb/test/API/macosx/branch-islands/padding4.s +++ /dev/null @@ -1,3 +0,0 @@ -.text -_padding4: -.space 120*1024*1024 From lldb-commits at lists.llvm.org Thu May 8 18:37:50 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 18:37:50 -0700 (PDT) Subject: [Lldb-commits] [lldb] Revert branch island experiments (PR #139192) In-Reply-To: Message-ID: <681d5c6e.170a0220.10e50c.9c36@mx.google.com> https://github.com/jimingham closed https://github.com/llvm/llvm-project/pull/139192 From lldb-commits at lists.llvm.org Thu May 8 18:37:54 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 18:37:54 -0700 (PDT) Subject: [Lldb-commits] [lldb] Revert branch island experiments (PR #139192) In-Reply-To: Message-ID: <681d5c72.170a0220.d71a0.84b3@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: None (jimingham)
Changes This test is failing because when we step to what is the branch island address and ask for its symbol, we can't resolve the symbol, and just call it the last padding symbol plus a bajillion. That has nothing to do with the changes in this patch, but I'll revert this and keep trying to figure out why symbol reading on this bot is wrong. --- Full diff: https://github.com/llvm/llvm-project/pull/139192.diff 9 Files Affected: - (modified) lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp (+7-34) - (removed) lldb/test/API/macosx/branch-islands/Makefile (-16) - (removed) lldb/test/API/macosx/branch-islands/TestBranchIslands.py (-72) - (removed) lldb/test/API/macosx/branch-islands/foo.c (-6) - (removed) lldb/test/API/macosx/branch-islands/main.c (-6) - (removed) lldb/test/API/macosx/branch-islands/padding1.s (-3) - (removed) lldb/test/API/macosx/branch-islands/padding2.s (-3) - (removed) lldb/test/API/macosx/branch-islands/padding3.s (-3) - (removed) lldb/test/API/macosx/branch-islands/padding4.s (-3) ``````````diff diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index 6c3040ef1a1da..e25c4ff55e408 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -26,7 +26,6 @@ #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Target/ThreadPlanRunToAddress.h" -#include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Utility/DataBuffer.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/LLDBLog.h" @@ -924,15 +923,15 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, if (current_symbol != nullptr) { std::vector
addresses; - ConstString current_name = - current_symbol->GetMangled().GetName(Mangled::ePreferMangled); if (current_symbol->IsTrampoline()) { + ConstString trampoline_name = + current_symbol->GetMangled().GetName(Mangled::ePreferMangled); - if (current_name) { + if (trampoline_name) { const ModuleList &images = target_sp->GetImages(); SymbolContextList code_symbols; - images.FindSymbolsWithNameAndType(current_name, eSymbolTypeCode, + images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeCode, code_symbols); for (const SymbolContext &context : code_symbols) { Address addr = context.GetFunctionOrSymbolAddress(); @@ -946,8 +945,8 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, } SymbolContextList reexported_symbols; - images.FindSymbolsWithNameAndType(current_name, eSymbolTypeReExported, - reexported_symbols); + images.FindSymbolsWithNameAndType( + trampoline_name, eSymbolTypeReExported, reexported_symbols); for (const SymbolContext &context : reexported_symbols) { if (context.symbol) { Symbol *actual_symbol = @@ -969,7 +968,7 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, } SymbolContextList indirect_symbols; - images.FindSymbolsWithNameAndType(current_name, eSymbolTypeResolver, + images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeResolver, indirect_symbols); for (const SymbolContext &context : indirect_symbols) { @@ -1029,32 +1028,6 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, thread_plan_sp = std::make_shared( thread, load_addrs, stop_others); } - // One more case we have to consider is "branch islands". These are regular - // TEXT symbols but their names end in .island plus maybe a .digit suffix. - // They are to allow arm64 code to branch further than the size of the - // address slot allows. We just need to single-instruction step in that - // case. - static const char *g_branch_island_pattern = "\\.island\\.?[0-9]*$"; - static RegularExpression g_branch_island_regex(g_branch_island_pattern); - - bool is_branch_island = g_branch_island_regex.Execute(current_name); - // FIXME: this is extra logging so I can figure out why this test is failing - // on the bot but not locally with all the same tools, etc... - if (thread_plan_sp && is_branch_island) { - if (log) { - StreamString s; - thread_plan_sp->GetDescription(&s, eDescriptionLevelVerbose); - LLDB_LOGF(log, "Am at a branch island, but already had plan: \n\t%s", s.GetData()); - } - } - if (!thread_plan_sp && is_branch_island) { - thread_plan_sp = std::make_shared( - thread, - /* step_over= */ false, /* stop_others */ false, eVoteNoOpinion, - eVoteNoOpinion); - LLDB_LOG(log, "Stepping one instruction over branch island: '{0}'.", - current_name); - } } else { LLDB_LOGF(log, "Could not find symbol for step through."); } diff --git a/lldb/test/API/macosx/branch-islands/Makefile b/lldb/test/API/macosx/branch-islands/Makefile deleted file mode 100644 index ff341522e15de..0000000000000 --- a/lldb/test/API/macosx/branch-islands/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -C_SOURCES := main.c foo.c -CFLAGS_EXTRAS := -std=c99 - -include Makefile.rules - -a.out: main.o padding1.o padding2.o padding3.o padding4.o foo.o - ${CC} ${LDFLAGS} -fuse-ld=/usr/bin/ld foo.o padding1.o padding2.o padding3.o padding4.o main.o -o a.out - -%.o: $(SRCDIR)/%.s - ${CC} -c $< - -#padding1.o: padding1.s -# ${CC} -c $(SRCDIR)/padding1.s - -#padding2.o: padding2.s -# ${CC} -c $(SRCDIR)/padding2.s diff --git a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py deleted file mode 100644 index 2d768d35aad03..0000000000000 --- a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Make sure that we can step in across an arm64 branch island -""" - -import os -import lldb -import lldbsuite.test.lldbutil as lldbutil -from lldbsuite.test.lldbtest import * -from lldbsuite.test.decorators import * - - -class TestBranchIslandStepping(TestBase): - NO_DEBUG_INFO_TESTCASE = True - - @skipUnlessAppleSilicon - def test_step_in_branch_island(self): - """Make sure we can step in across a branch island""" - self.build(debug_info="dwarf") - self.main_source_file = lldb.SBFileSpec("main.c") - self.do_test() - - def do_test(self): - (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( - self, "Set a breakpoint here", self.main_source_file - ) - - # Make sure that we did manage to generate a branch island for foo: - syms = target.FindSymbols("foo.island", lldb.eSymbolTypeCode) - self.assertEqual(len(syms), 1, "We did generate an island for foo") - - # Gathering some info to dump in case of failure: - trace_before = lldbutil.print_stacktrace(thread, True) - func_before = thread.frames[0].function - - log_file_path = os.path.join(self.getBuildDir(), "step-log.txt") - self.runCmd(f"log enable -f {log_file_path} lldb step") - - thread.StepInto() - stop_frame = thread.frames[0] - # This is failing on the bot, but I can't reproduce the failure - # locally. Let's see if we can dump some more info here to help - # figure out what went wrong... - if stop_frame.name.find("foo") == -1: - stream = lldb.SBStream() - print("Branch island symbols: ") - syms[0].GetDescription(stream) - for i in range(0, 6): - for sep in ["", "."]: - syms = target.FindSymbols( - f"foo.island{sep}{i}", lldb.eSymbolTypeCode - ) - if len(syms) > 0: - stream.Print("\n") - syms[0].GetDescription(stream) - - print(stream.GetData()) - print(f"Start backtrace:") - print(trace_before) - print(f"\n'main' disassembly:\n{lldbutil.disassemble(target, func_before)}") - print("\nEnd backtrace:\n") - lldbutil.print_stacktrace(thread) - print( - f"\nStop disassembly:\n {lldbutil.disassemble(target, stop_frame.function)}" - ) - with open(log_file_path, "r") as f: - data = f.read() - print("Step Log:") - print(data) - - self.assertIn("foo", stop_frame.name, "Stepped into foo") - var = stop_frame.FindVariable("a_variable_in_foo") - self.assertTrue(var.IsValid(), "Found the variable in foo") diff --git a/lldb/test/API/macosx/branch-islands/foo.c b/lldb/test/API/macosx/branch-islands/foo.c deleted file mode 100644 index a5dd2e59e1d82..0000000000000 --- a/lldb/test/API/macosx/branch-islands/foo.c +++ /dev/null @@ -1,6 +0,0 @@ -#include - -void foo() { - int a_variable_in_foo = 10; - printf("I am foo: %d.\n", a_variable_in_foo); -} diff --git a/lldb/test/API/macosx/branch-islands/main.c b/lldb/test/API/macosx/branch-islands/main.c deleted file mode 100644 index b5578bdd715df..0000000000000 --- a/lldb/test/API/macosx/branch-islands/main.c +++ /dev/null @@ -1,6 +0,0 @@ -extern void foo(); - -int main() { - foo(); // Set a breakpoint here - return 0; -} diff --git a/lldb/test/API/macosx/branch-islands/padding1.s b/lldb/test/API/macosx/branch-islands/padding1.s deleted file mode 100644 index 4911e53b0240d..0000000000000 --- a/lldb/test/API/macosx/branch-islands/padding1.s +++ /dev/null @@ -1,3 +0,0 @@ -.text -_padding1: -.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding2.s b/lldb/test/API/macosx/branch-islands/padding2.s deleted file mode 100644 index 5ad1bad11263b..0000000000000 --- a/lldb/test/API/macosx/branch-islands/padding2.s +++ /dev/null @@ -1,3 +0,0 @@ -.text -_padding2: -.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding3.s b/lldb/test/API/macosx/branch-islands/padding3.s deleted file mode 100644 index 9f614eecf56d9..0000000000000 --- a/lldb/test/API/macosx/branch-islands/padding3.s +++ /dev/null @@ -1,3 +0,0 @@ -.text -_padding3: -.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding4.s b/lldb/test/API/macosx/branch-islands/padding4.s deleted file mode 100644 index 12896cf5e5b8e..0000000000000 --- a/lldb/test/API/macosx/branch-islands/padding4.s +++ /dev/null @@ -1,3 +0,0 @@ -.text -_padding4: -.space 120*1024*1024 ``````````
https://github.com/llvm/llvm-project/pull/139192 From lldb-commits at lists.llvm.org Thu May 8 18:47:00 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Thu, 08 May 2025 18:47:00 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681d5e94.170a0220.8cac6.a97d@mx.google.com> https://github.com/jasonmolenda updated https://github.com/llvm/llvm-project/pull/138805 >From 4fc9acd03a58a3617b113352c48e5dd2fdb58eda Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Tue, 6 May 2025 22:37:17 -0700 Subject: [PATCH 1/6] [lldb] Provide lr value in faulting frame on arm64 When a frameless function faults or is interrupted asynchronously, the UnwindPlan MAY have no register location rule for the return address register (lr on arm64); the value is simply live in the lr register when it was interrupted, and the frame below this on the stack -- e.g. sigtramp on a Unix system -- has the full register context, including that register. RegisterContextUnwind::SavedLocationForRegister, when asked to find the caller's pc value, will first see if there is a pc register location. If there isn't, on a Return Address Register architecture like arm/mips/riscv, we rewrite the register request from "pc" to "RA register", and search for a location. On frame 0 (the live frame) and an interrupted frame, the UnwindPlan may have no register location rule for the RA Reg, that is valid. A frameless function that never calls another may simply keep the return address in the live register the whole way. Our instruction emulation unwind plans explicitly add a rule (see Pavel's May 2024 change https://github.com/llvm/llvm-project/pull/91321 ), but an UnwindPlan sourced from debug_frame may not. I've got a case where this exactly happens - clang debug_frame for arm64 where there is no register location for the lr in a frameless function. There is a fault in the middle of this frameless function and we only get the lr value from the fault handler below this frame if lr has a register location of `IsSame`, in line with Pavel's 2024 change. Similar to how we see a request of the RA Reg from frame 0 after failing to find an unwind location for the pc register, the same style of special casing is needed when this is a function that was interrupted. Without this change, we can find the pc of the frame that was executing when it was interrupted, but we need $lr to find its caller, and we don't descend down to the trap handler to get that value, truncating the stack. rdar://145614545 --- lldb/source/Target/RegisterContextUnwind.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 3ed49e12476dd..23a86bee2518b 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -1377,6 +1377,7 @@ RegisterContextUnwind::SavedLocationForRegister( } } + // Check if the active_row has a register location listed. if (regnum.IsValid() && active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind), unwindplan_regloc)) { @@ -1390,11 +1391,10 @@ RegisterContextUnwind::SavedLocationForRegister( // This is frame 0 and we're retrieving the PC and it's saved in a Return // Address register and it hasn't been saved anywhere yet -- that is, // it's still live in the actual register. Handle this specially. - if (!have_unwindplan_regloc && return_address_reg.IsValid() && - IsFrameZero()) { - if (return_address_reg.GetAsKind(eRegisterKindLLDB) != - LLDB_INVALID_REGNUM) { + return_address_reg.GetAsKind(eRegisterKindLLDB) != + LLDB_INVALID_REGNUM) { + if (IsFrameZero()) { lldb_private::UnwindLLDB::ConcreteRegisterLocation new_regloc; new_regloc.type = UnwindLLDB::ConcreteRegisterLocation:: eRegisterInLiveRegisterContext; @@ -1408,6 +1408,17 @@ RegisterContextUnwind::SavedLocationForRegister( return_address_reg.GetAsKind(eRegisterKindLLDB), return_address_reg.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } else if (BehavesLikeZerothFrame()) { + // This function was interrupted asynchronously -- it faulted, + // an async interrupt, a timer fired, a debugger expression etc. + // The caller's pc is in the Return Address register, but the + // UnwindPlan for this function may have no location rule for + // the RA reg. + // This means that the caller's return address is in the RA reg + // when the function was interrupted--descend down one stack frame + // to retrieve it from the trap handler's saved context. + unwindplan_regloc.SetSame(); + have_unwindplan_regloc = true; } } >From b10162deb49ecddca6439665c2b8ea1995fdd81f Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:24:17 -0700 Subject: [PATCH 2/6] Add the sources for an API test case to be written --- .../interrupt-and-trap-funcs.s | 92 +++++++++++++++++++ .../macosx/unwind-frameless-faulted/main.c | 6 ++ 2 files changed, 98 insertions(+) create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/main.c diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s new file mode 100644 index 0000000000000..23eb35c2b31ca --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -0,0 +1,92 @@ +#define DW_CFA_register 0x9 +#define ehframe_x22 22 +#define ehframe_x23 23 +#define ehframe_pc 32 + + .section __TEXT,__text,regular,pure_instructions + +//-------------------------------------- +// to_be_interrupted() a frameless function that does a non-ABI +// function call ("is interrupted/traps" simulated) to trap(). +// Before it branches to trap(), it puts its return address in +// x23. trap() knows to branch back to $x23 when it has finished. +//-------------------------------------- + .globl _to_be_interrupted + .p2align 2 +_to_be_interrupted: + .cfi_startproc + + // This is a garbage entry to ensure that eh_frame is emitted, + // it isn't used for anything. If there's no eh_frame, lldb + // can do an assembly emulation scan and add a rule for $lr + // which won't expose the issue at hand. + .cfi_escape DW_CFA_register, ehframe_x22, ehframe_x23 + mov x24, x0 + add x24, x24, #1 + + adrp x23, L_.return at PAGE ; put return address in x4 + add x23, x23, L_.return at PAGEOFF + + b _trap ; branch to trap handler, fake async interrupt + +L_.return: + mov x0, x24 + ret + .cfi_endproc + + + +//-------------------------------------- +// trap() trap handler function, sets up stack frame +// with special unwind rule for the pc value of the +// "interrupted" stack frame (it's in x23), then calls +// break_to_debugger(). +//-------------------------------------- + .globl _trap + .p2align 2 +_trap: + .cfi_startproc + .cfi_signal_frame + + // The pc value when we were interrupted is in x23 + .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 + + // standard prologue save of fp & lr so we can call puts() + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 + .cfi_def_cfa w29, 16 + .cfi_offset w30, -8 + .cfi_offset w29, -16 + + bl _break_to_debugger + + ldp x29, x30, [sp, #16] + add sp, sp, #32 + + // jump back to $x23 to resume execution of to_be_interrupted + br x23 + .cfi_endproc + +//-------------------------------------- +// break_to_debugger() executes a BRK instruction +//-------------------------------------- + .globl _break_to_debugger + .p2align 2 +_break_to_debugger: + .cfi_startproc + + // standard prologue save of fp & lr so we can call puts() + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 + .cfi_def_cfa w29, 16 + .cfi_offset w30, -8 + .cfi_offset w29, -16 + + brk #0xf000 ;; __builtin_debugtrap aarch64 instruction + + ldp x29, x30, [sp, #16] + add sp, sp, #32 + ret + .cfi_endproc diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c b/lldb/test/API/macosx/unwind-frameless-faulted/main.c new file mode 100644 index 0000000000000..e121809f25478 --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/main.c @@ -0,0 +1,6 @@ +int to_be_interrupted(int); +int main() { + int c = 10; + c = to_be_interrupted(c); + return c; +} >From 508b9cb404072263a6ed52a75b3b7aad5d949510 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:24:58 -0700 Subject: [PATCH 3/6] Add a log msg when a frame is marked as a trap handler via the UnwindPlan. And use the trap handler flag for zeroth frame so we can properly unwind a frameless function that was interrupted while in the trap handler function. --- lldb/source/Target/RegisterContextUnwind.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 51c0f56e9bc1e..cf4b96c6eda9f 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -248,6 +248,7 @@ void RegisterContextUnwind::InitializeZerothFrame() { active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); row_register_kind = m_full_unwind_plan_sp->GetRegisterKind(); + PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp); if (active_row && log) { StreamString active_row_strm; active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, @@ -1933,6 +1934,7 @@ void RegisterContextUnwind::PropagateTrapHandlerFlagFromUnwindPlan( } m_frame_type = eTrapHandlerFrame; + UnwindLogMsg("This frame is marked as a trap handler via its UnwindPlan"); if (m_current_offset_backed_up_one != m_current_offset) { // We backed up the pc by 1 to compute the symbol context, but >From d8843fe86c0eaaa1483bed0317f832b7630d8548 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:28:40 -0700 Subject: [PATCH 4/6] Update assembly a bit. --- .../interrupt-and-trap-funcs.s | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 23eb35c2b31ca..708ad9ed56a1c 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -51,7 +51,8 @@ _trap: // The pc value when we were interrupted is in x23 .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 - // standard prologue save of fp & lr so we can call puts() + // standard prologue save of fp & lr so we can call + // break_to_debugger() sub sp, sp, #32 stp x29, x30, [sp, #16] add x29, sp, #16 @@ -76,17 +77,7 @@ _trap: _break_to_debugger: .cfi_startproc - // standard prologue save of fp & lr so we can call puts() - sub sp, sp, #32 - stp x29, x30, [sp, #16] - add x29, sp, #16 - .cfi_def_cfa w29, 16 - .cfi_offset w30, -8 - .cfi_offset w29, -16 - brk #0xf000 ;; __builtin_debugtrap aarch64 instruction - ldp x29, x30, [sp, #16] - add sp, sp, #32 ret .cfi_endproc >From 1d90aa48c92d9f8ef43c889223c0804ac1491d53 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 18:18:14 -0700 Subject: [PATCH 5/6] Add unwind rules for x0 (volatile) and x20 (non-voltile) to trap() and break_to_debugger() for fun, and add unwind instructions for the epilogue of trap(). When stopped in `break_to_debugger`, ``` * frame #0: 0x00000001000003e8 a.out`break_to_debugger + 4 frame #1: 0x00000001000003d8 a.out`trap + 16 frame #2: 0x00000001000003c0 a.out`to_be_interrupted + 20 frame #3: 0x0000000100000398 a.out`main + 32 ``` Normally we can't provide a volatile register (e.g. x0) up the stack. And we can provide a non-volatile register (e.g. x20) up the stack. I added an IsSame rule for trap() and break_to_debugger() for x0, so it CAN be passed up the stack. And I added an Undefined rule for x20 to trap() so it CAN'T be provided up the stack. If a user selects `to_be_interrupted` and does `register read`, they'll get x0 and they won't get x20. From `main`, they will not get x0 or x20 (x0 because `to_be_interrupted` doesn't have an IsSame rule). --- .../interrupt-and-trap-funcs.s | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 708ad9ed56a1c..b50c4734f18f3 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -1,4 +1,6 @@ #define DW_CFA_register 0x9 +#define ehframe_x0 0 +#define ehframe_x20 20 #define ehframe_x22 22 #define ehframe_x23 23 #define ehframe_pc 32 @@ -7,9 +9,9 @@ //-------------------------------------- // to_be_interrupted() a frameless function that does a non-ABI -// function call ("is interrupted/traps" simulated) to trap(). -// Before it branches to trap(), it puts its return address in -// x23. trap() knows to branch back to $x23 when it has finished. +// function call to trap(), simulating an async signal/interrup/exception/fault. +// Before it branches to trap(), put the return address in x23. +// trap() knows to branch back to $x23 when it has finished. //-------------------------------------- .globl _to_be_interrupted .p2align 2 @@ -51,6 +53,15 @@ _trap: // The pc value when we were interrupted is in x23 .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 + // For fun, mark x0 as unmodified so the caller can + // retrieve the value if it wants. + .cfi_same_value ehframe_x0 + + // Mark x20 as undefined. This is a callee-preserved + // (non-volatile) register by the SysV AArch64 ABI, but + // it's be fun to see lldb not passing a value past this. + .cfi_undefined ehframe_x20 + // standard prologue save of fp & lr so we can call // break_to_debugger() sub sp, sp, #32 @@ -63,7 +74,11 @@ _trap: bl _break_to_debugger ldp x29, x30, [sp, #16] + .cfi_same_value x29 + .cfi_same_value x30 + .cfi_def_cfa sp, 32 add sp, sp, #32 + .cfi_same_value sp // jump back to $x23 to resume execution of to_be_interrupted br x23 @@ -77,6 +92,10 @@ _trap: _break_to_debugger: .cfi_startproc + // For fun, mark x0 as unmodified so the caller can + // retrieve the value if it wants. + .cfi_same_value ehframe_x0 + brk #0xf000 ;; __builtin_debugtrap aarch64 instruction ret >From 303edd47f7bcfb6af0a9947ca7b0b9a0c5bec589 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 18:46:14 -0700 Subject: [PATCH 6/6] small comment improvement --- .../unwind-frameless-faulted/interrupt-and-trap-funcs.s | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index b50c4734f18f3..38be06a681bf2 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -18,10 +18,10 @@ _to_be_interrupted: .cfi_startproc - // This is a garbage entry to ensure that eh_frame is emitted, - // it isn't used for anything. If there's no eh_frame, lldb - // can do an assembly emulation scan and add a rule for $lr - // which won't expose the issue at hand. + // This is a garbage entry to ensure that eh_frame is emitted. + // If there's no eh_frame, lldb can use the assembly emulation scan, + // which always includes a rule for $lr, and we won't replicate the + // bug we're testing for. .cfi_escape DW_CFA_register, ehframe_x22, ehframe_x23 mov x24, x0 add x24, x24, #1 From lldb-commits at lists.llvm.org Thu May 8 18:48:12 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Thu, 08 May 2025 18:48:12 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681d5edc.170a0220.3c00d4.b2ca@mx.google.com> https://github.com/jasonmolenda updated https://github.com/llvm/llvm-project/pull/138805 >From 4fc9acd03a58a3617b113352c48e5dd2fdb58eda Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Tue, 6 May 2025 22:37:17 -0700 Subject: [PATCH 1/7] [lldb] Provide lr value in faulting frame on arm64 When a frameless function faults or is interrupted asynchronously, the UnwindPlan MAY have no register location rule for the return address register (lr on arm64); the value is simply live in the lr register when it was interrupted, and the frame below this on the stack -- e.g. sigtramp on a Unix system -- has the full register context, including that register. RegisterContextUnwind::SavedLocationForRegister, when asked to find the caller's pc value, will first see if there is a pc register location. If there isn't, on a Return Address Register architecture like arm/mips/riscv, we rewrite the register request from "pc" to "RA register", and search for a location. On frame 0 (the live frame) and an interrupted frame, the UnwindPlan may have no register location rule for the RA Reg, that is valid. A frameless function that never calls another may simply keep the return address in the live register the whole way. Our instruction emulation unwind plans explicitly add a rule (see Pavel's May 2024 change https://github.com/llvm/llvm-project/pull/91321 ), but an UnwindPlan sourced from debug_frame may not. I've got a case where this exactly happens - clang debug_frame for arm64 where there is no register location for the lr in a frameless function. There is a fault in the middle of this frameless function and we only get the lr value from the fault handler below this frame if lr has a register location of `IsSame`, in line with Pavel's 2024 change. Similar to how we see a request of the RA Reg from frame 0 after failing to find an unwind location for the pc register, the same style of special casing is needed when this is a function that was interrupted. Without this change, we can find the pc of the frame that was executing when it was interrupted, but we need $lr to find its caller, and we don't descend down to the trap handler to get that value, truncating the stack. rdar://145614545 --- lldb/source/Target/RegisterContextUnwind.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 3ed49e12476dd..23a86bee2518b 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -1377,6 +1377,7 @@ RegisterContextUnwind::SavedLocationForRegister( } } + // Check if the active_row has a register location listed. if (regnum.IsValid() && active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind), unwindplan_regloc)) { @@ -1390,11 +1391,10 @@ RegisterContextUnwind::SavedLocationForRegister( // This is frame 0 and we're retrieving the PC and it's saved in a Return // Address register and it hasn't been saved anywhere yet -- that is, // it's still live in the actual register. Handle this specially. - if (!have_unwindplan_regloc && return_address_reg.IsValid() && - IsFrameZero()) { - if (return_address_reg.GetAsKind(eRegisterKindLLDB) != - LLDB_INVALID_REGNUM) { + return_address_reg.GetAsKind(eRegisterKindLLDB) != + LLDB_INVALID_REGNUM) { + if (IsFrameZero()) { lldb_private::UnwindLLDB::ConcreteRegisterLocation new_regloc; new_regloc.type = UnwindLLDB::ConcreteRegisterLocation:: eRegisterInLiveRegisterContext; @@ -1408,6 +1408,17 @@ RegisterContextUnwind::SavedLocationForRegister( return_address_reg.GetAsKind(eRegisterKindLLDB), return_address_reg.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } else if (BehavesLikeZerothFrame()) { + // This function was interrupted asynchronously -- it faulted, + // an async interrupt, a timer fired, a debugger expression etc. + // The caller's pc is in the Return Address register, but the + // UnwindPlan for this function may have no location rule for + // the RA reg. + // This means that the caller's return address is in the RA reg + // when the function was interrupted--descend down one stack frame + // to retrieve it from the trap handler's saved context. + unwindplan_regloc.SetSame(); + have_unwindplan_regloc = true; } } >From b10162deb49ecddca6439665c2b8ea1995fdd81f Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:24:17 -0700 Subject: [PATCH 2/7] Add the sources for an API test case to be written --- .../interrupt-and-trap-funcs.s | 92 +++++++++++++++++++ .../macosx/unwind-frameless-faulted/main.c | 6 ++ 2 files changed, 98 insertions(+) create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/main.c diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s new file mode 100644 index 0000000000000..23eb35c2b31ca --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -0,0 +1,92 @@ +#define DW_CFA_register 0x9 +#define ehframe_x22 22 +#define ehframe_x23 23 +#define ehframe_pc 32 + + .section __TEXT,__text,regular,pure_instructions + +//-------------------------------------- +// to_be_interrupted() a frameless function that does a non-ABI +// function call ("is interrupted/traps" simulated) to trap(). +// Before it branches to trap(), it puts its return address in +// x23. trap() knows to branch back to $x23 when it has finished. +//-------------------------------------- + .globl _to_be_interrupted + .p2align 2 +_to_be_interrupted: + .cfi_startproc + + // This is a garbage entry to ensure that eh_frame is emitted, + // it isn't used for anything. If there's no eh_frame, lldb + // can do an assembly emulation scan and add a rule for $lr + // which won't expose the issue at hand. + .cfi_escape DW_CFA_register, ehframe_x22, ehframe_x23 + mov x24, x0 + add x24, x24, #1 + + adrp x23, L_.return at PAGE ; put return address in x4 + add x23, x23, L_.return at PAGEOFF + + b _trap ; branch to trap handler, fake async interrupt + +L_.return: + mov x0, x24 + ret + .cfi_endproc + + + +//-------------------------------------- +// trap() trap handler function, sets up stack frame +// with special unwind rule for the pc value of the +// "interrupted" stack frame (it's in x23), then calls +// break_to_debugger(). +//-------------------------------------- + .globl _trap + .p2align 2 +_trap: + .cfi_startproc + .cfi_signal_frame + + // The pc value when we were interrupted is in x23 + .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 + + // standard prologue save of fp & lr so we can call puts() + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 + .cfi_def_cfa w29, 16 + .cfi_offset w30, -8 + .cfi_offset w29, -16 + + bl _break_to_debugger + + ldp x29, x30, [sp, #16] + add sp, sp, #32 + + // jump back to $x23 to resume execution of to_be_interrupted + br x23 + .cfi_endproc + +//-------------------------------------- +// break_to_debugger() executes a BRK instruction +//-------------------------------------- + .globl _break_to_debugger + .p2align 2 +_break_to_debugger: + .cfi_startproc + + // standard prologue save of fp & lr so we can call puts() + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 + .cfi_def_cfa w29, 16 + .cfi_offset w30, -8 + .cfi_offset w29, -16 + + brk #0xf000 ;; __builtin_debugtrap aarch64 instruction + + ldp x29, x30, [sp, #16] + add sp, sp, #32 + ret + .cfi_endproc diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c b/lldb/test/API/macosx/unwind-frameless-faulted/main.c new file mode 100644 index 0000000000000..e121809f25478 --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/main.c @@ -0,0 +1,6 @@ +int to_be_interrupted(int); +int main() { + int c = 10; + c = to_be_interrupted(c); + return c; +} >From 508b9cb404072263a6ed52a75b3b7aad5d949510 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:24:58 -0700 Subject: [PATCH 3/7] Add a log msg when a frame is marked as a trap handler via the UnwindPlan. And use the trap handler flag for zeroth frame so we can properly unwind a frameless function that was interrupted while in the trap handler function. --- lldb/source/Target/RegisterContextUnwind.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 51c0f56e9bc1e..cf4b96c6eda9f 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -248,6 +248,7 @@ void RegisterContextUnwind::InitializeZerothFrame() { active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); row_register_kind = m_full_unwind_plan_sp->GetRegisterKind(); + PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp); if (active_row && log) { StreamString active_row_strm; active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, @@ -1933,6 +1934,7 @@ void RegisterContextUnwind::PropagateTrapHandlerFlagFromUnwindPlan( } m_frame_type = eTrapHandlerFrame; + UnwindLogMsg("This frame is marked as a trap handler via its UnwindPlan"); if (m_current_offset_backed_up_one != m_current_offset) { // We backed up the pc by 1 to compute the symbol context, but >From d8843fe86c0eaaa1483bed0317f832b7630d8548 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:28:40 -0700 Subject: [PATCH 4/7] Update assembly a bit. --- .../interrupt-and-trap-funcs.s | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 23eb35c2b31ca..708ad9ed56a1c 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -51,7 +51,8 @@ _trap: // The pc value when we were interrupted is in x23 .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 - // standard prologue save of fp & lr so we can call puts() + // standard prologue save of fp & lr so we can call + // break_to_debugger() sub sp, sp, #32 stp x29, x30, [sp, #16] add x29, sp, #16 @@ -76,17 +77,7 @@ _trap: _break_to_debugger: .cfi_startproc - // standard prologue save of fp & lr so we can call puts() - sub sp, sp, #32 - stp x29, x30, [sp, #16] - add x29, sp, #16 - .cfi_def_cfa w29, 16 - .cfi_offset w30, -8 - .cfi_offset w29, -16 - brk #0xf000 ;; __builtin_debugtrap aarch64 instruction - ldp x29, x30, [sp, #16] - add sp, sp, #32 ret .cfi_endproc >From 1d90aa48c92d9f8ef43c889223c0804ac1491d53 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 18:18:14 -0700 Subject: [PATCH 5/7] Add unwind rules for x0 (volatile) and x20 (non-voltile) to trap() and break_to_debugger() for fun, and add unwind instructions for the epilogue of trap(). When stopped in `break_to_debugger`, ``` * frame #0: 0x00000001000003e8 a.out`break_to_debugger + 4 frame #1: 0x00000001000003d8 a.out`trap + 16 frame #2: 0x00000001000003c0 a.out`to_be_interrupted + 20 frame #3: 0x0000000100000398 a.out`main + 32 ``` Normally we can't provide a volatile register (e.g. x0) up the stack. And we can provide a non-volatile register (e.g. x20) up the stack. I added an IsSame rule for trap() and break_to_debugger() for x0, so it CAN be passed up the stack. And I added an Undefined rule for x20 to trap() so it CAN'T be provided up the stack. If a user selects `to_be_interrupted` and does `register read`, they'll get x0 and they won't get x20. From `main`, they will not get x0 or x20 (x0 because `to_be_interrupted` doesn't have an IsSame rule). --- .../interrupt-and-trap-funcs.s | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 708ad9ed56a1c..b50c4734f18f3 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -1,4 +1,6 @@ #define DW_CFA_register 0x9 +#define ehframe_x0 0 +#define ehframe_x20 20 #define ehframe_x22 22 #define ehframe_x23 23 #define ehframe_pc 32 @@ -7,9 +9,9 @@ //-------------------------------------- // to_be_interrupted() a frameless function that does a non-ABI -// function call ("is interrupted/traps" simulated) to trap(). -// Before it branches to trap(), it puts its return address in -// x23. trap() knows to branch back to $x23 when it has finished. +// function call to trap(), simulating an async signal/interrup/exception/fault. +// Before it branches to trap(), put the return address in x23. +// trap() knows to branch back to $x23 when it has finished. //-------------------------------------- .globl _to_be_interrupted .p2align 2 @@ -51,6 +53,15 @@ _trap: // The pc value when we were interrupted is in x23 .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 + // For fun, mark x0 as unmodified so the caller can + // retrieve the value if it wants. + .cfi_same_value ehframe_x0 + + // Mark x20 as undefined. This is a callee-preserved + // (non-volatile) register by the SysV AArch64 ABI, but + // it's be fun to see lldb not passing a value past this. + .cfi_undefined ehframe_x20 + // standard prologue save of fp & lr so we can call // break_to_debugger() sub sp, sp, #32 @@ -63,7 +74,11 @@ _trap: bl _break_to_debugger ldp x29, x30, [sp, #16] + .cfi_same_value x29 + .cfi_same_value x30 + .cfi_def_cfa sp, 32 add sp, sp, #32 + .cfi_same_value sp // jump back to $x23 to resume execution of to_be_interrupted br x23 @@ -77,6 +92,10 @@ _trap: _break_to_debugger: .cfi_startproc + // For fun, mark x0 as unmodified so the caller can + // retrieve the value if it wants. + .cfi_same_value ehframe_x0 + brk #0xf000 ;; __builtin_debugtrap aarch64 instruction ret >From 303edd47f7bcfb6af0a9947ca7b0b9a0c5bec589 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 18:46:14 -0700 Subject: [PATCH 6/7] small comment improvement --- .../unwind-frameless-faulted/interrupt-and-trap-funcs.s | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index b50c4734f18f3..38be06a681bf2 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -18,10 +18,10 @@ _to_be_interrupted: .cfi_startproc - // This is a garbage entry to ensure that eh_frame is emitted, - // it isn't used for anything. If there's no eh_frame, lldb - // can do an assembly emulation scan and add a rule for $lr - // which won't expose the issue at hand. + // This is a garbage entry to ensure that eh_frame is emitted. + // If there's no eh_frame, lldb can use the assembly emulation scan, + // which always includes a rule for $lr, and we won't replicate the + // bug we're testing for. .cfi_escape DW_CFA_register, ehframe_x22, ehframe_x23 mov x24, x0 add x24, x24, #1 >From 620eace34ae149b8c513767935118b6e870646fc Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 18:47:54 -0700 Subject: [PATCH 7/7] more comment fix --- .../macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 38be06a681bf2..b5c5cb79656c3 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -59,7 +59,8 @@ _trap: // Mark x20 as undefined. This is a callee-preserved // (non-volatile) register by the SysV AArch64 ABI, but - // it's be fun to see lldb not passing a value past this. + // it'll be fun to see lldb not passing a value past this + // point on the stack. .cfi_undefined ehframe_x20 // standard prologue save of fp & lr so we can call From lldb-commits at lists.llvm.org Thu May 8 19:20:26 2025 From: lldb-commits at lists.llvm.org (Igor Kudrin via lldb-commits) Date: Thu, 08 May 2025 19:20:26 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][core] Fix getting summary of a variable pointing to r/o memory (PR #139196) Message-ID: https://github.com/igorkudrin created https://github.com/llvm/llvm-project/pull/139196 Motivation example: ``` > lldb -c altmain2.core ... (lldb) var F (const char *) F = 0x0804a000 "" ``` The variable `F` points to a read-only memory page not dumped to the core file, so `Process::ReadMemory()` cannot read the data. The patch switches to `Target::ReadMemory()`, which can read data both from the process memory and the application binary. >From 499f723c3f974ff53deb8f354d879e0baaa7a9e8 Mon Sep 17 00:00:00 2001 From: Igor Kudrin Date: Wed, 7 May 2025 19:55:07 -0700 Subject: [PATCH] [lldb][core] Fix getting summary of a variable pointing to r/o memory Motivation example: ``` > lldb -c altmain2.core ... (lldb) var F (const char *) F = 0x0804a000 "" ``` The variable `F` points to a read-only memory page not dumped to the core file, so `Process::ReadMemory()` cannot read the data. The patch switches to `Target::ReadMemory()`, which can read data both from the process memory and the application binary. --- lldb/source/ValueObject/ValueObject.cpp | 13 ++++++++- .../postmortem/elf-core/TestLinuxCore.py | 27 ++++++++++++++++++ .../postmortem/elf-core/altmain2.core | Bin 0 -> 40960 bytes .../postmortem/elf-core/altmain2.out | Bin 0 -> 9776 bytes 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100755 lldb/test/API/functionalities/postmortem/elf-core/altmain2.core create mode 100755 lldb/test/API/functionalities/postmortem/elf-core/altmain2.out diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp index e1c66763ff0b8..aab78428d9103 100644 --- a/lldb/source/ValueObject/ValueObject.cpp +++ b/lldb/source/ValueObject/ValueObject.cpp @@ -735,7 +735,7 @@ size_t ValueObject::GetPointeeData(DataExtractor &data, uint32_t item_idx, case eAddressTypeLoad: { ExecutionContext exe_ctx(GetExecutionContextRef()); Process *process = exe_ctx.GetProcessPtr(); - if (process) { + if (process && process->IsLiveDebugSession()) { heap_buf_ptr->SetByteSize(bytes); size_t bytes_read = process->ReadMemory( addr + offset, heap_buf_ptr->GetBytes(), bytes, error); @@ -743,6 +743,17 @@ size_t ValueObject::GetPointeeData(DataExtractor &data, uint32_t item_idx, data.SetData(data_sp); return bytes_read; } + } else if (Target *target = exe_ctx.GetTargetPtr()) { + Address target_addr; + target_addr.SetLoadAddress(addr + offset, target); + heap_buf_ptr->SetByteSize(bytes); + size_t bytes_read = + target->ReadMemory(target_addr, heap_buf_ptr->GetBytes(), bytes, + error, /*force_live_memory=*/true); + if (error.Success() || bytes_read > 0) { + data.SetData(data_sp); + return bytes_read; + } } } break; case eAddressTypeHost: { diff --git a/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py b/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py index a287fd19ba352..d1e065a32efdc 100644 --- a/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py +++ b/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py @@ -977,6 +977,33 @@ def test_get_core_file_api(self): self.assertEqual(process.GetCoreFile().GetFilename(), core_file_name) self.dbg.DeleteTarget(target) + @skipIfLLVMTargetMissing("X86") + def test_ro_cstring(self): + """ + Test that we can show the summary for a cstring variable that points + to a r/o memory page which is not dumped to a core file. + """ + target = self.dbg.CreateTarget("altmain2.out") + process = target.LoadCore("altmain2.core") + self.assertTrue(process, PROCESS_IS_VALID) + + frame = process.GetSelectedThread().GetFrameAtIndex(0) + self.assertEqual(frame.GetFunctionName(), "_start") + + var = frame.FindVariable("F") + + # The variable points to a RO segment that is not dumped to the core + # file and thus process.ReadCStringFromMemory() cannot get the value. + error = lldb.SBError() + cstr = process.ReadCStringFromMemory(var.GetValueAsUnsigned(), 256, error) + self.assertFailure(error, error_str="core file does not contain 0x804a000") + self.assertEqual(cstr, "") + + # Nevertheless, when getting the summary, the value can be read from the + # application binary. + cstr = var.GetSummary() + self.assertEqual(cstr, '"_start"') + def check_memory_regions(self, process, region_count): region_list = process.GetMemoryRegions() self.assertEqual(region_list.GetSize(), region_count) diff --git a/lldb/test/API/functionalities/postmortem/elf-core/altmain2.core b/lldb/test/API/functionalities/postmortem/elf-core/altmain2.core new file mode 100755 index 0000000000000000000000000000000000000000..b9dd8de08b813442037fcb5bedd08e8bbabfdc0b GIT binary patch literal 40960 zcmeHQ4^&jwnSa9!I!%cK#cib0IB9HSqA(1g2 at tXp8Ij5$Wtd=M$vDi6Fme86<_(|; z6+_tg`VJeLbo*!1-R7KixAr7yY1h*llO~{6F(sCWr_zMokd!oo(v~!T)FyTI_ucp2 z%tOtlZMJ8(-FH3T-0yz(yZ3(gcfb4H_udT89k#l)I-QPFl7Z86N~u&4A}{64oKY?t zsH`Y~&;E#9A!n>A8-*T&)P#5twWFNXo5Amv>t%VSoTus^om+oN`+>Rj^Db^b`}?yb z;#NyEr~PKgiY`d?X7Hdn>?ZQ1?fSGja(xN^Hy`G^{4fU zJxcBl at IkjfR9qO2^U&f7EozCGL?0 at VJw2e>np%fqkAf4~`@fOMrU4MGt74NG{OXk# z&R&C@{G$ZfEEM3jT=HFUiQ)q}D0 at MKZz=qoe9H`?K8djGEGc`2Z4s;$&G|GMWy$%K za=t*$uR=~K5A?|0$C95w4)@`ffTnjGOPY{B201d8XslfzTV3s1We#KuGnn?Dvd1XL zeqvZ7=LQT$>X&kEP~t6oFk-RJS$OWl$jG(;h{jQD+o4awcJf at xIgWgn(sIZo?tku+ ztrZ5}vHX{7>uT$r8`1R+TCmHco+V_}Whyk$Ulju66RY6^8So3uC;qCIHZ>U~_ND|M zz_2*@0XkZ>hJc2EhJc2EhQMDF0y{3c|Iff34ZBCc{Q}+pkINV6bpM~pE#pkw0;!%r zp!*BZNw7r9eFtk#H%xTT0V)7XME3)8?*HjN1VraGk-ELn%yCBCn-I~x5|MK6^|{x0 zFN&8Ws;6PKq#>Xopdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|k zpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|k zpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|k zpdp|kpds*o4S|c_|EHJV|F;@1>!bJoZT`4qF}?rKfcO99;0=51{eG&6?EM>#BU0Y) zNAJ_k$2*o4qW6;%(fj=r8bG~5O^_2Qbq#pGp8*AhmZFYGsYg at xzJ2ySK=A7Se&7m} zX?)TleX at z{rS}Ue?_#Gql93+uG5<#6HLH#JJ&GRnvG4wlZ*k8p9%y{9sQuQ_{a5~S zr01b4|NiRxcHMB~Gp8Ty`cmG%tO<$Wp*kl~AA}vf*ZpI7YvIkvBN*fn3`%9ffm zKg!8liKq3H{(WioxC!ub`JiurC`oIr7D%4hw at HI z@~3^Ew=X2aZy_h&nnefXL#vUK-wcB29sktky<{>x4C8)}oP2H;`2ZRFM<&`wN}{pI z4oY=|7xHhJGMNoD7n-ahry}XnoT-bNdM8)0^p)@JUH$C`H$5`(nYSvgJ9pMy-K$%y zuQ1CxH>3T{;5Q+^7x`A?3FOoVCEfN$PIs?Sw>ZmS*oC at fkmZBUfyn2%ss+ZK8C46i z?$cK-$nG=L{CGiD)xibEs-Xo21xNJZkUHVGb~IT8It60;*reyp^Rl at Nz9?Il!LP{9 z$l#Y{>ofSJ*_j!9e)hZ!eo^-P41Qs at F@wMPR%qul?G>cGjI@`MwwbgSk at iB;&L-`f zsxij>pnE{HUpSM2e_Ds;dVWNgt<&?vy6g-+e^{5T*YktA>`XnsUza^k&+pS^&)4$< zx@@DKAIGFs>2r-v;Wbm~^F1wh+*m`W ziSzKaorm%+B$K@}D|*lA;#Zn<=eO+cJ-lMmovxNcz323N)_(dw6AsYFnb*aC|NKNU z+4l~ATI}_L6CZBJr(jEny#ZpJGvdP$CYcnRQ>-sxNH`_-QnRun at n?nMv%Z8dEd1=O zZw58a3E>?J)t`;+9z^OfteNKqdC8`Cao_Cqt+8NpzS0^ zL(NdmnC=ZRp2&Gu?4Hpb&v{vx)!QZolt%g&R^&RTrVC+uVm at Y?X~C9YGw)wH at pLlj z=$&a2Y%`+G*f(gp|6L#?qO_Z;gj0fZwD)zL(48>#{g!H~=NSNysqgnFu!;4ZFztGq zYPx5{x?7fspTA{=U>^r-6j~-w$`bFrWl`CX&^;-y`r*76g%?cU{ps1) zbCP#!mux7rjhg!Ig7Knr!uk?05n;>|qhqJv z88Vw}2cJwH`^O5CZLDnQNm#+dFq~{Ve(`>r1-s z$()uc(LO0UjlvZN#_X-xXI~XYQ5s6j6K*w#&MfO---&od at 8Kr4_`qW4z4Hthe~0OQ z+MjRko}M62fm76q_N at K&7*%*tY%wN6z*QJK^JCFDv-7|tOs7Wu_t7Nc^N`PA4!b9z zE_P=LSBX7S)Uw849X#`p5FZ!gGvYrpzj}%PmiTw%R{}&|A=eV0A$Jmgz`ToLBRdtX zR1fp2L-A*X?tSp7Q)hjX!k7>r5QfG0_*vhS;2ejK!NGv(UcvS_u~UL=k1#H_JTBOW zMLXQ=QR at l4ZTP5d1U7NG&Jm$yP;VPNYCBA2(Y80&c^FRDGKCN4B>ZhS2UFf7wCva0 z_KWsMkJ_G=8a at rLgSTM;MP{eoHqgKDid^Tu=`o=^)-MkHZ55p)-B0`#xm>{6pezNZ5HP!3X8;+lcIgJ!agaCFSbp^ zK8^JiMwi=1#g?hXEhhz(u;wjOrtiK86m&1jK8I~ueO10#ca at pUW$RNmYwBykvSCvR z$Kh-G at 4xqS65Gx61M--O%lIAa*7q8IluYJ6Y{X~E4VgE_7BgRDo=AQ;1V4O=fXW+- zzNUL)x)YedpUz;4{rqRR-HA4wVwn$~XZzCur`)+e|)acc>o*pF!q0+de~3Uk4*{(cfQ(- zaBRB&1_%%Xh9(WVS&A&T2g&K-r1$;F%ix~^{14st*n70+1e3DEX(z9vo%|6XAnoKm08kiH6n1_=-u at 9NV7Xqw za_!w5_W)T#{D^29?H?Gy(oa7FIX1G;J#uEH_yF6$-J{}H*#`b0ZC&x%OHKu}RyHsn z5TX7}DFnoynf7Du{h72 at LVS|NdLvGV-oXqU5?H1F{2~$F|3y-GXGj$0_P{WZJ3=x zdsxUAr;sr$*azi!p~MFS2PqPy!+;zo$OG&n6t?#qwb9iZr$fAXl!C{&&@zDNfY5MB z$k at w5#^cjp$Myg~LVT2TvXhP6Z?y_S4+fu3}jTyh{obQS1oh%vVdmqIvSC2-1S_tT>0or^8D4l-+Z z9-(8m_f)d?pswsq;ZWYY(br+iELqy~9t(GkSPN{UJITN2UauwEa8;lA;9K(bNh*Bd z65xSru&OM44UmT)K#3c}$^B_5dX> zU`vEfgV3C%M5)vT$-|hh6vqAoKwc({-3JBWKaOSSeN~BLKTgN7eW>^_ajbi89BXG$ z$k_i$7Rqc1M6>?w8Nzv?dmIjnt5ynuRJT_bFA%NbHo6ECiiPb4 at ouBIElY?Rh3#3? z=T}*58;w729h}ajUTp|$dO5I7v00t at -d{ps`@)|V*q$IyMqsnOnGS4MV6px at f$hkL z32ZDzlnqPKVVq^LWl~T(K;dKO5DODy$=*R-*>eZ56S=$>qf=5;qifMbE&?;o>iO{W zW`nT7D7dnwvkA9uggV?CA+k;my^eraPLYd553$JSO!U5raJP##cdoeJfa_J!fy>1y zaeWqD3fm`esVEAhTVYS&{I-wxFT4?*iQQBB;@s^9{b!A0%S7%SS*kzmW#@OVyAI4Q%Jb&+I-n_r(D_`UKUgAG; zls&2t))+Qf2k&e-B;J=m_h()ce!l+-788Uw6VKr>h%mN^Y7I*di3>sbOAkE-=}G8@ z at zn3T;OQJo^Gp2{%i1iWcjP$|4EiN z!ePHG%h!VMm*qcvJDI#+mOlo*TbBRg9bD?l@>Af0vfTE&WYR0k%fPqD at _XJ*CYxk= z7`$DU)58Jbc~hq8I|-8&asN-vYu2nOH!s5zoa at b2YhiI=5&nzLSbL0*^6plyuq_lX zY9pC6O+rPlK>u*tqlzkBAgVfBiDWXf1-YWP z3__wT639u>i=a$mnhQPmCR#^}DJR01QU^%sZ`VS69{?I>HPVOD^#igV^|u*|NVM)U zdbF>pO_5wj-#>ipB0Z`nBJ7lO0DAwX6p$<9sze&AA)q0kA)q0kA)q0kA)q0kA)q0k zA)q0kA)q0kA)q0kA)q0kA)q0kA at KhefsgJs(AN{`^r%#(=joq_9!pZi^~lRWYeAbk zjwN}}mqFhIJqdaRbRMMZJeK?vs1URsvEcvgXAAwGSUIvjJX3*-D7tUTog`x{*OOVat3kLdbBKfYkguZK_LJl%7h`ui< z?J*eXI}S65zLz4R?^h@(6KB6y1`hO{)M@#98OpDe%lM@$lV63LQXc4$yN at M5ft!wvM{D8l%s=lUjO`)@CZNW`kLsiqdN=rNzwcxk^?tmrM>JOEx z%BfyxP)B=C2r85?=1pKY#mSBjtcq|Ru`1Ww9#9Cr;c%uFYAG2Iq zqeze6?>a-!;>5DqreSv_b!&+FBvO*a% z)!BxKFXV8nODQx&!)^Gc)x6dp at Wsr_fb8`axsiFjAM>#mrOFMy=$3Fa=ni>&=B98s zu+`6Bz8`8~_eI-$irI5Uu{6Z7j2E}8#@D|VPkY!C4&bjZwprMU$RMk2v^0gIeAplJ z)nN4ZRq}@UknjDhTVr=`^}T;v>ev(XUf%X<{9%bNYLPSi$w)XJ_4s1y-kNLct2Z<| zE?s9yuJo&@rIu#SI?H{eD?ebJFV??!ot-g!18?!Sg`-<7RgnloyN+!XyWbNH$HH59 zWt~xt9Uwvf{>|hijagFd&9^zsYr at _+$g0~H%8SCoby5Q~lRqK1T-CLWm6m8cgkXV{wH6f>Te#|4M?+oJ?Um(4Tyxd!2sG7DlSG~F zZ2@=4TTsL`IO`lXm&4Y0o2{`j9`bidRwG2flz1TKbNHh8;RDy~s4I5WIBQ*14Gj*L zt-k8kI$L$6mE^Yi+bSt{)z-tz4GoeZ5Lik|N?raCtPV%G`YOAvvUa__sRX)-OvT}--B9l$zN)&q5k+ZU5jWp{F-EQN80T15cUxU$G1t&sU1{Ni5sQ`EP*=@L zALKtxKBxn#0#6jb8o{4lHI%J)Aud#g176Oh6f9m}hlRpwF?WXp at jW#)OJTUZAh at l7 zY;UNl!88%SVq6`WT+qnXIo52b+tBE!tm)d)<>~TvwRZX1d|r2}yUp!!dr|L_6{4}q zqH?dlvdCIdQd%AeF=kbHu)VUVs667YEG?=iDJhSInS5orS7BPcQdLEfwcMlP!%-%; zl01lRi^^m1%90x^O0DIww#t&CiqevDkDKItCo2}0hryIqloprAc&1re?(1U16_?*o z$h)JAw-VpBJ;l4D9uG$Lvz_U|NX}q#N^HDt@~(ZDz!c zY58`4B-K$`>P at pMg9FjDST#&Q)wOEG?Z84YkyjU5w-=`>TGKu9bNRFmpVsmET2oD< zkzk6yO9gSKiubAbn2L|8c(-aQ_~lWm&+XwfVxEXUT~MK?0>tA^o7U-1n-=R#^IjG2 zi+NK+xjXz at SYaiW`L0Ntiua`pL3IrZs;V|s`_`beqSRNURT`Y}Z7N>Ij^uosaJk;f~ZygQ{5v)l&-=tCY5;`A(n8 zRus~c4yz^_j7WQi4Og0O4SBlI5tf#Yl5-PAO<1k`-G&$d8H#hYfkJn50TIwgOh9=EA1^QdO)3#RqMZRy^_ZE4WmZdith zxQZ-`r`Rp{)m^F(ET!-!=>+{4H_cN#lqqpb-oo?7Bi^Jr8 z3Ll9hmdk}$1V+nzS1f{=Nrf*EQ&v=d!?g-i*y`Sz;yqDZqK5)uH#3{`3cZo7Z3SLG z#UrVuBnkOC=L&Fc%@yE0ObOJL;i#X at i}fCHN7EfgeTd`oAP7+EAyAyt4&|7;1Mx;y zk9DR8Qx6>XY?*633Dg}^Ic=SE^5O=89mT5C*Cn6tmW%Gm>}y<2_6BwdMdyFeQ^2k; aAa>MNSFS9>wE at m`bXZx$UDQ;$dHgS6POs$v literal 0 HcmV?d00001 diff --git a/lldb/test/API/functionalities/postmortem/elf-core/altmain2.out b/lldb/test/API/functionalities/postmortem/elf-core/altmain2.out new file mode 100755 index 0000000000000000000000000000000000000000..e930c3d8055e11ea8256faf244a208ff5a7bc033 GIT binary patch literal 9776 zcmeHNOK%%h6h3!6;~J)M?4%Nn3S>eHG$2o2R$BTXB*BRa77?OBV#6>VPizx=#u|@l ziV9RhNUaJ|x?{nDpTK{p0I`Av3;sYQ7D#~DL;@7fH=Y|g77MmW_bBI{*LUa6{qD_d zd_JmFH-r!*MWYNQ(Y~H5bMmLNgG!k!GUz;wQ+`lK`nH~f{>Uy=N3KAFI$PprKuhdG z4c4Q&#r~IEXFb6#ba;M_>$$J=9P}jDpjlSTfBj)xhdU~$V_7AN0mXn~Krx^gPz)#r z6a$I@#eiZ!F`yVw3>;@*`|iV!e<}R at BxjAkr-?qheemny;oZt#-&PKQyjMB+hUWIa zdvH%b_u%+c6)6T31BwB~fMP%~pcqgLC#cmWIf7#9T^-I?I z?OH#G`_{sey=2dqmitM#_~ByNo-fa)X?mPzYRT;6$NS_b|Jk9Zwb?gFB*g^x1d5Mh z;Kl%n%yc;xA}e#^i?=wTZ;;3o;c53{V;2zJd^ZWZBz$(VB71|)iMLV1Kw_NC8U5NM z&nb$h^OI&~`pk4uOk at j>5oK(#GL^|+GB2Ain9oij{nF|8rv#(*GG%gu!nH~5G`}<_ zQsf|!Jx8YbqM6C_de0D6pjTN9?pk;b3k&gQehdcP6Rhk^3v5&q)7toeBaV26AVb4W zCynAChIzklh=#^Z7b59hIAjqdZsktu`W=1 at 2u>uWYc-$yL|Yp({I40n1 at uKgX1R`d z^R;bjec?iSK31u-w8%r;5B#eQKk%cL$Dtc}&8w~Di_2E2VU>1*u;lh`23|7?gRtK# z^@AI&pk9jITHEh&ov(h8;@pkJoe~a@%poZ3VVRb8)9T zw~)Np$N+Yv==A%c^kEU3Y|-<9Sa2`O3|s6C at q{Mm3Y?US2}WGt at KlN~Fn)a`eq|&c zdP!%qy0x}ib+$G(-l$x6uCK0DD-Jo%-o<6R;m1zbbK+({*tOj~&enRpbgzzIcVm|v zKd2Mgy_+2-iftSXY*Tu%zZa7og$dejgt6UibHWQd9Y0{F?$`Pa$Bo>e;rC?N3U)&2 zyR};6zbB)1EAXYi6SQzGmOKTB)v7 at nHy-(t0P687tR5Ga*@%u zV-0$CMsfI;1}?*jb3`AHyrY~-dVFwC7g>RuXN8=yh at MZp!GId{a%u-zq7{pX|BeJ0 z2+mphh#iXvUll2`_9iE!kJ|SZQ3p;cvc^Yy40mOQXiI{IYRQ*tDE}$rULK0ESI7sf zc(&Z{4~)BoLMo_1 at 1$1Zs!tIWQw)s6!47>?=pDxGb8d_kYeEaGkFeq_r2NLXf7=Jv R7{;xfCHh=WVvF at 4{{Sg@>kj|` literal 0 HcmV?d00001 From lldb-commits at lists.llvm.org Thu May 8 19:20:59 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 19:20:59 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][core] Fix getting summary of a variable pointing to r/o memory (PR #139196) In-Reply-To: Message-ID: <681d668b.170a0220.98e81.874c@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Igor Kudrin (igorkudrin)
Changes Motivation example: ``` > lldb -c altmain2.core ... (lldb) var F (const char *) F = 0x0804a000 "" ``` The variable `F` points to a read-only memory page not dumped to the core file, so `Process::ReadMemory()` cannot read the data. The patch switches to `Target::ReadMemory()`, which can read data both from the process memory and the application binary. --- Full diff: https://github.com/llvm/llvm-project/pull/139196.diff 4 Files Affected: - (modified) lldb/source/ValueObject/ValueObject.cpp (+12-1) - (modified) lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py (+27) - (added) lldb/test/API/functionalities/postmortem/elf-core/altmain2.core () - (added) lldb/test/API/functionalities/postmortem/elf-core/altmain2.out () ``````````diff diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp index e1c66763ff0b8..aab78428d9103 100644 --- a/lldb/source/ValueObject/ValueObject.cpp +++ b/lldb/source/ValueObject/ValueObject.cpp @@ -735,7 +735,7 @@ size_t ValueObject::GetPointeeData(DataExtractor &data, uint32_t item_idx, case eAddressTypeLoad: { ExecutionContext exe_ctx(GetExecutionContextRef()); Process *process = exe_ctx.GetProcessPtr(); - if (process) { + if (process && process->IsLiveDebugSession()) { heap_buf_ptr->SetByteSize(bytes); size_t bytes_read = process->ReadMemory( addr + offset, heap_buf_ptr->GetBytes(), bytes, error); @@ -743,6 +743,17 @@ size_t ValueObject::GetPointeeData(DataExtractor &data, uint32_t item_idx, data.SetData(data_sp); return bytes_read; } + } else if (Target *target = exe_ctx.GetTargetPtr()) { + Address target_addr; + target_addr.SetLoadAddress(addr + offset, target); + heap_buf_ptr->SetByteSize(bytes); + size_t bytes_read = + target->ReadMemory(target_addr, heap_buf_ptr->GetBytes(), bytes, + error, /*force_live_memory=*/true); + if (error.Success() || bytes_read > 0) { + data.SetData(data_sp); + return bytes_read; + } } } break; case eAddressTypeHost: { diff --git a/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py b/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py index a287fd19ba352..d1e065a32efdc 100644 --- a/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py +++ b/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py @@ -977,6 +977,33 @@ def test_get_core_file_api(self): self.assertEqual(process.GetCoreFile().GetFilename(), core_file_name) self.dbg.DeleteTarget(target) + @skipIfLLVMTargetMissing("X86") + def test_ro_cstring(self): + """ + Test that we can show the summary for a cstring variable that points + to a r/o memory page which is not dumped to a core file. + """ + target = self.dbg.CreateTarget("altmain2.out") + process = target.LoadCore("altmain2.core") + self.assertTrue(process, PROCESS_IS_VALID) + + frame = process.GetSelectedThread().GetFrameAtIndex(0) + self.assertEqual(frame.GetFunctionName(), "_start") + + var = frame.FindVariable("F") + + # The variable points to a RO segment that is not dumped to the core + # file and thus process.ReadCStringFromMemory() cannot get the value. + error = lldb.SBError() + cstr = process.ReadCStringFromMemory(var.GetValueAsUnsigned(), 256, error) + self.assertFailure(error, error_str="core file does not contain 0x804a000") + self.assertEqual(cstr, "") + + # Nevertheless, when getting the summary, the value can be read from the + # application binary. + cstr = var.GetSummary() + self.assertEqual(cstr, '"_start"') + def check_memory_regions(self, process, region_count): region_list = process.GetMemoryRegions() self.assertEqual(region_list.GetSize(), region_count) diff --git a/lldb/test/API/functionalities/postmortem/elf-core/altmain2.core b/lldb/test/API/functionalities/postmortem/elf-core/altmain2.core new file mode 100755 index 0000000000000..b9dd8de08b813 Binary files /dev/null and b/lldb/test/API/functionalities/postmortem/elf-core/altmain2.core differ diff --git a/lldb/test/API/functionalities/postmortem/elf-core/altmain2.out b/lldb/test/API/functionalities/postmortem/elf-core/altmain2.out new file mode 100755 index 0000000000000..e930c3d8055e1 Binary files /dev/null and b/lldb/test/API/functionalities/postmortem/elf-core/altmain2.out differ ``````````
https://github.com/llvm/llvm-project/pull/139196 From lldb-commits at lists.llvm.org Thu May 8 19:22:35 2025 From: lldb-commits at lists.llvm.org (Igor Kudrin via lldb-commits) Date: Thu, 08 May 2025 19:22:35 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][NFC] Avoid an assertion failure in dwim-print (PR #139197) Message-ID: https://github.com/igorkudrin created https://github.com/llvm/llvm-project/pull/139197 In a Debug build on Windows, printing inline diagnostics resulted in an error, for example: ``` > cd llvm-project\lldb\test\API\functionalities\postmortem\elf-core > lldb.exe -c altmain.core > p dummy LLDB diagnostics will be written to ... Please include the directory content when filing a bug report Exception Code: 0x80000003 0x00007FF8FD6633EC, C:\llvm\build\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x13A33EC byte(s), std::_Vector_const_iterator > >::_Compat() + 0x6C byte(s), C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.43.34808\include\vector, line 202 + 0x5D byte(s) 0x00007FF8FD662ABE, C:\llvm\build\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x13A2ABE byte(s), std::_Vector_const_iterator > >::operator==() + 0x1E byte(s), C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.43.34808\include\vector, line 166 + 0x0 byte(s) 0x00007FF8FD662B2E, C:\llvm\build\\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x13A2B2E byte(s), std::_Vector_const_iterator > >::operator!=() + 0x1E byte(s), C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.43.34808\include\vector, line 176 + 0xF byte(s) 0x00007FF8FD65EE1C, C:\llvm\build\\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x139EE1C byte(s), std::operator!= > >,std::_Vector_iterator > > >() + 0x3C byte(s), C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.43.34808\include\xutility, line 1947 + 0x0 byte(s) 0x00007FF8FD65D4E5, C:\llvm\build\\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x139D4E5 byte(s), lldb_private::RenderDiagnosticDetails() + 0x8F5 byte(s), C:\llvm\src\llvm-project\lldb\source\Utility\DiagnosticsRendering.cpp, line 189 + 0x25 byte(s) ... ``` The comparison operator of the iterators checks that they belong to the same container, but `remaining_details.pop_back()` invalidates `detail` making it incompatible with `remaining_details.rend()`. >From 3faf0c0a4a19d7e1d503c31a684d79295e414be4 Mon Sep 17 00:00:00 2001 From: Igor Kudrin Date: Thu, 8 May 2025 18:37:34 -0700 Subject: [PATCH] [lldb][NFC] Avoid an assertion failure in dwim-print In a Debug build on Windows, printing inline diagnostics resulted in an error, for example: ``` > cd llvm-project\lldb\test\API\functionalities\postmortem\elf-core > lldb.exe -c altmain.core > p dummy LLDB diagnostics will be written to ... Please include the directory content when filing a bug report Exception Code: 0x80000003 0x00007FF8FD6633EC, C:\llvm\build\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x13A33EC byte(s), std::_Vector_const_iterator > >::_Compat() + 0x6C byte(s), C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.43.34808\include\vector, line 202 + 0x5D byte(s) 0x00007FF8FD662ABE, C:\llvm\build\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x13A2ABE byte(s), std::_Vector_const_iterator > >::operator==() + 0x1E byte(s), C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.43.34808\include\vector, line 166 + 0x0 byte(s) 0x00007FF8FD662B2E, C:\llvm\build\\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x13A2B2E byte(s), std::_Vector_const_iterator > >::operator!=() + 0x1E byte(s), C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.43.34808\include\vector, line 176 + 0xF byte(s) 0x00007FF8FD65EE1C, C:\llvm\build\\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x139EE1C byte(s), std::operator!= > >,std::_Vector_iterator > > >() + 0x3C byte(s), C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.43.34808\include\xutility, line 1947 + 0x0 byte(s) 0x00007FF8FD65D4E5, C:\llvm\build\\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x139D4E5 byte(s), lldb_private::RenderDiagnosticDetails() + 0x8F5 byte(s), C:\llvm\src\llvm-project\lldb\source\Utility\DiagnosticsRendering.cpp, line 189 + 0x25 byte(s) ... ``` The comparison operator of the iterators checks that they belong to the same container, but `remaining_details.pop_back()` invalidates `detail` making it incompatible with `remaining_details.rend()`. --- lldb/source/Utility/DiagnosticsRendering.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lldb/source/Utility/DiagnosticsRendering.cpp b/lldb/source/Utility/DiagnosticsRendering.cpp index 368e2199b749f..c43b39b6b8fe9 100644 --- a/lldb/source/Utility/DiagnosticsRendering.cpp +++ b/lldb/source/Utility/DiagnosticsRendering.cpp @@ -185,9 +185,8 @@ void RenderDiagnosticDetails(Stream &stream, // Work through each detail in reverse order using the vector/stack. bool did_print = false; - for (auto detail = remaining_details.rbegin(); - detail != remaining_details.rend(); - ++detail, remaining_details.pop_back()) { + for (; !remaining_details.empty(); remaining_details.pop_back()) { + auto &detail = remaining_details.back(); // Get the information to print this detail and remove it from the stack. // Print all the lines for all the other messages first. stream << std::string(padding, ' '); @@ -196,7 +195,7 @@ void RenderDiagnosticDetails(Stream &stream, llvm::ArrayRef(remaining_details).drop_back(1)) { uint16_t column = remaining_detail.source_location->column; // Is this a note with the same column as another diagnostic? - if (column == detail->source_location->column) + if (column == detail.source_location->column) continue; if (column >= x_pos) { @@ -205,16 +204,16 @@ void RenderDiagnosticDetails(Stream &stream, } } - uint16_t column = detail->source_location->column; + uint16_t column = detail.source_location->column; // Print the line connecting the ^ with the error message. if (column >= x_pos) stream << std::string(column - x_pos, ' ') << joint << hbar << spacer; // Print a colorized string based on the message's severity type. - PrintSeverity(stream, detail->severity); + PrintSeverity(stream, detail.severity); // Finally, print the message and start a new line. - stream << detail->message << '\n'; + stream << detail.message << '\n'; did_print = true; } From lldb-commits at lists.llvm.org Thu May 8 19:23:09 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Thu, 08 May 2025 19:23:09 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][NFC] Avoid an assertion failure in dwim-print (PR #139197) In-Reply-To: Message-ID: <681d670d.630a0220.33b7e5.4f5e@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Igor Kudrin (igorkudrin)
Changes In a Debug build on Windows, printing inline diagnostics resulted in an error, for example: ``` > cd llvm-project\lldb\test\API\functionalities\postmortem\elf-core > lldb.exe -c altmain.core > p dummy LLDB diagnostics will be written to ... Please include the directory content when filing a bug report Exception Code: 0x80000003 0x00007FF8FD6633EC, C:\llvm\build\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x13A33EC byte(s), std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<lldb_private::DiagnosticDetail> > >::_Compat() + 0x6C byte(s), C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.43.34808\include\vector, line 202 + 0x5D byte(s) 0x00007FF8FD662ABE, C:\llvm\build\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x13A2ABE byte(s), std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<lldb_private::DiagnosticDetail> > >::operator==() + 0x1E byte(s), C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.43.34808\include\vector, line 166 + 0x0 byte(s) 0x00007FF8FD662B2E, C:\llvm\build\\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x13A2B2E byte(s), std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<lldb_private::DiagnosticDetail> > >::operator!=() + 0x1E byte(s), C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.43.34808\include\vector, line 176 + 0xF byte(s) 0x00007FF8FD65EE1C, C:\llvm\build\\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x139EE1C byte(s), std::operator!=<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<lldb_private::DiagnosticDetail> > >,std::_Vector_iterator<std::_Vector_val<std::_Simple_types<lldb_private::DiagnosticDetail> > > >() + 0x3C byte(s), C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.43.34808\include\xutility, line 1947 + 0x0 byte(s) 0x00007FF8FD65D4E5, C:\llvm\build\\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x139D4E5 byte(s), lldb_private::RenderDiagnosticDetails() + 0x8F5 byte(s), C:\llvm\src\llvm-project\lldb\source\Utility\DiagnosticsRendering.cpp, line 189 + 0x25 byte(s) ... ``` The comparison operator of the iterators checks that they belong to the same container, but `remaining_details.pop_back()` invalidates `detail` making it incompatible with `remaining_details.rend()`. --- Full diff: https://github.com/llvm/llvm-project/pull/139197.diff 1 Files Affected: - (modified) lldb/source/Utility/DiagnosticsRendering.cpp (+6-7) ``````````diff diff --git a/lldb/source/Utility/DiagnosticsRendering.cpp b/lldb/source/Utility/DiagnosticsRendering.cpp index 368e2199b749f..c43b39b6b8fe9 100644 --- a/lldb/source/Utility/DiagnosticsRendering.cpp +++ b/lldb/source/Utility/DiagnosticsRendering.cpp @@ -185,9 +185,8 @@ void RenderDiagnosticDetails(Stream &stream, // Work through each detail in reverse order using the vector/stack. bool did_print = false; - for (auto detail = remaining_details.rbegin(); - detail != remaining_details.rend(); - ++detail, remaining_details.pop_back()) { + for (; !remaining_details.empty(); remaining_details.pop_back()) { + auto &detail = remaining_details.back(); // Get the information to print this detail and remove it from the stack. // Print all the lines for all the other messages first. stream << std::string(padding, ' '); @@ -196,7 +195,7 @@ void RenderDiagnosticDetails(Stream &stream, llvm::ArrayRef(remaining_details).drop_back(1)) { uint16_t column = remaining_detail.source_location->column; // Is this a note with the same column as another diagnostic? - if (column == detail->source_location->column) + if (column == detail.source_location->column) continue; if (column >= x_pos) { @@ -205,16 +204,16 @@ void RenderDiagnosticDetails(Stream &stream, } } - uint16_t column = detail->source_location->column; + uint16_t column = detail.source_location->column; // Print the line connecting the ^ with the error message. if (column >= x_pos) stream << std::string(column - x_pos, ' ') << joint << hbar << spacer; // Print a colorized string based on the message's severity type. - PrintSeverity(stream, detail->severity); + PrintSeverity(stream, detail.severity); // Finally, print the message and start a new line. - stream << detail->message << '\n'; + stream << detail.message << '\n'; did_print = true; } ``````````
https://github.com/llvm/llvm-project/pull/139197 From lldb-commits at lists.llvm.org Thu May 8 22:50:06 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Thu, 08 May 2025 22:50:06 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681d978e.170a0220.161d58.9efd@mx.google.com> https://github.com/jasonmolenda updated https://github.com/llvm/llvm-project/pull/138805 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Thu May 8 22:52:47 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Thu, 08 May 2025 22:52:47 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681d982f.170a0220.5b497.94cd@mx.google.com> https://github.com/jasonmolenda updated https://github.com/llvm/llvm-project/pull/138805 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Thu May 8 22:55:51 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Thu, 08 May 2025 22:55:51 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681d98e7.630a0220.b019c.715b@mx.google.com> jasonmolenda wrote: OK finished writing the test case. It starts at main() and instruction steps through the test program, checking that it can backtrace all the way to main() (including all the frames in the middle) at each instruction. I also added tests for the non-ABI compliant fetching of x0 mid-stack and x20 being unfetchable mid-stack, with the hand-written eh_frame instructions in the assembly file. It has nothing to do with the frameless function that faults that I'm trying to fix here, but it was interesting that it works as I wanted so I'll add a test on it. I have the test marked as skipUnlessDarwin but I'm not sure there's much to keep it from working on Linux. Maybe that I had to prefix the symbol names with _ on dawin? Might be easiest to update the .c file to call either to_be_interrupted or _to_be_interrupted depending on the OS if that's it. https://github.com/llvm/llvm-project/pull/138805 From lldb-commits at lists.llvm.org Fri May 9 00:30:02 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 00:30:02 -0700 (PDT) Subject: [Lldb-commits] [lldb] 60b62c6 - [lldb-dap] Move the event and progress event threads into DAP (NFC) (#139167) Message-ID: <681daefa.170a0220.fc349.a9e7@mx.google.com> Author: Jonas Devlieghere Date: 2025-05-09T00:29:59-07:00 New Revision: 60b62c65bd693fa104d913bb401de3b992902520 URL: https://github.com/llvm/llvm-project/commit/60b62c65bd693fa104d913bb401de3b992902520 DIFF: https://github.com/llvm/llvm-project/commit/60b62c65bd693fa104d913bb401de3b992902520.diff LOG: [lldb-dap] Move the event and progress event threads into DAP (NFC) (#139167) Added: Modified: lldb/tools/lldb-dap/DAP.cpp lldb/tools/lldb-dap/DAP.h lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp Removed: ################################################################################ diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 1b118bb8c6a2b..f6754b1f8d7a3 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -8,6 +8,7 @@ #include "DAP.h" #include "DAPLog.h" +#include "EventHelper.h" #include "Handler/RequestHandler.h" #include "Handler/ResponseHandler.h" #include "JSONUtils.h" @@ -20,6 +21,7 @@ #include "lldb/API/SBBreakpoint.h" #include "lldb/API/SBCommandInterpreter.h" #include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBEvent.h" #include "lldb/API/SBLanguageRuntime.h" #include "lldb/API/SBListener.h" #include "lldb/API/SBProcess.h" @@ -52,6 +54,7 @@ #include #include #include +#include #include #include @@ -77,6 +80,48 @@ const char DEV_NULL[] = "/dev/null"; namespace lldb_dap { +static std::string GetStringFromStructuredData(lldb::SBStructuredData &data, + const char *key) { + lldb::SBStructuredData keyValue = data.GetValueForKey(key); + if (!keyValue) + return std::string(); + + const size_t length = keyValue.GetStringValue(nullptr, 0); + + if (length == 0) + return std::string(); + + std::string str(length + 1, 0); + keyValue.GetStringValue(&str[0], length + 1); + return str; +} + +static uint64_t GetUintFromStructuredData(lldb::SBStructuredData &data, + const char *key) { + lldb::SBStructuredData keyValue = data.GetValueForKey(key); + + if (!keyValue.IsValid()) + return 0; + return keyValue.GetUnsignedIntegerValue(); +} + +static llvm::StringRef GetModuleEventReason(uint32_t event_mask) { + if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded) + return "new"; + if (event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded) + return "removed"; + assert(event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded || + event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged); + return "changed"; +} + +/// Return string with first character capitalized. +static std::string capitalize(llvm::StringRef str) { + if (str.empty()) + return ""; + return ((llvm::Twine)llvm::toUpper(str[0]) + str.drop_front()).str(); +} + llvm::StringRef DAP::debug_adapter_path = ""; DAP::DAP(Log *log, const ReplMode default_repl_mode, @@ -94,13 +139,6 @@ DAP::DAP(Log *log, const ReplMode default_repl_mode, DAP::~DAP() = default; -/// Return string with first character capitalized. -static std::string capitalize(llvm::StringRef str) { - if (str.empty()) - return ""; - return ((llvm::Twine)llvm::toUpper(str[0]) + str.drop_front()).str(); -} - void DAP::PopulateExceptionBreakpoints() { llvm::call_once(init_exception_breakpoints_flag, [this]() { exception_breakpoints = std::vector{}; @@ -1387,4 +1425,213 @@ protocol::Capabilities DAP::GetCapabilities() { return capabilities; } +void DAP::StartEventThread() { + event_thread = std::thread(&DAP::EventThread, this); +} + +void DAP::StartProgressEventThread() { + progress_event_thread = std::thread(&DAP::ProgressEventThread, this); +} + +void DAP::ProgressEventThread() { + lldb::SBListener listener("lldb-dap.progress.listener"); + debugger.GetBroadcaster().AddListener( + listener, lldb::SBDebugger::eBroadcastBitProgress | + lldb::SBDebugger::eBroadcastBitExternalProgress); + broadcaster.AddListener(listener, eBroadcastBitStopProgressThread); + lldb::SBEvent event; + bool done = false; + while (!done) { + if (listener.WaitForEvent(1, event)) { + const auto event_mask = event.GetType(); + if (event.BroadcasterMatchesRef(broadcaster)) { + if (event_mask & eBroadcastBitStopProgressThread) { + done = true; + } + } else { + lldb::SBStructuredData data = + lldb::SBDebugger::GetProgressDataFromEvent(event); + + const uint64_t progress_id = + GetUintFromStructuredData(data, "progress_id"); + const uint64_t completed = GetUintFromStructuredData(data, "completed"); + const uint64_t total = GetUintFromStructuredData(data, "total"); + const std::string details = + GetStringFromStructuredData(data, "details"); + + if (completed == 0) { + if (total == UINT64_MAX) { + // This progress is non deterministic and won't get updated until it + // is completed. Send the "message" which will be the combined title + // and detail. The only other progress event for thus + // non-deterministic progress will be the completed event So there + // will be no need to update the detail. + const std::string message = + GetStringFromStructuredData(data, "message"); + SendProgressEvent(progress_id, message.c_str(), completed, total); + } else { + // This progress is deterministic and will receive updates, + // on the progress creation event VSCode will save the message in + // the create packet and use that as the title, so we send just the + // title in the progressCreate packet followed immediately by a + // detail packet, if there is any detail. + const std::string title = + GetStringFromStructuredData(data, "title"); + SendProgressEvent(progress_id, title.c_str(), completed, total); + if (!details.empty()) + SendProgressEvent(progress_id, details.c_str(), completed, total); + } + } else { + // This progress event is either the end of the progress dialog, or an + // update with possible detail. The "detail" string we send to VS Code + // will be appended to the progress dialog's initial text from when it + // was created. + SendProgressEvent(progress_id, details.c_str(), completed, total); + } + } + } + } +} + +// All events from the debugger, target, process, thread and frames are +// received in this function that runs in its own thread. We are using a +// "FILE *" to output packets back to VS Code and they have mutexes in them +// them prevent multiple threads from writing simultaneously so no locking +// is required. +void DAP::EventThread() { + llvm::set_thread_name(transport.GetClientName() + ".event_handler"); + lldb::SBEvent event; + lldb::SBListener listener = debugger.GetListener(); + broadcaster.AddListener(listener, eBroadcastBitStopEventThread); + debugger.GetBroadcaster().AddListener( + listener, lldb::eBroadcastBitError | lldb::eBroadcastBitWarning); + bool done = false; + while (!done) { + if (listener.WaitForEvent(1, event)) { + const auto event_mask = event.GetType(); + if (lldb::SBProcess::EventIsProcessEvent(event)) { + lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event); + if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) { + auto state = lldb::SBProcess::GetStateFromEvent(event); + switch (state) { + case lldb::eStateConnected: + case lldb::eStateDetached: + case lldb::eStateInvalid: + case lldb::eStateUnloaded: + break; + case lldb::eStateAttaching: + case lldb::eStateCrashed: + case lldb::eStateLaunching: + case lldb::eStateStopped: + case lldb::eStateSuspended: + // Only report a stopped event if the process was not + // automatically restarted. + if (!lldb::SBProcess::GetRestartedFromEvent(event)) { + SendStdOutStdErr(*this, process); + SendThreadStoppedEvent(*this); + } + break; + case lldb::eStateRunning: + case lldb::eStateStepping: + WillContinue(); + SendContinuedEvent(*this); + break; + case lldb::eStateExited: + lldb::SBStream stream; + process.GetStatus(stream); + SendOutput(OutputType::Console, stream.GetData()); + + // When restarting, we can get an "exited" event for the process we + // just killed with the old PID, or even with no PID. In that case + // we don't have to terminate the session. + if (process.GetProcessID() == LLDB_INVALID_PROCESS_ID || + process.GetProcessID() == restarting_process_id) { + restarting_process_id = LLDB_INVALID_PROCESS_ID; + } else { + // Run any exit LLDB commands the user specified in the + // launch.json + RunExitCommands(); + SendProcessExitedEvent(*this, process); + SendTerminatedEvent(); + done = true; + } + break; + } + } else if ((event_mask & lldb::SBProcess::eBroadcastBitSTDOUT) || + (event_mask & lldb::SBProcess::eBroadcastBitSTDERR)) { + SendStdOutStdErr(*this, process); + } + } else if (lldb::SBTarget::EventIsTargetEvent(event)) { + if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded || + event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded || + event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded || + event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged) { + llvm::StringRef reason = GetModuleEventReason(event_mask); + const uint32_t num_modules = + lldb::SBTarget::GetNumModulesFromEvent(event); + for (uint32_t i = 0; i < num_modules; ++i) { + lldb::SBModule module = + lldb::SBTarget::GetModuleAtIndexFromEvent(i, event); + if (!module.IsValid()) + continue; + + llvm::json::Object body; + body.try_emplace("reason", reason); + body.try_emplace("module", CreateModule(target, module)); + llvm::json::Object module_event = CreateEventObject("module"); + module_event.try_emplace("body", std::move(body)); + SendJSON(llvm::json::Value(std::move(module_event))); + } + } + } else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) { + if (event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged) { + auto event_type = + lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event); + auto bp = Breakpoint( + *this, lldb::SBBreakpoint::GetBreakpointFromEvent(event)); + // If the breakpoint was set through DAP, it will have the + // BreakpointBase::kDAPBreakpointLabel. Regardless of whether + // locations were added, removed, or resolved, the breakpoint isn't + // going away and the reason is always "changed". + if ((event_type & lldb::eBreakpointEventTypeLocationsAdded || + event_type & lldb::eBreakpointEventTypeLocationsRemoved || + event_type & lldb::eBreakpointEventTypeLocationsResolved) && + bp.MatchesName(BreakpointBase::kDAPBreakpointLabel)) { + // As the DAP client already knows the path of this breakpoint, we + // don't need to send it back as part of the "changed" event. This + // avoids sending paths that should be source mapped. Note that + // CreateBreakpoint doesn't apply source mapping and certain + // implementation ignore the source part of this event anyway. + llvm::json::Value source_bp = CreateBreakpoint(&bp); + source_bp.getAsObject()->erase("source"); + + llvm::json::Object body; + body.try_emplace("breakpoint", source_bp); + body.try_emplace("reason", "changed"); + + llvm::json::Object bp_event = CreateEventObject("breakpoint"); + bp_event.try_emplace("body", std::move(body)); + + SendJSON(llvm::json::Value(std::move(bp_event))); + } + } + } else if (event_mask & lldb::eBroadcastBitError || + event_mask & lldb::eBroadcastBitWarning) { + lldb::SBStructuredData data = + lldb::SBDebugger::GetDiagnosticFromEvent(event); + if (!data.IsValid()) + continue; + std::string type = GetStringValue(data.GetValueForKey("type")); + std::string message = GetStringValue(data.GetValueForKey("message")); + SendOutput(OutputType::Important, + llvm::formatv("{0}: {1}", type, message).str()); + } else if (event.BroadcasterMatchesRef(broadcaster)) { + if (event_mask & eBroadcastBitStopEventThread) { + done = true; + } + } + } + } +} + } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index b581ae759b1bc..afeda8d81efb0 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -167,8 +167,6 @@ struct DAP { lldb::SBTarget target; Variables variables; lldb::SBBroadcaster broadcaster; - std::thread event_thread; - std::thread progress_event_thread; llvm::StringMap source_breakpoints; FunctionBreakpointMap function_breakpoints; InstructionBreakpointMap instruction_breakpoints; @@ -418,7 +416,19 @@ struct DAP { lldb::SBMutex GetAPIMutex() const { return target.GetAPIMutex(); } + void StartEventThread(); + void StartProgressEventThread(); + private: + /// Event threads. + /// @{ + void EventThread(); + void ProgressEventThread(); + + std::thread event_thread; + std::thread progress_event_thread; + /// @} + /// Queue for all incoming messages. std::deque m_queue; std::deque m_pending_queue; diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp index 6d1e2c7402f4c..b64987746b3d5 100644 --- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp @@ -12,255 +12,11 @@ #include "LLDBUtils.h" #include "Protocol/ProtocolRequests.h" #include "RequestHandler.h" -#include "lldb/API/SBEvent.h" -#include "lldb/API/SBListener.h" -#include "lldb/API/SBStream.h" #include "lldb/API/SBTarget.h" -#include -using namespace lldb; +using namespace lldb_dap; using namespace lldb_dap::protocol; -namespace lldb_dap { - -static std::string GetStringFromStructuredData(lldb::SBStructuredData &data, - const char *key) { - lldb::SBStructuredData keyValue = data.GetValueForKey(key); - if (!keyValue) - return std::string(); - - const size_t length = keyValue.GetStringValue(nullptr, 0); - - if (length == 0) - return std::string(); - - std::string str(length + 1, 0); - keyValue.GetStringValue(&str[0], length + 1); - return str; -} - -static uint64_t GetUintFromStructuredData(lldb::SBStructuredData &data, - const char *key) { - lldb::SBStructuredData keyValue = data.GetValueForKey(key); - - if (!keyValue.IsValid()) - return 0; - return keyValue.GetUnsignedIntegerValue(); -} - -void ProgressEventThreadFunction(DAP &dap) { - lldb::SBListener listener("lldb-dap.progress.listener"); - dap.debugger.GetBroadcaster().AddListener( - listener, lldb::SBDebugger::eBroadcastBitProgress | - lldb::SBDebugger::eBroadcastBitExternalProgress); - dap.broadcaster.AddListener(listener, eBroadcastBitStopProgressThread); - lldb::SBEvent event; - bool done = false; - while (!done) { - if (listener.WaitForEvent(1, event)) { - const auto event_mask = event.GetType(); - if (event.BroadcasterMatchesRef(dap.broadcaster)) { - if (event_mask & eBroadcastBitStopProgressThread) { - done = true; - } - } else { - lldb::SBStructuredData data = - lldb::SBDebugger::GetProgressDataFromEvent(event); - - const uint64_t progress_id = - GetUintFromStructuredData(data, "progress_id"); - const uint64_t completed = GetUintFromStructuredData(data, "completed"); - const uint64_t total = GetUintFromStructuredData(data, "total"); - const std::string details = - GetStringFromStructuredData(data, "details"); - - if (completed == 0) { - if (total == UINT64_MAX) { - // This progress is non deterministic and won't get updated until it - // is completed. Send the "message" which will be the combined title - // and detail. The only other progress event for thus - // non-deterministic progress will be the completed event So there - // will be no need to update the detail. - const std::string message = - GetStringFromStructuredData(data, "message"); - dap.SendProgressEvent(progress_id, message.c_str(), completed, - total); - } else { - // This progress is deterministic and will receive updates, - // on the progress creation event VSCode will save the message in - // the create packet and use that as the title, so we send just the - // title in the progressCreate packet followed immediately by a - // detail packet, if there is any detail. - const std::string title = - GetStringFromStructuredData(data, "title"); - dap.SendProgressEvent(progress_id, title.c_str(), completed, total); - if (!details.empty()) - dap.SendProgressEvent(progress_id, details.c_str(), completed, - total); - } - } else { - // This progress event is either the end of the progress dialog, or an - // update with possible detail. The "detail" string we send to VS Code - // will be appended to the progress dialog's initial text from when it - // was created. - dap.SendProgressEvent(progress_id, details.c_str(), completed, total); - } - } - } - } -} - -static llvm::StringRef GetModuleEventReason(uint32_t event_mask) { - if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded) - return "new"; - if (event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded) - return "removed"; - assert(event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded || - event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged); - return "changed"; -} - -// All events from the debugger, target, process, thread and frames are -// received in this function that runs in its own thread. We are using a -// "FILE *" to output packets back to VS Code and they have mutexes in them -// them prevent multiple threads from writing simultaneously so no locking -// is required. -static void EventThreadFunction(DAP &dap) { - llvm::set_thread_name(dap.transport.GetClientName() + ".event_handler"); - lldb::SBEvent event; - lldb::SBListener listener = dap.debugger.GetListener(); - dap.broadcaster.AddListener(listener, eBroadcastBitStopEventThread); - dap.debugger.GetBroadcaster().AddListener(listener, eBroadcastBitError | - eBroadcastBitWarning); - bool done = false; - while (!done) { - if (listener.WaitForEvent(1, event)) { - const auto event_mask = event.GetType(); - if (lldb::SBProcess::EventIsProcessEvent(event)) { - lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event); - if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) { - auto state = lldb::SBProcess::GetStateFromEvent(event); - - DAP_LOG(dap.log, "State = {0}", state); - switch (state) { - case lldb::eStateConnected: - case lldb::eStateDetached: - case lldb::eStateInvalid: - case lldb::eStateUnloaded: - break; - case lldb::eStateAttaching: - case lldb::eStateCrashed: - case lldb::eStateLaunching: - case lldb::eStateStopped: - case lldb::eStateSuspended: - // Only report a stopped event if the process was not - // automatically restarted. - if (!lldb::SBProcess::GetRestartedFromEvent(event)) { - SendStdOutStdErr(dap, process); - SendThreadStoppedEvent(dap); - } - break; - case lldb::eStateRunning: - case lldb::eStateStepping: - dap.WillContinue(); - SendContinuedEvent(dap); - break; - case lldb::eStateExited: - lldb::SBStream stream; - process.GetStatus(stream); - dap.SendOutput(OutputType::Console, stream.GetData()); - - // When restarting, we can get an "exited" event for the process we - // just killed with the old PID, or even with no PID. In that case - // we don't have to terminate the session. - if (process.GetProcessID() == LLDB_INVALID_PROCESS_ID || - process.GetProcessID() == dap.restarting_process_id) { - dap.restarting_process_id = LLDB_INVALID_PROCESS_ID; - } else { - // Run any exit LLDB commands the user specified in the - // launch.json - dap.RunExitCommands(); - SendProcessExitedEvent(dap, process); - dap.SendTerminatedEvent(); - done = true; - } - break; - } - } else if ((event_mask & lldb::SBProcess::eBroadcastBitSTDOUT) || - (event_mask & lldb::SBProcess::eBroadcastBitSTDERR)) { - SendStdOutStdErr(dap, process); - } - } else if (lldb::SBTarget::EventIsTargetEvent(event)) { - if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded || - event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded || - event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded || - event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged) { - llvm::StringRef reason = GetModuleEventReason(event_mask); - const uint32_t num_modules = SBTarget::GetNumModulesFromEvent(event); - for (uint32_t i = 0; i < num_modules; ++i) { - lldb::SBModule module = - SBTarget::GetModuleAtIndexFromEvent(i, event); - if (!module.IsValid()) - continue; - - llvm::json::Object body; - body.try_emplace("reason", reason); - body.try_emplace("module", CreateModule(dap.target, module)); - llvm::json::Object module_event = CreateEventObject("module"); - module_event.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(module_event))); - } - } - } else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) { - if (event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged) { - auto event_type = - lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event); - auto bp = Breakpoint( - dap, lldb::SBBreakpoint::GetBreakpointFromEvent(event)); - // If the breakpoint was set through DAP, it will have the - // BreakpointBase::kDAPBreakpointLabel. Regardless of whether - // locations were added, removed, or resolved, the breakpoint isn't - // going away and the reason is always "changed". - if ((event_type & lldb::eBreakpointEventTypeLocationsAdded || - event_type & lldb::eBreakpointEventTypeLocationsRemoved || - event_type & lldb::eBreakpointEventTypeLocationsResolved) && - bp.MatchesName(BreakpointBase::kDAPBreakpointLabel)) { - // As the DAP client already knows the path of this breakpoint, we - // don't need to send it back as part of the "changed" event. This - // avoids sending paths that should be source mapped. Note that - // CreateBreakpoint doesn't apply source mapping and certain - // implementation ignore the source part of this event anyway. - llvm::json::Value source_bp = CreateBreakpoint(&bp); - source_bp.getAsObject()->erase("source"); - - llvm::json::Object body; - body.try_emplace("breakpoint", source_bp); - body.try_emplace("reason", "changed"); - - llvm::json::Object bp_event = CreateEventObject("breakpoint"); - bp_event.try_emplace("body", std::move(body)); - - dap.SendJSON(llvm::json::Value(std::move(bp_event))); - } - } - } else if (event_mask & eBroadcastBitError || - event_mask & eBroadcastBitWarning) { - SBStructuredData data = SBDebugger::GetDiagnosticFromEvent(event); - if (!data.IsValid()) - continue; - std::string type = GetStringValue(data.GetValueForKey("type")); - std::string message = GetStringValue(data.GetValueForKey("message")); - dap.SendOutput(OutputType::Important, - llvm::formatv("{0}: {1}", type, message).str()); - } else if (event.BroadcasterMatchesRef(dap.broadcaster)) { - if (event_mask & eBroadcastBitStopEventThread) { - done = true; - } - } - } - } -} - /// Initialize request; value of command field is 'initialize'. llvm::Expected InitializeRequestHandler::Run( const InitializeRequestArguments &arguments) const { @@ -314,12 +70,11 @@ llvm::Expected InitializeRequestHandler::Run( "Sends an DAP event to the client."); if (arguments.supportedFeatures.contains(eClientFeatureProgressReporting)) - dap.progress_event_thread = - std::thread(ProgressEventThreadFunction, std::ref(dap)); + dap.StartProgressEventThread(); // Start our event thread so we can receive events from the debugger, target, // process and more. - dap.event_thread = std::thread(EventThreadFunction, std::ref(dap)); + dap.StartEventThread(); return dap.GetCapabilities(); } @@ -327,5 +82,3 @@ llvm::Expected InitializeRequestHandler::Run( void InitializeRequestHandler::PostRun() const { dap.SendJSON(CreateEventObject("initialized")); } - -} // namespace lldb_dap From lldb-commits at lists.llvm.org Fri May 9 00:30:05 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Fri, 09 May 2025 00:30:05 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Move the event and progress event threads into DAP (NFC) (PR #139167) In-Reply-To: Message-ID: <681daefd.170a0220.2a6287.dec9@mx.google.com> https://github.com/JDevlieghere closed https://github.com/llvm/llvm-project/pull/139167 From lldb-commits at lists.llvm.org Fri May 9 01:57:54 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Fri, 09 May 2025 01:57:54 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb]Make `list` command work with headers when possible. (PR #139002) In-Reply-To: Message-ID: <681dc392.170a0220.1b2979.b24b@mx.google.com> ================ @@ -0,0 +1,57 @@ +## Test that `list header.h:` works correctly when header is available. +## +# RUN: split-file %s %t + +# RUN: %clang_host -g %t/main_with_inlined.cc %t/foo.cc -o %t/main_with_inlined.out +# RUN: %clang_host -g %t/main_no_inlined.cc %t/foo.cc -o %t/main_no_inlined.out + +# RUN: %lldb %t/main_with_inlined.out -o "list foo.h:2" -o "exit" 2>&1 \ +# RUN: | FileCheck %s --check-prefix=CHECK-INLINED + +## Would be nice if this listed the header too - but probably not something +## we want to support right now. +# RUN: echo quit | %lldb %t/main_no_inlined.out -o "list foo.h:2" -o "exit" 2>&1 \ +# RUN: | FileCheck %s --check-prefix=CHECK-NO-INLINED + +# CHECK-INLINED: 2 extern int* ptr; +# CHECK-INLINED: 3 void f(int x); +# CHECK-INLINED: 4 +# CHECK-INLINED: 5 inline void g(int x) { +# CHECK-INLINED: 6 *ptr = x; // should raise a SIGILL ---------------- DavidSpickett wrote: If this code is not actually being executed, remove this comment because: * it doesn't seem relevant to the test case that this code produce a signal when run (`// crash!` would be fine instead, if you want to make it similar to the original example) * I'm not sure it is SIGILL, looks like SIGSEGV to me, which is going to be distracting for anyone who sees this test fail and has to read it for the first time https://github.com/llvm/llvm-project/pull/139002 From lldb-commits at lists.llvm.org Fri May 9 01:58:46 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Fri, 09 May 2025 01:58:46 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb]Make `list` command work with headers when possible. (PR #139002) In-Reply-To: Message-ID: <681dc3c6.620a0220.1ec2f3.f3af@mx.google.com> ================ @@ -0,0 +1,57 @@ +## Test that `list header.h:` works correctly when header is available. +## +# RUN: split-file %s %t + +# RUN: %clang_host -g %t/main_with_inlined.cc %t/foo.cc -o %t/main_with_inlined.out +# RUN: %clang_host -g %t/main_no_inlined.cc %t/foo.cc -o %t/main_no_inlined.out + +# RUN: %lldb %t/main_with_inlined.out -o "list foo.h:2" -o "exit" 2>&1 \ +# RUN: | FileCheck %s --check-prefix=CHECK-INLINED + +## Would be nice if this listed the header too - but probably not something +## we want to support right now. +# RUN: echo quit | %lldb %t/main_no_inlined.out -o "list foo.h:2" -o "exit" 2>&1 \ +# RUN: | FileCheck %s --check-prefix=CHECK-NO-INLINED + +# CHECK-INLINED: 2 extern int* ptr; +# CHECK-INLINED: 3 void f(int x); +# CHECK-INLINED: 4 +# CHECK-INLINED: 5 inline void g(int x) { +# CHECK-INLINED: 6 *ptr = x; // should raise a SIGILL ---------------- DavidSpickett wrote: In other words it's not totally wrong, it's just distracting. https://github.com/llvm/llvm-project/pull/139002 From lldb-commits at lists.llvm.org Fri May 9 02:07:13 2025 From: lldb-commits at lists.llvm.org (Michael Buch via lldb-commits) Date: Fri, 09 May 2025 02:07:13 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][NFC] Avoid an assertion failure in dwim-print (PR #139197) In-Reply-To: Message-ID: <681dc5c1.170a0220.1762a8.af6f@mx.google.com> https://github.com/Michael137 approved this pull request. https://github.com/llvm/llvm-project/pull/139197 From lldb-commits at lists.llvm.org Fri May 9 02:07:14 2025 From: lldb-commits at lists.llvm.org (Michael Buch via lldb-commits) Date: Fri, 09 May 2025 02:07:14 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][NFC] Avoid an assertion failure in dwim-print (PR #139197) In-Reply-To: Message-ID: <681dc5c2.170a0220.c53be.b191@mx.google.com> ================ @@ -185,9 +185,8 @@ void RenderDiagnosticDetails(Stream &stream, // Work through each detail in reverse order using the vector/stack. bool did_print = false; - for (auto detail = remaining_details.rbegin(); - detail != remaining_details.rend(); - ++detail, remaining_details.pop_back()) { + for (; !remaining_details.empty(); remaining_details.pop_back()) { + auto &detail = remaining_details.back(); ---------------- Michael137 wrote: ```suggestion const auto &detail = remaining_details.back(); ``` ? https://github.com/llvm/llvm-project/pull/139197 From lldb-commits at lists.llvm.org Fri May 9 02:27:12 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Fri, 09 May 2025 02:27:12 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Ptrace seize dead process (PR #137041) In-Reply-To: Message-ID: <681dca70.170a0220.2d80e1.f192@mx.google.com> DavidSpickett wrote: You can include your gist content in the PR summary, I've certainly seen longer commit messages than that in llvm :) The question I want to be able to answer from the final commit message is "what is a dead process and how do I make one?". So that in future if I need to investigate this code, I know where to start. You can do that by linking to well established documentation on the subject, or writing it in your own words, and/or including that example. If we are able to construct a test case, that would serve the same purpose. https://github.com/llvm/llvm-project/pull/137041 From lldb-commits at lists.llvm.org Fri May 9 02:28:05 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Fri, 09 May 2025 02:28:05 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB] Ptrace seize dead process (PR #137041) In-Reply-To: Message-ID: <681dcaa5.630a0220.39e75f.71b6@mx.google.com> DavidSpickett wrote: Also FYI this has CI failures, https://buildkite.com/llvm-project/github-pull-requests/builds/177176#0196b180-e7c0-49e9-99ac-65dcc8a3c1a9, not sure if you are aware. LLDB isn't known for being super stable in pre-commit CI, but they are on the same topic as this. https://github.com/llvm/llvm-project/pull/137041 From lldb-commits at lists.llvm.org Fri May 9 02:40:56 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Fri, 09 May 2025 02:40:56 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][core] Fix getting summary of a variable pointing to r/o memory (PR #139196) In-Reply-To: Message-ID: <681dcda8.170a0220.19d36f.e1b4@mx.google.com> https://github.com/DavidSpickett commented: Surprised no one found this sooner, thanks for contributing. https://github.com/llvm/llvm-project/pull/139196 From lldb-commits at lists.llvm.org Fri May 9 02:40:56 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Fri, 09 May 2025 02:40:56 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][core] Fix getting summary of a variable pointing to r/o memory (PR #139196) In-Reply-To: Message-ID: <681dcda8.170a0220.326dc.abbe@mx.google.com> https://github.com/DavidSpickett edited https://github.com/llvm/llvm-project/pull/139196 From lldb-commits at lists.llvm.org Fri May 9 02:40:56 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Fri, 09 May 2025 02:40:56 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][core] Fix getting summary of a variable pointing to r/o memory (PR #139196) In-Reply-To: Message-ID: <681dcda8.170a0220.1762c6.b368@mx.google.com> ================ @@ -977,6 +977,33 @@ def test_get_core_file_api(self): self.assertEqual(process.GetCoreFile().GetFilename(), core_file_name) self.dbg.DeleteTarget(target) + @skipIfLLVMTargetMissing("X86") + def test_ro_cstring(self): ---------------- DavidSpickett wrote: Please expand "ro" to "read_only" in all the places you've used it, if you're referring to the flags of the memory mapping then fine then it's fine to use what the flag is, but otherwise, expanding the name saves future authors a few seconds figuring out what you mean. Also if I'm going to grep for tests related to this topic, "ro" is not my first choice of search terms, though that is just me of course. https://github.com/llvm/llvm-project/pull/139196 From lldb-commits at lists.llvm.org Fri May 9 02:40:56 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Fri, 09 May 2025 02:40:56 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][core] Fix getting summary of a variable pointing to r/o memory (PR #139196) In-Reply-To: Message-ID: <681dcda8.170a0220.85a02.b34e@mx.google.com> ================ ---------------- DavidSpickett wrote: Could already checked in core files be used for this test? I assume not but please confirm. We like to avoid checked in binaries if we can. I think we need the core and the program file because the program file tells us the address of `F`, correct? https://github.com/llvm/llvm-project/pull/139196 From lldb-commits at lists.llvm.org Fri May 9 02:40:57 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Fri, 09 May 2025 02:40:57 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][core] Fix getting summary of a variable pointing to r/o memory (PR #139196) In-Reply-To: Message-ID: <681dcda9.630a0220.29264d.6fb0@mx.google.com> ================ @@ -735,14 +735,25 @@ size_t ValueObject::GetPointeeData(DataExtractor &data, uint32_t item_idx, case eAddressTypeLoad: { ExecutionContext exe_ctx(GetExecutionContextRef()); Process *process = exe_ctx.GetProcessPtr(); - if (process) { + if (process && process->IsLiveDebugSession()) { heap_buf_ptr->SetByteSize(bytes); size_t bytes_read = process->ReadMemory( addr + offset, heap_buf_ptr->GetBytes(), bytes, error); if (error.Success() || bytes_read > 0) { data.SetData(data_sp); return bytes_read; } + } else if (Target *target = exe_ctx.GetTargetPtr()) { + Address target_addr; + target_addr.SetLoadAddress(addr + offset, target); + heap_buf_ptr->SetByteSize(bytes); + size_t bytes_read = + target->ReadMemory(target_addr, heap_buf_ptr->GetBytes(), bytes, + error, /*force_live_memory=*/true); + if (error.Success() || bytes_read > 0) { + data.SetData(data_sp); + return bytes_read; + } ---------------- DavidSpickett wrote: I want to say factor this bit out, but there's no `else` clause for the `if (process &&`, so I don't think it can be done. https://github.com/llvm/llvm-project/pull/139196 From lldb-commits at lists.llvm.org Fri May 9 02:40:57 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Fri, 09 May 2025 02:40:57 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][core] Fix getting summary of a variable pointing to r/o memory (PR #139196) In-Reply-To: Message-ID: <681dcda9.170a0220.a1494.acb6@mx.google.com> ================ @@ -735,14 +735,25 @@ size_t ValueObject::GetPointeeData(DataExtractor &data, uint32_t item_idx, case eAddressTypeLoad: { ExecutionContext exe_ctx(GetExecutionContextRef()); Process *process = exe_ctx.GetProcessPtr(); - if (process) { + if (process && process->IsLiveDebugSession()) { heap_buf_ptr->SetByteSize(bytes); size_t bytes_read = process->ReadMemory( addr + offset, heap_buf_ptr->GetBytes(), bytes, error); if (error.Success() || bytes_read > 0) { data.SetData(data_sp); return bytes_read; } + } else if (Target *target = exe_ctx.GetTargetPtr()) { + Address target_addr; + target_addr.SetLoadAddress(addr + offset, target); + heap_buf_ptr->SetByteSize(bytes); + size_t bytes_read = + target->ReadMemory(target_addr, heap_buf_ptr->GetBytes(), bytes, + error, /*force_live_memory=*/true); ---------------- DavidSpickett wrote: force_live_memory being true seems backwards, but I suppose that it has no effect here because we know we're not in a live session. Should it be false just for consistency? Maybe not, because this `else if` executes if either `process` is null, or we are not in a live process. If it's the former condition then we do want to force live memory. Is that your logic here? https://github.com/llvm/llvm-project/pull/139196 From lldb-commits at lists.llvm.org Fri May 9 02:40:57 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Fri, 09 May 2025 02:40:57 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][core] Fix getting summary of a variable pointing to r/o memory (PR #139196) In-Reply-To: Message-ID: <681dcda9.170a0220.15a177.e6eb@mx.google.com> ================ ---------------- DavidSpickett wrote: And these were made using source code that's already checked in I assume? https://github.com/llvm/llvm-project/pull/139196 From lldb-commits at lists.llvm.org Fri May 9 02:58:01 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Fri, 09 May 2025 02:58:01 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Expose QueueThreadPlanForStepSingleInstruction function to SBThreadPlan (PR #137904) In-Reply-To: Message-ID: <681dd1a9.170a0220.103600.c804@mx.google.com> DavidSpickett wrote: At this time there is no pre-merge CI for Arm. If you feel like hacking something together, it is possible to use Github's Runners to test it on your own fork. I hope to make that route more user friendly at some point, and expand the automatic CI to Arm when we can. Fix ups after landing are a fact of life for the time being :) https://github.com/llvm/llvm-project/pull/137904 From lldb-commits at lists.llvm.org Fri May 9 03:31:48 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Fri, 09 May 2025 03:31:48 -0700 (PDT) Subject: [Lldb-commits] [lldb] fd8b84e - [lldb][test] Skip beginning/end of file tests on Windows Message-ID: <681dd994.170a0220.366d18.a121@mx.google.com> Author: David Spickett Date: 2025-05-09T10:31:17Z New Revision: fd8b84ea0fa1eb1da105257f419d926278dc0445 URL: https://github.com/llvm/llvm-project/commit/fd8b84ea0fa1eb1da105257f419d926278dc0445 DIFF: https://github.com/llvm/llvm-project/commit/fd8b84ea0fa1eb1da105257f419d926278dc0445.diff LOG: [lldb][test] Skip beginning/end of file tests on Windows Added in https://github.com/llvm/llvm-project/pull/137515, as the source uses unistd.h which isn't present there. | C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\test\Shell\Commands/Inputs/sigchld.c:4:10: fatal error: 'unistd.h' file not found | 4 | #include | | ^~~~~~~~~~ | 1 error generated. Added: Modified: lldb/test/Shell/Commands/command-list-reach-beginning-of-file.test lldb/test/Shell/Commands/command-list-reach-end-of-file.test Removed: ################################################################################ diff --git a/lldb/test/Shell/Commands/command-list-reach-beginning-of-file.test b/lldb/test/Shell/Commands/command-list-reach-beginning-of-file.test index 5ca1b5c2306a7..fa4a93e5904aa 100644 --- a/lldb/test/Shell/Commands/command-list-reach-beginning-of-file.test +++ b/lldb/test/Shell/Commands/command-list-reach-beginning-of-file.test @@ -1,3 +1,5 @@ +# Source uses unistd.h. +# UNSUPPORTED: system-windows # RUN: %clang_host -g -O0 %S/Inputs/sigchld.c -o %t.out # RUN: %lldb %t.out -b -s %s 2>&1 | FileCheck %s diff --git a/lldb/test/Shell/Commands/command-list-reach-end-of-file.test b/lldb/test/Shell/Commands/command-list-reach-end-of-file.test index c5e9c8169e7d9..edf4c521a9e76 100644 --- a/lldb/test/Shell/Commands/command-list-reach-end-of-file.test +++ b/lldb/test/Shell/Commands/command-list-reach-end-of-file.test @@ -1,3 +1,5 @@ +# Source uses unistd.h. +# UNSUPPORTED: system-windows # RUN: %clang_host -g -O0 %S/Inputs/sigchld.c -o %t.out # RUN: %lldb %t.out -b -s %s 2>&1 | FileCheck %s @@ -23,4 +25,4 @@ list # CHECK: note: Reached end of the file, no more to page list -# CHECK: note: Reached end of the file, no more to page \ No newline at end of file +# CHECK: note: Reached end of the file, no more to page From lldb-commits at lists.llvm.org Fri May 9 03:32:57 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Fri, 09 May 2025 03:32:57 -0700 (PDT) Subject: [Lldb-commits] =?utf-8?q?=5Blldb=5D_=5Blldb=5D_print_a_notice_whe?= =?utf-8?q?n_=60source_list=60_paging_reaches_the_end_of_th=E2=80=A6_=28PR?= =?utf-8?b?ICMxMzc1MTUp?= In-Reply-To: Message-ID: <681dd9d9.050a0220.cb9fb.f224@mx.google.com> DavidSpickett wrote: I've [skipped](https://github.com/llvm/llvm-project/commit/fd8b84ea0fa1eb1da105257f419d926278dc0445) the new tests on Windows because we don't have the headers the sigchld example uses. (no notification of the failure was sent because we were already failing some lldb-dap tests) https://github.com/llvm/llvm-project/pull/137515 From lldb-commits at lists.llvm.org Fri May 9 05:18:47 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Fri, 09 May 2025 05:18:47 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][lldb-dap] Disable more tests on Windows (PR #139251) Message-ID: https://github.com/DavidSpickett created https://github.com/llvm/llvm-project/pull/139251 These are currently failing on Windows on Arm: https://lab.llvm.org/buildbot/#/builders/141/builds/8556 ******************** Unresolved Tests (1): lldb-api :: tools/lldb-dap/memory/TestDAP_memory.py ******************** Failed Tests (1): lldb-api :: tools/lldb-dap/variables/TestDAP_variables.py Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Fri May 9 05:18:58 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Fri, 09 May 2025 05:18:58 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][lldb-dap] Disable more tests on Windows (PR #139251) In-Reply-To: Message-ID: <681df2b2.170a0220.385598.b523@mx.google.com> https://github.com/DavidSpickett edited https://github.com/llvm/llvm-project/pull/139251 From lldb-commits at lists.llvm.org Fri May 9 05:19:16 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 05:19:16 -0700 (PDT) Subject: [Lldb-commits] [lldb] c64c64d - [lldb][lldb-dap] Disable more tests on Windows (#139251) Message-ID: <681df2c4.170a0220.10baaf.d19e@mx.google.com> Author: David Spickett Date: 2025-05-09T13:19:13+01:00 New Revision: c64c64db7b4b30dc5c5fad3b854f567254d1a615 URL: https://github.com/llvm/llvm-project/commit/c64c64db7b4b30dc5c5fad3b854f567254d1a615 DIFF: https://github.com/llvm/llvm-project/commit/c64c64db7b4b30dc5c5fad3b854f567254d1a615.diff LOG: [lldb][lldb-dap] Disable more tests on Windows (#139251) These are currently failing on Windows on Arm: https://lab.llvm.org/buildbot/#/builders/141/builds/8556 ``` ******************** Unresolved Tests (1): lldb-api :: tools/lldb-dap/memory/TestDAP_memory.py ******************** Failed Tests (1): lldb-api :: tools/lldb-dap/variables/TestDAP_variables.py ``` Added: Modified: lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py Removed: ################################################################################ diff --git a/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py b/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py index ea43fccf016a7..74062f3ab2164 100644 --- a/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py +++ b/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py @@ -12,6 +12,7 @@ class TestDAP_memory(lldbdap_testcase.DAPTestCaseBase): + @skipIfWindows def test_memory_refs_variables(self): """ Tests memory references for evaluate @@ -33,6 +34,7 @@ def test_memory_refs_variables(self): # Non-pointers should also have memory-references self.assertIn("memoryReference", locals["not_a_ptr"].keys()) + @skipIfWindows def test_memory_refs_evaluate(self): """ Tests memory references for evaluate @@ -52,6 +54,7 @@ def test_memory_refs_evaluate(self): self.dap_server.request_evaluate("rawptr")["body"].keys(), ) + @skipIfWindows def test_memory_refs_set_variable(self): """ Tests memory references for `setVariable` @@ -74,6 +77,7 @@ def test_memory_refs_set_variable(self): ].keys(), ) + @skipIfWindows def test_readMemory(self): """ Tests the 'readMemory' request diff --git a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py index 3b45cdc245838..296e4911f4052 100644 --- a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py +++ b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py @@ -411,16 +411,19 @@ def do_test_scopes_variables_setVariable_evaluate( self.verify_variables(verify_locals, locals) + @skipIfWindows def test_scopes_variables_setVariable_evaluate(self): self.do_test_scopes_variables_setVariable_evaluate( enableAutoVariableSummaries=False ) + @skipIfWindows def test_scopes_variables_setVariable_evaluate_with_descriptive_summaries(self): self.do_test_scopes_variables_setVariable_evaluate( enableAutoVariableSummaries=True ) + @skipIfWindows def do_test_scopes_and_evaluate_expansion(self, enableAutoVariableSummaries: bool): """ Tests the evaluated expression expands successfully after "scopes" packets @@ -673,6 +676,7 @@ def do_test_indexedVariables(self, enableSyntheticChildDebugging: bool): ]["variables"] self.verify_variables(verify_children, children) + @skipIfWindows def test_return_variables(self): """ Test the stepping out of a function with return value show the variable correctly. From lldb-commits at lists.llvm.org Fri May 9 05:19:19 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Fri, 09 May 2025 05:19:19 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][lldb-dap] Disable more tests on Windows (PR #139251) In-Reply-To: Message-ID: <681df2c7.a70a0220.e4817.f949@mx.google.com> https://github.com/DavidSpickett closed https://github.com/llvm/llvm-project/pull/139251 From lldb-commits at lists.llvm.org Fri May 9 05:19:21 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 05:19:21 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][lldb-dap] Disable more tests on Windows (PR #139251) In-Reply-To: Message-ID: <681df2c9.170a0220.103600.cfd7@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: David Spickett (DavidSpickett)
Changes These are currently failing on Windows on Arm: https://lab.llvm.org/buildbot/#/builders/141/builds/8556 ``` ******************** Unresolved Tests (1): lldb-api :: tools/lldb-dap/memory/TestDAP_memory.py ******************** Failed Tests (1): lldb-api :: tools/lldb-dap/variables/TestDAP_variables.py ``` --- Full diff: https://github.com/llvm/llvm-project/pull/139251.diff 2 Files Affected: - (modified) lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py (+4) - (modified) lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py (+4) ``````````diff diff --git a/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py b/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py index ea43fccf016a7..74062f3ab2164 100644 --- a/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py +++ b/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py @@ -12,6 +12,7 @@ class TestDAP_memory(lldbdap_testcase.DAPTestCaseBase): + @skipIfWindows def test_memory_refs_variables(self): """ Tests memory references for evaluate @@ -33,6 +34,7 @@ def test_memory_refs_variables(self): # Non-pointers should also have memory-references self.assertIn("memoryReference", locals["not_a_ptr"].keys()) + @skipIfWindows def test_memory_refs_evaluate(self): """ Tests memory references for evaluate @@ -52,6 +54,7 @@ def test_memory_refs_evaluate(self): self.dap_server.request_evaluate("rawptr")["body"].keys(), ) + @skipIfWindows def test_memory_refs_set_variable(self): """ Tests memory references for `setVariable` @@ -74,6 +77,7 @@ def test_memory_refs_set_variable(self): ].keys(), ) + @skipIfWindows def test_readMemory(self): """ Tests the 'readMemory' request diff --git a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py index 3b45cdc245838..296e4911f4052 100644 --- a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py +++ b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py @@ -411,16 +411,19 @@ def do_test_scopes_variables_setVariable_evaluate( self.verify_variables(verify_locals, locals) + @skipIfWindows def test_scopes_variables_setVariable_evaluate(self): self.do_test_scopes_variables_setVariable_evaluate( enableAutoVariableSummaries=False ) + @skipIfWindows def test_scopes_variables_setVariable_evaluate_with_descriptive_summaries(self): self.do_test_scopes_variables_setVariable_evaluate( enableAutoVariableSummaries=True ) + @skipIfWindows def do_test_scopes_and_evaluate_expansion(self, enableAutoVariableSummaries: bool): """ Tests the evaluated expression expands successfully after "scopes" packets @@ -673,6 +676,7 @@ def do_test_indexedVariables(self, enableSyntheticChildDebugging: bool): ]["variables"] self.verify_variables(verify_children, children) + @skipIfWindows def test_return_variables(self): """ Test the stepping out of a function with return value show the variable correctly. ``````````
https://github.com/llvm/llvm-project/pull/139251 From lldb-commits at lists.llvm.org Fri May 9 05:21:31 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Fri, 09 May 2025 05:21:31 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][lldb-dap] Disable more tests on Windows (PR #139251) In-Reply-To: Message-ID: <681df34b.050a0220.4ea4a.e805@mx.google.com> DavidSpickett wrote: @JDevlieghere FYI. I realise this is not the most helpful or informative action to take, but Linaro folks will be busy with other things next week so I want this bot green again soon. If you need us to investigate locally I can do that but it'll be the week of the 19th before I will have time to do so. https://github.com/llvm/llvm-project/pull/139251 From lldb-commits at lists.llvm.org Fri May 9 05:23:44 2025 From: lldb-commits at lists.llvm.org (Jacques Pienaar via lldb-commits) Date: Fri, 09 May 2025 05:23:44 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][plugin] Clear in same thread as set (PR #139252) Message-ID: https://github.com/jpienaar created https://github.com/llvm/llvm-project/pull/139252 Here we were initializing & locking a shared_mutex in a thread, while releasing it in the parent which may/often turned out to be a different thread (shared_mutex::unlock_shared is undefined behavior if called from a thread that doesn't hold the lock). I'm not quite sure what the expectation is here as the variable is never used, so instead I've just reset in same thread as which it was set to ensure its freed in thread holding lock. >From c5ffbd84f8b68bae2112e8cec68803cefe571a72 Mon Sep 17 00:00:00 2001 From: Jacques Pienaar Date: Fri, 9 May 2025 05:23:00 -0700 Subject: [PATCH] [lldb][plugin] Clear in same thread as set Here we were initializing & locking a mutex in a thread, while releasing it in the parent which may/often turned out to be a different thread (shared_mutex::unlock_shared is undefined behavior if called from a thread that doesn't hold the lock). I'm not quite sure what the expectation is here as the variable is never used, so instead I've just reset in same thread as which it was set to ensure its freed in thread holding lock. --- lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp index 523820874752a..0f0226ea9650c 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp @@ -121,6 +121,7 @@ void ManualDWARFIndex::Index() { units_to_index.size()); for_each_unit([&clear_cu_dies](size_t, size_t idx, DWARFUnit *unit) { clear_cu_dies[idx] = unit->ExtractDIEsScoped(); + ckear_cu_duex[idx].reset(); }); // Now index all DWARF unit in parallel. From lldb-commits at lists.llvm.org Fri May 9 05:24:16 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 05:24:16 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][plugin] Clear in same thread as set (PR #139252) In-Reply-To: Message-ID: <681df3f0.a70a0220.ddcdc.f713@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Jacques Pienaar (jpienaar)
Changes Here we were initializing & locking a shared_mutex in a thread, while releasing it in the parent which may/often turned out to be a different thread (shared_mutex::unlock_shared is undefined behavior if called from a thread that doesn't hold the lock). I'm not quite sure what the expectation is here as the variable is never used, so instead I've just reset in same thread as which it was set to ensure its freed in thread holding lock. --- Full diff: https://github.com/llvm/llvm-project/pull/139252.diff 1 Files Affected: - (modified) lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp (+1) ``````````diff diff --git a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp index 523820874752a..0f0226ea9650c 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp @@ -121,6 +121,7 @@ void ManualDWARFIndex::Index() { units_to_index.size()); for_each_unit([&clear_cu_dies](size_t, size_t idx, DWARFUnit *unit) { clear_cu_dies[idx] = unit->ExtractDIEsScoped(); + ckear_cu_duex[idx].reset(); }); // Now index all DWARF unit in parallel. ``````````
https://github.com/llvm/llvm-project/pull/139252 From lldb-commits at lists.llvm.org Fri May 9 06:24:31 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 06:24:31 -0700 (PDT) Subject: [Lldb-commits] =?utf-8?q?=5Blldb=5D_=5Blldb=5D_print_a_notice_whe?= =?utf-8?q?n_=60source_list=60_paging_reaches_the_end_of_th=E2=80=A6_=28PR?= =?utf-8?b?ICMxMzc1MTUp?= In-Reply-To: Message-ID: <681e020f.630a0220.14b09f.87c6@mx.google.com> hapeeeeee wrote: > I've [skipped](https://github.com/llvm/llvm-project/commit/fd8b84ea0fa1eb1da105257f419d926278dc0445) the new tests on Windows because we don't have the headers the sigchld example uses.我跳过了 Windows 上的新测试,因为我们没有 sigchld 示例使用的头文件。 > > (no notification of the failure was sent because we were already failing some lldb-dap tests)(没有发送失败通知,因为我们已经有一些 lldb-dap 测试失败了) The new tests are only applied to the command line, and they should be cross-platform. Maybe I should choose a more general source code for testing? https://github.com/llvm/llvm-project/pull/137515 From lldb-commits at lists.llvm.org Fri May 9 06:43:54 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Fri, 09 May 2025 06:43:54 -0700 (PDT) Subject: [Lldb-commits] =?utf-8?q?=5Blldb=5D_=5Blldb=5D_print_a_notice_whe?= =?utf-8?q?n_=60source_list=60_paging_reaches_the_end_of_th=E2=80=A6_=28PR?= =?utf-8?b?ICMxMzc1MTUp?= In-Reply-To: Message-ID: <681e069a.a70a0220.3995f1.f29b@mx.google.com> DavidSpickett wrote: That would be great if you can, there isn't one in that folder right now but you can find another elsewhere, or add a new file. I didn't want to go changing all the check lines myself in case I broke something. https://github.com/llvm/llvm-project/pull/137515 From lldb-commits at lists.llvm.org Fri May 9 06:46:31 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 06:46:31 -0700 (PDT) Subject: [Lldb-commits] =?utf-8?q?=5Blldb=5D_=5Blldb=5D_print_a_notice_whe?= =?utf-8?q?n_=60source_list=60_paging_reaches_the_end_of_th=E2=80=A6_=28PR?= =?utf-8?b?ICMxMzc1MTUp?= In-Reply-To: Message-ID: <681e0737.170a0220.17bb0c.da28@mx.google.com> hapeeeeee wrote: > That would be great if you can, there isn't one in that folder right now but you can find another elsewhere, or add a new file. I didn't want to go changing all the check lines myself in case I broke something.如果你能做到那就太好了,那个文件夹里现在没有,但你可以在别处找到另一个,或者添加一个新文件。我不想自己去改所有的检查行,以免破坏了什么。 If I modified the test file, should I create a new PR to fix it? https://github.com/llvm/llvm-project/pull/137515 From lldb-commits at lists.llvm.org Fri May 9 06:49:42 2025 From: lldb-commits at lists.llvm.org (David Spickett via lldb-commits) Date: Fri, 09 May 2025 06:49:42 -0700 (PDT) Subject: [Lldb-commits] =?utf-8?q?=5Blldb=5D_=5Blldb=5D_print_a_notice_whe?= =?utf-8?q?n_=60source_list=60_paging_reaches_the_end_of_th=E2=80=A6_=28PR?= =?utf-8?b?ICMxMzc1MTUp?= In-Reply-To: Message-ID: <681e07f6.170a0220.371cf1.fb15@mx.google.com> DavidSpickett wrote: Yes please. https://github.com/llvm/llvm-project/pull/137515 From lldb-commits at lists.llvm.org Fri May 9 06:53:15 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 06:53:15 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fix crash after second run when set a previous watchpoint. (PR #136682) In-Reply-To: Message-ID: <681e08cb.050a0220.23be6a.faa3@mx.google.com> https://github.com/hapeeeeee closed https://github.com/llvm/llvm-project/pull/136682 From lldb-commits at lists.llvm.org Fri May 9 07:59:38 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Fri, 09 May 2025 07:59:38 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fix FindProcessImpl() for iOS simulators (PR #139174) In-Reply-To: Message-ID: <681e185a.050a0220.3b93f9.4495@mx.google.com> JDevlieghere wrote: Could we test this in `TestSimulatorPlatform.py`? https://github.com/llvm/llvm-project/pull/139174 From lldb-commits at lists.llvm.org Fri May 9 08:05:11 2025 From: lldb-commits at lists.llvm.org (Vy Nguyen via lldb-commits) Date: Fri, 09 May 2025 08:05:11 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb]Make `list` command work with headers when possible. (PR #139002) In-Reply-To: Message-ID: <681e19a7.050a0220.cb9fb.530d@mx.google.com> ================ @@ -0,0 +1,57 @@ +## Test that `list header.h:` works correctly when header is available. +## +# RUN: split-file %s %t + +# RUN: %clang_host -g %t/main_with_inlined.cc %t/foo.cc -o %t/main_with_inlined.out +# RUN: %clang_host -g %t/main_no_inlined.cc %t/foo.cc -o %t/main_no_inlined.out + +# RUN: %lldb %t/main_with_inlined.out -o "list foo.h:2" -o "exit" 2>&1 \ +# RUN: | FileCheck %s --check-prefix=CHECK-INLINED + +## Would be nice if this listed the header too - but probably not something +## we want to support right now. +# RUN: echo quit | %lldb %t/main_no_inlined.out -o "list foo.h:2" -o "exit" 2>&1 \ +# RUN: | FileCheck %s --check-prefix=CHECK-NO-INLINED + +# CHECK-INLINED: 2 extern int* ptr; +# CHECK-INLINED: 3 void f(int x); +# CHECK-INLINED: 4 +# CHECK-INLINED: 5 inline void g(int x) { +# CHECK-INLINED: 6 *ptr = x; // should raise a SIGILL ---------------- oontvoo wrote: the function shoud have something in its body (executable) so better demonstrate the test case. sure - can update the comment https://github.com/llvm/llvm-project/pull/139002 From lldb-commits at lists.llvm.org Fri May 9 08:06:27 2025 From: lldb-commits at lists.llvm.org (Vy Nguyen via lldb-commits) Date: Fri, 09 May 2025 08:06:27 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb]Make `list` command work with headers when possible. (PR #139002) In-Reply-To: Message-ID: <681e19f3.170a0220.8440c.fa71@mx.google.com> https://github.com/oontvoo updated https://github.com/llvm/llvm-project/pull/139002 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Fri May 9 08:09:23 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 08:09:23 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][test] Fix beginning/end of file test failed on Windows (PR #139278) Message-ID: https://github.com/hapeeeeee created https://github.com/llvm/llvm-project/pull/139278 As @DavidSpickett mentioned, this change fixed the test failure introduced in [#137515](https://github.com/llvm/llvm-project/pull/137515#issuecomment-2866632780), which was caused by the use of platform-specific headers not available on Windows. This PR does not modify any functional code; it only updates the test cases. Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Fri May 9 08:09:58 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 08:09:58 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][test] Fix beginning/end of file test failed on Windows (PR #139278) In-Reply-To: Message-ID: <681e1ac6.170a0220.b1539.6e56@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Zax (hapeeeeee)
Changes As @DavidSpickett mentioned, this change fixed the test failure introduced in [#137515](https://github.com/llvm/llvm-project/pull/137515#issuecomment-2866632780), which was caused by the use of platform-specific headers not available on Windows. This PR does not modify any functional code; it only updates the test cases. --- Full diff: https://github.com/llvm/llvm-project/pull/139278.diff 3 Files Affected: - (added) lldb/test/Shell/Commands/Inputs/cross_platform.c (+31) - (modified) lldb/test/Shell/Commands/command-list-reach-beginning-of-file.test (+5-7) - (modified) lldb/test/Shell/Commands/command-list-reach-end-of-file.test (+2-7) ``````````diff diff --git a/lldb/test/Shell/Commands/Inputs/cross_platform.c b/lldb/test/Shell/Commands/Inputs/cross_platform.c new file mode 100644 index 0000000000000..bcc484eb36ac5 --- /dev/null +++ b/lldb/test/Shell/Commands/Inputs/cross_platform.c @@ -0,0 +1,31 @@ +#include +void bubbleSort(int arr[], int n) { + for (int i = 0; i < n - 1; i++) { + int swapped = 0; + for (int j = 0; j < n - i - 1; j++) { + if (arr[j] > arr[j + 1]) { + int temp = arr[j]; + arr[j] = arr[j + 1]; + arr[j + 1] = temp; + swapped = 1; + } + } + if (!swapped) + break; + } +} + +int main() { + int arr[] = {64, 34, 25, 12, 22, 11, 90}; + int n = sizeof(arr) / sizeof(arr[0]); + + for (int i = 0; i < n; i++) + printf("%d ", arr[i]); + printf("\n"); + + bubbleSort(arr, n); + for (int i = 0; i < n; i++) + printf("%d ", arr[i]); + printf("\n"); + return 0; +} diff --git a/lldb/test/Shell/Commands/command-list-reach-beginning-of-file.test b/lldb/test/Shell/Commands/command-list-reach-beginning-of-file.test index fa4a93e5904aa..29c33f6fe105e 100644 --- a/lldb/test/Shell/Commands/command-list-reach-beginning-of-file.test +++ b/lldb/test/Shell/Commands/command-list-reach-beginning-of-file.test @@ -1,6 +1,4 @@ -# Source uses unistd.h. -# UNSUPPORTED: system-windows -# RUN: %clang_host -g -O0 %S/Inputs/sigchld.c -o %t.out +# RUN: %clang_host -g -O0 %S/Inputs/cross_platform.c -o %t.out # RUN: %lldb %t.out -b -s %s 2>&1 | FileCheck %s list @@ -13,13 +11,13 @@ r # CHECK: int main() list -# CHECK: if (child_pid == 0) +# CHECK: bubbleSort(arr, n); list - # CHECK: int main() -list -10 -# CHECK: #include +list -20 +# CHECK: #include list - # CHECK: note: Reached beginning of the file, no more to page @@ -28,4 +26,4 @@ list - # CHECK: note: Reached beginning of the file, no more to page list -# CHECK: int main() +# CHECK: bubbleSort(arr, n); diff --git a/lldb/test/Shell/Commands/command-list-reach-end-of-file.test b/lldb/test/Shell/Commands/command-list-reach-end-of-file.test index edf4c521a9e76..d6909b85a390b 100644 --- a/lldb/test/Shell/Commands/command-list-reach-end-of-file.test +++ b/lldb/test/Shell/Commands/command-list-reach-end-of-file.test @@ -1,6 +1,4 @@ -# Source uses unistd.h. -# UNSUPPORTED: system-windows -# RUN: %clang_host -g -O0 %S/Inputs/sigchld.c -o %t.out +# RUN: %clang_host -g -O0 %S/Inputs/cross_platform.c -o %t.out # RUN: %lldb %t.out -b -s %s 2>&1 | FileCheck %s list @@ -13,10 +11,7 @@ r # CHECK: int main() list -# CHECK: if (child_pid == 0) - -list -# CHECK: printf("signo = %d\n", SIGCHLD); +# CHECK: bubbleSort(arr, n); list # CHECK: return 0; ``````````
https://github.com/llvm/llvm-project/pull/139278 From lldb-commits at lists.llvm.org Fri May 9 08:15:33 2025 From: lldb-commits at lists.llvm.org (Jacques Pienaar via lldb-commits) Date: Fri, 09 May 2025 08:15:33 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][plugin] Clear in same thread as set (PR #139252) In-Reply-To: Message-ID: <681e1c15.170a0220.e1ca8.238f@mx.google.com> https://github.com/jpienaar updated https://github.com/llvm/llvm-project/pull/139252 >From c5ffbd84f8b68bae2112e8cec68803cefe571a72 Mon Sep 17 00:00:00 2001 From: Jacques Pienaar Date: Fri, 9 May 2025 05:23:00 -0700 Subject: [PATCH 1/2] [lldb][plugin] Clear in same thread as set Here we were initializing & locking a mutex in a thread, while releasing it in the parent which may/often turned out to be a different thread (shared_mutex::unlock_shared is undefined behavior if called from a thread that doesn't hold the lock). I'm not quite sure what the expectation is here as the variable is never used, so instead I've just reset in same thread as which it was set to ensure its freed in thread holding lock. --- lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp index 523820874752a..0f0226ea9650c 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp @@ -121,6 +121,7 @@ void ManualDWARFIndex::Index() { units_to_index.size()); for_each_unit([&clear_cu_dies](size_t, size_t idx, DWARFUnit *unit) { clear_cu_dies[idx] = unit->ExtractDIEsScoped(); + ckear_cu_duex[idx].reset(); }); // Now index all DWARF unit in parallel. >From 5f5b8dc0deae4f63ddb83e0dfab96ab3a9e0cc80 Mon Sep 17 00:00:00 2001 From: Jacques Pienaar Date: Fri, 9 May 2025 08:15:26 -0700 Subject: [PATCH 2/2] Fix typo --- lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp index 0f0226ea9650c..6139d005b4f2e 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp @@ -121,7 +121,7 @@ void ManualDWARFIndex::Index() { units_to_index.size()); for_each_unit([&clear_cu_dies](size_t, size_t idx, DWARFUnit *unit) { clear_cu_dies[idx] = unit->ExtractDIEsScoped(); - ckear_cu_duex[idx].reset(); + ckear_cu_dies[idx].reset(); }); // Now index all DWARF unit in parallel. From lldb-commits at lists.llvm.org Fri May 9 09:29:11 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 09:29:11 -0700 (PDT) Subject: [Lldb-commits] [lldb] 9818120 - [lldb-dap] Migrating breakpointLocations request to use typed RequestHandler (#137426) Message-ID: <681e2d57.050a0220.1cbea8.eccf@mx.google.com> Author: Ely Ronnen Date: 2025-05-09T18:29:06+02:00 New Revision: 98181200db2af6e0aa43318d11c5c37e65c72845 URL: https://github.com/llvm/llvm-project/commit/98181200db2af6e0aa43318d11c5c37e65c72845 DIFF: https://github.com/llvm/llvm-project/commit/98181200db2af6e0aa43318d11c5c37e65c72845.diff LOG: [lldb-dap] Migrating breakpointLocations request to use typed RequestHandler (#137426) * Migrating breakpointLocations request to use typed RequestHandle * Preliminary step in order to implement assembly source breakpoints Added: Modified: lldb/tools/lldb-dap/Handler/BreakpointLocationsHandler.cpp lldb/tools/lldb-dap/Handler/RequestHandler.h lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp lldb/tools/lldb-dap/Protocol/ProtocolRequests.h lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp lldb/tools/lldb-dap/Protocol/ProtocolTypes.h llvm/include/llvm/Support/JSON.h Removed: ################################################################################ diff --git a/lldb/tools/lldb-dap/Handler/BreakpointLocationsHandler.cpp b/lldb/tools/lldb-dap/Handler/BreakpointLocationsHandler.cpp index 7a477f3e97875..2ac886c3a5d2c 100644 --- a/lldb/tools/lldb-dap/Handler/BreakpointLocationsHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/BreakpointLocationsHandler.cpp @@ -9,136 +9,22 @@ #include "DAP.h" #include "JSONUtils.h" #include "RequestHandler.h" +#include namespace lldb_dap { -// "BreakpointLocationsRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "The `breakpointLocations` request returns all possible -// locations for source breakpoints in a given range.\nClients should only -// call this request if the corresponding capability -// `supportsBreakpointLocationsRequest` is true.", -// "properties": { -// "command": { -// "type": "string", -// "enum": [ "breakpointLocations" ] -// }, -// "arguments": { -// "$ref": "#/definitions/BreakpointLocationsArguments" -// } -// }, -// "required": [ "command" ] -// }] -// }, -// "BreakpointLocationsArguments": { -// "type": "object", -// "description": "Arguments for `breakpointLocations` request.", -// "properties": { -// "source": { -// "$ref": "#/definitions/Source", -// "description": "The source location of the breakpoints; either -// `source.path` or `source.sourceReference` must be specified." -// }, -// "line": { -// "type": "integer", -// "description": "Start line of range to search possible breakpoint -// locations in. If only the line is specified, the request returns all -// possible locations in that line." -// }, -// "column": { -// "type": "integer", -// "description": "Start position within `line` to search possible -// breakpoint locations in. It is measured in UTF-16 code units and the -// client capability `columnsStartAt1` determines whether it is 0- or -// 1-based. If no column is given, the first position in the start line is -// assumed." -// }, -// "endLine": { -// "type": "integer", -// "description": "End line of range to search possible breakpoint -// locations in. If no end line is given, then the end line is assumed to -// be the start line." -// }, -// "endColumn": { -// "type": "integer", -// "description": "End position within `endLine` to search possible -// breakpoint locations in. It is measured in UTF-16 code units and the -// client capability `columnsStartAt1` determines whether it is 0- or -// 1-based. If no end column is given, the last position in the end line -// is assumed." -// } -// }, -// "required": [ "source", "line" ] -// }, -// "BreakpointLocationsResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to `breakpointLocations` request.\nContains -// possible locations for source breakpoints.", -// "properties": { -// "body": { -// "type": "object", -// "properties": { -// "breakpoints": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/BreakpointLocation" -// }, -// "description": "Sorted set of possible breakpoint locations." -// } -// }, -// "required": [ "breakpoints" ] -// } -// }, -// "required": [ "body" ] -// }] -// }, -// "BreakpointLocation": { -// "type": "object", -// "description": "Properties of a breakpoint location returned from the -// `breakpointLocations` request.", -// "properties": { -// "line": { -// "type": "integer", -// "description": "Start line of breakpoint location." -// }, -// "column": { -// "type": "integer", -// "description": "The start position of a breakpoint location. Position -// is measured in UTF-16 code units and the client capability -// `columnsStartAt1` determines whether it is 0- or 1-based." -// }, -// "endLine": { -// "type": "integer", -// "description": "The end line of breakpoint location if the location -// covers a range." -// }, -// "endColumn": { -// "type": "integer", -// "description": "The end position of a breakpoint location (if the -// location covers a range). Position is measured in UTF-16 code units and -// the client capability `columnsStartAt1` determines whether it is 0- or -// 1-based." -// } -// }, -// "required": [ "line" ] -// }, -void BreakpointLocationsRequestHandler::operator()( - const llvm::json::Object &request) const { - llvm::json::Object response; - FillResponse(request, response); - auto *arguments = request.getObject("arguments"); - auto *source = arguments->getObject("source"); - std::string path = GetString(source, "path").value_or("").str(); - const auto start_line = GetInteger(arguments, "line") - .value_or(LLDB_INVALID_LINE_NUMBER); - const auto start_column = GetInteger(arguments, "column") - .value_or(LLDB_INVALID_COLUMN_NUMBER); - const auto end_line = - GetInteger(arguments, "endLine").value_or(start_line); - const auto end_column = GetInteger(arguments, "endColumn") - .value_or(std::numeric_limits::max()); +/// The `breakpointLocations` request returns all possible locations for source +/// breakpoints in a given range. Clients should only call this request if the +/// corresponding capability `supportsBreakpointLocationsRequest` is true. +llvm::Expected +BreakpointLocationsRequestHandler::Run( + const protocol::BreakpointLocationsArguments &args) const { + std::string path = args.source.path.value_or(""); + uint32_t start_line = args.line; + uint32_t start_column = args.column.value_or(LLDB_INVALID_COLUMN_NUMBER); + uint32_t end_line = args.endLine.value_or(start_line); + uint32_t end_column = + args.endColumn.value_or(std::numeric_limits::max()); lldb::SBFileSpec file_spec(path.c_str(), true); lldb::SBSymbolContextList compile_units = @@ -191,18 +77,16 @@ void BreakpointLocationsRequestHandler::operator()( std::sort(locations.begin(), locations.end()); locations.erase(llvm::unique(locations), locations.end()); - llvm::json::Array locations_json; + std::vector breakpoint_locations; for (auto &l : locations) { - llvm::json::Object location; - location.try_emplace("line", l.first); - location.try_emplace("column", l.second); - locations_json.emplace_back(std::move(location)); + protocol::BreakpointLocation lc; + lc.line = l.first; + lc.column = l.second; + breakpoint_locations.push_back(std::move(lc)); } - llvm::json::Object body; - body.try_emplace("breakpoints", std::move(locations_json)); - response.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(response))); + return protocol::BreakpointLocationsResponseBody{ + /*breakpoints=*/std::move(breakpoint_locations)}; } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index 948a29a005aa7..db7f05cb1f113 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.h +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h @@ -206,14 +206,18 @@ class AttachRequestHandler void PostRun() const override; }; -class BreakpointLocationsRequestHandler : public LegacyRequestHandler { +class BreakpointLocationsRequestHandler + : public RequestHandler< + protocol::BreakpointLocationsArguments, + llvm::Expected> { public: - using LegacyRequestHandler::LegacyRequestHandler; + using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "breakpointLocations"; } FeatureSet GetSupportedFeatures() const override { return {protocol::eAdapterFeatureBreakpointLocationsRequest}; } - void operator()(const llvm::json::Object &request) const override; + llvm::Expected + Run(const protocol::BreakpointLocationsArguments &args) const override; }; class CompletionsRequestHandler : public LegacyRequestHandler { diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp index cd507692f0c21..4204ae7785e63 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp @@ -248,6 +248,23 @@ bool fromJSON(const json::Value &Params, Configuration &C, json::Path P) { parseTimeout(Params, C.timeout, P); } +bool fromJSON(const json::Value &Params, BreakpointLocationsArguments &BLA, + json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("source", BLA.source) && O.map("line", BLA.line) && + O.mapOptional("column", BLA.column) && + O.mapOptional("endLine", BLA.endLine) && + O.mapOptional("endColumn", BLA.endColumn); +} + +llvm::json::Value toJSON(const BreakpointLocationsResponseBody &BLRB) { + llvm::json::Array breakpoints_json; + for (const auto &breakpoint : BLRB.breakpoints) { + breakpoints_json.push_back(toJSON(breakpoint)); + } + return llvm::json::Object{{"breakpoints", std::move(breakpoints_json)}}; +} + bool fromJSON(const json::Value &Params, LaunchRequestArguments &LRA, json::Path P) { json::ObjectMapper O(Params, P); diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h index acc3358736e70..4f17d6e79080d 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h @@ -31,6 +31,7 @@ #include #include #include +#include namespace lldb_dap::protocol { @@ -521,6 +522,43 @@ bool fromJSON(const llvm::json::Value &, StepOutArguments &, llvm::json::Path); /// body field is required. using StepOutResponse = VoidResponse; +/// Arguments for `breakpointLocations` request. +struct BreakpointLocationsArguments { + /// The source location of the breakpoints; either `source.path` or + /// `source.sourceReference` must be specified. + Source source; + + /// Start line of range to search possible breakpoint locations in. If only + /// the line is specified, the request returns all possible locations in that + /// line. + uint32_t line; + + /// Start position within `line` to search possible breakpoint locations in. + /// It is measured in UTF-16 code units and the client capability + /// `columnsStartAt1` determines whether it is 0- or 1-based. If no column is + /// given, the first position in the start line is assumed. + std::optional column; + + /// End line of range to search possible breakpoint locations in. If no end + /// line is given, then the end line is assumed to be the start line. + std::optional endLine; + + /// End position within `endLine` to search possible breakpoint locations in. + /// It is measured in UTF-16 code units and the client capability + /// `columnsStartAt1` determines whether it is 0- or 1-based. If no end column + /// is given, the last position in the end line is assumed. + std::optional endColumn; +}; +bool fromJSON(const llvm::json::Value &, BreakpointLocationsArguments &, + llvm::json::Path); + +/// Response to `breakpointLocations` request. +struct BreakpointLocationsResponseBody { + /// Content of the source reference. + std::vector breakpoints; +}; +llvm::json::Value toJSON(const BreakpointLocationsResponseBody &); + } // namespace lldb_dap::protocol #endif diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp index 8f4defb6fd2ea..967c1d2321502 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp @@ -260,4 +260,18 @@ bool fromJSON(const llvm::json::Value &Params, ValueFormat &VF, return O && O.mapOptional("hex", VF.hex); } +json::Value toJSON(const BreakpointLocation &B) { + json::Object result; + + result.insert({"line", B.line}); + if (B.column) + result.insert({"column", *B.column}); + if (B.endLine) + result.insert({"endLine", *B.endLine}); + if (B.endColumn) + result.insert({"endColumn", *B.endColumn}); + + return result; +} + } // namespace lldb_dap::protocol diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h index ae1ba90d1666a..8ea483d810e02 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h @@ -329,6 +329,27 @@ struct ValueFormat { }; bool fromJSON(const llvm::json::Value &, ValueFormat &, llvm::json::Path); +/// Properties of a breakpoint location returned from the `breakpointLocations` +/// request. +struct BreakpointLocation { + /// Start line of breakpoint location. + uint32_t line; + + /// The start position of a breakpoint location. Position is measured in + /// UTF-16 code units and the client capability `columnsStartAt1` determines + /// whether it is 0- or 1-based. + std::optional column; + + /// The end line of breakpoint location if the location covers a range. + std::optional endLine; + + /// The end position of a breakpoint location (if the location covers a + /// range). Position is measured in UTF-16 code units and the client + /// capability `columnsStartAt1` determines whether it is 0- or 1-based. + std::optional endColumn; +}; +llvm::json::Value toJSON(const BreakpointLocation &); + } // namespace lldb_dap::protocol #endif diff --git a/llvm/include/llvm/Support/JSON.h b/llvm/include/llvm/Support/JSON.h index 7f7f5f6228763..f1f4f4db709dd 100644 --- a/llvm/include/llvm/Support/JSON.h +++ b/llvm/include/llvm/Support/JSON.h @@ -776,6 +776,14 @@ inline bool fromJSON(const Value &E, bool &Out, Path P) { P.report("expected boolean"); return false; } +inline bool fromJSON(const Value &E, unsigned int &Out, Path P) { + if (auto S = E.getAsInteger()) { + Out = *S; + return true; + } + P.report("expected integer"); + return false; +} inline bool fromJSON(const Value &E, uint64_t &Out, Path P) { if (auto S = E.getAsUINT64()) { Out = *S; From lldb-commits at lists.llvm.org Fri May 9 09:29:12 2025 From: lldb-commits at lists.llvm.org (Ely Ronnen via lldb-commits) Date: Fri, 09 May 2025 09:29:12 -0700 (PDT) Subject: [Lldb-commits] [lldb] [llvm] [lldb-dap] Migrating breakpointLocations request to use typed RequestHandler (PR #137426) In-Reply-To: Message-ID: <681e2d58.170a0220.1159b6.51a2@mx.google.com> https://github.com/eronnen closed https://github.com/llvm/llvm-project/pull/137426 From lldb-commits at lists.llvm.org Fri May 9 10:31:04 2025 From: lldb-commits at lists.llvm.org (Ilia Kuklin via lldb-commits) Date: Fri, 09 May 2025 10:31:04 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][TypeSystemClang] Allow arrays to be dereferenced in C/C++. (PR #135843) In-Reply-To: Message-ID: <681e3bd8.050a0220.13ccdb.9ddd@mx.google.com> ================ @@ -2794,47 +2794,31 @@ ValueObjectSP ValueObject::Dereference(Status &error) { if (m_deref_valobj) return m_deref_valobj->GetSP(); - const bool is_pointer_or_reference_type = IsPointerOrReferenceType(); - if (is_pointer_or_reference_type) { - bool omit_empty_base_classes = true; - bool ignore_array_bounds = false; - - std::string child_name_str; - uint32_t child_byte_size = 0; - int32_t child_byte_offset = 0; - uint32_t child_bitfield_bit_size = 0; - uint32_t child_bitfield_bit_offset = 0; - bool child_is_base_class = false; - bool child_is_deref_of_parent = false; - const bool transparent_pointers = false; - CompilerType compiler_type = GetCompilerType(); - uint64_t language_flags = 0; + std::string child_name_str; + uint32_t child_byte_size = 0; ---------------- kuilpd wrote: You're right, I didn't notice this. `GetDereferencedType` arguments should also start with `deref`. https://github.com/llvm/llvm-project/pull/135843 From lldb-commits at lists.llvm.org Fri May 9 10:31:16 2025 From: lldb-commits at lists.llvm.org (Ilia Kuklin via lldb-commits) Date: Fri, 09 May 2025 10:31:16 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][TypeSystemClang] Allow arrays to be dereferenced in C/C++. (PR #135843) In-Reply-To: Message-ID: <681e3be4.170a0220.2cb820.895d@mx.google.com> https://github.com/kuilpd updated https://github.com/llvm/llvm-project/pull/135843 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Fri May 9 10:51:05 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 10:51:05 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Don't create instance of `SymbolFileDWARFDebugMap` for non-Mach-O files (PR #139170) In-Reply-To: Message-ID: <681e4089.050a0220.212b14.b272@mx.google.com> royitaqi wrote: @JDevlieghere I see your point (some learning questions below about the principle). However, I feel `ObjectFile::SupportsDebugMap()` is still closely tied to a specific plugin (not through compilation, but semantics). Imagine each plugin needs to add such a method into the `ObjectFile` class, then the class will be filled with plugin-specific methods like this, which I feel breaks the API scalability the plugin system wanted. Instead, maybe it's better if we frame "DebugMap" as an `ObjectFileCapability` enum. Other capabilities can be added, too, e.g. does the object file imply split dwarf, etc. Then the API will be `ObjectFile::GetSupportedCapabilities()`. WDYT? (Note: I am trying to think about how does this relate to the `CalculateAbilities()` function in those `SymbolFile` subclasses. I feel they are different, so it should be okay.) -- (my learning question about the principle of "Plugins shouldn't depend on each other") -- I guess I'm missing a lot of context here. From my limited and naive view, there *are* natural connections between different kinds of plugins because how they usually work together in existing systems. E.g. macOS usually use Mach-O files and can use either `SymbolFileDWARF` and `SymbolFileDWARFDebugMap`, but never split-DWARF (dwp/dwo). I don't understand the design principle of "Plugins shouldn't depend on each other". It feels like it's trying to ignore the natural connection between these plugins. I'm guessing we have that principle so as to avoid having to model a complex (and dynamic) dependency problem? (E.g. "A and B" and "B and C" should always work together, but "A and C" should never work together.) https://github.com/llvm/llvm-project/pull/139170 From lldb-commits at lists.llvm.org Fri May 9 11:04:47 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 11:04:47 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fix FindProcessImpl() for iOS simulators (PR #139174) In-Reply-To: Message-ID: <681e43bf.050a0220.17cdd.a2ba@mx.google.com> royitaqi wrote: > Could we test this in `TestSimulatorPlatform.py`? @JDevlieghere Thanks for pointer. It seems the tests in that file are all **skipped** because of this bug number: `rdar://76995109`. > UNSUPPORTED: LLDB (/Users/royshi/public_llvm/build/bin/clang-arm64) :: test_ios (TestSimulatorPlatform.TestSimulatorPlatformLaunching) (skipping unconditionally [rdar://76995109]) Did a bit internet search and couldn't find how to find more info about this bug or why these tests are all skipped. Not sure if I should un-skip them. What's your advice on my next steps? https://github.com/llvm/llvm-project/pull/139174 From lldb-commits at lists.llvm.org Fri May 9 11:06:05 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Fri, 09 May 2025 11:06:05 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681e440d.170a0220.206a00.8feb@mx.google.com> ================ @@ -0,0 +1,12 @@ +C_SOURCES := main.c + +interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.s + $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o $(SRCDIR)/interrupt-and-trap-funcs.s ---------------- JDevlieghere wrote: Should this pass a triple? I assume this works locally because you're on an arm64 host? https://github.com/llvm/llvm-project/pull/138805 From lldb-commits at lists.llvm.org Fri May 9 11:06:05 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Fri, 09 May 2025 11:06:05 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681e440d.170a0220.b6a2b.76c4@mx.google.com> https://github.com/JDevlieghere approved this pull request. Thanks for taking the time to com up with this test case. Pavel already signed off on the implementation so I think this is good to merge. https://github.com/llvm/llvm-project/pull/138805 From lldb-commits at lists.llvm.org Fri May 9 11:06:05 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Fri, 09 May 2025 11:06:05 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681e440d.a70a0220.39931a.b2c5@mx.google.com> https://github.com/JDevlieghere edited https://github.com/llvm/llvm-project/pull/138805 From lldb-commits at lists.llvm.org Fri May 9 11:06:26 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 11:06:26 -0700 (PDT) Subject: [Lldb-commits] [lldb] Support stepping through Darwin "branch islands" (PR #139301) Message-ID: https://github.com/jimingham created https://github.com/llvm/llvm-project/pull/139301 When an intra-module jump doesn't fit in the immediate branch slot, the Darwin linker inserts "branch island" symbols, and emits code to jump from branch island to branch island till it makes it to the actual function. The previous submissions failed because in that environment the linker was putting the `foo.island` symbol at the same address as the `padding` symbol we we emitting to make our faked-up large binary. This submission jams a byte after the padding symbol so that the other symbols can't overlap it. Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Fri May 9 11:07:04 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 11:07:04 -0700 (PDT) Subject: [Lldb-commits] [lldb] Support stepping through Darwin "branch islands" (PR #139301) In-Reply-To: Message-ID: <681e4448.170a0220.88a7b.75de@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: None (jimingham)
Changes When an intra-module jump doesn't fit in the immediate branch slot, the Darwin linker inserts "branch island" symbols, and emits code to jump from branch island to branch island till it makes it to the actual function. The previous submissions failed because in that environment the linker was putting the `foo.island` symbol at the same address as the `padding` symbol we we emitting to make our faked-up large binary. This submission jams a byte after the padding symbol so that the other symbols can't overlap it. --- Full diff: https://github.com/llvm/llvm-project/pull/139301.diff 9 Files Affected: - (modified) lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp (+25-7) - (added) lldb/test/API/macosx/branch-islands/Makefile (+16) - (added) lldb/test/API/macosx/branch-islands/TestBranchIslands.py (+35) - (added) lldb/test/API/macosx/branch-islands/foo.c (+6) - (added) lldb/test/API/macosx/branch-islands/main.c (+6) - (added) lldb/test/API/macosx/branch-islands/padding1.s (+5) - (added) lldb/test/API/macosx/branch-islands/padding2.s (+5) - (added) lldb/test/API/macosx/branch-islands/padding3.s (+5) - (added) lldb/test/API/macosx/branch-islands/padding4.s (+5) ``````````diff diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp index e25c4ff55e408..578ab12268ea3 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -26,6 +26,7 @@ #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Utility/DataBuffer.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/LLDBLog.h" @@ -923,15 +924,15 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, if (current_symbol != nullptr) { std::vector
addresses; + ConstString current_name = + current_symbol->GetMangled().GetName(Mangled::ePreferMangled); if (current_symbol->IsTrampoline()) { - ConstString trampoline_name = - current_symbol->GetMangled().GetName(Mangled::ePreferMangled); - if (trampoline_name) { + if (current_name) { const ModuleList &images = target_sp->GetImages(); SymbolContextList code_symbols; - images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeCode, + images.FindSymbolsWithNameAndType(current_name, eSymbolTypeCode, code_symbols); for (const SymbolContext &context : code_symbols) { Address addr = context.GetFunctionOrSymbolAddress(); @@ -945,8 +946,8 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, } SymbolContextList reexported_symbols; - images.FindSymbolsWithNameAndType( - trampoline_name, eSymbolTypeReExported, reexported_symbols); + images.FindSymbolsWithNameAndType(current_name, eSymbolTypeReExported, + reexported_symbols); for (const SymbolContext &context : reexported_symbols) { if (context.symbol) { Symbol *actual_symbol = @@ -968,7 +969,7 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, } SymbolContextList indirect_symbols; - images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeResolver, + images.FindSymbolsWithNameAndType(current_name, eSymbolTypeResolver, indirect_symbols); for (const SymbolContext &context : indirect_symbols) { @@ -1028,6 +1029,23 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, thread_plan_sp = std::make_shared( thread, load_addrs, stop_others); } + // One more case we have to consider is "branch islands". These are regular + // TEXT symbols but their names end in .island plus maybe a .digit suffix. + // They are to allow arm64 code to branch further than the size of the + // address slot allows. We just need to single-instruction step in that + // case. + static const char *g_branch_island_pattern = "\\.island\\.?[0-9]*$"; + static RegularExpression g_branch_island_regex(g_branch_island_pattern); + + bool is_branch_island = g_branch_island_regex.Execute(current_name); + if (!thread_plan_sp && is_branch_island) { + thread_plan_sp = std::make_shared( + thread, + /* step_over= */ false, /* stop_others */ false, eVoteNoOpinion, + eVoteNoOpinion); + LLDB_LOG(log, "Stepping one instruction over branch island: '{0}'.", + current_name); + } } else { LLDB_LOGF(log, "Could not find symbol for step through."); } diff --git a/lldb/test/API/macosx/branch-islands/Makefile b/lldb/test/API/macosx/branch-islands/Makefile new file mode 100644 index 0000000000000..062e947f6d6ee --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/Makefile @@ -0,0 +1,16 @@ +C_SOURCES := main.c foo.c +CFLAGS_EXTRAS := -std=c99 + +include Makefile.rules + +a.out: main.o padding1.o padding2.o padding3.o padding4.o foo.o + ${CC} ${LDFLAGS} foo.o padding1.o padding2.o padding3.o padding4.o main.o -o a.out + +%.o: $(SRCDIR)/%.s + ${CC} -c $< + +#padding1.o: padding1.s +# ${CC} -c $(SRCDIR)/padding1.s + +#padding2.o: padding2.s +# ${CC} -c $(SRCDIR)/padding2.s diff --git a/lldb/test/API/macosx/branch-islands/TestBranchIslands.py b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py new file mode 100644 index 0000000000000..d4885b6ead63f --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/TestBranchIslands.py @@ -0,0 +1,35 @@ +""" +Make sure that we can step in across an arm64 branch island +""" + + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * + + +class TestBranchIslandStepping(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + @skipUnlessAppleSilicon + def test_step_in_branch_island(self): + """Make sure we can step in across a branch island""" + self.build() + self.main_source_file = lldb.SBFileSpec("main.c") + self.do_test() + + def do_test(self): + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "Set a breakpoint here", self.main_source_file + ) + + # Make sure that we did manage to generate a branch island for foo: + syms = target.FindSymbols("foo.island", lldb.eSymbolTypeCode) + self.assertEqual(len(syms), 1, "We did generate an island for foo") + + thread.StepInto() + stop_frame = thread.frames[0] + self.assertIn("foo", stop_frame.name, "Stepped into foo") + var = stop_frame.FindVariable("a_variable_in_foo") + self.assertTrue(var.IsValid(), "Found the variable in foo") diff --git a/lldb/test/API/macosx/branch-islands/foo.c b/lldb/test/API/macosx/branch-islands/foo.c new file mode 100644 index 0000000000000..a5dd2e59e1d82 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/foo.c @@ -0,0 +1,6 @@ +#include + +void foo() { + int a_variable_in_foo = 10; + printf("I am foo: %d.\n", a_variable_in_foo); +} diff --git a/lldb/test/API/macosx/branch-islands/main.c b/lldb/test/API/macosx/branch-islands/main.c new file mode 100644 index 0000000000000..b5578bdd715df --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/main.c @@ -0,0 +1,6 @@ +extern void foo(); + +int main() { + foo(); // Set a breakpoint here + return 0; +} diff --git a/lldb/test/API/macosx/branch-islands/padding1.s b/lldb/test/API/macosx/branch-islands/padding1.s new file mode 100644 index 0000000000000..04abef5455c12 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding1.s @@ -0,0 +1,5 @@ +.text +_padding1: +.p2align 2 +.byte 0x10 +.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding2.s b/lldb/test/API/macosx/branch-islands/padding2.s new file mode 100644 index 0000000000000..dc66686cc779f --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding2.s @@ -0,0 +1,5 @@ +.text +_padding2: +.p2align 2 +.byte 0x10 +.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding3.s b/lldb/test/API/macosx/branch-islands/padding3.s new file mode 100644 index 0000000000000..bf920e2e4f643 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding3.s @@ -0,0 +1,5 @@ +.text +_padding3: +.p2align 2 +.byte 0x10 +.space 120*1024*1024 diff --git a/lldb/test/API/macosx/branch-islands/padding4.s b/lldb/test/API/macosx/branch-islands/padding4.s new file mode 100644 index 0000000000000..1430fd2fd9729 --- /dev/null +++ b/lldb/test/API/macosx/branch-islands/padding4.s @@ -0,0 +1,5 @@ +.text +_padding4: +.p2align 2 +.byte 0x10 +.space 120*1024*1024 ``````````
https://github.com/llvm/llvm-project/pull/139301 From lldb-commits at lists.llvm.org Fri May 9 11:08:26 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Fri, 09 May 2025 11:08:26 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681e449a.170a0220.352c29.9d5b@mx.google.com> ================ @@ -0,0 +1,12 @@ +C_SOURCES := main.c + +interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.s + $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o $(SRCDIR)/interrupt-and-trap-funcs.s ---------------- jasonmolenda wrote: ach, that reminds me I need to skip this test unless arm64. https://github.com/llvm/llvm-project/pull/138805 From lldb-commits at lists.llvm.org Fri May 9 11:14:12 2025 From: lldb-commits at lists.llvm.org (Felipe de Azevedo Piovezan via lldb-commits) Date: Fri, 09 May 2025 11:14:12 -0700 (PDT) Subject: [Lldb-commits] [lldb] Support stepping through Darwin "branch islands" (PR #139301) In-Reply-To: Message-ID: <681e45f4.170a0220.1166a9.794b@mx.google.com> https://github.com/felipepiovezan approved this pull request. LGTM https://github.com/llvm/llvm-project/pull/139301 From lldb-commits at lists.llvm.org Fri May 9 11:25:41 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 11:25:41 -0700 (PDT) Subject: [Lldb-commits] [lldb] [llvm] [NFC] Separate high-level-dependent portions of DWARFExpression (PR #139175) In-Reply-To: Message-ID: <681e48a5.050a0220.f79e7.b436@mx.google.com> https://github.com/Sterling-Augustine updated https://github.com/llvm/llvm-project/pull/139175 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Fri May 9 11:55:08 2025 From: lldb-commits at lists.llvm.org (David Blaikie via lldb-commits) Date: Fri, 09 May 2025 11:55:08 -0700 (PDT) Subject: [Lldb-commits] [lldb] [llvm] [NFC] Separate high-level-dependent portions of DWARFExpression (PR #139175) In-Reply-To: Message-ID: <681e4f8c.170a0220.bf47b.8672@mx.google.com> dwblaikie wrote: @jmorse DWARF expressions might be your wheelhouse/you know someone who'd be up for reviewing this? https://github.com/llvm/llvm-project/pull/139175 From lldb-commits at lists.llvm.org Fri May 9 11:59:20 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Fri, 09 May 2025 11:59:20 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Improving tests logging to understand CI failures. (PR #139311) Message-ID: https://github.com/ashgti created https://github.com/llvm/llvm-project/pull/139311 To improve logging this adjusts two properties of the existing tests: * Forwards stderr from lldb-dap to the process in case errors are reported to stderr. * Adjusts `DebugAdapterServer.terminate` to close stdin and wait for the process to exit instead of sending SIGTERM. Additionally, if we end up with a non-zero exit status we now raise an error to note the unexpected exit status. With these changes, I did find one test case in `TestDAP_console.test_diagnositcs` that was not waiting to ensure the expected event had arrived by the time it performed an assert. >From abb7aad60e314fadb235e370f431112fa078e023 Mon Sep 17 00:00:00 2001 From: John Harrison Date: Fri, 9 May 2025 11:58:35 -0700 Subject: [PATCH] [lldb-dap] Improving tests logging to understand CI failures. To improve logging this adjusts two properties of the existing tests: * Forwards stderr from lldb-dap to the process in case errors are reported to stderr. * Adjusts `DebugAdapterServer.terminate` to close stdin and wait for the process to exit instead of sending SIGTERM. Additionally, if we end up with a non-zero exit status we now raise an error to note the unexpected exit status. With these changes, I did find one test case in `TestDAP_console.test_diagnositcs` that was not waiting to ensure the expected event had arrived by the time it performed an assert. --- .../test/tools/lldb-dap/dap_server.py | 36 +++++++++++++++---- .../tools/lldb-dap/console/TestDAP_console.py | 5 +-- lldb/test/API/tools/lldb-dap/io/TestDAP_io.py | 4 --- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py index e10342b72f4f0..292209f8ab042 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py @@ -8,6 +8,7 @@ import socket import string import subprocess +import signal import sys import threading import time @@ -1269,7 +1270,7 @@ def launch(cls, /, executable, env=None, log_file=None, connection=None): args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + stderr=sys.stderr, env=adapter_env, ) @@ -1302,14 +1303,37 @@ def get_pid(self): def terminate(self): super(DebugAdapterServer, self).terminate() if self.process is not None: - self.process.terminate() + process = self.process + self.process = None try: - self.process.wait(timeout=20) + # When we close stdin it should signal the lldb-dap that no + # new messages will arrive and it should shutdown on its own. + process.stdin.close() + process.wait(timeout=20) except subprocess.TimeoutExpired: - self.process.kill() - self.process.wait() - self.process = None + process.kill() + process.wait() + if process.returncode != 0: + raise DebugAdapterProcessError(process.returncode) + +class DebugAdapterError(Exception): pass + +class DebugAdapterProcessError(DebugAdapterError): + """Raised when the lldb-dap process exits with a non-zero exit status. + """ + + def __init__(self, returncode): + self.returncode = returncode + + def __str__(self): + if self.returncode and self.returncode < 0: + try: + return f"lldb-dap died with {signal.Signals(-self.returncode).name}." + except ValueError: + return f"lldb-dap died with unknown signal {-self.returncode}." + else: + return f"lldb-dap returned non-zero exit status {self.returncode}." def attach_options_specified(options): if options.pid is not None: diff --git a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py index 8642e317f9b3a..f6809c0cdcb60 100644 --- a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py +++ b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py @@ -176,9 +176,10 @@ def test_diagnositcs(self): f"target create --core {core}", context="repl" ) - output = self.get_important() + diag_message = self.collect_important(timeout_secs=self.timeoutval, pattern="minidump file") + self.assertIn( "warning: unable to retrieve process ID from minidump file", - output, + diag_message, "diagnostic found in important output", ) diff --git a/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py b/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py index f05f876e57b49..b72b98de412b4 100644 --- a/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py +++ b/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py @@ -22,13 +22,9 @@ def cleanup(): process.terminate() process.wait() stdout_data = process.stdout.read().decode() - stderr_data = process.stderr.read().decode() print("========= STDOUT =========", file=sys.stderr) print(stdout_data, file=sys.stderr) print("========= END =========", file=sys.stderr) - print("========= STDERR =========", file=sys.stderr) - print(stderr_data, file=sys.stderr) - print("========= END =========", file=sys.stderr) print("========= DEBUG ADAPTER PROTOCOL LOGS =========", file=sys.stderr) with open(log_file_path, "r") as file: print(file.read(), file=sys.stderr) From lldb-commits at lists.llvm.org Fri May 9 11:59:51 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 11:59:51 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Improving tests logging to understand CI failures. (PR #139311) In-Reply-To: Message-ID: <681e50a7.170a0220.1b2f66.8f56@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: John Harrison (ashgti)
Changes To improve logging this adjusts two properties of the existing tests: * Forwards stderr from lldb-dap to the process in case errors are reported to stderr. * Adjusts `DebugAdapterServer.terminate` to close stdin and wait for the process to exit instead of sending SIGTERM. Additionally, if we end up with a non-zero exit status we now raise an error to note the unexpected exit status. With these changes, I did find one test case in `TestDAP_console.test_diagnositcs` that was not waiting to ensure the expected event had arrived by the time it performed an assert. --- Full diff: https://github.com/llvm/llvm-project/pull/139311.diff 3 Files Affected: - (modified) lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py (+30-6) - (modified) lldb/test/API/tools/lldb-dap/console/TestDAP_console.py (+3-2) - (modified) lldb/test/API/tools/lldb-dap/io/TestDAP_io.py (-4) ``````````diff diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py index e10342b72f4f0..292209f8ab042 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py @@ -8,6 +8,7 @@ import socket import string import subprocess +import signal import sys import threading import time @@ -1269,7 +1270,7 @@ def launch(cls, /, executable, env=None, log_file=None, connection=None): args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + stderr=sys.stderr, env=adapter_env, ) @@ -1302,14 +1303,37 @@ def get_pid(self): def terminate(self): super(DebugAdapterServer, self).terminate() if self.process is not None: - self.process.terminate() + process = self.process + self.process = None try: - self.process.wait(timeout=20) + # When we close stdin it should signal the lldb-dap that no + # new messages will arrive and it should shutdown on its own. + process.stdin.close() + process.wait(timeout=20) except subprocess.TimeoutExpired: - self.process.kill() - self.process.wait() - self.process = None + process.kill() + process.wait() + if process.returncode != 0: + raise DebugAdapterProcessError(process.returncode) + +class DebugAdapterError(Exception): pass + +class DebugAdapterProcessError(DebugAdapterError): + """Raised when the lldb-dap process exits with a non-zero exit status. + """ + + def __init__(self, returncode): + self.returncode = returncode + + def __str__(self): + if self.returncode and self.returncode < 0: + try: + return f"lldb-dap died with {signal.Signals(-self.returncode).name}." + except ValueError: + return f"lldb-dap died with unknown signal {-self.returncode}." + else: + return f"lldb-dap returned non-zero exit status {self.returncode}." def attach_options_specified(options): if options.pid is not None: diff --git a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py index 8642e317f9b3a..f6809c0cdcb60 100644 --- a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py +++ b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py @@ -176,9 +176,10 @@ def test_diagnositcs(self): f"target create --core {core}", context="repl" ) - output = self.get_important() + diag_message = self.collect_important(timeout_secs=self.timeoutval, pattern="minidump file") + self.assertIn( "warning: unable to retrieve process ID from minidump file", - output, + diag_message, "diagnostic found in important output", ) diff --git a/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py b/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py index f05f876e57b49..b72b98de412b4 100644 --- a/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py +++ b/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py @@ -22,13 +22,9 @@ def cleanup(): process.terminate() process.wait() stdout_data = process.stdout.read().decode() - stderr_data = process.stderr.read().decode() print("========= STDOUT =========", file=sys.stderr) print(stdout_data, file=sys.stderr) print("========= END =========", file=sys.stderr) - print("========= STDERR =========", file=sys.stderr) - print(stderr_data, file=sys.stderr) - print("========= END =========", file=sys.stderr) print("========= DEBUG ADAPTER PROTOCOL LOGS =========", file=sys.stderr) with open(log_file_path, "r") as file: print(file.read(), file=sys.stderr) ``````````
https://github.com/llvm/llvm-project/pull/139311 From lldb-commits at lists.llvm.org Fri May 9 12:01:42 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 12:01:42 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Improving tests logging to understand CI failures. (PR #139311) In-Reply-To: Message-ID: <681e5116.170a0220.3580a1.8c1b@mx.google.com> github-actions[bot] wrote: :warning: Python code formatter, darker found issues in your code. :warning:
You can test this locally with the following command: ``````````bash darker --check --diff -r HEAD~1...HEAD lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py lldb/test/API/tools/lldb-dap/console/TestDAP_console.py lldb/test/API/tools/lldb-dap/io/TestDAP_io.py ``````````
View the diff from darker here. ``````````diff --- packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py 2025-05-09 18:58:35.000000 +0000 +++ packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py 2025-05-09 19:01:13.215268 +0000 @@ -1304,26 +1304,27 @@ super(DebugAdapterServer, self).terminate() if self.process is not None: process = self.process self.process = None try: - # When we close stdin it should signal the lldb-dap that no + # When we close stdin it should signal the lldb-dap that no # new messages will arrive and it should shutdown on its own. process.stdin.close() process.wait(timeout=20) except subprocess.TimeoutExpired: process.kill() process.wait() if process.returncode != 0: raise DebugAdapterProcessError(process.returncode) -class DebugAdapterError(Exception): pass +class DebugAdapterError(Exception): + pass + class DebugAdapterProcessError(DebugAdapterError): - """Raised when the lldb-dap process exits with a non-zero exit status. - """ + """Raised when the lldb-dap process exits with a non-zero exit status.""" def __init__(self, returncode): self.returncode = returncode def __str__(self): --- test/API/tools/lldb-dap/console/TestDAP_console.py 2025-05-09 18:58:35.000000 +0000 +++ test/API/tools/lldb-dap/console/TestDAP_console.py 2025-05-09 19:01:13.291647 +0000 @@ -174,11 +174,13 @@ self.yaml2obj("minidump.yaml", core) self.dap_server.request_evaluate( f"target create --core {core}", context="repl" ) - diag_message = self.collect_important(timeout_secs=self.timeoutval, pattern="minidump file") + diag_message = self.collect_important( + timeout_secs=self.timeoutval, pattern="minidump file" + ) self.assertIn( "warning: unable to retrieve process ID from minidump file", diag_message, "diagnostic found in important output", ``````````
https://github.com/llvm/llvm-project/pull/139311 From lldb-commits at lists.llvm.org Fri May 9 12:02:51 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Fri, 09 May 2025 12:02:51 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream lldb-rpc-gen tool (PR #138031) In-Reply-To: Message-ID: <681e515b.170a0220.fc9e8.953e@mx.google.com> ================ @@ -0,0 +1,535 @@ +//===-- 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/Mangle.h" +#include "clang/Lex/Lexer.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +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", // What is this used for? + "SBInputReader", // What is this used for? + "SBCommandPluginInterface", // This is hard to support, we can do it if + // really needed though. + "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. +}; + +// We intentionally avoid generating certain methods either because they are +// difficult to support correctly or they aren't really used much from C++. +// FIXME: We should be able to annotate these methods instead of maintaining a +// list in the generator itself. +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", ---------------- chelcassanova wrote: @bulbazord Actually, I think this preprocessor macro has its link to the `[[deprecated]]` attribute removed in SBDefines if we're generating the SWIG bindings: ``` #if defined(SWIG) || _cplusplus < 201402L #undef LLDB_DEPRECATED #undef LLDB_DEPRECATED_FIXME #define LLDB_DEPRECATED(MSG) #define LLDB_DEPRECATED_FIXME(MSG, FIX) #endif ``` and we're always generating the SWIG bindings when building RPC because of testing right? The definition for `LLDB_DEPRECATED` that ties it to the compiler's deprecated attribute is coming from `lldb-defines.h` and is overridden in `SBDefines.h` to just be blank (IIUC?). I think this means that when `lldb-rpc-gen` is reading any function that has `LLDB_DEPRECATED`, there's no actual `[[deprecated]]` attribute for the tool to pick up on. I don't currently know why we don't add the deprecated attribute when generating SWIG bindings. If that's something that cannot change, is there a better way to work around this? If not then keeping the list and adding a note; as well as adding a check for the deprecated attribute anyways in `MethodIsDisallowed` would be the way to go here. https://github.com/llvm/llvm-project/pull/138031 From lldb-commits at lists.llvm.org Fri May 9 13:16:06 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Fri, 09 May 2025 13:16:06 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream lldb-rpc-gen tool (PR #138031) In-Reply-To: Message-ID: <681e6286.050a0220.1e1fc3.c9bc@mx.google.com> https://github.com/chelcassanova updated https://github.com/llvm/llvm-project/pull/138031 >From 59dcdc0da49646e2152b1e2b6158dd1b7137bea5 Mon Sep 17 00:00:00 2001 From: Chelsea Cassanova Date: Wed, 30 Apr 2025 14:15:39 -0700 Subject: [PATCH] [lldb][RPC] Upstream lldb-rpc-gen tool This commit upstreams the `lldb-rpc-gen` tool, a ClangTool that generates the LLDB RPC client and server interfaces. https://discourse.llvm.org/t/rfc-upstreaming-lldb-rpc/85804 --- .../tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp | 545 ++++++++++++++++++ lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.h | 107 ++++ .../lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp | 540 +++++++++++++++++ 3 files changed, 1192 insertions(+) create mode 100644 lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp create mode 100644 lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.h create mode 100644 lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp b/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp new file mode 100644 index 0000000000000..67897d32f586b --- /dev/null +++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp @@ -0,0 +1,545 @@ +//===-- RPCCommon.cpp -----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RPCCommon.h" + +#include "clang/AST/AST.h" +#include "clang/AST/Attr.h" +#include "clang/AST/DeclBase.h" +#include "clang/AST/Mangle.h" +#include "clang/Lex/Lexer.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +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", // What is this used for? + "SBInputReader", // What is this used for? + "SBCommandPluginInterface", // This is hard to support, we can do it if + // really needed though. + "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. +}; + +// We intentionally avoid generating certain methods either because they are +// difficult to support correctly or they aren't really used much from C++. +// NOTE: These methods are marked as deprecated using LLDB_DEPRECATED. +// Normally this macro defines to the deprecated annotation, but this +// functionality is removed in SBDefines.h when generating SWIG bindings which +// we use for testing. Because of this, there is no annotation for the tool to +// pick up on so this list will be used while we have this restriction in +// SBDefines.h. +static constexpr llvm::StringRef DisallowedMethods[] = { + // The threading functionality in SBHostOS is deprecated and thus we do not + // generate them. It would be ideal to add the annotations to the methods + // and then support not generating deprecated methods. However, without + // annotations the generator generates most things correctly. This one is + // problematic because it returns a pointer to an "opaque" structure + // (thread_t) that is not `void *`, so special casing it is more effort than + // it's worth. + "_ZN4lldb8SBHostOS10ThreadJoinEP17_opaque_pthread_tPPvPNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadCancelEP17_opaque_pthread_tPNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadCreateEPKcPFPvS3_ES3_PNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadDetachEP17_opaque_pthread_tPNS_7SBErrorE", + "_ZN4lldb8SBHostOS13ThreadCreatedEPKc", +}; + +static constexpr llvm::StringRef ClassesWithoutDefaultCtor[] = { + "SBHostOS", + "SBReproducer", +}; + +static constexpr llvm::StringRef ClassesWithoutCopyOperations[] = { + "SBHostOS", + "SBReproducer", + "SBStream", + "SBProgress", +}; + +static constexpr llvm::StringRef MethodsWithPointerPlusLen[] = { + "_ZN4lldb6SBData11ReadRawDataERNS_7SBErrorEyPvm", + "_ZN4lldb6SBData7SetDataERNS_7SBErrorEPKvmNS_9ByteOrderEh", + "_ZN4lldb6SBData20SetDataWithOwnershipERNS_7SBErrorEPKvmNS_9ByteOrderEh", + "_ZN4lldb6SBData25CreateDataFromUInt64ArrayENS_9ByteOrderEjPym", + "_ZN4lldb6SBData25CreateDataFromUInt32ArrayENS_9ByteOrderEjPjm", + "_ZN4lldb6SBData25CreateDataFromSInt64ArrayENS_9ByteOrderEjPxm", + "_ZN4lldb6SBData25CreateDataFromSInt32ArrayENS_9ByteOrderEjPim", + "_ZN4lldb6SBData25CreateDataFromDoubleArrayENS_9ByteOrderEjPdm", + "_ZN4lldb6SBData22SetDataFromUInt64ArrayEPym", + "_ZN4lldb6SBData22SetDataFromUInt32ArrayEPjm", + "_ZN4lldb6SBData22SetDataFromSInt64ArrayEPxm", + "_ZN4lldb6SBData22SetDataFromSInt32ArrayEPim", + "_ZN4lldb6SBData22SetDataFromDoubleArrayEPdm", + "_ZN4lldb10SBDebugger22GetDefaultArchitectureEPcm", + "_ZN4lldb10SBDebugger13DispatchInputEPvPKvm", + "_ZN4lldb10SBDebugger13DispatchInputEPKvm", + "_ZN4lldb6SBFile4ReadEPhmPm", + "_ZN4lldb6SBFile5WriteEPKhmPm", + "_ZNK4lldb10SBFileSpec7GetPathEPcm", + "_ZN4lldb10SBFileSpec11ResolvePathEPKcPcm", + "_ZN4lldb8SBModule10GetVersionEPjj", + "_ZN4lldb12SBModuleSpec12SetUUIDBytesEPKhm", + "_ZNK4lldb9SBProcess9GetSTDOUTEPcm", + "_ZNK4lldb9SBProcess9GetSTDERREPcm", + "_ZNK4lldb9SBProcess19GetAsyncProfileDataEPcm", + "_ZN4lldb9SBProcess10ReadMemoryEyPvmRNS_7SBErrorE", + "_ZN4lldb9SBProcess11WriteMemoryEyPKvmRNS_7SBErrorE", + "_ZN4lldb9SBProcess21ReadCStringFromMemoryEyPvmRNS_7SBErrorE", + "_ZNK4lldb16SBStructuredData14GetStringValueEPcm", + "_ZN4lldb8SBTarget23BreakpointCreateByNamesEPPKcjjRKNS_" + "14SBFileSpecListES6_", + "_ZN4lldb8SBTarget10ReadMemoryENS_9SBAddressEPvmRNS_7SBErrorE", + "_ZN4lldb8SBTarget15GetInstructionsENS_9SBAddressEPKvm", + "_ZN4lldb8SBTarget25GetInstructionsWithFlavorENS_9SBAddressEPKcPKvm", + "_ZN4lldb8SBTarget15GetInstructionsEyPKvm", + "_ZN4lldb8SBTarget25GetInstructionsWithFlavorEyPKcPKvm", + "_ZN4lldb8SBThread18GetStopDescriptionEPcm", + // The below mangled names are used for dummy methods in shell tests + // that test the emitters' output. If you're adding any new mangled names + // from the actual SB API to this list please add them above. + "_ZN4lldb33SBRPC_" + "CHECKCONSTCHARPTRPTRWITHLEN27CheckConstCharPtrPtrWithLenEPPKcm", + "_ZN4lldb19SBRPC_CHECKARRAYPTR13CheckArrayPtrEPPKcm", + "_ZN4lldb18SBRPC_CHECKVOIDPTR12CheckVoidPtrEPvm", +}; + +// These methods should take a connection parameter according to our logic in +// RequiresConnectionParameter() but in the handwritten version they +// don't take a connection. These methods need to have their implementation +// changed but for now, we just have an exception list of functions that will +// never be a given connection parameter. +// +// FIXME: Change the implementation of these methods so that they can be given a +// connection parameter. +static constexpr llvm::StringRef + MethodsThatUnconditionallyDoNotNeedConnection[] = { + "_ZN4lldb16SBBreakpointNameC1ERNS_8SBTargetEPKc", + "_ZN4lldb10SBDebugger7DestroyERS0_", + "_ZN4lldb18SBExecutionContextC1ENS_8SBThreadE", +}; + +// These classes inherit from rpc::ObjectRef directly (as opposed to +// rpc::LocalObjectRef). Changing them from ObjectRef to LocalObjectRef is ABI +// breaking, so we preserve that compatibility here. +// +// lldb-rpc-gen emits classes as LocalObjectRefs by default. +// +// FIXME: Does it matter which one it emits by default? +static constexpr llvm::StringRef ClassesThatInheritFromObjectRef[] = { + "SBAddress", + "SBBreakpointName", + "SBCommandInterpreter", + "SBCommandReturnObject", + "SBError", + "SBExecutionContext", + "SBExpressionOptions", + "SBFileSpec", + "SBFileSpecList", + "SBFormat", + "SBFunction", + "SBHistoricalFrame", + "SBHistoricalLineEntry", + "SBHistoricalLineEntryList", + "SBLineEntry", + "SBStream", + "SBStringList", + "SBStructuredData", + "SBSymbolContext", + "SBSymbolContextList", + "SBTypeMember", + "SBTypeSummaryOptions", + "SBValueList", +}; + +static llvm::StringMap> + ClassName_to_ParameterTypes = { + {"SBLaunchInfo", {"const char *"}}, + {"SBPlatformConnectOptions", {"const char *"}}, + {"SBPlatformShellCommand", {"const char *", "const char *"}}, + {"SBBreakpointList", {"SBTarget"}}, +}; + +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) { + QualType UnderlyingType = GetUnderlyingType(T); + return UnderlyingType.getUnqualifiedType(); +} + +std::string lldb_rpc_gen::GetMangledName(ASTContext &Context, + CXXMethodDecl *MDecl) { + std::string Mangled; + llvm::raw_string_ostream MangledStream(Mangled); + + GlobalDecl GDecl; + if (const auto *CtorDecl = dyn_cast(MDecl)) + GDecl = GlobalDecl(CtorDecl, Ctor_Complete); + else if (const auto *DtorDecl = dyn_cast(MDecl)) + GDecl = GlobalDecl(DtorDecl, Dtor_Deleting); + else + GDecl = GlobalDecl(MDecl); + + MangleContext *MC = Context.createMangleContext(); + MC->mangleName(GDecl, MangledStream); + return Mangled; +} + +bool lldb_rpc_gen::TypeIsFromLLDBPrivate(QualType T) { + + auto CheckTypeForLLDBPrivate = [](const Type *Ty) { + if (!Ty) + return false; + const auto *CXXRDecl = Ty->getAsCXXRecordDecl(); + if (!CXXRDecl) + return false; + const auto *NSDecl = + llvm::dyn_cast(CXXRDecl->getDeclContext()); + if (!NSDecl) + return false; + return NSDecl->getName() == "lldb_private"; + }; + + // First, get the underlying type (remove qualifications and strip off any + // pointers/references). Then we'll need to desugar this type. This will + // remove things like typedefs, so instead of seeing "lldb::DebuggerSP" we'll + // actually see something like "std::shared_ptr". + QualType UnqualifiedUnderlyingType = GetUnqualifiedUnderlyingType(T); + const Type *DesugaredType = + UnqualifiedUnderlyingType->getUnqualifiedDesugaredType(); + assert(DesugaredType && "DesugaredType from a valid Type is nullptr!"); + + // Check the type itself. + if (CheckTypeForLLDBPrivate(DesugaredType)) + return true; + + // If that didn't work, it's possible that the type has a template argument + // that is an lldb_private type. + if (const auto *TemplateSDecl = + llvm::dyn_cast_or_null( + DesugaredType->getAsCXXRecordDecl())) { + for (const TemplateArgument &TA : + TemplateSDecl->getTemplateArgs().asArray()) { + if (TA.getKind() != TemplateArgument::Type) + continue; + if (CheckTypeForLLDBPrivate(TA.getAsType().getTypePtr())) + return true; + } + } + return false; +} + +bool lldb_rpc_gen::TypeIsSBClass(QualType T) { + QualType UnqualifiedUnderlyingType = GetUnqualifiedUnderlyingType(T); + const auto *CXXRDecl = UnqualifiedUnderlyingType->getAsCXXRecordDecl(); + if (!CXXRDecl) + return false; // SB Classes are always C++ classes + + return CXXRDecl->getName().starts_with("SB"); +} + +bool lldb_rpc_gen::TypeIsConstCharPtr(QualType T) { + if (!T->isPointerType()) + return false; + + QualType UnderlyingType = T->getPointeeType(); + if (!UnderlyingType.isConstQualified()) + return false; + + // FIXME: We should be able to do `UnderlyingType->isCharType` but that will + // return true for `const uint8_t *` since that is effectively an unsigned + // char pointer. We currently do not support pointers other than `const char + // *` and `const char **`. + return UnderlyingType->isSpecificBuiltinType(BuiltinType::Char_S) || + UnderlyingType->isSpecificBuiltinType(BuiltinType::SChar); +} + +bool lldb_rpc_gen::TypeIsConstCharPtrPtr(QualType T) { + if (!T->isPointerType()) + return false; + + return TypeIsConstCharPtr(T->getPointeeType()); +} + +bool lldb_rpc_gen::TypeIsDisallowedClass(QualType T) { + QualType UUT = GetUnqualifiedUnderlyingType(T); + const auto *CXXRDecl = UUT->getAsCXXRecordDecl(); + if (!CXXRDecl) + return false; + + llvm::StringRef DeclName = CXXRDecl->getName(); + for (const llvm::StringRef DisallowedClass : DisallowedClasses) + if (DeclName == DisallowedClass) + return true; + return false; +} + +bool lldb_rpc_gen::TypeIsCallbackFunctionPointer(QualType T) { + return T->isFunctionPointerType(); +} + +bool lldb_rpc_gen::MethodIsDisallowed(ASTContext &Context, CXXMethodDecl *MDecl) { + std::string MangledName = lldb_rpc_gen::GetMangledName(Context, MDecl); + if (llvm::is_contained(DisallowedMethods, MangledName)) + return true; + + return MDecl->isDeprecatedInAnyTargetPlatform(); +} + +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; + continue; + } + + if ((*Iter)->getType()->isVoidPointerType()) + HasBatonParameter = true; + } + + return HasCallbackParameter && HasBatonParameter; +} + +// FIXME: Find a better way to do this. Here is why it is written this way: +// By the time we have already created a `Method` object, we have extracted the +// `QualifiedName` and the relevant QualTypes for parameters/return types, many +// of which contains "lldb::" in them. To change it in a way that would be +// friendly to liblldbrpc, we would need to have a way of replacing that +// namespace at the time of creating a Method, and only for liblldbrpc methods. +// IMO this would complicate Method more than what I'm doing here, and not +// necessarily for any more benefit. +// In clang-tools-extra, there is a ChangeNamespaces tool which tries to do +// something similar to this. It also operates primarily on string replacement, +// but uses more sophisticated clang tooling to do so. +// For now, this will do what we need it to do. +std::string +lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace(std::string Name) { + auto Pos = Name.find("lldb::"); + while (Pos != std::string::npos) { + constexpr size_t SizeOfLLDBNamespace = 4; + Name.replace(Pos, SizeOfLLDBNamespace, "lldb_rpc"); + Pos = Name.find("lldb::"); + } + return Name; +} + +std::string lldb_rpc_gen::StripLLDBNamespace(std::string Name) { + auto Pos = Name.find("lldb::"); + if (Pos != std::string::npos) { + constexpr size_t SizeOfLLDBNamespace = 6; + Name = Name.substr(Pos + SizeOfLLDBNamespace); + } + return Name; +} + +bool lldb_rpc_gen::SBClassRequiresDefaultCtor(const std::string &ClassName) { + return !llvm::is_contained(ClassesWithoutDefaultCtor, ClassName); +} + +bool lldb_rpc_gen::SBClassRequiresCopyCtorAssign(const std::string &ClassName) { + return !llvm::is_contained(ClassesWithoutCopyOperations, ClassName); +} + +bool lldb_rpc_gen::SBClassInheritsFromObjectRef(const std::string &ClassName) { + return llvm::is_contained(ClassesThatInheritFromObjectRef, ClassName); +} + +std::string lldb_rpc_gen::GetSBClassNameFromType(QualType T) { + assert(lldb_rpc_gen::TypeIsSBClass(T) && + "Cannot get SBClass name from non-SB class type!"); + + QualType UnqualifiedUnderlyingType = GetUnqualifiedUnderlyingType(T); + const auto *CXXRDecl = UnqualifiedUnderlyingType->getAsCXXRecordDecl(); + assert(CXXRDecl && "SB class was not CXXRecordDecl!"); + if (!CXXRDecl) + return std::string(); + + return CXXRDecl->getName().str(); +} +lldb_rpc_gen::Method::Method(CXXMethodDecl *MDecl, const PrintingPolicy &Policy, + ASTContext &Context) + : Policy(Policy), Context(Context), + QualifiedName(MDecl->getQualifiedNameAsString()), + BaseName(MDecl->getNameAsString()), + MangledName(lldb_rpc_gen::GetMangledName(Context, MDecl)), + ReturnType(MDecl->getReturnType()), IsConst(MDecl->isConst()), + IsInstance(MDecl->isInstance()), IsCtor(isa(MDecl)), + IsCopyAssign(MDecl->isCopyAssignmentOperator()), + IsMoveAssign(MDecl->isMoveAssignmentOperator()), + IsDtor(isa(MDecl)), + IsConversionMethod(isa(MDecl)) { + uint8_t UnnamedArgIdx = 0; + bool PrevParamWasPointer = false; + for (const auto *ParamDecl : MDecl->parameters()) { + Param param; + if (ParamDecl->hasDefaultArg()) + param.DefaultValueText = + Lexer::getSourceText( + CharSourceRange::getTokenRange( + ParamDecl->getDefaultArg()->getSourceRange()), + Context.getSourceManager(), Context.getLangOpts()) + .str(); + + param.IsFollowedByLen = false; + param.Name = ParamDecl->getNameAsString(); + // If the parameter has no name, we'll generate one + if (param.Name.empty()) { + param.Name = "arg" + std::to_string(UnnamedArgIdx); + UnnamedArgIdx++; + } + param.Type = ParamDecl->getType(); + + // FIXME: Instead of using this heuristic, the ideal thing would be to add + // annotations to the SBAPI methods themselves. For now, we have a list of + // methods that we know will need this. + if (PrevParamWasPointer) { + PrevParamWasPointer = false; + const bool IsIntegerType = param.Type->isIntegerType() && + !param.Type->isBooleanType() && + !param.Type->isEnumeralType(); + if (IsIntegerType && llvm::is_contained(MethodsWithPointerPlusLen, + llvm::StringRef(MangledName))) + Params.back().IsFollowedByLen = true; + } + + if (param.Type->isPointerType() && + !lldb_rpc_gen::TypeIsConstCharPtr(param.Type) && + !param.Type->isFunctionPointerType()) + PrevParamWasPointer = true; + + if (param.Type->isFunctionPointerType()) + ContainsFunctionPointerParameter = true; + + Params.push_back(param); + } + + if (IsInstance) + ThisType = MDecl->getThisType(); + + if (const auto *CtorDecl = dyn_cast(MDecl)) { + IsExplicitCtorOrConversionMethod = CtorDecl->isExplicit(); + IsCopyCtor = CtorDecl->isCopyConstructor(); + IsMoveCtor = CtorDecl->isMoveConstructor(); + } else if (const auto *ConversionDecl = dyn_cast(MDecl)) + IsExplicitCtorOrConversionMethod = ConversionDecl->isExplicit(); +} + +bool lldb_rpc_gen::Method::operator<(const lldb_rpc_gen::Method &rhs) const { + return this < &rhs; +} + +std::string +lldb_rpc_gen::Method::CreateParamListAsString(GenerationKind Generation, + bool IncludeDefaultValue) const { + assert((!IncludeDefaultValue || Generation == eLibrary) && + "Default values should only be emitted on the library side!"); + + std::vector ParamList; + + if (Generation == eLibrary && RequiresConnectionParameter()) + ParamList.push_back("const rpc::Connection &connection"); + + for (const auto &Param : Params) { + std::string ParamString; + llvm::raw_string_ostream ParamStringStream(ParamString); + + if (Generation == eLibrary) + ParamStringStream << lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace( + Param.Type.getAsString(Policy)); + else + ParamStringStream << Param.Type.getAsString(Policy); + + ParamStringStream << " " << Param.Name; + if (IncludeDefaultValue && Generation == eLibrary && + !Param.DefaultValueText.empty()) + ParamStringStream << " = " + << lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace( + Param.DefaultValueText); + + ParamList.push_back(ParamString); + } + + return llvm::join(ParamList, ", "); +} + +bool lldb_rpc_gen::Method::RequiresConnectionParameter() const { + if (llvm::is_contained(MethodsThatUnconditionallyDoNotNeedConnection, + MangledName)) { + return false; + } + if (!IsCtor && IsInstance) + return false; + if (IsCopyCtor || IsMoveCtor) + return false; + for (const auto &Param : Params) + // We can re-use the connection from our parameter if possible. + // Const-qualified parameters are input parameters and already + // have a valid connection to provide to the current method. + if (TypeIsSBClass(Param.Type) && + GetUnderlyingType(Param.Type).isConstQualified()) + return false; + + return true; +} + +std::string lldb_rpc_gen::GetDefaultArgumentsForConstructor( + std::string ClassName, const lldb_rpc_gen::Method &method) { + + std::string ParamString; + + const llvm::SmallVector &ParamTypes = + ClassName_to_ParameterTypes[ClassName]; + std::vector Params; + + Params.push_back("connection_sp"); + for (auto &ParamType : ParamTypes) { + if (ParamType == "const char *") + Params.push_back("nullptr"); + else if (ParamType == "bool") + Params.push_back("false"); + else if (ParamType.starts_with("SB")) { + // If the class to construct takes an SB parameter, + // go over the parameters from the method itself and + // see if it one of its parameters is that SB class. + // If not, see if we can use the method's class itself. + for (auto &CallingMethodParam : method.Params) { + QualType UUT = GetUnqualifiedUnderlyingType(CallingMethodParam.Type); + if (UUT.getAsString() == ParamType) { + Params.push_back(CallingMethodParam.Name); + } else if (GetSBClassNameFromType(method.ThisType) == ParamType) { + Params.push_back("*this"); + break; + } + } + } + } + + ParamString = llvm::join(Params, ", "); + return ParamString; +} diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.h b/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.h new file mode 100644 index 0000000000000..6498a32e137b9 --- /dev/null +++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.h @@ -0,0 +1,107 @@ +//===-- RPCCommon.h -------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_RPC_GEN_RPCCOMMON_H +#define LLDB_RPC_GEN_RPCCOMMON_H + +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclCXX.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace clang; + +namespace lldb_rpc_gen { +QualType GetUnderlyingType(QualType T); +QualType GetUnqualifiedUnderlyingType(QualType T); +std::string GetMangledName(ASTContext &Context, CXXMethodDecl *MDecl); + +bool TypeIsFromLLDBPrivate(QualType T); +bool TypeIsSBClass(QualType T); +bool TypeIsConstCharPtr(QualType T); +bool TypeIsConstCharPtrPtr(QualType T); +bool TypeIsDisallowedClass(QualType T); +bool TypeIsCallbackFunctionPointer(QualType T); + +bool MethodIsDisallowed(ASTContext &Context, CXXMethodDecl *MDecl); +bool HasCallbackParameter(CXXMethodDecl *MDecl); + +std::string ReplaceLLDBNamespaceWithRPCNamespace(std::string Name); +std::string StripLLDBNamespace(std::string Name); +bool SBClassRequiresDefaultCtor(const std::string &ClassName); +bool SBClassRequiresCopyCtorAssign(const std::string &ClassName); +bool SBClassInheritsFromObjectRef(const std::string &ClassName); +std::string GetSBClassNameFromType(QualType T); +struct Param { + std::string Name; + QualType Type; + std::string DefaultValueText; + bool IsFollowedByLen; +}; + +enum GenerationKind : bool { eServer, eLibrary }; + +struct Method { + enum Type { eOther, eConstructor, eDestructor }; + + Method(CXXMethodDecl *MDecl, const PrintingPolicy &Policy, + ASTContext &Context); + + // Adding a '<' allows us to use Methods in ordered containers. + bool operator<(const lldb_rpc_gen::Method &rhs) const; + const PrintingPolicy &Policy; + const ASTContext &Context; + std::string QualifiedName; + std::string BaseName; + std::string MangledName; + QualType ReturnType; + QualType ThisType; + std::vector Params; + bool IsConst = false; + bool IsInstance = false; + bool IsCtor = false; + bool IsCopyCtor = false; + bool IsCopyAssign = false; + bool IsMoveCtor = false; + bool IsMoveAssign = false; + bool IsDtor = false; + bool IsConversionMethod = false; + bool IsExplicitCtorOrConversionMethod = false; + bool ContainsFunctionPointerParameter = false; + + std::string CreateParamListAsString(GenerationKind Generation, + bool IncludeDefaultValue = false) const; + + bool RequiresConnectionParameter() const; +}; + +std::string +GetDefaultArgumentsForConstructor(std::string ClassName, + const lldb_rpc_gen::Method &method); + +class FileEmitter { +protected: + FileEmitter(std::unique_ptr &&OutputFile) + : OutputFile(std::move(OutputFile)), IndentLevel(0) {} + void EmitLine(const std::string &line) { + for (auto i = 0; i < IndentLevel; i++) + OutputFile->os() << " "; + + OutputFile->os() << line << "\n"; + } + + void EmitNewLine() { OutputFile->os() << "\n"; } + + std::unique_ptr OutputFile; + uint8_t IndentLevel; +}; +} // namespace lldb_rpc_gen +#endif // LLDB_RPC_GEN_RPCCOMMON_H diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp b/lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp new file mode 100644 index 0000000000000..3e145e94baf5c --- /dev/null +++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp @@ -0,0 +1,540 @@ +#include "RPCBindingsHarnessEmitter.h" +#include "RPCClientCallbacksSourceEmitter.h" +#include "RPCCommon.h" +#include "RPCLibraryHeaderEmitter.h" +#include "RPCLibrarySourceEmitter.h" +#include "RPCServerHeaderEmitter.h" +#include "RPCServerSourceEmitter.h" + +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/SourceManager.h" +#include "clang/CodeGen/ObjectFilePCHContainerWriter.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Serialization/ObjectFilePCHContainerReader.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/Tooling.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::driver; +using namespace clang::tooling; + +static llvm::cl::OptionCategory RPCGenCategory("Tool for generating LLDBRPC"); + +static llvm::cl::opt + OutputDir("output-dir", + llvm::cl::desc("Directory to output generated files to"), + llvm::cl::init(""), llvm::cl::cat(RPCGenCategory)); + +static std::string GetLibraryOutputDirectory() { + llvm::SmallString<128> Path(OutputDir.getValue()); + llvm::sys::path::append(Path, "lib"); + return std::string(Path); +} + +static std::string GetServerOutputDirectory() { + llvm::SmallString<128> Path(OutputDir.getValue()); + llvm::sys::path::append(Path, "server"); + return std::string(Path); +} + +static std::unique_ptr +CreateOutputFile(llvm::StringRef OutputDir, llvm::StringRef Filename) { + llvm::SmallString<128> Path(OutputDir); + llvm::sys::path::append(Path, Filename); + + std::error_code EC; + auto OutputFile = + std::make_unique(Path, EC, llvm::sys::fs::OF_None); + if (EC) { + llvm::errs() << "Failed to create output file: " << Path << "!\n"; + return nullptr; + } + return OutputFile; +} + +struct GeneratedByproducts { + std::set ClassNames; + std::set MangledMethodNames; + std::set SkippedMethodNames; + std::set CallbackMethods; +}; + +enum SupportLevel { + eUnsupported, + eUnimplemented, + eImplemented, +}; + +class SBVisitor : public RecursiveASTVisitor { +public: + SBVisitor( + GeneratedByproducts &Byproducts, SourceManager &Manager, + ASTContext &Context, + std::unique_ptr &&ServerMethodOutputFile, + std::unique_ptr &&ServerHeaderOutputFile, + std::unique_ptr &&LibrarySourceOutputFile, + std::unique_ptr &&LibraryHeaderOutputFile, + lldb_rpc_gen::RPCClientCallbacksSourceEmitter &UserClientSourceEmitter, + lldb_rpc_gen::RPCBindingsHarnessEmitter &BindingsHarnessEmitter) + : Byproducts(Byproducts), Manager(Manager), Context(Context), + ServerSourceEmitter(std::move(ServerMethodOutputFile)), + ServerHeaderEmitter(std::move(ServerHeaderOutputFile)), + LibrarySourceEmitter(std::move(LibrarySourceOutputFile)), + LibraryHeaderEmitter(std::move(LibraryHeaderOutputFile)), + ClientCallbacksSourceEmitter(UserClientSourceEmitter), + BindingsHarnessEmitter(BindingsHarnessEmitter) {} + + ~SBVisitor() {} + + bool VisitCXXRecordDecl(CXXRecordDecl *RDecl) { + if (ShouldSkipRecord(RDecl)) + return true; + + const std::string ClassName = RDecl->getNameAsString(); + Byproducts.ClassNames.insert(ClassName); + + // Print 'bool' instead of '_Bool'. + PrintingPolicy Policy(Context.getLangOpts()); + Policy.Bool = true; + + LibraryHeaderEmitter.StartClass(ClassName); + LibrarySourceEmitter.StartClass(ClassName); + BindingsHarnessEmitter.StartClass(ClassName); + for (Decl *D : RDecl->decls()) + if (auto *E = dyn_cast_or_null(D)) + LibraryHeaderEmitter.EmitEnum(E); + + for (CXXMethodDecl *MDecl : RDecl->methods()) { + const std::string MangledName = + lldb_rpc_gen::GetMangledName(Context, MDecl); + const bool IsDisallowed = lldb_rpc_gen::MethodIsDisallowed(Context, MDecl); + const bool HasCallbackParameter = + lldb_rpc_gen::HasCallbackParameter(MDecl); + SupportLevel MethodSupportLevel = GetMethodSupportLevel(MDecl); + if (MethodSupportLevel == eImplemented && !IsDisallowed) { + const lldb_rpc_gen::Method Method(MDecl, Policy, Context); + ServerSourceEmitter.EmitMethod(Method); + ServerHeaderEmitter.EmitMethod(Method); + LibrarySourceEmitter.EmitMethod(Method); + LibraryHeaderEmitter.EmitMethod(Method); + BindingsHarnessEmitter.EmitMethod(Method); + Byproducts.MangledMethodNames.insert(MangledName); + if (HasCallbackParameter) { + ClientCallbacksSourceEmitter.EmitMethod(Method); + Byproducts.CallbackMethods.insert(Method); + } + } else if (MethodSupportLevel == eUnimplemented) + Byproducts.SkippedMethodNames.insert(MangledName); + } + LibraryHeaderEmitter.EndClass(); + LibrarySourceEmitter.EndClass(); + BindingsHarnessEmitter.EndClass(); + return true; + } + +private: + /// Determines whether we should skip a RecordDecl. + /// Conditions for skipping: + /// - Anything not in the header itself + /// - Certain inconvenient classes + /// - Records without definitions (forward declarations) + bool ShouldSkipRecord(CXXRecordDecl *Decl) { + const Type *DeclType = Decl->getTypeForDecl(); + QualType CanonicalType = DeclType->getCanonicalTypeInternal(); + return !Manager.isInMainFile(Decl->getBeginLoc()) || + !Decl->hasDefinition() || Decl->getDefinition() != Decl || + lldb_rpc_gen::TypeIsDisallowedClass(CanonicalType); + } + + /// Check the support level for a type + /// Known unsupported types: + /// - FILE * (We do not want to expose this primitive) + /// - Types that are internal to LLDB + SupportLevel GetTypeSupportLevel(QualType Type) { + const std::string TypeName = Type.getAsString(); + if (TypeName == "FILE *" || lldb_rpc_gen::TypeIsFromLLDBPrivate(Type)) + return eUnsupported; + + if (lldb_rpc_gen::TypeIsDisallowedClass(Type)) + return eUnsupported; + + return eImplemented; + } + + /// Determine the support level of a given method. + /// Known unsupported methods: + /// - Non-public methods (lldb-rpc is a client and can only see public + /// things) + /// - Copy assignment operators (the client side will handle this) + /// - Move assignment operators (the client side will handle this) + /// - Methods involving unsupported types. + /// Known unimplemented methods: + /// - No variadic functions, e.g. Printf + SupportLevel GetMethodSupportLevel(CXXMethodDecl *MDecl) { + AccessSpecifier AS = MDecl->getAccess(); + if (AS != AccessSpecifier::AS_public) + return eUnsupported; + if (MDecl->isCopyAssignmentOperator()) + return eUnsupported; + if (MDecl->isMoveAssignmentOperator()) + return eUnsupported; + + if (MDecl->isVariadic()) + return eUnimplemented; + + SupportLevel ReturnTypeLevel = GetTypeSupportLevel(MDecl->getReturnType()); + if (ReturnTypeLevel != eImplemented) + return ReturnTypeLevel; + + for (auto *ParamDecl : MDecl->parameters()) { + SupportLevel ParamTypeLevel = GetTypeSupportLevel(ParamDecl->getType()); + if (ParamTypeLevel != eImplemented) + return ParamTypeLevel; + } + + // FIXME: If a callback does not take a `void *baton` parameter, it is + // considered unsupported at this time. On the server-side, we hijack the + // baton argument in order to pass additional information to the server-side + // callback so we can correctly perform a reverse RPC call back to the + // client. Without this baton, we would need the server-side callback to + // have some side channel by which it obtained that information, and + // spending time designing that doesn't outweight the cost of doing it at + // the moment. + bool HasCallbackParameter = false; + bool HasBatonParameter = false; + auto End = MDecl->parameters().end(); + for (auto Iter = MDecl->parameters().begin(); Iter != End; Iter++) { + if ((*Iter)->getType()->isFunctionPointerType()) { + HasCallbackParameter = true; + continue; + } + + // FIXME: We assume that if we have a function pointer and a void pointer + // together in the same parameter list, that it is not followed by a + // length argument. If that changes, we will need to revisit this + // implementation. + if ((*Iter)->getType()->isVoidPointerType()) + HasBatonParameter = true; + } + + if (HasCallbackParameter && !HasBatonParameter) + return eUnimplemented; + + return eImplemented; + } + + GeneratedByproducts &Byproducts; + SourceManager &Manager; + ASTContext &Context; + lldb_rpc_gen::RPCServerSourceEmitter ServerSourceEmitter; + lldb_rpc_gen::RPCServerHeaderEmitter ServerHeaderEmitter; + lldb_rpc_gen::RPCLibrarySourceEmitter LibrarySourceEmitter; + lldb_rpc_gen::RPCLibraryHeaderEmitter LibraryHeaderEmitter; + lldb_rpc_gen::RPCClientCallbacksSourceEmitter &ClientCallbacksSourceEmitter; + lldb_rpc_gen::RPCBindingsHarnessEmitter &BindingsHarnessEmitter; +}; + +class SBConsumer : public ASTConsumer { +public: + SBConsumer(GeneratedByproducts &Byproducts, SourceManager &Manager, + ASTContext &Context, + std::unique_ptr &&ServerMethodOutputFile, + std::unique_ptr &&ServerHeaderOutputFile, + std::unique_ptr &&LibrarySourceOutputFile, + std::unique_ptr &&LibraryHeaderOutputFile, + lldb_rpc_gen::RPCClientCallbacksSourceEmitter + &ClientCallbacksSourceEmitter, + lldb_rpc_gen::RPCBindingsHarnessEmitter &BindingsHarnessEmitter) + : Visitor(Byproducts, Manager, Context, std::move(ServerMethodOutputFile), + std::move(ServerHeaderOutputFile), + std::move(LibrarySourceOutputFile), + std::move(LibraryHeaderOutputFile), + ClientCallbacksSourceEmitter, BindingsHarnessEmitter) {} + bool HandleTopLevelDecl(DeclGroupRef DR) override { + for (Decl *D : DR) + Visitor.TraverseDecl(D); + + return true; + } + +private: + SBVisitor Visitor; +}; + +class SBAction : public ASTFrontendAction { +public: + SBAction( + GeneratedByproducts &Byproducts, + lldb_rpc_gen::RPCBindingsHarnessEmitter &BindingsHarnessEmitter, + lldb_rpc_gen::RPCClientCallbacksSourceEmitter &UserClientSourceEmitter) + : Byproducts(Byproducts), BindingsHarnessEmitter(BindingsHarnessEmitter), + ClientCallbacksSourceEmitter(UserClientSourceEmitter) {} + + std::unique_ptr + CreateASTConsumer(CompilerInstance &CI, llvm::StringRef File) override { + llvm::StringRef FilenameNoExt = + llvm::sys::path::stem(llvm::sys::path::filename(File)); + + const std::string ServerMethodFilename = + "Server_" + FilenameNoExt.str() + ".cpp"; + std::unique_ptr ServerMethodOutputFile = + CreateOutputFile(GetServerOutputDirectory(), ServerMethodFilename); + if (!ServerMethodOutputFile) + return nullptr; + + const std::string ServerHeaderFilename = + "Server_" + FilenameNoExt.str() + ".h"; + std::unique_ptr ServerHeaderOutputFile = + CreateOutputFile(GetServerOutputDirectory(), ServerHeaderFilename); + if (!ServerHeaderOutputFile) + return nullptr; + + const std::string LibrarySourceFilename = FilenameNoExt.str() + ".cpp"; + std::unique_ptr LibrarySourceOutputFile = + CreateOutputFile(GetLibraryOutputDirectory(), LibrarySourceFilename); + if (!LibrarySourceOutputFile) + return nullptr; + + const std::string LibraryHeaderFilename = FilenameNoExt.str() + ".h"; + std::unique_ptr LibraryHeaderOutputFile = + CreateOutputFile(GetLibraryOutputDirectory(), LibraryHeaderFilename); + if (!LibraryHeaderOutputFile) + return nullptr; + + ServerMethodOutputFile->keep(); + ServerHeaderOutputFile->keep(); + LibrarySourceOutputFile->keep(); + LibraryHeaderOutputFile->keep(); + return std::make_unique( + Byproducts, CI.getSourceManager(), CI.getASTContext(), + std::move(ServerMethodOutputFile), std::move(ServerHeaderOutputFile), + std::move(LibrarySourceOutputFile), std::move(LibraryHeaderOutputFile), + ClientCallbacksSourceEmitter, BindingsHarnessEmitter); + } + +private: + GeneratedByproducts &Byproducts; + lldb_rpc_gen::RPCBindingsHarnessEmitter &BindingsHarnessEmitter; + lldb_rpc_gen::RPCClientCallbacksSourceEmitter &ClientCallbacksSourceEmitter; +}; + +class SBActionFactory : public FrontendActionFactory { +public: + SBActionFactory( + GeneratedByproducts &Byproducts, + lldb_rpc_gen::RPCBindingsHarnessEmitter &BindingsHarnessEmitter, + lldb_rpc_gen::RPCClientCallbacksSourceEmitter + &ClientCallbacksSourceEmitter) + : Byproducts(Byproducts), BindingsHarnessEmitter(BindingsHarnessEmitter), + ClientCallbacksSourceEmitter(ClientCallbacksSourceEmitter) {} + + std::unique_ptr create() override { + return std::make_unique(Byproducts, BindingsHarnessEmitter, + ClientCallbacksSourceEmitter); + } + +private: + GeneratedByproducts &Byproducts; + lldb_rpc_gen::RPCBindingsHarnessEmitter &BindingsHarnessEmitter; + lldb_rpc_gen::RPCClientCallbacksSourceEmitter &ClientCallbacksSourceEmitter; +}; + +bool EmitAmalgamatedServerHeader(const std::vector &Files) { + // Create the file + static constexpr llvm::StringLiteral AmalgamatedServerHeaderName = "SBAPI.h"; + std::unique_ptr AmalgamatedServerHeader = + CreateOutputFile(GetServerOutputDirectory(), AmalgamatedServerHeaderName); + if (!AmalgamatedServerHeader) + return false; + + // Write the header + AmalgamatedServerHeader->os() + << "#ifndef GENERATED_LLDB_RPC_SERVER_SBAPI_H\n"; + AmalgamatedServerHeader->os() + << "#define GENERATED_LLDB_RPC_SERVER_SBAPI_H\n"; + for (const auto &File : Files) { + llvm::StringRef FilenameNoExt = + llvm::sys::path::stem(llvm::sys::path::filename(File)); + const std::string ServerHeaderFilename = + "Server_" + FilenameNoExt.str() + ".h"; + + AmalgamatedServerHeader->os() + << "#include \"" + ServerHeaderFilename + "\"\n"; + } + AmalgamatedServerHeader->os() << "#include \"SBAPIExtensions.h\"\n"; + AmalgamatedServerHeader->os() + << "#endif // GENERATED_LLDB_RPC_SERVER_SBAPI_H\n"; + AmalgamatedServerHeader->keep(); + return true; +} + +bool EmitAmalgamatedLibraryHeader(const std::vector &Files) { + static constexpr llvm::StringLiteral AmalgamatedLibraryHeaderName = + "LLDBRPC.h"; + std::unique_ptr AmalgamatedLibraryHeader = + CreateOutputFile(GetLibraryOutputDirectory(), + AmalgamatedLibraryHeaderName); + if (!AmalgamatedLibraryHeader) + return false; + + AmalgamatedLibraryHeader->os() << "#ifndef LLDBRPC_H\n"; + AmalgamatedLibraryHeader->os() << "#define LLDBRPC_H\n"; + AmalgamatedLibraryHeader->os() << "#include \"SBLanguages.h\"\n"; + for (const auto &File : Files) { + llvm::StringRef Filename = llvm::sys::path::filename(File); + AmalgamatedLibraryHeader->os() << "#include \"" << Filename << "\"\n"; + } + + AmalgamatedLibraryHeader->os() << "#endif // LLDBRPC_H\n"; + AmalgamatedLibraryHeader->keep(); + return true; +} + +bool EmitClassNamesFile(std::set &ClassNames) { + static constexpr llvm::StringLiteral ClassNamesFileName = "SBClasses.def"; + std::unique_ptr ClassNamesFile = + CreateOutputFile(OutputDir.getValue(), ClassNamesFileName); + if (!ClassNamesFile) + return false; + + ClassNamesFile->os() << "#ifndef SBCLASS\n"; + ClassNamesFile->os() << "#error \"SBClass must be defined\"\n"; + ClassNamesFile->os() << "#endif\n"; + + for (const auto &ClassName : ClassNames) { + if (ClassName == "SBStream" || ClassName == "SBProgress") + ClassNamesFile->os() << "#if !defined(SBCLASS_EXCLUDE_NONCOPYABLE)\n"; + else if (ClassName == "SBReproducer") + ClassNamesFile->os() << "#if !defined(SBCLASS_EXCLUDE_STATICONLY)\n"; + + ClassNamesFile->os() << "SBCLASS(" << ClassName << ")\n"; + if (ClassName == "SBStream" || ClassName == "SBReproducer" || + ClassName == "SBProgress") + ClassNamesFile->os() << "#endif\n"; + } + ClassNamesFile->keep(); + return true; +} + +bool EmitMethodNamesFile(std::set &MangledMethodNames) { + static constexpr llvm::StringLiteral MethodNamesFileName = "SBAPI.def"; + std::unique_ptr MethodNamesFile = + CreateOutputFile(OutputDir.getValue(), MethodNamesFileName); + if (!MethodNamesFile) + return false; + + MethodNamesFile->os() << "#ifndef GENERATE_SBAPI\n"; + MethodNamesFile->os() << "#error \"GENERATE_SBAPI must be defined\"\n"; + MethodNamesFile->os() << "#endif\n"; + + for (const auto &MangledName : MangledMethodNames) { + MethodNamesFile->os() << "GENERATE_SBAPI(" << MangledName << ")\n"; + } + MethodNamesFile->keep(); + return true; +} + +bool EmitSkippedMethodsFile(std::set &SkippedMethodNames) { + static constexpr llvm::StringLiteral FileName = "SkippedMethods.txt"; + std::unique_ptr File = + CreateOutputFile(OutputDir.getValue(), FileName); + if (!File) + return false; + + for (const auto &Skipped : SkippedMethodNames) { + File->os() << Skipped << "\n"; + } + File->keep(); + return true; +} + +int main(int argc, const char *argv[]) { + auto ExpectedParser = CommonOptionsParser::create( + argc, argv, RPCGenCategory, llvm::cl::OneOrMore, + "Tool for generating LLDBRPC interfaces and implementations"); + + if (!ExpectedParser) { + llvm::errs() << ExpectedParser.takeError(); + return 1; + } + + if (OutputDir.empty()) { + llvm::errs() << "Please specify an output directory for the generated " + "files with --output-dir!\n"; + return 1; + } + + CommonOptionsParser &OP = ExpectedParser.get(); + auto PCHOpts = std::make_shared(); + PCHOpts->registerWriter(std::make_unique()); + PCHOpts->registerReader(std::make_unique()); + + ClangTool T(OP.getCompilations(), OP.getSourcePathList(), PCHOpts); + + if (!EmitAmalgamatedServerHeader(OP.getSourcePathList())) { + llvm::errs() << "Failed to create amalgamated server header\n"; + return 1; + } + + if (!EmitAmalgamatedLibraryHeader(OP.getSourcePathList())) { + llvm::errs() << "Failed to create amalgamated library header\n"; + return 1; + } + + GeneratedByproducts Byproducts; + + constexpr llvm::StringLiteral BindingsHarnessFilename = "lldb.py"; + std::unique_ptr BindingsHarnessOutputFile = + CreateOutputFile(OutputDir.getValue(), BindingsHarnessFilename); + + if (!BindingsHarnessOutputFile) { + llvm::errs() << "Failed to create the bindings harness file\n"; + return 1; + } + BindingsHarnessOutputFile->keep(); + lldb_rpc_gen::RPCBindingsHarnessEmitter BindingsHarnessEmitter( + std::move(BindingsHarnessOutputFile)); + + static constexpr llvm::StringLiteral FileName = "RPCClientSideCallbacks.cpp"; + std::unique_ptr &&UserClientSourceOutputFile = + CreateOutputFile(GetLibraryOutputDirectory(), FileName); + if (!UserClientSourceOutputFile) { + llvm::errs() << "Failed to create the user client callbacks source file\n"; + return 1; + } + + UserClientSourceOutputFile->keep(); + lldb_rpc_gen::RPCClientCallbacksSourceEmitter ClientCallbacksSourceEmitter( + std::move(UserClientSourceOutputFile)); + + SBActionFactory Factory(Byproducts, BindingsHarnessEmitter, + ClientCallbacksSourceEmitter); + auto Result = T.run(&Factory); + ClientCallbacksSourceEmitter.EmitRPCClientInitialize( + Byproducts.CallbackMethods); + if (!EmitClassNamesFile(Byproducts.ClassNames)) { + llvm::errs() << "Failed to create SB Class file\n"; + return 1; + } + if (!EmitMethodNamesFile(Byproducts.MangledMethodNames)) { + llvm::errs() << "Failed to create Method Names file\n"; + return 1; + } + if (!EmitSkippedMethodsFile(Byproducts.SkippedMethodNames)) { + llvm::errs() << "Failed to create Skipped Methods file\n"; + return 1; + } + + return Result; +} From lldb-commits at lists.llvm.org Fri May 9 13:19:46 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 13:19:46 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream lldb-rpc-gen tool (PR #138031) In-Reply-To: Message-ID: <681e6362.050a0220.2b6a53.160f@mx.google.com> github-actions[bot] wrote: :warning: C/C++ code formatter, clang-format found issues in your code. :warning:
You can test this locally with the following command: ``````````bash git-clang-format --diff HEAD~1 HEAD --extensions h,cpp -- lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.h lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp ``````````
View the diff from clang-format here. ``````````diff diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp b/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp index 67897d32f..07b65f99a 100644 --- a/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp +++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp @@ -301,7 +301,8 @@ bool lldb_rpc_gen::TypeIsCallbackFunctionPointer(QualType T) { return T->isFunctionPointerType(); } -bool lldb_rpc_gen::MethodIsDisallowed(ASTContext &Context, CXXMethodDecl *MDecl) { +bool lldb_rpc_gen::MethodIsDisallowed(ASTContext &Context, + CXXMethodDecl *MDecl) { std::string MangledName = lldb_rpc_gen::GetMangledName(Context, MDecl); if (llvm::is_contained(DisallowedMethods, MangledName)) return true; diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp b/lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp index 3e145e94b..f6e729ddf 100644 --- a/lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp +++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp @@ -118,7 +118,8 @@ public: for (CXXMethodDecl *MDecl : RDecl->methods()) { const std::string MangledName = lldb_rpc_gen::GetMangledName(Context, MDecl); - const bool IsDisallowed = lldb_rpc_gen::MethodIsDisallowed(Context, MDecl); + const bool IsDisallowed = + lldb_rpc_gen::MethodIsDisallowed(Context, MDecl); const bool HasCallbackParameter = lldb_rpc_gen::HasCallbackParameter(MDecl); SupportLevel MethodSupportLevel = GetMethodSupportLevel(MDecl); ``````````
https://github.com/llvm/llvm-project/pull/138031 From lldb-commits at lists.llvm.org Fri May 9 13:35:15 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Fri, 09 May 2025 13:35:15 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Don't emit a removed module event for unseen modules (PR #139324) Message-ID: https://github.com/JDevlieghere created https://github.com/llvm/llvm-project/pull/139324 On macOS, lldb-dap is sending module events with reason "removed" for modules that we never told the client about in the first place. This is because when we create a target with a binary, by default we load all of its dependent libraries too. When we start executing and see the that the binary and dyld are loaded, we throw away the image list and let dyld tell us about the correct binaries to load. This PR addresses the issue by keeping track which modules the DAP client knows about. Clients can find out about modules either through the modules request, or through module events. Only when we have told a client about a module, we send module changed or module removed events. This PR also reduces the amount of data sent for removed module events. The DAP specification [1] says that for removed module events, only the ID is used. It also makes the testing more robust. Fixes #139323 [1] https://microsoft.github.io/debug-adapter-protocol/specification#Events_Module Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Fri May 9 13:35:55 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 13:35:55 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Don't emit a removed module event for unseen modules (PR #139324) In-Reply-To: Message-ID: <681e672b.170a0220.dfa74.a191@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Jonas Devlieghere (JDevlieghere)
Changes On macOS, lldb-dap is sending module events with reason "removed" for modules that we never told the client about in the first place. This is because when we create a target with a binary, by default we load all of its dependent libraries too. When we start executing and see the that the binary and dyld are loaded, we throw away the image list and let dyld tell us about the correct binaries to load. This PR addresses the issue by keeping track which modules the DAP client knows about. Clients can find out about modules either through the modules request, or through module events. Only when we have told a client about a module, we send module changed or module removed events. This PR also reduces the amount of data sent for removed module events. The DAP specification [1] says that for removed module events, only the ID is used. It also makes the testing more robust. Fixes #139323 [1] https://microsoft.github.io/debug-adapter-protocol/specification#Events_Module --- Full diff: https://github.com/llvm/llvm-project/pull/139324.diff 11 Files Affected: - (modified) lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py (-6) - (added) lldb/test/API/tools/lldb-dap/module-event/Makefile (+12) - (added) lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py (+55) - (added) lldb/test/API/tools/lldb-dap/module-event/main.cpp (+22) - (added) lldb/test/API/tools/lldb-dap/module-event/other.c (+5) - (modified) lldb/test/API/tools/lldb-dap/module/TestDAP_module.py (+6-4) - (modified) lldb/tools/lldb-dap/DAP.cpp (+27-12) - (modified) lldb/tools/lldb-dap/DAP.h (+8) - (modified) lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp (+14-3) - (modified) lldb/tools/lldb-dap/JSONUtils.cpp (+6-1) - (modified) lldb/tools/lldb-dap/JSONUtils.h (+6-1) ``````````diff diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py index e10342b72f4f0..c974866306d2a 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py @@ -134,7 +134,6 @@ def __init__(self, recv, send, init_commands, log_file=None): self.thread_stop_reasons = {} self.progress_events = [] self.reverse_requests = [] - self.module_events = [] self.sequence = 1 self.threads = None self.recv_thread.start() @@ -248,11 +247,6 @@ def handle_recv_packet(self, packet): # and 'progressEnd' events. Keep these around in case test # cases want to verify them. self.progress_events.append(packet) - elif event == "module": - # Module events indicate that some information about a module has changed. - self.module_events.append(packet) - # no need to add 'module' event packets to our packets list - return keepGoing elif packet_type == "response": if packet["command"] == "disconnect": diff --git a/lldb/test/API/tools/lldb-dap/module-event/Makefile b/lldb/test/API/tools/lldb-dap/module-event/Makefile new file mode 100644 index 0000000000000..99d79b8053878 --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/module-event/Makefile @@ -0,0 +1,12 @@ +CXX_SOURCES := main.cpp +LD_EXTRAS := -Wl,-rpath "-Wl,$(shell pwd)" +USE_LIBDL :=1 + +a.out: libother + +include Makefile.rules + +# The following shared library will be used to test breakpoints under dynamic loading +libother: other.c + "$(MAKE)" -f $(MAKEFILE_RULES) \ + DYLIB_ONLY=YES DYLIB_C_SOURCES=other.c DYLIB_NAME=other diff --git a/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py b/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py new file mode 100644 index 0000000000000..819640e5598bd --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py @@ -0,0 +1,55 @@ +import dap_server +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil +import lldbdap_testcase +import re + + +class TestDAP_module_event(lldbdap_testcase.DAPTestCaseBase): + + def test_module_event(self): + program = self.getBuildArtifact("a.out") + self.build_and_launch(program, stopOnEntry=True) + + source = "main.cpp" + breakpoint1_line = line_number(source, "// breakpoint 1") + breakpoint2_line = line_number(source, "// breakpoint 2") + breakpoint3_line = line_number(source, "// breakpoint 3") + + breakpoint_ids = self.set_source_breakpoints( + source, [breakpoint1_line, breakpoint2_line, breakpoint3_line] + ) + self.continue_to_breakpoints(breakpoint_ids) + + # We're now stopped at breakpoint 1 before the dlopen. Flush all the module events. + event = self.dap_server.wait_for_event("module", 0.25) + while event is not None: + event = self.dap_server.wait_for_event("module", 0.25) + + # Continue to the second breakpoint, before the dlclose. + self.continue_to_breakpoints(breakpoint_ids) + + # Make sure we got a module event for libother. + event = self.dap_server.wait_for_event("module", 5) + self.assertTrue(event, "didn't get a module event") + module_name = event["body"]["module"]["name"] + module_id = event["body"]["module"]["id"] + self.assertEqual(event["body"]["reason"], "new") + self.assertIn("libother", module_name) + + # Continue to the third breakpoint, after the dlclose. + self.continue_to_breakpoints(breakpoint_ids) + + # Make sure we got a module event for libother. + event = self.dap_server.wait_for_event("module", 5) + self.assertTrue(event, "didn't get a module event") + reason = event["body"]["reason"] + self.assertEqual(event["body"]["reason"], "removed") + self.assertEqual(event["body"]["module"]["id"], module_id) + + # The removed module event should omit everything but the module id. + # Check that there's no module name in the event. + self.assertNotIn("name", event["body"]["module"]) + + self.continue_to_exit() diff --git a/lldb/test/API/tools/lldb-dap/module-event/main.cpp b/lldb/test/API/tools/lldb-dap/module-event/main.cpp new file mode 100644 index 0000000000000..711471b9fadd0 --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/module-event/main.cpp @@ -0,0 +1,22 @@ +#include +#include + +int main(int argc, char const *argv[]) { + +#if defined(__APPLE__) + const char *libother_name = "libother.dylib"; +#else + const char *libother_name = "libother.so"; +#endif + + printf("before dlopen\n"); // breakpoint 1 + void *handle = dlopen(libother_name, RTLD_NOW); + int (*foo)(int) = (int (*)(int))dlsym(handle, "foo"); + foo(12); + + printf("before dlclose\n"); // breakpoint 2 + dlclose(handle); + printf("after dlclose\n"); // breakpoint 3 + + return 0; // breakpoint 1 +} diff --git a/lldb/test/API/tools/lldb-dap/module-event/other.c b/lldb/test/API/tools/lldb-dap/module-event/other.c new file mode 100644 index 0000000000000..dd164597269dc --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/module-event/other.c @@ -0,0 +1,5 @@ +extern int foo(int x) { + int y = x + 42; // break other + int z = y + 42; + return z; +} diff --git a/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py b/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py index 210819cfdd732..3fc0f752ee39e 100644 --- a/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py +++ b/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py @@ -60,13 +60,15 @@ def checkSymbolsLoadedWithSize(): # Collect all the module names we saw as events. module_new_names = [] module_changed_names = [] - for module_event in self.dap_server.module_events: - module_name = module_event["body"]["module"]["name"] + module_event = self.dap_server.wait_for_event("module", 1) + while module_event is not None: reason = module_event["body"]["reason"] if reason == "new": - module_new_names.append(module_name) + module_new_names.append(module_event["body"]["module"]["name"]) elif reason == "changed": - module_changed_names.append(module_name) + module_changed_names.append(module_event["body"]["module"]["name"]) + + module_event = self.dap_server.wait_for_event("module", 1) # Make sure we got an event for every active module. self.assertNotEqual(len(module_new_names), 0) diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index f6754b1f8d7a3..828d7d4873156 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -105,16 +105,6 @@ static uint64_t GetUintFromStructuredData(lldb::SBStructuredData &data, return keyValue.GetUnsignedIntegerValue(); } -static llvm::StringRef GetModuleEventReason(uint32_t event_mask) { - if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded) - return "new"; - if (event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded) - return "removed"; - assert(event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded || - event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged); - return "changed"; -} - /// Return string with first character capitalized. static std::string capitalize(llvm::StringRef str) { if (str.empty()) @@ -1566,7 +1556,6 @@ void DAP::EventThread() { event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded || event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded || event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged) { - llvm::StringRef reason = GetModuleEventReason(event_mask); const uint32_t num_modules = lldb::SBTarget::GetNumModulesFromEvent(event); for (uint32_t i = 0; i < num_modules; ++i) { @@ -1574,10 +1563,36 @@ void DAP::EventThread() { lldb::SBTarget::GetModuleAtIndexFromEvent(i, event); if (!module.IsValid()) continue; + llvm::StringRef module_id = module.GetUUIDString(); + if (module_id.empty()) + continue; + + llvm::StringRef reason; + bool id_only = false; + { + std::lock_guard guard(modules_mutex); + if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded) { + modules.insert(module_id); + reason = "new"; + } else { + // If this is a module we've never told the client about, don't + // send an event. + if (!modules.contains(module_id)) + continue; + + if (event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded) { + modules.erase(module_id); + reason = "removed"; + id_only = true; + } else { + reason = "changed"; + } + } + } llvm::json::Object body; body.try_emplace("reason", reason); - body.try_emplace("module", CreateModule(target, module)); + body.try_emplace("module", CreateModule(target, module, id_only)); llvm::json::Object module_event = CreateEventObject("module"); module_event.try_emplace("body", std::move(body)); SendJSON(llvm::json::Value(std::move(module_event))); diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index afeda8d81efb0..75e4ab0e23616 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -39,6 +39,7 @@ #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" #include "llvm/Support/Error.h" #include "llvm/Support/JSON.h" #include "llvm/Support/Threading.h" @@ -212,6 +213,13 @@ struct DAP { /// The initial thread list upon attaching. std::optional initial_thread_list; + /// Keep track of all the modules our client knows about: either through the + /// modules request or the module events. + /// @{ + std::mutex modules_mutex; + llvm::StringSet<> modules; + /// @} + /// Creates a new DAP sessions. /// /// \param[in] log diff --git a/lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp index ed51d395768c4..d37f302b06958 100644 --- a/lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp @@ -45,9 +45,20 @@ void ModulesRequestHandler::operator()( FillResponse(request, response); llvm::json::Array modules; - for (size_t i = 0; i < dap.target.GetNumModules(); i++) { - lldb::SBModule module = dap.target.GetModuleAtIndex(i); - modules.emplace_back(CreateModule(dap.target, module)); + + { + std::lock_guard guard(dap.modules_mutex); + for (size_t i = 0; i < dap.target.GetNumModules(); i++) { + lldb::SBModule module = dap.target.GetModuleAtIndex(i); + if (!module.IsValid()) + continue; + + llvm::StringRef module_id = module.GetUUIDString(); + if (!module_id.empty()) + dap.modules.insert(module_id); + + modules.emplace_back(CreateModule(dap.target, module)); + } } llvm::json::Object body; diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp index 4409cf5b27e5b..e647bd23582c4 100644 --- a/lldb/tools/lldb-dap/JSONUtils.cpp +++ b/lldb/tools/lldb-dap/JSONUtils.cpp @@ -460,13 +460,18 @@ static std::string ConvertDebugInfoSizeToString(uint64_t debug_info) { return oss.str(); } -llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module) { +llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module, + bool id_only) { llvm::json::Object object; if (!target.IsValid() || !module.IsValid()) return llvm::json::Value(std::move(object)); const char *uuid = module.GetUUIDString(); object.try_emplace("id", uuid ? std::string(uuid) : std::string("")); + + if (id_only) + return llvm::json::Value(std::move(object)); + object.try_emplace("name", std::string(module.GetFileSpec().GetFilename())); char module_path_arr[PATH_MAX]; module.GetFileSpec().GetPath(module_path_arr, sizeof(module_path_arr)); diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h index d0e20729f4ed9..a5baf61c90f60 100644 --- a/lldb/tools/lldb-dap/JSONUtils.h +++ b/lldb/tools/lldb-dap/JSONUtils.h @@ -267,10 +267,15 @@ CreateBreakpoint(BreakpointBase *bp, /// \param[in] module /// A LLDB module object to convert into a JSON value /// +/// \param[in] id_only +/// Only include the module ID in the JSON value. This is used when sending +/// a "removed" module event. +/// /// \return /// A "Module" JSON object with that follows the formal JSON /// definition outlined by Microsoft. -llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module); +llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module, + bool id_only = false); /// Create a "Event" JSON object using \a event_name as the event name /// ``````````
https://github.com/llvm/llvm-project/pull/139324 From lldb-commits at lists.llvm.org Fri May 9 13:36:28 2025 From: lldb-commits at lists.llvm.org (Igor Kudrin via lldb-commits) Date: Fri, 09 May 2025 13:36:28 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][NFC] Avoid an assertion failure in dwim-print (PR #139197) In-Reply-To: Message-ID: <681e674c.170a0220.1125e5.9952@mx.google.com> https://github.com/igorkudrin updated https://github.com/llvm/llvm-project/pull/139197 >From 3faf0c0a4a19d7e1d503c31a684d79295e414be4 Mon Sep 17 00:00:00 2001 From: Igor Kudrin Date: Thu, 8 May 2025 18:37:34 -0700 Subject: [PATCH 1/2] [lldb][NFC] Avoid an assertion failure in dwim-print In a Debug build on Windows, printing inline diagnostics resulted in an error, for example: ``` > cd llvm-project\lldb\test\API\functionalities\postmortem\elf-core > lldb.exe -c altmain.core > p dummy LLDB diagnostics will be written to ... Please include the directory content when filing a bug report Exception Code: 0x80000003 0x00007FF8FD6633EC, C:\llvm\build\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x13A33EC byte(s), std::_Vector_const_iterator > >::_Compat() + 0x6C byte(s), C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.43.34808\include\vector, line 202 + 0x5D byte(s) 0x00007FF8FD662ABE, C:\llvm\build\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x13A2ABE byte(s), std::_Vector_const_iterator > >::operator==() + 0x1E byte(s), C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.43.34808\include\vector, line 166 + 0x0 byte(s) 0x00007FF8FD662B2E, C:\llvm\build\\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x13A2B2E byte(s), std::_Vector_const_iterator > >::operator!=() + 0x1E byte(s), C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.43.34808\include\vector, line 176 + 0xF byte(s) 0x00007FF8FD65EE1C, C:\llvm\build\\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x139EE1C byte(s), std::operator!= > >,std::_Vector_iterator > > >() + 0x3C byte(s), C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.43.34808\include\xutility, line 1947 + 0x0 byte(s) 0x00007FF8FD65D4E5, C:\llvm\build\\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x139D4E5 byte(s), lldb_private::RenderDiagnosticDetails() + 0x8F5 byte(s), C:\llvm\src\llvm-project\lldb\source\Utility\DiagnosticsRendering.cpp, line 189 + 0x25 byte(s) ... ``` The comparison operator of the iterators checks that they belong to the same container, but `remaining_details.pop_back()` invalidates `detail` making it incompatible with `remaining_details.rend()`. --- lldb/source/Utility/DiagnosticsRendering.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lldb/source/Utility/DiagnosticsRendering.cpp b/lldb/source/Utility/DiagnosticsRendering.cpp index 368e2199b749f..c43b39b6b8fe9 100644 --- a/lldb/source/Utility/DiagnosticsRendering.cpp +++ b/lldb/source/Utility/DiagnosticsRendering.cpp @@ -185,9 +185,8 @@ void RenderDiagnosticDetails(Stream &stream, // Work through each detail in reverse order using the vector/stack. bool did_print = false; - for (auto detail = remaining_details.rbegin(); - detail != remaining_details.rend(); - ++detail, remaining_details.pop_back()) { + for (; !remaining_details.empty(); remaining_details.pop_back()) { + auto &detail = remaining_details.back(); // Get the information to print this detail and remove it from the stack. // Print all the lines for all the other messages first. stream << std::string(padding, ' '); @@ -196,7 +195,7 @@ void RenderDiagnosticDetails(Stream &stream, llvm::ArrayRef(remaining_details).drop_back(1)) { uint16_t column = remaining_detail.source_location->column; // Is this a note with the same column as another diagnostic? - if (column == detail->source_location->column) + if (column == detail.source_location->column) continue; if (column >= x_pos) { @@ -205,16 +204,16 @@ void RenderDiagnosticDetails(Stream &stream, } } - uint16_t column = detail->source_location->column; + uint16_t column = detail.source_location->column; // Print the line connecting the ^ with the error message. if (column >= x_pos) stream << std::string(column - x_pos, ' ') << joint << hbar << spacer; // Print a colorized string based on the message's severity type. - PrintSeverity(stream, detail->severity); + PrintSeverity(stream, detail.severity); // Finally, print the message and start a new line. - stream << detail->message << '\n'; + stream << detail.message << '\n'; did_print = true; } >From 169c3401717ccff0d28c18318a9b433165076d4b Mon Sep 17 00:00:00 2001 From: Igor Kudrin Date: Fri, 9 May 2025 13:36:20 -0700 Subject: [PATCH 2/2] Update lldb/source/Utility/DiagnosticsRendering.cpp Co-authored-by: Michael Buch --- lldb/source/Utility/DiagnosticsRendering.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/source/Utility/DiagnosticsRendering.cpp b/lldb/source/Utility/DiagnosticsRendering.cpp index c43b39b6b8fe9..e73d688bb4be5 100644 --- a/lldb/source/Utility/DiagnosticsRendering.cpp +++ b/lldb/source/Utility/DiagnosticsRendering.cpp @@ -186,7 +186,7 @@ void RenderDiagnosticDetails(Stream &stream, // Work through each detail in reverse order using the vector/stack. bool did_print = false; for (; !remaining_details.empty(); remaining_details.pop_back()) { - auto &detail = remaining_details.back(); + const auto &detail = remaining_details.back(); // Get the information to print this detail and remove it from the stack. // Print all the lines for all the other messages first. stream << std::string(padding, ' '); From lldb-commits at lists.llvm.org Fri May 9 13:36:43 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Fri, 09 May 2025 13:36:43 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Improving tests logging to understand CI failures. (PR #139311) In-Reply-To: Message-ID: <681e675b.170a0220.3a3462.8937@mx.google.com> https://github.com/JDevlieghere approved this pull request. https://github.com/llvm/llvm-project/pull/139311 From lldb-commits at lists.llvm.org Fri May 9 13:37:29 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 13:37:29 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Don't emit a removed module event for unseen modules (PR #139324) In-Reply-To: Message-ID: <681e6789.630a0220.72947.5e1a@mx.google.com> github-actions[bot] wrote: :warning: Python code formatter, darker found issues in your code. :warning:
You can test this locally with the following command: ``````````bash darker --check --diff -r HEAD~1...HEAD lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py lldb/test/API/tools/lldb-dap/module/TestDAP_module.py ``````````
View the diff from darker here. ``````````diff --- test/API/tools/lldb-dap/module-event/TestDAP_module_event.py 2025-05-09 20:26:39.000000 +0000 +++ test/API/tools/lldb-dap/module-event/TestDAP_module_event.py 2025-05-09 20:37:04.183047 +0000 @@ -5,11 +5,10 @@ import lldbdap_testcase import re class TestDAP_module_event(lldbdap_testcase.DAPTestCaseBase): - def test_module_event(self): program = self.getBuildArtifact("a.out") self.build_and_launch(program, stopOnEntry=True) source = "main.cpp" ``````````
https://github.com/llvm/llvm-project/pull/139324 From lldb-commits at lists.llvm.org Fri May 9 13:46:38 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 13:46:38 -0700 (PDT) Subject: [Lldb-commits] [lldb] d71b6cf - [lldb][NFC] Avoid an assertion failure in dwim-print (#139197) Message-ID: <681e69ae.630a0220.b019c.60db@mx.google.com> Author: Igor Kudrin Date: 2025-05-09T13:46:35-07:00 New Revision: d71b6cf6a54cd0e39a70b039b489fab27274280a URL: https://github.com/llvm/llvm-project/commit/d71b6cf6a54cd0e39a70b039b489fab27274280a DIFF: https://github.com/llvm/llvm-project/commit/d71b6cf6a54cd0e39a70b039b489fab27274280a.diff LOG: [lldb][NFC] Avoid an assertion failure in dwim-print (#139197) With a debug build on Windows, printing inline diagnostics resulted in an error, for example: ``` > cd llvm-project\lldb\test\API\functionalities\postmortem\elf-core > lldb.exe -c altmain.core > p dummy LLDB diagnostics will be written to ... Please include the directory content when filing a bug report Exception Code: 0x80000003 0x00007FF8FD6633EC, C:\llvm\build\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x13A33EC byte(s), std::_Vector_const_iterator > >::_Compat() + 0x6C byte(s), C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.43.34808\include\vector, line 202 + 0x5D byte(s) 0x00007FF8FD662ABE, C:\llvm\build\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x13A2ABE byte(s), std::_Vector_const_iterator > >::operator==() + 0x1E byte(s), C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.43.34808\include\vector, line 166 + 0x0 byte(s) 0x00007FF8FD662B2E, C:\llvm\build\\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x13A2B2E byte(s), std::_Vector_const_iterator > >::operator!=() + 0x1E byte(s), C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.43.34808\include\vector, line 176 + 0xF byte(s) 0x00007FF8FD65EE1C, C:\llvm\build\\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x139EE1C byte(s), std::operator!= > >,std::_Vector_iterator > > >() + 0x3C byte(s), C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.43.34808\include\xutility, line 1947 + 0x0 byte(s) 0x00007FF8FD65D4E5, C:\llvm\build\\bin\liblldb.dll(0x00007FF8FC2C0000) + 0x139D4E5 byte(s), lldb_private::RenderDiagnosticDetails() + 0x8F5 byte(s), C:\llvm\src\llvm-project\lldb\source\Utility\DiagnosticsRendering.cpp, line 189 + 0x25 byte(s) ... ``` The comparison operator of the iterators checks that they belong to the same container, but `remaining_details.pop_back()` invalidates `detail` making it incompatible with `remaining_details.rend()`. --------- Co-authored-by: Michael Buch Added: Modified: lldb/source/Utility/DiagnosticsRendering.cpp Removed: ################################################################################ diff --git a/lldb/source/Utility/DiagnosticsRendering.cpp b/lldb/source/Utility/DiagnosticsRendering.cpp index 368e2199b749f..6f276a81fbc8e 100644 --- a/lldb/source/Utility/DiagnosticsRendering.cpp +++ b/lldb/source/Utility/DiagnosticsRendering.cpp @@ -185,9 +185,8 @@ void RenderDiagnosticDetails(Stream &stream, // Work through each detail in reverse order using the vector/stack. bool did_print = false; - for (auto detail = remaining_details.rbegin(); - detail != remaining_details.rend(); - ++detail, remaining_details.pop_back()) { + for (; !remaining_details.empty(); remaining_details.pop_back()) { + const auto &detail = remaining_details.back(); // Get the information to print this detail and remove it from the stack. // Print all the lines for all the other messages first. stream << std::string(padding, ' '); @@ -196,7 +195,7 @@ void RenderDiagnosticDetails(Stream &stream, llvm::ArrayRef(remaining_details).drop_back(1)) { uint16_t column = remaining_detail.source_location->column; // Is this a note with the same column as another diagnostic? - if (column == detail->source_location->column) + if (column == detail.source_location->column) continue; if (column >= x_pos) { @@ -205,16 +204,16 @@ void RenderDiagnosticDetails(Stream &stream, } } - uint16_t column = detail->source_location->column; + uint16_t column = detail.source_location->column; // Print the line connecting the ^ with the error message. if (column >= x_pos) stream << std::string(column - x_pos, ' ') << joint << hbar << spacer; // Print a colorized string based on the message's severity type. - PrintSeverity(stream, detail->severity); + PrintSeverity(stream, detail.severity); // Finally, print the message and start a new line. - stream << detail->message << '\n'; + stream << detail.message << '\n'; did_print = true; } From lldb-commits at lists.llvm.org Fri May 9 13:46:41 2025 From: lldb-commits at lists.llvm.org (Igor Kudrin via lldb-commits) Date: Fri, 09 May 2025 13:46:41 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][NFC] Avoid an assertion failure in dwim-print (PR #139197) In-Reply-To: Message-ID: <681e69b1.170a0220.1519d2.bc6e@mx.google.com> https://github.com/igorkudrin closed https://github.com/llvm/llvm-project/pull/139197 From lldb-commits at lists.llvm.org Fri May 9 13:52:29 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Fri, 09 May 2025 13:52:29 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream lldb-rpc-gen tool (PR #138031) In-Reply-To: Message-ID: <681e6b0d.620a0220.1aaf07.e5c6@mx.google.com> https://github.com/chelcassanova updated https://github.com/llvm/llvm-project/pull/138031 >From ad185638b6369aea18848bcdbb38bd502d75ff71 Mon Sep 17 00:00:00 2001 From: Chelsea Cassanova Date: Wed, 30 Apr 2025 14:15:39 -0700 Subject: [PATCH] [lldb][RPC] Upstream lldb-rpc-gen tool This commit upstreams the `lldb-rpc-gen` tool, a ClangTool that generates the LLDB RPC client and server interfaces. https://discourse.llvm.org/t/rfc-upstreaming-lldb-rpc/85804 --- .../tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp | 546 ++++++++++++++++++ lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.h | 107 ++++ .../lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp | 541 +++++++++++++++++ 3 files changed, 1194 insertions(+) create mode 100644 lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp create mode 100644 lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.h create mode 100644 lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp b/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp new file mode 100644 index 0000000000000..07b65f99af84c --- /dev/null +++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp @@ -0,0 +1,546 @@ +//===-- RPCCommon.cpp -----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RPCCommon.h" + +#include "clang/AST/AST.h" +#include "clang/AST/Attr.h" +#include "clang/AST/DeclBase.h" +#include "clang/AST/Mangle.h" +#include "clang/Lex/Lexer.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +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", // What is this used for? + "SBInputReader", // What is this used for? + "SBCommandPluginInterface", // This is hard to support, we can do it if + // really needed though. + "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. +}; + +// We intentionally avoid generating certain methods either because they are +// difficult to support correctly or they aren't really used much from C++. +// NOTE: These methods are marked as deprecated using LLDB_DEPRECATED. +// Normally this macro defines to the deprecated annotation, but this +// functionality is removed in SBDefines.h when generating SWIG bindings which +// we use for testing. Because of this, there is no annotation for the tool to +// pick up on so this list will be used while we have this restriction in +// SBDefines.h. +static constexpr llvm::StringRef DisallowedMethods[] = { + // The threading functionality in SBHostOS is deprecated and thus we do not + // generate them. It would be ideal to add the annotations to the methods + // and then support not generating deprecated methods. However, without + // annotations the generator generates most things correctly. This one is + // problematic because it returns a pointer to an "opaque" structure + // (thread_t) that is not `void *`, so special casing it is more effort than + // it's worth. + "_ZN4lldb8SBHostOS10ThreadJoinEP17_opaque_pthread_tPPvPNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadCancelEP17_opaque_pthread_tPNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadCreateEPKcPFPvS3_ES3_PNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadDetachEP17_opaque_pthread_tPNS_7SBErrorE", + "_ZN4lldb8SBHostOS13ThreadCreatedEPKc", +}; + +static constexpr llvm::StringRef ClassesWithoutDefaultCtor[] = { + "SBHostOS", + "SBReproducer", +}; + +static constexpr llvm::StringRef ClassesWithoutCopyOperations[] = { + "SBHostOS", + "SBReproducer", + "SBStream", + "SBProgress", +}; + +static constexpr llvm::StringRef MethodsWithPointerPlusLen[] = { + "_ZN4lldb6SBData11ReadRawDataERNS_7SBErrorEyPvm", + "_ZN4lldb6SBData7SetDataERNS_7SBErrorEPKvmNS_9ByteOrderEh", + "_ZN4lldb6SBData20SetDataWithOwnershipERNS_7SBErrorEPKvmNS_9ByteOrderEh", + "_ZN4lldb6SBData25CreateDataFromUInt64ArrayENS_9ByteOrderEjPym", + "_ZN4lldb6SBData25CreateDataFromUInt32ArrayENS_9ByteOrderEjPjm", + "_ZN4lldb6SBData25CreateDataFromSInt64ArrayENS_9ByteOrderEjPxm", + "_ZN4lldb6SBData25CreateDataFromSInt32ArrayENS_9ByteOrderEjPim", + "_ZN4lldb6SBData25CreateDataFromDoubleArrayENS_9ByteOrderEjPdm", + "_ZN4lldb6SBData22SetDataFromUInt64ArrayEPym", + "_ZN4lldb6SBData22SetDataFromUInt32ArrayEPjm", + "_ZN4lldb6SBData22SetDataFromSInt64ArrayEPxm", + "_ZN4lldb6SBData22SetDataFromSInt32ArrayEPim", + "_ZN4lldb6SBData22SetDataFromDoubleArrayEPdm", + "_ZN4lldb10SBDebugger22GetDefaultArchitectureEPcm", + "_ZN4lldb10SBDebugger13DispatchInputEPvPKvm", + "_ZN4lldb10SBDebugger13DispatchInputEPKvm", + "_ZN4lldb6SBFile4ReadEPhmPm", + "_ZN4lldb6SBFile5WriteEPKhmPm", + "_ZNK4lldb10SBFileSpec7GetPathEPcm", + "_ZN4lldb10SBFileSpec11ResolvePathEPKcPcm", + "_ZN4lldb8SBModule10GetVersionEPjj", + "_ZN4lldb12SBModuleSpec12SetUUIDBytesEPKhm", + "_ZNK4lldb9SBProcess9GetSTDOUTEPcm", + "_ZNK4lldb9SBProcess9GetSTDERREPcm", + "_ZNK4lldb9SBProcess19GetAsyncProfileDataEPcm", + "_ZN4lldb9SBProcess10ReadMemoryEyPvmRNS_7SBErrorE", + "_ZN4lldb9SBProcess11WriteMemoryEyPKvmRNS_7SBErrorE", + "_ZN4lldb9SBProcess21ReadCStringFromMemoryEyPvmRNS_7SBErrorE", + "_ZNK4lldb16SBStructuredData14GetStringValueEPcm", + "_ZN4lldb8SBTarget23BreakpointCreateByNamesEPPKcjjRKNS_" + "14SBFileSpecListES6_", + "_ZN4lldb8SBTarget10ReadMemoryENS_9SBAddressEPvmRNS_7SBErrorE", + "_ZN4lldb8SBTarget15GetInstructionsENS_9SBAddressEPKvm", + "_ZN4lldb8SBTarget25GetInstructionsWithFlavorENS_9SBAddressEPKcPKvm", + "_ZN4lldb8SBTarget15GetInstructionsEyPKvm", + "_ZN4lldb8SBTarget25GetInstructionsWithFlavorEyPKcPKvm", + "_ZN4lldb8SBThread18GetStopDescriptionEPcm", + // The below mangled names are used for dummy methods in shell tests + // that test the emitters' output. If you're adding any new mangled names + // from the actual SB API to this list please add them above. + "_ZN4lldb33SBRPC_" + "CHECKCONSTCHARPTRPTRWITHLEN27CheckConstCharPtrPtrWithLenEPPKcm", + "_ZN4lldb19SBRPC_CHECKARRAYPTR13CheckArrayPtrEPPKcm", + "_ZN4lldb18SBRPC_CHECKVOIDPTR12CheckVoidPtrEPvm", +}; + +// These methods should take a connection parameter according to our logic in +// RequiresConnectionParameter() but in the handwritten version they +// don't take a connection. These methods need to have their implementation +// changed but for now, we just have an exception list of functions that will +// never be a given connection parameter. +// +// FIXME: Change the implementation of these methods so that they can be given a +// connection parameter. +static constexpr llvm::StringRef + MethodsThatUnconditionallyDoNotNeedConnection[] = { + "_ZN4lldb16SBBreakpointNameC1ERNS_8SBTargetEPKc", + "_ZN4lldb10SBDebugger7DestroyERS0_", + "_ZN4lldb18SBExecutionContextC1ENS_8SBThreadE", +}; + +// These classes inherit from rpc::ObjectRef directly (as opposed to +// rpc::LocalObjectRef). Changing them from ObjectRef to LocalObjectRef is ABI +// breaking, so we preserve that compatibility here. +// +// lldb-rpc-gen emits classes as LocalObjectRefs by default. +// +// FIXME: Does it matter which one it emits by default? +static constexpr llvm::StringRef ClassesThatInheritFromObjectRef[] = { + "SBAddress", + "SBBreakpointName", + "SBCommandInterpreter", + "SBCommandReturnObject", + "SBError", + "SBExecutionContext", + "SBExpressionOptions", + "SBFileSpec", + "SBFileSpecList", + "SBFormat", + "SBFunction", + "SBHistoricalFrame", + "SBHistoricalLineEntry", + "SBHistoricalLineEntryList", + "SBLineEntry", + "SBStream", + "SBStringList", + "SBStructuredData", + "SBSymbolContext", + "SBSymbolContextList", + "SBTypeMember", + "SBTypeSummaryOptions", + "SBValueList", +}; + +static llvm::StringMap> + ClassName_to_ParameterTypes = { + {"SBLaunchInfo", {"const char *"}}, + {"SBPlatformConnectOptions", {"const char *"}}, + {"SBPlatformShellCommand", {"const char *", "const char *"}}, + {"SBBreakpointList", {"SBTarget"}}, +}; + +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) { + QualType UnderlyingType = GetUnderlyingType(T); + return UnderlyingType.getUnqualifiedType(); +} + +std::string lldb_rpc_gen::GetMangledName(ASTContext &Context, + CXXMethodDecl *MDecl) { + std::string Mangled; + llvm::raw_string_ostream MangledStream(Mangled); + + GlobalDecl GDecl; + if (const auto *CtorDecl = dyn_cast(MDecl)) + GDecl = GlobalDecl(CtorDecl, Ctor_Complete); + else if (const auto *DtorDecl = dyn_cast(MDecl)) + GDecl = GlobalDecl(DtorDecl, Dtor_Deleting); + else + GDecl = GlobalDecl(MDecl); + + MangleContext *MC = Context.createMangleContext(); + MC->mangleName(GDecl, MangledStream); + return Mangled; +} + +bool lldb_rpc_gen::TypeIsFromLLDBPrivate(QualType T) { + + auto CheckTypeForLLDBPrivate = [](const Type *Ty) { + if (!Ty) + return false; + const auto *CXXRDecl = Ty->getAsCXXRecordDecl(); + if (!CXXRDecl) + return false; + const auto *NSDecl = + llvm::dyn_cast(CXXRDecl->getDeclContext()); + if (!NSDecl) + return false; + return NSDecl->getName() == "lldb_private"; + }; + + // First, get the underlying type (remove qualifications and strip off any + // pointers/references). Then we'll need to desugar this type. This will + // remove things like typedefs, so instead of seeing "lldb::DebuggerSP" we'll + // actually see something like "std::shared_ptr". + QualType UnqualifiedUnderlyingType = GetUnqualifiedUnderlyingType(T); + const Type *DesugaredType = + UnqualifiedUnderlyingType->getUnqualifiedDesugaredType(); + assert(DesugaredType && "DesugaredType from a valid Type is nullptr!"); + + // Check the type itself. + if (CheckTypeForLLDBPrivate(DesugaredType)) + return true; + + // If that didn't work, it's possible that the type has a template argument + // that is an lldb_private type. + if (const auto *TemplateSDecl = + llvm::dyn_cast_or_null( + DesugaredType->getAsCXXRecordDecl())) { + for (const TemplateArgument &TA : + TemplateSDecl->getTemplateArgs().asArray()) { + if (TA.getKind() != TemplateArgument::Type) + continue; + if (CheckTypeForLLDBPrivate(TA.getAsType().getTypePtr())) + return true; + } + } + return false; +} + +bool lldb_rpc_gen::TypeIsSBClass(QualType T) { + QualType UnqualifiedUnderlyingType = GetUnqualifiedUnderlyingType(T); + const auto *CXXRDecl = UnqualifiedUnderlyingType->getAsCXXRecordDecl(); + if (!CXXRDecl) + return false; // SB Classes are always C++ classes + + return CXXRDecl->getName().starts_with("SB"); +} + +bool lldb_rpc_gen::TypeIsConstCharPtr(QualType T) { + if (!T->isPointerType()) + return false; + + QualType UnderlyingType = T->getPointeeType(); + if (!UnderlyingType.isConstQualified()) + return false; + + // FIXME: We should be able to do `UnderlyingType->isCharType` but that will + // return true for `const uint8_t *` since that is effectively an unsigned + // char pointer. We currently do not support pointers other than `const char + // *` and `const char **`. + return UnderlyingType->isSpecificBuiltinType(BuiltinType::Char_S) || + UnderlyingType->isSpecificBuiltinType(BuiltinType::SChar); +} + +bool lldb_rpc_gen::TypeIsConstCharPtrPtr(QualType T) { + if (!T->isPointerType()) + return false; + + return TypeIsConstCharPtr(T->getPointeeType()); +} + +bool lldb_rpc_gen::TypeIsDisallowedClass(QualType T) { + QualType UUT = GetUnqualifiedUnderlyingType(T); + const auto *CXXRDecl = UUT->getAsCXXRecordDecl(); + if (!CXXRDecl) + return false; + + llvm::StringRef DeclName = CXXRDecl->getName(); + for (const llvm::StringRef DisallowedClass : DisallowedClasses) + if (DeclName == DisallowedClass) + return true; + return false; +} + +bool lldb_rpc_gen::TypeIsCallbackFunctionPointer(QualType T) { + return T->isFunctionPointerType(); +} + +bool lldb_rpc_gen::MethodIsDisallowed(ASTContext &Context, + CXXMethodDecl *MDecl) { + std::string MangledName = lldb_rpc_gen::GetMangledName(Context, MDecl); + if (llvm::is_contained(DisallowedMethods, MangledName)) + return true; + + return MDecl->isDeprecatedInAnyTargetPlatform(); +} + +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; + continue; + } + + if ((*Iter)->getType()->isVoidPointerType()) + HasBatonParameter = true; + } + + return HasCallbackParameter && HasBatonParameter; +} + +// FIXME: Find a better way to do this. Here is why it is written this way: +// By the time we have already created a `Method` object, we have extracted the +// `QualifiedName` and the relevant QualTypes for parameters/return types, many +// of which contains "lldb::" in them. To change it in a way that would be +// friendly to liblldbrpc, we would need to have a way of replacing that +// namespace at the time of creating a Method, and only for liblldbrpc methods. +// IMO this would complicate Method more than what I'm doing here, and not +// necessarily for any more benefit. +// In clang-tools-extra, there is a ChangeNamespaces tool which tries to do +// something similar to this. It also operates primarily on string replacement, +// but uses more sophisticated clang tooling to do so. +// For now, this will do what we need it to do. +std::string +lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace(std::string Name) { + auto Pos = Name.find("lldb::"); + while (Pos != std::string::npos) { + constexpr size_t SizeOfLLDBNamespace = 4; + Name.replace(Pos, SizeOfLLDBNamespace, "lldb_rpc"); + Pos = Name.find("lldb::"); + } + return Name; +} + +std::string lldb_rpc_gen::StripLLDBNamespace(std::string Name) { + auto Pos = Name.find("lldb::"); + if (Pos != std::string::npos) { + constexpr size_t SizeOfLLDBNamespace = 6; + Name = Name.substr(Pos + SizeOfLLDBNamespace); + } + return Name; +} + +bool lldb_rpc_gen::SBClassRequiresDefaultCtor(const std::string &ClassName) { + return !llvm::is_contained(ClassesWithoutDefaultCtor, ClassName); +} + +bool lldb_rpc_gen::SBClassRequiresCopyCtorAssign(const std::string &ClassName) { + return !llvm::is_contained(ClassesWithoutCopyOperations, ClassName); +} + +bool lldb_rpc_gen::SBClassInheritsFromObjectRef(const std::string &ClassName) { + return llvm::is_contained(ClassesThatInheritFromObjectRef, ClassName); +} + +std::string lldb_rpc_gen::GetSBClassNameFromType(QualType T) { + assert(lldb_rpc_gen::TypeIsSBClass(T) && + "Cannot get SBClass name from non-SB class type!"); + + QualType UnqualifiedUnderlyingType = GetUnqualifiedUnderlyingType(T); + const auto *CXXRDecl = UnqualifiedUnderlyingType->getAsCXXRecordDecl(); + assert(CXXRDecl && "SB class was not CXXRecordDecl!"); + if (!CXXRDecl) + return std::string(); + + return CXXRDecl->getName().str(); +} +lldb_rpc_gen::Method::Method(CXXMethodDecl *MDecl, const PrintingPolicy &Policy, + ASTContext &Context) + : Policy(Policy), Context(Context), + QualifiedName(MDecl->getQualifiedNameAsString()), + BaseName(MDecl->getNameAsString()), + MangledName(lldb_rpc_gen::GetMangledName(Context, MDecl)), + ReturnType(MDecl->getReturnType()), IsConst(MDecl->isConst()), + IsInstance(MDecl->isInstance()), IsCtor(isa(MDecl)), + IsCopyAssign(MDecl->isCopyAssignmentOperator()), + IsMoveAssign(MDecl->isMoveAssignmentOperator()), + IsDtor(isa(MDecl)), + IsConversionMethod(isa(MDecl)) { + uint8_t UnnamedArgIdx = 0; + bool PrevParamWasPointer = false; + for (const auto *ParamDecl : MDecl->parameters()) { + Param param; + if (ParamDecl->hasDefaultArg()) + param.DefaultValueText = + Lexer::getSourceText( + CharSourceRange::getTokenRange( + ParamDecl->getDefaultArg()->getSourceRange()), + Context.getSourceManager(), Context.getLangOpts()) + .str(); + + param.IsFollowedByLen = false; + param.Name = ParamDecl->getNameAsString(); + // If the parameter has no name, we'll generate one + if (param.Name.empty()) { + param.Name = "arg" + std::to_string(UnnamedArgIdx); + UnnamedArgIdx++; + } + param.Type = ParamDecl->getType(); + + // FIXME: Instead of using this heuristic, the ideal thing would be to add + // annotations to the SBAPI methods themselves. For now, we have a list of + // methods that we know will need this. + if (PrevParamWasPointer) { + PrevParamWasPointer = false; + const bool IsIntegerType = param.Type->isIntegerType() && + !param.Type->isBooleanType() && + !param.Type->isEnumeralType(); + if (IsIntegerType && llvm::is_contained(MethodsWithPointerPlusLen, + llvm::StringRef(MangledName))) + Params.back().IsFollowedByLen = true; + } + + if (param.Type->isPointerType() && + !lldb_rpc_gen::TypeIsConstCharPtr(param.Type) && + !param.Type->isFunctionPointerType()) + PrevParamWasPointer = true; + + if (param.Type->isFunctionPointerType()) + ContainsFunctionPointerParameter = true; + + Params.push_back(param); + } + + if (IsInstance) + ThisType = MDecl->getThisType(); + + if (const auto *CtorDecl = dyn_cast(MDecl)) { + IsExplicitCtorOrConversionMethod = CtorDecl->isExplicit(); + IsCopyCtor = CtorDecl->isCopyConstructor(); + IsMoveCtor = CtorDecl->isMoveConstructor(); + } else if (const auto *ConversionDecl = dyn_cast(MDecl)) + IsExplicitCtorOrConversionMethod = ConversionDecl->isExplicit(); +} + +bool lldb_rpc_gen::Method::operator<(const lldb_rpc_gen::Method &rhs) const { + return this < &rhs; +} + +std::string +lldb_rpc_gen::Method::CreateParamListAsString(GenerationKind Generation, + bool IncludeDefaultValue) const { + assert((!IncludeDefaultValue || Generation == eLibrary) && + "Default values should only be emitted on the library side!"); + + std::vector ParamList; + + if (Generation == eLibrary && RequiresConnectionParameter()) + ParamList.push_back("const rpc::Connection &connection"); + + for (const auto &Param : Params) { + std::string ParamString; + llvm::raw_string_ostream ParamStringStream(ParamString); + + if (Generation == eLibrary) + ParamStringStream << lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace( + Param.Type.getAsString(Policy)); + else + ParamStringStream << Param.Type.getAsString(Policy); + + ParamStringStream << " " << Param.Name; + if (IncludeDefaultValue && Generation == eLibrary && + !Param.DefaultValueText.empty()) + ParamStringStream << " = " + << lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace( + Param.DefaultValueText); + + ParamList.push_back(ParamString); + } + + return llvm::join(ParamList, ", "); +} + +bool lldb_rpc_gen::Method::RequiresConnectionParameter() const { + if (llvm::is_contained(MethodsThatUnconditionallyDoNotNeedConnection, + MangledName)) { + return false; + } + if (!IsCtor && IsInstance) + return false; + if (IsCopyCtor || IsMoveCtor) + return false; + for (const auto &Param : Params) + // We can re-use the connection from our parameter if possible. + // Const-qualified parameters are input parameters and already + // have a valid connection to provide to the current method. + if (TypeIsSBClass(Param.Type) && + GetUnderlyingType(Param.Type).isConstQualified()) + return false; + + return true; +} + +std::string lldb_rpc_gen::GetDefaultArgumentsForConstructor( + std::string ClassName, const lldb_rpc_gen::Method &method) { + + std::string ParamString; + + const llvm::SmallVector &ParamTypes = + ClassName_to_ParameterTypes[ClassName]; + std::vector Params; + + Params.push_back("connection_sp"); + for (auto &ParamType : ParamTypes) { + if (ParamType == "const char *") + Params.push_back("nullptr"); + else if (ParamType == "bool") + Params.push_back("false"); + else if (ParamType.starts_with("SB")) { + // If the class to construct takes an SB parameter, + // go over the parameters from the method itself and + // see if it one of its parameters is that SB class. + // If not, see if we can use the method's class itself. + for (auto &CallingMethodParam : method.Params) { + QualType UUT = GetUnqualifiedUnderlyingType(CallingMethodParam.Type); + if (UUT.getAsString() == ParamType) { + Params.push_back(CallingMethodParam.Name); + } else if (GetSBClassNameFromType(method.ThisType) == ParamType) { + Params.push_back("*this"); + break; + } + } + } + } + + ParamString = llvm::join(Params, ", "); + return ParamString; +} diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.h b/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.h new file mode 100644 index 0000000000000..6498a32e137b9 --- /dev/null +++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.h @@ -0,0 +1,107 @@ +//===-- RPCCommon.h -------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_RPC_GEN_RPCCOMMON_H +#define LLDB_RPC_GEN_RPCCOMMON_H + +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclCXX.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace clang; + +namespace lldb_rpc_gen { +QualType GetUnderlyingType(QualType T); +QualType GetUnqualifiedUnderlyingType(QualType T); +std::string GetMangledName(ASTContext &Context, CXXMethodDecl *MDecl); + +bool TypeIsFromLLDBPrivate(QualType T); +bool TypeIsSBClass(QualType T); +bool TypeIsConstCharPtr(QualType T); +bool TypeIsConstCharPtrPtr(QualType T); +bool TypeIsDisallowedClass(QualType T); +bool TypeIsCallbackFunctionPointer(QualType T); + +bool MethodIsDisallowed(ASTContext &Context, CXXMethodDecl *MDecl); +bool HasCallbackParameter(CXXMethodDecl *MDecl); + +std::string ReplaceLLDBNamespaceWithRPCNamespace(std::string Name); +std::string StripLLDBNamespace(std::string Name); +bool SBClassRequiresDefaultCtor(const std::string &ClassName); +bool SBClassRequiresCopyCtorAssign(const std::string &ClassName); +bool SBClassInheritsFromObjectRef(const std::string &ClassName); +std::string GetSBClassNameFromType(QualType T); +struct Param { + std::string Name; + QualType Type; + std::string DefaultValueText; + bool IsFollowedByLen; +}; + +enum GenerationKind : bool { eServer, eLibrary }; + +struct Method { + enum Type { eOther, eConstructor, eDestructor }; + + Method(CXXMethodDecl *MDecl, const PrintingPolicy &Policy, + ASTContext &Context); + + // Adding a '<' allows us to use Methods in ordered containers. + bool operator<(const lldb_rpc_gen::Method &rhs) const; + const PrintingPolicy &Policy; + const ASTContext &Context; + std::string QualifiedName; + std::string BaseName; + std::string MangledName; + QualType ReturnType; + QualType ThisType; + std::vector Params; + bool IsConst = false; + bool IsInstance = false; + bool IsCtor = false; + bool IsCopyCtor = false; + bool IsCopyAssign = false; + bool IsMoveCtor = false; + bool IsMoveAssign = false; + bool IsDtor = false; + bool IsConversionMethod = false; + bool IsExplicitCtorOrConversionMethod = false; + bool ContainsFunctionPointerParameter = false; + + std::string CreateParamListAsString(GenerationKind Generation, + bool IncludeDefaultValue = false) const; + + bool RequiresConnectionParameter() const; +}; + +std::string +GetDefaultArgumentsForConstructor(std::string ClassName, + const lldb_rpc_gen::Method &method); + +class FileEmitter { +protected: + FileEmitter(std::unique_ptr &&OutputFile) + : OutputFile(std::move(OutputFile)), IndentLevel(0) {} + void EmitLine(const std::string &line) { + for (auto i = 0; i < IndentLevel; i++) + OutputFile->os() << " "; + + OutputFile->os() << line << "\n"; + } + + void EmitNewLine() { OutputFile->os() << "\n"; } + + std::unique_ptr OutputFile; + uint8_t IndentLevel; +}; +} // namespace lldb_rpc_gen +#endif // LLDB_RPC_GEN_RPCCOMMON_H diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp b/lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp new file mode 100644 index 0000000000000..f6e729ddf1028 --- /dev/null +++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp @@ -0,0 +1,541 @@ +#include "RPCBindingsHarnessEmitter.h" +#include "RPCClientCallbacksSourceEmitter.h" +#include "RPCCommon.h" +#include "RPCLibraryHeaderEmitter.h" +#include "RPCLibrarySourceEmitter.h" +#include "RPCServerHeaderEmitter.h" +#include "RPCServerSourceEmitter.h" + +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/SourceManager.h" +#include "clang/CodeGen/ObjectFilePCHContainerWriter.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Serialization/ObjectFilePCHContainerReader.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/Tooling.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::driver; +using namespace clang::tooling; + +static llvm::cl::OptionCategory RPCGenCategory("Tool for generating LLDBRPC"); + +static llvm::cl::opt + OutputDir("output-dir", + llvm::cl::desc("Directory to output generated files to"), + llvm::cl::init(""), llvm::cl::cat(RPCGenCategory)); + +static std::string GetLibraryOutputDirectory() { + llvm::SmallString<128> Path(OutputDir.getValue()); + llvm::sys::path::append(Path, "lib"); + return std::string(Path); +} + +static std::string GetServerOutputDirectory() { + llvm::SmallString<128> Path(OutputDir.getValue()); + llvm::sys::path::append(Path, "server"); + return std::string(Path); +} + +static std::unique_ptr +CreateOutputFile(llvm::StringRef OutputDir, llvm::StringRef Filename) { + llvm::SmallString<128> Path(OutputDir); + llvm::sys::path::append(Path, Filename); + + std::error_code EC; + auto OutputFile = + std::make_unique(Path, EC, llvm::sys::fs::OF_None); + if (EC) { + llvm::errs() << "Failed to create output file: " << Path << "!\n"; + return nullptr; + } + return OutputFile; +} + +struct GeneratedByproducts { + std::set ClassNames; + std::set MangledMethodNames; + std::set SkippedMethodNames; + std::set CallbackMethods; +}; + +enum SupportLevel { + eUnsupported, + eUnimplemented, + eImplemented, +}; + +class SBVisitor : public RecursiveASTVisitor { +public: + SBVisitor( + GeneratedByproducts &Byproducts, SourceManager &Manager, + ASTContext &Context, + std::unique_ptr &&ServerMethodOutputFile, + std::unique_ptr &&ServerHeaderOutputFile, + std::unique_ptr &&LibrarySourceOutputFile, + std::unique_ptr &&LibraryHeaderOutputFile, + lldb_rpc_gen::RPCClientCallbacksSourceEmitter &UserClientSourceEmitter, + lldb_rpc_gen::RPCBindingsHarnessEmitter &BindingsHarnessEmitter) + : Byproducts(Byproducts), Manager(Manager), Context(Context), + ServerSourceEmitter(std::move(ServerMethodOutputFile)), + ServerHeaderEmitter(std::move(ServerHeaderOutputFile)), + LibrarySourceEmitter(std::move(LibrarySourceOutputFile)), + LibraryHeaderEmitter(std::move(LibraryHeaderOutputFile)), + ClientCallbacksSourceEmitter(UserClientSourceEmitter), + BindingsHarnessEmitter(BindingsHarnessEmitter) {} + + ~SBVisitor() {} + + bool VisitCXXRecordDecl(CXXRecordDecl *RDecl) { + if (ShouldSkipRecord(RDecl)) + return true; + + const std::string ClassName = RDecl->getNameAsString(); + Byproducts.ClassNames.insert(ClassName); + + // Print 'bool' instead of '_Bool'. + PrintingPolicy Policy(Context.getLangOpts()); + Policy.Bool = true; + + LibraryHeaderEmitter.StartClass(ClassName); + LibrarySourceEmitter.StartClass(ClassName); + BindingsHarnessEmitter.StartClass(ClassName); + for (Decl *D : RDecl->decls()) + if (auto *E = dyn_cast_or_null(D)) + LibraryHeaderEmitter.EmitEnum(E); + + for (CXXMethodDecl *MDecl : RDecl->methods()) { + const std::string MangledName = + lldb_rpc_gen::GetMangledName(Context, MDecl); + const bool IsDisallowed = + lldb_rpc_gen::MethodIsDisallowed(Context, MDecl); + const bool HasCallbackParameter = + lldb_rpc_gen::HasCallbackParameter(MDecl); + SupportLevel MethodSupportLevel = GetMethodSupportLevel(MDecl); + if (MethodSupportLevel == eImplemented && !IsDisallowed) { + const lldb_rpc_gen::Method Method(MDecl, Policy, Context); + ServerSourceEmitter.EmitMethod(Method); + ServerHeaderEmitter.EmitMethod(Method); + LibrarySourceEmitter.EmitMethod(Method); + LibraryHeaderEmitter.EmitMethod(Method); + BindingsHarnessEmitter.EmitMethod(Method); + Byproducts.MangledMethodNames.insert(MangledName); + if (HasCallbackParameter) { + ClientCallbacksSourceEmitter.EmitMethod(Method); + Byproducts.CallbackMethods.insert(Method); + } + } else if (MethodSupportLevel == eUnimplemented) + Byproducts.SkippedMethodNames.insert(MangledName); + } + LibraryHeaderEmitter.EndClass(); + LibrarySourceEmitter.EndClass(); + BindingsHarnessEmitter.EndClass(); + return true; + } + +private: + /// Determines whether we should skip a RecordDecl. + /// Conditions for skipping: + /// - Anything not in the header itself + /// - Certain inconvenient classes + /// - Records without definitions (forward declarations) + bool ShouldSkipRecord(CXXRecordDecl *Decl) { + const Type *DeclType = Decl->getTypeForDecl(); + QualType CanonicalType = DeclType->getCanonicalTypeInternal(); + return !Manager.isInMainFile(Decl->getBeginLoc()) || + !Decl->hasDefinition() || Decl->getDefinition() != Decl || + lldb_rpc_gen::TypeIsDisallowedClass(CanonicalType); + } + + /// Check the support level for a type + /// Known unsupported types: + /// - FILE * (We do not want to expose this primitive) + /// - Types that are internal to LLDB + SupportLevel GetTypeSupportLevel(QualType Type) { + const std::string TypeName = Type.getAsString(); + if (TypeName == "FILE *" || lldb_rpc_gen::TypeIsFromLLDBPrivate(Type)) + return eUnsupported; + + if (lldb_rpc_gen::TypeIsDisallowedClass(Type)) + return eUnsupported; + + return eImplemented; + } + + /// Determine the support level of a given method. + /// Known unsupported methods: + /// - Non-public methods (lldb-rpc is a client and can only see public + /// things) + /// - Copy assignment operators (the client side will handle this) + /// - Move assignment operators (the client side will handle this) + /// - Methods involving unsupported types. + /// Known unimplemented methods: + /// - No variadic functions, e.g. Printf + SupportLevel GetMethodSupportLevel(CXXMethodDecl *MDecl) { + AccessSpecifier AS = MDecl->getAccess(); + if (AS != AccessSpecifier::AS_public) + return eUnsupported; + if (MDecl->isCopyAssignmentOperator()) + return eUnsupported; + if (MDecl->isMoveAssignmentOperator()) + return eUnsupported; + + if (MDecl->isVariadic()) + return eUnimplemented; + + SupportLevel ReturnTypeLevel = GetTypeSupportLevel(MDecl->getReturnType()); + if (ReturnTypeLevel != eImplemented) + return ReturnTypeLevel; + + for (auto *ParamDecl : MDecl->parameters()) { + SupportLevel ParamTypeLevel = GetTypeSupportLevel(ParamDecl->getType()); + if (ParamTypeLevel != eImplemented) + return ParamTypeLevel; + } + + // FIXME: If a callback does not take a `void *baton` parameter, it is + // considered unsupported at this time. On the server-side, we hijack the + // baton argument in order to pass additional information to the server-side + // callback so we can correctly perform a reverse RPC call back to the + // client. Without this baton, we would need the server-side callback to + // have some side channel by which it obtained that information, and + // spending time designing that doesn't outweight the cost of doing it at + // the moment. + bool HasCallbackParameter = false; + bool HasBatonParameter = false; + auto End = MDecl->parameters().end(); + for (auto Iter = MDecl->parameters().begin(); Iter != End; Iter++) { + if ((*Iter)->getType()->isFunctionPointerType()) { + HasCallbackParameter = true; + continue; + } + + // FIXME: We assume that if we have a function pointer and a void pointer + // together in the same parameter list, that it is not followed by a + // length argument. If that changes, we will need to revisit this + // implementation. + if ((*Iter)->getType()->isVoidPointerType()) + HasBatonParameter = true; + } + + if (HasCallbackParameter && !HasBatonParameter) + return eUnimplemented; + + return eImplemented; + } + + GeneratedByproducts &Byproducts; + SourceManager &Manager; + ASTContext &Context; + lldb_rpc_gen::RPCServerSourceEmitter ServerSourceEmitter; + lldb_rpc_gen::RPCServerHeaderEmitter ServerHeaderEmitter; + lldb_rpc_gen::RPCLibrarySourceEmitter LibrarySourceEmitter; + lldb_rpc_gen::RPCLibraryHeaderEmitter LibraryHeaderEmitter; + lldb_rpc_gen::RPCClientCallbacksSourceEmitter &ClientCallbacksSourceEmitter; + lldb_rpc_gen::RPCBindingsHarnessEmitter &BindingsHarnessEmitter; +}; + +class SBConsumer : public ASTConsumer { +public: + SBConsumer(GeneratedByproducts &Byproducts, SourceManager &Manager, + ASTContext &Context, + std::unique_ptr &&ServerMethodOutputFile, + std::unique_ptr &&ServerHeaderOutputFile, + std::unique_ptr &&LibrarySourceOutputFile, + std::unique_ptr &&LibraryHeaderOutputFile, + lldb_rpc_gen::RPCClientCallbacksSourceEmitter + &ClientCallbacksSourceEmitter, + lldb_rpc_gen::RPCBindingsHarnessEmitter &BindingsHarnessEmitter) + : Visitor(Byproducts, Manager, Context, std::move(ServerMethodOutputFile), + std::move(ServerHeaderOutputFile), + std::move(LibrarySourceOutputFile), + std::move(LibraryHeaderOutputFile), + ClientCallbacksSourceEmitter, BindingsHarnessEmitter) {} + bool HandleTopLevelDecl(DeclGroupRef DR) override { + for (Decl *D : DR) + Visitor.TraverseDecl(D); + + return true; + } + +private: + SBVisitor Visitor; +}; + +class SBAction : public ASTFrontendAction { +public: + SBAction( + GeneratedByproducts &Byproducts, + lldb_rpc_gen::RPCBindingsHarnessEmitter &BindingsHarnessEmitter, + lldb_rpc_gen::RPCClientCallbacksSourceEmitter &UserClientSourceEmitter) + : Byproducts(Byproducts), BindingsHarnessEmitter(BindingsHarnessEmitter), + ClientCallbacksSourceEmitter(UserClientSourceEmitter) {} + + std::unique_ptr + CreateASTConsumer(CompilerInstance &CI, llvm::StringRef File) override { + llvm::StringRef FilenameNoExt = + llvm::sys::path::stem(llvm::sys::path::filename(File)); + + const std::string ServerMethodFilename = + "Server_" + FilenameNoExt.str() + ".cpp"; + std::unique_ptr ServerMethodOutputFile = + CreateOutputFile(GetServerOutputDirectory(), ServerMethodFilename); + if (!ServerMethodOutputFile) + return nullptr; + + const std::string ServerHeaderFilename = + "Server_" + FilenameNoExt.str() + ".h"; + std::unique_ptr ServerHeaderOutputFile = + CreateOutputFile(GetServerOutputDirectory(), ServerHeaderFilename); + if (!ServerHeaderOutputFile) + return nullptr; + + const std::string LibrarySourceFilename = FilenameNoExt.str() + ".cpp"; + std::unique_ptr LibrarySourceOutputFile = + CreateOutputFile(GetLibraryOutputDirectory(), LibrarySourceFilename); + if (!LibrarySourceOutputFile) + return nullptr; + + const std::string LibraryHeaderFilename = FilenameNoExt.str() + ".h"; + std::unique_ptr LibraryHeaderOutputFile = + CreateOutputFile(GetLibraryOutputDirectory(), LibraryHeaderFilename); + if (!LibraryHeaderOutputFile) + return nullptr; + + ServerMethodOutputFile->keep(); + ServerHeaderOutputFile->keep(); + LibrarySourceOutputFile->keep(); + LibraryHeaderOutputFile->keep(); + return std::make_unique( + Byproducts, CI.getSourceManager(), CI.getASTContext(), + std::move(ServerMethodOutputFile), std::move(ServerHeaderOutputFile), + std::move(LibrarySourceOutputFile), std::move(LibraryHeaderOutputFile), + ClientCallbacksSourceEmitter, BindingsHarnessEmitter); + } + +private: + GeneratedByproducts &Byproducts; + lldb_rpc_gen::RPCBindingsHarnessEmitter &BindingsHarnessEmitter; + lldb_rpc_gen::RPCClientCallbacksSourceEmitter &ClientCallbacksSourceEmitter; +}; + +class SBActionFactory : public FrontendActionFactory { +public: + SBActionFactory( + GeneratedByproducts &Byproducts, + lldb_rpc_gen::RPCBindingsHarnessEmitter &BindingsHarnessEmitter, + lldb_rpc_gen::RPCClientCallbacksSourceEmitter + &ClientCallbacksSourceEmitter) + : Byproducts(Byproducts), BindingsHarnessEmitter(BindingsHarnessEmitter), + ClientCallbacksSourceEmitter(ClientCallbacksSourceEmitter) {} + + std::unique_ptr create() override { + return std::make_unique(Byproducts, BindingsHarnessEmitter, + ClientCallbacksSourceEmitter); + } + +private: + GeneratedByproducts &Byproducts; + lldb_rpc_gen::RPCBindingsHarnessEmitter &BindingsHarnessEmitter; + lldb_rpc_gen::RPCClientCallbacksSourceEmitter &ClientCallbacksSourceEmitter; +}; + +bool EmitAmalgamatedServerHeader(const std::vector &Files) { + // Create the file + static constexpr llvm::StringLiteral AmalgamatedServerHeaderName = "SBAPI.h"; + std::unique_ptr AmalgamatedServerHeader = + CreateOutputFile(GetServerOutputDirectory(), AmalgamatedServerHeaderName); + if (!AmalgamatedServerHeader) + return false; + + // Write the header + AmalgamatedServerHeader->os() + << "#ifndef GENERATED_LLDB_RPC_SERVER_SBAPI_H\n"; + AmalgamatedServerHeader->os() + << "#define GENERATED_LLDB_RPC_SERVER_SBAPI_H\n"; + for (const auto &File : Files) { + llvm::StringRef FilenameNoExt = + llvm::sys::path::stem(llvm::sys::path::filename(File)); + const std::string ServerHeaderFilename = + "Server_" + FilenameNoExt.str() + ".h"; + + AmalgamatedServerHeader->os() + << "#include \"" + ServerHeaderFilename + "\"\n"; + } + AmalgamatedServerHeader->os() << "#include \"SBAPIExtensions.h\"\n"; + AmalgamatedServerHeader->os() + << "#endif // GENERATED_LLDB_RPC_SERVER_SBAPI_H\n"; + AmalgamatedServerHeader->keep(); + return true; +} + +bool EmitAmalgamatedLibraryHeader(const std::vector &Files) { + static constexpr llvm::StringLiteral AmalgamatedLibraryHeaderName = + "LLDBRPC.h"; + std::unique_ptr AmalgamatedLibraryHeader = + CreateOutputFile(GetLibraryOutputDirectory(), + AmalgamatedLibraryHeaderName); + if (!AmalgamatedLibraryHeader) + return false; + + AmalgamatedLibraryHeader->os() << "#ifndef LLDBRPC_H\n"; + AmalgamatedLibraryHeader->os() << "#define LLDBRPC_H\n"; + AmalgamatedLibraryHeader->os() << "#include \"SBLanguages.h\"\n"; + for (const auto &File : Files) { + llvm::StringRef Filename = llvm::sys::path::filename(File); + AmalgamatedLibraryHeader->os() << "#include \"" << Filename << "\"\n"; + } + + AmalgamatedLibraryHeader->os() << "#endif // LLDBRPC_H\n"; + AmalgamatedLibraryHeader->keep(); + return true; +} + +bool EmitClassNamesFile(std::set &ClassNames) { + static constexpr llvm::StringLiteral ClassNamesFileName = "SBClasses.def"; + std::unique_ptr ClassNamesFile = + CreateOutputFile(OutputDir.getValue(), ClassNamesFileName); + if (!ClassNamesFile) + return false; + + ClassNamesFile->os() << "#ifndef SBCLASS\n"; + ClassNamesFile->os() << "#error \"SBClass must be defined\"\n"; + ClassNamesFile->os() << "#endif\n"; + + for (const auto &ClassName : ClassNames) { + if (ClassName == "SBStream" || ClassName == "SBProgress") + ClassNamesFile->os() << "#if !defined(SBCLASS_EXCLUDE_NONCOPYABLE)\n"; + else if (ClassName == "SBReproducer") + ClassNamesFile->os() << "#if !defined(SBCLASS_EXCLUDE_STATICONLY)\n"; + + ClassNamesFile->os() << "SBCLASS(" << ClassName << ")\n"; + if (ClassName == "SBStream" || ClassName == "SBReproducer" || + ClassName == "SBProgress") + ClassNamesFile->os() << "#endif\n"; + } + ClassNamesFile->keep(); + return true; +} + +bool EmitMethodNamesFile(std::set &MangledMethodNames) { + static constexpr llvm::StringLiteral MethodNamesFileName = "SBAPI.def"; + std::unique_ptr MethodNamesFile = + CreateOutputFile(OutputDir.getValue(), MethodNamesFileName); + if (!MethodNamesFile) + return false; + + MethodNamesFile->os() << "#ifndef GENERATE_SBAPI\n"; + MethodNamesFile->os() << "#error \"GENERATE_SBAPI must be defined\"\n"; + MethodNamesFile->os() << "#endif\n"; + + for (const auto &MangledName : MangledMethodNames) { + MethodNamesFile->os() << "GENERATE_SBAPI(" << MangledName << ")\n"; + } + MethodNamesFile->keep(); + return true; +} + +bool EmitSkippedMethodsFile(std::set &SkippedMethodNames) { + static constexpr llvm::StringLiteral FileName = "SkippedMethods.txt"; + std::unique_ptr File = + CreateOutputFile(OutputDir.getValue(), FileName); + if (!File) + return false; + + for (const auto &Skipped : SkippedMethodNames) { + File->os() << Skipped << "\n"; + } + File->keep(); + return true; +} + +int main(int argc, const char *argv[]) { + auto ExpectedParser = CommonOptionsParser::create( + argc, argv, RPCGenCategory, llvm::cl::OneOrMore, + "Tool for generating LLDBRPC interfaces and implementations"); + + if (!ExpectedParser) { + llvm::errs() << ExpectedParser.takeError(); + return 1; + } + + if (OutputDir.empty()) { + llvm::errs() << "Please specify an output directory for the generated " + "files with --output-dir!\n"; + return 1; + } + + CommonOptionsParser &OP = ExpectedParser.get(); + auto PCHOpts = std::make_shared(); + PCHOpts->registerWriter(std::make_unique()); + PCHOpts->registerReader(std::make_unique()); + + ClangTool T(OP.getCompilations(), OP.getSourcePathList(), PCHOpts); + + if (!EmitAmalgamatedServerHeader(OP.getSourcePathList())) { + llvm::errs() << "Failed to create amalgamated server header\n"; + return 1; + } + + if (!EmitAmalgamatedLibraryHeader(OP.getSourcePathList())) { + llvm::errs() << "Failed to create amalgamated library header\n"; + return 1; + } + + GeneratedByproducts Byproducts; + + constexpr llvm::StringLiteral BindingsHarnessFilename = "lldb.py"; + std::unique_ptr BindingsHarnessOutputFile = + CreateOutputFile(OutputDir.getValue(), BindingsHarnessFilename); + + if (!BindingsHarnessOutputFile) { + llvm::errs() << "Failed to create the bindings harness file\n"; + return 1; + } + BindingsHarnessOutputFile->keep(); + lldb_rpc_gen::RPCBindingsHarnessEmitter BindingsHarnessEmitter( + std::move(BindingsHarnessOutputFile)); + + static constexpr llvm::StringLiteral FileName = "RPCClientSideCallbacks.cpp"; + std::unique_ptr &&UserClientSourceOutputFile = + CreateOutputFile(GetLibraryOutputDirectory(), FileName); + if (!UserClientSourceOutputFile) { + llvm::errs() << "Failed to create the user client callbacks source file\n"; + return 1; + } + + UserClientSourceOutputFile->keep(); + lldb_rpc_gen::RPCClientCallbacksSourceEmitter ClientCallbacksSourceEmitter( + std::move(UserClientSourceOutputFile)); + + SBActionFactory Factory(Byproducts, BindingsHarnessEmitter, + ClientCallbacksSourceEmitter); + auto Result = T.run(&Factory); + ClientCallbacksSourceEmitter.EmitRPCClientInitialize( + Byproducts.CallbackMethods); + if (!EmitClassNamesFile(Byproducts.ClassNames)) { + llvm::errs() << "Failed to create SB Class file\n"; + return 1; + } + if (!EmitMethodNamesFile(Byproducts.MangledMethodNames)) { + llvm::errs() << "Failed to create Method Names file\n"; + return 1; + } + if (!EmitSkippedMethodsFile(Byproducts.SkippedMethodNames)) { + llvm::errs() << "Failed to create Skipped Methods file\n"; + return 1; + } + + return Result; +} From lldb-commits at lists.llvm.org Fri May 9 13:54:13 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 13:54:13 -0700 (PDT) Subject: [Lldb-commits] [lldb] Support stepping through Darwin "branch islands" (PR #139301) In-Reply-To: Message-ID: <681e6b75.170a0220.1761f3.a3b2@mx.google.com> jimingham wrote: No, this change wasn't sufficient. There's something in the symbol table emitted on the tools on the builder that causes lldb to miscalculate the extent of the "spacing" such that its range encompasses the branch island symbols. That's why when we stop at the branch island, the stop address is attributed to the padding symbol that's at the beginning of the section and not the islands that the linker inserted at the end of this space. https://github.com/llvm/llvm-project/pull/139301 From lldb-commits at lists.llvm.org Fri May 9 14:12:26 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Fri, 09 May 2025 14:12:26 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream lldb-rpc-gen tool (PR #138031) In-Reply-To: Message-ID: <681e6fba.170a0220.bd6f.d3c3@mx.google.com> ================ @@ -0,0 +1,535 @@ +//===-- 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/Mangle.h" +#include "clang/Lex/Lexer.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +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", // What is this used for? + "SBInputReader", // What is this used for? + "SBCommandPluginInterface", // This is hard to support, we can do it if + // really needed though. + "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. +}; + +// We intentionally avoid generating certain methods either because they are +// difficult to support correctly or they aren't really used much from C++. +// FIXME: We should be able to annotate these methods instead of maintaining a +// list in the generator itself. +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", ---------------- bulbazord wrote: lldb-rpc-gen should not satisfy `defined(SWIG)` so you should have access to the `[[deprecated]]` annotation. https://github.com/llvm/llvm-project/pull/138031 From lldb-commits at lists.llvm.org Fri May 9 14:17:51 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Fri, 09 May 2025 14:17:51 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Fix FindProcessImpl() for iOS simulators (PR #139174) In-Reply-To: Message-ID: <681e70ff.170a0220.37cf9a.9cf2@mx.google.com> JDevlieghere wrote: > > Could we test this in `TestSimulatorPlatform.py`? > > @JDevlieghere Thanks for pointer. It seems the tests in that file are all **skipped** because of this bug number: `rdar://76995109`. > > E.g. > > ``` > UNSUPPORTED: LLDB (/Users/royshi/public_llvm/build/bin/clang-arm64) :: test_ios (TestSimulatorPlatform.TestSimulatorPlatformLaunching) (skipping unconditionally [rdar://76995109]) > ``` > > Did a bit internet search and couldn't find how to find more info about this bug or why these tests are all skipped. Not sure if I should un-skip them. Ha, that's my radar, and it's no longer relevant. I bisected an issue with the test suite to that particular test, but the last comment says that it wasn't the culprit after all, so there's no reason it should still be disabled. https://github.com/llvm/llvm-project/pull/139174 From lldb-commits at lists.llvm.org Fri May 9 14:25:18 2025 From: lldb-commits at lists.llvm.org (Igor Kudrin via lldb-commits) Date: Fri, 09 May 2025 14:25:18 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][core] Fix getting summary of a variable pointing to r/o memory (PR #139196) In-Reply-To: Message-ID: <681e72be.170a0220.100a0f.a3ce@mx.google.com> ================ @@ -735,14 +735,25 @@ size_t ValueObject::GetPointeeData(DataExtractor &data, uint32_t item_idx, case eAddressTypeLoad: { ExecutionContext exe_ctx(GetExecutionContextRef()); Process *process = exe_ctx.GetProcessPtr(); - if (process) { + if (process && process->IsLiveDebugSession()) { heap_buf_ptr->SetByteSize(bytes); size_t bytes_read = process->ReadMemory( addr + offset, heap_buf_ptr->GetBytes(), bytes, error); if (error.Success() || bytes_read > 0) { data.SetData(data_sp); return bytes_read; } + } else if (Target *target = exe_ctx.GetTargetPtr()) { + Address target_addr; + target_addr.SetLoadAddress(addr + offset, target); + heap_buf_ptr->SetByteSize(bytes); + size_t bytes_read = + target->ReadMemory(target_addr, heap_buf_ptr->GetBytes(), bytes, + error, /*force_live_memory=*/true); ---------------- igorkudrin wrote: The difference is that with `force_live_memory==true`, `Target::ReadMemory()` tries to get the data from the process first, and resorts to reading from the file cache if that fails. With `force_live_memory==false` and if the data is in a read-only section, it reads from the file cache into a temporary buffer, and then calls `Process::ReadMemory()` anyway. So, with either setting, it prefers the data from the process, but `force_live_memory==true` seems to be just a bit more efficient. https://github.com/llvm/llvm-project/pull/139196 From lldb-commits at lists.llvm.org Fri May 9 14:34:42 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 14:34:42 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][TypeSystemClang] Allow arrays to be dereferenced in C/C++. (PR #135843) In-Reply-To: Message-ID: <681e74f2.170a0220.1d5bdb.8b28@mx.google.com> https://github.com/jimingham approved this pull request. LGTM https://github.com/llvm/llvm-project/pull/135843 From lldb-commits at lists.llvm.org Fri May 9 14:35:05 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Fri, 09 May 2025 14:35:05 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Minor visual changes to the modules UI (PR #139328) Message-ID: https://github.com/JDevlieghere created https://github.com/llvm/llvm-project/pull/139328 Small assortment of changes to the modules UI after trying it out: - Print the load address as hexadecimal. - Remove spurious space before colon. - Drop "Module" prefix from tooltip title. - Capitalize bold list items. Screenshot 2025-05-09 at 2 34 10 PM Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Fri May 9 14:35:43 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 14:35:43 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Minor visual changes to the modules UI (PR #139328) In-Reply-To: Message-ID: <681e752f.170a0220.17617b.a0dc@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Jonas Devlieghere (JDevlieghere)
Changes Small assortment of changes to the modules UI after trying it out: - Print the load address as hexadecimal. - Remove spurious space before colon. - Drop "Module" prefix from tooltip title. - Capitalize bold list items. <img width="817" alt="Screenshot 2025-05-09 at 2 34 10 PM" src="https://github.com/user-attachments/assets/5f902dae-87f2-4716-92f4-27e9dd3f6b37" /> --- Full diff: https://github.com/llvm/llvm-project/pull/139328.diff 1 Files Affected: - (modified) lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts (+9-7) ``````````diff diff --git a/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts b/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts index 5af3d52e9870c..07b89b267b536 100644 --- a/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts +++ b/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts @@ -25,23 +25,25 @@ export class ModulesDataProvider } const tooltip = new vscode.MarkdownString(); - tooltip.appendMarkdown(`# Module '${module.name}'\n\n`); - tooltip.appendMarkdown(`- **id** : ${module.id}\n`); + tooltip.appendMarkdown(`# ${module.name}\n\n`); + tooltip.appendMarkdown(`- **ID** : ${module.id}\n`); if (module.addressRange) { - tooltip.appendMarkdown(`- **load address** : ${module.addressRange}\n`); + tooltip.appendMarkdown( + `- **Load address**: 0x${Number(module.addressRange).toString(16)}\n`, + ); } if (module.path) { - tooltip.appendMarkdown(`- **path** : ${module.path}\n`); + tooltip.appendMarkdown(`- **Path**: ${module.path}\n`); } if (module.version) { - tooltip.appendMarkdown(`- **version** : ${module.version}\n`); + tooltip.appendMarkdown(`- **Version**: ${module.version}\n`); } if (module.symbolStatus) { - tooltip.appendMarkdown(`- **symbol status** : ${module.symbolStatus}\n`); + tooltip.appendMarkdown(`- **Symbol status**: ${module.symbolStatus}\n`); } if (module.symbolFilePath) { tooltip.appendMarkdown( - `- **symbol file path** : ${module.symbolFilePath}\n`, + `- **Symbol file path**: ${module.symbolFilePath}\n`, ); } ``````````
https://github.com/llvm/llvm-project/pull/139328 From lldb-commits at lists.llvm.org Fri May 9 14:56:26 2025 From: lldb-commits at lists.llvm.org (Igor Kudrin via lldb-commits) Date: Fri, 09 May 2025 14:56:26 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][core] Fix getting summary of a variable pointing to r/o memory (PR #139196) In-Reply-To: Message-ID: <681e7a0a.170a0220.2761a8.8c2c@mx.google.com> ================ @@ -735,14 +735,25 @@ size_t ValueObject::GetPointeeData(DataExtractor &data, uint32_t item_idx, case eAddressTypeLoad: { ExecutionContext exe_ctx(GetExecutionContextRef()); Process *process = exe_ctx.GetProcessPtr(); - if (process) { + if (process && process->IsLiveDebugSession()) { heap_buf_ptr->SetByteSize(bytes); size_t bytes_read = process->ReadMemory( addr + offset, heap_buf_ptr->GetBytes(), bytes, error); if (error.Success() || bytes_read > 0) { data.SetData(data_sp); return bytes_read; } + } else if (Target *target = exe_ctx.GetTargetPtr()) { + Address target_addr; + target_addr.SetLoadAddress(addr + offset, target); + heap_buf_ptr->SetByteSize(bytes); + size_t bytes_read = + target->ReadMemory(target_addr, heap_buf_ptr->GetBytes(), bytes, + error, /*force_live_memory=*/true); + if (error.Success() || bytes_read > 0) { + data.SetData(data_sp); + return bytes_read; + } ---------------- igorkudrin wrote: I think it can still be factored out, even without the `else` branch. The only difference will be that `data` will be updated if we don't have a target, and that seems negligible. Thanks for the idea. https://github.com/llvm/llvm-project/pull/139196 From lldb-commits at lists.llvm.org Fri May 9 15:04:22 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Fri, 09 May 2025 15:04:22 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB][SBSaveCoreOptions] Add new API to expose the expected core size in bytes (PR #138169) In-Reply-To: Message-ID: <681e7be6.170a0220.fb9ee.fd1d@mx.google.com> https://github.com/Jlalond updated https://github.com/llvm/llvm-project/pull/138169 >From ef04502d17c36044cd5fb96f333c328c8215f354 Mon Sep 17 00:00:00 2001 From: Jacob Lalonde Date: Thu, 1 May 2025 10:11:10 -0700 Subject: [PATCH 1/6] Add new API to expose the expected size in bytes of a core before generation --- lldb/include/lldb/API/SBSaveCoreOptions.h | 13 ++++++++++++ lldb/include/lldb/Symbol/SaveCoreOptions.h | 2 ++ lldb/source/API/SBSaveCoreOptions.cpp | 5 +++++ lldb/source/Symbol/SaveCoreOptions.cpp | 18 ++++++++++++++++ .../TestSBSaveCoreOptions.py | 21 +++++++++++++++++++ .../sbsavecoreoptions/basic_minidump.yaml | 5 +++++ 6 files changed, 64 insertions(+) diff --git a/lldb/include/lldb/API/SBSaveCoreOptions.h b/lldb/include/lldb/API/SBSaveCoreOptions.h index c6d2ab6099b3c..4c051353a714e 100644 --- a/lldb/include/lldb/API/SBSaveCoreOptions.h +++ b/lldb/include/lldb/API/SBSaveCoreOptions.h @@ -119,6 +119,19 @@ class LLDB_API SBSaveCoreOptions { /// an empty collection will be returned. SBThreadCollection GetThreadsToSave() const; + /// Get the current total number of bytes the core is expected to be but not + /// including the overhead of the core file format. Requires a Process and + /// Style to be specified. + /// + /// \note + /// This can cause some modification of the underlying data store + /// as regions with no permissions, or invalid permissions will be removed + /// and stacks will be minified up to their stack pointer + the redzone. + /// + /// \returns + /// The expected size of the data contained in the core in bytes. + uint64_t GetCurrentSizeInBytes(SBError &error); + /// Reset all options. void Clear(); diff --git a/lldb/include/lldb/Symbol/SaveCoreOptions.h b/lldb/include/lldb/Symbol/SaveCoreOptions.h index bcf0087fbea5c..319d44a6b0c87 100644 --- a/lldb/include/lldb/Symbol/SaveCoreOptions.h +++ b/lldb/include/lldb/Symbol/SaveCoreOptions.h @@ -49,6 +49,8 @@ class SaveCoreOptions { lldb_private::ThreadCollection::collection GetThreadsToSave() const; + uint64_t GetCurrentSizeInBytes(Status &error); + void Clear(); private: diff --git a/lldb/source/API/SBSaveCoreOptions.cpp b/lldb/source/API/SBSaveCoreOptions.cpp index 35b9da569dfa1..b67df513fe91b 100644 --- a/lldb/source/API/SBSaveCoreOptions.cpp +++ b/lldb/source/API/SBSaveCoreOptions.cpp @@ -114,6 +114,11 @@ void SBSaveCoreOptions::Clear() { m_opaque_up->Clear(); } +uint64_t SBSaveCoreOptions::GetCurrentSizeInBytes(SBError &error) { + LLDB_INSTRUMENT_VA(this, error); + return m_opaque_up->GetCurrentSizeInBytes(error.ref()); +} + lldb_private::SaveCoreOptions &SBSaveCoreOptions::ref() const { return *m_opaque_up.get(); } diff --git a/lldb/source/Symbol/SaveCoreOptions.cpp b/lldb/source/Symbol/SaveCoreOptions.cpp index c9f6efeb25d22..1da3e1cc9f834 100644 --- a/lldb/source/Symbol/SaveCoreOptions.cpp +++ b/lldb/source/Symbol/SaveCoreOptions.cpp @@ -145,6 +145,24 @@ SaveCoreOptions::GetThreadsToSave() const { return thread_collection; } +uint64_t SaveCoreOptions::GetCurrentSizeInBytes(Status &error) { + if (!m_process_sp) { + error = Status::FromErrorString("Requires a process to be set."); + return 0; + } + + CoreFileMemoryRanges ranges; + error = m_process_sp->CalculateCoreFileSaveRanges(*this, ranges); + if (error.Fail()) + return 0; + + uint64_t total_in_bytes = 0; + for (auto& core_range : ranges) + total_in_bytes += core_range.data.range.size(); + + return total_in_bytes; +} + void SaveCoreOptions::ClearProcessSpecificData() { // Deliberately not following the formatter style here to indicate that // this method will be expanded in the future. diff --git a/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py b/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py index ace84e8497a59..215f8440cc68a 100644 --- a/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py +++ b/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py @@ -104,3 +104,24 @@ def test_removing_and_adding_insertion_order(self): thread_collection = options.GetThreadsToSave() self.assertEqual(thread_collection.GetSize(), 3) self.assertIn(middle_thread, thread_collection) + + def test_get_total_in_bytes(self): + """ + Tests that get total in bytes properly returns an error without a process, + and the readable regions with a process. + """ + + options = lldb.SBSaveCoreOptions() + options.SetStyle(lldb.eSaveCoreCustomOnly) + process = self.get_basic_process() + memory_range = lldb.SBMemoryRegionInfo() + process.GetMemoryRegionInfo(0x7FFF12A84030, memory_range) + options.AddMemoryRegionToSave(memory_range) + error = lldb.SBError() + total = options.GetCurrentSizeInBytes(error) + self.assertTrue(error.Fail(), error.GetCString()) + options.SetProcess(process) + total = options.GetCurrentSizeInBytes(error) + self.assertTrue(error.Success(), error.GetCString()) + expected_size = memory_range.GetRegionEnd() - memory_range.GetRegionBase() + self.assertEqual(total, expected_size) diff --git a/lldb/test/API/python_api/sbsavecoreoptions/basic_minidump.yaml b/lldb/test/API/python_api/sbsavecoreoptions/basic_minidump.yaml index 96302fbfb6b5c..5033787019d7b 100644 --- a/lldb/test/API/python_api/sbsavecoreoptions/basic_minidump.yaml +++ b/lldb/test/API/python_api/sbsavecoreoptions/basic_minidump.yaml @@ -34,3 +34,8 @@ Streams: Stack: Start of Memory Range: 0x00007FFFC8DFF000 Content: 'BAADBEEF' + - Type: Memory64List + Memory Ranges: + - Start of Memory Range: 0x7FFF12A84030 + Data Size: 0x2FD0 + Content : '' >From 6f41d701d87dbcd1a09debc490ee954bfc4049c2 Mon Sep 17 00:00:00 2001 From: Jacob Lalonde Date: Thu, 1 May 2025 10:17:00 -0700 Subject: [PATCH 2/6] Add check to EnsureValidConfiguration to filter out some corner cases --- lldb/include/lldb/API/SBSaveCoreOptions.h | 6 +++--- lldb/source/Symbol/SaveCoreOptions.cpp | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lldb/include/lldb/API/SBSaveCoreOptions.h b/lldb/include/lldb/API/SBSaveCoreOptions.h index 4c051353a714e..2066ab3e8246c 100644 --- a/lldb/include/lldb/API/SBSaveCoreOptions.h +++ b/lldb/include/lldb/API/SBSaveCoreOptions.h @@ -120,11 +120,11 @@ class LLDB_API SBSaveCoreOptions { SBThreadCollection GetThreadsToSave() const; /// Get the current total number of bytes the core is expected to be but not - /// including the overhead of the core file format. Requires a Process and + /// including the overhead of the core file format. Requires a Process and /// Style to be specified. - /// + /// /// \note - /// This can cause some modification of the underlying data store + /// This can cause some modification of the underlying data store /// as regions with no permissions, or invalid permissions will be removed /// and stacks will be minified up to their stack pointer + the redzone. /// diff --git a/lldb/source/Symbol/SaveCoreOptions.cpp b/lldb/source/Symbol/SaveCoreOptions.cpp index 1da3e1cc9f834..cf6fc99f7236e 100644 --- a/lldb/source/Symbol/SaveCoreOptions.cpp +++ b/lldb/source/Symbol/SaveCoreOptions.cpp @@ -151,13 +151,17 @@ uint64_t SaveCoreOptions::GetCurrentSizeInBytes(Status &error) { return 0; } + error = EnsureValidConfiguration(m_process_sp); + if (error.Fail()) + return 0; + CoreFileMemoryRanges ranges; error = m_process_sp->CalculateCoreFileSaveRanges(*this, ranges); if (error.Fail()) return 0; uint64_t total_in_bytes = 0; - for (auto& core_range : ranges) + for (auto &core_range : ranges) total_in_bytes += core_range.data.range.size(); return total_in_bytes; >From 53d8c0b4ec3f55db71f5d1aa3c56ebbaa860f807 Mon Sep 17 00:00:00 2001 From: Jacob Lalonde Date: Thu, 1 May 2025 10:20:51 -0700 Subject: [PATCH 3/6] Py formatting --- .../API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py b/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py index 215f8440cc68a..a8a6302d7cf13 100644 --- a/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py +++ b/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py @@ -107,7 +107,7 @@ def test_removing_and_adding_insertion_order(self): def test_get_total_in_bytes(self): """ - Tests that get total in bytes properly returns an error without a process, + Tests that get total in bytes properly returns an error without a process, and the readable regions with a process. """ >From ef3cf723251225f46b4e0bff980b86cd1d05c0bb Mon Sep 17 00:00:00 2001 From: Jacob Lalonde Date: Fri, 2 May 2025 14:36:12 -0700 Subject: [PATCH 4/6] Move to llvm::expected, clean up comments --- lldb/include/lldb/API/SBSaveCoreOptions.h | 4 ++-- lldb/include/lldb/Symbol/SaveCoreOptions.h | 2 +- lldb/source/API/SBSaveCoreOptions.cpp | 9 ++++++++- lldb/source/Symbol/SaveCoreOptions.cpp | 14 +++++++------- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/lldb/include/lldb/API/SBSaveCoreOptions.h b/lldb/include/lldb/API/SBSaveCoreOptions.h index 2066ab3e8246c..37552c13d0f36 100644 --- a/lldb/include/lldb/API/SBSaveCoreOptions.h +++ b/lldb/include/lldb/API/SBSaveCoreOptions.h @@ -119,8 +119,8 @@ class LLDB_API SBSaveCoreOptions { /// an empty collection will be returned. SBThreadCollection GetThreadsToSave() const; - /// Get the current total number of bytes the core is expected to be but not - /// including the overhead of the core file format. Requires a Process and + /// Get the current total number of bytes the core is expected to have + /// excluding the overhead of the core file format. Requires a Process and /// Style to be specified. /// /// \note diff --git a/lldb/include/lldb/Symbol/SaveCoreOptions.h b/lldb/include/lldb/Symbol/SaveCoreOptions.h index 319d44a6b0c87..da66b184745db 100644 --- a/lldb/include/lldb/Symbol/SaveCoreOptions.h +++ b/lldb/include/lldb/Symbol/SaveCoreOptions.h @@ -49,7 +49,7 @@ class SaveCoreOptions { lldb_private::ThreadCollection::collection GetThreadsToSave() const; - uint64_t GetCurrentSizeInBytes(Status &error); + llvm::Expected GetCurrentSizeInBytes(); void Clear(); diff --git a/lldb/source/API/SBSaveCoreOptions.cpp b/lldb/source/API/SBSaveCoreOptions.cpp index b67df513fe91b..410fb673b347a 100644 --- a/lldb/source/API/SBSaveCoreOptions.cpp +++ b/lldb/source/API/SBSaveCoreOptions.cpp @@ -116,7 +116,14 @@ void SBSaveCoreOptions::Clear() { uint64_t SBSaveCoreOptions::GetCurrentSizeInBytes(SBError &error) { LLDB_INSTRUMENT_VA(this, error); - return m_opaque_up->GetCurrentSizeInBytes(error.ref()); + llvm::Expected expected_bytes = m_opaque_up->GetCurrentSizeInBytes(); + if (!expected_bytes) { + error = SBError(lldb_private::Status::FromError(expected_bytes.takeError())); + return 0; + } + // Clear the error, so if the clearer uses it we set it to success. + error.Clear(); + return *expected_bytes; } lldb_private::SaveCoreOptions &SBSaveCoreOptions::ref() const { diff --git a/lldb/source/Symbol/SaveCoreOptions.cpp b/lldb/source/Symbol/SaveCoreOptions.cpp index cf6fc99f7236e..9f4351fd54a7c 100644 --- a/lldb/source/Symbol/SaveCoreOptions.cpp +++ b/lldb/source/Symbol/SaveCoreOptions.cpp @@ -145,20 +145,20 @@ SaveCoreOptions::GetThreadsToSave() const { return thread_collection; } -uint64_t SaveCoreOptions::GetCurrentSizeInBytes(Status &error) { - if (!m_process_sp) { - error = Status::FromErrorString("Requires a process to be set."); - return 0; - } +llvm::Expected SaveCoreOptions::GetCurrentSizeInBytes() { + Status error; + if (!m_process_sp) + return Status::FromErrorString("Requires a process to be set.").takeError(); + error = EnsureValidConfiguration(m_process_sp); if (error.Fail()) - return 0; + return error.takeError(); CoreFileMemoryRanges ranges; error = m_process_sp->CalculateCoreFileSaveRanges(*this, ranges); if (error.Fail()) - return 0; + return error.takeError(); uint64_t total_in_bytes = 0; for (auto &core_range : ranges) >From a82385393fcaea7f472651d2e259005fd406aacb Mon Sep 17 00:00:00 2001 From: Jacob Lalonde Date: Fri, 2 May 2025 14:37:59 -0700 Subject: [PATCH 5/6] Add a new docstring --- lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i | 5 +++++ lldb/source/API/SBSaveCoreOptions.cpp | 8 +++++--- lldb/source/Symbol/SaveCoreOptions.cpp | 1 - 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i b/lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i index 6efbe45d2d3ab..b676a00bd1113 100644 --- a/lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i +++ b/lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i @@ -63,6 +63,11 @@ Note that currently ELF Core files are not supported." Get an SBThreadCollection of all threads marked to be saved. This collection is not sorted according to insertion order." ) lldb::SBSaveCoreOptions::GetThreadsToSave; +%feature("docstring", " + Get the current total number of bytes the core is expectd to have, excluding the overhead of the core file format. + Requires both a Process and a Style to be specified." +) lldb::SBSaveCoreOptions::GetCurrentSizeInBytes; + %feature("docstring", " Unset all options." ) lldb::SBSaveCoreOptions::Clear; diff --git a/lldb/source/API/SBSaveCoreOptions.cpp b/lldb/source/API/SBSaveCoreOptions.cpp index 410fb673b347a..e101f6a25783c 100644 --- a/lldb/source/API/SBSaveCoreOptions.cpp +++ b/lldb/source/API/SBSaveCoreOptions.cpp @@ -116,14 +116,16 @@ void SBSaveCoreOptions::Clear() { uint64_t SBSaveCoreOptions::GetCurrentSizeInBytes(SBError &error) { LLDB_INSTRUMENT_VA(this, error); - llvm::Expected expected_bytes = m_opaque_up->GetCurrentSizeInBytes(); + llvm::Expected expected_bytes = + m_opaque_up->GetCurrentSizeInBytes(); if (!expected_bytes) { - error = SBError(lldb_private::Status::FromError(expected_bytes.takeError())); + error = + SBError(lldb_private::Status::FromError(expected_bytes.takeError())); return 0; } // Clear the error, so if the clearer uses it we set it to success. error.Clear(); - return *expected_bytes; + return *expected_bytes; } lldb_private::SaveCoreOptions &SBSaveCoreOptions::ref() const { diff --git a/lldb/source/Symbol/SaveCoreOptions.cpp b/lldb/source/Symbol/SaveCoreOptions.cpp index 9f4351fd54a7c..e51ae27954934 100644 --- a/lldb/source/Symbol/SaveCoreOptions.cpp +++ b/lldb/source/Symbol/SaveCoreOptions.cpp @@ -150,7 +150,6 @@ llvm::Expected SaveCoreOptions::GetCurrentSizeInBytes() { if (!m_process_sp) return Status::FromErrorString("Requires a process to be set.").takeError(); - error = EnsureValidConfiguration(m_process_sp); if (error.Fail()) return error.takeError(); >From 9ed39146607e19fd45c83d1fa440a0d04333486e Mon Sep 17 00:00:00 2001 From: Jacob Lalonde Date: Fri, 9 May 2025 15:02:52 -0700 Subject: [PATCH 6/6] Implement feedback from Dave --- .../interface/SBSaveCoreOptionsDocstrings.i | 4 +- .../TestSBSaveCoreOptions.py | 45 +++++++++++++++++-- .../sbsavecoreoptions/basic_minidump.yaml | 7 ++- 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i b/lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i index b676a00bd1113..6907164a1b95c 100644 --- a/lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i +++ b/lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i @@ -64,8 +64,8 @@ Note that currently ELF Core files are not supported." ) lldb::SBSaveCoreOptions::GetThreadsToSave; %feature("docstring", " - Get the current total number of bytes the core is expectd to have, excluding the overhead of the core file format. - Requires both a Process and a Style to be specified." + Get the current total number of bytes the core is expected to have, excluding the overhead of the core file format. + Requires both a Process and a Style to be specified. An error will be returned if the provided options would result in no data being saved." ) lldb::SBSaveCoreOptions::GetCurrentSizeInBytes; %feature("docstring", " diff --git a/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py b/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py index a8a6302d7cf13..87e75a5f3a38e 100644 --- a/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py +++ b/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py @@ -105,9 +105,9 @@ def test_removing_and_adding_insertion_order(self): self.assertEqual(thread_collection.GetSize(), 3) self.assertIn(middle_thread, thread_collection) - def test_get_total_in_bytes(self): + def test_get_current_size_in_bytes(self): """ - Tests that get total in bytes properly returns an error without a process, + Tests that ensures GetCurrentSizeInBytes properly returns an error without a process, and the readable regions with a process. """ @@ -115,13 +115,52 @@ def test_get_total_in_bytes(self): options.SetStyle(lldb.eSaveCoreCustomOnly) process = self.get_basic_process() memory_range = lldb.SBMemoryRegionInfo() - process.GetMemoryRegionInfo(0x7FFF12A84030, memory_range) + + # Add the memory range of 0x1000-0x1100 + process.GetMemoryRegionInfo(0x1000, memory_range) options.AddMemoryRegionToSave(memory_range) + + # Check that we fail when we have no process set + # even though we added a memory region. error = lldb.SBError() total = options.GetCurrentSizeInBytes(error) self.assertTrue(error.Fail(), error.GetCString()) + + # Check that we don't get an error now that we've added a process options.SetProcess(process) total = options.GetCurrentSizeInBytes(error) self.assertTrue(error.Success(), error.GetCString()) + + # Validate the size returned is the same size as the single region we added. expected_size = memory_range.GetRegionEnd() - memory_range.GetRegionBase() self.assertEqual(total, expected_size) + + def test_get_total_in_bytes_missing_requirements(self): + """ + Tests the matrix of error responses that GetCurrentSizeInBytes + """ + + options = lldb.SBSaveCoreOptions() + + # No process, no style returns an error. + error = lldb.SBError() + total = options.GetCurrentSizeInBytes(error) + self.assertTrue(error.Fail(), error.GetCString()) + + # No process returns an error + options.SetStyle(lldb.eSaveCoreCustomOnly) + total = options.GetCurrentSizeInBytes(error) + self.assertTrue(error.Fail(), error.GetCString()) + + options.Clear() + + # No style returns an error + process = self.get_basic_process() + options.SetProcess(process) + total = options.GetCurrentSizeInBytes(error) + self.assertTrue(error.Fail(), error.GetCString()) + + # Options that result in no valid data returns an error. + options.SetStyle(lldb.eSaveCoreCustomOnly) + total = options.GetCurrentSizeInBytes(error) + self.assertTrue(error.Fail(), error.GetCString()) diff --git a/lldb/test/API/python_api/sbsavecoreoptions/basic_minidump.yaml b/lldb/test/API/python_api/sbsavecoreoptions/basic_minidump.yaml index 5033787019d7b..e79262b3a85ce 100644 --- a/lldb/test/API/python_api/sbsavecoreoptions/basic_minidump.yaml +++ b/lldb/test/API/python_api/sbsavecoreoptions/basic_minidump.yaml @@ -36,6 +36,9 @@ Streams: Content: 'BAADBEEF' - Type: Memory64List Memory Ranges: - - Start of Memory Range: 0x7FFF12A84030 - Data Size: 0x2FD0 + - Start of Memory Range: 0x1000 + Data Size: 0x100 + Content : '' + - Start of Memory Range: 0x2000 + Data Size: 0x200 Content : '' From lldb-commits at lists.llvm.org Fri May 9 15:05:10 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Fri, 09 May 2025 15:05:10 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB][SBSaveCoreOptions] Add new API to expose the expected core size in bytes (PR #138169) In-Reply-To: Message-ID: <681e7c16.050a0220.cb9fb.d99f@mx.google.com> Jlalond wrote: @dmpots Thanks for the feedback, I implemented several more test cases, and fixed those typos. https://github.com/llvm/llvm-project/pull/138169 From lldb-commits at lists.llvm.org Fri May 9 15:06:05 2025 From: lldb-commits at lists.llvm.org (Ely Ronnen via lldb-commits) Date: Fri, 09 May 2025 15:06:05 -0700 (PDT) Subject: [Lldb-commits] [lldb] [llvm] [lldb-dap] migrate set breakpoint requests (PR #137448) In-Reply-To: Message-ID: <681e7c4d.170a0220.13f7c8.bcee@mx.google.com> https://github.com/eronnen closed https://github.com/llvm/llvm-project/pull/137448 From lldb-commits at lists.llvm.org Fri May 9 15:06:03 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 15:06:03 -0700 (PDT) Subject: [Lldb-commits] [lldb] 8630c22 - [lldb-dap] migrate set breakpoint requests (#137448) Message-ID: <681e7c4b.630a0220.8f157.5613@mx.google.com> Author: Ely Ronnen Date: 2025-05-10T00:05:59+02:00 New Revision: 8630c22083e3ebab5955c0c46caa89b59f283fdb URL: https://github.com/llvm/llvm-project/commit/8630c22083e3ebab5955c0c46caa89b59f283fdb DIFF: https://github.com/llvm/llvm-project/commit/8630c22083e3ebab5955c0c46caa89b59f283fdb.diff LOG: [lldb-dap] migrate set breakpoint requests (#137448) - Migrate set breakpoint requests to use typed RequestHandler - `SetBreakpointsRequestHandler` - `SetDataBreakpointsRequestHandler` - `SetFunctionBreakpointsRequestHandler` - `SetInstructionBreakpointsRequestHandler` - `DataBreakpointInfoRequestHandler` - Decouple JSON from lldb-dap `Breakpoint` classes Added: Modified: lldb/tools/lldb-dap/Breakpoint.cpp lldb/tools/lldb-dap/Breakpoint.h lldb/tools/lldb-dap/BreakpointBase.cpp lldb/tools/lldb-dap/BreakpointBase.h lldb/tools/lldb-dap/DAP.cpp lldb/tools/lldb-dap/DAP.h lldb/tools/lldb-dap/FunctionBreakpoint.cpp lldb/tools/lldb-dap/FunctionBreakpoint.h lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp lldb/tools/lldb-dap/Handler/RequestHandler.h lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp lldb/tools/lldb-dap/Handler/TestGetTargetBreakpointsRequestHandler.cpp lldb/tools/lldb-dap/InstructionBreakpoint.cpp lldb/tools/lldb-dap/InstructionBreakpoint.h lldb/tools/lldb-dap/JSONUtils.cpp lldb/tools/lldb-dap/JSONUtils.h lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp lldb/tools/lldb-dap/Protocol/ProtocolRequests.h lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp lldb/tools/lldb-dap/Protocol/ProtocolTypes.h lldb/tools/lldb-dap/SourceBreakpoint.cpp lldb/tools/lldb-dap/SourceBreakpoint.h lldb/tools/lldb-dap/Watchpoint.cpp lldb/tools/lldb-dap/Watchpoint.h llvm/include/llvm/Support/JSON.h Removed: ################################################################################ diff --git a/lldb/tools/lldb-dap/Breakpoint.cpp b/lldb/tools/lldb-dap/Breakpoint.cpp index 5679fd545d53f..26d633d1d172e 100644 --- a/lldb/tools/lldb-dap/Breakpoint.cpp +++ b/lldb/tools/lldb-dap/Breakpoint.cpp @@ -14,7 +14,6 @@ #include "lldb/API/SBLineEntry.h" #include "lldb/API/SBMutex.h" #include "llvm/ADT/StringExtras.h" -#include "llvm/Support/JSON.h" #include #include #include @@ -30,13 +29,16 @@ void Breakpoint::SetHitCondition() { m_bp.SetIgnoreCount(hitCount - 1); } -void Breakpoint::CreateJsonObject(llvm::json::Object &object) { +protocol::Breakpoint Breakpoint::ToProtocolBreakpoint() { + protocol::Breakpoint breakpoint; + // Each breakpoint location is treated as a separate breakpoint for VS code. // They don't have the notion of a single breakpoint with multiple locations. if (!m_bp.IsValid()) - return; - object.try_emplace("verified", m_bp.GetNumResolvedLocations() > 0); - object.try_emplace("id", m_bp.GetID()); + return breakpoint; + + breakpoint.verified = m_bp.GetNumResolvedLocations() > 0; + breakpoint.id = m_bp.GetID(); // VS Code DAP doesn't currently allow one breakpoint to have multiple // locations so we just report the first one. If we report all locations // then the IDE starts showing the wrong line numbers and locations for @@ -60,16 +62,18 @@ void Breakpoint::CreateJsonObject(llvm::json::Object &object) { if (bp_addr.IsValid()) { std::string formatted_addr = "0x" + llvm::utohexstr(bp_addr.GetLoadAddress(m_bp.GetTarget())); - object.try_emplace("instructionReference", formatted_addr); + breakpoint.instructionReference = formatted_addr; auto line_entry = bp_addr.GetLineEntry(); const auto line = line_entry.GetLine(); if (line != UINT32_MAX) - object.try_emplace("line", line); + breakpoint.line = line; const auto column = line_entry.GetColumn(); if (column != 0) - object.try_emplace("column", column); - object.try_emplace("source", CreateSource(line_entry)); + breakpoint.column = column; + breakpoint.source = CreateSource(line_entry); } + + return breakpoint; } bool Breakpoint::MatchesName(const char *name) { diff --git a/lldb/tools/lldb-dap/Breakpoint.h b/lldb/tools/lldb-dap/Breakpoint.h index 580017125af44..c4f1fa291f181 100644 --- a/lldb/tools/lldb-dap/Breakpoint.h +++ b/lldb/tools/lldb-dap/Breakpoint.h @@ -17,14 +17,16 @@ namespace lldb_dap { class Breakpoint : public BreakpointBase { public: - Breakpoint(DAP &d, const llvm::json::Object &obj) : BreakpointBase(d, obj) {} + Breakpoint(DAP &d, const std::optional &condition, + const std::optional &hit_condition) + : BreakpointBase(d, condition, hit_condition) {} Breakpoint(DAP &d, lldb::SBBreakpoint bp) : BreakpointBase(d), m_bp(bp) {} lldb::break_id_t GetID() const { return m_bp.GetID(); } void SetCondition() override; void SetHitCondition() override; - void CreateJsonObject(llvm::json::Object &object) override; + protocol::Breakpoint ToProtocolBreakpoint() override; bool MatchesName(const char *name); void SetBreakpoint(); diff --git a/lldb/tools/lldb-dap/BreakpointBase.cpp b/lldb/tools/lldb-dap/BreakpointBase.cpp index 331ce8efee9bc..1a4da92e44d31 100644 --- a/lldb/tools/lldb-dap/BreakpointBase.cpp +++ b/lldb/tools/lldb-dap/BreakpointBase.cpp @@ -7,16 +7,14 @@ //===----------------------------------------------------------------------===// #include "BreakpointBase.h" -#include "JSONUtils.h" -#include "llvm/ADT/StringRef.h" using namespace lldb_dap; -BreakpointBase::BreakpointBase(DAP &d, const llvm::json::Object &obj) - : m_dap(d), - m_condition(std::string(GetString(obj, "condition").value_or(""))), - m_hit_condition( - std::string(GetString(obj, "hitCondition").value_or(""))) {} +BreakpointBase::BreakpointBase(DAP &d, + const std::optional &condition, + const std::optional &hit_condition) + : m_dap(d), m_condition(condition.value_or("")), + m_hit_condition(hit_condition.value_or("")) {} void BreakpointBase::UpdateBreakpoint(const BreakpointBase &request_bp) { if (m_condition != request_bp.m_condition) { diff --git a/lldb/tools/lldb-dap/BreakpointBase.h b/lldb/tools/lldb-dap/BreakpointBase.h index 4c13326624831..e9cfc112675c3 100644 --- a/lldb/tools/lldb-dap/BreakpointBase.h +++ b/lldb/tools/lldb-dap/BreakpointBase.h @@ -10,7 +10,8 @@ #define LLDB_TOOLS_LLDB_DAP_BREAKPOINTBASE_H #include "DAPForward.h" -#include "llvm/ADT/StringRef.h" +#include "Protocol/ProtocolTypes.h" +#include #include namespace lldb_dap { @@ -18,12 +19,13 @@ namespace lldb_dap { class BreakpointBase { public: explicit BreakpointBase(DAP &d) : m_dap(d) {} - BreakpointBase(DAP &d, const llvm::json::Object &obj); + BreakpointBase(DAP &d, const std::optional &condition, + const std::optional &hit_condition); virtual ~BreakpointBase() = default; virtual void SetCondition() = 0; virtual void SetHitCondition() = 0; - virtual void CreateJsonObject(llvm::json::Object &object) = 0; + virtual protocol::Breakpoint ToProtocolBreakpoint() = 0; void UpdateBreakpoint(const BreakpointBase &request_bp); diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index f6754b1f8d7a3..80c150018f0b3 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -552,9 +553,7 @@ lldb::SBThread DAP::GetLLDBThread(const llvm::json::Object &arguments) { return target.GetProcess().GetThreadByID(tid); } -lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) { - const uint64_t frame_id = - GetInteger(arguments, "frameId").value_or(UINT64_MAX); +lldb::SBFrame DAP::GetLLDBFrame(uint64_t frame_id) { lldb::SBProcess process = target.GetProcess(); // Upper 32 bits is the thread index ID lldb::SBThread thread = @@ -563,6 +562,12 @@ lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) { return thread.GetFrameAtIndex(GetLLDBFrameID(frame_id)); } +lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) { + const auto frame_id = + GetInteger(arguments, "frameId").value_or(UINT64_MAX); + return GetLLDBFrame(frame_id); +} + llvm::json::Value DAP::CreateTopLevelScopes() { llvm::json::Array scopes; scopes.emplace_back( @@ -1602,7 +1607,7 @@ void DAP::EventThread() { // avoids sending paths that should be source mapped. Note that // CreateBreakpoint doesn't apply source mapping and certain // implementation ignore the source part of this event anyway. - llvm::json::Value source_bp = CreateBreakpoint(&bp); + llvm::json::Value source_bp = bp.ToProtocolBreakpoint(); source_bp.getAsObject()->erase("source"); llvm::json::Object body; diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index afeda8d81efb0..ecde222c9263c 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -275,6 +275,7 @@ struct DAP { lldb::SBThread GetLLDBThread(lldb::tid_t id); lldb::SBThread GetLLDBThread(const llvm::json::Object &arguments); + lldb::SBFrame GetLLDBFrame(uint64_t frame_id); lldb::SBFrame GetLLDBFrame(const llvm::json::Object &arguments); llvm::json::Value CreateTopLevelScopes(); diff --git a/lldb/tools/lldb-dap/FunctionBreakpoint.cpp b/lldb/tools/lldb-dap/FunctionBreakpoint.cpp index d87723f7557bd..1ea9cddb9f689 100644 --- a/lldb/tools/lldb-dap/FunctionBreakpoint.cpp +++ b/lldb/tools/lldb-dap/FunctionBreakpoint.cpp @@ -8,15 +8,15 @@ #include "FunctionBreakpoint.h" #include "DAP.h" -#include "JSONUtils.h" #include "lldb/API/SBMutex.h" #include namespace lldb_dap { -FunctionBreakpoint::FunctionBreakpoint(DAP &d, const llvm::json::Object &obj) - : Breakpoint(d, obj), - m_function_name(std::string(GetString(obj, "name").value_or(""))) {} +FunctionBreakpoint::FunctionBreakpoint( + DAP &d, const protocol::FunctionBreakpoint &breakpoint) + : Breakpoint(d, breakpoint.condition, breakpoint.hitCondition), + m_function_name(breakpoint.name) {} void FunctionBreakpoint::SetBreakpoint() { lldb::SBMutex lock = m_dap.GetAPIMutex(); diff --git a/lldb/tools/lldb-dap/FunctionBreakpoint.h b/lldb/tools/lldb-dap/FunctionBreakpoint.h index 7100360cd7ec1..76fbdb3e886a4 100644 --- a/lldb/tools/lldb-dap/FunctionBreakpoint.h +++ b/lldb/tools/lldb-dap/FunctionBreakpoint.h @@ -11,12 +11,13 @@ #include "Breakpoint.h" #include "DAPForward.h" +#include "Protocol/ProtocolTypes.h" namespace lldb_dap { class FunctionBreakpoint : public Breakpoint { public: - FunctionBreakpoint(DAP &dap, const llvm::json::Object &obj); + FunctionBreakpoint(DAP &dap, const protocol::FunctionBreakpoint &breakpoint); /// Set this breakpoint in LLDB as a new breakpoint. void SetBreakpoint(); diff --git a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp index 4d920f8556254..8cb25d0603449 100644 --- a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp @@ -8,144 +8,50 @@ #include "DAP.h" #include "EventHelper.h" -#include "JSONUtils.h" +#include "Protocol/ProtocolTypes.h" #include "RequestHandler.h" #include "lldb/API/SBMemoryRegionInfo.h" #include "llvm/ADT/StringExtras.h" +#include namespace lldb_dap { -// "DataBreakpointInfoRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "Obtains information on a possible data breakpoint that -// could be set on an expression or variable.\nClients should only call this -// request if the corresponding capability `supportsDataBreakpoints` is -// true.", "properties": { -// "command": { -// "type": "string", -// "enum": [ "dataBreakpointInfo" ] -// }, -// "arguments": { -// "$ref": "#/definitions/DataBreakpointInfoArguments" -// } -// }, -// "required": [ "command", "arguments" ] -// }] -// }, -// "DataBreakpointInfoArguments": { -// "type": "object", -// "description": "Arguments for `dataBreakpointInfo` request.", -// "properties": { -// "variablesReference": { -// "type": "integer", -// "description": "Reference to the variable container if the data -// breakpoint is requested for a child of the container. The -// `variablesReference` must have been obtained in the current suspended -// state. See 'Lifetime of Object References' in the Overview section for -// details." -// }, -// "name": { -// "type": "string", -// "description": "The name of the variable's child to obtain data -// breakpoint information for.\nIf `variablesReference` isn't specified, -// this can be an expression." -// }, -// "frameId": { -// "type": "integer", -// "description": "When `name` is an expression, evaluate it in the scope -// of this stack frame. If not specified, the expression is evaluated in -// the global scope. When `variablesReference` is specified, this property -// has no effect." -// } -// }, -// "required": [ "name" ] -// }, -// "DataBreakpointInfoResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to `dataBreakpointInfo` request.", -// "properties": { -// "body": { -// "type": "object", -// "properties": { -// "dataId": { -// "type": [ "string", "null" ], -// "description": "An identifier for the data on which a data -// breakpoint can be registered with the `setDataBreakpoints` -// request or null if no data breakpoint is available. If a -// `variablesReference` or `frameId` is passed, the `dataId` is -// valid in the current suspended state, otherwise it's valid -// indefinitely. See 'Lifetime of Object References' in the Overview -// section for details. Breakpoints set using the `dataId` in the -// `setDataBreakpoints` request may outlive the lifetime of the -// associated `dataId`." -// }, -// "description": { -// "type": "string", -// "description": "UI string that describes on what data the -// breakpoint is set on or why a data breakpoint is not available." -// }, -// "accessTypes": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/DataBreakpointAccessType" -// }, -// "description": "Attribute lists the available access types for a -// potential data breakpoint. A UI client could surface this -// information." -// }, -// "canPersist": { -// "type": "boolean", -// "description": "Attribute indicates that a potential data -// breakpoint could be persisted across sessions." -// } -// }, -// "required": [ "dataId", "description" ] -// } -// }, -// "required": [ "body" ] -// }] -// } -void DataBreakpointInfoRequestHandler::operator()( - const llvm::json::Object &request) const { - llvm::json::Object response; - FillResponse(request, response); - llvm::json::Object body; - lldb::SBError error; - llvm::json::Array accessTypes{"read", "write", "readWrite"}; - const auto *arguments = request.getObject("arguments"); - const auto variablesReference = - GetInteger(arguments, "variablesReference").value_or(0); - llvm::StringRef name = GetString(arguments, "name").value_or(""); - lldb::SBFrame frame = dap.GetLLDBFrame(*arguments); - lldb::SBValue variable = dap.variables.FindVariable(variablesReference, name); +/// Obtains information on a possible data breakpoint that could be set on an +/// expression or variable. Clients should only call this request if the +/// corresponding capability supportsDataBreakpoints is true. +llvm::Expected +DataBreakpointInfoRequestHandler::Run( + const protocol::DataBreakpointInfoArguments &args) const { + protocol::DataBreakpointInfoResponseBody response; + lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId.value_or(UINT64_MAX)); + lldb::SBValue variable = dap.variables.FindVariable( + args.variablesReference.value_or(0), args.name); std::string addr, size; + bool is_data_ok = true; if (variable.IsValid()) { lldb::addr_t load_addr = variable.GetLoadAddress(); size_t byte_size = variable.GetByteSize(); if (load_addr == LLDB_INVALID_ADDRESS) { - body.try_emplace("dataId", nullptr); - body.try_emplace("description", - "does not exist in memory, its location is " + - std::string(variable.GetLocation())); + is_data_ok = false; + response.description = "does not exist in memory, its location is " + + std::string(variable.GetLocation()); } else if (byte_size == 0) { - body.try_emplace("dataId", nullptr); - body.try_emplace("description", "variable size is 0"); + is_data_ok = false; + response.description = "variable size is 0"; } else { addr = llvm::utohexstr(load_addr); size = llvm::utostr(byte_size); } - } else if (variablesReference == 0 && frame.IsValid()) { - lldb::SBValue value = frame.EvaluateExpression(name.data()); + } else if (args.variablesReference.value_or(0) == 0 && frame.IsValid()) { + lldb::SBValue value = frame.EvaluateExpression(args.name.c_str()); if (value.GetError().Fail()) { lldb::SBError error = value.GetError(); const char *error_cstr = error.GetCString(); - body.try_emplace("dataId", nullptr); - body.try_emplace("description", error_cstr && error_cstr[0] - ? std::string(error_cstr) - : "evaluation failed"); + is_data_ok = false; + response.description = error_cstr && error_cstr[0] + ? std::string(error_cstr) + : "evaluation failed"; } else { uint64_t load_addr = value.GetValueAsUnsigned(); lldb::SBData data = value.GetPointeeData(); @@ -159,32 +65,31 @@ void DataBreakpointInfoRequestHandler::operator()( // request if SBProcess::GetMemoryRegionInfo returns error. if (err.Success()) { if (!(region.IsReadable() || region.IsWritable())) { - body.try_emplace("dataId", nullptr); - body.try_emplace("description", - "memory region for address " + addr + - " has no read or write permissions"); + is_data_ok = false; + response.description = "memory region for address " + addr + + " has no read or write permissions"; } } } else { - body.try_emplace("dataId", nullptr); - body.try_emplace("description", - "unable to get byte size for expression: " + - name.str()); + is_data_ok = false; + response.description = + "unable to get byte size for expression: " + args.name; } } } else { - body.try_emplace("dataId", nullptr); - body.try_emplace("description", "variable not found: " + name.str()); + is_data_ok = false; + response.description = "variable not found: " + args.name; } - if (!body.getObject("dataId")) { - body.try_emplace("dataId", addr + "/" + size); - body.try_emplace("accessTypes", std::move(accessTypes)); - body.try_emplace("description", - size + " bytes at " + addr + " " + name.str()); + if (is_data_ok) { + response.dataId = addr + "/" + size; + response.accessTypes = {protocol::eDataBreakpointAccessTypeRead, + protocol::eDataBreakpointAccessTypeWrite, + protocol::eDataBreakpointAccessTypeReadWrite}; + response.description = size + " bytes at " + addr + " " + args.name; } - response.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(response))); + + return response; } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index db7f05cb1f113..b0002440cf72e 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.h +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h @@ -15,7 +15,6 @@ #include "Protocol/ProtocolBase.h" #include "Protocol/ProtocolRequests.h" #include "Protocol/ProtocolTypes.h" -#include "lldb/API/SBError.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" @@ -349,15 +348,19 @@ class StepOutRequestHandler : public RequestHandler> { public: - using LegacyRequestHandler::LegacyRequestHandler; + using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "setBreakpoints"; } FeatureSet GetSupportedFeatures() const override { return {protocol::eAdapterFeatureConditionalBreakpoints, protocol::eAdapterFeatureHitConditionalBreakpoints}; } - void operator()(const llvm::json::Object &request) const override; + llvm::Expected + Run(const protocol::SetBreakpointsArguments &args) const override; }; class SetExceptionBreakpointsRequestHandler : public LegacyRequestHandler { @@ -370,43 +373,59 @@ class SetExceptionBreakpointsRequestHandler : public LegacyRequestHandler { void operator()(const llvm::json::Object &request) const override; }; -class SetFunctionBreakpointsRequestHandler : public LegacyRequestHandler { +class SetFunctionBreakpointsRequestHandler + : public RequestHandler< + protocol::SetFunctionBreakpointsArguments, + llvm::Expected> { public: - using LegacyRequestHandler::LegacyRequestHandler; + using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "setFunctionBreakpoints"; } FeatureSet GetSupportedFeatures() const override { return {protocol::eAdapterFeatureFunctionBreakpoints}; } - void operator()(const llvm::json::Object &request) const override; + llvm::Expected + Run(const protocol::SetFunctionBreakpointsArguments &args) const override; }; -class DataBreakpointInfoRequestHandler : public LegacyRequestHandler { +class DataBreakpointInfoRequestHandler + : public RequestHandler< + protocol::DataBreakpointInfoArguments, + llvm::Expected> { public: - using LegacyRequestHandler::LegacyRequestHandler; + using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "dataBreakpointInfo"; } - void operator()(const llvm::json::Object &request) const override; + llvm::Expected + Run(const protocol::DataBreakpointInfoArguments &args) const override; }; -class SetDataBreakpointsRequestHandler : public LegacyRequestHandler { +class SetDataBreakpointsRequestHandler + : public RequestHandler< + protocol::SetDataBreakpointsArguments, + llvm::Expected> { public: - using LegacyRequestHandler::LegacyRequestHandler; + using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "setDataBreakpoints"; } - void operator()(const llvm::json::Object &request) const override; FeatureSet GetSupportedFeatures() const override { return {protocol::eAdapterFeatureDataBreakpoints}; } + llvm::Expected + Run(const protocol::SetDataBreakpointsArguments &args) const override; }; -class SetInstructionBreakpointsRequestHandler : public LegacyRequestHandler { +class SetInstructionBreakpointsRequestHandler + : public RequestHandler< + protocol::SetInstructionBreakpointsArguments, + llvm::Expected> { public: - using LegacyRequestHandler::LegacyRequestHandler; + using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "setInstructionBreakpoints"; } - void operator()(const llvm::json::Object &request) const override; FeatureSet GetSupportedFeatures() const override { return {protocol::eAdapterFeatureInstructionBreakpoints}; } + llvm::Expected + Run(const protocol::SetInstructionBreakpointsArguments &args) const override; }; class CompileUnitsRequestHandler : public LegacyRequestHandler { diff --git a/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp index dc0368852101f..86e090b66afe9 100644 --- a/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp @@ -9,153 +9,52 @@ #include "DAP.h" #include "EventHelper.h" #include "JSONUtils.h" +#include "Protocol/ProtocolRequests.h" #include "RequestHandler.h" +#include namespace lldb_dap { -// "SetBreakpointsRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "SetBreakpoints request; value of command field is -// 'setBreakpoints'. Sets multiple breakpoints for a single source and -// clears all previous breakpoints in that source. To clear all breakpoint -// for a source, specify an empty array. When a breakpoint is hit, a -// StoppedEvent (event type 'breakpoint') is generated.", "properties": { -// "command": { -// "type": "string", -// "enum": [ "setBreakpoints" ] -// }, -// "arguments": { -// "$ref": "#/definitions/SetBreakpointsArguments" -// } -// }, -// "required": [ "command", "arguments" ] -// }] -// }, -// "SetBreakpointsArguments": { -// "type": "object", -// "description": "Arguments for 'setBreakpoints' request.", -// "properties": { -// "source": { -// "$ref": "#/definitions/Source", -// "description": "The source location of the breakpoints; either -// source.path or source.reference must be specified." -// }, -// "breakpoints": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/SourceBreakpoint" -// }, -// "description": "The code locations of the breakpoints." -// }, -// "lines": { -// "type": "array", -// "items": { -// "type": "integer" -// }, -// "description": "Deprecated: The code locations of the breakpoints." -// }, -// "sourceModified": { -// "type": "boolean", -// "description": "A value of true indicates that the underlying source -// has been modified which results in new breakpoint locations." -// } -// }, -// "required": [ "source" ] -// }, -// "SetBreakpointsResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to 'setBreakpoints' request. Returned is -// information about each breakpoint created by this request. This includes -// the actual code location and whether the breakpoint could be verified. -// The breakpoints returned are in the same order as the elements of the -// 'breakpoints' (or the deprecated 'lines') in the -// SetBreakpointsArguments.", "properties": { -// "body": { -// "type": "object", -// "properties": { -// "breakpoints": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/Breakpoint" -// }, -// "description": "Information about the breakpoints. The array -// elements are in the same order as the elements of the -// 'breakpoints' (or the deprecated 'lines') in the -// SetBreakpointsArguments." -// } -// }, -// "required": [ "breakpoints" ] -// } -// }, -// "required": [ "body" ] -// }] -// }, -// "SourceBreakpoint": { -// "type": "object", -// "description": "Properties of a breakpoint or logpoint passed to the -// setBreakpoints request.", "properties": { -// "line": { -// "type": "integer", -// "description": "The source line of the breakpoint or logpoint." -// }, -// "column": { -// "type": "integer", -// "description": "An optional source column of the breakpoint." -// }, -// "condition": { -// "type": "string", -// "description": "An optional expression for conditional breakpoints." -// }, -// "hitCondition": { -// "type": "string", -// "description": "An optional expression that controls how many hits of -// the breakpoint are ignored. The backend is expected to interpret the -// expression as needed." -// }, -// "logMessage": { -// "type": "string", -// "description": "If this attribute exists and is non-empty, the backend -// must not 'break' (stop) but log the message instead. Expressions within -// {} are interpolated." -// } -// }, -// "required": [ "line" ] -// } -void SetBreakpointsRequestHandler::operator()( - const llvm::json::Object &request) const { - llvm::json::Object response; - lldb::SBError error; - FillResponse(request, response); - const auto *arguments = request.getObject("arguments"); - const auto *source = arguments->getObject("source"); - const auto path = GetString(source, "path").value_or(""); - const auto *breakpoints = arguments->getArray("breakpoints"); - llvm::json::Array response_breakpoints; +/// Sets multiple breakpoints for a single source and clears all previous +/// breakpoints in that source. To clear all breakpoint for a source, specify an +/// empty array. When a breakpoint is hit, a `stopped` event (with reason +/// `breakpoint`) is generated. +llvm::Expected +SetBreakpointsRequestHandler::Run( + const protocol::SetBreakpointsArguments &args) const { + const auto &source = args.source; + const auto path = source.path.value_or(""); + std::vector response_breakpoints; // Decode the source breakpoint infos for this "setBreakpoints" request SourceBreakpointMap request_bps; // "breakpoints" may be unset, in which case we treat it the same as being set // to an empty array. - if (breakpoints) { - for (const auto &bp : *breakpoints) { - const auto *bp_obj = bp.getAsObject(); - if (bp_obj) { - SourceBreakpoint src_bp(dap, *bp_obj); - std::pair bp_pos(src_bp.GetLine(), - src_bp.GetColumn()); - request_bps.try_emplace(bp_pos, src_bp); - const auto [iv, inserted] = - dap.source_breakpoints[path].try_emplace(bp_pos, src_bp); - // We check if this breakpoint already exists to update it - if (inserted) - iv->getSecond().SetBreakpoint(path.data()); - else - iv->getSecond().UpdateBreakpoint(src_bp); - AppendBreakpoint(&iv->getSecond(), response_breakpoints, path, - src_bp.GetLine()); - } + if (args.breakpoints) { + for (const auto &bp : *args.breakpoints) { + SourceBreakpoint src_bp(dap, bp); + std::pair bp_pos(src_bp.GetLine(), + src_bp.GetColumn()); + request_bps.try_emplace(bp_pos, src_bp); + const auto [iv, inserted] = + dap.source_breakpoints[path].try_emplace(bp_pos, src_bp); + // We check if this breakpoint already exists to update it + if (inserted) + iv->getSecond().SetBreakpoint(path.data()); + else + iv->getSecond().UpdateBreakpoint(src_bp); + + protocol::Breakpoint response_bp = iv->getSecond().ToProtocolBreakpoint(); + + // Use the path from the request if it is set + if (!path.empty()) + response_bp.source = CreateSource(path); + + if (!response_bp.line) + response_bp.line = src_bp.GetLine(); + if (!response_bp.column) + response_bp.column = src_bp.GetColumn(); + response_breakpoints.push_back(response_bp); } } @@ -174,10 +73,7 @@ void SetBreakpointsRequestHandler::operator()( } } - llvm::json::Object body; - body.try_emplace("breakpoints", std::move(response_breakpoints)); - response.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(response))); + return protocol::SetBreakpointsResponseBody{std::move(response_breakpoints)}; } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp index 365c9f0d722d4..1caaa23bf06f6 100644 --- a/lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp @@ -8,90 +8,28 @@ #include "DAP.h" #include "EventHelper.h" -#include "JSONUtils.h" +#include "Protocol/ProtocolRequests.h" #include "RequestHandler.h" #include "Watchpoint.h" #include namespace lldb_dap { -// "SetDataBreakpointsRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "Replaces all existing data breakpoints with new data -// breakpoints.\nTo clear all data breakpoints, specify an empty -// array.\nWhen a data breakpoint is hit, a `stopped` event (with reason -// `data breakpoint`) is generated.\nClients should only call this request -// if the corresponding capability `supportsDataBreakpoints` is true.", -// "properties": { -// "command": { -// "type": "string", -// "enum": [ "setDataBreakpoints" ] -// }, -// "arguments": { -// "$ref": "#/definitions/SetDataBreakpointsArguments" -// } -// }, -// "required": [ "command", "arguments" ] -// }] -// }, -// "SetDataBreakpointsArguments": { -// "type": "object", -// "description": "Arguments for `setDataBreakpoints` request.", -// "properties": { -// "breakpoints": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/DataBreakpoint" -// }, -// "description": "The contents of this array replaces all existing data -// breakpoints. An empty array clears all data breakpoints." -// } -// }, -// "required": [ "breakpoints" ] -// }, -// "SetDataBreakpointsResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to `setDataBreakpoints` request.\nReturned is -// information about each breakpoint created by this request.", -// "properties": { -// "body": { -// "type": "object", -// "properties": { -// "breakpoints": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/Breakpoint" -// }, -// "description": "Information about the data breakpoints. The array -// elements correspond to the elements of the input argument -// `breakpoints` array." -// } -// }, -// "required": [ "breakpoints" ] -// } -// }, -// "required": [ "body" ] -// }] -// } -void SetDataBreakpointsRequestHandler::operator()( - const llvm::json::Object &request) const { - llvm::json::Object response; - lldb::SBError error; - FillResponse(request, response); - const auto *arguments = request.getObject("arguments"); - const auto *breakpoints = arguments->getArray("breakpoints"); - llvm::json::Array response_breakpoints; +/// Replaces all existing data breakpoints with new data breakpoints. +/// To clear all data breakpoints, specify an empty array. +/// When a data breakpoint is hit, a stopped event (with reason data breakpoint) +/// is generated. Clients should only call this request if the corresponding +/// capability supportsDataBreakpoints is true. +llvm::Expected +SetDataBreakpointsRequestHandler::Run( + const protocol::SetDataBreakpointsArguments &args) const { + std::vector response_breakpoints; + dap.target.DeleteAllWatchpoints(); std::vector watchpoints; - if (breakpoints) { - for (const auto &bp : *breakpoints) { - const auto *bp_obj = bp.getAsObject(); - if (bp_obj) - watchpoints.emplace_back(dap, *bp_obj); - } - } + for (const auto &bp : args.breakpoints) + watchpoints.emplace_back(dap, bp); + // If two watchpoints start at the same address, the latter overwrite the // former. So, we only enable those at first-seen addresses when iterating // backward. @@ -103,12 +41,10 @@ void SetDataBreakpointsRequestHandler::operator()( } } for (auto wp : watchpoints) - AppendBreakpoint(&wp, response_breakpoints); + response_breakpoints.push_back(wp.ToProtocolBreakpoint()); - llvm::json::Object body; - body.try_emplace("breakpoints", std::move(response_breakpoints)); - response.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(response))); + return protocol::SetDataBreakpointsResponseBody{ + std::move(response_breakpoints)}; } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp index c45dc0d0d6553..1367aa3e864d9 100644 --- a/lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp @@ -8,116 +8,35 @@ #include "DAP.h" #include "EventHelper.h" -#include "JSONUtils.h" #include "RequestHandler.h" namespace lldb_dap { -// "SetFunctionBreakpointsRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "SetFunctionBreakpoints request; value of command field is -// 'setFunctionBreakpoints'. Sets multiple function breakpoints and clears -// all previous function breakpoints. To clear all function breakpoint, -// specify an empty array. When a function breakpoint is hit, a StoppedEvent -// (event type 'function breakpoint') is generated.", "properties": { -// "command": { -// "type": "string", -// "enum": [ "setFunctionBreakpoints" ] -// }, -// "arguments": { -// "$ref": "#/definitions/SetFunctionBreakpointsArguments" -// } -// }, -// "required": [ "command", "arguments" ] -// }] -// }, -// "SetFunctionBreakpointsArguments": { -// "type": "object", -// "description": "Arguments for 'setFunctionBreakpoints' request.", -// "properties": { -// "breakpoints": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/FunctionBreakpoint" -// }, -// "description": "The function names of the breakpoints." -// } -// }, -// "required": [ "breakpoints" ] -// }, -// "FunctionBreakpoint": { -// "type": "object", -// "description": "Properties of a breakpoint passed to the -// setFunctionBreakpoints request.", "properties": { -// "name": { -// "type": "string", -// "description": "The name of the function." -// }, -// "condition": { -// "type": "string", -// "description": "An optional expression for conditional breakpoints." -// }, -// "hitCondition": { -// "type": "string", -// "description": "An optional expression that controls how many hits of -// the breakpoint are ignored. The backend is expected to interpret the -// expression as needed." -// } -// }, -// "required": [ "name" ] -// }, -// "SetFunctionBreakpointsResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to 'setFunctionBreakpoints' request. Returned is -// information about each breakpoint created by this request.", -// "properties": { -// "body": { -// "type": "object", -// "properties": { -// "breakpoints": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/Breakpoint" -// }, -// "description": "Information about the breakpoints. The array -// elements correspond to the elements of the 'breakpoints' array." -// } -// }, -// "required": [ "breakpoints" ] -// } -// }, -// "required": [ "body" ] -// }] -// } -void SetFunctionBreakpointsRequestHandler::operator()( - const llvm::json::Object &request) const { - llvm::json::Object response; - lldb::SBError error; - FillResponse(request, response); - const auto *arguments = request.getObject("arguments"); - const auto *breakpoints = arguments->getArray("breakpoints"); - llvm::json::Array response_breakpoints; +/// Replaces all existing function breakpoints with new function breakpoints. +/// To clear all function breakpoints, specify an empty array. +/// When a function breakpoint is hit, a stopped event (with reason function +/// breakpoint) is generated. Clients should only call this request if the +/// corresponding capability supportsFunctionBreakpoints is true. +llvm::Expected +SetFunctionBreakpointsRequestHandler::Run( + const protocol::SetFunctionBreakpointsArguments &args) const { + std::vector response_breakpoints; // Disable any function breakpoints that aren't in this request. // There is no call to remove function breakpoints other than calling this // function with a smaller or empty "breakpoints" list. const auto name_iter = dap.function_breakpoints.keys(); llvm::DenseSet seen(name_iter.begin(), name_iter.end()); - for (const auto &value : *breakpoints) { - const auto *bp_obj = value.getAsObject(); - if (!bp_obj) - continue; - FunctionBreakpoint fn_bp(dap, *bp_obj); - const auto [it, inserted] = dap.function_breakpoints.try_emplace( - fn_bp.GetFunctionName(), dap, *bp_obj); + for (const auto &fb : args.breakpoints) { + FunctionBreakpoint fn_bp(dap, fb); + const auto [it, inserted] = + dap.function_breakpoints.try_emplace(fn_bp.GetFunctionName(), dap, fb); if (inserted) it->second.SetBreakpoint(); else it->second.UpdateBreakpoint(fn_bp); - AppendBreakpoint(&it->second, response_breakpoints); + response_breakpoints.push_back(it->second.ToProtocolBreakpoint()); seen.erase(fn_bp.GetFunctionName()); } @@ -130,10 +49,8 @@ void SetFunctionBreakpointsRequestHandler::operator()( dap.function_breakpoints.erase(name); } - llvm::json::Object body; - body.try_emplace("breakpoints", std::move(response_breakpoints)); - response.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(response))); + return protocol::SetFunctionBreakpointsResponseBody{ + std::move(response_breakpoints)}; } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp index 4e555ad605a26..48b2ba5a25594 100644 --- a/lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp @@ -8,207 +8,20 @@ #include "DAP.h" #include "EventHelper.h" -#include "JSONUtils.h" #include "RequestHandler.h" namespace lldb_dap { -// "SetInstructionBreakpointsRequest": { -// "allOf": [ -// {"$ref": "#/definitions/Request"}, -// { -// "type": "object", -// "description" : -// "Replaces all existing instruction breakpoints. Typically, " -// "instruction breakpoints would be set from a disassembly window. " -// "\nTo clear all instruction breakpoints, specify an empty " -// "array.\nWhen an instruction breakpoint is hit, a `stopped` event " -// "(with reason `instruction breakpoint`) is generated.\nClients " -// "should only call this request if the corresponding capability " -// "`supportsInstructionBreakpoints` is true.", -// "properties": { -// "command": { "type": "string", "enum": ["setInstructionBreakpoints"] -// }, "arguments": {"$ref": -// "#/definitions/SetInstructionBreakpointsArguments"} -// }, -// "required": [ "command", "arguments" ] -// } -// ] -// }, -// "SetInstructionBreakpointsArguments": { -// "type": "object", -// "description": "Arguments for `setInstructionBreakpoints` request", -// "properties": { -// "breakpoints": { -// "type": "array", -// "items": {"$ref": "#/definitions/InstructionBreakpoint"}, -// "description": "The instruction references of the breakpoints" -// } -// }, -// "required": ["breakpoints"] -// }, -// "SetInstructionBreakpointsResponse": { -// "allOf": [ -// {"$ref": "#/definitions/Response"}, -// { -// "type": "object", -// "description": "Response to `setInstructionBreakpoints` request", -// "properties": { -// "body": { -// "type": "object", -// "properties": { -// "breakpoints": { -// "type": "array", -// "items": {"$ref": "#/definitions/Breakpoint"}, -// "description": -// "Information about the breakpoints. The array elements -// " "correspond to the elements of the `breakpoints` -// array." -// } -// }, -// "required": ["breakpoints"] -// } -// }, -// "required": ["body"] -// } -// ] -// }, -// "InstructionBreakpoint": { -// "type": "object", -// "description": "Properties of a breakpoint passed to the " -// "`setInstructionBreakpoints` request", -// "properties": { -// "instructionReference": { -// "type": "string", -// "description" : -// "The instruction reference of the breakpoint.\nThis should be a " -// "memory or instruction pointer reference from an -// `EvaluateResponse`, " -// "`Variable`, `StackFrame`, `GotoTarget`, or `Breakpoint`." -// }, -// "offset": { -// "type": "integer", -// "description": "The offset from the instruction reference in " -// "bytes.\nThis can be negative." -// }, -// "condition": { -// "type": "string", -// "description": "An expression for conditional breakpoints.\nIt is only -// " -// "honored by a debug adapter if the corresponding " -// "capability `supportsConditionalBreakpoints` is true." -// }, -// "hitCondition": { -// "type": "string", -// "description": "An expression that controls how many hits of the " -// "breakpoint are ignored.\nThe debug adapter is expected -// " "to interpret the expression as needed.\nThe -// attribute " "is only honored by a debug adapter if the -// corresponding " "capability -// `supportsHitConditionalBreakpoints` is true." -// }, -// "mode": { -// "type": "string", -// "description": "The mode of this breakpoint. If defined, this must be -// " -// "one of the `breakpointModes` the debug adapter " -// "advertised in its `Capabilities`." -// } -// }, -// "required": ["instructionReference"] -// }, -// "Breakpoint": { -// "type": "object", -// "description" : -// "Information about a breakpoint created in `setBreakpoints`, " -// "`setFunctionBreakpoints`, `setInstructionBreakpoints`, or " -// "`setDataBreakpoints` requests.", -// "properties": { -// "id": { -// "type": "integer", -// "description" : -// "The identifier for the breakpoint. It is needed if breakpoint -// " "events are used to update or remove breakpoints." -// }, -// "verified": { -// "type": "boolean", -// "description": "If true, the breakpoint could be set (but not " -// "necessarily at the desired location)." -// }, -// "message": { -// "type": "string", -// "description": "A message about the state of the breakpoint.\nThis -// " -// "is shown to the user and can be used to explain -// why " "a breakpoint could not be verified." -// }, -// "source": { -// "$ref": "#/definitions/Source", -// "description": "The source where the breakpoint is located." -// }, -// "line": { -// "type": "integer", -// "description" : -// "The start line of the actual range covered by the breakpoint." -// }, -// "column": { -// "type": "integer", -// "description" : -// "Start position of the source range covered by the breakpoint. -// " "It is measured in UTF-16 code units and the client -// capability " -// "`columnsStartAt1` determines whether it is 0- or 1-based." -// }, -// "endLine": { -// "type": "integer", -// "description" : -// "The end line of the actual range covered by the breakpoint." -// }, -// "endColumn": { -// "type": "integer", -// "description" : -// "End position of the source range covered by the breakpoint. It -// " "is measured in UTF-16 code units and the client capability " -// "`columnsStartAt1` determines whether it is 0- or 1-based.\nIf -// " "no end line is given, then the end column is assumed to be -// in " "the start line." -// }, -// "instructionReference": { -// "type": "string", -// "description": "A memory reference to where the breakpoint is -// set." -// }, -// "offset": { -// "type": "integer", -// "description": "The offset from the instruction reference.\nThis " -// "can be negative." -// }, -// "reason": { -// "type": "string", -// "description" : -// "A machine-readable explanation of why a breakpoint may not be -// " "verified. If a breakpoint is verified or a specific reason -// is " "not known, the adapter should omit this property. -// Possible " "values include:\n\n- `pending`: Indicates a -// breakpoint might be " "verified in the future, but the adapter -// cannot verify it in the " "current state.\n - `failed`: -// Indicates a breakpoint was not " "able to be verified, and the -// adapter does not believe it can be " "verified without -// intervention.", -// "enum": [ "pending", "failed" ] -// } -// }, -// "required": ["verified"] -// }, -void SetInstructionBreakpointsRequestHandler::operator()( - const llvm::json::Object &request) const { - llvm::json::Object response; - llvm::json::Array response_breakpoints; - llvm::json::Object body; - FillResponse(request, response); - - const auto *arguments = request.getObject("arguments"); - const auto *breakpoints = arguments->getArray("breakpoints"); +/// Replaces all existing instruction breakpoints. Typically, instruction +/// breakpoints would be set from a disassembly window. To clear all instruction +/// breakpoints, specify an empty array. When an instruction breakpoint is hit, +/// a stopped event (with reason instruction breakpoint) is generated. Clients +/// should only call this request if the corresponding capability +/// supportsInstructionBreakpoints is true. +llvm::Expected +SetInstructionBreakpointsRequestHandler::Run( + const protocol::SetInstructionBreakpointsArguments &args) const { + std::vector response_breakpoints; // Disable any instruction breakpoints that aren't in this request. // There is no call to remove instruction breakpoints other than calling this @@ -216,19 +29,16 @@ void SetInstructionBreakpointsRequestHandler::operator()( llvm::DenseSet seen( llvm::from_range, llvm::make_first_range(dap.instruction_breakpoints)); - for (const auto &bp : *breakpoints) { - const auto *bp_obj = bp.getAsObject(); - if (!bp_obj) - continue; + for (const auto &bp : args.breakpoints) { // Read instruction breakpoint request. - InstructionBreakpoint inst_bp(dap, *bp_obj); + InstructionBreakpoint inst_bp(dap, bp); const auto [iv, inserted] = dap.instruction_breakpoints.try_emplace( - inst_bp.GetInstructionAddressReference(), dap, *bp_obj); + inst_bp.GetInstructionAddressReference(), dap, bp); if (inserted) iv->second.SetBreakpoint(); else iv->second.UpdateBreakpoint(inst_bp); - AppendBreakpoint(&iv->second, response_breakpoints); + response_breakpoints.push_back(iv->second.ToProtocolBreakpoint()); seen.erase(inst_bp.GetInstructionAddressReference()); } @@ -240,9 +50,8 @@ void SetInstructionBreakpointsRequestHandler::operator()( dap.instruction_breakpoints.erase(addr); } - body.try_emplace("breakpoints", std::move(response_breakpoints)); - response.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(response))); + return protocol::SetInstructionBreakpointsResponseBody{ + std::move(response_breakpoints)}; } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/TestGetTargetBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/TestGetTargetBreakpointsRequestHandler.cpp index 54c11cfa0b791..5f4f016f6a1ef 100644 --- a/lldb/tools/lldb-dap/Handler/TestGetTargetBreakpointsRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/TestGetTargetBreakpointsRequestHandler.cpp @@ -20,7 +20,7 @@ void TestGetTargetBreakpointsRequestHandler::operator()( llvm::json::Array response_breakpoints; for (uint32_t i = 0; dap.target.GetBreakpointAtIndex(i).IsValid(); ++i) { auto bp = Breakpoint(dap, dap.target.GetBreakpointAtIndex(i)); - AppendBreakpoint(&bp, response_breakpoints); + response_breakpoints.push_back(bp.ToProtocolBreakpoint()); } llvm::json::Object body; body.try_emplace("breakpoints", std::move(response_breakpoints)); diff --git a/lldb/tools/lldb-dap/InstructionBreakpoint.cpp b/lldb/tools/lldb-dap/InstructionBreakpoint.cpp index dfdc6319ac9e8..ddae1a8b20243 100644 --- a/lldb/tools/lldb-dap/InstructionBreakpoint.cpp +++ b/lldb/tools/lldb-dap/InstructionBreakpoint.cpp @@ -9,20 +9,19 @@ #include "InstructionBreakpoint.h" #include "DAP.h" -#include "JSONUtils.h" #include "lldb/API/SBBreakpoint.h" #include "lldb/API/SBTarget.h" #include "llvm/ADT/StringRef.h" namespace lldb_dap { -InstructionBreakpoint::InstructionBreakpoint(DAP &d, - const llvm::json::Object &obj) - : Breakpoint(d, obj), m_instruction_address_reference(LLDB_INVALID_ADDRESS), - m_offset(GetInteger(obj, "offset").value_or(0)) { - GetString(obj, "instructionReference") - .value_or("") - .getAsInteger(0, m_instruction_address_reference); +InstructionBreakpoint::InstructionBreakpoint( + DAP &d, const protocol::InstructionBreakpoint &breakpoint) + : Breakpoint(d, breakpoint.condition, breakpoint.hitCondition), + m_instruction_address_reference(LLDB_INVALID_ADDRESS), + m_offset(breakpoint.offset.value_or(0)) { + llvm::StringRef instruction_reference(breakpoint.instructionReference); + instruction_reference.getAsInteger(0, m_instruction_address_reference); m_instruction_address_reference += m_offset; } diff --git a/lldb/tools/lldb-dap/InstructionBreakpoint.h b/lldb/tools/lldb-dap/InstructionBreakpoint.h index 6ed980e00d038..a8c8f2113e5eb 100644 --- a/lldb/tools/lldb-dap/InstructionBreakpoint.h +++ b/lldb/tools/lldb-dap/InstructionBreakpoint.h @@ -12,6 +12,7 @@ #include "Breakpoint.h" #include "DAPForward.h" +#include "Protocol/ProtocolTypes.h" #include "lldb/lldb-types.h" #include @@ -20,7 +21,8 @@ namespace lldb_dap { /// Instruction Breakpoint class InstructionBreakpoint : public Breakpoint { public: - InstructionBreakpoint(DAP &d, const llvm::json::Object &obj); + InstructionBreakpoint(DAP &d, + const protocol::InstructionBreakpoint &breakpoint); /// Set instruction breakpoint in LLDB as a new breakpoint. void SetBreakpoint(); diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp index 4409cf5b27e5b..e184e7602ef14 100644 --- a/lldb/tools/lldb-dap/JSONUtils.cpp +++ b/lldb/tools/lldb-dap/JSONUtils.cpp @@ -7,7 +7,6 @@ //===----------------------------------------------------------------------===// #include "JSONUtils.h" -#include "BreakpointBase.h" #include "DAP.h" #include "ExceptionBreakpoint.h" #include "LLDBUtils.h" @@ -354,70 +353,6 @@ llvm::json::Value CreateScope(const llvm::StringRef name, return llvm::json::Value(std::move(object)); } -// "Breakpoint": { -// "type": "object", -// "description": "Information about a Breakpoint created in setBreakpoints -// or setFunctionBreakpoints.", -// "properties": { -// "id": { -// "type": "integer", -// "description": "An optional unique identifier for the breakpoint." -// }, -// "verified": { -// "type": "boolean", -// "description": "If true breakpoint could be set (but not necessarily -// at the desired location)." -// }, -// "message": { -// "type": "string", -// "description": "An optional message about the state of the breakpoint. -// This is shown to the user and can be used to explain -// why a breakpoint could not be verified." -// }, -// "source": { -// "$ref": "#/definitions/Source", -// "description": "The source where the breakpoint is located." -// }, -// "line": { -// "type": "integer", -// "description": "The start line of the actual range covered by the -// breakpoint." -// }, -// "column": { -// "type": "integer", -// "description": "An optional start column of the actual range covered -// by the breakpoint." -// }, -// "endLine": { -// "type": "integer", -// "description": "An optional end line of the actual range covered by -// the breakpoint." -// }, -// "endColumn": { -// "type": "integer", -// "description": "An optional end column of the actual range covered by -// the breakpoint. If no end line is given, then the end -// column is assumed to be in the start line." -// } -// }, -// "required": [ "verified" ] -// } -llvm::json::Value CreateBreakpoint(BreakpointBase *bp, - std::optional request_path, - std::optional request_line, - std::optional request_column) { - llvm::json::Object object; - if (request_path) - object.try_emplace("source", CreateSource(*request_path)); - bp->CreateJsonObject(object); - // We try to add request_line as a fallback - if (request_line) - object.try_emplace("line", *request_line); - if (request_column) - object.try_emplace("column", *request_column); - return llvm::json::Value(std::move(object)); -} - static uint64_t GetDebugInfoSizeInSection(lldb::SBSection section) { uint64_t debug_info_size = 0; llvm::StringRef section_name(section.GetName()); @@ -506,12 +441,6 @@ llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module) { return llvm::json::Value(std::move(object)); } -void AppendBreakpoint(BreakpointBase *bp, llvm::json::Array &breakpoints, - std::optional request_path, - std::optional request_line) { - breakpoints.emplace_back(CreateBreakpoint(bp, request_path, request_line)); -} - // "Event": { // "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, { // "type": "object", @@ -567,96 +496,30 @@ CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) { return filter; } -// "Source": { -// "type": "object", -// "description": "A Source is a descriptor for source code. It is returned -// from the debug adapter as part of a StackFrame and it is -// used by clients when specifying breakpoints.", -// "properties": { -// "name": { -// "type": "string", -// "description": "The short name of the source. Every source returned -// from the debug adapter has a name. When sending a -// source to the debug adapter this name is optional." -// }, -// "path": { -// "type": "string", -// "description": "The path of the source to be shown in the UI. It is -// only used to locate and load the content of the -// source if no sourceReference is specified (or its -// value is 0)." -// }, -// "sourceReference": { -// "type": "number", -// "description": "If sourceReference > 0 the contents of the source must -// be retrieved through the SourceRequest (even if a path -// is specified). A sourceReference is only valid for a -// session, so it must not be used to persist a source." -// }, -// "presentationHint": { -// "type": "string", -// "description": "An optional hint for how to present the source in the -// UI. A value of 'deemphasize' can be used to indicate -// that the source is not available or that it is -// skipped on stepping.", -// "enum": [ "normal", "emphasize", "deemphasize" ] -// }, -// "origin": { -// "type": "string", -// "description": "The (optional) origin of this source: possible values -// 'internal module', 'inlined content from source map', -// etc." -// }, -// "sources": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/Source" -// }, -// "description": "An optional list of sources that are related to this -// source. These may be the source that generated this -// source." -// }, -// "adapterData": { -// "type":["array","boolean","integer","null","number","object","string"], -// "description": "Optional data that a debug adapter might want to loop -// through the client. The client should leave the data -// intact and persist it across sessions. The client -// should not interpret the data." -// }, -// "checksums": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/Checksum" -// }, -// "description": "The checksums associated with this file." -// } -// } -// } -llvm::json::Value CreateSource(const lldb::SBFileSpec &file) { - llvm::json::Object object; +protocol::Source CreateSource(const lldb::SBFileSpec &file) { + protocol::Source source; if (file.IsValid()) { const char *name = file.GetFilename(); if (name) - EmplaceSafeString(object, "name", name); + source.name = name; char path[PATH_MAX] = ""; if (file.GetPath(path, sizeof(path)) && - lldb::SBFileSpec::ResolvePath(path, path, PATH_MAX)) { - EmplaceSafeString(object, "path", std::string(path)); - } + lldb::SBFileSpec::ResolvePath(path, path, PATH_MAX)) + source.path = path; } - return llvm::json::Value(std::move(object)); + return source; } -llvm::json::Value CreateSource(const lldb::SBLineEntry &line_entry) { +protocol::Source CreateSource(const lldb::SBLineEntry &line_entry) { return CreateSource(line_entry.GetFileSpec()); } -llvm::json::Value CreateSource(llvm::StringRef source_path) { - llvm::json::Object source; +protocol::Source CreateSource(llvm::StringRef source_path) { + protocol::Source source; llvm::StringRef name = llvm::sys::path::filename(source_path); - EmplaceSafeString(source, "name", name); - EmplaceSafeString(source, "path", source_path); - return llvm::json::Value(std::move(source)); + source.name = name; + source.path = source_path; + return source; } bool ShouldDisplayAssemblySource( diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h index d0e20729f4ed9..7c7cf620f0c06 100644 --- a/lldb/tools/lldb-dap/JSONUtils.h +++ b/lldb/tools/lldb-dap/JSONUtils.h @@ -198,67 +198,6 @@ GetStringMap(const llvm::json::Object &obj, llvm::StringRef key); void FillResponse(const llvm::json::Object &request, llvm::json::Object &response); -/// Converts \a bp to a JSON value and appends the first valid location to the -/// \a breakpoints array. -/// -/// \param[in] bp -/// A LLDB breakpoint object which will get the first valid location -/// extracted and converted into a JSON object in the \a breakpoints array -/// -/// \param[in] breakpoints -/// A JSON array that will get a llvm::json::Value for \a bp -/// appended to it. -/// -/// \param[in] request_path -/// An optional source path to use when creating the "Source" object of this -/// breakpoint. If not specified, the "Source" object is created from the -/// breakpoint's address' LineEntry. It is useful to ensure the same source -/// paths provided by the setBreakpoints request are returned to the IDE. -/// -/// \param[in] request_line -/// An optional line to use when creating the "Breakpoint" object to append. -/// It is used if the breakpoint has no valid locations. -/// It is useful to ensure the same line -/// provided by the setBreakpoints request are returned to the IDE as a -/// fallback. -void AppendBreakpoint( - BreakpointBase *bp, llvm::json::Array &breakpoints, - std::optional request_path = std::nullopt, - std::optional request_line = std::nullopt); - -/// Converts breakpoint location to a debug adapter protocol "Breakpoint". -/// -/// \param[in] bp -/// A LLDB breakpoint object to convert into a JSON value -/// -/// \param[in] request_path -/// An optional source path to use when creating the "Source" object of this -/// breakpoint. If not specified, the "Source" object is created from the -/// breakpoint's address' LineEntry. It is useful to ensure the same source -/// paths provided by the setBreakpoints request are returned to the IDE. -/// -/// \param[in] request_line -/// An optional line to use when creating the resulting "Breakpoint" object. -/// It is used if the breakpoint has no valid locations. -/// It is useful to ensure the same line -/// provided by the setBreakpoints request are returned to the IDE as a -/// fallback. -/// -/// \param[in] request_column -/// An optional column to use when creating the resulting "Breakpoint" -/// object. It is used if the breakpoint has no valid locations. It is -/// useful to ensure the same column provided by the setBreakpoints request -/// are returned to the IDE as a fallback. -/// -/// \return -/// A "Breakpoint" JSON object with that follows the formal JSON -/// definition outlined by Microsoft. -llvm::json::Value -CreateBreakpoint(BreakpointBase *bp, - std::optional request_path = std::nullopt, - std::optional request_line = std::nullopt, - std::optional request_column = std::nullopt); - /// Converts a LLDB module to a VS Code DAP module for use in "modules" events. /// /// \param[in] target @@ -323,7 +262,7 @@ llvm::json::Value CreateScope(const llvm::StringRef name, /// \return /// A "Source" JSON object that follows the formal JSON /// definition outlined by Microsoft. -llvm::json::Value CreateSource(const lldb::SBFileSpec &file); +protocol::Source CreateSource(const lldb::SBFileSpec &file); /// Create a "Source" JSON object as described in the debug adapter definition. /// @@ -334,7 +273,7 @@ llvm::json::Value CreateSource(const lldb::SBFileSpec &file); /// \return /// A "Source" JSON object that follows the formal JSON /// definition outlined by Microsoft. -llvm::json::Value CreateSource(const lldb::SBLineEntry &line_entry); +protocol::Source CreateSource(const lldb::SBLineEntry &line_entry); /// Create a "Source" object for a given source path. /// @@ -344,7 +283,7 @@ llvm::json::Value CreateSource(const lldb::SBLineEntry &line_entry); /// \return /// A "Source" JSON object that follows the formal JSON /// definition outlined by Microsoft. -llvm::json::Value CreateSource(llvm::StringRef source_path); +protocol::Source CreateSource(llvm::StringRef source_path); /// Return true if the given line entry should be displayed as assembly. /// diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp index 4204ae7785e63..316e146d43a0f 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp @@ -376,4 +376,74 @@ bool fromJSON(const llvm::json::Value &Params, StepOutArguments &SOA, OM.mapOptional("granularity", SOA.granularity); } +bool fromJSON(const llvm::json::Value &Params, SetBreakpointsArguments &SBA, + llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("source", SBA.source) && + O.map("breakpoints", SBA.breakpoints) && O.map("lines", SBA.lines) && + O.map("sourceModified", SBA.sourceModified); +} + +llvm::json::Value toJSON(const SetBreakpointsResponseBody &SBR) { + json::Object result; + result["breakpoints"] = SBR.breakpoints; + return result; +} + +bool fromJSON(const llvm::json::Value &Params, + SetFunctionBreakpointsArguments &SFBA, llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("breakpoints", SFBA.breakpoints); +} + +llvm::json::Value toJSON(const SetFunctionBreakpointsResponseBody &SFBR) { + json::Object result; + result["breakpoints"] = SFBR.breakpoints; + return result; +} + +bool fromJSON(const llvm::json::Value &Params, + SetInstructionBreakpointsArguments &SIBA, llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("breakpoints", SIBA.breakpoints); +} + +llvm::json::Value toJSON(const SetInstructionBreakpointsResponseBody &SIBR) { + json::Object result; + result["breakpoints"] = SIBR.breakpoints; + return result; +} + +bool fromJSON(const llvm::json::Value &Params, + DataBreakpointInfoArguments &DBIA, llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("variablesReference", DBIA.variablesReference) && + O.map("name", DBIA.name) && O.map("frameId", DBIA.frameId) && + O.map("bytes", DBIA.bytes) && O.map("asAddress", DBIA.asAddress) && + O.map("mode", DBIA.mode); +} + +llvm::json::Value toJSON(const DataBreakpointInfoResponseBody &DBIRB) { + json::Object result; + result["dataId"] = DBIRB.dataId ? *DBIRB.dataId : llvm::json::Value(nullptr); + result["description"] = DBIRB.description; + if (DBIRB.accessTypes) + result["accessTypes"] = *DBIRB.accessTypes; + if (DBIRB.canPersist) + result["canPersist"] = *DBIRB.canPersist; + return result; +} + +bool fromJSON(const llvm::json::Value &Params, + SetDataBreakpointsArguments &SDBA, llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("breakpoints", SDBA.breakpoints); +} + +llvm::json::Value toJSON(const SetDataBreakpointsResponseBody &SDBR) { + json::Object result; + result["breakpoints"] = SDBR.breakpoints; + return result; +} + } // namespace lldb_dap::protocol diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h index 4f17d6e79080d..c6456b4113320 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h @@ -559,6 +559,153 @@ struct BreakpointLocationsResponseBody { }; llvm::json::Value toJSON(const BreakpointLocationsResponseBody &); +/// Arguments for `setBreakpoints` request. +struct SetBreakpointsArguments { + /// The source location of the breakpoints; either `source.path` or + /// `source.sourceReference` must be specified. + Source source; + + /// The code locations of the breakpoints. + std::optional> breakpoints; + + /// Deprecated: The code locations of the breakpoints. + std::optional> lines; + + /// A value of true indicates that the underlying source has been modified + /// which results in new breakpoint locations. + std::optional sourceModified; +}; +bool fromJSON(const llvm::json::Value &, SetBreakpointsArguments &, + llvm::json::Path); + +/// Response to `setBreakpoints` request. +/// Returned is information about each breakpoint created by this request. +/// This includes the actual code location and whether the breakpoint could be +/// verified. The breakpoints returned are in the same order as the elements of +/// the breakpoints (or the deprecated lines) array in the arguments. +struct SetBreakpointsResponseBody { + /// Information about the breakpoints. + /// The array elements are in the same order as the elements of the + /// `breakpoints` (or the deprecated `lines`) array in the arguments. + std::vector breakpoints; +}; +llvm::json::Value toJSON(const SetBreakpointsResponseBody &); + +/// Arguments for `setFunctionBreakpoints` request. +struct SetFunctionBreakpointsArguments { + /// The function names of the breakpoints. + std::vector breakpoints; +}; +bool fromJSON(const llvm::json::Value &, SetFunctionBreakpointsArguments &, + llvm::json::Path); + +/// Response to `setFunctionBreakpoints` request. +/// Returned is information about each breakpoint created by this request. +struct SetFunctionBreakpointsResponseBody { + /// Information about the breakpoints. The array elements correspond to the + /// elements of the `breakpoints` array. + std::vector breakpoints; +}; +llvm::json::Value toJSON(const SetFunctionBreakpointsResponseBody &); + +/// Arguments for `setInstructionBreakpoints` request. +struct SetInstructionBreakpointsArguments { + /// The instruction references of the breakpoints. + std::vector breakpoints; +}; +bool fromJSON(const llvm::json::Value &, SetInstructionBreakpointsArguments &, + llvm::json::Path); + +/// Response to `setInstructionBreakpoints` request. +struct SetInstructionBreakpointsResponseBody { + /// Information about the breakpoints. The array elements correspond to the + /// elements of the `breakpoints` array. + std::vector breakpoints; +}; +llvm::json::Value toJSON(const SetInstructionBreakpointsResponseBody &); + +/// Arguments for `dataBreakpointInfo` request. +struct DataBreakpointInfoArguments { + /// Reference to the variable container if the data breakpoint is requested + /// for a child of the container. The `variablesReference` must have been + /// obtained in the current suspended state.See 'Lifetime of Object + /// References' in the Overview section for details. + std::optional variablesReference; + + /// The name of the variable's child to obtain data breakpoint information + /// for. If `variablesReference` isn't specified, this can be an expression, + /// or an address if `asAddress` is also true. + std::string name; + + /// When `name` is an expression, evaluate it in the scope of this stack + /// frame. If not specified, the expression is evaluated in the global scope. + /// When `asAddress` is true, the `frameId` is ignored. + std::optional frameId; + + /// If specified, a debug adapter should return information for the range of + /// memory extending `bytes` number of bytes from the address or variable + /// specified by `name`. Breakpoints set using the resulting data ID should + /// pause on data access anywhere within that range. + /// Clients may set this property only if the `supportsDataBreakpointBytes` + /// capability is true. + std::optional bytes; + + /// If `true`, the `name` is a memory address and the debugger should + /// interpret it as a decimal value, or hex value if it is prefixed with `0x`. + /// Clients may set this property only if the `supportsDataBreakpointBytes` + /// capability is true. + std::optional asAddress; + + /// The mode of the desired breakpoint. If defined, this must be one of the + /// `breakpointModes` the debug adapter advertised in its `Capabilities`. + std::optional mode; +}; +bool fromJSON(const llvm::json::Value &, DataBreakpointInfoArguments &, + llvm::json::Path); + +/// Response to `dataBreakpointInfo` request. +struct DataBreakpointInfoResponseBody { + /// An identifier for the data on which a data breakpoint can be registered + /// with the `setDataBreakpoints` request or null if no data breakpoint is + /// available. If a `variablesReference` or `frameId` is passed, the `dataId` + /// is valid in the current suspended state, otherwise it's valid + /// indefinitely. See 'Lifetime of Object References' in the Overview section + /// for details. Breakpoints set using the `dataId` in the + /// `setDataBreakpoints` request may outlive the lifetime of the associated + /// `dataId`. + std::optional dataId; + + /// UI string that describes on what data the breakpoint is set on or why a + /// data breakpoint is not available. + std::string description; + + /// Attribute lists the available access types for a potential data + /// breakpoint. A UI client could surface this information. + std::optional> accessTypes; + + /// Attribute indicates that a potential data breakpoint could be persisted + /// across sessions. + std::optional canPersist; +}; +llvm::json::Value toJSON(const DataBreakpointInfoResponseBody &); + +/// Arguments for `setDataBreakpoints` request. +struct SetDataBreakpointsArguments { + /// The contents of this array replaces all existing data breakpoints. An + /// empty array clears all data breakpoints. + std::vector breakpoints; +}; +bool fromJSON(const llvm::json::Value &, SetDataBreakpointsArguments &, + llvm::json::Path); + +/// Response to `setDataBreakpoints` request. +struct SetDataBreakpointsResponseBody { + /// Information about the data breakpoints. The array elements correspond to + /// the elements of the input argument `breakpoints` array. + std::vector breakpoints; +}; +llvm::json::Value toJSON(const SetDataBreakpointsResponseBody &); + } // namespace lldb_dap::protocol #endif diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp index 967c1d2321502..2b7419916268b 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp @@ -43,6 +43,32 @@ bool fromJSON(const json::Value &Params, Source &S, json::Path P) { O.map("sourceReference", S.sourceReference); } +llvm::json::Value toJSON(PresentationHint hint) { + switch (hint) { + case ePresentationHintNormal: + return "normal"; + case ePresentationHintEmphasize: + return "emphasize"; + case ePresentationHintDeemphasize: + return "deemphasize"; + } + llvm_unreachable("unhandled presentation hint."); +} + +llvm::json::Value toJSON(const Source &S) { + json::Object result; + if (S.name) + result.insert({"name", *S.name}); + if (S.path) + result.insert({"path", *S.path}); + if (S.sourceReference) + result.insert({"sourceReference", *S.sourceReference}); + if (S.presentationHint) + result.insert({"presentationHint", *S.presentationHint}); + + return result; +} + json::Value toJSON(const ExceptionBreakpointsFilter &EBF) { json::Object result{{"filter", EBF.filter}, {"label", EBF.label}}; @@ -274,4 +300,108 @@ json::Value toJSON(const BreakpointLocation &B) { return result; } +llvm::json::Value toJSON(const BreakpointReason &BR) { + switch (BR) { + case BreakpointReason::eBreakpointReasonPending: + return "pending"; + case BreakpointReason::eBreakpointReasonFailed: + return "failed"; + } + llvm_unreachable("unhandled breakpoint reason."); +} + +json::Value toJSON(const Breakpoint &BP) { + json::Object result{{"verified", BP.verified}}; + + if (BP.id) + result.insert({"id", *BP.id}); + if (BP.message) + result.insert({"message", *BP.message}); + if (BP.source) + result.insert({"source", *BP.source}); + if (BP.line) + result.insert({"line", *BP.line}); + if (BP.column) + result.insert({"column", *BP.column}); + if (BP.endLine) + result.insert({"endLine", *BP.endLine}); + if (BP.endColumn) + result.insert({"endColumn", *BP.endColumn}); + if (BP.instructionReference) + result.insert({"instructionReference", *BP.instructionReference}); + if (BP.offset) + result.insert({"offset", *BP.offset}); + if (BP.reason) { + result.insert({"reason", *BP.reason}); + } + + return result; +} + +bool fromJSON(const llvm::json::Value &Params, SourceBreakpoint &SB, + llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("line", SB.line) && O.map("column", SB.column) && + O.map("condition", SB.condition) && + O.map("hitCondition", SB.hitCondition) && + O.map("logMessage", SB.logMessage) && O.map("mode", SB.mode); +} + +bool fromJSON(const llvm::json::Value &Params, FunctionBreakpoint &FB, + llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("name", FB.name) && O.map("condition", FB.condition) && + O.map("hitCondition", FB.hitCondition); +} + +bool fromJSON(const llvm::json::Value &Params, DataBreakpointAccessType &DBAT, + llvm::json::Path P) { + auto rawAccessType = Params.getAsString(); + if (!rawAccessType) { + P.report("expected a string"); + return false; + } + std::optional accessType = + StringSwitch>(*rawAccessType) + .Case("read", eDataBreakpointAccessTypeRead) + .Case("write", eDataBreakpointAccessTypeWrite) + .Case("readWrite", eDataBreakpointAccessTypeReadWrite) + .Default(std::nullopt); + if (!accessType) { + P.report("unexpected value, expected 'read', 'write', or 'readWrite'"); + return false; + } + DBAT = *accessType; + return true; +} + +llvm::json::Value toJSON(const DataBreakpointAccessType &DBAT) { + switch (DBAT) { + case eDataBreakpointAccessTypeRead: + return "read"; + case eDataBreakpointAccessTypeWrite: + return "write"; + case eDataBreakpointAccessTypeReadWrite: + return "readWrite"; + } + llvm_unreachable("unhandled data breakpoint access type."); +} + +bool fromJSON(const llvm::json::Value &Params, DataBreakpointInfo &DBI, + llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("dataId", DBI.dataId) && + O.map("accessType", DBI.accessType) && + O.map("condition", DBI.condition) && + O.map("hitCondition", DBI.hitCondition); +} + +bool fromJSON(const llvm::json::Value &Params, InstructionBreakpoint &IB, + llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("instructionReference", IB.instructionReference) && + O.map("offset", IB.offset) && O.map("condition", IB.condition) && + O.map("hitCondition", IB.hitCondition) && O.map("mode", IB.mode); +} + } // namespace lldb_dap::protocol diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h index 8ea483d810e02..1f0cb1e0b2d41 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h @@ -20,6 +20,7 @@ #ifndef LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_TYPES_H #define LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_TYPES_H +#include "lldb/lldb-defines.h" #include "llvm/ADT/DenseSet.h" #include "llvm/Support/JSON.h" #include @@ -273,6 +274,7 @@ enum PresentationHint : unsigned { ePresentationHintEmphasize, ePresentationHintDeemphasize, }; +llvm::json::Value toJSON(PresentationHint hint); /// A `Source` is a descriptor for source code. It is returned from the debug /// adapter as part of a `StackFrame` and it is used by clients when specifying @@ -302,6 +304,7 @@ struct Source { // unsupported keys: origin, sources, adapterData, checksums }; bool fromJSON(const llvm::json::Value &, Source &, llvm::json::Path); +llvm::json::Value toJSON(const Source &); /// The granularity of one `step` in the stepping requests `next`, `stepIn`, /// `stepOut` and `stepBack`. @@ -350,6 +353,187 @@ struct BreakpointLocation { }; llvm::json::Value toJSON(const BreakpointLocation &); +/// A machine-readable explanation of why a breakpoint may not be verified. +enum class BreakpointReason : unsigned { + /// Indicates a breakpoint might be verified in the future, but + /// the adapter cannot verify it in the current state. + eBreakpointReasonPending, + /// Indicates a breakpoint was not able to be verified, and the + /// adapter does not believe it can be verified without intervention. + eBreakpointReasonFailed, +}; +llvm::json::Value toJSON(const BreakpointReason &); + +/// Information about a breakpoint created in `setBreakpoints`, +/// `setFunctionBreakpoints`, `setInstructionBreakpoints`, or +/// `setDataBreakpoints` requests. +struct Breakpoint { + /// The identifier for the breakpoint. It is needed if breakpoint events are + /// used to update or remove breakpoints. + std::optional id; + + /// If true, the breakpoint could be set (but not necessarily at the desired + /// location). + bool verified = false; + + /// A message about the state of the breakpoint. + /// This is shown to the user and can be used to explain why a breakpoint + /// could not be verified. + std::optional message; + + /// The source where the breakpoint is located. + std::optional source; + + /// The start line of the actual range covered by the breakpoint. + std::optional line; + + /// Start position of the source range covered by the breakpoint. It is + /// measured in UTF-16 code units and the client capability `columnsStartAt1` + /// determines whether it is 0- or 1-based. + std::optional column; + + /// The end line of the actual range covered by the breakpoint. + std::optional endLine; + + /// End position of the source range covered by the breakpoint. It is measured + /// in UTF-16 code units and the client capability `columnsStartAt1` + /// determines whether it is 0- or 1-based. If no end line is given, then the + /// end column is assumed to be in the start line. + std::optional endColumn; + + /// A memory reference to where the breakpoint is set. + std::optional instructionReference; + + /// The offset from the instruction reference. + /// This can be negative. + std::optional offset; + + /// A machine-readable explanation of why a breakpoint may not be verified. If + /// a breakpoint is verified or a specific reason is not known, the adapter + /// should omit this property. + std::optional reason; +}; +llvm::json::Value toJSON(const Breakpoint &); + +/// Properties of a breakpoint or logpoint passed to the `setBreakpoints` +/// request +struct SourceBreakpoint { + /// The source line of the breakpoint or logpoint. + uint32_t line = LLDB_INVALID_LINE_NUMBER; + + /// Start position within source line of the breakpoint or logpoint. It is + /// measured in UTF-16 code units and the client capability `columnsStartAt1` + /// determines whether it is 0- or 1-based. + std::optional column; + + /// The expression for conditional breakpoints. + /// It is only honored by a debug adapter if the corresponding capability + /// `supportsConditionalBreakpoints` is true. + std::optional condition; + + /// The expression that controls how many hits of the breakpoint are ignored. + /// The debug adapter is expected to interpret the expression as needed. + /// The attribute is only honored by a debug adapter if the corresponding + /// capability `supportsHitConditionalBreakpoints` is true. + /// If both this property and `condition` are specified, `hitCondition` should + /// be evaluated only if the `condition` is met, and the debug adapter should + /// stop only if both conditions are met. + std::optional hitCondition; + + /// If this attribute exists and is non-empty, the debug adapter must not + /// 'break' (stop) + /// but log the message instead. Expressions within `{}` are interpolated. + /// The attribute is only honored by a debug adapter if the corresponding + /// capability `supportsLogPoints` is true. + /// If either `hitCondition` or `condition` is specified, then the message + /// should only be logged if those conditions are met. + std::optional logMessage; + + /// The mode of this breakpoint. If defined, this must be one of the + /// `breakpointModes` the debug adapter advertised in its `Capabilities`. + std::optional mode; +}; +bool fromJSON(const llvm::json::Value &, SourceBreakpoint &, llvm::json::Path); + +/// Properties of a breakpoint passed to the `setFunctionBreakpoints` request. +struct FunctionBreakpoint { + /// The name of the function. + std::string name; + + /// An expression for conditional breakpoints. + /// It is only honored by a debug adapter if the corresponding capability + /// `supportsConditionalBreakpoints` is true. + std::optional condition; + + /// An expression that controls how many hits of the breakpoint are ignored. + /// The debug adapter is expected to interpret the expression as needed. + /// The attribute is only honored by a debug adapter if the corresponding + /// capability `supportsHitConditionalBreakpoints` is true. + std::optional hitCondition; +}; +bool fromJSON(const llvm::json::Value &, FunctionBreakpoint &, + llvm::json::Path); + +/// This enumeration defines all possible access types for data breakpoints. +/// Values: ‘read’, ‘write’, ‘readWrite’ +enum DataBreakpointAccessType : unsigned { + eDataBreakpointAccessTypeRead, + eDataBreakpointAccessTypeWrite, + eDataBreakpointAccessTypeReadWrite +}; +bool fromJSON(const llvm::json::Value &, DataBreakpointAccessType &, + llvm::json::Path); +llvm::json::Value toJSON(const DataBreakpointAccessType &); + +/// Properties of a data breakpoint passed to the `setDataBreakpoints` request. +struct DataBreakpointInfo { + /// An id representing the data. This id is returned from the + /// `dataBreakpointInfo` request. + std::string dataId; + + /// The access type of the data. + std::optional accessType; + + /// An expression for conditional breakpoints. + std::optional condition; + + /// An expression that controls how many hits of the breakpoint are ignored. + /// The debug adapter is expected to interpret the expression as needed. + std::optional hitCondition; +}; +bool fromJSON(const llvm::json::Value &, DataBreakpointInfo &, + llvm::json::Path); + +/// Properties of a breakpoint passed to the `setInstructionBreakpoints` request +struct InstructionBreakpoint { + /// The instruction reference of the breakpoint. + /// This should be a memory or instruction pointer reference from an + /// `EvaluateResponse`, `Variable`, `StackFrame`, `GotoTarget`, or + /// `Breakpoint`. + std::string instructionReference; + + /// The offset from the instruction reference in bytes. + /// This can be negative. + std::optional offset; + + /// An expression for conditional breakpoints. + /// It is only honored by a debug adapter if the corresponding capability + /// `supportsConditionalBreakpoints` is true. + std::optional condition; + + /// An expression that controls how many hits of the breakpoint are ignored. + /// The debug adapter is expected to interpret the expression as needed. + /// The attribute is only honored by a debug adapter if the corresponding + /// capability `supportsHitConditionalBreakpoints` is true. + std::optional hitCondition; + + /// The mode of this breakpoint. If defined, this must be one of the + /// `breakpointModes` the debug adapter advertised in its `Capabilities`. + std::optional mode; +}; +bool fromJSON(const llvm::json::Value &, InstructionBreakpoint &, + llvm::json::Path); + } // namespace lldb_dap::protocol #endif diff --git a/lldb/tools/lldb-dap/SourceBreakpoint.cpp b/lldb/tools/lldb-dap/SourceBreakpoint.cpp index a7e00cae36fbc..4581c995b4260 100644 --- a/lldb/tools/lldb-dap/SourceBreakpoint.cpp +++ b/lldb/tools/lldb-dap/SourceBreakpoint.cpp @@ -26,13 +26,12 @@ namespace lldb_dap { -SourceBreakpoint::SourceBreakpoint(DAP &dap, const llvm::json::Object &obj) - : Breakpoint(dap, obj), - m_log_message(GetString(obj, "logMessage").value_or("").str()), - m_line( - GetInteger(obj, "line").value_or(LLDB_INVALID_LINE_NUMBER)), - m_column(GetInteger(obj, "column") - .value_or(LLDB_INVALID_COLUMN_NUMBER)) {} +SourceBreakpoint::SourceBreakpoint(DAP &dap, + const protocol::SourceBreakpoint &breakpoint) + : Breakpoint(dap, breakpoint.condition, breakpoint.hitCondition), + m_log_message(breakpoint.logMessage.value_or("")), + m_line(breakpoint.line), + m_column(breakpoint.column.value_or(LLDB_INVALID_COLUMN_NUMBER)) {} void SourceBreakpoint::SetBreakpoint(const llvm::StringRef source_path) { lldb::SBMutex lock = m_dap.GetAPIMutex(); diff --git a/lldb/tools/lldb-dap/SourceBreakpoint.h b/lldb/tools/lldb-dap/SourceBreakpoint.h index d01411547d12a..5b15296f861c5 100644 --- a/lldb/tools/lldb-dap/SourceBreakpoint.h +++ b/lldb/tools/lldb-dap/SourceBreakpoint.h @@ -11,6 +11,7 @@ #include "Breakpoint.h" #include "DAPForward.h" +#include "Protocol/ProtocolTypes.h" #include "lldb/API/SBError.h" #include "llvm/ADT/StringRef.h" #include @@ -21,7 +22,7 @@ namespace lldb_dap { class SourceBreakpoint : public Breakpoint { public: - SourceBreakpoint(DAP &d, const llvm::json::Object &obj); + SourceBreakpoint(DAP &d, const protocol::SourceBreakpoint &breakpoint); // Set this breakpoint in LLDB as a new breakpoint void SetBreakpoint(const llvm::StringRef source_path); diff --git a/lldb/tools/lldb-dap/Watchpoint.cpp b/lldb/tools/lldb-dap/Watchpoint.cpp index a94cbcdbc4122..73ed4fdbae1b8 100644 --- a/lldb/tools/lldb-dap/Watchpoint.cpp +++ b/lldb/tools/lldb-dap/Watchpoint.cpp @@ -8,25 +8,24 @@ #include "Watchpoint.h" #include "DAP.h" -#include "JSONUtils.h" +#include "Protocol/ProtocolTypes.h" #include "lldb/API/SBTarget.h" #include "lldb/lldb-enumerations.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" -#include "llvm/Support/JSON.h" #include #include namespace lldb_dap { -Watchpoint::Watchpoint(DAP &d, const llvm::json::Object &obj) - : BreakpointBase(d, obj) { - llvm::StringRef dataId = GetString(obj, "dataId").value_or(""); - std::string accessType = GetString(obj, "accessType").value_or("").str(); +Watchpoint::Watchpoint(DAP &d, const protocol::DataBreakpointInfo &breakpoint) + : BreakpointBase(d, breakpoint.condition, breakpoint.hitCondition) { + llvm::StringRef dataId = breakpoint.dataId; auto [addr_str, size_str] = dataId.split('/'); llvm::to_integer(addr_str, m_addr, 16); llvm::to_integer(size_str, m_size); - m_options.SetWatchpointTypeRead(accessType != "write"); - if (accessType != "read") + m_options.SetWatchpointTypeRead(breakpoint.accessType != + protocol::eDataBreakpointAccessTypeWrite); + if (breakpoint.accessType != protocol::eDataBreakpointAccessTypeRead) m_options.SetWatchpointTypeWrite(lldb::eWatchpointWriteTypeOnModify); } @@ -38,14 +37,17 @@ void Watchpoint::SetHitCondition() { m_wp.SetIgnoreCount(hitCount - 1); } -void Watchpoint::CreateJsonObject(llvm::json::Object &object) { +protocol::Breakpoint Watchpoint::ToProtocolBreakpoint() { + protocol::Breakpoint breakpoint; if (!m_error.IsValid() || m_error.Fail()) { - object.try_emplace("verified", false); + breakpoint.verified = false; if (m_error.Fail()) - EmplaceSafeString(object, "message", m_error.GetCString()); + breakpoint.message = m_error.GetCString(); } else { - object.try_emplace("verified", true); + breakpoint.verified = true; } + + return breakpoint; } void Watchpoint::SetWatchpoint() { diff --git a/lldb/tools/lldb-dap/Watchpoint.h b/lldb/tools/lldb-dap/Watchpoint.h index bf52b41281f29..b7fe58fe73501 100644 --- a/lldb/tools/lldb-dap/Watchpoint.h +++ b/lldb/tools/lldb-dap/Watchpoint.h @@ -11,6 +11,7 @@ #include "BreakpointBase.h" #include "DAPForward.h" +#include "Protocol/ProtocolTypes.h" #include "lldb/API/SBError.h" #include "lldb/API/SBWatchpoint.h" #include "lldb/API/SBWatchpointOptions.h" @@ -21,12 +22,13 @@ namespace lldb_dap { class Watchpoint : public BreakpointBase { public: - Watchpoint(DAP &d, const llvm::json::Object &obj); + Watchpoint(DAP &d, const protocol::DataBreakpointInfo &breakpoint); Watchpoint(DAP &d, lldb::SBWatchpoint wp) : BreakpointBase(d), m_wp(wp) {} void SetCondition() override; void SetHitCondition() override; - void CreateJsonObject(llvm::json::Object &object) override; + + protocol::Breakpoint ToProtocolBreakpoint() override; void SetWatchpoint(); diff --git a/llvm/include/llvm/Support/JSON.h b/llvm/include/llvm/Support/JSON.h index f1f4f4db709dd..0a6f3f2cb7755 100644 --- a/llvm/include/llvm/Support/JSON.h +++ b/llvm/include/llvm/Support/JSON.h @@ -781,7 +781,7 @@ inline bool fromJSON(const Value &E, unsigned int &Out, Path P) { Out = *S; return true; } - P.report("expected integer"); + P.report("expected unsigned integer"); return false; } inline bool fromJSON(const Value &E, uint64_t &Out, Path P) { From lldb-commits at lists.llvm.org Fri May 9 15:16:55 2025 From: lldb-commits at lists.llvm.org (David Peixotto via lldb-commits) Date: Fri, 09 May 2025 15:16:55 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB][SBSaveCoreOptions] Add new API to expose the expected core size in bytes (PR #138169) In-Reply-To: Message-ID: <681e7ed7.170a0220.a5bf0.b9d5@mx.google.com> https://github.com/dmpots approved this pull request. LGTM. https://github.com/llvm/llvm-project/pull/138169 From lldb-commits at lists.llvm.org Fri May 9 15:29:25 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Fri, 09 May 2025 15:29:25 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB][SBSaveCoreOptions] Add new API to expose the expected core size in bytes (PR #138169) In-Reply-To: Message-ID: <681e81c5.170a0220.3a842.b224@mx.google.com> https://github.com/Jlalond updated https://github.com/llvm/llvm-project/pull/138169 >From ef04502d17c36044cd5fb96f333c328c8215f354 Mon Sep 17 00:00:00 2001 From: Jacob Lalonde Date: Thu, 1 May 2025 10:11:10 -0700 Subject: [PATCH 1/7] Add new API to expose the expected size in bytes of a core before generation --- lldb/include/lldb/API/SBSaveCoreOptions.h | 13 ++++++++++++ lldb/include/lldb/Symbol/SaveCoreOptions.h | 2 ++ lldb/source/API/SBSaveCoreOptions.cpp | 5 +++++ lldb/source/Symbol/SaveCoreOptions.cpp | 18 ++++++++++++++++ .../TestSBSaveCoreOptions.py | 21 +++++++++++++++++++ .../sbsavecoreoptions/basic_minidump.yaml | 5 +++++ 6 files changed, 64 insertions(+) diff --git a/lldb/include/lldb/API/SBSaveCoreOptions.h b/lldb/include/lldb/API/SBSaveCoreOptions.h index c6d2ab6099b3c..4c051353a714e 100644 --- a/lldb/include/lldb/API/SBSaveCoreOptions.h +++ b/lldb/include/lldb/API/SBSaveCoreOptions.h @@ -119,6 +119,19 @@ class LLDB_API SBSaveCoreOptions { /// an empty collection will be returned. SBThreadCollection GetThreadsToSave() const; + /// Get the current total number of bytes the core is expected to be but not + /// including the overhead of the core file format. Requires a Process and + /// Style to be specified. + /// + /// \note + /// This can cause some modification of the underlying data store + /// as regions with no permissions, or invalid permissions will be removed + /// and stacks will be minified up to their stack pointer + the redzone. + /// + /// \returns + /// The expected size of the data contained in the core in bytes. + uint64_t GetCurrentSizeInBytes(SBError &error); + /// Reset all options. void Clear(); diff --git a/lldb/include/lldb/Symbol/SaveCoreOptions.h b/lldb/include/lldb/Symbol/SaveCoreOptions.h index bcf0087fbea5c..319d44a6b0c87 100644 --- a/lldb/include/lldb/Symbol/SaveCoreOptions.h +++ b/lldb/include/lldb/Symbol/SaveCoreOptions.h @@ -49,6 +49,8 @@ class SaveCoreOptions { lldb_private::ThreadCollection::collection GetThreadsToSave() const; + uint64_t GetCurrentSizeInBytes(Status &error); + void Clear(); private: diff --git a/lldb/source/API/SBSaveCoreOptions.cpp b/lldb/source/API/SBSaveCoreOptions.cpp index 35b9da569dfa1..b67df513fe91b 100644 --- a/lldb/source/API/SBSaveCoreOptions.cpp +++ b/lldb/source/API/SBSaveCoreOptions.cpp @@ -114,6 +114,11 @@ void SBSaveCoreOptions::Clear() { m_opaque_up->Clear(); } +uint64_t SBSaveCoreOptions::GetCurrentSizeInBytes(SBError &error) { + LLDB_INSTRUMENT_VA(this, error); + return m_opaque_up->GetCurrentSizeInBytes(error.ref()); +} + lldb_private::SaveCoreOptions &SBSaveCoreOptions::ref() const { return *m_opaque_up.get(); } diff --git a/lldb/source/Symbol/SaveCoreOptions.cpp b/lldb/source/Symbol/SaveCoreOptions.cpp index c9f6efeb25d22..1da3e1cc9f834 100644 --- a/lldb/source/Symbol/SaveCoreOptions.cpp +++ b/lldb/source/Symbol/SaveCoreOptions.cpp @@ -145,6 +145,24 @@ SaveCoreOptions::GetThreadsToSave() const { return thread_collection; } +uint64_t SaveCoreOptions::GetCurrentSizeInBytes(Status &error) { + if (!m_process_sp) { + error = Status::FromErrorString("Requires a process to be set."); + return 0; + } + + CoreFileMemoryRanges ranges; + error = m_process_sp->CalculateCoreFileSaveRanges(*this, ranges); + if (error.Fail()) + return 0; + + uint64_t total_in_bytes = 0; + for (auto& core_range : ranges) + total_in_bytes += core_range.data.range.size(); + + return total_in_bytes; +} + void SaveCoreOptions::ClearProcessSpecificData() { // Deliberately not following the formatter style here to indicate that // this method will be expanded in the future. diff --git a/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py b/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py index ace84e8497a59..215f8440cc68a 100644 --- a/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py +++ b/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py @@ -104,3 +104,24 @@ def test_removing_and_adding_insertion_order(self): thread_collection = options.GetThreadsToSave() self.assertEqual(thread_collection.GetSize(), 3) self.assertIn(middle_thread, thread_collection) + + def test_get_total_in_bytes(self): + """ + Tests that get total in bytes properly returns an error without a process, + and the readable regions with a process. + """ + + options = lldb.SBSaveCoreOptions() + options.SetStyle(lldb.eSaveCoreCustomOnly) + process = self.get_basic_process() + memory_range = lldb.SBMemoryRegionInfo() + process.GetMemoryRegionInfo(0x7FFF12A84030, memory_range) + options.AddMemoryRegionToSave(memory_range) + error = lldb.SBError() + total = options.GetCurrentSizeInBytes(error) + self.assertTrue(error.Fail(), error.GetCString()) + options.SetProcess(process) + total = options.GetCurrentSizeInBytes(error) + self.assertTrue(error.Success(), error.GetCString()) + expected_size = memory_range.GetRegionEnd() - memory_range.GetRegionBase() + self.assertEqual(total, expected_size) diff --git a/lldb/test/API/python_api/sbsavecoreoptions/basic_minidump.yaml b/lldb/test/API/python_api/sbsavecoreoptions/basic_minidump.yaml index 96302fbfb6b5c..5033787019d7b 100644 --- a/lldb/test/API/python_api/sbsavecoreoptions/basic_minidump.yaml +++ b/lldb/test/API/python_api/sbsavecoreoptions/basic_minidump.yaml @@ -34,3 +34,8 @@ Streams: Stack: Start of Memory Range: 0x00007FFFC8DFF000 Content: 'BAADBEEF' + - Type: Memory64List + Memory Ranges: + - Start of Memory Range: 0x7FFF12A84030 + Data Size: 0x2FD0 + Content : '' >From 6f41d701d87dbcd1a09debc490ee954bfc4049c2 Mon Sep 17 00:00:00 2001 From: Jacob Lalonde Date: Thu, 1 May 2025 10:17:00 -0700 Subject: [PATCH 2/7] Add check to EnsureValidConfiguration to filter out some corner cases --- lldb/include/lldb/API/SBSaveCoreOptions.h | 6 +++--- lldb/source/Symbol/SaveCoreOptions.cpp | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lldb/include/lldb/API/SBSaveCoreOptions.h b/lldb/include/lldb/API/SBSaveCoreOptions.h index 4c051353a714e..2066ab3e8246c 100644 --- a/lldb/include/lldb/API/SBSaveCoreOptions.h +++ b/lldb/include/lldb/API/SBSaveCoreOptions.h @@ -120,11 +120,11 @@ class LLDB_API SBSaveCoreOptions { SBThreadCollection GetThreadsToSave() const; /// Get the current total number of bytes the core is expected to be but not - /// including the overhead of the core file format. Requires a Process and + /// including the overhead of the core file format. Requires a Process and /// Style to be specified. - /// + /// /// \note - /// This can cause some modification of the underlying data store + /// This can cause some modification of the underlying data store /// as regions with no permissions, or invalid permissions will be removed /// and stacks will be minified up to their stack pointer + the redzone. /// diff --git a/lldb/source/Symbol/SaveCoreOptions.cpp b/lldb/source/Symbol/SaveCoreOptions.cpp index 1da3e1cc9f834..cf6fc99f7236e 100644 --- a/lldb/source/Symbol/SaveCoreOptions.cpp +++ b/lldb/source/Symbol/SaveCoreOptions.cpp @@ -151,13 +151,17 @@ uint64_t SaveCoreOptions::GetCurrentSizeInBytes(Status &error) { return 0; } + error = EnsureValidConfiguration(m_process_sp); + if (error.Fail()) + return 0; + CoreFileMemoryRanges ranges; error = m_process_sp->CalculateCoreFileSaveRanges(*this, ranges); if (error.Fail()) return 0; uint64_t total_in_bytes = 0; - for (auto& core_range : ranges) + for (auto &core_range : ranges) total_in_bytes += core_range.data.range.size(); return total_in_bytes; >From 53d8c0b4ec3f55db71f5d1aa3c56ebbaa860f807 Mon Sep 17 00:00:00 2001 From: Jacob Lalonde Date: Thu, 1 May 2025 10:20:51 -0700 Subject: [PATCH 3/7] Py formatting --- .../API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py b/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py index 215f8440cc68a..a8a6302d7cf13 100644 --- a/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py +++ b/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py @@ -107,7 +107,7 @@ def test_removing_and_adding_insertion_order(self): def test_get_total_in_bytes(self): """ - Tests that get total in bytes properly returns an error without a process, + Tests that get total in bytes properly returns an error without a process, and the readable regions with a process. """ >From ef3cf723251225f46b4e0bff980b86cd1d05c0bb Mon Sep 17 00:00:00 2001 From: Jacob Lalonde Date: Fri, 2 May 2025 14:36:12 -0700 Subject: [PATCH 4/7] Move to llvm::expected, clean up comments --- lldb/include/lldb/API/SBSaveCoreOptions.h | 4 ++-- lldb/include/lldb/Symbol/SaveCoreOptions.h | 2 +- lldb/source/API/SBSaveCoreOptions.cpp | 9 ++++++++- lldb/source/Symbol/SaveCoreOptions.cpp | 14 +++++++------- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/lldb/include/lldb/API/SBSaveCoreOptions.h b/lldb/include/lldb/API/SBSaveCoreOptions.h index 2066ab3e8246c..37552c13d0f36 100644 --- a/lldb/include/lldb/API/SBSaveCoreOptions.h +++ b/lldb/include/lldb/API/SBSaveCoreOptions.h @@ -119,8 +119,8 @@ class LLDB_API SBSaveCoreOptions { /// an empty collection will be returned. SBThreadCollection GetThreadsToSave() const; - /// Get the current total number of bytes the core is expected to be but not - /// including the overhead of the core file format. Requires a Process and + /// Get the current total number of bytes the core is expected to have + /// excluding the overhead of the core file format. Requires a Process and /// Style to be specified. /// /// \note diff --git a/lldb/include/lldb/Symbol/SaveCoreOptions.h b/lldb/include/lldb/Symbol/SaveCoreOptions.h index 319d44a6b0c87..da66b184745db 100644 --- a/lldb/include/lldb/Symbol/SaveCoreOptions.h +++ b/lldb/include/lldb/Symbol/SaveCoreOptions.h @@ -49,7 +49,7 @@ class SaveCoreOptions { lldb_private::ThreadCollection::collection GetThreadsToSave() const; - uint64_t GetCurrentSizeInBytes(Status &error); + llvm::Expected GetCurrentSizeInBytes(); void Clear(); diff --git a/lldb/source/API/SBSaveCoreOptions.cpp b/lldb/source/API/SBSaveCoreOptions.cpp index b67df513fe91b..410fb673b347a 100644 --- a/lldb/source/API/SBSaveCoreOptions.cpp +++ b/lldb/source/API/SBSaveCoreOptions.cpp @@ -116,7 +116,14 @@ void SBSaveCoreOptions::Clear() { uint64_t SBSaveCoreOptions::GetCurrentSizeInBytes(SBError &error) { LLDB_INSTRUMENT_VA(this, error); - return m_opaque_up->GetCurrentSizeInBytes(error.ref()); + llvm::Expected expected_bytes = m_opaque_up->GetCurrentSizeInBytes(); + if (!expected_bytes) { + error = SBError(lldb_private::Status::FromError(expected_bytes.takeError())); + return 0; + } + // Clear the error, so if the clearer uses it we set it to success. + error.Clear(); + return *expected_bytes; } lldb_private::SaveCoreOptions &SBSaveCoreOptions::ref() const { diff --git a/lldb/source/Symbol/SaveCoreOptions.cpp b/lldb/source/Symbol/SaveCoreOptions.cpp index cf6fc99f7236e..9f4351fd54a7c 100644 --- a/lldb/source/Symbol/SaveCoreOptions.cpp +++ b/lldb/source/Symbol/SaveCoreOptions.cpp @@ -145,20 +145,20 @@ SaveCoreOptions::GetThreadsToSave() const { return thread_collection; } -uint64_t SaveCoreOptions::GetCurrentSizeInBytes(Status &error) { - if (!m_process_sp) { - error = Status::FromErrorString("Requires a process to be set."); - return 0; - } +llvm::Expected SaveCoreOptions::GetCurrentSizeInBytes() { + Status error; + if (!m_process_sp) + return Status::FromErrorString("Requires a process to be set.").takeError(); + error = EnsureValidConfiguration(m_process_sp); if (error.Fail()) - return 0; + return error.takeError(); CoreFileMemoryRanges ranges; error = m_process_sp->CalculateCoreFileSaveRanges(*this, ranges); if (error.Fail()) - return 0; + return error.takeError(); uint64_t total_in_bytes = 0; for (auto &core_range : ranges) >From a82385393fcaea7f472651d2e259005fd406aacb Mon Sep 17 00:00:00 2001 From: Jacob Lalonde Date: Fri, 2 May 2025 14:37:59 -0700 Subject: [PATCH 5/7] Add a new docstring --- lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i | 5 +++++ lldb/source/API/SBSaveCoreOptions.cpp | 8 +++++--- lldb/source/Symbol/SaveCoreOptions.cpp | 1 - 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i b/lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i index 6efbe45d2d3ab..b676a00bd1113 100644 --- a/lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i +++ b/lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i @@ -63,6 +63,11 @@ Note that currently ELF Core files are not supported." Get an SBThreadCollection of all threads marked to be saved. This collection is not sorted according to insertion order." ) lldb::SBSaveCoreOptions::GetThreadsToSave; +%feature("docstring", " + Get the current total number of bytes the core is expectd to have, excluding the overhead of the core file format. + Requires both a Process and a Style to be specified." +) lldb::SBSaveCoreOptions::GetCurrentSizeInBytes; + %feature("docstring", " Unset all options." ) lldb::SBSaveCoreOptions::Clear; diff --git a/lldb/source/API/SBSaveCoreOptions.cpp b/lldb/source/API/SBSaveCoreOptions.cpp index 410fb673b347a..e101f6a25783c 100644 --- a/lldb/source/API/SBSaveCoreOptions.cpp +++ b/lldb/source/API/SBSaveCoreOptions.cpp @@ -116,14 +116,16 @@ void SBSaveCoreOptions::Clear() { uint64_t SBSaveCoreOptions::GetCurrentSizeInBytes(SBError &error) { LLDB_INSTRUMENT_VA(this, error); - llvm::Expected expected_bytes = m_opaque_up->GetCurrentSizeInBytes(); + llvm::Expected expected_bytes = + m_opaque_up->GetCurrentSizeInBytes(); if (!expected_bytes) { - error = SBError(lldb_private::Status::FromError(expected_bytes.takeError())); + error = + SBError(lldb_private::Status::FromError(expected_bytes.takeError())); return 0; } // Clear the error, so if the clearer uses it we set it to success. error.Clear(); - return *expected_bytes; + return *expected_bytes; } lldb_private::SaveCoreOptions &SBSaveCoreOptions::ref() const { diff --git a/lldb/source/Symbol/SaveCoreOptions.cpp b/lldb/source/Symbol/SaveCoreOptions.cpp index 9f4351fd54a7c..e51ae27954934 100644 --- a/lldb/source/Symbol/SaveCoreOptions.cpp +++ b/lldb/source/Symbol/SaveCoreOptions.cpp @@ -150,7 +150,6 @@ llvm::Expected SaveCoreOptions::GetCurrentSizeInBytes() { if (!m_process_sp) return Status::FromErrorString("Requires a process to be set.").takeError(); - error = EnsureValidConfiguration(m_process_sp); if (error.Fail()) return error.takeError(); >From 9ed39146607e19fd45c83d1fa440a0d04333486e Mon Sep 17 00:00:00 2001 From: Jacob Lalonde Date: Fri, 9 May 2025 15:02:52 -0700 Subject: [PATCH 6/7] Implement feedback from Dave --- .../interface/SBSaveCoreOptionsDocstrings.i | 4 +- .../TestSBSaveCoreOptions.py | 45 +++++++++++++++++-- .../sbsavecoreoptions/basic_minidump.yaml | 7 ++- 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i b/lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i index b676a00bd1113..6907164a1b95c 100644 --- a/lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i +++ b/lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i @@ -64,8 +64,8 @@ Note that currently ELF Core files are not supported." ) lldb::SBSaveCoreOptions::GetThreadsToSave; %feature("docstring", " - Get the current total number of bytes the core is expectd to have, excluding the overhead of the core file format. - Requires both a Process and a Style to be specified." + Get the current total number of bytes the core is expected to have, excluding the overhead of the core file format. + Requires both a Process and a Style to be specified. An error will be returned if the provided options would result in no data being saved." ) lldb::SBSaveCoreOptions::GetCurrentSizeInBytes; %feature("docstring", " diff --git a/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py b/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py index a8a6302d7cf13..87e75a5f3a38e 100644 --- a/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py +++ b/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py @@ -105,9 +105,9 @@ def test_removing_and_adding_insertion_order(self): self.assertEqual(thread_collection.GetSize(), 3) self.assertIn(middle_thread, thread_collection) - def test_get_total_in_bytes(self): + def test_get_current_size_in_bytes(self): """ - Tests that get total in bytes properly returns an error without a process, + Tests that ensures GetCurrentSizeInBytes properly returns an error without a process, and the readable regions with a process. """ @@ -115,13 +115,52 @@ def test_get_total_in_bytes(self): options.SetStyle(lldb.eSaveCoreCustomOnly) process = self.get_basic_process() memory_range = lldb.SBMemoryRegionInfo() - process.GetMemoryRegionInfo(0x7FFF12A84030, memory_range) + + # Add the memory range of 0x1000-0x1100 + process.GetMemoryRegionInfo(0x1000, memory_range) options.AddMemoryRegionToSave(memory_range) + + # Check that we fail when we have no process set + # even though we added a memory region. error = lldb.SBError() total = options.GetCurrentSizeInBytes(error) self.assertTrue(error.Fail(), error.GetCString()) + + # Check that we don't get an error now that we've added a process options.SetProcess(process) total = options.GetCurrentSizeInBytes(error) self.assertTrue(error.Success(), error.GetCString()) + + # Validate the size returned is the same size as the single region we added. expected_size = memory_range.GetRegionEnd() - memory_range.GetRegionBase() self.assertEqual(total, expected_size) + + def test_get_total_in_bytes_missing_requirements(self): + """ + Tests the matrix of error responses that GetCurrentSizeInBytes + """ + + options = lldb.SBSaveCoreOptions() + + # No process, no style returns an error. + error = lldb.SBError() + total = options.GetCurrentSizeInBytes(error) + self.assertTrue(error.Fail(), error.GetCString()) + + # No process returns an error + options.SetStyle(lldb.eSaveCoreCustomOnly) + total = options.GetCurrentSizeInBytes(error) + self.assertTrue(error.Fail(), error.GetCString()) + + options.Clear() + + # No style returns an error + process = self.get_basic_process() + options.SetProcess(process) + total = options.GetCurrentSizeInBytes(error) + self.assertTrue(error.Fail(), error.GetCString()) + + # Options that result in no valid data returns an error. + options.SetStyle(lldb.eSaveCoreCustomOnly) + total = options.GetCurrentSizeInBytes(error) + self.assertTrue(error.Fail(), error.GetCString()) diff --git a/lldb/test/API/python_api/sbsavecoreoptions/basic_minidump.yaml b/lldb/test/API/python_api/sbsavecoreoptions/basic_minidump.yaml index 5033787019d7b..e79262b3a85ce 100644 --- a/lldb/test/API/python_api/sbsavecoreoptions/basic_minidump.yaml +++ b/lldb/test/API/python_api/sbsavecoreoptions/basic_minidump.yaml @@ -36,6 +36,9 @@ Streams: Content: 'BAADBEEF' - Type: Memory64List Memory Ranges: - - Start of Memory Range: 0x7FFF12A84030 - Data Size: 0x2FD0 + - Start of Memory Range: 0x1000 + Data Size: 0x100 + Content : '' + - Start of Memory Range: 0x2000 + Data Size: 0x200 Content : '' >From 27ea1b35e4c3b79af3ce3eb8024154b63788737e Mon Sep 17 00:00:00 2001 From: Jacob Lalonde Date: Fri, 9 May 2025 15:29:10 -0700 Subject: [PATCH 7/7] Formatting --- .../API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py b/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py index 87e75a5f3a38e..31e35e0285f17 100644 --- a/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py +++ b/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py @@ -115,7 +115,7 @@ def test_get_current_size_in_bytes(self): options.SetStyle(lldb.eSaveCoreCustomOnly) process = self.get_basic_process() memory_range = lldb.SBMemoryRegionInfo() - + # Add the memory range of 0x1000-0x1100 process.GetMemoryRegionInfo(0x1000, memory_range) options.AddMemoryRegionToSave(memory_range) From lldb-commits at lists.llvm.org Fri May 9 15:32:24 2025 From: lldb-commits at lists.llvm.org (Chelsea Cassanova via lldb-commits) Date: Fri, 09 May 2025 15:32:24 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][RPC] Upstream lldb-rpc-gen tool (PR #138031) In-Reply-To: Message-ID: <681e8278.050a0220.1c974c.caf5@mx.google.com> https://github.com/chelcassanova updated https://github.com/llvm/llvm-project/pull/138031 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Fri May 9 15:44:49 2025 From: lldb-commits at lists.llvm.org (Igor Kudrin via lldb-commits) Date: Fri, 09 May 2025 15:44:49 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][core] Fix getting summary of a variable pointing to r/o memory (PR #139196) In-Reply-To: Message-ID: <681e8561.a70a0220.d2712.d6d5@mx.google.com> ================ ---------------- igorkudrin wrote: Yes, the program file contains the debug information and the read-only section with the data, so both the core and program files are required. And there are no core files with the same properties already committed. The program is built from the existing `altmain.c`. https://github.com/llvm/llvm-project/pull/139196 From lldb-commits at lists.llvm.org Fri May 9 15:48:52 2025 From: lldb-commits at lists.llvm.org (Ilia Kuklin via lldb-commits) Date: Fri, 09 May 2025 15:48:52 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][TypeSystemClang] Allow arrays to be dereferenced in C/C++. (PR #135843) In-Reply-To: Message-ID: <681e8654.170a0220.18e312.a4ae@mx.google.com> https://github.com/kuilpd edited https://github.com/llvm/llvm-project/pull/135843 From lldb-commits at lists.llvm.org Fri May 9 15:49:57 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 15:49:57 -0700 (PDT) Subject: [Lldb-commits] [lldb] 7517a1b - [LLDB][SBSaveCoreOptions] Add new API to expose the expected core size in bytes (#138169) Message-ID: <681e8695.170a0220.1fd770.6349@mx.google.com> Author: Jacob Lalonde Date: 2025-05-09T15:49:54-07:00 New Revision: 7517a1bb486f397d45a776e127445596e00c55eb URL: https://github.com/llvm/llvm-project/commit/7517a1bb486f397d45a776e127445596e00c55eb DIFF: https://github.com/llvm/llvm-project/commit/7517a1bb486f397d45a776e127445596e00c55eb.diff LOG: [LLDB][SBSaveCoreOptions] Add new API to expose the expected core size in bytes (#138169) My current internal work requires some sensitivity to IO usage. I had a work around to calculate the expected size of a Minidump, but I've added this PR so an automated system could look at the expected size of an LLDB generated Minidump and then choose if it has the space or wants to generate it. There are some prerequisites to calculating the correct size, so I have the API take a reference for an SBError, I originally tried to return an SBError and instead take a uint64_t reference, but this made the API very difficult to use in python. Added a test case as well. Added: Modified: lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i lldb/include/lldb/API/SBSaveCoreOptions.h lldb/include/lldb/Symbol/SaveCoreOptions.h lldb/source/API/SBSaveCoreOptions.cpp lldb/source/Symbol/SaveCoreOptions.cpp lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py lldb/test/API/python_api/sbsavecoreoptions/basic_minidump.yaml Removed: ################################################################################ diff --git a/lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i b/lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i index 6efbe45d2d3ab..6907164a1b95c 100644 --- a/lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i +++ b/lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i @@ -63,6 +63,11 @@ Note that currently ELF Core files are not supported." Get an SBThreadCollection of all threads marked to be saved. This collection is not sorted according to insertion order." ) lldb::SBSaveCoreOptions::GetThreadsToSave; +%feature("docstring", " + Get the current total number of bytes the core is expected to have, excluding the overhead of the core file format. + Requires both a Process and a Style to be specified. An error will be returned if the provided options would result in no data being saved." +) lldb::SBSaveCoreOptions::GetCurrentSizeInBytes; + %feature("docstring", " Unset all options." ) lldb::SBSaveCoreOptions::Clear; diff --git a/lldb/include/lldb/API/SBSaveCoreOptions.h b/lldb/include/lldb/API/SBSaveCoreOptions.h index c6d2ab6099b3c..37552c13d0f36 100644 --- a/lldb/include/lldb/API/SBSaveCoreOptions.h +++ b/lldb/include/lldb/API/SBSaveCoreOptions.h @@ -119,6 +119,19 @@ class LLDB_API SBSaveCoreOptions { /// an empty collection will be returned. SBThreadCollection GetThreadsToSave() const; + /// Get the current total number of bytes the core is expected to have + /// excluding the overhead of the core file format. Requires a Process and + /// Style to be specified. + /// + /// \note + /// This can cause some modification of the underlying data store + /// as regions with no permissions, or invalid permissions will be removed + /// and stacks will be minified up to their stack pointer + the redzone. + /// + /// \returns + /// The expected size of the data contained in the core in bytes. + uint64_t GetCurrentSizeInBytes(SBError &error); + /// Reset all options. void Clear(); diff --git a/lldb/include/lldb/Symbol/SaveCoreOptions.h b/lldb/include/lldb/Symbol/SaveCoreOptions.h index bcf0087fbea5c..da66b184745db 100644 --- a/lldb/include/lldb/Symbol/SaveCoreOptions.h +++ b/lldb/include/lldb/Symbol/SaveCoreOptions.h @@ -49,6 +49,8 @@ class SaveCoreOptions { lldb_private::ThreadCollection::collection GetThreadsToSave() const; + llvm::Expected GetCurrentSizeInBytes(); + void Clear(); private: diff --git a/lldb/source/API/SBSaveCoreOptions.cpp b/lldb/source/API/SBSaveCoreOptions.cpp index 35b9da569dfa1..e101f6a25783c 100644 --- a/lldb/source/API/SBSaveCoreOptions.cpp +++ b/lldb/source/API/SBSaveCoreOptions.cpp @@ -114,6 +114,20 @@ void SBSaveCoreOptions::Clear() { m_opaque_up->Clear(); } +uint64_t SBSaveCoreOptions::GetCurrentSizeInBytes(SBError &error) { + LLDB_INSTRUMENT_VA(this, error); + llvm::Expected expected_bytes = + m_opaque_up->GetCurrentSizeInBytes(); + if (!expected_bytes) { + error = + SBError(lldb_private::Status::FromError(expected_bytes.takeError())); + return 0; + } + // Clear the error, so if the clearer uses it we set it to success. + error.Clear(); + return *expected_bytes; +} + lldb_private::SaveCoreOptions &SBSaveCoreOptions::ref() const { return *m_opaque_up.get(); } diff --git a/lldb/source/Symbol/SaveCoreOptions.cpp b/lldb/source/Symbol/SaveCoreOptions.cpp index c9f6efeb25d22..e51ae27954934 100644 --- a/lldb/source/Symbol/SaveCoreOptions.cpp +++ b/lldb/source/Symbol/SaveCoreOptions.cpp @@ -145,6 +145,27 @@ SaveCoreOptions::GetThreadsToSave() const { return thread_collection; } +llvm::Expected SaveCoreOptions::GetCurrentSizeInBytes() { + Status error; + if (!m_process_sp) + return Status::FromErrorString("Requires a process to be set.").takeError(); + + error = EnsureValidConfiguration(m_process_sp); + if (error.Fail()) + return error.takeError(); + + CoreFileMemoryRanges ranges; + error = m_process_sp->CalculateCoreFileSaveRanges(*this, ranges); + if (error.Fail()) + return error.takeError(); + + uint64_t total_in_bytes = 0; + for (auto &core_range : ranges) + total_in_bytes += core_range.data.range.size(); + + return total_in_bytes; +} + void SaveCoreOptions::ClearProcessSpecificData() { // Deliberately not following the formatter style here to indicate that // this method will be expanded in the future. diff --git a/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py b/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py index ace84e8497a59..31e35e0285f17 100644 --- a/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py +++ b/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py @@ -104,3 +104,63 @@ def test_removing_and_adding_insertion_order(self): thread_collection = options.GetThreadsToSave() self.assertEqual(thread_collection.GetSize(), 3) self.assertIn(middle_thread, thread_collection) + + def test_get_current_size_in_bytes(self): + """ + Tests that ensures GetCurrentSizeInBytes properly returns an error without a process, + and the readable regions with a process. + """ + + options = lldb.SBSaveCoreOptions() + options.SetStyle(lldb.eSaveCoreCustomOnly) + process = self.get_basic_process() + memory_range = lldb.SBMemoryRegionInfo() + + # Add the memory range of 0x1000-0x1100 + process.GetMemoryRegionInfo(0x1000, memory_range) + options.AddMemoryRegionToSave(memory_range) + + # Check that we fail when we have no process set + # even though we added a memory region. + error = lldb.SBError() + total = options.GetCurrentSizeInBytes(error) + self.assertTrue(error.Fail(), error.GetCString()) + + # Check that we don't get an error now that we've added a process + options.SetProcess(process) + total = options.GetCurrentSizeInBytes(error) + self.assertTrue(error.Success(), error.GetCString()) + + # Validate the size returned is the same size as the single region we added. + expected_size = memory_range.GetRegionEnd() - memory_range.GetRegionBase() + self.assertEqual(total, expected_size) + + def test_get_total_in_bytes_missing_requirements(self): + """ + Tests the matrix of error responses that GetCurrentSizeInBytes + """ + + options = lldb.SBSaveCoreOptions() + + # No process, no style returns an error. + error = lldb.SBError() + total = options.GetCurrentSizeInBytes(error) + self.assertTrue(error.Fail(), error.GetCString()) + + # No process returns an error + options.SetStyle(lldb.eSaveCoreCustomOnly) + total = options.GetCurrentSizeInBytes(error) + self.assertTrue(error.Fail(), error.GetCString()) + + options.Clear() + + # No style returns an error + process = self.get_basic_process() + options.SetProcess(process) + total = options.GetCurrentSizeInBytes(error) + self.assertTrue(error.Fail(), error.GetCString()) + + # Options that result in no valid data returns an error. + options.SetStyle(lldb.eSaveCoreCustomOnly) + total = options.GetCurrentSizeInBytes(error) + self.assertTrue(error.Fail(), error.GetCString()) diff --git a/lldb/test/API/python_api/sbsavecoreoptions/basic_minidump.yaml b/lldb/test/API/python_api/sbsavecoreoptions/basic_minidump.yaml index 96302fbfb6b5c..e79262b3a85ce 100644 --- a/lldb/test/API/python_api/sbsavecoreoptions/basic_minidump.yaml +++ b/lldb/test/API/python_api/sbsavecoreoptions/basic_minidump.yaml @@ -34,3 +34,11 @@ Streams: Stack: Start of Memory Range: 0x00007FFFC8DFF000 Content: 'BAADBEEF' + - Type: Memory64List + Memory Ranges: + - Start of Memory Range: 0x1000 + Data Size: 0x100 + Content : '' + - Start of Memory Range: 0x2000 + Data Size: 0x200 + Content : '' From lldb-commits at lists.llvm.org Fri May 9 15:50:02 2025 From: lldb-commits at lists.llvm.org (Jacob Lalonde via lldb-commits) Date: Fri, 09 May 2025 15:50:02 -0700 (PDT) Subject: [Lldb-commits] [lldb] [LLDB][SBSaveCoreOptions] Add new API to expose the expected core size in bytes (PR #138169) In-Reply-To: Message-ID: <681e869a.170a0220.156ead.de68@mx.google.com> https://github.com/Jlalond closed https://github.com/llvm/llvm-project/pull/138169 From lldb-commits at lists.llvm.org Fri May 9 16:02:09 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Fri, 09 May 2025 16:02:09 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Don't emit a removed module event for unseen modules (PR #139324) In-Reply-To: Message-ID: <681e8971.170a0220.57336.a841@mx.google.com> https://github.com/ashgti edited https://github.com/llvm/llvm-project/pull/139324 From lldb-commits at lists.llvm.org Fri May 9 16:02:09 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Fri, 09 May 2025 16:02:09 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Don't emit a removed module event for unseen modules (PR #139324) In-Reply-To: Message-ID: <681e8971.170a0220.17bb0c.b9be@mx.google.com> https://github.com/ashgti approved this pull request. One question about the logic, but otherwise LGTM https://github.com/llvm/llvm-project/pull/139324 From lldb-commits at lists.llvm.org Fri May 9 16:02:10 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Fri, 09 May 2025 16:02:10 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Don't emit a removed module event for unseen modules (PR #139324) In-Reply-To: Message-ID: <681e8972.170a0220.1f3c0b.89c6@mx.google.com> ================ @@ -1566,18 +1556,43 @@ void DAP::EventThread() { event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded || event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded || event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged) { - llvm::StringRef reason = GetModuleEventReason(event_mask); const uint32_t num_modules = lldb::SBTarget::GetNumModulesFromEvent(event); for (uint32_t i = 0; i < num_modules; ++i) { lldb::SBModule module = lldb::SBTarget::GetModuleAtIndexFromEvent(i, event); if (!module.IsValid()) continue; + llvm::StringRef module_id = module.GetUUIDString(); + if (module_id.empty()) + continue; + + llvm::StringRef reason; + bool id_only = false; + { + std::lock_guard guard(modules_mutex); ---------------- ashgti wrote: Should we grab this lock while we iterate the entire list of modules from the event? I think we could we get a modules request in the middle of processing this list, which might be a bit inconsistent when we get back to handling the events. https://github.com/llvm/llvm-project/pull/139324 From lldb-commits at lists.llvm.org Fri May 9 16:04:19 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Fri, 09 May 2025 16:04:19 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Minor visual changes to the modules UI (PR #139328) In-Reply-To: Message-ID: <681e89f3.050a0220.d1fe5.39a4@mx.google.com> https://github.com/ashgti updated https://github.com/llvm/llvm-project/pull/139328 >From e2f9bc83de4ed72dd4025de195372813a034e0e7 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Fri, 9 May 2025 14:31:30 -0700 Subject: [PATCH 1/2] [lldb-dap] Minor visual changes to the modules UI Small assortment of changes to the modules UI after trying it out: - Print the load address as hexadecimal. - Remove spurious space before colon. - Drop "Module" prefix from tooltip title. - Capitalize bold list items. --- .../lldb-dap/src-ts/ui/modules-data-provider.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts b/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts index 5af3d52e9870c..07b89b267b536 100644 --- a/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts +++ b/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts @@ -25,23 +25,25 @@ export class ModulesDataProvider } const tooltip = new vscode.MarkdownString(); - tooltip.appendMarkdown(`# Module '${module.name}'\n\n`); - tooltip.appendMarkdown(`- **id** : ${module.id}\n`); + tooltip.appendMarkdown(`# ${module.name}\n\n`); + tooltip.appendMarkdown(`- **ID** : ${module.id}\n`); if (module.addressRange) { - tooltip.appendMarkdown(`- **load address** : ${module.addressRange}\n`); + tooltip.appendMarkdown( + `- **Load address**: 0x${Number(module.addressRange).toString(16)}\n`, + ); } if (module.path) { - tooltip.appendMarkdown(`- **path** : ${module.path}\n`); + tooltip.appendMarkdown(`- **Path**: ${module.path}\n`); } if (module.version) { - tooltip.appendMarkdown(`- **version** : ${module.version}\n`); + tooltip.appendMarkdown(`- **Version**: ${module.version}\n`); } if (module.symbolStatus) { - tooltip.appendMarkdown(`- **symbol status** : ${module.symbolStatus}\n`); + tooltip.appendMarkdown(`- **Symbol status**: ${module.symbolStatus}\n`); } if (module.symbolFilePath) { tooltip.appendMarkdown( - `- **symbol file path** : ${module.symbolFilePath}\n`, + `- **Symbol file path**: ${module.symbolFilePath}\n`, ); } >From f8ade5ac2f75c6bd9da699e117ff477759333c5c Mon Sep 17 00:00:00 2001 From: John Harrison Date: Fri, 9 May 2025 16:04:11 -0700 Subject: [PATCH 2/2] Nit on whitespace. --- lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts b/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts index 07b89b267b536..478c162de8878 100644 --- a/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts +++ b/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts @@ -26,7 +26,7 @@ export class ModulesDataProvider const tooltip = new vscode.MarkdownString(); tooltip.appendMarkdown(`# ${module.name}\n\n`); - tooltip.appendMarkdown(`- **ID** : ${module.id}\n`); + tooltip.appendMarkdown(`- **ID**: ${module.id}\n`); if (module.addressRange) { tooltip.appendMarkdown( `- **Load address**: 0x${Number(module.addressRange).toString(16)}\n`, From lldb-commits at lists.llvm.org Fri May 9 16:05:17 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Fri, 09 May 2025 16:05:17 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Minor visual changes to the modules UI (PR #139328) In-Reply-To: Message-ID: <681e8a2d.170a0220.8f373.bd10@mx.google.com> https://github.com/ashgti approved this pull request. LGTM, and I didn't realize I could directly edit this, I thought I was doing a suggestion but clearly I need to learn the github pull request UI better... https://github.com/llvm/llvm-project/pull/139328 From lldb-commits at lists.llvm.org Fri May 9 16:17:44 2025 From: lldb-commits at lists.llvm.org (Igor Kudrin via lldb-commits) Date: Fri, 09 May 2025 16:17:44 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][core] Fix getting summary of a variable pointing to r/o memory (PR #139196) In-Reply-To: Message-ID: <681e8d18.170a0220.272872.9bb9@mx.google.com> https://github.com/igorkudrin updated https://github.com/llvm/llvm-project/pull/139196 >From 499f723c3f974ff53deb8f354d879e0baaa7a9e8 Mon Sep 17 00:00:00 2001 From: Igor Kudrin Date: Wed, 7 May 2025 19:55:07 -0700 Subject: [PATCH 1/3] [lldb][core] Fix getting summary of a variable pointing to r/o memory Motivation example: ``` > lldb -c altmain2.core ... (lldb) var F (const char *) F = 0x0804a000 "" ``` The variable `F` points to a read-only memory page not dumped to the core file, so `Process::ReadMemory()` cannot read the data. The patch switches to `Target::ReadMemory()`, which can read data both from the process memory and the application binary. --- lldb/source/ValueObject/ValueObject.cpp | 13 ++++++++- .../postmortem/elf-core/TestLinuxCore.py | 27 ++++++++++++++++++ .../postmortem/elf-core/altmain2.core | Bin 0 -> 40960 bytes .../postmortem/elf-core/altmain2.out | Bin 0 -> 9776 bytes 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100755 lldb/test/API/functionalities/postmortem/elf-core/altmain2.core create mode 100755 lldb/test/API/functionalities/postmortem/elf-core/altmain2.out diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp index e1c66763ff0b8..aab78428d9103 100644 --- a/lldb/source/ValueObject/ValueObject.cpp +++ b/lldb/source/ValueObject/ValueObject.cpp @@ -735,7 +735,7 @@ size_t ValueObject::GetPointeeData(DataExtractor &data, uint32_t item_idx, case eAddressTypeLoad: { ExecutionContext exe_ctx(GetExecutionContextRef()); Process *process = exe_ctx.GetProcessPtr(); - if (process) { + if (process && process->IsLiveDebugSession()) { heap_buf_ptr->SetByteSize(bytes); size_t bytes_read = process->ReadMemory( addr + offset, heap_buf_ptr->GetBytes(), bytes, error); @@ -743,6 +743,17 @@ size_t ValueObject::GetPointeeData(DataExtractor &data, uint32_t item_idx, data.SetData(data_sp); return bytes_read; } + } else if (Target *target = exe_ctx.GetTargetPtr()) { + Address target_addr; + target_addr.SetLoadAddress(addr + offset, target); + heap_buf_ptr->SetByteSize(bytes); + size_t bytes_read = + target->ReadMemory(target_addr, heap_buf_ptr->GetBytes(), bytes, + error, /*force_live_memory=*/true); + if (error.Success() || bytes_read > 0) { + data.SetData(data_sp); + return bytes_read; + } } } break; case eAddressTypeHost: { diff --git a/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py b/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py index a287fd19ba352..d1e065a32efdc 100644 --- a/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py +++ b/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py @@ -977,6 +977,33 @@ def test_get_core_file_api(self): self.assertEqual(process.GetCoreFile().GetFilename(), core_file_name) self.dbg.DeleteTarget(target) + @skipIfLLVMTargetMissing("X86") + def test_ro_cstring(self): + """ + Test that we can show the summary for a cstring variable that points + to a r/o memory page which is not dumped to a core file. + """ + target = self.dbg.CreateTarget("altmain2.out") + process = target.LoadCore("altmain2.core") + self.assertTrue(process, PROCESS_IS_VALID) + + frame = process.GetSelectedThread().GetFrameAtIndex(0) + self.assertEqual(frame.GetFunctionName(), "_start") + + var = frame.FindVariable("F") + + # The variable points to a RO segment that is not dumped to the core + # file and thus process.ReadCStringFromMemory() cannot get the value. + error = lldb.SBError() + cstr = process.ReadCStringFromMemory(var.GetValueAsUnsigned(), 256, error) + self.assertFailure(error, error_str="core file does not contain 0x804a000") + self.assertEqual(cstr, "") + + # Nevertheless, when getting the summary, the value can be read from the + # application binary. + cstr = var.GetSummary() + self.assertEqual(cstr, '"_start"') + def check_memory_regions(self, process, region_count): region_list = process.GetMemoryRegions() self.assertEqual(region_list.GetSize(), region_count) diff --git a/lldb/test/API/functionalities/postmortem/elf-core/altmain2.core b/lldb/test/API/functionalities/postmortem/elf-core/altmain2.core new file mode 100755 index 0000000000000000000000000000000000000000..b9dd8de08b813442037fcb5bedd08e8bbabfdc0b GIT binary patch literal 40960 zcmeHQ4^&jwnSa9!I!%cK#cib0IB9HSqA(1g2 at tXp8Ij5$Wtd=M$vDi6Fme86<_(|; z6+_tg`VJeLbo*!1-R7KixAr7yY1h*llO~{6F(sCWr_zMokd!oo(v~!T)FyTI_ucp2 z%tOtlZMJ8(-FH3T-0yz(yZ3(gcfb4H_udT89k#l)I-QPFl7Z86N~u&4A}{64oKY?t zsH`Y~&;E#9A!n>A8-*T&)P#5twWFNXo5Amv>t%VSoTus^om+oN`+>Rj^Db^b`}?yb z;#NyEr~PKgiY`d?X7Hdn>?ZQ1?fSGja(xN^Hy`G^{4fU zJxcBl at IkjfR9qO2^U&f7EozCGL?0 at VJw2e>np%fqkAf4~`@fOMrU4MGt74NG{OXk# z&R&C@{G$ZfEEM3jT=HFUiQ)q}D0 at MKZz=qoe9H`?K8djGEGc`2Z4s;$&G|GMWy$%K za=t*$uR=~K5A?|0$C95w4)@`ffTnjGOPY{B201d8XslfzTV3s1We#KuGnn?Dvd1XL zeqvZ7=LQT$>X&kEP~t6oFk-RJS$OWl$jG(;h{jQD+o4awcJf at xIgWgn(sIZo?tku+ ztrZ5}vHX{7>uT$r8`1R+TCmHco+V_}Whyk$Ulju66RY6^8So3uC;qCIHZ>U~_ND|M zz_2*@0XkZ>hJc2EhJc2EhQMDF0y{3c|Iff34ZBCc{Q}+pkINV6bpM~pE#pkw0;!%r zp!*BZNw7r9eFtk#H%xTT0V)7XME3)8?*HjN1VraGk-ELn%yCBCn-I~x5|MK6^|{x0 zFN&8Ws;6PKq#>Xopdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|k zpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|k zpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|kpdp|k zpdp|kpds*o4S|c_|EHJV|F;@1>!bJoZT`4qF}?rKfcO99;0=51{eG&6?EM>#BU0Y) zNAJ_k$2*o4qW6;%(fj=r8bG~5O^_2Qbq#pGp8*AhmZFYGsYg at xzJ2ySK=A7Se&7m} zX?)TleX at z{rS}Ue?_#Gql93+uG5<#6HLH#JJ&GRnvG4wlZ*k8p9%y{9sQuQ_{a5~S zr01b4|NiRxcHMB~Gp8Ty`cmG%tO<$Wp*kl~AA}vf*ZpI7YvIkvBN*fn3`%9ffm zKg!8liKq3H{(WioxC!ub`JiurC`oIr7D%4hw at HI z@~3^Ew=X2aZy_h&nnefXL#vUK-wcB29sktky<{>x4C8)}oP2H;`2ZRFM<&`wN}{pI z4oY=|7xHhJGMNoD7n-ahry}XnoT-bNdM8)0^p)@JUH$C`H$5`(nYSvgJ9pMy-K$%y zuQ1CxH>3T{;5Q+^7x`A?3FOoVCEfN$PIs?Sw>ZmS*oC at fkmZBUfyn2%ss+ZK8C46i z?$cK-$nG=L{CGiD)xibEs-Xo21xNJZkUHVGb~IT8It60;*reyp^Rl at Nz9?Il!LP{9 z$l#Y{>ofSJ*_j!9e)hZ!eo^-P41Qs at F@wMPR%qul?G>cGjI@`MwwbgSk at iB;&L-`f zsxij>pnE{HUpSM2e_Ds;dVWNgt<&?vy6g-+e^{5T*YktA>`XnsUza^k&+pS^&)4$< zx@@DKAIGFs>2r-v;Wbm~^F1wh+*m`W ziSzKaorm%+B$K@}D|*lA;#Zn<=eO+cJ-lMmovxNcz323N)_(dw6AsYFnb*aC|NKNU z+4l~ATI}_L6CZBJr(jEny#ZpJGvdP$CYcnRQ>-sxNH`_-QnRun at n?nMv%Z8dEd1=O zZw58a3E>?J)t`;+9z^OfteNKqdC8`Cao_Cqt+8NpzS0^ zL(NdmnC=ZRp2&Gu?4Hpb&v{vx)!QZolt%g&R^&RTrVC+uVm at Y?X~C9YGw)wH at pLlj z=$&a2Y%`+G*f(gp|6L#?qO_Z;gj0fZwD)zL(48>#{g!H~=NSNysqgnFu!;4ZFztGq zYPx5{x?7fspTA{=U>^r-6j~-w$`bFrWl`CX&^;-y`r*76g%?cU{ps1) zbCP#!mux7rjhg!Ig7Knr!uk?05n;>|qhqJv z88Vw}2cJwH`^O5CZLDnQNm#+dFq~{Ve(`>r1-s z$()uc(LO0UjlvZN#_X-xXI~XYQ5s6j6K*w#&MfO---&od at 8Kr4_`qW4z4Hthe~0OQ z+MjRko}M62fm76q_N at K&7*%*tY%wN6z*QJK^JCFDv-7|tOs7Wu_t7Nc^N`PA4!b9z zE_P=LSBX7S)Uw849X#`p5FZ!gGvYrpzj}%PmiTw%R{}&|A=eV0A$Jmgz`ToLBRdtX zR1fp2L-A*X?tSp7Q)hjX!k7>r5QfG0_*vhS;2ejK!NGv(UcvS_u~UL=k1#H_JTBOW zMLXQ=QR at l4ZTP5d1U7NG&Jm$yP;VPNYCBA2(Y80&c^FRDGKCN4B>ZhS2UFf7wCva0 z_KWsMkJ_G=8a at rLgSTM;MP{eoHqgKDid^Tu=`o=^)-MkHZ55p)-B0`#xm>{6pezNZ5HP!3X8;+lcIgJ!agaCFSbp^ zK8^JiMwi=1#g?hXEhhz(u;wjOrtiK86m&1jK8I~ueO10#ca at pUW$RNmYwBykvSCvR z$Kh-G at 4xqS65Gx61M--O%lIAa*7q8IluYJ6Y{X~E4VgE_7BgRDo=AQ;1V4O=fXW+- zzNUL)x)YedpUz;4{rqRR-HA4wVwn$~XZzCur`)+e|)acc>o*pF!q0+de~3Uk4*{(cfQ(- zaBRB&1_%%Xh9(WVS&A&T2g&K-r1$;F%ix~^{14st*n70+1e3DEX(z9vo%|6XAnoKm08kiH6n1_=-u at 9NV7Xqw za_!w5_W)T#{D^29?H?Gy(oa7FIX1G;J#uEH_yF6$-J{}H*#`b0ZC&x%OHKu}RyHsn z5TX7}DFnoynf7Du{h72 at LVS|NdLvGV-oXqU5?H1F{2~$F|3y-GXGj$0_P{WZJ3=x zdsxUAr;sr$*azi!p~MFS2PqPy!+;zo$OG&n6t?#qwb9iZr$fAXl!C{&&@zDNfY5MB z$k at w5#^cjp$Myg~LVT2TvXhP6Z?y_S4+fu3}jTyh{obQS1oh%vVdmqIvSC2-1S_tT>0or^8D4l-+Z z9-(8m_f)d?pswsq;ZWYY(br+iELqy~9t(GkSPN{UJITN2UauwEa8;lA;9K(bNh*Bd z65xSru&OM44UmT)K#3c}$^B_5dX> zU`vEfgV3C%M5)vT$-|hh6vqAoKwc({-3JBWKaOSSeN~BLKTgN7eW>^_ajbi89BXG$ z$k_i$7Rqc1M6>?w8Nzv?dmIjnt5ynuRJT_bFA%NbHo6ECiiPb4 at ouBIElY?Rh3#3? z=T}*58;w729h}ajUTp|$dO5I7v00t at -d{ps`@)|V*q$IyMqsnOnGS4MV6px at f$hkL z32ZDzlnqPKVVq^LWl~T(K;dKO5DODy$=*R-*>eZ56S=$>qf=5;qifMbE&?;o>iO{W zW`nT7D7dnwvkA9uggV?CA+k;my^eraPLYd553$JSO!U5raJP##cdoeJfa_J!fy>1y zaeWqD3fm`esVEAhTVYS&{I-wxFT4?*iQQBB;@s^9{b!A0%S7%SS*kzmW#@OVyAI4Q%Jb&+I-n_r(D_`UKUgAG; zls&2t))+Qf2k&e-B;J=m_h()ce!l+-788Uw6VKr>h%mN^Y7I*di3>sbOAkE-=}G8@ z at zn3T;OQJo^Gp2{%i1iWcjP$|4EiN z!ePHG%h!VMm*qcvJDI#+mOlo*TbBRg9bD?l@>Af0vfTE&WYR0k%fPqD at _XJ*CYxk= z7`$DU)58Jbc~hq8I|-8&asN-vYu2nOH!s5zoa at b2YhiI=5&nzLSbL0*^6plyuq_lX zY9pC6O+rPlK>u*tqlzkBAgVfBiDWXf1-YWP z3__wT639u>i=a$mnhQPmCR#^}DJR01QU^%sZ`VS69{?I>HPVOD^#igV^|u*|NVM)U zdbF>pO_5wj-#>ipB0Z`nBJ7lO0DAwX6p$<9sze&AA)q0kA)q0kA)q0kA)q0kA)q0k zA)q0kA)q0kA)q0kA)q0kA)q0kA at KhefsgJs(AN{`^r%#(=joq_9!pZi^~lRWYeAbk zjwN}}mqFhIJqdaRbRMMZJeK?vs1URsvEcvgXAAwGSUIvjJX3*-D7tUTog`x{*OOVat3kLdbBKfYkguZK_LJl%7h`ui< z?J*eXI}S65zLz4R?^h@(6KB6y1`hO{)M@#98OpDe%lM@$lV63LQXc4$yN at M5ft!wvM{D8l%s=lUjO`)@CZNW`kLsiqdN=rNzwcxk^?tmrM>JOEx z%BfyxP)B=C2r85?=1pKY#mSBjtcq|Ru`1Ww9#9Cr;c%uFYAG2Iq zqeze6?>a-!;>5DqreSv_b!&+FBvO*a% z)!BxKFXV8nODQx&!)^Gc)x6dp at Wsr_fb8`axsiFjAM>#mrOFMy=$3Fa=ni>&=B98s zu+`6Bz8`8~_eI-$irI5Uu{6Z7j2E}8#@D|VPkY!C4&bjZwprMU$RMk2v^0gIeAplJ z)nN4ZRq}@UknjDhTVr=`^}T;v>ev(XUf%X<{9%bNYLPSi$w)XJ_4s1y-kNLct2Z<| zE?s9yuJo&@rIu#SI?H{eD?ebJFV??!ot-g!18?!Sg`-<7RgnloyN+!XyWbNH$HH59 zWt~xt9Uwvf{>|hijagFd&9^zsYr at _+$g0~H%8SCoby5Q~lRqK1T-CLWm6m8cgkXV{wH6f>Te#|4M?+oJ?Um(4Tyxd!2sG7DlSG~F zZ2@=4TTsL`IO`lXm&4Y0o2{`j9`bidRwG2flz1TKbNHh8;RDy~s4I5WIBQ*14Gj*L zt-k8kI$L$6mE^Yi+bSt{)z-tz4GoeZ5Lik|N?raCtPV%G`YOAvvUa__sRX)-OvT}--B9l$zN)&q5k+ZU5jWp{F-EQN80T15cUxU$G1t&sU1{Ni5sQ`EP*=@L zALKtxKBxn#0#6jb8o{4lHI%J)Aud#g176Oh6f9m}hlRpwF?WXp at jW#)OJTUZAh at l7 zY;UNl!88%SVq6`WT+qnXIo52b+tBE!tm)d)<>~TvwRZX1d|r2}yUp!!dr|L_6{4}q zqH?dlvdCIdQd%AeF=kbHu)VUVs667YEG?=iDJhSInS5orS7BPcQdLEfwcMlP!%-%; zl01lRi^^m1%90x^O0DIww#t&CiqevDkDKItCo2}0hryIqloprAc&1re?(1U16_?*o z$h)JAw-VpBJ;l4D9uG$Lvz_U|NX}q#N^HDt@~(ZDz!c zY58`4B-K$`>P at pMg9FjDST#&Q)wOEG?Z84YkyjU5w-=`>TGKu9bNRFmpVsmET2oD< zkzk6yO9gSKiubAbn2L|8c(-aQ_~lWm&+XwfVxEXUT~MK?0>tA^o7U-1n-=R#^IjG2 zi+NK+xjXz at SYaiW`L0Ntiua`pL3IrZs;V|s`_`beqSRNURT`Y}Z7N>Ij^uosaJk;f~ZygQ{5v)l&-=tCY5;`A(n8 zRus~c4yz^_j7WQi4Og0O4SBlI5tf#Yl5-PAO<1k`-G&$d8H#hYfkJn50TIwgOh9=EA1^QdO)3#RqMZRy^_ZE4WmZdith zxQZ-`r`Rp{)m^F(ET!-!=>+{4H_cN#lqqpb-oo?7Bi^Jr8 z3Ll9hmdk}$1V+nzS1f{=Nrf*EQ&v=d!?g-i*y`Sz;yqDZqK5)uH#3{`3cZo7Z3SLG z#UrVuBnkOC=L&Fc%@yE0ObOJL;i#X at i}fCHN7EfgeTd`oAP7+EAyAyt4&|7;1Mx;y zk9DR8Qx6>XY?*633Dg}^Ic=SE^5O=89mT5C*Cn6tmW%Gm>}y<2_6BwdMdyFeQ^2k; aAa>MNSFS9>wE at m`bXZx$UDQ;$dHgS6POs$v literal 0 HcmV?d00001 diff --git a/lldb/test/API/functionalities/postmortem/elf-core/altmain2.out b/lldb/test/API/functionalities/postmortem/elf-core/altmain2.out new file mode 100755 index 0000000000000000000000000000000000000000..e930c3d8055e11ea8256faf244a208ff5a7bc033 GIT binary patch literal 9776 zcmeHNOK%%h6h3!6;~J)M?4%Nn3S>eHG$2o2R$BTXB*BRa77?OBV#6>VPizx=#u|@l ziV9RhNUaJ|x?{nDpTK{p0I`Av3;sYQ7D#~DL;@7fH=Y|g77MmW_bBI{*LUa6{qD_d zd_JmFH-r!*MWYNQ(Y~H5bMmLNgG!k!GUz;wQ+`lK`nH~f{>Uy=N3KAFI$PprKuhdG z4c4Q&#r~IEXFb6#ba;M_>$$J=9P}jDpjlSTfBj)xhdU~$V_7AN0mXn~Krx^gPz)#r z6a$I@#eiZ!F`yVw3>;@*`|iV!e<}R at BxjAkr-?qheemny;oZt#-&PKQyjMB+hUWIa zdvH%b_u%+c6)6T31BwB~fMP%~pcqgLC#cmWIf7#9T^-I?I z?OH#G`_{sey=2dqmitM#_~ByNo-fa)X?mPzYRT;6$NS_b|Jk9Zwb?gFB*g^x1d5Mh z;Kl%n%yc;xA}e#^i?=wTZ;;3o;c53{V;2zJd^ZWZBz$(VB71|)iMLV1Kw_NC8U5NM z&nb$h^OI&~`pk4uOk at j>5oK(#GL^|+GB2Ain9oij{nF|8rv#(*GG%gu!nH~5G`}<_ zQsf|!Jx8YbqM6C_de0D6pjTN9?pk;b3k&gQehdcP6Rhk^3v5&q)7toeBaV26AVb4W zCynAChIzklh=#^Z7b59hIAjqdZsktu`W=1 at 2u>uWYc-$yL|Yp({I40n1 at uKgX1R`d z^R;bjec?iSK31u-w8%r;5B#eQKk%cL$Dtc}&8w~Di_2E2VU>1*u;lh`23|7?gRtK# z^@AI&pk9jITHEh&ov(h8;@pkJoe~a@%poZ3VVRb8)9T zw~)Np$N+Yv==A%c^kEU3Y|-<9Sa2`O3|s6C at q{Mm3Y?US2}WGt at KlN~Fn)a`eq|&c zdP!%qy0x}ib+$G(-l$x6uCK0DD-Jo%-o<6R;m1zbbK+({*tOj~&enRpbgzzIcVm|v zKd2Mgy_+2-iftSXY*Tu%zZa7og$dejgt6UibHWQd9Y0{F?$`Pa$Bo>e;rC?N3U)&2 zyR};6zbB)1EAXYi6SQzGmOKTB)v7 at nHy-(t0P687tR5Ga*@%u zV-0$CMsfI;1}?*jb3`AHyrY~-dVFwC7g>RuXN8=yh at MZp!GId{a%u-zq7{pX|BeJ0 z2+mphh#iXvUll2`_9iE!kJ|SZQ3p;cvc^Yy40mOQXiI{IYRQ*tDE}$rULK0ESI7sf zc(&Z{4~)BoLMo_1 at 1$1Zs!tIWQw)s6!47>?=pDxGb8d_kYeEaGkFeq_r2NLXf7=Jv R7{;xfCHh=WVvF at 4{{Sg@>kj|` literal 0 HcmV?d00001 >From ff536ae4a96612fb18165d22acecf74043290476 Mon Sep 17 00:00:00 2001 From: Igor Kudrin Date: Fri, 9 May 2025 16:15:25 -0700 Subject: [PATCH 2/3] fixup: ro -> read-only --- .../functionalities/postmortem/elf-core/TestLinuxCore.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py b/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py index d1e065a32efdc..a68175dc4e4d7 100644 --- a/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py +++ b/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py @@ -978,10 +978,10 @@ def test_get_core_file_api(self): self.dbg.DeleteTarget(target) @skipIfLLVMTargetMissing("X86") - def test_ro_cstring(self): + def test_read_only_cstring(self): """ Test that we can show the summary for a cstring variable that points - to a r/o memory page which is not dumped to a core file. + to a read-only memory page which is not dumped to a core file. """ target = self.dbg.CreateTarget("altmain2.out") process = target.LoadCore("altmain2.core") @@ -992,8 +992,9 @@ def test_ro_cstring(self): var = frame.FindVariable("F") - # The variable points to a RO segment that is not dumped to the core - # file and thus process.ReadCStringFromMemory() cannot get the value. + # The variable points to a read-only segment that is not dumped to + # the core file and thus 'process.ReadCStringFromMemory()' cannot get + # the value. error = lldb.SBError() cstr = process.ReadCStringFromMemory(var.GetValueAsUnsigned(), 256, error) self.assertFailure(error, error_str="core file does not contain 0x804a000") >From 17210b1357f5544145c9dcb8d4677e116bf538a5 Mon Sep 17 00:00:00 2001 From: Igor Kudrin Date: Fri, 9 May 2025 16:13:58 -0700 Subject: [PATCH 3/3] fixup: Factor out the common code --- lldb/source/ValueObject/ValueObject.cpp | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp index aab78428d9103..1375b67d72ff6 100644 --- a/lldb/source/ValueObject/ValueObject.cpp +++ b/lldb/source/ValueObject/ValueObject.cpp @@ -734,26 +734,22 @@ size_t ValueObject::GetPointeeData(DataExtractor &data, uint32_t item_idx, } break; case eAddressTypeLoad: { ExecutionContext exe_ctx(GetExecutionContextRef()); - Process *process = exe_ctx.GetProcessPtr(); - if (process && process->IsLiveDebugSession()) { - heap_buf_ptr->SetByteSize(bytes); - size_t bytes_read = process->ReadMemory( + heap_buf_ptr->SetByteSize(bytes); + size_t bytes_read = 0; + if (Process *process = exe_ctx.GetProcessPtr(); + process && process->IsLiveDebugSession()) { + bytes_read = process->ReadMemory( addr + offset, heap_buf_ptr->GetBytes(), bytes, error); - if (error.Success() || bytes_read > 0) { - data.SetData(data_sp); - return bytes_read; - } } else if (Target *target = exe_ctx.GetTargetPtr()) { Address target_addr; target_addr.SetLoadAddress(addr + offset, target); - heap_buf_ptr->SetByteSize(bytes); - size_t bytes_read = + bytes_read = target->ReadMemory(target_addr, heap_buf_ptr->GetBytes(), bytes, error, /*force_live_memory=*/true); - if (error.Success() || bytes_read > 0) { - data.SetData(data_sp); - return bytes_read; - } + } + if (error.Success() || bytes_read > 0) { + data.SetData(data_sp); + return bytes_read; } } break; case eAddressTypeHost: { From lldb-commits at lists.llvm.org Fri May 9 16:49:09 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 16:49:09 -0700 (PDT) Subject: [Lldb-commits] [lldb] fbcde15 - [lldb-dap] Minor visual changes to the modules UI (#139328) Message-ID: <681e9475.050a0220.3201f7.e4ac@mx.google.com> Author: Jonas Devlieghere Date: 2025-05-09T16:49:05-07:00 New Revision: fbcde15978ef060dfc59314d77447b984f863039 URL: https://github.com/llvm/llvm-project/commit/fbcde15978ef060dfc59314d77447b984f863039 DIFF: https://github.com/llvm/llvm-project/commit/fbcde15978ef060dfc59314d77447b984f863039.diff LOG: [lldb-dap] Minor visual changes to the modules UI (#139328) Small assortment of changes to the modules UI after trying it out: - Print the load address as hexadecimal. - Remove spurious space before colon. - Drop "Module" prefix from tooltip title. - Capitalize bold list items. Added: Modified: lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts Removed: ################################################################################ diff --git a/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts b/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts index 5af3d52e9870c..478c162de8878 100644 --- a/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts +++ b/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts @@ -25,23 +25,25 @@ export class ModulesDataProvider } const tooltip = new vscode.MarkdownString(); - tooltip.appendMarkdown(`# Module '${module.name}'\n\n`); - tooltip.appendMarkdown(`- **id** : ${module.id}\n`); + tooltip.appendMarkdown(`# ${module.name}\n\n`); + tooltip.appendMarkdown(`- **ID**: ${module.id}\n`); if (module.addressRange) { - tooltip.appendMarkdown(`- **load address** : ${module.addressRange}\n`); + tooltip.appendMarkdown( + `- **Load address**: 0x${Number(module.addressRange).toString(16)}\n`, + ); } if (module.path) { - tooltip.appendMarkdown(`- **path** : ${module.path}\n`); + tooltip.appendMarkdown(`- **Path**: ${module.path}\n`); } if (module.version) { - tooltip.appendMarkdown(`- **version** : ${module.version}\n`); + tooltip.appendMarkdown(`- **Version**: ${module.version}\n`); } if (module.symbolStatus) { - tooltip.appendMarkdown(`- **symbol status** : ${module.symbolStatus}\n`); + tooltip.appendMarkdown(`- **Symbol status**: ${module.symbolStatus}\n`); } if (module.symbolFilePath) { tooltip.appendMarkdown( - `- **symbol file path** : ${module.symbolFilePath}\n`, + `- **Symbol file path**: ${module.symbolFilePath}\n`, ); } From lldb-commits at lists.llvm.org Fri May 9 16:49:11 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Fri, 09 May 2025 16:49:11 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Minor visual changes to the modules UI (PR #139328) In-Reply-To: Message-ID: <681e9477.170a0220.ce8e8.bb54@mx.google.com> https://github.com/JDevlieghere closed https://github.com/llvm/llvm-project/pull/139328 From lldb-commits at lists.llvm.org Fri May 9 17:05:14 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Fri, 09 May 2025 17:05:14 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][breakpoint] Grey out disabled breakpoints (PR #91404) In-Reply-To: Message-ID: <681e983a.170a0220.311f7d.babf@mx.google.com> https://github.com/JDevlieghere requested changes to this pull request. I like the feautre but I think this PR can be a lot simpler: 1. I don't think you need any of the changes to `Stream` or `SBStream`. You already have to get the setting from the debugger, so just check if it has colors enabled and write the prefix (and fixed suffix) if colors are enabled. 2. I think we had settled on adding a new setting for highlighting "disabled things", with breakpoints being just one example of that. The setting should be generic, something like `disable-ansi-prefix` (*). I'm find with omitting the suffix and always issuing a clear. I can't think of a reason anyone would ever want to change that. (*) I would prefer to call the setting`disable-format`, but then it should go through FormatEntity which is definitely overkill. I think given the existing settings, `disable-ansi-prefix` is the most consistent. https://github.com/llvm/llvm-project/pull/91404 From lldb-commits at lists.llvm.org Fri May 9 17:05:15 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Fri, 09 May 2025 17:05:15 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][breakpoint] Grey out disabled breakpoints (PR #91404) In-Reply-To: Message-ID: <681e983b.170a0220.1fc9ce.93d8@mx.google.com> https://github.com/JDevlieghere edited https://github.com/llvm/llvm-project/pull/91404 From lldb-commits at lists.llvm.org Fri May 9 17:05:15 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Fri, 09 May 2025 17:05:15 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb][breakpoint] Grey out disabled breakpoints (PR #91404) In-Reply-To: Message-ID: <681e983b.170a0220.32f084.88a8@mx.google.com> ================ @@ -47,6 +48,10 @@ class LLDB_API SBStream { void Print(const char *str); + bool HasColor(); + + void FormatAnsiTerminalCodes(llvm::StringRef format); ---------------- JDevlieghere wrote: IIUC we don't need any changes to the SBStream class anymore, right? https://github.com/llvm/llvm-project/pull/91404 From lldb-commits at lists.llvm.org Fri May 9 17:05:43 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Fri, 09 May 2025 17:05:43 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Don't emit a removed module event for unseen modules (PR #139324) In-Reply-To: Message-ID: <681e9857.170a0220.9a29b.be4c@mx.google.com> https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/139324 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Fri May 9 17:58:24 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Fri, 09 May 2025 17:58:24 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Don't emit a removed module event for unseen modules (PR #139324) In-Reply-To: Message-ID: <681ea4b0.630a0220.2e524d.76cb@mx.google.com> https://github.com/ashgti approved this pull request. https://github.com/llvm/llvm-project/pull/139324 From lldb-commits at lists.llvm.org Fri May 9 18:46:29 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Fri, 09 May 2025 18:46:29 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681eaff5.170a0220.12d995.c241@mx.google.com> https://github.com/jasonmolenda updated https://github.com/llvm/llvm-project/pull/138805 >From 4fc9acd03a58a3617b113352c48e5dd2fdb58eda Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Tue, 6 May 2025 22:37:17 -0700 Subject: [PATCH 01/11] [lldb] Provide lr value in faulting frame on arm64 When a frameless function faults or is interrupted asynchronously, the UnwindPlan MAY have no register location rule for the return address register (lr on arm64); the value is simply live in the lr register when it was interrupted, and the frame below this on the stack -- e.g. sigtramp on a Unix system -- has the full register context, including that register. RegisterContextUnwind::SavedLocationForRegister, when asked to find the caller's pc value, will first see if there is a pc register location. If there isn't, on a Return Address Register architecture like arm/mips/riscv, we rewrite the register request from "pc" to "RA register", and search for a location. On frame 0 (the live frame) and an interrupted frame, the UnwindPlan may have no register location rule for the RA Reg, that is valid. A frameless function that never calls another may simply keep the return address in the live register the whole way. Our instruction emulation unwind plans explicitly add a rule (see Pavel's May 2024 change https://github.com/llvm/llvm-project/pull/91321 ), but an UnwindPlan sourced from debug_frame may not. I've got a case where this exactly happens - clang debug_frame for arm64 where there is no register location for the lr in a frameless function. There is a fault in the middle of this frameless function and we only get the lr value from the fault handler below this frame if lr has a register location of `IsSame`, in line with Pavel's 2024 change. Similar to how we see a request of the RA Reg from frame 0 after failing to find an unwind location for the pc register, the same style of special casing is needed when this is a function that was interrupted. Without this change, we can find the pc of the frame that was executing when it was interrupted, but we need $lr to find its caller, and we don't descend down to the trap handler to get that value, truncating the stack. rdar://145614545 --- lldb/source/Target/RegisterContextUnwind.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 3ed49e12476dd..23a86bee2518b 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -1377,6 +1377,7 @@ RegisterContextUnwind::SavedLocationForRegister( } } + // Check if the active_row has a register location listed. if (regnum.IsValid() && active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind), unwindplan_regloc)) { @@ -1390,11 +1391,10 @@ RegisterContextUnwind::SavedLocationForRegister( // This is frame 0 and we're retrieving the PC and it's saved in a Return // Address register and it hasn't been saved anywhere yet -- that is, // it's still live in the actual register. Handle this specially. - if (!have_unwindplan_regloc && return_address_reg.IsValid() && - IsFrameZero()) { - if (return_address_reg.GetAsKind(eRegisterKindLLDB) != - LLDB_INVALID_REGNUM) { + return_address_reg.GetAsKind(eRegisterKindLLDB) != + LLDB_INVALID_REGNUM) { + if (IsFrameZero()) { lldb_private::UnwindLLDB::ConcreteRegisterLocation new_regloc; new_regloc.type = UnwindLLDB::ConcreteRegisterLocation:: eRegisterInLiveRegisterContext; @@ -1408,6 +1408,17 @@ RegisterContextUnwind::SavedLocationForRegister( return_address_reg.GetAsKind(eRegisterKindLLDB), return_address_reg.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } else if (BehavesLikeZerothFrame()) { + // This function was interrupted asynchronously -- it faulted, + // an async interrupt, a timer fired, a debugger expression etc. + // The caller's pc is in the Return Address register, but the + // UnwindPlan for this function may have no location rule for + // the RA reg. + // This means that the caller's return address is in the RA reg + // when the function was interrupted--descend down one stack frame + // to retrieve it from the trap handler's saved context. + unwindplan_regloc.SetSame(); + have_unwindplan_regloc = true; } } >From b10162deb49ecddca6439665c2b8ea1995fdd81f Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:24:17 -0700 Subject: [PATCH 02/11] Add the sources for an API test case to be written --- .../interrupt-and-trap-funcs.s | 92 +++++++++++++++++++ .../macosx/unwind-frameless-faulted/main.c | 6 ++ 2 files changed, 98 insertions(+) create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/main.c diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s new file mode 100644 index 0000000000000..23eb35c2b31ca --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -0,0 +1,92 @@ +#define DW_CFA_register 0x9 +#define ehframe_x22 22 +#define ehframe_x23 23 +#define ehframe_pc 32 + + .section __TEXT,__text,regular,pure_instructions + +//-------------------------------------- +// to_be_interrupted() a frameless function that does a non-ABI +// function call ("is interrupted/traps" simulated) to trap(). +// Before it branches to trap(), it puts its return address in +// x23. trap() knows to branch back to $x23 when it has finished. +//-------------------------------------- + .globl _to_be_interrupted + .p2align 2 +_to_be_interrupted: + .cfi_startproc + + // This is a garbage entry to ensure that eh_frame is emitted, + // it isn't used for anything. If there's no eh_frame, lldb + // can do an assembly emulation scan and add a rule for $lr + // which won't expose the issue at hand. + .cfi_escape DW_CFA_register, ehframe_x22, ehframe_x23 + mov x24, x0 + add x24, x24, #1 + + adrp x23, L_.return at PAGE ; put return address in x4 + add x23, x23, L_.return at PAGEOFF + + b _trap ; branch to trap handler, fake async interrupt + +L_.return: + mov x0, x24 + ret + .cfi_endproc + + + +//-------------------------------------- +// trap() trap handler function, sets up stack frame +// with special unwind rule for the pc value of the +// "interrupted" stack frame (it's in x23), then calls +// break_to_debugger(). +//-------------------------------------- + .globl _trap + .p2align 2 +_trap: + .cfi_startproc + .cfi_signal_frame + + // The pc value when we were interrupted is in x23 + .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 + + // standard prologue save of fp & lr so we can call puts() + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 + .cfi_def_cfa w29, 16 + .cfi_offset w30, -8 + .cfi_offset w29, -16 + + bl _break_to_debugger + + ldp x29, x30, [sp, #16] + add sp, sp, #32 + + // jump back to $x23 to resume execution of to_be_interrupted + br x23 + .cfi_endproc + +//-------------------------------------- +// break_to_debugger() executes a BRK instruction +//-------------------------------------- + .globl _break_to_debugger + .p2align 2 +_break_to_debugger: + .cfi_startproc + + // standard prologue save of fp & lr so we can call puts() + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 + .cfi_def_cfa w29, 16 + .cfi_offset w30, -8 + .cfi_offset w29, -16 + + brk #0xf000 ;; __builtin_debugtrap aarch64 instruction + + ldp x29, x30, [sp, #16] + add sp, sp, #32 + ret + .cfi_endproc diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c b/lldb/test/API/macosx/unwind-frameless-faulted/main.c new file mode 100644 index 0000000000000..e121809f25478 --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/main.c @@ -0,0 +1,6 @@ +int to_be_interrupted(int); +int main() { + int c = 10; + c = to_be_interrupted(c); + return c; +} >From 508b9cb404072263a6ed52a75b3b7aad5d949510 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:24:58 -0700 Subject: [PATCH 03/11] Add a log msg when a frame is marked as a trap handler via the UnwindPlan. And use the trap handler flag for zeroth frame so we can properly unwind a frameless function that was interrupted while in the trap handler function. --- lldb/source/Target/RegisterContextUnwind.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 51c0f56e9bc1e..cf4b96c6eda9f 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -248,6 +248,7 @@ void RegisterContextUnwind::InitializeZerothFrame() { active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); row_register_kind = m_full_unwind_plan_sp->GetRegisterKind(); + PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp); if (active_row && log) { StreamString active_row_strm; active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, @@ -1933,6 +1934,7 @@ void RegisterContextUnwind::PropagateTrapHandlerFlagFromUnwindPlan( } m_frame_type = eTrapHandlerFrame; + UnwindLogMsg("This frame is marked as a trap handler via its UnwindPlan"); if (m_current_offset_backed_up_one != m_current_offset) { // We backed up the pc by 1 to compute the symbol context, but >From d8843fe86c0eaaa1483bed0317f832b7630d8548 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:28:40 -0700 Subject: [PATCH 04/11] Update assembly a bit. --- .../interrupt-and-trap-funcs.s | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 23eb35c2b31ca..708ad9ed56a1c 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -51,7 +51,8 @@ _trap: // The pc value when we were interrupted is in x23 .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 - // standard prologue save of fp & lr so we can call puts() + // standard prologue save of fp & lr so we can call + // break_to_debugger() sub sp, sp, #32 stp x29, x30, [sp, #16] add x29, sp, #16 @@ -76,17 +77,7 @@ _trap: _break_to_debugger: .cfi_startproc - // standard prologue save of fp & lr so we can call puts() - sub sp, sp, #32 - stp x29, x30, [sp, #16] - add x29, sp, #16 - .cfi_def_cfa w29, 16 - .cfi_offset w30, -8 - .cfi_offset w29, -16 - brk #0xf000 ;; __builtin_debugtrap aarch64 instruction - ldp x29, x30, [sp, #16] - add sp, sp, #32 ret .cfi_endproc >From 1d90aa48c92d9f8ef43c889223c0804ac1491d53 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 18:18:14 -0700 Subject: [PATCH 05/11] Add unwind rules for x0 (volatile) and x20 (non-voltile) to trap() and break_to_debugger() for fun, and add unwind instructions for the epilogue of trap(). When stopped in `break_to_debugger`, ``` * frame #0: 0x00000001000003e8 a.out`break_to_debugger + 4 frame #1: 0x00000001000003d8 a.out`trap + 16 frame #2: 0x00000001000003c0 a.out`to_be_interrupted + 20 frame #3: 0x0000000100000398 a.out`main + 32 ``` Normally we can't provide a volatile register (e.g. x0) up the stack. And we can provide a non-volatile register (e.g. x20) up the stack. I added an IsSame rule for trap() and break_to_debugger() for x0, so it CAN be passed up the stack. And I added an Undefined rule for x20 to trap() so it CAN'T be provided up the stack. If a user selects `to_be_interrupted` and does `register read`, they'll get x0 and they won't get x20. From `main`, they will not get x0 or x20 (x0 because `to_be_interrupted` doesn't have an IsSame rule). --- .../interrupt-and-trap-funcs.s | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 708ad9ed56a1c..b50c4734f18f3 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -1,4 +1,6 @@ #define DW_CFA_register 0x9 +#define ehframe_x0 0 +#define ehframe_x20 20 #define ehframe_x22 22 #define ehframe_x23 23 #define ehframe_pc 32 @@ -7,9 +9,9 @@ //-------------------------------------- // to_be_interrupted() a frameless function that does a non-ABI -// function call ("is interrupted/traps" simulated) to trap(). -// Before it branches to trap(), it puts its return address in -// x23. trap() knows to branch back to $x23 when it has finished. +// function call to trap(), simulating an async signal/interrup/exception/fault. +// Before it branches to trap(), put the return address in x23. +// trap() knows to branch back to $x23 when it has finished. //-------------------------------------- .globl _to_be_interrupted .p2align 2 @@ -51,6 +53,15 @@ _trap: // The pc value when we were interrupted is in x23 .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 + // For fun, mark x0 as unmodified so the caller can + // retrieve the value if it wants. + .cfi_same_value ehframe_x0 + + // Mark x20 as undefined. This is a callee-preserved + // (non-volatile) register by the SysV AArch64 ABI, but + // it's be fun to see lldb not passing a value past this. + .cfi_undefined ehframe_x20 + // standard prologue save of fp & lr so we can call // break_to_debugger() sub sp, sp, #32 @@ -63,7 +74,11 @@ _trap: bl _break_to_debugger ldp x29, x30, [sp, #16] + .cfi_same_value x29 + .cfi_same_value x30 + .cfi_def_cfa sp, 32 add sp, sp, #32 + .cfi_same_value sp // jump back to $x23 to resume execution of to_be_interrupted br x23 @@ -77,6 +92,10 @@ _trap: _break_to_debugger: .cfi_startproc + // For fun, mark x0 as unmodified so the caller can + // retrieve the value if it wants. + .cfi_same_value ehframe_x0 + brk #0xf000 ;; __builtin_debugtrap aarch64 instruction ret >From 303edd47f7bcfb6af0a9947ca7b0b9a0c5bec589 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 18:46:14 -0700 Subject: [PATCH 06/11] small comment improvement --- .../unwind-frameless-faulted/interrupt-and-trap-funcs.s | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index b50c4734f18f3..38be06a681bf2 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -18,10 +18,10 @@ _to_be_interrupted: .cfi_startproc - // This is a garbage entry to ensure that eh_frame is emitted, - // it isn't used for anything. If there's no eh_frame, lldb - // can do an assembly emulation scan and add a rule for $lr - // which won't expose the issue at hand. + // This is a garbage entry to ensure that eh_frame is emitted. + // If there's no eh_frame, lldb can use the assembly emulation scan, + // which always includes a rule for $lr, and we won't replicate the + // bug we're testing for. .cfi_escape DW_CFA_register, ehframe_x22, ehframe_x23 mov x24, x0 add x24, x24, #1 >From 620eace34ae149b8c513767935118b6e870646fc Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 18:47:54 -0700 Subject: [PATCH 07/11] more comment fix --- .../macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 38be06a681bf2..b5c5cb79656c3 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -59,7 +59,8 @@ _trap: // Mark x20 as undefined. This is a callee-preserved // (non-volatile) register by the SysV AArch64 ABI, but - // it's be fun to see lldb not passing a value past this. + // it'll be fun to see lldb not passing a value past this + // point on the stack. .cfi_undefined ehframe_x20 // standard prologue save of fp & lr so we can call >From 6185c513beaa2f63dc04b54f723b209de8af2286 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 22:36:21 -0700 Subject: [PATCH 08/11] Add test case Makefile and API test python code. Also fix one bug in the by-hand unwind state in the assembly file. --- .../macosx/unwind-frameless-faulted/Makefile | 12 ++++ .../TestUnwindFramelessFaulted.py | 59 +++++++++++++++++++ .../interrupt-and-trap-funcs.s | 1 + 3 files changed, 72 insertions(+) create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/Makefile create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile new file mode 100644 index 0000000000000..76548928a3622 --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile @@ -0,0 +1,12 @@ +C_SOURCES := main.c + +interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.s + $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o $(SRCDIR)/interrupt-and-trap-funcs.s + +include Makefile.rules + +a.out: interrupt-and-trap-funcs.o + +# Needs to come after include +OBJECTS += interrupt-and-trap-funcs.o + diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py new file mode 100644 index 0000000000000..34e7fbfffacde --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py @@ -0,0 +1,59 @@ +"""Test that lldb backtraces a frameless function that faults correctly.""" + +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +import shutil +import os + + +class TestUnwindFramelessFaulted(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + @skipUnlessDarwin + def test_frameless_faulted_unwind(self): + self.build() + + (target, process, thread, bp) = lldbutil.run_to_name_breakpoint( + self, "main", only_one_thread=False + ) + + # The test program will have a backtrace like this at its deepest: + # + # * frame #0: 0x0000000102adc468 a.out`break_to_debugger + 4 + # frame #1: 0x0000000102adc458 a.out`trap + 16 + # frame #2: 0x0000000102adc440 a.out`to_be_interrupted + 20 + # frame #3: 0x0000000102adc418 a.out`main at main.c:4:7 + # frame #4: 0x0000000193b7eb4c dyld`start + 6000 + + correct_frames = ["break_to_debugger", "trap", "to_be_interrupted", "main"] + + # Keep track of when main has branch & linked, instruction step until we're + # back in main() + main_has_bl_ed = False + + # Instruction step through the binary until we are in a function not + # listed in correct_frames. + while ( + process.GetState() == lldb.eStateStopped + and thread.GetFrameAtIndex(0).name in correct_frames + ): + starting_index = 0 + if self.TraceOn(): + self.runCmd("bt") + + # Find which index into correct_frames the current stack frame is + for idx, name in enumerate(correct_frames): + if thread.GetFrameAtIndex(0).name == name: + starting_index = idx + + # Test that all frames after the current frame listed in + # correct_frames appears in the backtrace. + frame_idx = 0 + for expected_frame in correct_frames[starting_index:]: + self.assertEqual(thread.GetFrameAtIndex(frame_idx).name, expected_frame) + frame_idx = frame_idx + 1 + + if self.TraceOn(): + print("StepInstruction") + thread.StepInstruction(False) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index b5c5cb79656c3..5bc991fc0f67d 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -80,6 +80,7 @@ _trap: .cfi_def_cfa sp, 32 add sp, sp, #32 .cfi_same_value sp + .cfi_def_cfa sp, 0 // jump back to $x23 to resume execution of to_be_interrupted br x23 >From d239b7a84c1e489b2630aa63358061435a749ce7 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 22:49:20 -0700 Subject: [PATCH 09/11] Add tests for x0 and x20, which I treat in normally ABI-wrong ways in these hand-written UnwindPlans. --- .../TestUnwindFramelessFaulted.py | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py index 34e7fbfffacde..d899b7a2f7369 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py +++ b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py @@ -34,17 +34,15 @@ def test_frameless_faulted_unwind(self): # Instruction step through the binary until we are in a function not # listed in correct_frames. - while ( - process.GetState() == lldb.eStateStopped - and thread.GetFrameAtIndex(0).name in correct_frames - ): + frame = thread.GetFrameAtIndex(0) + while process.GetState() == lldb.eStateStopped and frame.name in correct_frames: starting_index = 0 if self.TraceOn(): self.runCmd("bt") # Find which index into correct_frames the current stack frame is for idx, name in enumerate(correct_frames): - if thread.GetFrameAtIndex(0).name == name: + if frame.name == name: starting_index = idx # Test that all frames after the current frame listed in @@ -54,6 +52,32 @@ def test_frameless_faulted_unwind(self): self.assertEqual(thread.GetFrameAtIndex(frame_idx).name, expected_frame) frame_idx = frame_idx + 1 + # When we're at our deepest level, test that register passing of x0 and x20 + # follow the by-hand UnwindPlan rules. In this test program, we can get x0 + # in the middle of the stack and we CAN'T get x20. The opposites of the normal + # AArch64 SysV ABI. + if frame.name == "break_to_debugger": + tbi_frame = thread.GetFrameAtIndex(2) + self.assertEqual(tbi_frame.name, "to_be_interrupted") + + # The original argument to to_be_interrupted(), 10 + # Normally can't get x0 mid-stack, but UnwindPlans have special rules to + # make this possible. + x0_reg = tbi_frame.register["x0"] + self.assertTrue(x0_reg.IsValid()) + self.assertEqual(x0_reg.GetValueAsUnsigned(), 10) + + # The incremented return value from to_be_interrupted(), 11 + x24_reg = tbi_frame.register["x24"] + self.assertTrue(x24_reg.IsValid()) + self.assertEqual(x24_reg.GetValueAsUnsigned(), 11) + + # x20 can normally be fetched mid-stack, but the UnwindPlan + # has a rule saying it can't be fetched. + x20_reg = tbi_frame.register["x20"] + self.assertTrue(x20_reg.error.fail) + if self.TraceOn(): print("StepInstruction") thread.StepInstruction(False) + frame = thread.GetFrameAtIndex(0) >From 33563049b37479db9ebebdad0a1c982deb4c6f14 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 22:52:04 -0700 Subject: [PATCH 10/11] Remove some Mach-O/Darwinisms, get closer to this compiling on linux? I think maybe the only difference is that the symbol names are prefixed with _ on darwin? --- .../unwind-frameless-faulted/interrupt-and-trap-funcs.s | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 5bc991fc0f67d..2ba6a711767b3 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -5,7 +5,7 @@ #define ehframe_x23 23 #define ehframe_pc 32 - .section __TEXT,__text,regular,pure_instructions + .text //-------------------------------------- // to_be_interrupted() a frameless function that does a non-ABI @@ -14,7 +14,6 @@ // trap() knows to branch back to $x23 when it has finished. //-------------------------------------- .globl _to_be_interrupted - .p2align 2 _to_be_interrupted: .cfi_startproc @@ -45,7 +44,6 @@ L_.return: // break_to_debugger(). //-------------------------------------- .globl _trap - .p2align 2 _trap: .cfi_startproc .cfi_signal_frame @@ -90,7 +88,6 @@ _trap: // break_to_debugger() executes a BRK instruction //-------------------------------------- .globl _break_to_debugger - .p2align 2 _break_to_debugger: .cfi_startproc >From 80248f5a5841645a1da0026799c040d4db7ab6f3 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 18:44:27 -0700 Subject: [PATCH 11/11] Adapt test case to build & run on linux or Darwin. There's a slight syntax diff for the ADRP + ADD pair and the label name. And the function names in Darwin are prepended with an _ and not on linux. clang on darwin runs the .s file through the preprocessor, but it doesn't seem to on linux. I renamed interrupt-and-trap-funcs.s to interrupt-and-trap-funcs.c and run it through the preprocessor then assemble it in Makefile now. The linux version would probably run on other AArch64 systems like FreeBSD but I haven't checked those. --- .../API/macosx/unwind-frameless-faulted/Makefile | 5 +++-- .../TestUnwindFramelessFaulted.py | 5 ++++- ...d-trap-funcs.s => interrupt-and-trap-funcs.c} | 16 ++++++++++++---- .../API/macosx/unwind-frameless-faulted/main.c | 10 ++++++++++ 4 files changed, 29 insertions(+), 7 deletions(-) rename lldb/test/API/macosx/unwind-frameless-faulted/{interrupt-and-trap-funcs.s => interrupt-and-trap-funcs.c} (88%) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile index 76548928a3622..fca47ae47491c 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile +++ b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile @@ -1,7 +1,8 @@ C_SOURCES := main.c -interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.s - $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o $(SRCDIR)/interrupt-and-trap-funcs.s +interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.c + $(CC) $(CFLAGS) -E -o interrupt-and-trap-funcs.s $(SRCDIR)/interrupt-and-trap-funcs.c + $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o interrupt-and-trap-funcs.s include Makefile.rules diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py index d899b7a2f7369..fe4b9b52abebb 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py +++ b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py @@ -10,7 +10,10 @@ class TestUnwindFramelessFaulted(TestBase): NO_DEBUG_INFO_TESTCASE = True - @skipUnlessDarwin + @skipIf( + oslist=no_match([lldbplatformutil.getDarwinOSTriples(), "linux"]), + archs=no_match(["aarch64", "arm64", "arm64e"]), + ) def test_frameless_faulted_unwind(self): self.build() diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c similarity index 88% rename from lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s rename to lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c index 2ba6a711767b3..23c6cfd688969 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c @@ -6,7 +6,6 @@ #define ehframe_pc 32 .text - //-------------------------------------- // to_be_interrupted() a frameless function that does a non-ABI // function call to trap(), simulating an async signal/interrup/exception/fault. @@ -25,12 +24,21 @@ mov x24, x0 add x24, x24, #1 - adrp x23, L_.return at PAGE ; put return address in x4 +#if defined(__APPLE__) + adrp x23, L_.return at PAGE // put return address in x4 add x23, x23, L_.return at PAGEOFF +#else + adrp x23, .L.return + add x23, x23, :lo12:.L.return +#endif - b _trap ; branch to trap handler, fake async interrupt + b _trap // branch to trap handler, fake async interrupt +#if defined(__APPLE__) L_.return: +#else +.L.return: +#endif mov x0, x24 ret .cfi_endproc @@ -95,7 +103,7 @@ L_.return: // retrieve the value if it wants. .cfi_same_value ehframe_x0 - brk #0xf000 ;; __builtin_debugtrap aarch64 instruction + brk #0xf000 // __builtin_debugtrap aarch64 instruction ret .cfi_endproc diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c b/lldb/test/API/macosx/unwind-frameless-faulted/main.c index e121809f25478..27a2f228b5feb 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/main.c +++ b/lldb/test/API/macosx/unwind-frameless-faulted/main.c @@ -1,6 +1,16 @@ +#if defined(__APPLE__) int to_be_interrupted(int); +#else +int _to_be_interrupted(int); +#endif + int main() { int c = 10; +#if defined(__APPLE__) c = to_be_interrupted(c); +#else + c = _to_be_interrupted(c); + #endif + return c; } From lldb-commits at lists.llvm.org Fri May 9 18:48:34 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Fri, 09 May 2025 18:48:34 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681eb072.170a0220.43e58.b1d6@mx.google.com> https://github.com/jasonmolenda updated https://github.com/llvm/llvm-project/pull/138805 >From 4fc9acd03a58a3617b113352c48e5dd2fdb58eda Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Tue, 6 May 2025 22:37:17 -0700 Subject: [PATCH 01/12] [lldb] Provide lr value in faulting frame on arm64 When a frameless function faults or is interrupted asynchronously, the UnwindPlan MAY have no register location rule for the return address register (lr on arm64); the value is simply live in the lr register when it was interrupted, and the frame below this on the stack -- e.g. sigtramp on a Unix system -- has the full register context, including that register. RegisterContextUnwind::SavedLocationForRegister, when asked to find the caller's pc value, will first see if there is a pc register location. If there isn't, on a Return Address Register architecture like arm/mips/riscv, we rewrite the register request from "pc" to "RA register", and search for a location. On frame 0 (the live frame) and an interrupted frame, the UnwindPlan may have no register location rule for the RA Reg, that is valid. A frameless function that never calls another may simply keep the return address in the live register the whole way. Our instruction emulation unwind plans explicitly add a rule (see Pavel's May 2024 change https://github.com/llvm/llvm-project/pull/91321 ), but an UnwindPlan sourced from debug_frame may not. I've got a case where this exactly happens - clang debug_frame for arm64 where there is no register location for the lr in a frameless function. There is a fault in the middle of this frameless function and we only get the lr value from the fault handler below this frame if lr has a register location of `IsSame`, in line with Pavel's 2024 change. Similar to how we see a request of the RA Reg from frame 0 after failing to find an unwind location for the pc register, the same style of special casing is needed when this is a function that was interrupted. Without this change, we can find the pc of the frame that was executing when it was interrupted, but we need $lr to find its caller, and we don't descend down to the trap handler to get that value, truncating the stack. rdar://145614545 --- lldb/source/Target/RegisterContextUnwind.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 3ed49e12476dd..23a86bee2518b 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -1377,6 +1377,7 @@ RegisterContextUnwind::SavedLocationForRegister( } } + // Check if the active_row has a register location listed. if (regnum.IsValid() && active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind), unwindplan_regloc)) { @@ -1390,11 +1391,10 @@ RegisterContextUnwind::SavedLocationForRegister( // This is frame 0 and we're retrieving the PC and it's saved in a Return // Address register and it hasn't been saved anywhere yet -- that is, // it's still live in the actual register. Handle this specially. - if (!have_unwindplan_regloc && return_address_reg.IsValid() && - IsFrameZero()) { - if (return_address_reg.GetAsKind(eRegisterKindLLDB) != - LLDB_INVALID_REGNUM) { + return_address_reg.GetAsKind(eRegisterKindLLDB) != + LLDB_INVALID_REGNUM) { + if (IsFrameZero()) { lldb_private::UnwindLLDB::ConcreteRegisterLocation new_regloc; new_regloc.type = UnwindLLDB::ConcreteRegisterLocation:: eRegisterInLiveRegisterContext; @@ -1408,6 +1408,17 @@ RegisterContextUnwind::SavedLocationForRegister( return_address_reg.GetAsKind(eRegisterKindLLDB), return_address_reg.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } else if (BehavesLikeZerothFrame()) { + // This function was interrupted asynchronously -- it faulted, + // an async interrupt, a timer fired, a debugger expression etc. + // The caller's pc is in the Return Address register, but the + // UnwindPlan for this function may have no location rule for + // the RA reg. + // This means that the caller's return address is in the RA reg + // when the function was interrupted--descend down one stack frame + // to retrieve it from the trap handler's saved context. + unwindplan_regloc.SetSame(); + have_unwindplan_regloc = true; } } >From b10162deb49ecddca6439665c2b8ea1995fdd81f Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:24:17 -0700 Subject: [PATCH 02/12] Add the sources for an API test case to be written --- .../interrupt-and-trap-funcs.s | 92 +++++++++++++++++++ .../macosx/unwind-frameless-faulted/main.c | 6 ++ 2 files changed, 98 insertions(+) create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/main.c diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s new file mode 100644 index 0000000000000..23eb35c2b31ca --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -0,0 +1,92 @@ +#define DW_CFA_register 0x9 +#define ehframe_x22 22 +#define ehframe_x23 23 +#define ehframe_pc 32 + + .section __TEXT,__text,regular,pure_instructions + +//-------------------------------------- +// to_be_interrupted() a frameless function that does a non-ABI +// function call ("is interrupted/traps" simulated) to trap(). +// Before it branches to trap(), it puts its return address in +// x23. trap() knows to branch back to $x23 when it has finished. +//-------------------------------------- + .globl _to_be_interrupted + .p2align 2 +_to_be_interrupted: + .cfi_startproc + + // This is a garbage entry to ensure that eh_frame is emitted, + // it isn't used for anything. If there's no eh_frame, lldb + // can do an assembly emulation scan and add a rule for $lr + // which won't expose the issue at hand. + .cfi_escape DW_CFA_register, ehframe_x22, ehframe_x23 + mov x24, x0 + add x24, x24, #1 + + adrp x23, L_.return at PAGE ; put return address in x4 + add x23, x23, L_.return at PAGEOFF + + b _trap ; branch to trap handler, fake async interrupt + +L_.return: + mov x0, x24 + ret + .cfi_endproc + + + +//-------------------------------------- +// trap() trap handler function, sets up stack frame +// with special unwind rule for the pc value of the +// "interrupted" stack frame (it's in x23), then calls +// break_to_debugger(). +//-------------------------------------- + .globl _trap + .p2align 2 +_trap: + .cfi_startproc + .cfi_signal_frame + + // The pc value when we were interrupted is in x23 + .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 + + // standard prologue save of fp & lr so we can call puts() + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 + .cfi_def_cfa w29, 16 + .cfi_offset w30, -8 + .cfi_offset w29, -16 + + bl _break_to_debugger + + ldp x29, x30, [sp, #16] + add sp, sp, #32 + + // jump back to $x23 to resume execution of to_be_interrupted + br x23 + .cfi_endproc + +//-------------------------------------- +// break_to_debugger() executes a BRK instruction +//-------------------------------------- + .globl _break_to_debugger + .p2align 2 +_break_to_debugger: + .cfi_startproc + + // standard prologue save of fp & lr so we can call puts() + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 + .cfi_def_cfa w29, 16 + .cfi_offset w30, -8 + .cfi_offset w29, -16 + + brk #0xf000 ;; __builtin_debugtrap aarch64 instruction + + ldp x29, x30, [sp, #16] + add sp, sp, #32 + ret + .cfi_endproc diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c b/lldb/test/API/macosx/unwind-frameless-faulted/main.c new file mode 100644 index 0000000000000..e121809f25478 --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/main.c @@ -0,0 +1,6 @@ +int to_be_interrupted(int); +int main() { + int c = 10; + c = to_be_interrupted(c); + return c; +} >From 508b9cb404072263a6ed52a75b3b7aad5d949510 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:24:58 -0700 Subject: [PATCH 03/12] Add a log msg when a frame is marked as a trap handler via the UnwindPlan. And use the trap handler flag for zeroth frame so we can properly unwind a frameless function that was interrupted while in the trap handler function. --- lldb/source/Target/RegisterContextUnwind.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 51c0f56e9bc1e..cf4b96c6eda9f 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -248,6 +248,7 @@ void RegisterContextUnwind::InitializeZerothFrame() { active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); row_register_kind = m_full_unwind_plan_sp->GetRegisterKind(); + PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp); if (active_row && log) { StreamString active_row_strm; active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, @@ -1933,6 +1934,7 @@ void RegisterContextUnwind::PropagateTrapHandlerFlagFromUnwindPlan( } m_frame_type = eTrapHandlerFrame; + UnwindLogMsg("This frame is marked as a trap handler via its UnwindPlan"); if (m_current_offset_backed_up_one != m_current_offset) { // We backed up the pc by 1 to compute the symbol context, but >From d8843fe86c0eaaa1483bed0317f832b7630d8548 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:28:40 -0700 Subject: [PATCH 04/12] Update assembly a bit. --- .../interrupt-and-trap-funcs.s | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 23eb35c2b31ca..708ad9ed56a1c 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -51,7 +51,8 @@ _trap: // The pc value when we were interrupted is in x23 .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 - // standard prologue save of fp & lr so we can call puts() + // standard prologue save of fp & lr so we can call + // break_to_debugger() sub sp, sp, #32 stp x29, x30, [sp, #16] add x29, sp, #16 @@ -76,17 +77,7 @@ _trap: _break_to_debugger: .cfi_startproc - // standard prologue save of fp & lr so we can call puts() - sub sp, sp, #32 - stp x29, x30, [sp, #16] - add x29, sp, #16 - .cfi_def_cfa w29, 16 - .cfi_offset w30, -8 - .cfi_offset w29, -16 - brk #0xf000 ;; __builtin_debugtrap aarch64 instruction - ldp x29, x30, [sp, #16] - add sp, sp, #32 ret .cfi_endproc >From 1d90aa48c92d9f8ef43c889223c0804ac1491d53 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 18:18:14 -0700 Subject: [PATCH 05/12] Add unwind rules for x0 (volatile) and x20 (non-voltile) to trap() and break_to_debugger() for fun, and add unwind instructions for the epilogue of trap(). When stopped in `break_to_debugger`, ``` * frame #0: 0x00000001000003e8 a.out`break_to_debugger + 4 frame #1: 0x00000001000003d8 a.out`trap + 16 frame #2: 0x00000001000003c0 a.out`to_be_interrupted + 20 frame #3: 0x0000000100000398 a.out`main + 32 ``` Normally we can't provide a volatile register (e.g. x0) up the stack. And we can provide a non-volatile register (e.g. x20) up the stack. I added an IsSame rule for trap() and break_to_debugger() for x0, so it CAN be passed up the stack. And I added an Undefined rule for x20 to trap() so it CAN'T be provided up the stack. If a user selects `to_be_interrupted` and does `register read`, they'll get x0 and they won't get x20. From `main`, they will not get x0 or x20 (x0 because `to_be_interrupted` doesn't have an IsSame rule). --- .../interrupt-and-trap-funcs.s | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 708ad9ed56a1c..b50c4734f18f3 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -1,4 +1,6 @@ #define DW_CFA_register 0x9 +#define ehframe_x0 0 +#define ehframe_x20 20 #define ehframe_x22 22 #define ehframe_x23 23 #define ehframe_pc 32 @@ -7,9 +9,9 @@ //-------------------------------------- // to_be_interrupted() a frameless function that does a non-ABI -// function call ("is interrupted/traps" simulated) to trap(). -// Before it branches to trap(), it puts its return address in -// x23. trap() knows to branch back to $x23 when it has finished. +// function call to trap(), simulating an async signal/interrup/exception/fault. +// Before it branches to trap(), put the return address in x23. +// trap() knows to branch back to $x23 when it has finished. //-------------------------------------- .globl _to_be_interrupted .p2align 2 @@ -51,6 +53,15 @@ _trap: // The pc value when we were interrupted is in x23 .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 + // For fun, mark x0 as unmodified so the caller can + // retrieve the value if it wants. + .cfi_same_value ehframe_x0 + + // Mark x20 as undefined. This is a callee-preserved + // (non-volatile) register by the SysV AArch64 ABI, but + // it's be fun to see lldb not passing a value past this. + .cfi_undefined ehframe_x20 + // standard prologue save of fp & lr so we can call // break_to_debugger() sub sp, sp, #32 @@ -63,7 +74,11 @@ _trap: bl _break_to_debugger ldp x29, x30, [sp, #16] + .cfi_same_value x29 + .cfi_same_value x30 + .cfi_def_cfa sp, 32 add sp, sp, #32 + .cfi_same_value sp // jump back to $x23 to resume execution of to_be_interrupted br x23 @@ -77,6 +92,10 @@ _trap: _break_to_debugger: .cfi_startproc + // For fun, mark x0 as unmodified so the caller can + // retrieve the value if it wants. + .cfi_same_value ehframe_x0 + brk #0xf000 ;; __builtin_debugtrap aarch64 instruction ret >From 303edd47f7bcfb6af0a9947ca7b0b9a0c5bec589 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 18:46:14 -0700 Subject: [PATCH 06/12] small comment improvement --- .../unwind-frameless-faulted/interrupt-and-trap-funcs.s | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index b50c4734f18f3..38be06a681bf2 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -18,10 +18,10 @@ _to_be_interrupted: .cfi_startproc - // This is a garbage entry to ensure that eh_frame is emitted, - // it isn't used for anything. If there's no eh_frame, lldb - // can do an assembly emulation scan and add a rule for $lr - // which won't expose the issue at hand. + // This is a garbage entry to ensure that eh_frame is emitted. + // If there's no eh_frame, lldb can use the assembly emulation scan, + // which always includes a rule for $lr, and we won't replicate the + // bug we're testing for. .cfi_escape DW_CFA_register, ehframe_x22, ehframe_x23 mov x24, x0 add x24, x24, #1 >From 620eace34ae149b8c513767935118b6e870646fc Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 18:47:54 -0700 Subject: [PATCH 07/12] more comment fix --- .../macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 38be06a681bf2..b5c5cb79656c3 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -59,7 +59,8 @@ _trap: // Mark x20 as undefined. This is a callee-preserved // (non-volatile) register by the SysV AArch64 ABI, but - // it's be fun to see lldb not passing a value past this. + // it'll be fun to see lldb not passing a value past this + // point on the stack. .cfi_undefined ehframe_x20 // standard prologue save of fp & lr so we can call >From 6185c513beaa2f63dc04b54f723b209de8af2286 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 22:36:21 -0700 Subject: [PATCH 08/12] Add test case Makefile and API test python code. Also fix one bug in the by-hand unwind state in the assembly file. --- .../macosx/unwind-frameless-faulted/Makefile | 12 ++++ .../TestUnwindFramelessFaulted.py | 59 +++++++++++++++++++ .../interrupt-and-trap-funcs.s | 1 + 3 files changed, 72 insertions(+) create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/Makefile create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile new file mode 100644 index 0000000000000..76548928a3622 --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile @@ -0,0 +1,12 @@ +C_SOURCES := main.c + +interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.s + $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o $(SRCDIR)/interrupt-and-trap-funcs.s + +include Makefile.rules + +a.out: interrupt-and-trap-funcs.o + +# Needs to come after include +OBJECTS += interrupt-and-trap-funcs.o + diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py new file mode 100644 index 0000000000000..34e7fbfffacde --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py @@ -0,0 +1,59 @@ +"""Test that lldb backtraces a frameless function that faults correctly.""" + +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +import shutil +import os + + +class TestUnwindFramelessFaulted(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + @skipUnlessDarwin + def test_frameless_faulted_unwind(self): + self.build() + + (target, process, thread, bp) = lldbutil.run_to_name_breakpoint( + self, "main", only_one_thread=False + ) + + # The test program will have a backtrace like this at its deepest: + # + # * frame #0: 0x0000000102adc468 a.out`break_to_debugger + 4 + # frame #1: 0x0000000102adc458 a.out`trap + 16 + # frame #2: 0x0000000102adc440 a.out`to_be_interrupted + 20 + # frame #3: 0x0000000102adc418 a.out`main at main.c:4:7 + # frame #4: 0x0000000193b7eb4c dyld`start + 6000 + + correct_frames = ["break_to_debugger", "trap", "to_be_interrupted", "main"] + + # Keep track of when main has branch & linked, instruction step until we're + # back in main() + main_has_bl_ed = False + + # Instruction step through the binary until we are in a function not + # listed in correct_frames. + while ( + process.GetState() == lldb.eStateStopped + and thread.GetFrameAtIndex(0).name in correct_frames + ): + starting_index = 0 + if self.TraceOn(): + self.runCmd("bt") + + # Find which index into correct_frames the current stack frame is + for idx, name in enumerate(correct_frames): + if thread.GetFrameAtIndex(0).name == name: + starting_index = idx + + # Test that all frames after the current frame listed in + # correct_frames appears in the backtrace. + frame_idx = 0 + for expected_frame in correct_frames[starting_index:]: + self.assertEqual(thread.GetFrameAtIndex(frame_idx).name, expected_frame) + frame_idx = frame_idx + 1 + + if self.TraceOn(): + print("StepInstruction") + thread.StepInstruction(False) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index b5c5cb79656c3..5bc991fc0f67d 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -80,6 +80,7 @@ _trap: .cfi_def_cfa sp, 32 add sp, sp, #32 .cfi_same_value sp + .cfi_def_cfa sp, 0 // jump back to $x23 to resume execution of to_be_interrupted br x23 >From d239b7a84c1e489b2630aa63358061435a749ce7 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 22:49:20 -0700 Subject: [PATCH 09/12] Add tests for x0 and x20, which I treat in normally ABI-wrong ways in these hand-written UnwindPlans. --- .../TestUnwindFramelessFaulted.py | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py index 34e7fbfffacde..d899b7a2f7369 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py +++ b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py @@ -34,17 +34,15 @@ def test_frameless_faulted_unwind(self): # Instruction step through the binary until we are in a function not # listed in correct_frames. - while ( - process.GetState() == lldb.eStateStopped - and thread.GetFrameAtIndex(0).name in correct_frames - ): + frame = thread.GetFrameAtIndex(0) + while process.GetState() == lldb.eStateStopped and frame.name in correct_frames: starting_index = 0 if self.TraceOn(): self.runCmd("bt") # Find which index into correct_frames the current stack frame is for idx, name in enumerate(correct_frames): - if thread.GetFrameAtIndex(0).name == name: + if frame.name == name: starting_index = idx # Test that all frames after the current frame listed in @@ -54,6 +52,32 @@ def test_frameless_faulted_unwind(self): self.assertEqual(thread.GetFrameAtIndex(frame_idx).name, expected_frame) frame_idx = frame_idx + 1 + # When we're at our deepest level, test that register passing of x0 and x20 + # follow the by-hand UnwindPlan rules. In this test program, we can get x0 + # in the middle of the stack and we CAN'T get x20. The opposites of the normal + # AArch64 SysV ABI. + if frame.name == "break_to_debugger": + tbi_frame = thread.GetFrameAtIndex(2) + self.assertEqual(tbi_frame.name, "to_be_interrupted") + + # The original argument to to_be_interrupted(), 10 + # Normally can't get x0 mid-stack, but UnwindPlans have special rules to + # make this possible. + x0_reg = tbi_frame.register["x0"] + self.assertTrue(x0_reg.IsValid()) + self.assertEqual(x0_reg.GetValueAsUnsigned(), 10) + + # The incremented return value from to_be_interrupted(), 11 + x24_reg = tbi_frame.register["x24"] + self.assertTrue(x24_reg.IsValid()) + self.assertEqual(x24_reg.GetValueAsUnsigned(), 11) + + # x20 can normally be fetched mid-stack, but the UnwindPlan + # has a rule saying it can't be fetched. + x20_reg = tbi_frame.register["x20"] + self.assertTrue(x20_reg.error.fail) + if self.TraceOn(): print("StepInstruction") thread.StepInstruction(False) + frame = thread.GetFrameAtIndex(0) >From 33563049b37479db9ebebdad0a1c982deb4c6f14 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 22:52:04 -0700 Subject: [PATCH 10/12] Remove some Mach-O/Darwinisms, get closer to this compiling on linux? I think maybe the only difference is that the symbol names are prefixed with _ on darwin? --- .../unwind-frameless-faulted/interrupt-and-trap-funcs.s | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 5bc991fc0f67d..2ba6a711767b3 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -5,7 +5,7 @@ #define ehframe_x23 23 #define ehframe_pc 32 - .section __TEXT,__text,regular,pure_instructions + .text //-------------------------------------- // to_be_interrupted() a frameless function that does a non-ABI @@ -14,7 +14,6 @@ // trap() knows to branch back to $x23 when it has finished. //-------------------------------------- .globl _to_be_interrupted - .p2align 2 _to_be_interrupted: .cfi_startproc @@ -45,7 +44,6 @@ L_.return: // break_to_debugger(). //-------------------------------------- .globl _trap - .p2align 2 _trap: .cfi_startproc .cfi_signal_frame @@ -90,7 +88,6 @@ _trap: // break_to_debugger() executes a BRK instruction //-------------------------------------- .globl _break_to_debugger - .p2align 2 _break_to_debugger: .cfi_startproc >From 80248f5a5841645a1da0026799c040d4db7ab6f3 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 18:44:27 -0700 Subject: [PATCH 11/12] Adapt test case to build & run on linux or Darwin. There's a slight syntax diff for the ADRP + ADD pair and the label name. And the function names in Darwin are prepended with an _ and not on linux. clang on darwin runs the .s file through the preprocessor, but it doesn't seem to on linux. I renamed interrupt-and-trap-funcs.s to interrupt-and-trap-funcs.c and run it through the preprocessor then assemble it in Makefile now. The linux version would probably run on other AArch64 systems like FreeBSD but I haven't checked those. --- .../API/macosx/unwind-frameless-faulted/Makefile | 5 +++-- .../TestUnwindFramelessFaulted.py | 5 ++++- ...d-trap-funcs.s => interrupt-and-trap-funcs.c} | 16 ++++++++++++---- .../API/macosx/unwind-frameless-faulted/main.c | 10 ++++++++++ 4 files changed, 29 insertions(+), 7 deletions(-) rename lldb/test/API/macosx/unwind-frameless-faulted/{interrupt-and-trap-funcs.s => interrupt-and-trap-funcs.c} (88%) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile index 76548928a3622..fca47ae47491c 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile +++ b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile @@ -1,7 +1,8 @@ C_SOURCES := main.c -interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.s - $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o $(SRCDIR)/interrupt-and-trap-funcs.s +interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.c + $(CC) $(CFLAGS) -E -o interrupt-and-trap-funcs.s $(SRCDIR)/interrupt-and-trap-funcs.c + $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o interrupt-and-trap-funcs.s include Makefile.rules diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py index d899b7a2f7369..fe4b9b52abebb 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py +++ b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py @@ -10,7 +10,10 @@ class TestUnwindFramelessFaulted(TestBase): NO_DEBUG_INFO_TESTCASE = True - @skipUnlessDarwin + @skipIf( + oslist=no_match([lldbplatformutil.getDarwinOSTriples(), "linux"]), + archs=no_match(["aarch64", "arm64", "arm64e"]), + ) def test_frameless_faulted_unwind(self): self.build() diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c similarity index 88% rename from lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s rename to lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c index 2ba6a711767b3..23c6cfd688969 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c @@ -6,7 +6,6 @@ #define ehframe_pc 32 .text - //-------------------------------------- // to_be_interrupted() a frameless function that does a non-ABI // function call to trap(), simulating an async signal/interrup/exception/fault. @@ -25,12 +24,21 @@ mov x24, x0 add x24, x24, #1 - adrp x23, L_.return at PAGE ; put return address in x4 +#if defined(__APPLE__) + adrp x23, L_.return at PAGE // put return address in x4 add x23, x23, L_.return at PAGEOFF +#else + adrp x23, .L.return + add x23, x23, :lo12:.L.return +#endif - b _trap ; branch to trap handler, fake async interrupt + b _trap // branch to trap handler, fake async interrupt +#if defined(__APPLE__) L_.return: +#else +.L.return: +#endif mov x0, x24 ret .cfi_endproc @@ -95,7 +103,7 @@ L_.return: // retrieve the value if it wants. .cfi_same_value ehframe_x0 - brk #0xf000 ;; __builtin_debugtrap aarch64 instruction + brk #0xf000 // __builtin_debugtrap aarch64 instruction ret .cfi_endproc diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c b/lldb/test/API/macosx/unwind-frameless-faulted/main.c index e121809f25478..27a2f228b5feb 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/main.c +++ b/lldb/test/API/macosx/unwind-frameless-faulted/main.c @@ -1,6 +1,16 @@ +#if defined(__APPLE__) int to_be_interrupted(int); +#else +int _to_be_interrupted(int); +#endif + int main() { int c = 10; +#if defined(__APPLE__) c = to_be_interrupted(c); +#else + c = _to_be_interrupted(c); + #endif + return c; } >From 98be98ce9eb68dd89179ed1dc1905db9e5fd9cbf Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 18:48:00 -0700 Subject: [PATCH 12/12] Move this test from macosx/ to functionalities/unwind because it can run on darwin or linux aarch64. --- .../unwind/frameless-faulted}/Makefile | 0 .../unwind/frameless-faulted}/TestUnwindFramelessFaulted.py | 0 .../unwind/frameless-faulted}/interrupt-and-trap-funcs.c | 0 .../unwind/frameless-faulted}/main.c | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename lldb/test/API/{macosx/unwind-frameless-faulted => functionalities/unwind/frameless-faulted}/Makefile (100%) rename lldb/test/API/{macosx/unwind-frameless-faulted => functionalities/unwind/frameless-faulted}/TestUnwindFramelessFaulted.py (100%) rename lldb/test/API/{macosx/unwind-frameless-faulted => functionalities/unwind/frameless-faulted}/interrupt-and-trap-funcs.c (100%) rename lldb/test/API/{macosx/unwind-frameless-faulted => functionalities/unwind/frameless-faulted}/main.c (100%) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile b/lldb/test/API/functionalities/unwind/frameless-faulted/Makefile similarity index 100% rename from lldb/test/API/macosx/unwind-frameless-faulted/Makefile rename to lldb/test/API/functionalities/unwind/frameless-faulted/Makefile diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py similarity index 100% rename from lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py rename to lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c similarity index 100% rename from lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c rename to lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c b/lldb/test/API/functionalities/unwind/frameless-faulted/main.c similarity index 100% rename from lldb/test/API/macosx/unwind-frameless-faulted/main.c rename to lldb/test/API/functionalities/unwind/frameless-faulted/main.c From lldb-commits at lists.llvm.org Fri May 9 18:48:46 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 18:48:46 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681eb07e.170a0220.2e20fd.8755@mx.google.com> github-actions[bot] wrote: :warning: C/C++ code formatter, clang-format found issues in your code. :warning:
You can test this locally with the following command: ``````````bash git-clang-format --diff HEAD~1 HEAD --extensions c,cpp -- lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c lldb/test/API/macosx/unwind-frameless-faulted/main.c lldb/source/Target/RegisterContextUnwind.cpp ``````````
View the diff from clang-format here. ``````````diff diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c index 23c6cfd68..ec2af07ac 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c @@ -1,11 +1,11 @@ #define DW_CFA_register 0x9 -#define ehframe_x0 0 -#define ehframe_x20 20 -#define ehframe_x22 22 -#define ehframe_x23 23 +#define ehframe_x0 0 +#define ehframe_x20 20 +#define ehframe_x22 22 +#define ehframe_x23 23 #define ehframe_pc 32 - .text +.text //-------------------------------------- // to_be_interrupted() a frameless function that does a non-ABI // function call to trap(), simulating an async signal/interrup/exception/fault. diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c b/lldb/test/API/macosx/unwind-frameless-faulted/main.c index 27a2f228b..1a016ea61 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/main.c +++ b/lldb/test/API/macosx/unwind-frameless-faulted/main.c @@ -10,7 +10,7 @@ int main() { c = to_be_interrupted(c); #else c = _to_be_interrupted(c); - #endif +#endif return c; } ``````````
https://github.com/llvm/llvm-project/pull/138805 From lldb-commits at lists.llvm.org Fri May 9 18:49:35 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Fri, 09 May 2025 18:49:35 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681eb0af.170a0220.1761f3.b7ff@mx.google.com> jasonmolenda wrote: Minor syntax adaptations to the assembly file to build on an aarch64 unbuntu 24.04, test updated and will run on linux or darwin now. https://github.com/llvm/llvm-project/pull/138805 From lldb-commits at lists.llvm.org Fri May 9 18:51:33 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Fri, 09 May 2025 18:51:33 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681eb125.170a0220.32f084.9825@mx.google.com> https://github.com/jasonmolenda updated https://github.com/llvm/llvm-project/pull/138805 Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Fri May 9 19:06:08 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Fri, 09 May 2025 19:06:08 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681eb490.050a0220.270b55.f59f@mx.google.com> https://github.com/jasonmolenda updated https://github.com/llvm/llvm-project/pull/138805 >From 4fc9acd03a58a3617b113352c48e5dd2fdb58eda Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Tue, 6 May 2025 22:37:17 -0700 Subject: [PATCH 01/14] [lldb] Provide lr value in faulting frame on arm64 When a frameless function faults or is interrupted asynchronously, the UnwindPlan MAY have no register location rule for the return address register (lr on arm64); the value is simply live in the lr register when it was interrupted, and the frame below this on the stack -- e.g. sigtramp on a Unix system -- has the full register context, including that register. RegisterContextUnwind::SavedLocationForRegister, when asked to find the caller's pc value, will first see if there is a pc register location. If there isn't, on a Return Address Register architecture like arm/mips/riscv, we rewrite the register request from "pc" to "RA register", and search for a location. On frame 0 (the live frame) and an interrupted frame, the UnwindPlan may have no register location rule for the RA Reg, that is valid. A frameless function that never calls another may simply keep the return address in the live register the whole way. Our instruction emulation unwind plans explicitly add a rule (see Pavel's May 2024 change https://github.com/llvm/llvm-project/pull/91321 ), but an UnwindPlan sourced from debug_frame may not. I've got a case where this exactly happens - clang debug_frame for arm64 where there is no register location for the lr in a frameless function. There is a fault in the middle of this frameless function and we only get the lr value from the fault handler below this frame if lr has a register location of `IsSame`, in line with Pavel's 2024 change. Similar to how we see a request of the RA Reg from frame 0 after failing to find an unwind location for the pc register, the same style of special casing is needed when this is a function that was interrupted. Without this change, we can find the pc of the frame that was executing when it was interrupted, but we need $lr to find its caller, and we don't descend down to the trap handler to get that value, truncating the stack. rdar://145614545 --- lldb/source/Target/RegisterContextUnwind.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 3ed49e12476dd..23a86bee2518b 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -1377,6 +1377,7 @@ RegisterContextUnwind::SavedLocationForRegister( } } + // Check if the active_row has a register location listed. if (regnum.IsValid() && active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind), unwindplan_regloc)) { @@ -1390,11 +1391,10 @@ RegisterContextUnwind::SavedLocationForRegister( // This is frame 0 and we're retrieving the PC and it's saved in a Return // Address register and it hasn't been saved anywhere yet -- that is, // it's still live in the actual register. Handle this specially. - if (!have_unwindplan_regloc && return_address_reg.IsValid() && - IsFrameZero()) { - if (return_address_reg.GetAsKind(eRegisterKindLLDB) != - LLDB_INVALID_REGNUM) { + return_address_reg.GetAsKind(eRegisterKindLLDB) != + LLDB_INVALID_REGNUM) { + if (IsFrameZero()) { lldb_private::UnwindLLDB::ConcreteRegisterLocation new_regloc; new_regloc.type = UnwindLLDB::ConcreteRegisterLocation:: eRegisterInLiveRegisterContext; @@ -1408,6 +1408,17 @@ RegisterContextUnwind::SavedLocationForRegister( return_address_reg.GetAsKind(eRegisterKindLLDB), return_address_reg.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } else if (BehavesLikeZerothFrame()) { + // This function was interrupted asynchronously -- it faulted, + // an async interrupt, a timer fired, a debugger expression etc. + // The caller's pc is in the Return Address register, but the + // UnwindPlan for this function may have no location rule for + // the RA reg. + // This means that the caller's return address is in the RA reg + // when the function was interrupted--descend down one stack frame + // to retrieve it from the trap handler's saved context. + unwindplan_regloc.SetSame(); + have_unwindplan_regloc = true; } } >From b10162deb49ecddca6439665c2b8ea1995fdd81f Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:24:17 -0700 Subject: [PATCH 02/14] Add the sources for an API test case to be written --- .../interrupt-and-trap-funcs.s | 92 +++++++++++++++++++ .../macosx/unwind-frameless-faulted/main.c | 6 ++ 2 files changed, 98 insertions(+) create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/main.c diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s new file mode 100644 index 0000000000000..23eb35c2b31ca --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -0,0 +1,92 @@ +#define DW_CFA_register 0x9 +#define ehframe_x22 22 +#define ehframe_x23 23 +#define ehframe_pc 32 + + .section __TEXT,__text,regular,pure_instructions + +//-------------------------------------- +// to_be_interrupted() a frameless function that does a non-ABI +// function call ("is interrupted/traps" simulated) to trap(). +// Before it branches to trap(), it puts its return address in +// x23. trap() knows to branch back to $x23 when it has finished. +//-------------------------------------- + .globl _to_be_interrupted + .p2align 2 +_to_be_interrupted: + .cfi_startproc + + // This is a garbage entry to ensure that eh_frame is emitted, + // it isn't used for anything. If there's no eh_frame, lldb + // can do an assembly emulation scan and add a rule for $lr + // which won't expose the issue at hand. + .cfi_escape DW_CFA_register, ehframe_x22, ehframe_x23 + mov x24, x0 + add x24, x24, #1 + + adrp x23, L_.return at PAGE ; put return address in x4 + add x23, x23, L_.return at PAGEOFF + + b _trap ; branch to trap handler, fake async interrupt + +L_.return: + mov x0, x24 + ret + .cfi_endproc + + + +//-------------------------------------- +// trap() trap handler function, sets up stack frame +// with special unwind rule for the pc value of the +// "interrupted" stack frame (it's in x23), then calls +// break_to_debugger(). +//-------------------------------------- + .globl _trap + .p2align 2 +_trap: + .cfi_startproc + .cfi_signal_frame + + // The pc value when we were interrupted is in x23 + .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 + + // standard prologue save of fp & lr so we can call puts() + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 + .cfi_def_cfa w29, 16 + .cfi_offset w30, -8 + .cfi_offset w29, -16 + + bl _break_to_debugger + + ldp x29, x30, [sp, #16] + add sp, sp, #32 + + // jump back to $x23 to resume execution of to_be_interrupted + br x23 + .cfi_endproc + +//-------------------------------------- +// break_to_debugger() executes a BRK instruction +//-------------------------------------- + .globl _break_to_debugger + .p2align 2 +_break_to_debugger: + .cfi_startproc + + // standard prologue save of fp & lr so we can call puts() + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 + .cfi_def_cfa w29, 16 + .cfi_offset w30, -8 + .cfi_offset w29, -16 + + brk #0xf000 ;; __builtin_debugtrap aarch64 instruction + + ldp x29, x30, [sp, #16] + add sp, sp, #32 + ret + .cfi_endproc diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c b/lldb/test/API/macosx/unwind-frameless-faulted/main.c new file mode 100644 index 0000000000000..e121809f25478 --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/main.c @@ -0,0 +1,6 @@ +int to_be_interrupted(int); +int main() { + int c = 10; + c = to_be_interrupted(c); + return c; +} >From 508b9cb404072263a6ed52a75b3b7aad5d949510 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:24:58 -0700 Subject: [PATCH 03/14] Add a log msg when a frame is marked as a trap handler via the UnwindPlan. And use the trap handler flag for zeroth frame so we can properly unwind a frameless function that was interrupted while in the trap handler function. --- lldb/source/Target/RegisterContextUnwind.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 51c0f56e9bc1e..cf4b96c6eda9f 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -248,6 +248,7 @@ void RegisterContextUnwind::InitializeZerothFrame() { active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); row_register_kind = m_full_unwind_plan_sp->GetRegisterKind(); + PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp); if (active_row && log) { StreamString active_row_strm; active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, @@ -1933,6 +1934,7 @@ void RegisterContextUnwind::PropagateTrapHandlerFlagFromUnwindPlan( } m_frame_type = eTrapHandlerFrame; + UnwindLogMsg("This frame is marked as a trap handler via its UnwindPlan"); if (m_current_offset_backed_up_one != m_current_offset) { // We backed up the pc by 1 to compute the symbol context, but >From d8843fe86c0eaaa1483bed0317f832b7630d8548 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:28:40 -0700 Subject: [PATCH 04/14] Update assembly a bit. --- .../interrupt-and-trap-funcs.s | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 23eb35c2b31ca..708ad9ed56a1c 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -51,7 +51,8 @@ _trap: // The pc value when we were interrupted is in x23 .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 - // standard prologue save of fp & lr so we can call puts() + // standard prologue save of fp & lr so we can call + // break_to_debugger() sub sp, sp, #32 stp x29, x30, [sp, #16] add x29, sp, #16 @@ -76,17 +77,7 @@ _trap: _break_to_debugger: .cfi_startproc - // standard prologue save of fp & lr so we can call puts() - sub sp, sp, #32 - stp x29, x30, [sp, #16] - add x29, sp, #16 - .cfi_def_cfa w29, 16 - .cfi_offset w30, -8 - .cfi_offset w29, -16 - brk #0xf000 ;; __builtin_debugtrap aarch64 instruction - ldp x29, x30, [sp, #16] - add sp, sp, #32 ret .cfi_endproc >From 1d90aa48c92d9f8ef43c889223c0804ac1491d53 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 18:18:14 -0700 Subject: [PATCH 05/14] Add unwind rules for x0 (volatile) and x20 (non-voltile) to trap() and break_to_debugger() for fun, and add unwind instructions for the epilogue of trap(). When stopped in `break_to_debugger`, ``` * frame #0: 0x00000001000003e8 a.out`break_to_debugger + 4 frame #1: 0x00000001000003d8 a.out`trap + 16 frame #2: 0x00000001000003c0 a.out`to_be_interrupted + 20 frame #3: 0x0000000100000398 a.out`main + 32 ``` Normally we can't provide a volatile register (e.g. x0) up the stack. And we can provide a non-volatile register (e.g. x20) up the stack. I added an IsSame rule for trap() and break_to_debugger() for x0, so it CAN be passed up the stack. And I added an Undefined rule for x20 to trap() so it CAN'T be provided up the stack. If a user selects `to_be_interrupted` and does `register read`, they'll get x0 and they won't get x20. From `main`, they will not get x0 or x20 (x0 because `to_be_interrupted` doesn't have an IsSame rule). --- .../interrupt-and-trap-funcs.s | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 708ad9ed56a1c..b50c4734f18f3 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -1,4 +1,6 @@ #define DW_CFA_register 0x9 +#define ehframe_x0 0 +#define ehframe_x20 20 #define ehframe_x22 22 #define ehframe_x23 23 #define ehframe_pc 32 @@ -7,9 +9,9 @@ //-------------------------------------- // to_be_interrupted() a frameless function that does a non-ABI -// function call ("is interrupted/traps" simulated) to trap(). -// Before it branches to trap(), it puts its return address in -// x23. trap() knows to branch back to $x23 when it has finished. +// function call to trap(), simulating an async signal/interrup/exception/fault. +// Before it branches to trap(), put the return address in x23. +// trap() knows to branch back to $x23 when it has finished. //-------------------------------------- .globl _to_be_interrupted .p2align 2 @@ -51,6 +53,15 @@ _trap: // The pc value when we were interrupted is in x23 .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 + // For fun, mark x0 as unmodified so the caller can + // retrieve the value if it wants. + .cfi_same_value ehframe_x0 + + // Mark x20 as undefined. This is a callee-preserved + // (non-volatile) register by the SysV AArch64 ABI, but + // it's be fun to see lldb not passing a value past this. + .cfi_undefined ehframe_x20 + // standard prologue save of fp & lr so we can call // break_to_debugger() sub sp, sp, #32 @@ -63,7 +74,11 @@ _trap: bl _break_to_debugger ldp x29, x30, [sp, #16] + .cfi_same_value x29 + .cfi_same_value x30 + .cfi_def_cfa sp, 32 add sp, sp, #32 + .cfi_same_value sp // jump back to $x23 to resume execution of to_be_interrupted br x23 @@ -77,6 +92,10 @@ _trap: _break_to_debugger: .cfi_startproc + // For fun, mark x0 as unmodified so the caller can + // retrieve the value if it wants. + .cfi_same_value ehframe_x0 + brk #0xf000 ;; __builtin_debugtrap aarch64 instruction ret >From 303edd47f7bcfb6af0a9947ca7b0b9a0c5bec589 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 18:46:14 -0700 Subject: [PATCH 06/14] small comment improvement --- .../unwind-frameless-faulted/interrupt-and-trap-funcs.s | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index b50c4734f18f3..38be06a681bf2 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -18,10 +18,10 @@ _to_be_interrupted: .cfi_startproc - // This is a garbage entry to ensure that eh_frame is emitted, - // it isn't used for anything. If there's no eh_frame, lldb - // can do an assembly emulation scan and add a rule for $lr - // which won't expose the issue at hand. + // This is a garbage entry to ensure that eh_frame is emitted. + // If there's no eh_frame, lldb can use the assembly emulation scan, + // which always includes a rule for $lr, and we won't replicate the + // bug we're testing for. .cfi_escape DW_CFA_register, ehframe_x22, ehframe_x23 mov x24, x0 add x24, x24, #1 >From 620eace34ae149b8c513767935118b6e870646fc Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 18:47:54 -0700 Subject: [PATCH 07/14] more comment fix --- .../macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 38be06a681bf2..b5c5cb79656c3 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -59,7 +59,8 @@ _trap: // Mark x20 as undefined. This is a callee-preserved // (non-volatile) register by the SysV AArch64 ABI, but - // it's be fun to see lldb not passing a value past this. + // it'll be fun to see lldb not passing a value past this + // point on the stack. .cfi_undefined ehframe_x20 // standard prologue save of fp & lr so we can call >From 6185c513beaa2f63dc04b54f723b209de8af2286 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 22:36:21 -0700 Subject: [PATCH 08/14] Add test case Makefile and API test python code. Also fix one bug in the by-hand unwind state in the assembly file. --- .../macosx/unwind-frameless-faulted/Makefile | 12 ++++ .../TestUnwindFramelessFaulted.py | 59 +++++++++++++++++++ .../interrupt-and-trap-funcs.s | 1 + 3 files changed, 72 insertions(+) create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/Makefile create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile new file mode 100644 index 0000000000000..76548928a3622 --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile @@ -0,0 +1,12 @@ +C_SOURCES := main.c + +interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.s + $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o $(SRCDIR)/interrupt-and-trap-funcs.s + +include Makefile.rules + +a.out: interrupt-and-trap-funcs.o + +# Needs to come after include +OBJECTS += interrupt-and-trap-funcs.o + diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py new file mode 100644 index 0000000000000..34e7fbfffacde --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py @@ -0,0 +1,59 @@ +"""Test that lldb backtraces a frameless function that faults correctly.""" + +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +import shutil +import os + + +class TestUnwindFramelessFaulted(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + @skipUnlessDarwin + def test_frameless_faulted_unwind(self): + self.build() + + (target, process, thread, bp) = lldbutil.run_to_name_breakpoint( + self, "main", only_one_thread=False + ) + + # The test program will have a backtrace like this at its deepest: + # + # * frame #0: 0x0000000102adc468 a.out`break_to_debugger + 4 + # frame #1: 0x0000000102adc458 a.out`trap + 16 + # frame #2: 0x0000000102adc440 a.out`to_be_interrupted + 20 + # frame #3: 0x0000000102adc418 a.out`main at main.c:4:7 + # frame #4: 0x0000000193b7eb4c dyld`start + 6000 + + correct_frames = ["break_to_debugger", "trap", "to_be_interrupted", "main"] + + # Keep track of when main has branch & linked, instruction step until we're + # back in main() + main_has_bl_ed = False + + # Instruction step through the binary until we are in a function not + # listed in correct_frames. + while ( + process.GetState() == lldb.eStateStopped + and thread.GetFrameAtIndex(0).name in correct_frames + ): + starting_index = 0 + if self.TraceOn(): + self.runCmd("bt") + + # Find which index into correct_frames the current stack frame is + for idx, name in enumerate(correct_frames): + if thread.GetFrameAtIndex(0).name == name: + starting_index = idx + + # Test that all frames after the current frame listed in + # correct_frames appears in the backtrace. + frame_idx = 0 + for expected_frame in correct_frames[starting_index:]: + self.assertEqual(thread.GetFrameAtIndex(frame_idx).name, expected_frame) + frame_idx = frame_idx + 1 + + if self.TraceOn(): + print("StepInstruction") + thread.StepInstruction(False) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index b5c5cb79656c3..5bc991fc0f67d 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -80,6 +80,7 @@ _trap: .cfi_def_cfa sp, 32 add sp, sp, #32 .cfi_same_value sp + .cfi_def_cfa sp, 0 // jump back to $x23 to resume execution of to_be_interrupted br x23 >From d239b7a84c1e489b2630aa63358061435a749ce7 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 22:49:20 -0700 Subject: [PATCH 09/14] Add tests for x0 and x20, which I treat in normally ABI-wrong ways in these hand-written UnwindPlans. --- .../TestUnwindFramelessFaulted.py | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py index 34e7fbfffacde..d899b7a2f7369 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py +++ b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py @@ -34,17 +34,15 @@ def test_frameless_faulted_unwind(self): # Instruction step through the binary until we are in a function not # listed in correct_frames. - while ( - process.GetState() == lldb.eStateStopped - and thread.GetFrameAtIndex(0).name in correct_frames - ): + frame = thread.GetFrameAtIndex(0) + while process.GetState() == lldb.eStateStopped and frame.name in correct_frames: starting_index = 0 if self.TraceOn(): self.runCmd("bt") # Find which index into correct_frames the current stack frame is for idx, name in enumerate(correct_frames): - if thread.GetFrameAtIndex(0).name == name: + if frame.name == name: starting_index = idx # Test that all frames after the current frame listed in @@ -54,6 +52,32 @@ def test_frameless_faulted_unwind(self): self.assertEqual(thread.GetFrameAtIndex(frame_idx).name, expected_frame) frame_idx = frame_idx + 1 + # When we're at our deepest level, test that register passing of x0 and x20 + # follow the by-hand UnwindPlan rules. In this test program, we can get x0 + # in the middle of the stack and we CAN'T get x20. The opposites of the normal + # AArch64 SysV ABI. + if frame.name == "break_to_debugger": + tbi_frame = thread.GetFrameAtIndex(2) + self.assertEqual(tbi_frame.name, "to_be_interrupted") + + # The original argument to to_be_interrupted(), 10 + # Normally can't get x0 mid-stack, but UnwindPlans have special rules to + # make this possible. + x0_reg = tbi_frame.register["x0"] + self.assertTrue(x0_reg.IsValid()) + self.assertEqual(x0_reg.GetValueAsUnsigned(), 10) + + # The incremented return value from to_be_interrupted(), 11 + x24_reg = tbi_frame.register["x24"] + self.assertTrue(x24_reg.IsValid()) + self.assertEqual(x24_reg.GetValueAsUnsigned(), 11) + + # x20 can normally be fetched mid-stack, but the UnwindPlan + # has a rule saying it can't be fetched. + x20_reg = tbi_frame.register["x20"] + self.assertTrue(x20_reg.error.fail) + if self.TraceOn(): print("StepInstruction") thread.StepInstruction(False) + frame = thread.GetFrameAtIndex(0) >From 33563049b37479db9ebebdad0a1c982deb4c6f14 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 22:52:04 -0700 Subject: [PATCH 10/14] Remove some Mach-O/Darwinisms, get closer to this compiling on linux? I think maybe the only difference is that the symbol names are prefixed with _ on darwin? --- .../unwind-frameless-faulted/interrupt-and-trap-funcs.s | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 5bc991fc0f67d..2ba6a711767b3 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -5,7 +5,7 @@ #define ehframe_x23 23 #define ehframe_pc 32 - .section __TEXT,__text,regular,pure_instructions + .text //-------------------------------------- // to_be_interrupted() a frameless function that does a non-ABI @@ -14,7 +14,6 @@ // trap() knows to branch back to $x23 when it has finished. //-------------------------------------- .globl _to_be_interrupted - .p2align 2 _to_be_interrupted: .cfi_startproc @@ -45,7 +44,6 @@ L_.return: // break_to_debugger(). //-------------------------------------- .globl _trap - .p2align 2 _trap: .cfi_startproc .cfi_signal_frame @@ -90,7 +88,6 @@ _trap: // break_to_debugger() executes a BRK instruction //-------------------------------------- .globl _break_to_debugger - .p2align 2 _break_to_debugger: .cfi_startproc >From 80248f5a5841645a1da0026799c040d4db7ab6f3 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 18:44:27 -0700 Subject: [PATCH 11/14] Adapt test case to build & run on linux or Darwin. There's a slight syntax diff for the ADRP + ADD pair and the label name. And the function names in Darwin are prepended with an _ and not on linux. clang on darwin runs the .s file through the preprocessor, but it doesn't seem to on linux. I renamed interrupt-and-trap-funcs.s to interrupt-and-trap-funcs.c and run it through the preprocessor then assemble it in Makefile now. The linux version would probably run on other AArch64 systems like FreeBSD but I haven't checked those. --- .../API/macosx/unwind-frameless-faulted/Makefile | 5 +++-- .../TestUnwindFramelessFaulted.py | 5 ++++- ...d-trap-funcs.s => interrupt-and-trap-funcs.c} | 16 ++++++++++++---- .../API/macosx/unwind-frameless-faulted/main.c | 10 ++++++++++ 4 files changed, 29 insertions(+), 7 deletions(-) rename lldb/test/API/macosx/unwind-frameless-faulted/{interrupt-and-trap-funcs.s => interrupt-and-trap-funcs.c} (88%) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile index 76548928a3622..fca47ae47491c 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile +++ b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile @@ -1,7 +1,8 @@ C_SOURCES := main.c -interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.s - $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o $(SRCDIR)/interrupt-and-trap-funcs.s +interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.c + $(CC) $(CFLAGS) -E -o interrupt-and-trap-funcs.s $(SRCDIR)/interrupt-and-trap-funcs.c + $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o interrupt-and-trap-funcs.s include Makefile.rules diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py index d899b7a2f7369..fe4b9b52abebb 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py +++ b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py @@ -10,7 +10,10 @@ class TestUnwindFramelessFaulted(TestBase): NO_DEBUG_INFO_TESTCASE = True - @skipUnlessDarwin + @skipIf( + oslist=no_match([lldbplatformutil.getDarwinOSTriples(), "linux"]), + archs=no_match(["aarch64", "arm64", "arm64e"]), + ) def test_frameless_faulted_unwind(self): self.build() diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c similarity index 88% rename from lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s rename to lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c index 2ba6a711767b3..23c6cfd688969 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c @@ -6,7 +6,6 @@ #define ehframe_pc 32 .text - //-------------------------------------- // to_be_interrupted() a frameless function that does a non-ABI // function call to trap(), simulating an async signal/interrup/exception/fault. @@ -25,12 +24,21 @@ mov x24, x0 add x24, x24, #1 - adrp x23, L_.return at PAGE ; put return address in x4 +#if defined(__APPLE__) + adrp x23, L_.return at PAGE // put return address in x4 add x23, x23, L_.return at PAGEOFF +#else + adrp x23, .L.return + add x23, x23, :lo12:.L.return +#endif - b _trap ; branch to trap handler, fake async interrupt + b _trap // branch to trap handler, fake async interrupt +#if defined(__APPLE__) L_.return: +#else +.L.return: +#endif mov x0, x24 ret .cfi_endproc @@ -95,7 +103,7 @@ L_.return: // retrieve the value if it wants. .cfi_same_value ehframe_x0 - brk #0xf000 ;; __builtin_debugtrap aarch64 instruction + brk #0xf000 // __builtin_debugtrap aarch64 instruction ret .cfi_endproc diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c b/lldb/test/API/macosx/unwind-frameless-faulted/main.c index e121809f25478..27a2f228b5feb 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/main.c +++ b/lldb/test/API/macosx/unwind-frameless-faulted/main.c @@ -1,6 +1,16 @@ +#if defined(__APPLE__) int to_be_interrupted(int); +#else +int _to_be_interrupted(int); +#endif + int main() { int c = 10; +#if defined(__APPLE__) c = to_be_interrupted(c); +#else + c = _to_be_interrupted(c); + #endif + return c; } >From 98be98ce9eb68dd89179ed1dc1905db9e5fd9cbf Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 18:48:00 -0700 Subject: [PATCH 12/14] Move this test from macosx/ to functionalities/unwind because it can run on darwin or linux aarch64. --- .../unwind/frameless-faulted}/Makefile | 0 .../unwind/frameless-faulted}/TestUnwindFramelessFaulted.py | 0 .../unwind/frameless-faulted}/interrupt-and-trap-funcs.c | 0 .../unwind/frameless-faulted}/main.c | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename lldb/test/API/{macosx/unwind-frameless-faulted => functionalities/unwind/frameless-faulted}/Makefile (100%) rename lldb/test/API/{macosx/unwind-frameless-faulted => functionalities/unwind/frameless-faulted}/TestUnwindFramelessFaulted.py (100%) rename lldb/test/API/{macosx/unwind-frameless-faulted => functionalities/unwind/frameless-faulted}/interrupt-and-trap-funcs.c (100%) rename lldb/test/API/{macosx/unwind-frameless-faulted => functionalities/unwind/frameless-faulted}/main.c (100%) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile b/lldb/test/API/functionalities/unwind/frameless-faulted/Makefile similarity index 100% rename from lldb/test/API/macosx/unwind-frameless-faulted/Makefile rename to lldb/test/API/functionalities/unwind/frameless-faulted/Makefile diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py similarity index 100% rename from lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py rename to lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c similarity index 100% rename from lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c rename to lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c b/lldb/test/API/functionalities/unwind/frameless-faulted/main.c similarity index 100% rename from lldb/test/API/macosx/unwind-frameless-faulted/main.c rename to lldb/test/API/functionalities/unwind/frameless-faulted/main.c >From 2e1250cf6bdaed75a3829a8cf465b2fb798505d3 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 18:51:15 -0700 Subject: [PATCH 13/14] typeo --- lldb/test/API/functionalities/unwind/frameless-faulted/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/main.c b/lldb/test/API/functionalities/unwind/frameless-faulted/main.c index 27a2f228b5feb..1a016ea61affb 100644 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/main.c +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/main.c @@ -10,7 +10,7 @@ int main() { c = to_be_interrupted(c); #else c = _to_be_interrupted(c); - #endif +#endif return c; } >From 2d30011693cf3c3aa1ebdfde57a2c43687b37757 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 19:05:15 -0700 Subject: [PATCH 14/14] Disabling clang-format of the assembly file called ".c" Add two more checks for non-ABI compliant register availability from the custom UnwindPlans. --- .../TestUnwindFramelessFaulted.py | 37 ++++++++++++++----- .../interrupt-and-trap-funcs.c | 7 ++++ 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py index fe4b9b52abebb..dfb42c56164ac 100644 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py @@ -55,31 +55,50 @@ def test_frameless_faulted_unwind(self): self.assertEqual(thread.GetFrameAtIndex(frame_idx).name, expected_frame) frame_idx = frame_idx + 1 - # When we're at our deepest level, test that register passing of x0 and x20 - # follow the by-hand UnwindPlan rules. In this test program, we can get x0 - # in the middle of the stack and we CAN'T get x20. The opposites of the normal - # AArch64 SysV ABI. + # When we're at our deepest level, test that register passing of + # x0 and x20 follow the by-hand UnwindPlan rules. + # In this test program, we can get x0 in the middle of the stack + # and we CAN'T get x20. The opposites of the normal AArch64 SysV + # ABI. if frame.name == "break_to_debugger": tbi_frame = thread.GetFrameAtIndex(2) self.assertEqual(tbi_frame.name, "to_be_interrupted") - # The original argument to to_be_interrupted(), 10 - # Normally can't get x0 mid-stack, but UnwindPlans have special rules to - # make this possible. + # Normally can't get x0 mid-stack, but UnwindPlans have + # special rules to make this possible. x0_reg = tbi_frame.register["x0"] self.assertTrue(x0_reg.IsValid()) self.assertEqual(x0_reg.GetValueAsUnsigned(), 10) - # The incremented return value from to_be_interrupted(), 11 x24_reg = tbi_frame.register["x24"] self.assertTrue(x24_reg.IsValid()) self.assertEqual(x24_reg.GetValueAsUnsigned(), 11) - # x20 can normally be fetched mid-stack, but the UnwindPlan # has a rule saying it can't be fetched. x20_reg = tbi_frame.register["x20"] self.assertTrue(x20_reg.error.fail) + trap_frame = thread.GetFrameAtIndex(1) + self.assertEqual(trap_frame.name, "trap") + # Confirm that we can fetch x0 from trap() which + # is normally not possible w/ SysV AbI, but special + # UnwindPlans in use. + x0_reg = trap_frame.register["x0"] + self.assertTrue(x0_reg.IsValid()) + self.assertEqual(x0_reg.GetValueAsUnsigned(), 10) + x1_reg = trap_frame.register["x1"] + self.assertTrue(x1_reg.error.fail) + + main_frame = thread.GetFrameAtIndex(3) + self.assertEqual(main_frame.name, "main") + # x20 can normally be fetched mid-stack, but the UnwindPlan + # has a rule saying it can't be fetched. + x20_reg = main_frame.register["x20"] + self.assertTrue(x20_reg.error.fail) + # x21 can be fetched mid-stack. + x21_reg = main_frame.register["x21"] + self.assertTrue(x21_reg.error.success) + if self.TraceOn(): print("StepInstruction") thread.StepInstruction(False) diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c index 23c6cfd688969..d32f01a2ea429 100644 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c @@ -1,3 +1,10 @@ +// This is assembly code that needs to be run +// through the preprocessor, for simplicity of +// preprocessing it's named .c to start with. +// +// clang-format off + + #define DW_CFA_register 0x9 #define ehframe_x0 0 #define ehframe_x20 20 From lldb-commits at lists.llvm.org Fri May 9 19:09:26 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Fri, 09 May 2025 19:09:26 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681eb556.170a0220.30d981.93fd@mx.google.com> jasonmolenda wrote: Ah, lldb-server doesn't secretly migrate the pc past a builtin_debugtrap() BRK instruction on linux like debugserver does, the test will inf loop never advancing past the BRK. https://github.com/llvm/llvm-project/pull/138805 From lldb-commits at lists.llvm.org Fri May 9 19:21:54 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Fri, 09 May 2025 19:21:54 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681eb842.050a0220.35a46c.e05b@mx.google.com> https://github.com/jasonmolenda updated https://github.com/llvm/llvm-project/pull/138805 >From 4fc9acd03a58a3617b113352c48e5dd2fdb58eda Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Tue, 6 May 2025 22:37:17 -0700 Subject: [PATCH 01/15] [lldb] Provide lr value in faulting frame on arm64 When a frameless function faults or is interrupted asynchronously, the UnwindPlan MAY have no register location rule for the return address register (lr on arm64); the value is simply live in the lr register when it was interrupted, and the frame below this on the stack -- e.g. sigtramp on a Unix system -- has the full register context, including that register. RegisterContextUnwind::SavedLocationForRegister, when asked to find the caller's pc value, will first see if there is a pc register location. If there isn't, on a Return Address Register architecture like arm/mips/riscv, we rewrite the register request from "pc" to "RA register", and search for a location. On frame 0 (the live frame) and an interrupted frame, the UnwindPlan may have no register location rule for the RA Reg, that is valid. A frameless function that never calls another may simply keep the return address in the live register the whole way. Our instruction emulation unwind plans explicitly add a rule (see Pavel's May 2024 change https://github.com/llvm/llvm-project/pull/91321 ), but an UnwindPlan sourced from debug_frame may not. I've got a case where this exactly happens - clang debug_frame for arm64 where there is no register location for the lr in a frameless function. There is a fault in the middle of this frameless function and we only get the lr value from the fault handler below this frame if lr has a register location of `IsSame`, in line with Pavel's 2024 change. Similar to how we see a request of the RA Reg from frame 0 after failing to find an unwind location for the pc register, the same style of special casing is needed when this is a function that was interrupted. Without this change, we can find the pc of the frame that was executing when it was interrupted, but we need $lr to find its caller, and we don't descend down to the trap handler to get that value, truncating the stack. rdar://145614545 --- lldb/source/Target/RegisterContextUnwind.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 3ed49e12476dd..23a86bee2518b 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -1377,6 +1377,7 @@ RegisterContextUnwind::SavedLocationForRegister( } } + // Check if the active_row has a register location listed. if (regnum.IsValid() && active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind), unwindplan_regloc)) { @@ -1390,11 +1391,10 @@ RegisterContextUnwind::SavedLocationForRegister( // This is frame 0 and we're retrieving the PC and it's saved in a Return // Address register and it hasn't been saved anywhere yet -- that is, // it's still live in the actual register. Handle this specially. - if (!have_unwindplan_regloc && return_address_reg.IsValid() && - IsFrameZero()) { - if (return_address_reg.GetAsKind(eRegisterKindLLDB) != - LLDB_INVALID_REGNUM) { + return_address_reg.GetAsKind(eRegisterKindLLDB) != + LLDB_INVALID_REGNUM) { + if (IsFrameZero()) { lldb_private::UnwindLLDB::ConcreteRegisterLocation new_regloc; new_regloc.type = UnwindLLDB::ConcreteRegisterLocation:: eRegisterInLiveRegisterContext; @@ -1408,6 +1408,17 @@ RegisterContextUnwind::SavedLocationForRegister( return_address_reg.GetAsKind(eRegisterKindLLDB), return_address_reg.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } else if (BehavesLikeZerothFrame()) { + // This function was interrupted asynchronously -- it faulted, + // an async interrupt, a timer fired, a debugger expression etc. + // The caller's pc is in the Return Address register, but the + // UnwindPlan for this function may have no location rule for + // the RA reg. + // This means that the caller's return address is in the RA reg + // when the function was interrupted--descend down one stack frame + // to retrieve it from the trap handler's saved context. + unwindplan_regloc.SetSame(); + have_unwindplan_regloc = true; } } >From b10162deb49ecddca6439665c2b8ea1995fdd81f Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:24:17 -0700 Subject: [PATCH 02/15] Add the sources for an API test case to be written --- .../interrupt-and-trap-funcs.s | 92 +++++++++++++++++++ .../macosx/unwind-frameless-faulted/main.c | 6 ++ 2 files changed, 98 insertions(+) create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/main.c diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s new file mode 100644 index 0000000000000..23eb35c2b31ca --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -0,0 +1,92 @@ +#define DW_CFA_register 0x9 +#define ehframe_x22 22 +#define ehframe_x23 23 +#define ehframe_pc 32 + + .section __TEXT,__text,regular,pure_instructions + +//-------------------------------------- +// to_be_interrupted() a frameless function that does a non-ABI +// function call ("is interrupted/traps" simulated) to trap(). +// Before it branches to trap(), it puts its return address in +// x23. trap() knows to branch back to $x23 when it has finished. +//-------------------------------------- + .globl _to_be_interrupted + .p2align 2 +_to_be_interrupted: + .cfi_startproc + + // This is a garbage entry to ensure that eh_frame is emitted, + // it isn't used for anything. If there's no eh_frame, lldb + // can do an assembly emulation scan and add a rule for $lr + // which won't expose the issue at hand. + .cfi_escape DW_CFA_register, ehframe_x22, ehframe_x23 + mov x24, x0 + add x24, x24, #1 + + adrp x23, L_.return at PAGE ; put return address in x4 + add x23, x23, L_.return at PAGEOFF + + b _trap ; branch to trap handler, fake async interrupt + +L_.return: + mov x0, x24 + ret + .cfi_endproc + + + +//-------------------------------------- +// trap() trap handler function, sets up stack frame +// with special unwind rule for the pc value of the +// "interrupted" stack frame (it's in x23), then calls +// break_to_debugger(). +//-------------------------------------- + .globl _trap + .p2align 2 +_trap: + .cfi_startproc + .cfi_signal_frame + + // The pc value when we were interrupted is in x23 + .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 + + // standard prologue save of fp & lr so we can call puts() + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 + .cfi_def_cfa w29, 16 + .cfi_offset w30, -8 + .cfi_offset w29, -16 + + bl _break_to_debugger + + ldp x29, x30, [sp, #16] + add sp, sp, #32 + + // jump back to $x23 to resume execution of to_be_interrupted + br x23 + .cfi_endproc + +//-------------------------------------- +// break_to_debugger() executes a BRK instruction +//-------------------------------------- + .globl _break_to_debugger + .p2align 2 +_break_to_debugger: + .cfi_startproc + + // standard prologue save of fp & lr so we can call puts() + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 + .cfi_def_cfa w29, 16 + .cfi_offset w30, -8 + .cfi_offset w29, -16 + + brk #0xf000 ;; __builtin_debugtrap aarch64 instruction + + ldp x29, x30, [sp, #16] + add sp, sp, #32 + ret + .cfi_endproc diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c b/lldb/test/API/macosx/unwind-frameless-faulted/main.c new file mode 100644 index 0000000000000..e121809f25478 --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/main.c @@ -0,0 +1,6 @@ +int to_be_interrupted(int); +int main() { + int c = 10; + c = to_be_interrupted(c); + return c; +} >From 508b9cb404072263a6ed52a75b3b7aad5d949510 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:24:58 -0700 Subject: [PATCH 03/15] Add a log msg when a frame is marked as a trap handler via the UnwindPlan. And use the trap handler flag for zeroth frame so we can properly unwind a frameless function that was interrupted while in the trap handler function. --- lldb/source/Target/RegisterContextUnwind.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 51c0f56e9bc1e..cf4b96c6eda9f 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -248,6 +248,7 @@ void RegisterContextUnwind::InitializeZerothFrame() { active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); row_register_kind = m_full_unwind_plan_sp->GetRegisterKind(); + PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp); if (active_row && log) { StreamString active_row_strm; active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, @@ -1933,6 +1934,7 @@ void RegisterContextUnwind::PropagateTrapHandlerFlagFromUnwindPlan( } m_frame_type = eTrapHandlerFrame; + UnwindLogMsg("This frame is marked as a trap handler via its UnwindPlan"); if (m_current_offset_backed_up_one != m_current_offset) { // We backed up the pc by 1 to compute the symbol context, but >From d8843fe86c0eaaa1483bed0317f832b7630d8548 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:28:40 -0700 Subject: [PATCH 04/15] Update assembly a bit. --- .../interrupt-and-trap-funcs.s | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 23eb35c2b31ca..708ad9ed56a1c 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -51,7 +51,8 @@ _trap: // The pc value when we were interrupted is in x23 .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 - // standard prologue save of fp & lr so we can call puts() + // standard prologue save of fp & lr so we can call + // break_to_debugger() sub sp, sp, #32 stp x29, x30, [sp, #16] add x29, sp, #16 @@ -76,17 +77,7 @@ _trap: _break_to_debugger: .cfi_startproc - // standard prologue save of fp & lr so we can call puts() - sub sp, sp, #32 - stp x29, x30, [sp, #16] - add x29, sp, #16 - .cfi_def_cfa w29, 16 - .cfi_offset w30, -8 - .cfi_offset w29, -16 - brk #0xf000 ;; __builtin_debugtrap aarch64 instruction - ldp x29, x30, [sp, #16] - add sp, sp, #32 ret .cfi_endproc >From 1d90aa48c92d9f8ef43c889223c0804ac1491d53 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 18:18:14 -0700 Subject: [PATCH 05/15] Add unwind rules for x0 (volatile) and x20 (non-voltile) to trap() and break_to_debugger() for fun, and add unwind instructions for the epilogue of trap(). When stopped in `break_to_debugger`, ``` * frame #0: 0x00000001000003e8 a.out`break_to_debugger + 4 frame #1: 0x00000001000003d8 a.out`trap + 16 frame #2: 0x00000001000003c0 a.out`to_be_interrupted + 20 frame #3: 0x0000000100000398 a.out`main + 32 ``` Normally we can't provide a volatile register (e.g. x0) up the stack. And we can provide a non-volatile register (e.g. x20) up the stack. I added an IsSame rule for trap() and break_to_debugger() for x0, so it CAN be passed up the stack. And I added an Undefined rule for x20 to trap() so it CAN'T be provided up the stack. If a user selects `to_be_interrupted` and does `register read`, they'll get x0 and they won't get x20. From `main`, they will not get x0 or x20 (x0 because `to_be_interrupted` doesn't have an IsSame rule). --- .../interrupt-and-trap-funcs.s | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 708ad9ed56a1c..b50c4734f18f3 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -1,4 +1,6 @@ #define DW_CFA_register 0x9 +#define ehframe_x0 0 +#define ehframe_x20 20 #define ehframe_x22 22 #define ehframe_x23 23 #define ehframe_pc 32 @@ -7,9 +9,9 @@ //-------------------------------------- // to_be_interrupted() a frameless function that does a non-ABI -// function call ("is interrupted/traps" simulated) to trap(). -// Before it branches to trap(), it puts its return address in -// x23. trap() knows to branch back to $x23 when it has finished. +// function call to trap(), simulating an async signal/interrup/exception/fault. +// Before it branches to trap(), put the return address in x23. +// trap() knows to branch back to $x23 when it has finished. //-------------------------------------- .globl _to_be_interrupted .p2align 2 @@ -51,6 +53,15 @@ _trap: // The pc value when we were interrupted is in x23 .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 + // For fun, mark x0 as unmodified so the caller can + // retrieve the value if it wants. + .cfi_same_value ehframe_x0 + + // Mark x20 as undefined. This is a callee-preserved + // (non-volatile) register by the SysV AArch64 ABI, but + // it's be fun to see lldb not passing a value past this. + .cfi_undefined ehframe_x20 + // standard prologue save of fp & lr so we can call // break_to_debugger() sub sp, sp, #32 @@ -63,7 +74,11 @@ _trap: bl _break_to_debugger ldp x29, x30, [sp, #16] + .cfi_same_value x29 + .cfi_same_value x30 + .cfi_def_cfa sp, 32 add sp, sp, #32 + .cfi_same_value sp // jump back to $x23 to resume execution of to_be_interrupted br x23 @@ -77,6 +92,10 @@ _trap: _break_to_debugger: .cfi_startproc + // For fun, mark x0 as unmodified so the caller can + // retrieve the value if it wants. + .cfi_same_value ehframe_x0 + brk #0xf000 ;; __builtin_debugtrap aarch64 instruction ret >From 303edd47f7bcfb6af0a9947ca7b0b9a0c5bec589 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 18:46:14 -0700 Subject: [PATCH 06/15] small comment improvement --- .../unwind-frameless-faulted/interrupt-and-trap-funcs.s | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index b50c4734f18f3..38be06a681bf2 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -18,10 +18,10 @@ _to_be_interrupted: .cfi_startproc - // This is a garbage entry to ensure that eh_frame is emitted, - // it isn't used for anything. If there's no eh_frame, lldb - // can do an assembly emulation scan and add a rule for $lr - // which won't expose the issue at hand. + // This is a garbage entry to ensure that eh_frame is emitted. + // If there's no eh_frame, lldb can use the assembly emulation scan, + // which always includes a rule for $lr, and we won't replicate the + // bug we're testing for. .cfi_escape DW_CFA_register, ehframe_x22, ehframe_x23 mov x24, x0 add x24, x24, #1 >From 620eace34ae149b8c513767935118b6e870646fc Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 18:47:54 -0700 Subject: [PATCH 07/15] more comment fix --- .../macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 38be06a681bf2..b5c5cb79656c3 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -59,7 +59,8 @@ _trap: // Mark x20 as undefined. This is a callee-preserved // (non-volatile) register by the SysV AArch64 ABI, but - // it's be fun to see lldb not passing a value past this. + // it'll be fun to see lldb not passing a value past this + // point on the stack. .cfi_undefined ehframe_x20 // standard prologue save of fp & lr so we can call >From 6185c513beaa2f63dc04b54f723b209de8af2286 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 22:36:21 -0700 Subject: [PATCH 08/15] Add test case Makefile and API test python code. Also fix one bug in the by-hand unwind state in the assembly file. --- .../macosx/unwind-frameless-faulted/Makefile | 12 ++++ .../TestUnwindFramelessFaulted.py | 59 +++++++++++++++++++ .../interrupt-and-trap-funcs.s | 1 + 3 files changed, 72 insertions(+) create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/Makefile create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile new file mode 100644 index 0000000000000..76548928a3622 --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile @@ -0,0 +1,12 @@ +C_SOURCES := main.c + +interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.s + $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o $(SRCDIR)/interrupt-and-trap-funcs.s + +include Makefile.rules + +a.out: interrupt-and-trap-funcs.o + +# Needs to come after include +OBJECTS += interrupt-and-trap-funcs.o + diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py new file mode 100644 index 0000000000000..34e7fbfffacde --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py @@ -0,0 +1,59 @@ +"""Test that lldb backtraces a frameless function that faults correctly.""" + +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +import shutil +import os + + +class TestUnwindFramelessFaulted(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + @skipUnlessDarwin + def test_frameless_faulted_unwind(self): + self.build() + + (target, process, thread, bp) = lldbutil.run_to_name_breakpoint( + self, "main", only_one_thread=False + ) + + # The test program will have a backtrace like this at its deepest: + # + # * frame #0: 0x0000000102adc468 a.out`break_to_debugger + 4 + # frame #1: 0x0000000102adc458 a.out`trap + 16 + # frame #2: 0x0000000102adc440 a.out`to_be_interrupted + 20 + # frame #3: 0x0000000102adc418 a.out`main at main.c:4:7 + # frame #4: 0x0000000193b7eb4c dyld`start + 6000 + + correct_frames = ["break_to_debugger", "trap", "to_be_interrupted", "main"] + + # Keep track of when main has branch & linked, instruction step until we're + # back in main() + main_has_bl_ed = False + + # Instruction step through the binary until we are in a function not + # listed in correct_frames. + while ( + process.GetState() == lldb.eStateStopped + and thread.GetFrameAtIndex(0).name in correct_frames + ): + starting_index = 0 + if self.TraceOn(): + self.runCmd("bt") + + # Find which index into correct_frames the current stack frame is + for idx, name in enumerate(correct_frames): + if thread.GetFrameAtIndex(0).name == name: + starting_index = idx + + # Test that all frames after the current frame listed in + # correct_frames appears in the backtrace. + frame_idx = 0 + for expected_frame in correct_frames[starting_index:]: + self.assertEqual(thread.GetFrameAtIndex(frame_idx).name, expected_frame) + frame_idx = frame_idx + 1 + + if self.TraceOn(): + print("StepInstruction") + thread.StepInstruction(False) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index b5c5cb79656c3..5bc991fc0f67d 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -80,6 +80,7 @@ _trap: .cfi_def_cfa sp, 32 add sp, sp, #32 .cfi_same_value sp + .cfi_def_cfa sp, 0 // jump back to $x23 to resume execution of to_be_interrupted br x23 >From d239b7a84c1e489b2630aa63358061435a749ce7 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 22:49:20 -0700 Subject: [PATCH 09/15] Add tests for x0 and x20, which I treat in normally ABI-wrong ways in these hand-written UnwindPlans. --- .../TestUnwindFramelessFaulted.py | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py index 34e7fbfffacde..d899b7a2f7369 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py +++ b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py @@ -34,17 +34,15 @@ def test_frameless_faulted_unwind(self): # Instruction step through the binary until we are in a function not # listed in correct_frames. - while ( - process.GetState() == lldb.eStateStopped - and thread.GetFrameAtIndex(0).name in correct_frames - ): + frame = thread.GetFrameAtIndex(0) + while process.GetState() == lldb.eStateStopped and frame.name in correct_frames: starting_index = 0 if self.TraceOn(): self.runCmd("bt") # Find which index into correct_frames the current stack frame is for idx, name in enumerate(correct_frames): - if thread.GetFrameAtIndex(0).name == name: + if frame.name == name: starting_index = idx # Test that all frames after the current frame listed in @@ -54,6 +52,32 @@ def test_frameless_faulted_unwind(self): self.assertEqual(thread.GetFrameAtIndex(frame_idx).name, expected_frame) frame_idx = frame_idx + 1 + # When we're at our deepest level, test that register passing of x0 and x20 + # follow the by-hand UnwindPlan rules. In this test program, we can get x0 + # in the middle of the stack and we CAN'T get x20. The opposites of the normal + # AArch64 SysV ABI. + if frame.name == "break_to_debugger": + tbi_frame = thread.GetFrameAtIndex(2) + self.assertEqual(tbi_frame.name, "to_be_interrupted") + + # The original argument to to_be_interrupted(), 10 + # Normally can't get x0 mid-stack, but UnwindPlans have special rules to + # make this possible. + x0_reg = tbi_frame.register["x0"] + self.assertTrue(x0_reg.IsValid()) + self.assertEqual(x0_reg.GetValueAsUnsigned(), 10) + + # The incremented return value from to_be_interrupted(), 11 + x24_reg = tbi_frame.register["x24"] + self.assertTrue(x24_reg.IsValid()) + self.assertEqual(x24_reg.GetValueAsUnsigned(), 11) + + # x20 can normally be fetched mid-stack, but the UnwindPlan + # has a rule saying it can't be fetched. + x20_reg = tbi_frame.register["x20"] + self.assertTrue(x20_reg.error.fail) + if self.TraceOn(): print("StepInstruction") thread.StepInstruction(False) + frame = thread.GetFrameAtIndex(0) >From 33563049b37479db9ebebdad0a1c982deb4c6f14 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 22:52:04 -0700 Subject: [PATCH 10/15] Remove some Mach-O/Darwinisms, get closer to this compiling on linux? I think maybe the only difference is that the symbol names are prefixed with _ on darwin? --- .../unwind-frameless-faulted/interrupt-and-trap-funcs.s | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 5bc991fc0f67d..2ba6a711767b3 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -5,7 +5,7 @@ #define ehframe_x23 23 #define ehframe_pc 32 - .section __TEXT,__text,regular,pure_instructions + .text //-------------------------------------- // to_be_interrupted() a frameless function that does a non-ABI @@ -14,7 +14,6 @@ // trap() knows to branch back to $x23 when it has finished. //-------------------------------------- .globl _to_be_interrupted - .p2align 2 _to_be_interrupted: .cfi_startproc @@ -45,7 +44,6 @@ L_.return: // break_to_debugger(). //-------------------------------------- .globl _trap - .p2align 2 _trap: .cfi_startproc .cfi_signal_frame @@ -90,7 +88,6 @@ _trap: // break_to_debugger() executes a BRK instruction //-------------------------------------- .globl _break_to_debugger - .p2align 2 _break_to_debugger: .cfi_startproc >From 80248f5a5841645a1da0026799c040d4db7ab6f3 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 18:44:27 -0700 Subject: [PATCH 11/15] Adapt test case to build & run on linux or Darwin. There's a slight syntax diff for the ADRP + ADD pair and the label name. And the function names in Darwin are prepended with an _ and not on linux. clang on darwin runs the .s file through the preprocessor, but it doesn't seem to on linux. I renamed interrupt-and-trap-funcs.s to interrupt-and-trap-funcs.c and run it through the preprocessor then assemble it in Makefile now. The linux version would probably run on other AArch64 systems like FreeBSD but I haven't checked those. --- .../API/macosx/unwind-frameless-faulted/Makefile | 5 +++-- .../TestUnwindFramelessFaulted.py | 5 ++++- ...d-trap-funcs.s => interrupt-and-trap-funcs.c} | 16 ++++++++++++---- .../API/macosx/unwind-frameless-faulted/main.c | 10 ++++++++++ 4 files changed, 29 insertions(+), 7 deletions(-) rename lldb/test/API/macosx/unwind-frameless-faulted/{interrupt-and-trap-funcs.s => interrupt-and-trap-funcs.c} (88%) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile index 76548928a3622..fca47ae47491c 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile +++ b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile @@ -1,7 +1,8 @@ C_SOURCES := main.c -interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.s - $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o $(SRCDIR)/interrupt-and-trap-funcs.s +interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.c + $(CC) $(CFLAGS) -E -o interrupt-and-trap-funcs.s $(SRCDIR)/interrupt-and-trap-funcs.c + $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o interrupt-and-trap-funcs.s include Makefile.rules diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py index d899b7a2f7369..fe4b9b52abebb 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py +++ b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py @@ -10,7 +10,10 @@ class TestUnwindFramelessFaulted(TestBase): NO_DEBUG_INFO_TESTCASE = True - @skipUnlessDarwin + @skipIf( + oslist=no_match([lldbplatformutil.getDarwinOSTriples(), "linux"]), + archs=no_match(["aarch64", "arm64", "arm64e"]), + ) def test_frameless_faulted_unwind(self): self.build() diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c similarity index 88% rename from lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s rename to lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c index 2ba6a711767b3..23c6cfd688969 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c @@ -6,7 +6,6 @@ #define ehframe_pc 32 .text - //-------------------------------------- // to_be_interrupted() a frameless function that does a non-ABI // function call to trap(), simulating an async signal/interrup/exception/fault. @@ -25,12 +24,21 @@ mov x24, x0 add x24, x24, #1 - adrp x23, L_.return at PAGE ; put return address in x4 +#if defined(__APPLE__) + adrp x23, L_.return at PAGE // put return address in x4 add x23, x23, L_.return at PAGEOFF +#else + adrp x23, .L.return + add x23, x23, :lo12:.L.return +#endif - b _trap ; branch to trap handler, fake async interrupt + b _trap // branch to trap handler, fake async interrupt +#if defined(__APPLE__) L_.return: +#else +.L.return: +#endif mov x0, x24 ret .cfi_endproc @@ -95,7 +103,7 @@ L_.return: // retrieve the value if it wants. .cfi_same_value ehframe_x0 - brk #0xf000 ;; __builtin_debugtrap aarch64 instruction + brk #0xf000 // __builtin_debugtrap aarch64 instruction ret .cfi_endproc diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c b/lldb/test/API/macosx/unwind-frameless-faulted/main.c index e121809f25478..27a2f228b5feb 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/main.c +++ b/lldb/test/API/macosx/unwind-frameless-faulted/main.c @@ -1,6 +1,16 @@ +#if defined(__APPLE__) int to_be_interrupted(int); +#else +int _to_be_interrupted(int); +#endif + int main() { int c = 10; +#if defined(__APPLE__) c = to_be_interrupted(c); +#else + c = _to_be_interrupted(c); + #endif + return c; } >From 98be98ce9eb68dd89179ed1dc1905db9e5fd9cbf Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 18:48:00 -0700 Subject: [PATCH 12/15] Move this test from macosx/ to functionalities/unwind because it can run on darwin or linux aarch64. --- .../unwind/frameless-faulted}/Makefile | 0 .../unwind/frameless-faulted}/TestUnwindFramelessFaulted.py | 0 .../unwind/frameless-faulted}/interrupt-and-trap-funcs.c | 0 .../unwind/frameless-faulted}/main.c | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename lldb/test/API/{macosx/unwind-frameless-faulted => functionalities/unwind/frameless-faulted}/Makefile (100%) rename lldb/test/API/{macosx/unwind-frameless-faulted => functionalities/unwind/frameless-faulted}/TestUnwindFramelessFaulted.py (100%) rename lldb/test/API/{macosx/unwind-frameless-faulted => functionalities/unwind/frameless-faulted}/interrupt-and-trap-funcs.c (100%) rename lldb/test/API/{macosx/unwind-frameless-faulted => functionalities/unwind/frameless-faulted}/main.c (100%) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile b/lldb/test/API/functionalities/unwind/frameless-faulted/Makefile similarity index 100% rename from lldb/test/API/macosx/unwind-frameless-faulted/Makefile rename to lldb/test/API/functionalities/unwind/frameless-faulted/Makefile diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py similarity index 100% rename from lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py rename to lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c similarity index 100% rename from lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c rename to lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c b/lldb/test/API/functionalities/unwind/frameless-faulted/main.c similarity index 100% rename from lldb/test/API/macosx/unwind-frameless-faulted/main.c rename to lldb/test/API/functionalities/unwind/frameless-faulted/main.c >From 2e1250cf6bdaed75a3829a8cf465b2fb798505d3 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 18:51:15 -0700 Subject: [PATCH 13/15] typeo --- lldb/test/API/functionalities/unwind/frameless-faulted/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/main.c b/lldb/test/API/functionalities/unwind/frameless-faulted/main.c index 27a2f228b5feb..1a016ea61affb 100644 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/main.c +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/main.c @@ -10,7 +10,7 @@ int main() { c = to_be_interrupted(c); #else c = _to_be_interrupted(c); - #endif +#endif return c; } >From 2d30011693cf3c3aa1ebdfde57a2c43687b37757 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 19:05:15 -0700 Subject: [PATCH 14/15] Disabling clang-format of the assembly file called ".c" Add two more checks for non-ABI compliant register availability from the custom UnwindPlans. --- .../TestUnwindFramelessFaulted.py | 37 ++++++++++++++----- .../interrupt-and-trap-funcs.c | 7 ++++ 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py index fe4b9b52abebb..dfb42c56164ac 100644 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py @@ -55,31 +55,50 @@ def test_frameless_faulted_unwind(self): self.assertEqual(thread.GetFrameAtIndex(frame_idx).name, expected_frame) frame_idx = frame_idx + 1 - # When we're at our deepest level, test that register passing of x0 and x20 - # follow the by-hand UnwindPlan rules. In this test program, we can get x0 - # in the middle of the stack and we CAN'T get x20. The opposites of the normal - # AArch64 SysV ABI. + # When we're at our deepest level, test that register passing of + # x0 and x20 follow the by-hand UnwindPlan rules. + # In this test program, we can get x0 in the middle of the stack + # and we CAN'T get x20. The opposites of the normal AArch64 SysV + # ABI. if frame.name == "break_to_debugger": tbi_frame = thread.GetFrameAtIndex(2) self.assertEqual(tbi_frame.name, "to_be_interrupted") - # The original argument to to_be_interrupted(), 10 - # Normally can't get x0 mid-stack, but UnwindPlans have special rules to - # make this possible. + # Normally can't get x0 mid-stack, but UnwindPlans have + # special rules to make this possible. x0_reg = tbi_frame.register["x0"] self.assertTrue(x0_reg.IsValid()) self.assertEqual(x0_reg.GetValueAsUnsigned(), 10) - # The incremented return value from to_be_interrupted(), 11 x24_reg = tbi_frame.register["x24"] self.assertTrue(x24_reg.IsValid()) self.assertEqual(x24_reg.GetValueAsUnsigned(), 11) - # x20 can normally be fetched mid-stack, but the UnwindPlan # has a rule saying it can't be fetched. x20_reg = tbi_frame.register["x20"] self.assertTrue(x20_reg.error.fail) + trap_frame = thread.GetFrameAtIndex(1) + self.assertEqual(trap_frame.name, "trap") + # Confirm that we can fetch x0 from trap() which + # is normally not possible w/ SysV AbI, but special + # UnwindPlans in use. + x0_reg = trap_frame.register["x0"] + self.assertTrue(x0_reg.IsValid()) + self.assertEqual(x0_reg.GetValueAsUnsigned(), 10) + x1_reg = trap_frame.register["x1"] + self.assertTrue(x1_reg.error.fail) + + main_frame = thread.GetFrameAtIndex(3) + self.assertEqual(main_frame.name, "main") + # x20 can normally be fetched mid-stack, but the UnwindPlan + # has a rule saying it can't be fetched. + x20_reg = main_frame.register["x20"] + self.assertTrue(x20_reg.error.fail) + # x21 can be fetched mid-stack. + x21_reg = main_frame.register["x21"] + self.assertTrue(x21_reg.error.success) + if self.TraceOn(): print("StepInstruction") thread.StepInstruction(False) diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c index 23c6cfd688969..d32f01a2ea429 100644 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c @@ -1,3 +1,10 @@ +// This is assembly code that needs to be run +// through the preprocessor, for simplicity of +// preprocessing it's named .c to start with. +// +// clang-format off + + #define DW_CFA_register 0x9 #define ehframe_x0 0 #define ehframe_x20 20 >From a555af1ba3c17076708aea0548997fd3b579e2a7 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 19:20:02 -0700 Subject: [PATCH 15/15] Advance past the BRK instruction in break_to_debugger() for lldb-server where it does not advance past this BRK instruction automatically. Also add a limit on instruction stepping, so if this does get stuck in a loop it won't run forever. I haven't tested this on a linux system yet but in by-hand running on the binary, I saw the lldb-server behavior w.r.t builtin_debugtrap and this should be necessary. --- .../TestUnwindFramelessFaulted.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py index dfb42c56164ac..a03d994045773 100644 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py @@ -38,7 +38,13 @@ def test_frameless_faulted_unwind(self): # Instruction step through the binary until we are in a function not # listed in correct_frames. frame = thread.GetFrameAtIndex(0) - while process.GetState() == lldb.eStateStopped and frame.name in correct_frames: + step_count = 0 + max_step_count = 200 + while ( + process.GetState() == lldb.eStateStopped + and frame.name in correct_frames + and step_count < max_step_count + ): starting_index = 0 if self.TraceOn(): self.runCmd("bt") @@ -99,7 +105,18 @@ def test_frameless_faulted_unwind(self): x21_reg = main_frame.register["x21"] self.assertTrue(x21_reg.error.success) + # manually move past the BRK instruction in + # break_to_debugger(). lldb-server doesn't + # advance past the builtin_debugtrap() BRK + # instruction. + if ( + thread.GetStopReason() == lldb.eStopReasonException + and frame.name == "break_to_debugger" + ): + frame.SetPC(frame.GetPC() + 4) + if self.TraceOn(): print("StepInstruction") thread.StepInstruction(False) frame = thread.GetFrameAtIndex(0) + step_count = step_count + 1 From lldb-commits at lists.llvm.org Fri May 9 19:23:35 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Fri, 09 May 2025 19:23:35 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681eb8a7.170a0220.274bf7.e5f6@mx.google.com> https://github.com/jasonmolenda updated https://github.com/llvm/llvm-project/pull/138805 >From 4fc9acd03a58a3617b113352c48e5dd2fdb58eda Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Tue, 6 May 2025 22:37:17 -0700 Subject: [PATCH 01/16] [lldb] Provide lr value in faulting frame on arm64 When a frameless function faults or is interrupted asynchronously, the UnwindPlan MAY have no register location rule for the return address register (lr on arm64); the value is simply live in the lr register when it was interrupted, and the frame below this on the stack -- e.g. sigtramp on a Unix system -- has the full register context, including that register. RegisterContextUnwind::SavedLocationForRegister, when asked to find the caller's pc value, will first see if there is a pc register location. If there isn't, on a Return Address Register architecture like arm/mips/riscv, we rewrite the register request from "pc" to "RA register", and search for a location. On frame 0 (the live frame) and an interrupted frame, the UnwindPlan may have no register location rule for the RA Reg, that is valid. A frameless function that never calls another may simply keep the return address in the live register the whole way. Our instruction emulation unwind plans explicitly add a rule (see Pavel's May 2024 change https://github.com/llvm/llvm-project/pull/91321 ), but an UnwindPlan sourced from debug_frame may not. I've got a case where this exactly happens - clang debug_frame for arm64 where there is no register location for the lr in a frameless function. There is a fault in the middle of this frameless function and we only get the lr value from the fault handler below this frame if lr has a register location of `IsSame`, in line with Pavel's 2024 change. Similar to how we see a request of the RA Reg from frame 0 after failing to find an unwind location for the pc register, the same style of special casing is needed when this is a function that was interrupted. Without this change, we can find the pc of the frame that was executing when it was interrupted, but we need $lr to find its caller, and we don't descend down to the trap handler to get that value, truncating the stack. rdar://145614545 --- lldb/source/Target/RegisterContextUnwind.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 3ed49e12476dd..23a86bee2518b 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -1377,6 +1377,7 @@ RegisterContextUnwind::SavedLocationForRegister( } } + // Check if the active_row has a register location listed. if (regnum.IsValid() && active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind), unwindplan_regloc)) { @@ -1390,11 +1391,10 @@ RegisterContextUnwind::SavedLocationForRegister( // This is frame 0 and we're retrieving the PC and it's saved in a Return // Address register and it hasn't been saved anywhere yet -- that is, // it's still live in the actual register. Handle this specially. - if (!have_unwindplan_regloc && return_address_reg.IsValid() && - IsFrameZero()) { - if (return_address_reg.GetAsKind(eRegisterKindLLDB) != - LLDB_INVALID_REGNUM) { + return_address_reg.GetAsKind(eRegisterKindLLDB) != + LLDB_INVALID_REGNUM) { + if (IsFrameZero()) { lldb_private::UnwindLLDB::ConcreteRegisterLocation new_regloc; new_regloc.type = UnwindLLDB::ConcreteRegisterLocation:: eRegisterInLiveRegisterContext; @@ -1408,6 +1408,17 @@ RegisterContextUnwind::SavedLocationForRegister( return_address_reg.GetAsKind(eRegisterKindLLDB), return_address_reg.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } else if (BehavesLikeZerothFrame()) { + // This function was interrupted asynchronously -- it faulted, + // an async interrupt, a timer fired, a debugger expression etc. + // The caller's pc is in the Return Address register, but the + // UnwindPlan for this function may have no location rule for + // the RA reg. + // This means that the caller's return address is in the RA reg + // when the function was interrupted--descend down one stack frame + // to retrieve it from the trap handler's saved context. + unwindplan_regloc.SetSame(); + have_unwindplan_regloc = true; } } >From b10162deb49ecddca6439665c2b8ea1995fdd81f Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:24:17 -0700 Subject: [PATCH 02/16] Add the sources for an API test case to be written --- .../interrupt-and-trap-funcs.s | 92 +++++++++++++++++++ .../macosx/unwind-frameless-faulted/main.c | 6 ++ 2 files changed, 98 insertions(+) create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/main.c diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s new file mode 100644 index 0000000000000..23eb35c2b31ca --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -0,0 +1,92 @@ +#define DW_CFA_register 0x9 +#define ehframe_x22 22 +#define ehframe_x23 23 +#define ehframe_pc 32 + + .section __TEXT,__text,regular,pure_instructions + +//-------------------------------------- +// to_be_interrupted() a frameless function that does a non-ABI +// function call ("is interrupted/traps" simulated) to trap(). +// Before it branches to trap(), it puts its return address in +// x23. trap() knows to branch back to $x23 when it has finished. +//-------------------------------------- + .globl _to_be_interrupted + .p2align 2 +_to_be_interrupted: + .cfi_startproc + + // This is a garbage entry to ensure that eh_frame is emitted, + // it isn't used for anything. If there's no eh_frame, lldb + // can do an assembly emulation scan and add a rule for $lr + // which won't expose the issue at hand. + .cfi_escape DW_CFA_register, ehframe_x22, ehframe_x23 + mov x24, x0 + add x24, x24, #1 + + adrp x23, L_.return at PAGE ; put return address in x4 + add x23, x23, L_.return at PAGEOFF + + b _trap ; branch to trap handler, fake async interrupt + +L_.return: + mov x0, x24 + ret + .cfi_endproc + + + +//-------------------------------------- +// trap() trap handler function, sets up stack frame +// with special unwind rule for the pc value of the +// "interrupted" stack frame (it's in x23), then calls +// break_to_debugger(). +//-------------------------------------- + .globl _trap + .p2align 2 +_trap: + .cfi_startproc + .cfi_signal_frame + + // The pc value when we were interrupted is in x23 + .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 + + // standard prologue save of fp & lr so we can call puts() + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 + .cfi_def_cfa w29, 16 + .cfi_offset w30, -8 + .cfi_offset w29, -16 + + bl _break_to_debugger + + ldp x29, x30, [sp, #16] + add sp, sp, #32 + + // jump back to $x23 to resume execution of to_be_interrupted + br x23 + .cfi_endproc + +//-------------------------------------- +// break_to_debugger() executes a BRK instruction +//-------------------------------------- + .globl _break_to_debugger + .p2align 2 +_break_to_debugger: + .cfi_startproc + + // standard prologue save of fp & lr so we can call puts() + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 + .cfi_def_cfa w29, 16 + .cfi_offset w30, -8 + .cfi_offset w29, -16 + + brk #0xf000 ;; __builtin_debugtrap aarch64 instruction + + ldp x29, x30, [sp, #16] + add sp, sp, #32 + ret + .cfi_endproc diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c b/lldb/test/API/macosx/unwind-frameless-faulted/main.c new file mode 100644 index 0000000000000..e121809f25478 --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/main.c @@ -0,0 +1,6 @@ +int to_be_interrupted(int); +int main() { + int c = 10; + c = to_be_interrupted(c); + return c; +} >From 508b9cb404072263a6ed52a75b3b7aad5d949510 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:24:58 -0700 Subject: [PATCH 03/16] Add a log msg when a frame is marked as a trap handler via the UnwindPlan. And use the trap handler flag for zeroth frame so we can properly unwind a frameless function that was interrupted while in the trap handler function. --- lldb/source/Target/RegisterContextUnwind.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 51c0f56e9bc1e..cf4b96c6eda9f 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -248,6 +248,7 @@ void RegisterContextUnwind::InitializeZerothFrame() { active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); row_register_kind = m_full_unwind_plan_sp->GetRegisterKind(); + PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp); if (active_row && log) { StreamString active_row_strm; active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, @@ -1933,6 +1934,7 @@ void RegisterContextUnwind::PropagateTrapHandlerFlagFromUnwindPlan( } m_frame_type = eTrapHandlerFrame; + UnwindLogMsg("This frame is marked as a trap handler via its UnwindPlan"); if (m_current_offset_backed_up_one != m_current_offset) { // We backed up the pc by 1 to compute the symbol context, but >From d8843fe86c0eaaa1483bed0317f832b7630d8548 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:28:40 -0700 Subject: [PATCH 04/16] Update assembly a bit. --- .../interrupt-and-trap-funcs.s | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 23eb35c2b31ca..708ad9ed56a1c 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -51,7 +51,8 @@ _trap: // The pc value when we were interrupted is in x23 .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 - // standard prologue save of fp & lr so we can call puts() + // standard prologue save of fp & lr so we can call + // break_to_debugger() sub sp, sp, #32 stp x29, x30, [sp, #16] add x29, sp, #16 @@ -76,17 +77,7 @@ _trap: _break_to_debugger: .cfi_startproc - // standard prologue save of fp & lr so we can call puts() - sub sp, sp, #32 - stp x29, x30, [sp, #16] - add x29, sp, #16 - .cfi_def_cfa w29, 16 - .cfi_offset w30, -8 - .cfi_offset w29, -16 - brk #0xf000 ;; __builtin_debugtrap aarch64 instruction - ldp x29, x30, [sp, #16] - add sp, sp, #32 ret .cfi_endproc >From 1d90aa48c92d9f8ef43c889223c0804ac1491d53 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 18:18:14 -0700 Subject: [PATCH 05/16] Add unwind rules for x0 (volatile) and x20 (non-voltile) to trap() and break_to_debugger() for fun, and add unwind instructions for the epilogue of trap(). When stopped in `break_to_debugger`, ``` * frame #0: 0x00000001000003e8 a.out`break_to_debugger + 4 frame #1: 0x00000001000003d8 a.out`trap + 16 frame #2: 0x00000001000003c0 a.out`to_be_interrupted + 20 frame #3: 0x0000000100000398 a.out`main + 32 ``` Normally we can't provide a volatile register (e.g. x0) up the stack. And we can provide a non-volatile register (e.g. x20) up the stack. I added an IsSame rule for trap() and break_to_debugger() for x0, so it CAN be passed up the stack. And I added an Undefined rule for x20 to trap() so it CAN'T be provided up the stack. If a user selects `to_be_interrupted` and does `register read`, they'll get x0 and they won't get x20. From `main`, they will not get x0 or x20 (x0 because `to_be_interrupted` doesn't have an IsSame rule). --- .../interrupt-and-trap-funcs.s | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 708ad9ed56a1c..b50c4734f18f3 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -1,4 +1,6 @@ #define DW_CFA_register 0x9 +#define ehframe_x0 0 +#define ehframe_x20 20 #define ehframe_x22 22 #define ehframe_x23 23 #define ehframe_pc 32 @@ -7,9 +9,9 @@ //-------------------------------------- // to_be_interrupted() a frameless function that does a non-ABI -// function call ("is interrupted/traps" simulated) to trap(). -// Before it branches to trap(), it puts its return address in -// x23. trap() knows to branch back to $x23 when it has finished. +// function call to trap(), simulating an async signal/interrup/exception/fault. +// Before it branches to trap(), put the return address in x23. +// trap() knows to branch back to $x23 when it has finished. //-------------------------------------- .globl _to_be_interrupted .p2align 2 @@ -51,6 +53,15 @@ _trap: // The pc value when we were interrupted is in x23 .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 + // For fun, mark x0 as unmodified so the caller can + // retrieve the value if it wants. + .cfi_same_value ehframe_x0 + + // Mark x20 as undefined. This is a callee-preserved + // (non-volatile) register by the SysV AArch64 ABI, but + // it's be fun to see lldb not passing a value past this. + .cfi_undefined ehframe_x20 + // standard prologue save of fp & lr so we can call // break_to_debugger() sub sp, sp, #32 @@ -63,7 +74,11 @@ _trap: bl _break_to_debugger ldp x29, x30, [sp, #16] + .cfi_same_value x29 + .cfi_same_value x30 + .cfi_def_cfa sp, 32 add sp, sp, #32 + .cfi_same_value sp // jump back to $x23 to resume execution of to_be_interrupted br x23 @@ -77,6 +92,10 @@ _trap: _break_to_debugger: .cfi_startproc + // For fun, mark x0 as unmodified so the caller can + // retrieve the value if it wants. + .cfi_same_value ehframe_x0 + brk #0xf000 ;; __builtin_debugtrap aarch64 instruction ret >From 303edd47f7bcfb6af0a9947ca7b0b9a0c5bec589 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 18:46:14 -0700 Subject: [PATCH 06/16] small comment improvement --- .../unwind-frameless-faulted/interrupt-and-trap-funcs.s | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index b50c4734f18f3..38be06a681bf2 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -18,10 +18,10 @@ _to_be_interrupted: .cfi_startproc - // This is a garbage entry to ensure that eh_frame is emitted, - // it isn't used for anything. If there's no eh_frame, lldb - // can do an assembly emulation scan and add a rule for $lr - // which won't expose the issue at hand. + // This is a garbage entry to ensure that eh_frame is emitted. + // If there's no eh_frame, lldb can use the assembly emulation scan, + // which always includes a rule for $lr, and we won't replicate the + // bug we're testing for. .cfi_escape DW_CFA_register, ehframe_x22, ehframe_x23 mov x24, x0 add x24, x24, #1 >From 620eace34ae149b8c513767935118b6e870646fc Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 18:47:54 -0700 Subject: [PATCH 07/16] more comment fix --- .../macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 38be06a681bf2..b5c5cb79656c3 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -59,7 +59,8 @@ _trap: // Mark x20 as undefined. This is a callee-preserved // (non-volatile) register by the SysV AArch64 ABI, but - // it's be fun to see lldb not passing a value past this. + // it'll be fun to see lldb not passing a value past this + // point on the stack. .cfi_undefined ehframe_x20 // standard prologue save of fp & lr so we can call >From 6185c513beaa2f63dc04b54f723b209de8af2286 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 22:36:21 -0700 Subject: [PATCH 08/16] Add test case Makefile and API test python code. Also fix one bug in the by-hand unwind state in the assembly file. --- .../macosx/unwind-frameless-faulted/Makefile | 12 ++++ .../TestUnwindFramelessFaulted.py | 59 +++++++++++++++++++ .../interrupt-and-trap-funcs.s | 1 + 3 files changed, 72 insertions(+) create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/Makefile create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile new file mode 100644 index 0000000000000..76548928a3622 --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile @@ -0,0 +1,12 @@ +C_SOURCES := main.c + +interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.s + $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o $(SRCDIR)/interrupt-and-trap-funcs.s + +include Makefile.rules + +a.out: interrupt-and-trap-funcs.o + +# Needs to come after include +OBJECTS += interrupt-and-trap-funcs.o + diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py new file mode 100644 index 0000000000000..34e7fbfffacde --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py @@ -0,0 +1,59 @@ +"""Test that lldb backtraces a frameless function that faults correctly.""" + +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +import shutil +import os + + +class TestUnwindFramelessFaulted(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + @skipUnlessDarwin + def test_frameless_faulted_unwind(self): + self.build() + + (target, process, thread, bp) = lldbutil.run_to_name_breakpoint( + self, "main", only_one_thread=False + ) + + # The test program will have a backtrace like this at its deepest: + # + # * frame #0: 0x0000000102adc468 a.out`break_to_debugger + 4 + # frame #1: 0x0000000102adc458 a.out`trap + 16 + # frame #2: 0x0000000102adc440 a.out`to_be_interrupted + 20 + # frame #3: 0x0000000102adc418 a.out`main at main.c:4:7 + # frame #4: 0x0000000193b7eb4c dyld`start + 6000 + + correct_frames = ["break_to_debugger", "trap", "to_be_interrupted", "main"] + + # Keep track of when main has branch & linked, instruction step until we're + # back in main() + main_has_bl_ed = False + + # Instruction step through the binary until we are in a function not + # listed in correct_frames. + while ( + process.GetState() == lldb.eStateStopped + and thread.GetFrameAtIndex(0).name in correct_frames + ): + starting_index = 0 + if self.TraceOn(): + self.runCmd("bt") + + # Find which index into correct_frames the current stack frame is + for idx, name in enumerate(correct_frames): + if thread.GetFrameAtIndex(0).name == name: + starting_index = idx + + # Test that all frames after the current frame listed in + # correct_frames appears in the backtrace. + frame_idx = 0 + for expected_frame in correct_frames[starting_index:]: + self.assertEqual(thread.GetFrameAtIndex(frame_idx).name, expected_frame) + frame_idx = frame_idx + 1 + + if self.TraceOn(): + print("StepInstruction") + thread.StepInstruction(False) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index b5c5cb79656c3..5bc991fc0f67d 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -80,6 +80,7 @@ _trap: .cfi_def_cfa sp, 32 add sp, sp, #32 .cfi_same_value sp + .cfi_def_cfa sp, 0 // jump back to $x23 to resume execution of to_be_interrupted br x23 >From d239b7a84c1e489b2630aa63358061435a749ce7 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 22:49:20 -0700 Subject: [PATCH 09/16] Add tests for x0 and x20, which I treat in normally ABI-wrong ways in these hand-written UnwindPlans. --- .../TestUnwindFramelessFaulted.py | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py index 34e7fbfffacde..d899b7a2f7369 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py +++ b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py @@ -34,17 +34,15 @@ def test_frameless_faulted_unwind(self): # Instruction step through the binary until we are in a function not # listed in correct_frames. - while ( - process.GetState() == lldb.eStateStopped - and thread.GetFrameAtIndex(0).name in correct_frames - ): + frame = thread.GetFrameAtIndex(0) + while process.GetState() == lldb.eStateStopped and frame.name in correct_frames: starting_index = 0 if self.TraceOn(): self.runCmd("bt") # Find which index into correct_frames the current stack frame is for idx, name in enumerate(correct_frames): - if thread.GetFrameAtIndex(0).name == name: + if frame.name == name: starting_index = idx # Test that all frames after the current frame listed in @@ -54,6 +52,32 @@ def test_frameless_faulted_unwind(self): self.assertEqual(thread.GetFrameAtIndex(frame_idx).name, expected_frame) frame_idx = frame_idx + 1 + # When we're at our deepest level, test that register passing of x0 and x20 + # follow the by-hand UnwindPlan rules. In this test program, we can get x0 + # in the middle of the stack and we CAN'T get x20. The opposites of the normal + # AArch64 SysV ABI. + if frame.name == "break_to_debugger": + tbi_frame = thread.GetFrameAtIndex(2) + self.assertEqual(tbi_frame.name, "to_be_interrupted") + + # The original argument to to_be_interrupted(), 10 + # Normally can't get x0 mid-stack, but UnwindPlans have special rules to + # make this possible. + x0_reg = tbi_frame.register["x0"] + self.assertTrue(x0_reg.IsValid()) + self.assertEqual(x0_reg.GetValueAsUnsigned(), 10) + + # The incremented return value from to_be_interrupted(), 11 + x24_reg = tbi_frame.register["x24"] + self.assertTrue(x24_reg.IsValid()) + self.assertEqual(x24_reg.GetValueAsUnsigned(), 11) + + # x20 can normally be fetched mid-stack, but the UnwindPlan + # has a rule saying it can't be fetched. + x20_reg = tbi_frame.register["x20"] + self.assertTrue(x20_reg.error.fail) + if self.TraceOn(): print("StepInstruction") thread.StepInstruction(False) + frame = thread.GetFrameAtIndex(0) >From 33563049b37479db9ebebdad0a1c982deb4c6f14 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 22:52:04 -0700 Subject: [PATCH 10/16] Remove some Mach-O/Darwinisms, get closer to this compiling on linux? I think maybe the only difference is that the symbol names are prefixed with _ on darwin? --- .../unwind-frameless-faulted/interrupt-and-trap-funcs.s | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 5bc991fc0f67d..2ba6a711767b3 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -5,7 +5,7 @@ #define ehframe_x23 23 #define ehframe_pc 32 - .section __TEXT,__text,regular,pure_instructions + .text //-------------------------------------- // to_be_interrupted() a frameless function that does a non-ABI @@ -14,7 +14,6 @@ // trap() knows to branch back to $x23 when it has finished. //-------------------------------------- .globl _to_be_interrupted - .p2align 2 _to_be_interrupted: .cfi_startproc @@ -45,7 +44,6 @@ L_.return: // break_to_debugger(). //-------------------------------------- .globl _trap - .p2align 2 _trap: .cfi_startproc .cfi_signal_frame @@ -90,7 +88,6 @@ _trap: // break_to_debugger() executes a BRK instruction //-------------------------------------- .globl _break_to_debugger - .p2align 2 _break_to_debugger: .cfi_startproc >From 80248f5a5841645a1da0026799c040d4db7ab6f3 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 18:44:27 -0700 Subject: [PATCH 11/16] Adapt test case to build & run on linux or Darwin. There's a slight syntax diff for the ADRP + ADD pair and the label name. And the function names in Darwin are prepended with an _ and not on linux. clang on darwin runs the .s file through the preprocessor, but it doesn't seem to on linux. I renamed interrupt-and-trap-funcs.s to interrupt-and-trap-funcs.c and run it through the preprocessor then assemble it in Makefile now. The linux version would probably run on other AArch64 systems like FreeBSD but I haven't checked those. --- .../API/macosx/unwind-frameless-faulted/Makefile | 5 +++-- .../TestUnwindFramelessFaulted.py | 5 ++++- ...d-trap-funcs.s => interrupt-and-trap-funcs.c} | 16 ++++++++++++---- .../API/macosx/unwind-frameless-faulted/main.c | 10 ++++++++++ 4 files changed, 29 insertions(+), 7 deletions(-) rename lldb/test/API/macosx/unwind-frameless-faulted/{interrupt-and-trap-funcs.s => interrupt-and-trap-funcs.c} (88%) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile index 76548928a3622..fca47ae47491c 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile +++ b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile @@ -1,7 +1,8 @@ C_SOURCES := main.c -interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.s - $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o $(SRCDIR)/interrupt-and-trap-funcs.s +interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.c + $(CC) $(CFLAGS) -E -o interrupt-and-trap-funcs.s $(SRCDIR)/interrupt-and-trap-funcs.c + $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o interrupt-and-trap-funcs.s include Makefile.rules diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py index d899b7a2f7369..fe4b9b52abebb 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py +++ b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py @@ -10,7 +10,10 @@ class TestUnwindFramelessFaulted(TestBase): NO_DEBUG_INFO_TESTCASE = True - @skipUnlessDarwin + @skipIf( + oslist=no_match([lldbplatformutil.getDarwinOSTriples(), "linux"]), + archs=no_match(["aarch64", "arm64", "arm64e"]), + ) def test_frameless_faulted_unwind(self): self.build() diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c similarity index 88% rename from lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s rename to lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c index 2ba6a711767b3..23c6cfd688969 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c @@ -6,7 +6,6 @@ #define ehframe_pc 32 .text - //-------------------------------------- // to_be_interrupted() a frameless function that does a non-ABI // function call to trap(), simulating an async signal/interrup/exception/fault. @@ -25,12 +24,21 @@ mov x24, x0 add x24, x24, #1 - adrp x23, L_.return at PAGE ; put return address in x4 +#if defined(__APPLE__) + adrp x23, L_.return at PAGE // put return address in x4 add x23, x23, L_.return at PAGEOFF +#else + adrp x23, .L.return + add x23, x23, :lo12:.L.return +#endif - b _trap ; branch to trap handler, fake async interrupt + b _trap // branch to trap handler, fake async interrupt +#if defined(__APPLE__) L_.return: +#else +.L.return: +#endif mov x0, x24 ret .cfi_endproc @@ -95,7 +103,7 @@ L_.return: // retrieve the value if it wants. .cfi_same_value ehframe_x0 - brk #0xf000 ;; __builtin_debugtrap aarch64 instruction + brk #0xf000 // __builtin_debugtrap aarch64 instruction ret .cfi_endproc diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c b/lldb/test/API/macosx/unwind-frameless-faulted/main.c index e121809f25478..27a2f228b5feb 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/main.c +++ b/lldb/test/API/macosx/unwind-frameless-faulted/main.c @@ -1,6 +1,16 @@ +#if defined(__APPLE__) int to_be_interrupted(int); +#else +int _to_be_interrupted(int); +#endif + int main() { int c = 10; +#if defined(__APPLE__) c = to_be_interrupted(c); +#else + c = _to_be_interrupted(c); + #endif + return c; } >From 98be98ce9eb68dd89179ed1dc1905db9e5fd9cbf Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 18:48:00 -0700 Subject: [PATCH 12/16] Move this test from macosx/ to functionalities/unwind because it can run on darwin or linux aarch64. --- .../unwind/frameless-faulted}/Makefile | 0 .../unwind/frameless-faulted}/TestUnwindFramelessFaulted.py | 0 .../unwind/frameless-faulted}/interrupt-and-trap-funcs.c | 0 .../unwind/frameless-faulted}/main.c | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename lldb/test/API/{macosx/unwind-frameless-faulted => functionalities/unwind/frameless-faulted}/Makefile (100%) rename lldb/test/API/{macosx/unwind-frameless-faulted => functionalities/unwind/frameless-faulted}/TestUnwindFramelessFaulted.py (100%) rename lldb/test/API/{macosx/unwind-frameless-faulted => functionalities/unwind/frameless-faulted}/interrupt-and-trap-funcs.c (100%) rename lldb/test/API/{macosx/unwind-frameless-faulted => functionalities/unwind/frameless-faulted}/main.c (100%) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile b/lldb/test/API/functionalities/unwind/frameless-faulted/Makefile similarity index 100% rename from lldb/test/API/macosx/unwind-frameless-faulted/Makefile rename to lldb/test/API/functionalities/unwind/frameless-faulted/Makefile diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py similarity index 100% rename from lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py rename to lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c similarity index 100% rename from lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c rename to lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c b/lldb/test/API/functionalities/unwind/frameless-faulted/main.c similarity index 100% rename from lldb/test/API/macosx/unwind-frameless-faulted/main.c rename to lldb/test/API/functionalities/unwind/frameless-faulted/main.c >From 2e1250cf6bdaed75a3829a8cf465b2fb798505d3 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 18:51:15 -0700 Subject: [PATCH 13/16] typeo --- lldb/test/API/functionalities/unwind/frameless-faulted/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/main.c b/lldb/test/API/functionalities/unwind/frameless-faulted/main.c index 27a2f228b5feb..1a016ea61affb 100644 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/main.c +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/main.c @@ -10,7 +10,7 @@ int main() { c = to_be_interrupted(c); #else c = _to_be_interrupted(c); - #endif +#endif return c; } >From 2d30011693cf3c3aa1ebdfde57a2c43687b37757 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 19:05:15 -0700 Subject: [PATCH 14/16] Disabling clang-format of the assembly file called ".c" Add two more checks for non-ABI compliant register availability from the custom UnwindPlans. --- .../TestUnwindFramelessFaulted.py | 37 ++++++++++++++----- .../interrupt-and-trap-funcs.c | 7 ++++ 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py index fe4b9b52abebb..dfb42c56164ac 100644 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py @@ -55,31 +55,50 @@ def test_frameless_faulted_unwind(self): self.assertEqual(thread.GetFrameAtIndex(frame_idx).name, expected_frame) frame_idx = frame_idx + 1 - # When we're at our deepest level, test that register passing of x0 and x20 - # follow the by-hand UnwindPlan rules. In this test program, we can get x0 - # in the middle of the stack and we CAN'T get x20. The opposites of the normal - # AArch64 SysV ABI. + # When we're at our deepest level, test that register passing of + # x0 and x20 follow the by-hand UnwindPlan rules. + # In this test program, we can get x0 in the middle of the stack + # and we CAN'T get x20. The opposites of the normal AArch64 SysV + # ABI. if frame.name == "break_to_debugger": tbi_frame = thread.GetFrameAtIndex(2) self.assertEqual(tbi_frame.name, "to_be_interrupted") - # The original argument to to_be_interrupted(), 10 - # Normally can't get x0 mid-stack, but UnwindPlans have special rules to - # make this possible. + # Normally can't get x0 mid-stack, but UnwindPlans have + # special rules to make this possible. x0_reg = tbi_frame.register["x0"] self.assertTrue(x0_reg.IsValid()) self.assertEqual(x0_reg.GetValueAsUnsigned(), 10) - # The incremented return value from to_be_interrupted(), 11 x24_reg = tbi_frame.register["x24"] self.assertTrue(x24_reg.IsValid()) self.assertEqual(x24_reg.GetValueAsUnsigned(), 11) - # x20 can normally be fetched mid-stack, but the UnwindPlan # has a rule saying it can't be fetched. x20_reg = tbi_frame.register["x20"] self.assertTrue(x20_reg.error.fail) + trap_frame = thread.GetFrameAtIndex(1) + self.assertEqual(trap_frame.name, "trap") + # Confirm that we can fetch x0 from trap() which + # is normally not possible w/ SysV AbI, but special + # UnwindPlans in use. + x0_reg = trap_frame.register["x0"] + self.assertTrue(x0_reg.IsValid()) + self.assertEqual(x0_reg.GetValueAsUnsigned(), 10) + x1_reg = trap_frame.register["x1"] + self.assertTrue(x1_reg.error.fail) + + main_frame = thread.GetFrameAtIndex(3) + self.assertEqual(main_frame.name, "main") + # x20 can normally be fetched mid-stack, but the UnwindPlan + # has a rule saying it can't be fetched. + x20_reg = main_frame.register["x20"] + self.assertTrue(x20_reg.error.fail) + # x21 can be fetched mid-stack. + x21_reg = main_frame.register["x21"] + self.assertTrue(x21_reg.error.success) + if self.TraceOn(): print("StepInstruction") thread.StepInstruction(False) diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c index 23c6cfd688969..d32f01a2ea429 100644 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c @@ -1,3 +1,10 @@ +// This is assembly code that needs to be run +// through the preprocessor, for simplicity of +// preprocessing it's named .c to start with. +// +// clang-format off + + #define DW_CFA_register 0x9 #define ehframe_x0 0 #define ehframe_x20 20 >From a555af1ba3c17076708aea0548997fd3b579e2a7 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 19:20:02 -0700 Subject: [PATCH 15/16] Advance past the BRK instruction in break_to_debugger() for lldb-server where it does not advance past this BRK instruction automatically. Also add a limit on instruction stepping, so if this does get stuck in a loop it won't run forever. I haven't tested this on a linux system yet but in by-hand running on the binary, I saw the lldb-server behavior w.r.t builtin_debugtrap and this should be necessary. --- .../TestUnwindFramelessFaulted.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py index dfb42c56164ac..a03d994045773 100644 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py @@ -38,7 +38,13 @@ def test_frameless_faulted_unwind(self): # Instruction step through the binary until we are in a function not # listed in correct_frames. frame = thread.GetFrameAtIndex(0) - while process.GetState() == lldb.eStateStopped and frame.name in correct_frames: + step_count = 0 + max_step_count = 200 + while ( + process.GetState() == lldb.eStateStopped + and frame.name in correct_frames + and step_count < max_step_count + ): starting_index = 0 if self.TraceOn(): self.runCmd("bt") @@ -99,7 +105,18 @@ def test_frameless_faulted_unwind(self): x21_reg = main_frame.register["x21"] self.assertTrue(x21_reg.error.success) + # manually move past the BRK instruction in + # break_to_debugger(). lldb-server doesn't + # advance past the builtin_debugtrap() BRK + # instruction. + if ( + thread.GetStopReason() == lldb.eStopReasonException + and frame.name == "break_to_debugger" + ): + frame.SetPC(frame.GetPC() + 4) + if self.TraceOn(): print("StepInstruction") thread.StepInstruction(False) frame = thread.GetFrameAtIndex(0) + step_count = step_count + 1 >From da712493ad7917218603c0f622d4390bcf6810ed Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 19:23:15 -0700 Subject: [PATCH 16/16] typeo --- .../unwind/frameless-faulted/interrupt-and-trap-funcs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c index d32f01a2ea429..7525200595f77 100644 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c @@ -32,7 +32,7 @@ add x24, x24, #1 #if defined(__APPLE__) - adrp x23, L_.return at PAGE // put return address in x4 + adrp x23, L_.return at PAGE // put return address in x23 add x23, x23, L_.return at PAGEOFF #else adrp x23, .L.return From lldb-commits at lists.llvm.org Fri May 9 19:29:17 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Fri, 09 May 2025 19:29:17 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681eb9fd.170a0220.15b25e.e9ce@mx.google.com> https://github.com/jasonmolenda updated https://github.com/llvm/llvm-project/pull/138805 >From 4fc9acd03a58a3617b113352c48e5dd2fdb58eda Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Tue, 6 May 2025 22:37:17 -0700 Subject: [PATCH 01/17] [lldb] Provide lr value in faulting frame on arm64 When a frameless function faults or is interrupted asynchronously, the UnwindPlan MAY have no register location rule for the return address register (lr on arm64); the value is simply live in the lr register when it was interrupted, and the frame below this on the stack -- e.g. sigtramp on a Unix system -- has the full register context, including that register. RegisterContextUnwind::SavedLocationForRegister, when asked to find the caller's pc value, will first see if there is a pc register location. If there isn't, on a Return Address Register architecture like arm/mips/riscv, we rewrite the register request from "pc" to "RA register", and search for a location. On frame 0 (the live frame) and an interrupted frame, the UnwindPlan may have no register location rule for the RA Reg, that is valid. A frameless function that never calls another may simply keep the return address in the live register the whole way. Our instruction emulation unwind plans explicitly add a rule (see Pavel's May 2024 change https://github.com/llvm/llvm-project/pull/91321 ), but an UnwindPlan sourced from debug_frame may not. I've got a case where this exactly happens - clang debug_frame for arm64 where there is no register location for the lr in a frameless function. There is a fault in the middle of this frameless function and we only get the lr value from the fault handler below this frame if lr has a register location of `IsSame`, in line with Pavel's 2024 change. Similar to how we see a request of the RA Reg from frame 0 after failing to find an unwind location for the pc register, the same style of special casing is needed when this is a function that was interrupted. Without this change, we can find the pc of the frame that was executing when it was interrupted, but we need $lr to find its caller, and we don't descend down to the trap handler to get that value, truncating the stack. rdar://145614545 --- lldb/source/Target/RegisterContextUnwind.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 3ed49e12476dd..23a86bee2518b 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -1377,6 +1377,7 @@ RegisterContextUnwind::SavedLocationForRegister( } } + // Check if the active_row has a register location listed. if (regnum.IsValid() && active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind), unwindplan_regloc)) { @@ -1390,11 +1391,10 @@ RegisterContextUnwind::SavedLocationForRegister( // This is frame 0 and we're retrieving the PC and it's saved in a Return // Address register and it hasn't been saved anywhere yet -- that is, // it's still live in the actual register. Handle this specially. - if (!have_unwindplan_regloc && return_address_reg.IsValid() && - IsFrameZero()) { - if (return_address_reg.GetAsKind(eRegisterKindLLDB) != - LLDB_INVALID_REGNUM) { + return_address_reg.GetAsKind(eRegisterKindLLDB) != + LLDB_INVALID_REGNUM) { + if (IsFrameZero()) { lldb_private::UnwindLLDB::ConcreteRegisterLocation new_regloc; new_regloc.type = UnwindLLDB::ConcreteRegisterLocation:: eRegisterInLiveRegisterContext; @@ -1408,6 +1408,17 @@ RegisterContextUnwind::SavedLocationForRegister( return_address_reg.GetAsKind(eRegisterKindLLDB), return_address_reg.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } else if (BehavesLikeZerothFrame()) { + // This function was interrupted asynchronously -- it faulted, + // an async interrupt, a timer fired, a debugger expression etc. + // The caller's pc is in the Return Address register, but the + // UnwindPlan for this function may have no location rule for + // the RA reg. + // This means that the caller's return address is in the RA reg + // when the function was interrupted--descend down one stack frame + // to retrieve it from the trap handler's saved context. + unwindplan_regloc.SetSame(); + have_unwindplan_regloc = true; } } >From b10162deb49ecddca6439665c2b8ea1995fdd81f Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:24:17 -0700 Subject: [PATCH 02/17] Add the sources for an API test case to be written --- .../interrupt-and-trap-funcs.s | 92 +++++++++++++++++++ .../macosx/unwind-frameless-faulted/main.c | 6 ++ 2 files changed, 98 insertions(+) create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/main.c diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s new file mode 100644 index 0000000000000..23eb35c2b31ca --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -0,0 +1,92 @@ +#define DW_CFA_register 0x9 +#define ehframe_x22 22 +#define ehframe_x23 23 +#define ehframe_pc 32 + + .section __TEXT,__text,regular,pure_instructions + +//-------------------------------------- +// to_be_interrupted() a frameless function that does a non-ABI +// function call ("is interrupted/traps" simulated) to trap(). +// Before it branches to trap(), it puts its return address in +// x23. trap() knows to branch back to $x23 when it has finished. +//-------------------------------------- + .globl _to_be_interrupted + .p2align 2 +_to_be_interrupted: + .cfi_startproc + + // This is a garbage entry to ensure that eh_frame is emitted, + // it isn't used for anything. If there's no eh_frame, lldb + // can do an assembly emulation scan and add a rule for $lr + // which won't expose the issue at hand. + .cfi_escape DW_CFA_register, ehframe_x22, ehframe_x23 + mov x24, x0 + add x24, x24, #1 + + adrp x23, L_.return at PAGE ; put return address in x4 + add x23, x23, L_.return at PAGEOFF + + b _trap ; branch to trap handler, fake async interrupt + +L_.return: + mov x0, x24 + ret + .cfi_endproc + + + +//-------------------------------------- +// trap() trap handler function, sets up stack frame +// with special unwind rule for the pc value of the +// "interrupted" stack frame (it's in x23), then calls +// break_to_debugger(). +//-------------------------------------- + .globl _trap + .p2align 2 +_trap: + .cfi_startproc + .cfi_signal_frame + + // The pc value when we were interrupted is in x23 + .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 + + // standard prologue save of fp & lr so we can call puts() + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 + .cfi_def_cfa w29, 16 + .cfi_offset w30, -8 + .cfi_offset w29, -16 + + bl _break_to_debugger + + ldp x29, x30, [sp, #16] + add sp, sp, #32 + + // jump back to $x23 to resume execution of to_be_interrupted + br x23 + .cfi_endproc + +//-------------------------------------- +// break_to_debugger() executes a BRK instruction +//-------------------------------------- + .globl _break_to_debugger + .p2align 2 +_break_to_debugger: + .cfi_startproc + + // standard prologue save of fp & lr so we can call puts() + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 + .cfi_def_cfa w29, 16 + .cfi_offset w30, -8 + .cfi_offset w29, -16 + + brk #0xf000 ;; __builtin_debugtrap aarch64 instruction + + ldp x29, x30, [sp, #16] + add sp, sp, #32 + ret + .cfi_endproc diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c b/lldb/test/API/macosx/unwind-frameless-faulted/main.c new file mode 100644 index 0000000000000..e121809f25478 --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/main.c @@ -0,0 +1,6 @@ +int to_be_interrupted(int); +int main() { + int c = 10; + c = to_be_interrupted(c); + return c; +} >From 508b9cb404072263a6ed52a75b3b7aad5d949510 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:24:58 -0700 Subject: [PATCH 03/17] Add a log msg when a frame is marked as a trap handler via the UnwindPlan. And use the trap handler flag for zeroth frame so we can properly unwind a frameless function that was interrupted while in the trap handler function. --- lldb/source/Target/RegisterContextUnwind.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 51c0f56e9bc1e..cf4b96c6eda9f 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -248,6 +248,7 @@ void RegisterContextUnwind::InitializeZerothFrame() { active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); row_register_kind = m_full_unwind_plan_sp->GetRegisterKind(); + PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp); if (active_row && log) { StreamString active_row_strm; active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, @@ -1933,6 +1934,7 @@ void RegisterContextUnwind::PropagateTrapHandlerFlagFromUnwindPlan( } m_frame_type = eTrapHandlerFrame; + UnwindLogMsg("This frame is marked as a trap handler via its UnwindPlan"); if (m_current_offset_backed_up_one != m_current_offset) { // We backed up the pc by 1 to compute the symbol context, but >From d8843fe86c0eaaa1483bed0317f832b7630d8548 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 7 May 2025 23:28:40 -0700 Subject: [PATCH 04/17] Update assembly a bit. --- .../interrupt-and-trap-funcs.s | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 23eb35c2b31ca..708ad9ed56a1c 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -51,7 +51,8 @@ _trap: // The pc value when we were interrupted is in x23 .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 - // standard prologue save of fp & lr so we can call puts() + // standard prologue save of fp & lr so we can call + // break_to_debugger() sub sp, sp, #32 stp x29, x30, [sp, #16] add x29, sp, #16 @@ -76,17 +77,7 @@ _trap: _break_to_debugger: .cfi_startproc - // standard prologue save of fp & lr so we can call puts() - sub sp, sp, #32 - stp x29, x30, [sp, #16] - add x29, sp, #16 - .cfi_def_cfa w29, 16 - .cfi_offset w30, -8 - .cfi_offset w29, -16 - brk #0xf000 ;; __builtin_debugtrap aarch64 instruction - ldp x29, x30, [sp, #16] - add sp, sp, #32 ret .cfi_endproc >From 1d90aa48c92d9f8ef43c889223c0804ac1491d53 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 18:18:14 -0700 Subject: [PATCH 05/17] Add unwind rules for x0 (volatile) and x20 (non-voltile) to trap() and break_to_debugger() for fun, and add unwind instructions for the epilogue of trap(). When stopped in `break_to_debugger`, ``` * frame #0: 0x00000001000003e8 a.out`break_to_debugger + 4 frame #1: 0x00000001000003d8 a.out`trap + 16 frame #2: 0x00000001000003c0 a.out`to_be_interrupted + 20 frame #3: 0x0000000100000398 a.out`main + 32 ``` Normally we can't provide a volatile register (e.g. x0) up the stack. And we can provide a non-volatile register (e.g. x20) up the stack. I added an IsSame rule for trap() and break_to_debugger() for x0, so it CAN be passed up the stack. And I added an Undefined rule for x20 to trap() so it CAN'T be provided up the stack. If a user selects `to_be_interrupted` and does `register read`, they'll get x0 and they won't get x20. From `main`, they will not get x0 or x20 (x0 because `to_be_interrupted` doesn't have an IsSame rule). --- .../interrupt-and-trap-funcs.s | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 708ad9ed56a1c..b50c4734f18f3 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -1,4 +1,6 @@ #define DW_CFA_register 0x9 +#define ehframe_x0 0 +#define ehframe_x20 20 #define ehframe_x22 22 #define ehframe_x23 23 #define ehframe_pc 32 @@ -7,9 +9,9 @@ //-------------------------------------- // to_be_interrupted() a frameless function that does a non-ABI -// function call ("is interrupted/traps" simulated) to trap(). -// Before it branches to trap(), it puts its return address in -// x23. trap() knows to branch back to $x23 when it has finished. +// function call to trap(), simulating an async signal/interrup/exception/fault. +// Before it branches to trap(), put the return address in x23. +// trap() knows to branch back to $x23 when it has finished. //-------------------------------------- .globl _to_be_interrupted .p2align 2 @@ -51,6 +53,15 @@ _trap: // The pc value when we were interrupted is in x23 .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 + // For fun, mark x0 as unmodified so the caller can + // retrieve the value if it wants. + .cfi_same_value ehframe_x0 + + // Mark x20 as undefined. This is a callee-preserved + // (non-volatile) register by the SysV AArch64 ABI, but + // it's be fun to see lldb not passing a value past this. + .cfi_undefined ehframe_x20 + // standard prologue save of fp & lr so we can call // break_to_debugger() sub sp, sp, #32 @@ -63,7 +74,11 @@ _trap: bl _break_to_debugger ldp x29, x30, [sp, #16] + .cfi_same_value x29 + .cfi_same_value x30 + .cfi_def_cfa sp, 32 add sp, sp, #32 + .cfi_same_value sp // jump back to $x23 to resume execution of to_be_interrupted br x23 @@ -77,6 +92,10 @@ _trap: _break_to_debugger: .cfi_startproc + // For fun, mark x0 as unmodified so the caller can + // retrieve the value if it wants. + .cfi_same_value ehframe_x0 + brk #0xf000 ;; __builtin_debugtrap aarch64 instruction ret >From 303edd47f7bcfb6af0a9947ca7b0b9a0c5bec589 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 18:46:14 -0700 Subject: [PATCH 06/17] small comment improvement --- .../unwind-frameless-faulted/interrupt-and-trap-funcs.s | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index b50c4734f18f3..38be06a681bf2 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -18,10 +18,10 @@ _to_be_interrupted: .cfi_startproc - // This is a garbage entry to ensure that eh_frame is emitted, - // it isn't used for anything. If there's no eh_frame, lldb - // can do an assembly emulation scan and add a rule for $lr - // which won't expose the issue at hand. + // This is a garbage entry to ensure that eh_frame is emitted. + // If there's no eh_frame, lldb can use the assembly emulation scan, + // which always includes a rule for $lr, and we won't replicate the + // bug we're testing for. .cfi_escape DW_CFA_register, ehframe_x22, ehframe_x23 mov x24, x0 add x24, x24, #1 >From 620eace34ae149b8c513767935118b6e870646fc Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 18:47:54 -0700 Subject: [PATCH 07/17] more comment fix --- .../macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 38be06a681bf2..b5c5cb79656c3 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -59,7 +59,8 @@ _trap: // Mark x20 as undefined. This is a callee-preserved // (non-volatile) register by the SysV AArch64 ABI, but - // it's be fun to see lldb not passing a value past this. + // it'll be fun to see lldb not passing a value past this + // point on the stack. .cfi_undefined ehframe_x20 // standard prologue save of fp & lr so we can call >From 6185c513beaa2f63dc04b54f723b209de8af2286 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 22:36:21 -0700 Subject: [PATCH 08/17] Add test case Makefile and API test python code. Also fix one bug in the by-hand unwind state in the assembly file. --- .../macosx/unwind-frameless-faulted/Makefile | 12 ++++ .../TestUnwindFramelessFaulted.py | 59 +++++++++++++++++++ .../interrupt-and-trap-funcs.s | 1 + 3 files changed, 72 insertions(+) create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/Makefile create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile new file mode 100644 index 0000000000000..76548928a3622 --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile @@ -0,0 +1,12 @@ +C_SOURCES := main.c + +interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.s + $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o $(SRCDIR)/interrupt-and-trap-funcs.s + +include Makefile.rules + +a.out: interrupt-and-trap-funcs.o + +# Needs to come after include +OBJECTS += interrupt-and-trap-funcs.o + diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py new file mode 100644 index 0000000000000..34e7fbfffacde --- /dev/null +++ b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py @@ -0,0 +1,59 @@ +"""Test that lldb backtraces a frameless function that faults correctly.""" + +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +import shutil +import os + + +class TestUnwindFramelessFaulted(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + @skipUnlessDarwin + def test_frameless_faulted_unwind(self): + self.build() + + (target, process, thread, bp) = lldbutil.run_to_name_breakpoint( + self, "main", only_one_thread=False + ) + + # The test program will have a backtrace like this at its deepest: + # + # * frame #0: 0x0000000102adc468 a.out`break_to_debugger + 4 + # frame #1: 0x0000000102adc458 a.out`trap + 16 + # frame #2: 0x0000000102adc440 a.out`to_be_interrupted + 20 + # frame #3: 0x0000000102adc418 a.out`main at main.c:4:7 + # frame #4: 0x0000000193b7eb4c dyld`start + 6000 + + correct_frames = ["break_to_debugger", "trap", "to_be_interrupted", "main"] + + # Keep track of when main has branch & linked, instruction step until we're + # back in main() + main_has_bl_ed = False + + # Instruction step through the binary until we are in a function not + # listed in correct_frames. + while ( + process.GetState() == lldb.eStateStopped + and thread.GetFrameAtIndex(0).name in correct_frames + ): + starting_index = 0 + if self.TraceOn(): + self.runCmd("bt") + + # Find which index into correct_frames the current stack frame is + for idx, name in enumerate(correct_frames): + if thread.GetFrameAtIndex(0).name == name: + starting_index = idx + + # Test that all frames after the current frame listed in + # correct_frames appears in the backtrace. + frame_idx = 0 + for expected_frame in correct_frames[starting_index:]: + self.assertEqual(thread.GetFrameAtIndex(frame_idx).name, expected_frame) + frame_idx = frame_idx + 1 + + if self.TraceOn(): + print("StepInstruction") + thread.StepInstruction(False) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index b5c5cb79656c3..5bc991fc0f67d 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -80,6 +80,7 @@ _trap: .cfi_def_cfa sp, 32 add sp, sp, #32 .cfi_same_value sp + .cfi_def_cfa sp, 0 // jump back to $x23 to resume execution of to_be_interrupted br x23 >From d239b7a84c1e489b2630aa63358061435a749ce7 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 22:49:20 -0700 Subject: [PATCH 09/17] Add tests for x0 and x20, which I treat in normally ABI-wrong ways in these hand-written UnwindPlans. --- .../TestUnwindFramelessFaulted.py | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py index 34e7fbfffacde..d899b7a2f7369 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py +++ b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py @@ -34,17 +34,15 @@ def test_frameless_faulted_unwind(self): # Instruction step through the binary until we are in a function not # listed in correct_frames. - while ( - process.GetState() == lldb.eStateStopped - and thread.GetFrameAtIndex(0).name in correct_frames - ): + frame = thread.GetFrameAtIndex(0) + while process.GetState() == lldb.eStateStopped and frame.name in correct_frames: starting_index = 0 if self.TraceOn(): self.runCmd("bt") # Find which index into correct_frames the current stack frame is for idx, name in enumerate(correct_frames): - if thread.GetFrameAtIndex(0).name == name: + if frame.name == name: starting_index = idx # Test that all frames after the current frame listed in @@ -54,6 +52,32 @@ def test_frameless_faulted_unwind(self): self.assertEqual(thread.GetFrameAtIndex(frame_idx).name, expected_frame) frame_idx = frame_idx + 1 + # When we're at our deepest level, test that register passing of x0 and x20 + # follow the by-hand UnwindPlan rules. In this test program, we can get x0 + # in the middle of the stack and we CAN'T get x20. The opposites of the normal + # AArch64 SysV ABI. + if frame.name == "break_to_debugger": + tbi_frame = thread.GetFrameAtIndex(2) + self.assertEqual(tbi_frame.name, "to_be_interrupted") + + # The original argument to to_be_interrupted(), 10 + # Normally can't get x0 mid-stack, but UnwindPlans have special rules to + # make this possible. + x0_reg = tbi_frame.register["x0"] + self.assertTrue(x0_reg.IsValid()) + self.assertEqual(x0_reg.GetValueAsUnsigned(), 10) + + # The incremented return value from to_be_interrupted(), 11 + x24_reg = tbi_frame.register["x24"] + self.assertTrue(x24_reg.IsValid()) + self.assertEqual(x24_reg.GetValueAsUnsigned(), 11) + + # x20 can normally be fetched mid-stack, but the UnwindPlan + # has a rule saying it can't be fetched. + x20_reg = tbi_frame.register["x20"] + self.assertTrue(x20_reg.error.fail) + if self.TraceOn(): print("StepInstruction") thread.StepInstruction(False) + frame = thread.GetFrameAtIndex(0) >From 33563049b37479db9ebebdad0a1c982deb4c6f14 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 8 May 2025 22:52:04 -0700 Subject: [PATCH 10/17] Remove some Mach-O/Darwinisms, get closer to this compiling on linux? I think maybe the only difference is that the symbol names are prefixed with _ on darwin? --- .../unwind-frameless-faulted/interrupt-and-trap-funcs.s | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s index 5bc991fc0f67d..2ba6a711767b3 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s @@ -5,7 +5,7 @@ #define ehframe_x23 23 #define ehframe_pc 32 - .section __TEXT,__text,regular,pure_instructions + .text //-------------------------------------- // to_be_interrupted() a frameless function that does a non-ABI @@ -14,7 +14,6 @@ // trap() knows to branch back to $x23 when it has finished. //-------------------------------------- .globl _to_be_interrupted - .p2align 2 _to_be_interrupted: .cfi_startproc @@ -45,7 +44,6 @@ L_.return: // break_to_debugger(). //-------------------------------------- .globl _trap - .p2align 2 _trap: .cfi_startproc .cfi_signal_frame @@ -90,7 +88,6 @@ _trap: // break_to_debugger() executes a BRK instruction //-------------------------------------- .globl _break_to_debugger - .p2align 2 _break_to_debugger: .cfi_startproc >From 80248f5a5841645a1da0026799c040d4db7ab6f3 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 18:44:27 -0700 Subject: [PATCH 11/17] Adapt test case to build & run on linux or Darwin. There's a slight syntax diff for the ADRP + ADD pair and the label name. And the function names in Darwin are prepended with an _ and not on linux. clang on darwin runs the .s file through the preprocessor, but it doesn't seem to on linux. I renamed interrupt-and-trap-funcs.s to interrupt-and-trap-funcs.c and run it through the preprocessor then assemble it in Makefile now. The linux version would probably run on other AArch64 systems like FreeBSD but I haven't checked those. --- .../API/macosx/unwind-frameless-faulted/Makefile | 5 +++-- .../TestUnwindFramelessFaulted.py | 5 ++++- ...d-trap-funcs.s => interrupt-and-trap-funcs.c} | 16 ++++++++++++---- .../API/macosx/unwind-frameless-faulted/main.c | 10 ++++++++++ 4 files changed, 29 insertions(+), 7 deletions(-) rename lldb/test/API/macosx/unwind-frameless-faulted/{interrupt-and-trap-funcs.s => interrupt-and-trap-funcs.c} (88%) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile index 76548928a3622..fca47ae47491c 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile +++ b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile @@ -1,7 +1,8 @@ C_SOURCES := main.c -interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.s - $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o $(SRCDIR)/interrupt-and-trap-funcs.s +interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.c + $(CC) $(CFLAGS) -E -o interrupt-and-trap-funcs.s $(SRCDIR)/interrupt-and-trap-funcs.c + $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o interrupt-and-trap-funcs.s include Makefile.rules diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py index d899b7a2f7369..fe4b9b52abebb 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py +++ b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py @@ -10,7 +10,10 @@ class TestUnwindFramelessFaulted(TestBase): NO_DEBUG_INFO_TESTCASE = True - @skipUnlessDarwin + @skipIf( + oslist=no_match([lldbplatformutil.getDarwinOSTriples(), "linux"]), + archs=no_match(["aarch64", "arm64", "arm64e"]), + ) def test_frameless_faulted_unwind(self): self.build() diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c similarity index 88% rename from lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s rename to lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c index 2ba6a711767b3..23c6cfd688969 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s +++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c @@ -6,7 +6,6 @@ #define ehframe_pc 32 .text - //-------------------------------------- // to_be_interrupted() a frameless function that does a non-ABI // function call to trap(), simulating an async signal/interrup/exception/fault. @@ -25,12 +24,21 @@ mov x24, x0 add x24, x24, #1 - adrp x23, L_.return at PAGE ; put return address in x4 +#if defined(__APPLE__) + adrp x23, L_.return at PAGE // put return address in x4 add x23, x23, L_.return at PAGEOFF +#else + adrp x23, .L.return + add x23, x23, :lo12:.L.return +#endif - b _trap ; branch to trap handler, fake async interrupt + b _trap // branch to trap handler, fake async interrupt +#if defined(__APPLE__) L_.return: +#else +.L.return: +#endif mov x0, x24 ret .cfi_endproc @@ -95,7 +103,7 @@ L_.return: // retrieve the value if it wants. .cfi_same_value ehframe_x0 - brk #0xf000 ;; __builtin_debugtrap aarch64 instruction + brk #0xf000 // __builtin_debugtrap aarch64 instruction ret .cfi_endproc diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c b/lldb/test/API/macosx/unwind-frameless-faulted/main.c index e121809f25478..27a2f228b5feb 100644 --- a/lldb/test/API/macosx/unwind-frameless-faulted/main.c +++ b/lldb/test/API/macosx/unwind-frameless-faulted/main.c @@ -1,6 +1,16 @@ +#if defined(__APPLE__) int to_be_interrupted(int); +#else +int _to_be_interrupted(int); +#endif + int main() { int c = 10; +#if defined(__APPLE__) c = to_be_interrupted(c); +#else + c = _to_be_interrupted(c); + #endif + return c; } >From 98be98ce9eb68dd89179ed1dc1905db9e5fd9cbf Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 18:48:00 -0700 Subject: [PATCH 12/17] Move this test from macosx/ to functionalities/unwind because it can run on darwin or linux aarch64. --- .../unwind/frameless-faulted}/Makefile | 0 .../unwind/frameless-faulted}/TestUnwindFramelessFaulted.py | 0 .../unwind/frameless-faulted}/interrupt-and-trap-funcs.c | 0 .../unwind/frameless-faulted}/main.c | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename lldb/test/API/{macosx/unwind-frameless-faulted => functionalities/unwind/frameless-faulted}/Makefile (100%) rename lldb/test/API/{macosx/unwind-frameless-faulted => functionalities/unwind/frameless-faulted}/TestUnwindFramelessFaulted.py (100%) rename lldb/test/API/{macosx/unwind-frameless-faulted => functionalities/unwind/frameless-faulted}/interrupt-and-trap-funcs.c (100%) rename lldb/test/API/{macosx/unwind-frameless-faulted => functionalities/unwind/frameless-faulted}/main.c (100%) diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile b/lldb/test/API/functionalities/unwind/frameless-faulted/Makefile similarity index 100% rename from lldb/test/API/macosx/unwind-frameless-faulted/Makefile rename to lldb/test/API/functionalities/unwind/frameless-faulted/Makefile diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py similarity index 100% rename from lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py rename to lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c similarity index 100% rename from lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c rename to lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c b/lldb/test/API/functionalities/unwind/frameless-faulted/main.c similarity index 100% rename from lldb/test/API/macosx/unwind-frameless-faulted/main.c rename to lldb/test/API/functionalities/unwind/frameless-faulted/main.c >From 2e1250cf6bdaed75a3829a8cf465b2fb798505d3 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 18:51:15 -0700 Subject: [PATCH 13/17] typeo --- lldb/test/API/functionalities/unwind/frameless-faulted/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/main.c b/lldb/test/API/functionalities/unwind/frameless-faulted/main.c index 27a2f228b5feb..1a016ea61affb 100644 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/main.c +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/main.c @@ -10,7 +10,7 @@ int main() { c = to_be_interrupted(c); #else c = _to_be_interrupted(c); - #endif +#endif return c; } >From 2d30011693cf3c3aa1ebdfde57a2c43687b37757 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 19:05:15 -0700 Subject: [PATCH 14/17] Disabling clang-format of the assembly file called ".c" Add two more checks for non-ABI compliant register availability from the custom UnwindPlans. --- .../TestUnwindFramelessFaulted.py | 37 ++++++++++++++----- .../interrupt-and-trap-funcs.c | 7 ++++ 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py index fe4b9b52abebb..dfb42c56164ac 100644 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py @@ -55,31 +55,50 @@ def test_frameless_faulted_unwind(self): self.assertEqual(thread.GetFrameAtIndex(frame_idx).name, expected_frame) frame_idx = frame_idx + 1 - # When we're at our deepest level, test that register passing of x0 and x20 - # follow the by-hand UnwindPlan rules. In this test program, we can get x0 - # in the middle of the stack and we CAN'T get x20. The opposites of the normal - # AArch64 SysV ABI. + # When we're at our deepest level, test that register passing of + # x0 and x20 follow the by-hand UnwindPlan rules. + # In this test program, we can get x0 in the middle of the stack + # and we CAN'T get x20. The opposites of the normal AArch64 SysV + # ABI. if frame.name == "break_to_debugger": tbi_frame = thread.GetFrameAtIndex(2) self.assertEqual(tbi_frame.name, "to_be_interrupted") - # The original argument to to_be_interrupted(), 10 - # Normally can't get x0 mid-stack, but UnwindPlans have special rules to - # make this possible. + # Normally can't get x0 mid-stack, but UnwindPlans have + # special rules to make this possible. x0_reg = tbi_frame.register["x0"] self.assertTrue(x0_reg.IsValid()) self.assertEqual(x0_reg.GetValueAsUnsigned(), 10) - # The incremented return value from to_be_interrupted(), 11 x24_reg = tbi_frame.register["x24"] self.assertTrue(x24_reg.IsValid()) self.assertEqual(x24_reg.GetValueAsUnsigned(), 11) - # x20 can normally be fetched mid-stack, but the UnwindPlan # has a rule saying it can't be fetched. x20_reg = tbi_frame.register["x20"] self.assertTrue(x20_reg.error.fail) + trap_frame = thread.GetFrameAtIndex(1) + self.assertEqual(trap_frame.name, "trap") + # Confirm that we can fetch x0 from trap() which + # is normally not possible w/ SysV AbI, but special + # UnwindPlans in use. + x0_reg = trap_frame.register["x0"] + self.assertTrue(x0_reg.IsValid()) + self.assertEqual(x0_reg.GetValueAsUnsigned(), 10) + x1_reg = trap_frame.register["x1"] + self.assertTrue(x1_reg.error.fail) + + main_frame = thread.GetFrameAtIndex(3) + self.assertEqual(main_frame.name, "main") + # x20 can normally be fetched mid-stack, but the UnwindPlan + # has a rule saying it can't be fetched. + x20_reg = main_frame.register["x20"] + self.assertTrue(x20_reg.error.fail) + # x21 can be fetched mid-stack. + x21_reg = main_frame.register["x21"] + self.assertTrue(x21_reg.error.success) + if self.TraceOn(): print("StepInstruction") thread.StepInstruction(False) diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c index 23c6cfd688969..d32f01a2ea429 100644 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c @@ -1,3 +1,10 @@ +// This is assembly code that needs to be run +// through the preprocessor, for simplicity of +// preprocessing it's named .c to start with. +// +// clang-format off + + #define DW_CFA_register 0x9 #define ehframe_x0 0 #define ehframe_x20 20 >From a555af1ba3c17076708aea0548997fd3b579e2a7 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 19:20:02 -0700 Subject: [PATCH 15/17] Advance past the BRK instruction in break_to_debugger() for lldb-server where it does not advance past this BRK instruction automatically. Also add a limit on instruction stepping, so if this does get stuck in a loop it won't run forever. I haven't tested this on a linux system yet but in by-hand running on the binary, I saw the lldb-server behavior w.r.t builtin_debugtrap and this should be necessary. --- .../TestUnwindFramelessFaulted.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py index dfb42c56164ac..a03d994045773 100644 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py @@ -38,7 +38,13 @@ def test_frameless_faulted_unwind(self): # Instruction step through the binary until we are in a function not # listed in correct_frames. frame = thread.GetFrameAtIndex(0) - while process.GetState() == lldb.eStateStopped and frame.name in correct_frames: + step_count = 0 + max_step_count = 200 + while ( + process.GetState() == lldb.eStateStopped + and frame.name in correct_frames + and step_count < max_step_count + ): starting_index = 0 if self.TraceOn(): self.runCmd("bt") @@ -99,7 +105,18 @@ def test_frameless_faulted_unwind(self): x21_reg = main_frame.register["x21"] self.assertTrue(x21_reg.error.success) + # manually move past the BRK instruction in + # break_to_debugger(). lldb-server doesn't + # advance past the builtin_debugtrap() BRK + # instruction. + if ( + thread.GetStopReason() == lldb.eStopReasonException + and frame.name == "break_to_debugger" + ): + frame.SetPC(frame.GetPC() + 4) + if self.TraceOn(): print("StepInstruction") thread.StepInstruction(False) frame = thread.GetFrameAtIndex(0) + step_count = step_count + 1 >From da712493ad7917218603c0f622d4390bcf6810ed Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 19:23:15 -0700 Subject: [PATCH 16/17] typeo --- .../unwind/frameless-faulted/interrupt-and-trap-funcs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c index d32f01a2ea429..7525200595f77 100644 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c @@ -32,7 +32,7 @@ add x24, x24, #1 #if defined(__APPLE__) - adrp x23, L_.return at PAGE // put return address in x4 + adrp x23, L_.return at PAGE // put return address in x23 add x23, x23, L_.return at PAGEOFF #else adrp x23, .L.return >From a1b574a0654b1a80d78b336cf397c11016ec110a Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Fri, 9 May 2025 19:28:20 -0700 Subject: [PATCH 17/17] Shift the underscore-versus-not assembly naming convention into the .s file -- the Test python file also hardcodes the names of the functions and I don't want to prepend _ on linux to those names. --- .../interrupt-and-trap-funcs.c | 26 +++++++++++++------ .../unwind/frameless-faulted/main.c | 9 ------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c index 7525200595f77..ba6537226f920 100644 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c @@ -12,6 +12,16 @@ #define ehframe_x23 23 #define ehframe_pc 32 +#if defined(__APPLE__) +#define TO_BE_INTERRUPTED _to_be_interrupted +#define TRAP _trap +#define BREAK_TO_DEBUGGER _break_to_debugger +#else +#define TO_BE_INTERRUPTED to_be_interrupted +#define TRAP trap +#define BREAK_TO_DEBUGGER break_to_debugger +#endif + .text //-------------------------------------- // to_be_interrupted() a frameless function that does a non-ABI @@ -19,8 +29,8 @@ // Before it branches to trap(), put the return address in x23. // trap() knows to branch back to $x23 when it has finished. //-------------------------------------- - .globl _to_be_interrupted -_to_be_interrupted: + .globl TO_BE_INTERRUPTED +TO_BE_INTERRUPTED: .cfi_startproc // This is a garbage entry to ensure that eh_frame is emitted. @@ -39,7 +49,7 @@ add x23, x23, :lo12:.L.return #endif - b _trap // branch to trap handler, fake async interrupt + b TRAP // branch to trap handler, fake async interrupt #if defined(__APPLE__) L_.return: @@ -58,8 +68,8 @@ L_.return: // "interrupted" stack frame (it's in x23), then calls // break_to_debugger(). //-------------------------------------- - .globl _trap -_trap: + .globl TRAP +TRAP: .cfi_startproc .cfi_signal_frame @@ -85,7 +95,7 @@ L_.return: .cfi_offset w30, -8 .cfi_offset w29, -16 - bl _break_to_debugger + bl BREAK_TO_DEBUGGER ldp x29, x30, [sp, #16] .cfi_same_value x29 @@ -102,8 +112,8 @@ L_.return: //-------------------------------------- // break_to_debugger() executes a BRK instruction //-------------------------------------- - .globl _break_to_debugger -_break_to_debugger: + .globl BREAK_TO_DEBUGGER +BREAK_TO_DEBUGGER: .cfi_startproc // For fun, mark x0 as unmodified so the caller can diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/main.c b/lldb/test/API/functionalities/unwind/frameless-faulted/main.c index 1a016ea61affb..e5f690a21a45e 100644 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/main.c +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/main.c @@ -1,16 +1,7 @@ -#if defined(__APPLE__) int to_be_interrupted(int); -#else -int _to_be_interrupted(int); -#endif int main() { int c = 10; -#if defined(__APPLE__) c = to_be_interrupted(c); -#else - c = _to_be_interrupted(c); -#endif - return c; } From lldb-commits at lists.llvm.org Fri May 9 20:07:16 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 20:07:16 -0700 (PDT) Subject: [Lldb-commits] [lldb] e897cb1 - [lldb] Provide lr value in faulting frame on arm64 (#138805) Message-ID: <681ec2e4.a70a0220.2d17f9.eed7@mx.google.com> Author: Jason Molenda Date: 2025-05-09T20:07:12-07:00 New Revision: e897cb139ee6ef5c145fed5394c4d96baa658e6b URL: https://github.com/llvm/llvm-project/commit/e897cb139ee6ef5c145fed5394c4d96baa658e6b DIFF: https://github.com/llvm/llvm-project/commit/e897cb139ee6ef5c145fed5394c4d96baa658e6b.diff LOG: [lldb] Provide lr value in faulting frame on arm64 (#138805) When a frameless function faults or is interrupted asynchronously, the UnwindPlan MAY have no register location rule for the return address register (lr on arm64); the value is simply live in the lr register when it was interrupted, and the frame below this on the stack -- e.g. sigtramp on a Unix system -- has the full register context, including that register. RegisterContextUnwind::SavedLocationForRegister, when asked to find the caller's pc value, will first see if there is a pc register location. If there isn't, on a Return Address Register architecture like arm/mips/riscv, we rewrite the register request from "pc" to "RA register", and search for a location. On frame 0 (the live frame) and an interrupted frame, the UnwindPlan may have no register location rule for the RA Reg, that is valid. A frameless function that never calls another may simply keep the return address in the live register the whole way. Our instruction emulation unwind plans explicitly add a rule (see Pavel's May 2024 change https://github.com/llvm/llvm-project/pull/91321 ), but an UnwindPlan sourced from debug_frame may not. I've got a case where this exactly happens - clang debug_frame for arm64 where there is no register location for the lr in a frameless function. There is a fault in the middle of this frameless function and we only get the lr value from the fault handler below this frame if lr has a register location of `IsSame`, in line with Pavel's 2024 change. Similar to how we see a request of the RA Reg from frame 0 after failing to find an unwind location for the pc register, the same style of special casing is needed when this is a function that was interrupted. Without this change, we can find the pc of the frame that was executing when it was interrupted, but we need $lr to find its caller, and we don't descend down to the trap handler to get that value, truncating the stack. rdar://145614545 Added: lldb/test/API/functionalities/unwind/frameless-faulted/Makefile lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c lldb/test/API/functionalities/unwind/frameless-faulted/main.c Modified: lldb/source/Target/RegisterContextUnwind.cpp Removed: ################################################################################ diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 4c760b84e45a5..cf4b96c6eda9f 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -248,6 +248,7 @@ void RegisterContextUnwind::InitializeZerothFrame() { active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); row_register_kind = m_full_unwind_plan_sp->GetRegisterKind(); + PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp); if (active_row && log) { StreamString active_row_strm; active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, @@ -1375,6 +1376,7 @@ RegisterContextUnwind::SavedLocationForRegister( } } + // Check if the active_row has a register location listed. if (regnum.IsValid() && active_row && active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind), unwindplan_regloc)) { @@ -1388,11 +1390,10 @@ RegisterContextUnwind::SavedLocationForRegister( // This is frame 0 and we're retrieving the PC and it's saved in a Return // Address register and it hasn't been saved anywhere yet -- that is, // it's still live in the actual register. Handle this specially. - if (!have_unwindplan_regloc && return_address_reg.IsValid() && - IsFrameZero()) { - if (return_address_reg.GetAsKind(eRegisterKindLLDB) != - LLDB_INVALID_REGNUM) { + return_address_reg.GetAsKind(eRegisterKindLLDB) != + LLDB_INVALID_REGNUM) { + if (IsFrameZero()) { lldb_private::UnwindLLDB::ConcreteRegisterLocation new_regloc; new_regloc.type = UnwindLLDB::ConcreteRegisterLocation:: eRegisterInLiveRegisterContext; @@ -1406,6 +1407,17 @@ RegisterContextUnwind::SavedLocationForRegister( return_address_reg.GetAsKind(eRegisterKindLLDB), return_address_reg.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } else if (BehavesLikeZerothFrame()) { + // This function was interrupted asynchronously -- it faulted, + // an async interrupt, a timer fired, a debugger expression etc. + // The caller's pc is in the Return Address register, but the + // UnwindPlan for this function may have no location rule for + // the RA reg. + // This means that the caller's return address is in the RA reg + // when the function was interrupted--descend down one stack frame + // to retrieve it from the trap handler's saved context. + unwindplan_regloc.SetSame(); + have_unwindplan_regloc = true; } } @@ -1922,6 +1934,7 @@ void RegisterContextUnwind::PropagateTrapHandlerFlagFromUnwindPlan( } m_frame_type = eTrapHandlerFrame; + UnwindLogMsg("This frame is marked as a trap handler via its UnwindPlan"); if (m_current_offset_backed_up_one != m_current_offset) { // We backed up the pc by 1 to compute the symbol context, but diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/Makefile b/lldb/test/API/functionalities/unwind/frameless-faulted/Makefile new file mode 100644 index 0000000000000..fca47ae47491c --- /dev/null +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/Makefile @@ -0,0 +1,13 @@ +C_SOURCES := main.c + +interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.c + $(CC) $(CFLAGS) -E -o interrupt-and-trap-funcs.s $(SRCDIR)/interrupt-and-trap-funcs.c + $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o interrupt-and-trap-funcs.s + +include Makefile.rules + +a.out: interrupt-and-trap-funcs.o + +# Needs to come after include +OBJECTS += interrupt-and-trap-funcs.o + diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py new file mode 100644 index 0000000000000..a03d994045773 --- /dev/null +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py @@ -0,0 +1,122 @@ +"""Test that lldb backtraces a frameless function that faults correctly.""" + +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +import shutil +import os + + +class TestUnwindFramelessFaulted(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + @skipIf( + oslist=no_match([lldbplatformutil.getDarwinOSTriples(), "linux"]), + archs=no_match(["aarch64", "arm64", "arm64e"]), + ) + def test_frameless_faulted_unwind(self): + self.build() + + (target, process, thread, bp) = lldbutil.run_to_name_breakpoint( + self, "main", only_one_thread=False + ) + + # The test program will have a backtrace like this at its deepest: + # + # * frame #0: 0x0000000102adc468 a.out`break_to_debugger + 4 + # frame #1: 0x0000000102adc458 a.out`trap + 16 + # frame #2: 0x0000000102adc440 a.out`to_be_interrupted + 20 + # frame #3: 0x0000000102adc418 a.out`main at main.c:4:7 + # frame #4: 0x0000000193b7eb4c dyld`start + 6000 + + correct_frames = ["break_to_debugger", "trap", "to_be_interrupted", "main"] + + # Keep track of when main has branch & linked, instruction step until we're + # back in main() + main_has_bl_ed = False + + # Instruction step through the binary until we are in a function not + # listed in correct_frames. + frame = thread.GetFrameAtIndex(0) + step_count = 0 + max_step_count = 200 + while ( + process.GetState() == lldb.eStateStopped + and frame.name in correct_frames + and step_count < max_step_count + ): + starting_index = 0 + if self.TraceOn(): + self.runCmd("bt") + + # Find which index into correct_frames the current stack frame is + for idx, name in enumerate(correct_frames): + if frame.name == name: + starting_index = idx + + # Test that all frames after the current frame listed in + # correct_frames appears in the backtrace. + frame_idx = 0 + for expected_frame in correct_frames[starting_index:]: + self.assertEqual(thread.GetFrameAtIndex(frame_idx).name, expected_frame) + frame_idx = frame_idx + 1 + + # When we're at our deepest level, test that register passing of + # x0 and x20 follow the by-hand UnwindPlan rules. + # In this test program, we can get x0 in the middle of the stack + # and we CAN'T get x20. The opposites of the normal AArch64 SysV + # ABI. + if frame.name == "break_to_debugger": + tbi_frame = thread.GetFrameAtIndex(2) + self.assertEqual(tbi_frame.name, "to_be_interrupted") + # The original argument to to_be_interrupted(), 10 + # Normally can't get x0 mid-stack, but UnwindPlans have + # special rules to make this possible. + x0_reg = tbi_frame.register["x0"] + self.assertTrue(x0_reg.IsValid()) + self.assertEqual(x0_reg.GetValueAsUnsigned(), 10) + # The incremented return value from to_be_interrupted(), 11 + x24_reg = tbi_frame.register["x24"] + self.assertTrue(x24_reg.IsValid()) + self.assertEqual(x24_reg.GetValueAsUnsigned(), 11) + # x20 can normally be fetched mid-stack, but the UnwindPlan + # has a rule saying it can't be fetched. + x20_reg = tbi_frame.register["x20"] + self.assertTrue(x20_reg.error.fail) + + trap_frame = thread.GetFrameAtIndex(1) + self.assertEqual(trap_frame.name, "trap") + # Confirm that we can fetch x0 from trap() which + # is normally not possible w/ SysV AbI, but special + # UnwindPlans in use. + x0_reg = trap_frame.register["x0"] + self.assertTrue(x0_reg.IsValid()) + self.assertEqual(x0_reg.GetValueAsUnsigned(), 10) + x1_reg = trap_frame.register["x1"] + self.assertTrue(x1_reg.error.fail) + + main_frame = thread.GetFrameAtIndex(3) + self.assertEqual(main_frame.name, "main") + # x20 can normally be fetched mid-stack, but the UnwindPlan + # has a rule saying it can't be fetched. + x20_reg = main_frame.register["x20"] + self.assertTrue(x20_reg.error.fail) + # x21 can be fetched mid-stack. + x21_reg = main_frame.register["x21"] + self.assertTrue(x21_reg.error.success) + + # manually move past the BRK instruction in + # break_to_debugger(). lldb-server doesn't + # advance past the builtin_debugtrap() BRK + # instruction. + if ( + thread.GetStopReason() == lldb.eStopReasonException + and frame.name == "break_to_debugger" + ): + frame.SetPC(frame.GetPC() + 4) + + if self.TraceOn(): + print("StepInstruction") + thread.StepInstruction(False) + frame = thread.GetFrameAtIndex(0) + step_count = step_count + 1 diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c new file mode 100644 index 0000000000000..ba6537226f920 --- /dev/null +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c @@ -0,0 +1,126 @@ +// This is assembly code that needs to be run +// through the preprocessor, for simplicity of +// preprocessing it's named .c to start with. +// +// clang-format off + + +#define DW_CFA_register 0x9 +#define ehframe_x0 0 +#define ehframe_x20 20 +#define ehframe_x22 22 +#define ehframe_x23 23 +#define ehframe_pc 32 + +#if defined(__APPLE__) +#define TO_BE_INTERRUPTED _to_be_interrupted +#define TRAP _trap +#define BREAK_TO_DEBUGGER _break_to_debugger +#else +#define TO_BE_INTERRUPTED to_be_interrupted +#define TRAP trap +#define BREAK_TO_DEBUGGER break_to_debugger +#endif + + .text +//-------------------------------------- +// to_be_interrupted() a frameless function that does a non-ABI +// function call to trap(), simulating an async signal/interrup/exception/fault. +// Before it branches to trap(), put the return address in x23. +// trap() knows to branch back to $x23 when it has finished. +//-------------------------------------- + .globl TO_BE_INTERRUPTED +TO_BE_INTERRUPTED: + .cfi_startproc + + // This is a garbage entry to ensure that eh_frame is emitted. + // If there's no eh_frame, lldb can use the assembly emulation scan, + // which always includes a rule for $lr, and we won't replicate the + // bug we're testing for. + .cfi_escape DW_CFA_register, ehframe_x22, ehframe_x23 + mov x24, x0 + add x24, x24, #1 + +#if defined(__APPLE__) + adrp x23, L_.return at PAGE // put return address in x23 + add x23, x23, L_.return at PAGEOFF +#else + adrp x23, .L.return + add x23, x23, :lo12:.L.return +#endif + + b TRAP // branch to trap handler, fake async interrupt + +#if defined(__APPLE__) +L_.return: +#else +.L.return: +#endif + mov x0, x24 + ret + .cfi_endproc + + + +//-------------------------------------- +// trap() trap handler function, sets up stack frame +// with special unwind rule for the pc value of the +// "interrupted" stack frame (it's in x23), then calls +// break_to_debugger(). +//-------------------------------------- + .globl TRAP +TRAP: + .cfi_startproc + .cfi_signal_frame + + // The pc value when we were interrupted is in x23 + .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 + + // For fun, mark x0 as unmodified so the caller can + // retrieve the value if it wants. + .cfi_same_value ehframe_x0 + + // Mark x20 as undefined. This is a callee-preserved + // (non-volatile) register by the SysV AArch64 ABI, but + // it'll be fun to see lldb not passing a value past this + // point on the stack. + .cfi_undefined ehframe_x20 + + // standard prologue save of fp & lr so we can call + // break_to_debugger() + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 + .cfi_def_cfa w29, 16 + .cfi_offset w30, -8 + .cfi_offset w29, -16 + + bl BREAK_TO_DEBUGGER + + ldp x29, x30, [sp, #16] + .cfi_same_value x29 + .cfi_same_value x30 + .cfi_def_cfa sp, 32 + add sp, sp, #32 + .cfi_same_value sp + .cfi_def_cfa sp, 0 + + // jump back to $x23 to resume execution of to_be_interrupted + br x23 + .cfi_endproc + +//-------------------------------------- +// break_to_debugger() executes a BRK instruction +//-------------------------------------- + .globl BREAK_TO_DEBUGGER +BREAK_TO_DEBUGGER: + .cfi_startproc + + // For fun, mark x0 as unmodified so the caller can + // retrieve the value if it wants. + .cfi_same_value ehframe_x0 + + brk #0xf000 // __builtin_debugtrap aarch64 instruction + + ret + .cfi_endproc diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/main.c b/lldb/test/API/functionalities/unwind/frameless-faulted/main.c new file mode 100644 index 0000000000000..e5f690a21a45e --- /dev/null +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/main.c @@ -0,0 +1,7 @@ +int to_be_interrupted(int); + +int main() { + int c = 10; + c = to_be_interrupted(c); + return c; +} From lldb-commits at lists.llvm.org Fri May 9 20:07:26 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Fri, 09 May 2025 20:07:26 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681ec2ee.050a0220.122171.ed8e@mx.google.com> https://github.com/jasonmolenda closed https://github.com/llvm/llvm-project/pull/138805 From lldb-commits at lists.llvm.org Fri May 9 20:10:40 2025 From: lldb-commits at lists.llvm.org (LLVM Continuous Integration via lldb-commits) Date: Fri, 09 May 2025 20:10:40 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681ec3b0.630a0220.18e240.75e8@mx.google.com> llvm-ci wrote: LLVM Buildbot has detected a new failure on builder `lldb-x86_64-debian` running on `lldb-x86_64-debian` while building `lldb` at step 6 "test". Full details are available at: https://lab.llvm.org/buildbot/#/builders/162/builds/22097
Here is the relevant piece of the build log for the reference ``` Step 6 (test) failure: build (failure) ... UNSUPPORTED: lldb-shell :: Register/x86-zmm-write.test (2882 of 2895) UNSUPPORTED: lldb-shell :: ScriptInterpreter/Lua/breakpoint_callback.test (2883 of 2895) UNSUPPORTED: lldb-shell :: ScriptInterpreter/Python/Crashlog/altered_threadState.test (2884 of 2895) UNSUPPORTED: lldb-shell :: ScriptInterpreter/Lua/bindings.test (2885 of 2895) UNSUPPORTED: lldb-shell :: Unwind/windows-unaligned-x86_64.test (2886 of 2895) UNSUPPORTED: lldb-shell :: Heap/heap-cstr.test (2887 of 2895) PASS: lldb-unit :: Utility/./UtilityTests/116/129 (2888 of 2895) UNSUPPORTED: lldb-shell :: ScriptInterpreter/Lua/persistent_state.test (2889 of 2895) UNSUPPORTED: lldb-shell :: ScriptInterpreter/Lua/io.test (2890 of 2895) UNRESOLVED: lldb-api :: functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py (2891 of 2895) ******************** TEST 'lldb-api :: functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py' FAILED ******************** Script: -- /usr/bin/python3 /home/worker/2.0.1/lldb-x86_64-debian/llvm-project/lldb/test/API/dotest.py -u CXXFLAGS -u CFLAGS --env LLVM_LIBS_DIR=/home/worker/2.0.1/lldb-x86_64-debian/build/./lib --env LLVM_INCLUDE_DIR=/home/worker/2.0.1/lldb-x86_64-debian/build/include --env LLVM_TOOLS_DIR=/home/worker/2.0.1/lldb-x86_64-debian/build/./bin --arch x86_64 --build-dir /home/worker/2.0.1/lldb-x86_64-debian/build/lldb-test-build.noindex --lldb-module-cache-dir /home/worker/2.0.1/lldb-x86_64-debian/build/lldb-test-build.noindex/module-cache-lldb/lldb-api --clang-module-cache-dir /home/worker/2.0.1/lldb-x86_64-debian/build/lldb-test-build.noindex/module-cache-clang/lldb-api --executable /home/worker/2.0.1/lldb-x86_64-debian/build/./bin/lldb --compiler /home/worker/2.0.1/lldb-x86_64-debian/build/./bin/clang --dsymutil /home/worker/2.0.1/lldb-x86_64-debian/build/./bin/dsymutil --make /usr/bin/gmake --llvm-tools-dir /home/worker/2.0.1/lldb-x86_64-debian/build/./bin --lldb-obj-root /home/worker/2.0.1/lldb-x86_64-debian/build/tools/lldb --lldb-libs-dir /home/worker/2.0.1/lldb-x86_64-debian/build/./lib -t /home/worker/2.0.1/lldb-x86_64-debian/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted -p TestUnwindFramelessFaulted.py -- Exit Code: 1 Command Output (stdout): -- lldb version 21.0.0git (https://github.com/llvm/llvm-project.git revision e897cb139ee6ef5c145fed5394c4d96baa658e6b) clang revision e897cb139ee6ef5c145fed5394c4d96baa658e6b llvm revision e897cb139ee6ef5c145fed5394c4d96baa658e6b Skipping the following test categories: ['libc++', 'dsym', 'gmodules', 'debugserver', 'objc'] -- Command Output (stderr): -- Change dir to: /home/worker/2.0.1/lldb-x86_64-debian/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted runCmd: settings clear --all output: runCmd: settings set symbols.enable-external-lookup false output: runCmd: settings set target.inherit-tcc true output: runCmd: settings set target.disable-aslr false output: runCmd: settings set target.detach-on-error false output: runCmd: settings set target.auto-apply-fixits false ```
https://github.com/llvm/llvm-project/pull/138805 From lldb-commits at lists.llvm.org Fri May 9 20:13:28 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Fri, 09 May 2025 20:13:28 -0700 (PDT) Subject: [Lldb-commits] [lldb] 05a2b33 - Fix skipIf which was doing || and I need && Message-ID: <681ec458.630a0220.1f5179.75fc@mx.google.com> Author: Jason Molenda Date: 2025-05-09T20:12:56-07:00 New Revision: 05a2b33f7b36d4fc91b7a957aa00100bc8e38f04 URL: https://github.com/llvm/llvm-project/commit/05a2b33f7b36d4fc91b7a957aa00100bc8e38f04 DIFF: https://github.com/llvm/llvm-project/commit/05a2b33f7b36d4fc91b7a957aa00100bc8e38f04.diff LOG: Fix skipIf which was doing || and I need && only run this test on linux or darwin when targetting arm64/aarch64. Added: Modified: lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py Removed: ################################################################################ diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py index a03d994045773..f1fce8535ca35 100644 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py @@ -10,10 +10,8 @@ class TestUnwindFramelessFaulted(TestBase): NO_DEBUG_INFO_TESTCASE = True - @skipIf( - oslist=no_match([lldbplatformutil.getDarwinOSTriples(), "linux"]), - archs=no_match(["aarch64", "arm64", "arm64e"]), - ) + @skipIf(oslist=no_match([lldbplatformutil.getDarwinOSTriples(), "linux"])) + @skipIf(archs=no_match(["aarch64", "arm64", "arm64e"])) def test_frameless_faulted_unwind(self): self.build() From lldb-commits at lists.llvm.org Fri May 9 20:16:59 2025 From: lldb-commits at lists.llvm.org (LLVM Continuous Integration via lldb-commits) Date: Fri, 09 May 2025 20:16:59 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681ec52b.630a0220.1d31d0.78db@mx.google.com> llvm-ci wrote: LLVM Buildbot has detected a new failure on builder `lldb-aarch64-ubuntu` running on `linaro-lldb-aarch64-ubuntu` while building `lldb` at step 6 "test". Full details are available at: https://lab.llvm.org/buildbot/#/builders/59/builds/17444
Here is the relevant piece of the build log for the reference ``` Step 6 (test) failure: build (failure) ... UNSUPPORTED: lldb-api :: functionalities/tsan/thread_numbers/TestTsanThreadNumbers.py (685 of 2165) UNSUPPORTED: lldb-api :: functionalities/type_lookup/TestTypeLookup.py (686 of 2165) PASS: lldb-api :: functionalities/type_find_first/TestFindFirstType.py (687 of 2165) PASS: lldb-api :: functionalities/type_get_module/TestTypeGetModule.py (688 of 2165) PASS: lldb-api :: functionalities/type_types/TestFindTypes.py (689 of 2165) UNSUPPORTED: lldb-api :: functionalities/ubsan/basic/TestUbsanBasic.py (690 of 2165) UNSUPPORTED: lldb-api :: functionalities/ubsan/user-expression/TestUbsanUserExpression.py (691 of 2165) UNSUPPORTED: lldb-api :: functionalities/unwind/ehframe/TestEhFrameUnwind.py (692 of 2165) PASS: lldb-api :: functionalities/statusline/TestStatusline.py (693 of 2165) UNRESOLVED: lldb-api :: functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py (694 of 2165) ******************** TEST 'lldb-api :: functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py' FAILED ******************** Script: -- /usr/bin/python3.10 /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/test/API/dotest.py -u CXXFLAGS -u CFLAGS --env LLVM_LIBS_DIR=/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./lib --env LLVM_INCLUDE_DIR=/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/include --env LLVM_TOOLS_DIR=/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin --arch aarch64 --build-dir /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/lldb-test-build.noindex --lldb-module-cache-dir /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/lldb-test-build.noindex/module-cache-lldb/lldb-api --clang-module-cache-dir /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/lldb-test-build.noindex/module-cache-clang/lldb-api --executable /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin/lldb --compiler /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin/clang --dsymutil /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin/dsymutil --make /usr/bin/gmake --llvm-tools-dir /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin --lldb-obj-root /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/tools/lldb --lldb-libs-dir /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./lib /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted -p TestUnwindFramelessFaulted.py -- Exit Code: 1 Command Output (stdout): -- lldb version 21.0.0git (https://github.com/llvm/llvm-project.git revision e897cb139ee6ef5c145fed5394c4d96baa658e6b) clang revision e897cb139ee6ef5c145fed5394c4d96baa658e6b llvm revision e897cb139ee6ef5c145fed5394c4d96baa658e6b Skipping the following test categories: ['libc++', 'dsym', 'gmodules', 'debugserver', 'objc'] -- Command Output (stderr): -- FAIL: LLDB (/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/bin/clang-aarch64) :: test_frameless_faulted_unwind (TestUnwindFramelessFaulted.TestUnwindFramelessFaulted) ====================================================================== ERROR: test_frameless_faulted_unwind (TestUnwindFramelessFaulted.TestUnwindFramelessFaulted) ---------------------------------------------------------------------- Error when building test subject. Build Command: /usr/bin/gmake VPATH=/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted -C /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/lldb-test-build.noindex/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.test_frameless_faulted_unwind -I /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted -I /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make -f /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted/Makefile all ARCH=aarch64 CC=/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/bin/clang CC_TYPE=clang CXX=/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/bin/clang++ LLVM_AR=/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin/llvm-ar AR=/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin/llvm-ar OBJCOPY=/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin/llvm-objcopy STRIP=/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin/llvm-strip ARCHIVER=/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin/llvm-ar DWP=/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin/llvm-dwp CLANG_MODULE_CACHE_DIR=/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/lldb-test-build.noindex/module-cache-clang/lldb-api LLDB_OBJ_ROOT=/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/tools/lldb OS=Linux HOST_OS=Linux Build Command Output: gmake: Entering directory '/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/lldb-test-build.noindex/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.test_frameless_faulted_unwind' /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/bin/clang -g -O0 -I/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make/../../../../..//include -I/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/tools/lldb/include -I/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted -I/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make -include /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make/test_common.h -fno-limit-debug-info -MT main.o -MD -MP -MF main.d -c -o main.o /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted/main.c /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/bin/clang -g -O0 -I/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make/../../../../..//include -I/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/tools/lldb/include -I/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted -I/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make -include /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make/test_common.h -fno-limit-debug-info -E -o interrupt-and-trap-funcs.s /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/bin/clang -g -O0 -I/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make/../../../../..//include -I/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/tools/lldb/include -I/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted -I/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make -include /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make/test_common.h -fno-limit-debug-info -c -o interrupt-and-trap-funcs.o interrupt-and-trap-funcs.s clang: warning: argument unused during compilation: '-O0' [-Wunused-command-line-argument] clang: warning: argument unused during compilation: '-include /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make/test_common.h' [-Wunused-command-line-argument] clang: warning: argument unused during compilation: '-fno-limit-debug-info' [-Wunused-command-line-argument] /usr/include/asm-generic/int-ll64.h:20:20: error: unexpected token in argument list typedef __signed__ char __s8; ^ /usr/include/asm-generic/int-ll64.h:21:18: error: unexpected token in argument list typedef unsigned char __u8; ```
https://github.com/llvm/llvm-project/pull/138805 From lldb-commits at lists.llvm.org Fri May 9 20:20:57 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Fri, 09 May 2025 20:20:57 -0700 (PDT) Subject: [Lldb-commits] [lldb] 76f0f4c - Stop running test on Linux for now Message-ID: <681ec619.170a0220.f1106.c2c6@mx.google.com> Author: Jason Molenda Date: 2025-05-09T20:20:51-07:00 New Revision: 76f0f4cdf4bf9ebf476af99ad9911c687910d66d URL: https://github.com/llvm/llvm-project/commit/76f0f4cdf4bf9ebf476af99ad9911c687910d66d DIFF: https://github.com/llvm/llvm-project/commit/76f0f4cdf4bf9ebf476af99ad9911c687910d66d.diff LOG: Stop running test on Linux for now Failed at compile time lldb-aarch64-ubuntu bot. It did clang -E -o interrupt-and-trap-funcs.s interrupt-and-trap-funcs.c and that added a bunch of standard C header typedefs to the output .s file which then turn into compile errors when it tries to compile the .s file as assembly. Never saw that behavior in my testing on an ubuntu 24.04 system. It would have been nice to have the test run on Linux as well as Darwin, but it's not essential. Added: Modified: lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py Removed: ################################################################################ diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py index f1fce8535ca35..53c5e1182cf24 100644 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py @@ -10,7 +10,7 @@ class TestUnwindFramelessFaulted(TestBase): NO_DEBUG_INFO_TESTCASE = True - @skipIf(oslist=no_match([lldbplatformutil.getDarwinOSTriples(), "linux"])) + @skipIf(oslist=no_match([lldbplatformutil.getDarwinOSTriples()])) @skipIf(archs=no_match(["aarch64", "arm64", "arm64e"])) def test_frameless_faulted_unwind(self): self.build() From lldb-commits at lists.llvm.org Fri May 9 20:25:58 2025 From: lldb-commits at lists.llvm.org (LLVM Continuous Integration via lldb-commits) Date: Fri, 09 May 2025 20:25:58 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681ec746.630a0220.b012b.797a@mx.google.com> llvm-ci wrote: LLVM Buildbot has detected a new failure on builder `lldb-arm-ubuntu` running on `linaro-lldb-arm-ubuntu` while building `lldb` at step 6 "test". Full details are available at: https://lab.llvm.org/buildbot/#/builders/18/builds/15782
Here is the relevant piece of the build log for the reference ``` Step 6 (test) failure: build (failure) ... UNSUPPORTED: lldb-api :: functionalities/type_lookup/TestTypeLookup.py (689 of 3022) PASS: lldb-api :: functionalities/thread/thread_specific_break_plus_condition/TestThreadSpecificBpPlusCondition.py (690 of 3022) PASS: lldb-api :: functionalities/type_find_first/TestFindFirstType.py (691 of 3022) PASS: lldb-api :: functionalities/type_get_module/TestTypeGetModule.py (692 of 3022) UNSUPPORTED: lldb-api :: functionalities/ubsan/basic/TestUbsanBasic.py (693 of 3022) UNSUPPORTED: lldb-api :: functionalities/ubsan/user-expression/TestUbsanUserExpression.py (694 of 3022) PASS: lldb-api :: functionalities/type_types/TestFindTypes.py (695 of 3022) UNSUPPORTED: lldb-api :: functionalities/unwind/aarch64_unwind_pac/TestAArch64UnwindPAC.py (696 of 3022) UNSUPPORTED: lldb-api :: functionalities/unwind/ehframe/TestEhFrameUnwind.py (697 of 3022) UNRESOLVED: lldb-api :: functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py (698 of 3022) ******************** TEST 'lldb-api :: functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py' FAILED ******************** Script: -- /usr/bin/python3.10 /home/tcwg-buildbot/worker/lldb-arm-ubuntu/llvm-project/lldb/test/API/dotest.py -u CXXFLAGS -u CFLAGS --env LLVM_LIBS_DIR=/home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/./lib --env LLVM_INCLUDE_DIR=/home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/include --env LLVM_TOOLS_DIR=/home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/./bin --arch armv8l --build-dir /home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/lldb-test-build.noindex --lldb-module-cache-dir /home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/lldb-test-build.noindex/module-cache-lldb/lldb-api --clang-module-cache-dir /home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/lldb-test-build.noindex/module-cache-clang/lldb-api --executable /home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/./bin/lldb --compiler /home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/./bin/clang --dsymutil /home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/./bin/dsymutil --make /usr/bin/gmake --llvm-tools-dir /home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/./bin --lldb-obj-root /home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/tools/lldb --lldb-libs-dir /home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/./lib /home/tcwg-buildbot/worker/lldb-arm-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted -p TestUnwindFramelessFaulted.py -- Exit Code: 1 Command Output (stdout): -- lldb version 21.0.0git (https://github.com/llvm/llvm-project.git revision e897cb139ee6ef5c145fed5394c4d96baa658e6b) clang revision e897cb139ee6ef5c145fed5394c4d96baa658e6b llvm revision e897cb139ee6ef5c145fed5394c4d96baa658e6b Skipping the following test categories: ['libc++', 'dsym', 'gmodules', 'debugserver', 'objc'] -- Command Output (stderr): -- FAIL: LLDB (/home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/bin/clang-arm) :: test_frameless_faulted_unwind (TestUnwindFramelessFaulted.TestUnwindFramelessFaulted) ====================================================================== ERROR: test_frameless_faulted_unwind (TestUnwindFramelessFaulted.TestUnwindFramelessFaulted) ---------------------------------------------------------------------- Error when building test subject. Build Command: /usr/bin/gmake VPATH=/home/tcwg-buildbot/worker/lldb-arm-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted -C /home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/lldb-test-build.noindex/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.test_frameless_faulted_unwind -I /home/tcwg-buildbot/worker/lldb-arm-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted -I /home/tcwg-buildbot/worker/lldb-arm-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make -f /home/tcwg-buildbot/worker/lldb-arm-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted/Makefile all ARCH=armv8l CC=/home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/bin/clang CC_TYPE=clang CXX=/home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/bin/clang++ LLVM_AR=/home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/./bin/llvm-ar AR=/home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/./bin/llvm-ar OBJCOPY=/home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/./bin/llvm-objcopy STRIP=/home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/./bin/llvm-strip ARCHIVER=/home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/./bin/llvm-ar DWP=/home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/./bin/llvm-dwp CLANG_MODULE_CACHE_DIR=/home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/lldb-test-build.noindex/module-cache-clang/lldb-api LLDB_OBJ_ROOT=/home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/tools/lldb OS=Linux HOST_OS=Linux Build Command Output: gmake: Entering directory '/home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/lldb-test-build.noindex/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.test_frameless_faulted_unwind' /home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/bin/clang -g -O0 -I/home/tcwg-buildbot/worker/lldb-arm-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make/../../../../..//include -I/home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/tools/lldb/include -I/home/tcwg-buildbot/worker/lldb-arm-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted -I/home/tcwg-buildbot/worker/lldb-arm-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make -include /home/tcwg-buildbot/worker/lldb-arm-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make/test_common.h -fno-limit-debug-info -MT main.o -MD -MP -MF main.d -c -o main.o /home/tcwg-buildbot/worker/lldb-arm-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted/main.c /home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/bin/clang -g -O0 -I/home/tcwg-buildbot/worker/lldb-arm-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make/../../../../..//include -I/home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/tools/lldb/include -I/home/tcwg-buildbot/worker/lldb-arm-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted -I/home/tcwg-buildbot/worker/lldb-arm-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make -include /home/tcwg-buildbot/worker/lldb-arm-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make/test_common.h -fno-limit-debug-info -E -o interrupt-and-trap-funcs.s /home/tcwg-buildbot/worker/lldb-arm-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c /home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/bin/clang -g -O0 -I/home/tcwg-buildbot/worker/lldb-arm-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make/../../../../..//include -I/home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/tools/lldb/include -I/home/tcwg-buildbot/worker/lldb-arm-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted -I/home/tcwg-buildbot/worker/lldb-arm-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make -include /home/tcwg-buildbot/worker/lldb-arm-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make/test_common.h -fno-limit-debug-info -c -o interrupt-and-trap-funcs.o interrupt-and-trap-funcs.s clang: warning: argument unused during compilation: '-O0' [-Wunused-command-line-argument] clang: warning: argument unused during compilation: '-include /home/tcwg-buildbot/worker/lldb-arm-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make/test_common.h' [-Wunused-command-line-argument] clang: warning: argument unused during compilation: '-fno-limit-debug-info' [-Wunused-command-line-argument] /usr/include/asm-generic/int-ll64.h:20:20: error: unexpected token in argument list typedef __signed__ char __s8; ^ /usr/include/asm-generic/int-ll64.h:21:18: error: unexpected token in argument list typedef unsigned char __u8; ```
https://github.com/llvm/llvm-project/pull/138805 From lldb-commits at lists.llvm.org Fri May 9 20:28:53 2025 From: lldb-commits at lists.llvm.org (LLVM Continuous Integration via lldb-commits) Date: Fri, 09 May 2025 20:28:53 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681ec7f5.050a0220.3c4f8a.e83d@mx.google.com> llvm-ci wrote: LLVM Buildbot has detected a new failure on builder `lldb-remote-linux-ubuntu` running on `as-builder-9` while building `lldb` at step 16 "test-check-lldb-api". Full details are available at: https://lab.llvm.org/buildbot/#/builders/195/builds/8813
Here is the relevant piece of the build log for the reference ``` Step 16 (test-check-lldb-api) failure: Test just built components: check-lldb-api completed (failure) ... UNSUPPORTED: lldb-api :: functionalities/tty/TestTerminal.py (685 of 1267) UNSUPPORTED: lldb-api :: functionalities/type_lookup/TestTypeLookup.py (686 of 1267) PASS: lldb-api :: functionalities/type_find_first/TestFindFirstType.py (687 of 1267) PASS: lldb-api :: functionalities/type_get_module/TestTypeGetModule.py (688 of 1267) PASS: lldb-api :: functionalities/thread/thread_specific_break/TestThreadSpecificBreakpoint.py (689 of 1267) UNSUPPORTED: lldb-api :: functionalities/ubsan/basic/TestUbsanBasic.py (690 of 1267) PASS: lldb-api :: functionalities/type_types/TestFindTypes.py (691 of 1267) UNSUPPORTED: lldb-api :: functionalities/ubsan/user-expression/TestUbsanUserExpression.py (692 of 1267) UNSUPPORTED: lldb-api :: functionalities/unwind/ehframe/TestEhFrameUnwind.py (693 of 1267) UNRESOLVED: lldb-api :: functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py (694 of 1267) ******************** TEST 'lldb-api :: functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py' FAILED ******************** Script: -- /usr/bin/python3.12 /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/llvm-project/lldb/test/API/dotest.py -u CXXFLAGS -u CFLAGS --env LLVM_LIBS_DIR=/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/./lib --env LLVM_INCLUDE_DIR=/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/include --env LLVM_TOOLS_DIR=/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/./bin --libcxx-include-dir /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/include/c++/v1 --libcxx-include-target-dir /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/include/aarch64-unknown-linux-gnu/c++/v1 --libcxx-library-dir /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/./lib/aarch64-unknown-linux-gnu --arch aarch64 --build-dir /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/lldb-test-build.noindex --lldb-module-cache-dir /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/lldb-test-build.noindex/module-cache-lldb/lldb-api --clang-module-cache-dir /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/lldb-test-build.noindex/module-cache-clang/lldb-api --executable /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/./bin/lldb --compiler /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/bin/clang --dsymutil /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/./bin/dsymutil --make /usr/bin/gmake --llvm-tools-dir /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/./bin --lldb-obj-root /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/tools/lldb --lldb-libs-dir /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/./lib --platform-url connect://jetson-agx-2198.lab.llvm.org:1234 --platform-working-dir /home/ubuntu/lldb-tests --sysroot /mnt/fs/jetson-agx-ubuntu --env ARCH_CFLAGS=-mcpu=cortex-a78 --platform-name remote-linux --skip-category=lldb-server /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted -p TestUnwindFramelessFaulted.py -- Exit Code: 1 Command Output (stdout): -- lldb version 21.0.0git (https://github.com/llvm/llvm-project.git revision e897cb139ee6ef5c145fed5394c4d96baa658e6b) clang revision e897cb139ee6ef5c145fed5394c4d96baa658e6b llvm revision e897cb139ee6ef5c145fed5394c4d96baa658e6b Setting up remote platform 'remote-linux' Connecting to remote platform 'remote-linux' at 'connect://jetson-agx-2198.lab.llvm.org:1234'... Connected. Setting remote platform working directory to '/home/ubuntu/lldb-tests'... Skipping the following test categories: ['lldb-server', 'dsym', 'gmodules', 'debugserver', 'objc', 'lldb-dap'] -- Command Output (stderr): -- WARNING:root:Custom libc++ is not supported for remote runs: ignoring --libcxx arguments FAIL: LLDB (/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/bin/clang-aarch64) :: test_frameless_faulted_unwind (TestUnwindFramelessFaulted.TestUnwindFramelessFaulted.test_frameless_faulted_unwind) ====================================================================== ERROR: test_frameless_faulted_unwind (TestUnwindFramelessFaulted.TestUnwindFramelessFaulted.test_frameless_faulted_unwind) ---------------------------------------------------------------------- Error when building test subject. Build Command: /usr/bin/gmake VPATH=/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted -C /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/lldb-test-build.noindex/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.test_frameless_faulted_unwind -I /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted -I /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make -f /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted/Makefile all ARCH=aarch64 CC=/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/bin/clang CC_TYPE=clang CXX=/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/bin/clang++ LLVM_AR=/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/./bin/llvm-ar AR=/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/./bin/llvm-ar OBJCOPY=/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/./bin/llvm-objcopy STRIP=/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/./bin/llvm-strip ARCHIVER=/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/./bin/llvm-ar DWP=/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/./bin/llvm-dwp SDKROOT=/mnt/fs/jetson-agx-ubuntu CLANG_MODULE_CACHE_DIR=/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/lldb-test-build.noindex/module-cache-clang/lldb-api LIBCPP_INCLUDE_DIR=/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/include/c++/v1 LIBCPP_LIBRARY_DIR=/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/./lib/aarch64-unknown-linux-gnu LIBCPP_INCLUDE_TARGET_DIR=/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/include/aarch64-unknown-linux-gnu/c++/v1 LLDB_OBJ_ROOT=/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/tools/lldb OS=Linux HOST_OS=Linux Build Command Output: gmake: Entering directory '/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/lldb-test-build.noindex/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.test_frameless_faulted_unwind' /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/bin/clang -g -O0 --sysroot "/mnt/fs/jetson-agx-ubuntu" -I/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make/../../../../..//include -I/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/tools/lldb/include -I/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted -I/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make -include /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make/test_common.h -fno-limit-debug-info -MT main.o -MD -MP -MF main.d -c -o main.o /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted/main.c /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/bin/clang -g -O0 --sysroot "/mnt/fs/jetson-agx-ubuntu" -I/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make/../../../../..//include -I/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/tools/lldb/include -I/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted -I/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make -include /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make/test_common.h -fno-limit-debug-info -E -o interrupt-and-trap-funcs.s /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/bin/clang -g -O0 --sysroot "/mnt/fs/jetson-agx-ubuntu" -I/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make/../../../../..//include -I/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/tools/lldb/include -I/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/llvm-project/lldb/test/API/functionalities/unwind/frameless-faulted -I/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make -include /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make/test_common.h -fno-limit-debug-info -c -o interrupt-and-trap-funcs.o interrupt-and-trap-funcs.s clang: warning: argument unused during compilation: '-O0' [-Wunused-command-line-argument] clang: warning: argument unused during compilation: '-include /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/llvm-project/lldb/packages/Python/lldbsuite/test/make/test_common.h' [-Wunused-command-line-argument] clang: warning: argument unused during compilation: '-fno-limit-debug-info' [-Wunused-command-line-argument] ```
https://github.com/llvm/llvm-project/pull/138805 From lldb-commits at lists.llvm.org Fri May 9 20:35:38 2025 From: lldb-commits at lists.llvm.org (LLVM Continuous Integration via lldb-commits) Date: Fri, 09 May 2025 20:35:38 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681ec98a.170a0220.272ffa.cf8d@mx.google.com> llvm-ci wrote: LLVM Buildbot has detected a new failure on builder `lldb-remote-linux-win` running on `as-builder-10` while building `lldb` at step 17 "test-check-lldb-api". Full details are available at: https://lab.llvm.org/buildbot/#/builders/197/builds/5139
Here is the relevant piece of the build log for the reference ``` Step 17 (test-check-lldb-api) failure: Test just built components: check-lldb-api completed (failure) ... UNSUPPORTED: lldb-api :: functionalities/tty/TestTerminal.py (685 of 1267) UNSUPPORTED: lldb-api :: functionalities/type_lookup/TestTypeLookup.py (686 of 1267) PASS: lldb-api :: functionalities/type_find_first/TestFindFirstType.py (687 of 1267) PASS: lldb-api :: functionalities/type_get_module/TestTypeGetModule.py (688 of 1267) PASS: lldb-api :: functionalities/type_types/TestFindTypes.py (689 of 1267) UNSUPPORTED: lldb-api :: functionalities/ubsan/basic/TestUbsanBasic.py (690 of 1267) UNSUPPORTED: lldb-api :: functionalities/ubsan/user-expression/TestUbsanUserExpression.py (691 of 1267) PASS: lldb-api :: functionalities/thread/thread_specific_break/TestThreadSpecificBreakpoint.py (692 of 1267) UNSUPPORTED: lldb-api :: functionalities/unwind/ehframe/TestEhFrameUnwind.py (693 of 1267) UNRESOLVED: lldb-api :: functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py (694 of 1267) ******************** TEST 'lldb-api :: functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py' FAILED ******************** Script: -- C:/Python312/python.exe C:/buildbot/as-builder-10/lldb-x-aarch64/llvm-project/lldb\test\API\dotest.py -u CXXFLAGS -u CFLAGS --env LLVM_LIBS_DIR=C:/buildbot/as-builder-10/lldb-x-aarch64/build/./lib --env LLVM_INCLUDE_DIR=C:/buildbot/as-builder-10/lldb-x-aarch64/build/include --env LLVM_TOOLS_DIR=C:/buildbot/as-builder-10/lldb-x-aarch64/build/./bin --arch aarch64 --build-dir C:/buildbot/as-builder-10/lldb-x-aarch64/build/lldb-test-build.noindex --lldb-module-cache-dir C:/buildbot/as-builder-10/lldb-x-aarch64/build/lldb-test-build.noindex/module-cache-lldb\lldb-api --clang-module-cache-dir C:/buildbot/as-builder-10/lldb-x-aarch64/build/lldb-test-build.noindex/module-cache-clang\lldb-api --executable C:/buildbot/as-builder-10/lldb-x-aarch64/build/./bin/lldb.exe --compiler C:/buildbot/as-builder-10/lldb-x-aarch64/build/./bin/clang.exe --dsymutil C:/buildbot/as-builder-10/lldb-x-aarch64/build/./bin/dsymutil.exe --make C:/ninja/make.exe --llvm-tools-dir C:/buildbot/as-builder-10/lldb-x-aarch64/build/./bin --lldb-obj-root C:/buildbot/as-builder-10/lldb-x-aarch64/build/tools/lldb --lldb-libs-dir C:/buildbot/as-builder-10/lldb-x-aarch64/build/./lib --platform-url connect://jetson-agx-0086.lab.llvm.org:1234 --platform-working-dir /home/ubuntu/lldb-tests --sysroot c:/buildbot/fs/jetson-agx-ubuntu --env ARCH_CFLAGS=-mcpu=cortex-a78 --platform-name remote-linux --skip-category=lldb-server C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\test\API\functionalities\unwind\frameless-faulted -p TestUnwindFramelessFaulted.py -- Exit Code: 1 Command Output (stdout): -- lldb version 21.0.0git (https://github.com/llvm/llvm-project.git revision e897cb139ee6ef5c145fed5394c4d96baa658e6b) clang revision e897cb139ee6ef5c145fed5394c4d96baa658e6b llvm revision e897cb139ee6ef5c145fed5394c4d96baa658e6b Setting up remote platform 'remote-linux' Connecting to remote platform 'remote-linux' at 'connect://jetson-agx-0086.lab.llvm.org:1234'... Connected. Setting remote platform working directory to '/home/ubuntu/lldb-tests'... Skipping the following test categories: ['lldb-server', 'dsym', 'gmodules', 'debugserver', 'objc', 'lldb-dap'] -- Command Output (stderr): -- FAIL: LLDB (C:\buildbot\as-builder-10\lldb-x-aarch64\build\bin\clang.exe-aarch64) :: test_frameless_faulted_unwind (TestUnwindFramelessFaulted.TestUnwindFramelessFaulted.test_frameless_faulted_unwind) ====================================================================== ERROR: test_frameless_faulted_unwind (TestUnwindFramelessFaulted.TestUnwindFramelessFaulted.test_frameless_faulted_unwind) ---------------------------------------------------------------------- Error when building test subject. Build Command: ```
https://github.com/llvm/llvm-project/pull/138805 From lldb-commits at lists.llvm.org Fri May 9 20:37:24 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Fri, 09 May 2025 20:37:24 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681ec9f4.a70a0220.5ad31.fc18@mx.google.com> jasonmolenda wrote: I had two commits to address bots. The first was my skipIf to run the test only darwin/linux only for arm64/aarch64 acted like an or so it could run on either of those. Second, the aarch64-ubuntu bot failed when I had it my assembly file named ".c" and did `clang -E -o interrupt-and-trap-funcs.s interrupt-and-trap-funcs.c` to run it through the preprocessor - clang on that system when run by the API tests injected a bunch of C headers and it failed to compile as assembly then. For now, I changed the test to only run on Darwin. https://github.com/llvm/llvm-project/pull/138805 From lldb-commits at lists.llvm.org Fri May 9 20:42:40 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Fri, 09 May 2025 20:42:40 -0700 (PDT) Subject: [Lldb-commits] [lldb] f6ca690 - Revert "Stop running test on Linux for now" Message-ID: <681ecb30.050a0220.1fa9bd.f817@mx.google.com> Author: Jason Molenda Date: 2025-05-09T20:41:02-07:00 New Revision: f6ca690c4325f6c7b22eca69fae6e5fa069cc7ab URL: https://github.com/llvm/llvm-project/commit/f6ca690c4325f6c7b22eca69fae6e5fa069cc7ab DIFF: https://github.com/llvm/llvm-project/commit/f6ca690c4325f6c7b22eca69fae6e5fa069cc7ab.diff LOG: Revert "Stop running test on Linux for now" This reverts commit 76f0f4cdf4bf9ebf476af99ad9911c687910d66d. Added: Modified: lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py Removed: ################################################################################ diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py index 53c5e1182cf24..f1fce8535ca35 100644 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py @@ -10,7 +10,7 @@ class TestUnwindFramelessFaulted(TestBase): NO_DEBUG_INFO_TESTCASE = True - @skipIf(oslist=no_match([lldbplatformutil.getDarwinOSTriples()])) + @skipIf(oslist=no_match([lldbplatformutil.getDarwinOSTriples(), "linux"])) @skipIf(archs=no_match(["aarch64", "arm64", "arm64e"])) def test_frameless_faulted_unwind(self): self.build() From lldb-commits at lists.llvm.org Fri May 9 20:42:41 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Fri, 09 May 2025 20:42:41 -0700 (PDT) Subject: [Lldb-commits] [lldb] d2f6ac2 - Revert "Fix skipIf which was doing || and I need &&" Message-ID: <681ecb31.050a0220.2599cb.e4be@mx.google.com> Author: Jason Molenda Date: 2025-05-09T20:41:08-07:00 New Revision: d2f6ac2c10758d2bd994827610695a8c7f2625fe URL: https://github.com/llvm/llvm-project/commit/d2f6ac2c10758d2bd994827610695a8c7f2625fe DIFF: https://github.com/llvm/llvm-project/commit/d2f6ac2c10758d2bd994827610695a8c7f2625fe.diff LOG: Revert "Fix skipIf which was doing || and I need &&" This reverts commit 05a2b33f7b36d4fc91b7a957aa00100bc8e38f04. Added: Modified: lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py Removed: ################################################################################ diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py index f1fce8535ca35..a03d994045773 100644 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py +++ b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py @@ -10,8 +10,10 @@ class TestUnwindFramelessFaulted(TestBase): NO_DEBUG_INFO_TESTCASE = True - @skipIf(oslist=no_match([lldbplatformutil.getDarwinOSTriples(), "linux"])) - @skipIf(archs=no_match(["aarch64", "arm64", "arm64e"])) + @skipIf( + oslist=no_match([lldbplatformutil.getDarwinOSTriples(), "linux"]), + archs=no_match(["aarch64", "arm64", "arm64e"]), + ) def test_frameless_faulted_unwind(self): self.build() From lldb-commits at lists.llvm.org Fri May 9 20:42:42 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Fri, 09 May 2025 20:42:42 -0700 (PDT) Subject: [Lldb-commits] [lldb] a230bb0 - Revert "[lldb] Provide lr value in faulting frame on arm64 (#138805)" Message-ID: <681ecb32.170a0220.bf47b.b833@mx.google.com> Author: Jason Molenda Date: 2025-05-09T20:41:15-07:00 New Revision: a230bb029813b2988019dce342e2e622af14bd1d URL: https://github.com/llvm/llvm-project/commit/a230bb029813b2988019dce342e2e622af14bd1d DIFF: https://github.com/llvm/llvm-project/commit/a230bb029813b2988019dce342e2e622af14bd1d.diff LOG: Revert "[lldb] Provide lr value in faulting frame on arm64 (#138805)" This test is failing on the LLDB Incremental bot (arm64), which is running an older set of tools (Xcode 15.2) and OS (macOS 14.1) and the CFI directives must not be emitted correctly by either the tools or the OS. I will need to reproduce how this is compiling on that older setup and see what the issue is. Reverting for now so the bots are not blocked. This reverts commit e897cb139ee6ef5c145fed5394c4d96baa658e6b. Added: Modified: lldb/source/Target/RegisterContextUnwind.cpp Removed: lldb/test/API/functionalities/unwind/frameless-faulted/Makefile lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c lldb/test/API/functionalities/unwind/frameless-faulted/main.c ################################################################################ diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index cf4b96c6eda9f..4c760b84e45a5 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -248,7 +248,6 @@ void RegisterContextUnwind::InitializeZerothFrame() { active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); row_register_kind = m_full_unwind_plan_sp->GetRegisterKind(); - PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp); if (active_row && log) { StreamString active_row_strm; active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, @@ -1376,7 +1375,6 @@ RegisterContextUnwind::SavedLocationForRegister( } } - // Check if the active_row has a register location listed. if (regnum.IsValid() && active_row && active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind), unwindplan_regloc)) { @@ -1390,10 +1388,11 @@ RegisterContextUnwind::SavedLocationForRegister( // This is frame 0 and we're retrieving the PC and it's saved in a Return // Address register and it hasn't been saved anywhere yet -- that is, // it's still live in the actual register. Handle this specially. + if (!have_unwindplan_regloc && return_address_reg.IsValid() && - return_address_reg.GetAsKind(eRegisterKindLLDB) != - LLDB_INVALID_REGNUM) { - if (IsFrameZero()) { + IsFrameZero()) { + if (return_address_reg.GetAsKind(eRegisterKindLLDB) != + LLDB_INVALID_REGNUM) { lldb_private::UnwindLLDB::ConcreteRegisterLocation new_regloc; new_regloc.type = UnwindLLDB::ConcreteRegisterLocation:: eRegisterInLiveRegisterContext; @@ -1407,17 +1406,6 @@ RegisterContextUnwind::SavedLocationForRegister( return_address_reg.GetAsKind(eRegisterKindLLDB), return_address_reg.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; - } else if (BehavesLikeZerothFrame()) { - // This function was interrupted asynchronously -- it faulted, - // an async interrupt, a timer fired, a debugger expression etc. - // The caller's pc is in the Return Address register, but the - // UnwindPlan for this function may have no location rule for - // the RA reg. - // This means that the caller's return address is in the RA reg - // when the function was interrupted--descend down one stack frame - // to retrieve it from the trap handler's saved context. - unwindplan_regloc.SetSame(); - have_unwindplan_regloc = true; } } @@ -1934,7 +1922,6 @@ void RegisterContextUnwind::PropagateTrapHandlerFlagFromUnwindPlan( } m_frame_type = eTrapHandlerFrame; - UnwindLogMsg("This frame is marked as a trap handler via its UnwindPlan"); if (m_current_offset_backed_up_one != m_current_offset) { // We backed up the pc by 1 to compute the symbol context, but diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/Makefile b/lldb/test/API/functionalities/unwind/frameless-faulted/Makefile deleted file mode 100644 index fca47ae47491c..0000000000000 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -C_SOURCES := main.c - -interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.c - $(CC) $(CFLAGS) -E -o interrupt-and-trap-funcs.s $(SRCDIR)/interrupt-and-trap-funcs.c - $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o interrupt-and-trap-funcs.s - -include Makefile.rules - -a.out: interrupt-and-trap-funcs.o - -# Needs to come after include -OBJECTS += interrupt-and-trap-funcs.o - diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py deleted file mode 100644 index a03d994045773..0000000000000 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py +++ /dev/null @@ -1,122 +0,0 @@ -"""Test that lldb backtraces a frameless function that faults correctly.""" - -import lldbsuite.test.lldbutil as lldbutil -from lldbsuite.test.lldbtest import * -from lldbsuite.test.decorators import * -import shutil -import os - - -class TestUnwindFramelessFaulted(TestBase): - NO_DEBUG_INFO_TESTCASE = True - - @skipIf( - oslist=no_match([lldbplatformutil.getDarwinOSTriples(), "linux"]), - archs=no_match(["aarch64", "arm64", "arm64e"]), - ) - def test_frameless_faulted_unwind(self): - self.build() - - (target, process, thread, bp) = lldbutil.run_to_name_breakpoint( - self, "main", only_one_thread=False - ) - - # The test program will have a backtrace like this at its deepest: - # - # * frame #0: 0x0000000102adc468 a.out`break_to_debugger + 4 - # frame #1: 0x0000000102adc458 a.out`trap + 16 - # frame #2: 0x0000000102adc440 a.out`to_be_interrupted + 20 - # frame #3: 0x0000000102adc418 a.out`main at main.c:4:7 - # frame #4: 0x0000000193b7eb4c dyld`start + 6000 - - correct_frames = ["break_to_debugger", "trap", "to_be_interrupted", "main"] - - # Keep track of when main has branch & linked, instruction step until we're - # back in main() - main_has_bl_ed = False - - # Instruction step through the binary until we are in a function not - # listed in correct_frames. - frame = thread.GetFrameAtIndex(0) - step_count = 0 - max_step_count = 200 - while ( - process.GetState() == lldb.eStateStopped - and frame.name in correct_frames - and step_count < max_step_count - ): - starting_index = 0 - if self.TraceOn(): - self.runCmd("bt") - - # Find which index into correct_frames the current stack frame is - for idx, name in enumerate(correct_frames): - if frame.name == name: - starting_index = idx - - # Test that all frames after the current frame listed in - # correct_frames appears in the backtrace. - frame_idx = 0 - for expected_frame in correct_frames[starting_index:]: - self.assertEqual(thread.GetFrameAtIndex(frame_idx).name, expected_frame) - frame_idx = frame_idx + 1 - - # When we're at our deepest level, test that register passing of - # x0 and x20 follow the by-hand UnwindPlan rules. - # In this test program, we can get x0 in the middle of the stack - # and we CAN'T get x20. The opposites of the normal AArch64 SysV - # ABI. - if frame.name == "break_to_debugger": - tbi_frame = thread.GetFrameAtIndex(2) - self.assertEqual(tbi_frame.name, "to_be_interrupted") - # The original argument to to_be_interrupted(), 10 - # Normally can't get x0 mid-stack, but UnwindPlans have - # special rules to make this possible. - x0_reg = tbi_frame.register["x0"] - self.assertTrue(x0_reg.IsValid()) - self.assertEqual(x0_reg.GetValueAsUnsigned(), 10) - # The incremented return value from to_be_interrupted(), 11 - x24_reg = tbi_frame.register["x24"] - self.assertTrue(x24_reg.IsValid()) - self.assertEqual(x24_reg.GetValueAsUnsigned(), 11) - # x20 can normally be fetched mid-stack, but the UnwindPlan - # has a rule saying it can't be fetched. - x20_reg = tbi_frame.register["x20"] - self.assertTrue(x20_reg.error.fail) - - trap_frame = thread.GetFrameAtIndex(1) - self.assertEqual(trap_frame.name, "trap") - # Confirm that we can fetch x0 from trap() which - # is normally not possible w/ SysV AbI, but special - # UnwindPlans in use. - x0_reg = trap_frame.register["x0"] - self.assertTrue(x0_reg.IsValid()) - self.assertEqual(x0_reg.GetValueAsUnsigned(), 10) - x1_reg = trap_frame.register["x1"] - self.assertTrue(x1_reg.error.fail) - - main_frame = thread.GetFrameAtIndex(3) - self.assertEqual(main_frame.name, "main") - # x20 can normally be fetched mid-stack, but the UnwindPlan - # has a rule saying it can't be fetched. - x20_reg = main_frame.register["x20"] - self.assertTrue(x20_reg.error.fail) - # x21 can be fetched mid-stack. - x21_reg = main_frame.register["x21"] - self.assertTrue(x21_reg.error.success) - - # manually move past the BRK instruction in - # break_to_debugger(). lldb-server doesn't - # advance past the builtin_debugtrap() BRK - # instruction. - if ( - thread.GetStopReason() == lldb.eStopReasonException - and frame.name == "break_to_debugger" - ): - frame.SetPC(frame.GetPC() + 4) - - if self.TraceOn(): - print("StepInstruction") - thread.StepInstruction(False) - frame = thread.GetFrameAtIndex(0) - step_count = step_count + 1 diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c deleted file mode 100644 index ba6537226f920..0000000000000 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c +++ /dev/null @@ -1,126 +0,0 @@ -// This is assembly code that needs to be run -// through the preprocessor, for simplicity of -// preprocessing it's named .c to start with. -// -// clang-format off - - -#define DW_CFA_register 0x9 -#define ehframe_x0 0 -#define ehframe_x20 20 -#define ehframe_x22 22 -#define ehframe_x23 23 -#define ehframe_pc 32 - -#if defined(__APPLE__) -#define TO_BE_INTERRUPTED _to_be_interrupted -#define TRAP _trap -#define BREAK_TO_DEBUGGER _break_to_debugger -#else -#define TO_BE_INTERRUPTED to_be_interrupted -#define TRAP trap -#define BREAK_TO_DEBUGGER break_to_debugger -#endif - - .text -//-------------------------------------- -// to_be_interrupted() a frameless function that does a non-ABI -// function call to trap(), simulating an async signal/interrup/exception/fault. -// Before it branches to trap(), put the return address in x23. -// trap() knows to branch back to $x23 when it has finished. -//-------------------------------------- - .globl TO_BE_INTERRUPTED -TO_BE_INTERRUPTED: - .cfi_startproc - - // This is a garbage entry to ensure that eh_frame is emitted. - // If there's no eh_frame, lldb can use the assembly emulation scan, - // which always includes a rule for $lr, and we won't replicate the - // bug we're testing for. - .cfi_escape DW_CFA_register, ehframe_x22, ehframe_x23 - mov x24, x0 - add x24, x24, #1 - -#if defined(__APPLE__) - adrp x23, L_.return at PAGE // put return address in x23 - add x23, x23, L_.return at PAGEOFF -#else - adrp x23, .L.return - add x23, x23, :lo12:.L.return -#endif - - b TRAP // branch to trap handler, fake async interrupt - -#if defined(__APPLE__) -L_.return: -#else -.L.return: -#endif - mov x0, x24 - ret - .cfi_endproc - - - -//-------------------------------------- -// trap() trap handler function, sets up stack frame -// with special unwind rule for the pc value of the -// "interrupted" stack frame (it's in x23), then calls -// break_to_debugger(). -//-------------------------------------- - .globl TRAP -TRAP: - .cfi_startproc - .cfi_signal_frame - - // The pc value when we were interrupted is in x23 - .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23 - - // For fun, mark x0 as unmodified so the caller can - // retrieve the value if it wants. - .cfi_same_value ehframe_x0 - - // Mark x20 as undefined. This is a callee-preserved - // (non-volatile) register by the SysV AArch64 ABI, but - // it'll be fun to see lldb not passing a value past this - // point on the stack. - .cfi_undefined ehframe_x20 - - // standard prologue save of fp & lr so we can call - // break_to_debugger() - sub sp, sp, #32 - stp x29, x30, [sp, #16] - add x29, sp, #16 - .cfi_def_cfa w29, 16 - .cfi_offset w30, -8 - .cfi_offset w29, -16 - - bl BREAK_TO_DEBUGGER - - ldp x29, x30, [sp, #16] - .cfi_same_value x29 - .cfi_same_value x30 - .cfi_def_cfa sp, 32 - add sp, sp, #32 - .cfi_same_value sp - .cfi_def_cfa sp, 0 - - // jump back to $x23 to resume execution of to_be_interrupted - br x23 - .cfi_endproc - -//-------------------------------------- -// break_to_debugger() executes a BRK instruction -//-------------------------------------- - .globl BREAK_TO_DEBUGGER -BREAK_TO_DEBUGGER: - .cfi_startproc - - // For fun, mark x0 as unmodified so the caller can - // retrieve the value if it wants. - .cfi_same_value ehframe_x0 - - brk #0xf000 // __builtin_debugtrap aarch64 instruction - - ret - .cfi_endproc diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/main.c b/lldb/test/API/functionalities/unwind/frameless-faulted/main.c deleted file mode 100644 index e5f690a21a45e..0000000000000 --- a/lldb/test/API/functionalities/unwind/frameless-faulted/main.c +++ /dev/null @@ -1,7 +0,0 @@ -int to_be_interrupted(int); - -int main() { - int c = 10; - c = to_be_interrupted(c); - return c; -} From lldb-commits at lists.llvm.org Fri May 9 20:49:12 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Fri, 09 May 2025 20:49:12 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Don't emit a removed module event for unseen modules (PR #139324) In-Reply-To: Message-ID: <681eccb8.050a0220.176032.f2bf@mx.google.com> https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/139324 >From 018bb069e873007501d639dceaf8581eb34ddbb9 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Fri, 9 May 2025 13:18:49 -0700 Subject: [PATCH 1/3] [lldb-dap] Don't emit a removed module event for unseen modules On macOS, lldb-dap is sending module events with reason "removed" for modules that we never told the client about in the first place. This is because when we create a target with a binary, by default all of its dependent libraries are loaded too. When we start executing and see the that the binary and dyld are loaded, we throw away the image list and then as dyld tell us about the correct binaries being loaded in the process, we add them. This PR addresses the issue by keeping track which modules the DAP client knows about. Clients can find out about modules either through the modules request, or through module events. Only when we have told a client about a module, we send module changed or module removed events. This PR also reduces the amount of data sent for removed module events. The DAP specification [1] says that for removed module events, only the ID is used. Fixes #139323 [1] https://microsoft.github.io/debug-adapter-protocol/specification#Events_Module --- .../test/tools/lldb-dap/dap_server.py | 6 -- .../API/tools/lldb-dap/module-event/Makefile | 12 ++++ .../module-event/TestDAP_module_event.py | 55 +++++++++++++++++++ .../API/tools/lldb-dap/module-event/main.cpp | 22 ++++++++ .../API/tools/lldb-dap/module-event/other.c | 5 ++ .../tools/lldb-dap/module/TestDAP_module.py | 10 ++-- lldb/tools/lldb-dap/DAP.cpp | 39 +++++++++---- lldb/tools/lldb-dap/DAP.h | 8 +++ .../Handler/ModulesRequestHandler.cpp | 17 +++++- lldb/tools/lldb-dap/JSONUtils.cpp | 7 ++- lldb/tools/lldb-dap/JSONUtils.h | 7 ++- 11 files changed, 161 insertions(+), 27 deletions(-) create mode 100644 lldb/test/API/tools/lldb-dap/module-event/Makefile create mode 100644 lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py create mode 100644 lldb/test/API/tools/lldb-dap/module-event/main.cpp create mode 100644 lldb/test/API/tools/lldb-dap/module-event/other.c diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py index e10342b72f4f0..c974866306d2a 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py @@ -134,7 +134,6 @@ def __init__(self, recv, send, init_commands, log_file=None): self.thread_stop_reasons = {} self.progress_events = [] self.reverse_requests = [] - self.module_events = [] self.sequence = 1 self.threads = None self.recv_thread.start() @@ -248,11 +247,6 @@ def handle_recv_packet(self, packet): # and 'progressEnd' events. Keep these around in case test # cases want to verify them. self.progress_events.append(packet) - elif event == "module": - # Module events indicate that some information about a module has changed. - self.module_events.append(packet) - # no need to add 'module' event packets to our packets list - return keepGoing elif packet_type == "response": if packet["command"] == "disconnect": diff --git a/lldb/test/API/tools/lldb-dap/module-event/Makefile b/lldb/test/API/tools/lldb-dap/module-event/Makefile new file mode 100644 index 0000000000000..99d79b8053878 --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/module-event/Makefile @@ -0,0 +1,12 @@ +CXX_SOURCES := main.cpp +LD_EXTRAS := -Wl,-rpath "-Wl,$(shell pwd)" +USE_LIBDL :=1 + +a.out: libother + +include Makefile.rules + +# The following shared library will be used to test breakpoints under dynamic loading +libother: other.c + "$(MAKE)" -f $(MAKEFILE_RULES) \ + DYLIB_ONLY=YES DYLIB_C_SOURCES=other.c DYLIB_NAME=other diff --git a/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py b/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py new file mode 100644 index 0000000000000..819640e5598bd --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py @@ -0,0 +1,55 @@ +import dap_server +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil +import lldbdap_testcase +import re + + +class TestDAP_module_event(lldbdap_testcase.DAPTestCaseBase): + + def test_module_event(self): + program = self.getBuildArtifact("a.out") + self.build_and_launch(program, stopOnEntry=True) + + source = "main.cpp" + breakpoint1_line = line_number(source, "// breakpoint 1") + breakpoint2_line = line_number(source, "// breakpoint 2") + breakpoint3_line = line_number(source, "// breakpoint 3") + + breakpoint_ids = self.set_source_breakpoints( + source, [breakpoint1_line, breakpoint2_line, breakpoint3_line] + ) + self.continue_to_breakpoints(breakpoint_ids) + + # We're now stopped at breakpoint 1 before the dlopen. Flush all the module events. + event = self.dap_server.wait_for_event("module", 0.25) + while event is not None: + event = self.dap_server.wait_for_event("module", 0.25) + + # Continue to the second breakpoint, before the dlclose. + self.continue_to_breakpoints(breakpoint_ids) + + # Make sure we got a module event for libother. + event = self.dap_server.wait_for_event("module", 5) + self.assertTrue(event, "didn't get a module event") + module_name = event["body"]["module"]["name"] + module_id = event["body"]["module"]["id"] + self.assertEqual(event["body"]["reason"], "new") + self.assertIn("libother", module_name) + + # Continue to the third breakpoint, after the dlclose. + self.continue_to_breakpoints(breakpoint_ids) + + # Make sure we got a module event for libother. + event = self.dap_server.wait_for_event("module", 5) + self.assertTrue(event, "didn't get a module event") + reason = event["body"]["reason"] + self.assertEqual(event["body"]["reason"], "removed") + self.assertEqual(event["body"]["module"]["id"], module_id) + + # The removed module event should omit everything but the module id. + # Check that there's no module name in the event. + self.assertNotIn("name", event["body"]["module"]) + + self.continue_to_exit() diff --git a/lldb/test/API/tools/lldb-dap/module-event/main.cpp b/lldb/test/API/tools/lldb-dap/module-event/main.cpp new file mode 100644 index 0000000000000..711471b9fadd0 --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/module-event/main.cpp @@ -0,0 +1,22 @@ +#include +#include + +int main(int argc, char const *argv[]) { + +#if defined(__APPLE__) + const char *libother_name = "libother.dylib"; +#else + const char *libother_name = "libother.so"; +#endif + + printf("before dlopen\n"); // breakpoint 1 + void *handle = dlopen(libother_name, RTLD_NOW); + int (*foo)(int) = (int (*)(int))dlsym(handle, "foo"); + foo(12); + + printf("before dlclose\n"); // breakpoint 2 + dlclose(handle); + printf("after dlclose\n"); // breakpoint 3 + + return 0; // breakpoint 1 +} diff --git a/lldb/test/API/tools/lldb-dap/module-event/other.c b/lldb/test/API/tools/lldb-dap/module-event/other.c new file mode 100644 index 0000000000000..dd164597269dc --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/module-event/other.c @@ -0,0 +1,5 @@ +extern int foo(int x) { + int y = x + 42; // break other + int z = y + 42; + return z; +} diff --git a/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py b/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py index 210819cfdd732..3fc0f752ee39e 100644 --- a/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py +++ b/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py @@ -60,13 +60,15 @@ def checkSymbolsLoadedWithSize(): # Collect all the module names we saw as events. module_new_names = [] module_changed_names = [] - for module_event in self.dap_server.module_events: - module_name = module_event["body"]["module"]["name"] + module_event = self.dap_server.wait_for_event("module", 1) + while module_event is not None: reason = module_event["body"]["reason"] if reason == "new": - module_new_names.append(module_name) + module_new_names.append(module_event["body"]["module"]["name"]) elif reason == "changed": - module_changed_names.append(module_name) + module_changed_names.append(module_event["body"]["module"]["name"]) + + module_event = self.dap_server.wait_for_event("module", 1) # Make sure we got an event for every active module. self.assertNotEqual(len(module_new_names), 0) diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index f6754b1f8d7a3..828d7d4873156 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -105,16 +105,6 @@ static uint64_t GetUintFromStructuredData(lldb::SBStructuredData &data, return keyValue.GetUnsignedIntegerValue(); } -static llvm::StringRef GetModuleEventReason(uint32_t event_mask) { - if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded) - return "new"; - if (event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded) - return "removed"; - assert(event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded || - event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged); - return "changed"; -} - /// Return string with first character capitalized. static std::string capitalize(llvm::StringRef str) { if (str.empty()) @@ -1566,7 +1556,6 @@ void DAP::EventThread() { event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded || event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded || event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged) { - llvm::StringRef reason = GetModuleEventReason(event_mask); const uint32_t num_modules = lldb::SBTarget::GetNumModulesFromEvent(event); for (uint32_t i = 0; i < num_modules; ++i) { @@ -1574,10 +1563,36 @@ void DAP::EventThread() { lldb::SBTarget::GetModuleAtIndexFromEvent(i, event); if (!module.IsValid()) continue; + llvm::StringRef module_id = module.GetUUIDString(); + if (module_id.empty()) + continue; + + llvm::StringRef reason; + bool id_only = false; + { + std::lock_guard guard(modules_mutex); + if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded) { + modules.insert(module_id); + reason = "new"; + } else { + // If this is a module we've never told the client about, don't + // send an event. + if (!modules.contains(module_id)) + continue; + + if (event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded) { + modules.erase(module_id); + reason = "removed"; + id_only = true; + } else { + reason = "changed"; + } + } + } llvm::json::Object body; body.try_emplace("reason", reason); - body.try_emplace("module", CreateModule(target, module)); + body.try_emplace("module", CreateModule(target, module, id_only)); llvm::json::Object module_event = CreateEventObject("module"); module_event.try_emplace("body", std::move(body)); SendJSON(llvm::json::Value(std::move(module_event))); diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index afeda8d81efb0..75e4ab0e23616 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -39,6 +39,7 @@ #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" #include "llvm/Support/Error.h" #include "llvm/Support/JSON.h" #include "llvm/Support/Threading.h" @@ -212,6 +213,13 @@ struct DAP { /// The initial thread list upon attaching. std::optional initial_thread_list; + /// Keep track of all the modules our client knows about: either through the + /// modules request or the module events. + /// @{ + std::mutex modules_mutex; + llvm::StringSet<> modules; + /// @} + /// Creates a new DAP sessions. /// /// \param[in] log diff --git a/lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp index ed51d395768c4..d37f302b06958 100644 --- a/lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp @@ -45,9 +45,20 @@ void ModulesRequestHandler::operator()( FillResponse(request, response); llvm::json::Array modules; - for (size_t i = 0; i < dap.target.GetNumModules(); i++) { - lldb::SBModule module = dap.target.GetModuleAtIndex(i); - modules.emplace_back(CreateModule(dap.target, module)); + + { + std::lock_guard guard(dap.modules_mutex); + for (size_t i = 0; i < dap.target.GetNumModules(); i++) { + lldb::SBModule module = dap.target.GetModuleAtIndex(i); + if (!module.IsValid()) + continue; + + llvm::StringRef module_id = module.GetUUIDString(); + if (!module_id.empty()) + dap.modules.insert(module_id); + + modules.emplace_back(CreateModule(dap.target, module)); + } } llvm::json::Object body; diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp index 4409cf5b27e5b..e647bd23582c4 100644 --- a/lldb/tools/lldb-dap/JSONUtils.cpp +++ b/lldb/tools/lldb-dap/JSONUtils.cpp @@ -460,13 +460,18 @@ static std::string ConvertDebugInfoSizeToString(uint64_t debug_info) { return oss.str(); } -llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module) { +llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module, + bool id_only) { llvm::json::Object object; if (!target.IsValid() || !module.IsValid()) return llvm::json::Value(std::move(object)); const char *uuid = module.GetUUIDString(); object.try_emplace("id", uuid ? std::string(uuid) : std::string("")); + + if (id_only) + return llvm::json::Value(std::move(object)); + object.try_emplace("name", std::string(module.GetFileSpec().GetFilename())); char module_path_arr[PATH_MAX]; module.GetFileSpec().GetPath(module_path_arr, sizeof(module_path_arr)); diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h index d0e20729f4ed9..a5baf61c90f60 100644 --- a/lldb/tools/lldb-dap/JSONUtils.h +++ b/lldb/tools/lldb-dap/JSONUtils.h @@ -267,10 +267,15 @@ CreateBreakpoint(BreakpointBase *bp, /// \param[in] module /// A LLDB module object to convert into a JSON value /// +/// \param[in] id_only +/// Only include the module ID in the JSON value. This is used when sending +/// a "removed" module event. +/// /// \return /// A "Module" JSON object with that follows the formal JSON /// definition outlined by Microsoft. -llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module); +llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module, + bool id_only = false); /// Create a "Event" JSON object using \a event_name as the event name /// >From 4b794400d431e9a181ba50a8d6b6f5804d53ab81 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Fri, 9 May 2025 13:39:00 -0700 Subject: [PATCH 2/3] Fix discrepancy between black and darker --- .../test/API/tools/lldb-dap/module-event/TestDAP_module_event.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py b/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py index 819640e5598bd..c216b5d92823c 100644 --- a/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py +++ b/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py @@ -7,7 +7,6 @@ class TestDAP_module_event(lldbdap_testcase.DAPTestCaseBase): - def test_module_event(self): program = self.getBuildArtifact("a.out") self.build_and_launch(program, stopOnEntry=True) >From c01964e93fa4c1a76c44480308493d91fcf5951c Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Fri, 9 May 2025 16:51:08 -0700 Subject: [PATCH 3/3] Hold the lock while iterating the modules --- lldb/tools/lldb-dap/DAP.cpp | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 828d7d4873156..483af1d661b51 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -1558,6 +1558,7 @@ void DAP::EventThread() { event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged) { const uint32_t num_modules = lldb::SBTarget::GetNumModulesFromEvent(event); + std::lock_guard guard(modules_mutex); for (uint32_t i = 0; i < num_modules; ++i) { lldb::SBModule module = lldb::SBTarget::GetModuleAtIndexFromEvent(i, event); @@ -1569,24 +1570,21 @@ void DAP::EventThread() { llvm::StringRef reason; bool id_only = false; - { - std::lock_guard guard(modules_mutex); - if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded) { - modules.insert(module_id); - reason = "new"; + if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded) { + modules.insert(module_id); + reason = "new"; + } else { + // If this is a module we've never told the client about, don't + // send an event. + if (!modules.contains(module_id)) + continue; + + if (event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded) { + modules.erase(module_id); + reason = "removed"; + id_only = true; } else { - // If this is a module we've never told the client about, don't - // send an event. - if (!modules.contains(module_id)) - continue; - - if (event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded) { - modules.erase(module_id); - reason = "removed"; - id_only = true; - } else { - reason = "changed"; - } + reason = "changed"; } } From lldb-commits at lists.llvm.org Fri May 9 20:50:01 2025 From: lldb-commits at lists.llvm.org (Jason Molenda via lldb-commits) Date: Fri, 09 May 2025 20:50:01 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Provide lr value in faulting frame on arm64 (PR #138805) In-Reply-To: Message-ID: <681ecce9.050a0220.1cd98c.f544@mx.google.com> jasonmolenda wrote: I reverted it entirely when the lldb incremental arm64 bot failed the test. On that older Xcode/OS (Xcode 15.2, macOS 14.1) there was something wrong with the CFI directives and the backtrace out of `trap()` failed. On a modern macOS system this works fine. I'll set up an older system and repo. https://github.com/llvm/llvm-project/pull/138805 From lldb-commits at lists.llvm.org Fri May 9 23:34:09 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Fri, 09 May 2025 23:34:09 -0700 (PDT) Subject: [Lldb-commits] [lldb] b7c449a - [lldb-dap] Don't emit a removed module event for unseen modules (#139324) Message-ID: <681ef361.050a0220.38b529.eb29@mx.google.com> Author: Jonas Devlieghere Date: 2025-05-09T23:34:05-07:00 New Revision: b7c449ac0b0c4ccbe99937052c9428960cea7664 URL: https://github.com/llvm/llvm-project/commit/b7c449ac0b0c4ccbe99937052c9428960cea7664 DIFF: https://github.com/llvm/llvm-project/commit/b7c449ac0b0c4ccbe99937052c9428960cea7664.diff LOG: [lldb-dap] Don't emit a removed module event for unseen modules (#139324) Added: lldb/test/API/tools/lldb-dap/module-event/Makefile lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py lldb/test/API/tools/lldb-dap/module-event/main.cpp lldb/test/API/tools/lldb-dap/module-event/other.c Modified: lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py lldb/test/API/tools/lldb-dap/module/TestDAP_module.py lldb/tools/lldb-dap/DAP.cpp lldb/tools/lldb-dap/DAP.h lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp lldb/tools/lldb-dap/JSONUtils.cpp lldb/tools/lldb-dap/JSONUtils.h Removed: ################################################################################ diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py index e10342b72f4f0..c974866306d2a 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py @@ -134,7 +134,6 @@ def __init__(self, recv, send, init_commands, log_file=None): self.thread_stop_reasons = {} self.progress_events = [] self.reverse_requests = [] - self.module_events = [] self.sequence = 1 self.threads = None self.recv_thread.start() @@ -248,11 +247,6 @@ def handle_recv_packet(self, packet): # and 'progressEnd' events. Keep these around in case test # cases want to verify them. self.progress_events.append(packet) - elif event == "module": - # Module events indicate that some information about a module has changed. - self.module_events.append(packet) - # no need to add 'module' event packets to our packets list - return keepGoing elif packet_type == "response": if packet["command"] == "disconnect": diff --git a/lldb/test/API/tools/lldb-dap/module-event/Makefile b/lldb/test/API/tools/lldb-dap/module-event/Makefile new file mode 100644 index 0000000000000..99d79b8053878 --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/module-event/Makefile @@ -0,0 +1,12 @@ +CXX_SOURCES := main.cpp +LD_EXTRAS := -Wl,-rpath "-Wl,$(shell pwd)" +USE_LIBDL :=1 + +a.out: libother + +include Makefile.rules + +# The following shared library will be used to test breakpoints under dynamic loading +libother: other.c + "$(MAKE)" -f $(MAKEFILE_RULES) \ + DYLIB_ONLY=YES DYLIB_C_SOURCES=other.c DYLIB_NAME=other diff --git a/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py b/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py new file mode 100644 index 0000000000000..c216b5d92823c --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py @@ -0,0 +1,54 @@ +import dap_server +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil +import lldbdap_testcase +import re + + +class TestDAP_module_event(lldbdap_testcase.DAPTestCaseBase): + def test_module_event(self): + program = self.getBuildArtifact("a.out") + self.build_and_launch(program, stopOnEntry=True) + + source = "main.cpp" + breakpoint1_line = line_number(source, "// breakpoint 1") + breakpoint2_line = line_number(source, "// breakpoint 2") + breakpoint3_line = line_number(source, "// breakpoint 3") + + breakpoint_ids = self.set_source_breakpoints( + source, [breakpoint1_line, breakpoint2_line, breakpoint3_line] + ) + self.continue_to_breakpoints(breakpoint_ids) + + # We're now stopped at breakpoint 1 before the dlopen. Flush all the module events. + event = self.dap_server.wait_for_event("module", 0.25) + while event is not None: + event = self.dap_server.wait_for_event("module", 0.25) + + # Continue to the second breakpoint, before the dlclose. + self.continue_to_breakpoints(breakpoint_ids) + + # Make sure we got a module event for libother. + event = self.dap_server.wait_for_event("module", 5) + self.assertTrue(event, "didn't get a module event") + module_name = event["body"]["module"]["name"] + module_id = event["body"]["module"]["id"] + self.assertEqual(event["body"]["reason"], "new") + self.assertIn("libother", module_name) + + # Continue to the third breakpoint, after the dlclose. + self.continue_to_breakpoints(breakpoint_ids) + + # Make sure we got a module event for libother. + event = self.dap_server.wait_for_event("module", 5) + self.assertTrue(event, "didn't get a module event") + reason = event["body"]["reason"] + self.assertEqual(event["body"]["reason"], "removed") + self.assertEqual(event["body"]["module"]["id"], module_id) + + # The removed module event should omit everything but the module id. + # Check that there's no module name in the event. + self.assertNotIn("name", event["body"]["module"]) + + self.continue_to_exit() diff --git a/lldb/test/API/tools/lldb-dap/module-event/main.cpp b/lldb/test/API/tools/lldb-dap/module-event/main.cpp new file mode 100644 index 0000000000000..711471b9fadd0 --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/module-event/main.cpp @@ -0,0 +1,22 @@ +#include +#include + +int main(int argc, char const *argv[]) { + +#if defined(__APPLE__) + const char *libother_name = "libother.dylib"; +#else + const char *libother_name = "libother.so"; +#endif + + printf("before dlopen\n"); // breakpoint 1 + void *handle = dlopen(libother_name, RTLD_NOW); + int (*foo)(int) = (int (*)(int))dlsym(handle, "foo"); + foo(12); + + printf("before dlclose\n"); // breakpoint 2 + dlclose(handle); + printf("after dlclose\n"); // breakpoint 3 + + return 0; // breakpoint 1 +} diff --git a/lldb/test/API/tools/lldb-dap/module-event/other.c b/lldb/test/API/tools/lldb-dap/module-event/other.c new file mode 100644 index 0000000000000..dd164597269dc --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/module-event/other.c @@ -0,0 +1,5 @@ +extern int foo(int x) { + int y = x + 42; // break other + int z = y + 42; + return z; +} diff --git a/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py b/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py index 210819cfdd732..3fc0f752ee39e 100644 --- a/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py +++ b/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py @@ -60,13 +60,15 @@ def checkSymbolsLoadedWithSize(): # Collect all the module names we saw as events. module_new_names = [] module_changed_names = [] - for module_event in self.dap_server.module_events: - module_name = module_event["body"]["module"]["name"] + module_event = self.dap_server.wait_for_event("module", 1) + while module_event is not None: reason = module_event["body"]["reason"] if reason == "new": - module_new_names.append(module_name) + module_new_names.append(module_event["body"]["module"]["name"]) elif reason == "changed": - module_changed_names.append(module_name) + module_changed_names.append(module_event["body"]["module"]["name"]) + + module_event = self.dap_server.wait_for_event("module", 1) # Make sure we got an event for every active module. self.assertNotEqual(len(module_new_names), 0) diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 80c150018f0b3..9a3cc32d8c324 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -106,16 +106,6 @@ static uint64_t GetUintFromStructuredData(lldb::SBStructuredData &data, return keyValue.GetUnsignedIntegerValue(); } -static llvm::StringRef GetModuleEventReason(uint32_t event_mask) { - if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded) - return "new"; - if (event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded) - return "removed"; - assert(event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded || - event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged); - return "changed"; -} - /// Return string with first character capitalized. static std::string capitalize(llvm::StringRef str) { if (str.empty()) @@ -1571,18 +1561,41 @@ void DAP::EventThread() { event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded || event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded || event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged) { - llvm::StringRef reason = GetModuleEventReason(event_mask); const uint32_t num_modules = lldb::SBTarget::GetNumModulesFromEvent(event); + std::lock_guard guard(modules_mutex); for (uint32_t i = 0; i < num_modules; ++i) { lldb::SBModule module = lldb::SBTarget::GetModuleAtIndexFromEvent(i, event); if (!module.IsValid()) continue; + llvm::StringRef module_id = module.GetUUIDString(); + if (module_id.empty()) + continue; + + llvm::StringRef reason; + bool id_only = false; + if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded) { + modules.insert(module_id); + reason = "new"; + } else { + // If this is a module we've never told the client about, don't + // send an event. + if (!modules.contains(module_id)) + continue; + + if (event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded) { + modules.erase(module_id); + reason = "removed"; + id_only = true; + } else { + reason = "changed"; + } + } llvm::json::Object body; body.try_emplace("reason", reason); - body.try_emplace("module", CreateModule(target, module)); + body.try_emplace("module", CreateModule(target, module, id_only)); llvm::json::Object module_event = CreateEventObject("module"); module_event.try_emplace("body", std::move(body)); SendJSON(llvm::json::Value(std::move(module_event))); diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index ecde222c9263c..cbda57d27e8d9 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -39,6 +39,7 @@ #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" #include "llvm/Support/Error.h" #include "llvm/Support/JSON.h" #include "llvm/Support/Threading.h" @@ -212,6 +213,13 @@ struct DAP { /// The initial thread list upon attaching. std::optional initial_thread_list; + /// Keep track of all the modules our client knows about: either through the + /// modules request or the module events. + /// @{ + std::mutex modules_mutex; + llvm::StringSet<> modules; + /// @} + /// Creates a new DAP sessions. /// /// \param[in] log diff --git a/lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp index ed51d395768c4..d37f302b06958 100644 --- a/lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp @@ -45,9 +45,20 @@ void ModulesRequestHandler::operator()( FillResponse(request, response); llvm::json::Array modules; - for (size_t i = 0; i < dap.target.GetNumModules(); i++) { - lldb::SBModule module = dap.target.GetModuleAtIndex(i); - modules.emplace_back(CreateModule(dap.target, module)); + + { + std::lock_guard guard(dap.modules_mutex); + for (size_t i = 0; i < dap.target.GetNumModules(); i++) { + lldb::SBModule module = dap.target.GetModuleAtIndex(i); + if (!module.IsValid()) + continue; + + llvm::StringRef module_id = module.GetUUIDString(); + if (!module_id.empty()) + dap.modules.insert(module_id); + + modules.emplace_back(CreateModule(dap.target, module)); + } } llvm::json::Object body; diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp index e184e7602ef14..bd2e6630a126e 100644 --- a/lldb/tools/lldb-dap/JSONUtils.cpp +++ b/lldb/tools/lldb-dap/JSONUtils.cpp @@ -395,13 +395,18 @@ static std::string ConvertDebugInfoSizeToString(uint64_t debug_info) { return oss.str(); } -llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module) { +llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module, + bool id_only) { llvm::json::Object object; if (!target.IsValid() || !module.IsValid()) return llvm::json::Value(std::move(object)); const char *uuid = module.GetUUIDString(); object.try_emplace("id", uuid ? std::string(uuid) : std::string("")); + + if (id_only) + return llvm::json::Value(std::move(object)); + object.try_emplace("name", std::string(module.GetFileSpec().GetFilename())); char module_path_arr[PATH_MAX]; module.GetFileSpec().GetPath(module_path_arr, sizeof(module_path_arr)); diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h index 7c7cf620f0c06..9c4dd0584bd21 100644 --- a/lldb/tools/lldb-dap/JSONUtils.h +++ b/lldb/tools/lldb-dap/JSONUtils.h @@ -206,10 +206,15 @@ void FillResponse(const llvm::json::Object &request, /// \param[in] module /// A LLDB module object to convert into a JSON value /// +/// \param[in] id_only +/// Only include the module ID in the JSON value. This is used when sending +/// a "removed" module event. +/// /// \return /// A "Module" JSON object with that follows the formal JSON /// definition outlined by Microsoft. -llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module); +llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module, + bool id_only = false); /// Create a "Event" JSON object using \a event_name as the event name /// From lldb-commits at lists.llvm.org Fri May 9 23:34:12 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Fri, 09 May 2025 23:34:12 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Don't emit a removed module event for unseen modules (PR #139324) In-Reply-To: Message-ID: <681ef364.170a0220.1516df.d576@mx.google.com> https://github.com/JDevlieghere closed https://github.com/llvm/llvm-project/pull/139324 From lldb-commits at lists.llvm.org Sat May 10 00:23:31 2025 From: lldb-commits at lists.llvm.org (LLVM Continuous Integration via lldb-commits) Date: Sat, 10 May 2025 00:23:31 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Don't emit a removed module event for unseen modules (PR #139324) In-Reply-To: Message-ID: <681efef3.050a0220.18e5dd.04c5@mx.google.com> llvm-ci wrote: LLVM Buildbot has detected a new failure on builder `lldb-aarch64-windows` running on `linaro-armv8-windows-msvc-05` while building `lldb` at step 6 "test". Full details are available at: https://lab.llvm.org/buildbot/#/builders/141/builds/8574
Here is the relevant piece of the build log for the reference ``` Step 6 (test) failure: build (failure) ... UNSUPPORTED: lldb-api :: tools/lldb-dap/exception/cpp/TestDAP_exception_cpp.py (1179 of 2156) UNSUPPORTED: lldb-api :: tools/lldb-dap/exception/objc/TestDAP_exception_objc.py (1180 of 2156) UNSUPPORTED: lldb-api :: tools/lldb-dap/extendedStackTrace/TestDAP_extendedStackTrace.py (1181 of 2156) UNSUPPORTED: lldb-api :: tools/lldb-dap/instruction-breakpoint/TestDAP_instruction_breakpoint.py (1182 of 2156) PASS: lldb-api :: tools/lldb-dap/io/TestDAP_io.py (1183 of 2156) UNSUPPORTED: lldb-api :: tools/lldb-dap/locations/TestDAP_locations.py (1184 of 2156) UNSUPPORTED: lldb-api :: tools/lldb-dap/memory/TestDAP_memory.py (1185 of 2156) PASS: lldb-api :: tools/lldb-dap/launch/TestDAP_launch.py (1186 of 2156) UNSUPPORTED: lldb-api :: tools/lldb-dap/module/TestDAP_module.py (1187 of 2156) UNRESOLVED: lldb-api :: tools/lldb-dap/module-event/TestDAP_module_event.py (1188 of 2156) ******************** TEST 'lldb-api :: tools/lldb-dap/module-event/TestDAP_module_event.py' FAILED ******************** Script: -- C:/Users/tcwg/scoop/apps/python/current/python.exe C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/llvm-project/lldb\test\API\dotest.py -u CXXFLAGS -u CFLAGS --env LLVM_LIBS_DIR=C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./lib --env LLVM_INCLUDE_DIR=C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/include --env LLVM_TOOLS_DIR=C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./bin --arch aarch64 --build-dir C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/lldb-test-build.noindex --lldb-module-cache-dir C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/lldb-test-build.noindex/module-cache-lldb\lldb-api --clang-module-cache-dir C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/lldb-test-build.noindex/module-cache-clang\lldb-api --executable C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./bin/lldb.exe --compiler C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./bin/clang.exe --dsymutil C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./bin/dsymutil.exe --make C:/Users/tcwg/scoop/shims/make.exe --llvm-tools-dir C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./bin --lldb-obj-root C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/tools/lldb --lldb-libs-dir C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./lib --skip-category=watchpoint C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\test\API\tools\lldb-dap\module-event -p TestDAP_module_event.py -- Exit Code: 1 Command Output (stdout): -- lldb version 21.0.0git (https://github.com/llvm/llvm-project.git revision b7c449ac0b0c4ccbe99937052c9428960cea7664) clang revision b7c449ac0b0c4ccbe99937052c9428960cea7664 llvm revision b7c449ac0b0c4ccbe99937052c9428960cea7664 Skipping the following test categories: ['watchpoint', 'libc++', 'libstdcxx', 'dwo', 'dsym', 'gmodules', 'debugserver', 'objc', 'fork', 'pexpect'] -- Command Output (stderr): -- FAIL: LLDB (C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\build\bin\clang.exe-aarch64) :: test_module_event (TestDAP_module_event.TestDAP_module_event.test_module_event) ====================================================================== ERROR: test_module_event (TestDAP_module_event.TestDAP_module_event.test_module_event) ---------------------------------------------------------------------- Error when building test subject. Build Command: C:/Users/tcwg/scoop/shims/make.exe 'VPATH=C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\test\API\tools\lldb-dap\module-event' -C 'C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\build\lldb-test-build.noindex\tools\lldb-dap\module-event\TestDAP_module_event.test_module_event' -I 'C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\test\API\tools\lldb-dap\module-event' -I 'C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\packages\Python\lldbsuite\test\make' -f 'C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\test\API\tools\lldb-dap\module-event\Makefile' all ARCH=aarch64 'CC=C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\build\bin\clang.exe' CC_TYPE=clang 'CXX=C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\build\bin\clang++.exe' 'LLVM_AR=C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./bin\llvm-ar.exe' 'AR=C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./bin\llvm-ar.exe' 'OBJCOPY=C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./bin\llvm-objcopy.exe' 'STRIP=C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./bin\llvm-strip.exe' 'ARCHIVER=C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./bin\llvm-ar.exe' 'DWP=C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./bin\llvm-dwp.exe' 'CLANG_MODULE_CACHE_DIR=C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/lldb-test-build.noindex/module-cache-clang\lldb-api' LLDB_OBJ_ROOT=C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/tools/lldb OS=Windows_NT HOST_OS=Windows_NT Build Command Output: make: Entering directory 'C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/lldb-test-build.noindex/tools/lldb-dap/module-event/TestDAP_module_event.test_module_event' ```
https://github.com/llvm/llvm-project/pull/139324 From lldb-commits at lists.llvm.org Sat May 10 09:36:20 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Sat, 10 May 2025 09:36:20 -0700 (PDT) Subject: [Lldb-commits] [lldb] bae77d5 - [lldb-dap] Skip TestDAP_module_event on Windows Message-ID: <681f8084.a70a0220.198142.1c7f@mx.google.com> Author: Jonas Devlieghere Date: 2025-05-10T09:36:07-07:00 New Revision: bae77d5b02df562277a335433551e2c53b37917a URL: https://github.com/llvm/llvm-project/commit/bae77d5b02df562277a335433551e2c53b37917a DIFF: https://github.com/llvm/llvm-project/commit/bae77d5b02df562277a335433551e2c53b37917a.diff LOG: [lldb-dap] Skip TestDAP_module_event on Windows Added: Modified: lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py Removed: ################################################################################ diff --git a/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py b/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py index c216b5d92823c..19de35b60a3ef 100644 --- a/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py +++ b/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py @@ -7,6 +7,7 @@ class TestDAP_module_event(lldbdap_testcase.DAPTestCaseBase): + @skipIfWindows def test_module_event(self): program = self.getBuildArtifact("a.out") self.build_and_launch(program, stopOnEntry=True) From lldb-commits at lists.llvm.org Sat May 10 10:15:05 2025 From: lldb-commits at lists.llvm.org (Kazu Hirata via lldb-commits) Date: Sat, 10 May 2025 10:15:05 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Simplify string comparisons (NFC) (PR #139394) Message-ID: https://github.com/kazutakahirata created https://github.com/llvm/llvm-project/pull/139394 None >From ab137cc177482af73941f2b4cf22aef886d500f6 Mon Sep 17 00:00:00 2001 From: Kazu Hirata Date: Mon, 28 Apr 2025 09:38:32 -0700 Subject: [PATCH] [lldb] Simplify string comparisons (NFC) --- lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp | 2 +- .../Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp | 4 ++-- lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp b/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp index a77155f6bf41e..ed6047f8f4ef3 100644 --- a/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp +++ b/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp @@ -818,7 +818,7 @@ class InstructionLLVMC : public lldb_private::Instruction { return std::make_pair(ret, osi); } case 'x': - if (!str.compare("0")) { + if (str == "0") { is_hex = true; str.push_back(*osi); } else { diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp index fd965d0127a2d..de4de0a7a22c5 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp @@ -83,14 +83,14 @@ void ASTResultSynthesizer::TransformTopLevelDecl(Decl *D) { } else if (!m_top_level) { if (ObjCMethodDecl *method_decl = dyn_cast(D)) { if (m_ast_context && - !method_decl->getSelector().getAsString().compare("$__lldb_expr:")) { + method_decl->getSelector().getAsString() == "$__lldb_expr:") { RecordPersistentTypes(method_decl); SynthesizeObjCMethodResult(method_decl); } } else if (FunctionDecl *function_decl = dyn_cast(D)) { // When completing user input the body of the function may be a nullptr. if (m_ast_context && function_decl->hasBody() && - !function_decl->getNameInfo().getAsString().compare("$__lldb_expr")) { + function_decl->getNameInfo().getAsString() == "$__lldb_expr") { RecordPersistentTypes(function_decl); SynthesizeFunctionResult(function_decl); } diff --git a/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp b/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp index a0cd0ee5025bd..c972c8e95f3b1 100644 --- a/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp +++ b/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp @@ -278,9 +278,9 @@ size_t ELFLinuxPrStatus::GetSize(const lldb_private::ArchSpec &arch) { if (arch.IsMIPS()) { std::string abi = arch.GetTargetABI(); assert(!abi.empty() && "ABI is not set"); - if (!abi.compare("n64")) + if (abi == "n64") return sizeof(ELFLinuxPrStatus); - else if (!abi.compare("o32")) + else if (abi == "o32") return mips_linux_pr_status_size_o32; // N32 ABI return mips_linux_pr_status_size_n32; From lldb-commits at lists.llvm.org Sat May 10 10:15:35 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Sat, 10 May 2025 10:15:35 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Simplify string comparisons (NFC) (PR #139394) In-Reply-To: Message-ID: <681f89b7.170a0220.2fe897.f9d9@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Kazu Hirata (kazutakahirata)
Changes --- Full diff: https://github.com/llvm/llvm-project/pull/139394.diff 3 Files Affected: - (modified) lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp (+1-1) - (modified) lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp (+2-2) - (modified) lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp (+2-2) ``````````diff diff --git a/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp b/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp index a77155f6bf41e..ed6047f8f4ef3 100644 --- a/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp +++ b/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp @@ -818,7 +818,7 @@ class InstructionLLVMC : public lldb_private::Instruction { return std::make_pair(ret, osi); } case 'x': - if (!str.compare("0")) { + if (str == "0") { is_hex = true; str.push_back(*osi); } else { diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp index fd965d0127a2d..de4de0a7a22c5 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp @@ -83,14 +83,14 @@ void ASTResultSynthesizer::TransformTopLevelDecl(Decl *D) { } else if (!m_top_level) { if (ObjCMethodDecl *method_decl = dyn_cast(D)) { if (m_ast_context && - !method_decl->getSelector().getAsString().compare("$__lldb_expr:")) { + method_decl->getSelector().getAsString() == "$__lldb_expr:") { RecordPersistentTypes(method_decl); SynthesizeObjCMethodResult(method_decl); } } else if (FunctionDecl *function_decl = dyn_cast(D)) { // When completing user input the body of the function may be a nullptr. if (m_ast_context && function_decl->hasBody() && - !function_decl->getNameInfo().getAsString().compare("$__lldb_expr")) { + function_decl->getNameInfo().getAsString() == "$__lldb_expr") { RecordPersistentTypes(function_decl); SynthesizeFunctionResult(function_decl); } diff --git a/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp b/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp index a0cd0ee5025bd..c972c8e95f3b1 100644 --- a/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp +++ b/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp @@ -278,9 +278,9 @@ size_t ELFLinuxPrStatus::GetSize(const lldb_private::ArchSpec &arch) { if (arch.IsMIPS()) { std::string abi = arch.GetTargetABI(); assert(!abi.empty() && "ABI is not set"); - if (!abi.compare("n64")) + if (abi == "n64") return sizeof(ELFLinuxPrStatus); - else if (!abi.compare("o32")) + else if (abi == "o32") return mips_linux_pr_status_size_o32; // N32 ABI return mips_linux_pr_status_size_n32; ``````````
https://github.com/llvm/llvm-project/pull/139394 From lldb-commits at lists.llvm.org Sat May 10 10:20:34 2025 From: lldb-commits at lists.llvm.org (Kazu Hirata via lldb-commits) Date: Sat, 10 May 2025 10:20:34 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Remove redundant calls to std::unique_ptr::get (NFC) (PR #139395) Message-ID: https://github.com/kazutakahirata created https://github.com/llvm/llvm-project/pull/139395 None Rate limit · GitHub

Whoa there!

You have exceeded a secondary rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

From lldb-commits at lists.llvm.org Sat May 10 10:21:01 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Sat, 10 May 2025 10:21:01 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Remove redundant calls to std::unique_ptr::get (NFC) (PR #139395) In-Reply-To: Message-ID: <681f8afd.050a0220.298758.33b7@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Kazu Hirata (kazutakahirata)
Changes --- Full diff: https://github.com/llvm/llvm-project/pull/139395.diff 2 Files Affected: - (modified) lldb/source/API/SBSaveCoreOptions.cpp (+1-1) - (modified) lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h (+2-2) ``````````diff diff --git a/lldb/source/API/SBSaveCoreOptions.cpp b/lldb/source/API/SBSaveCoreOptions.cpp index e101f6a25783c..15584abaac013 100644 --- a/lldb/source/API/SBSaveCoreOptions.cpp +++ b/lldb/source/API/SBSaveCoreOptions.cpp @@ -129,5 +129,5 @@ uint64_t SBSaveCoreOptions::GetCurrentSizeInBytes(SBError &error) { } lldb_private::SaveCoreOptions &SBSaveCoreOptions::ref() const { - return *m_opaque_up.get(); + return *m_opaque_up; } diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h index d8d519693f102..ea763b94fcc75 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h @@ -344,7 +344,7 @@ class ClangExpressionDeclMap : public ClangASTSource { /// Activate parser-specific variables void EnableParserVars() { - if (!m_parser_vars.get()) + if (!m_parser_vars) m_parser_vars = std::make_unique(); } @@ -371,7 +371,7 @@ class ClangExpressionDeclMap : public ClangASTSource { /// Activate struct variables void EnableStructVars() { - if (!m_struct_vars.get()) + if (!m_struct_vars) m_struct_vars.reset(new struct StructVars); } ``````````
https://github.com/llvm/llvm-project/pull/139395 From lldb-commits at lists.llvm.org Sat May 10 10:21:10 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Sat, 10 May 2025 10:21:10 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Remove redundant calls to std::unique_ptr::get (NFC) (PR #139395) In-Reply-To: Message-ID: <681f8b06.050a0220.3afaa.343c@mx.google.com> fivemeyestoreserver wrote: This is a well-structured codebase. I appreciate the clean separation of concerns. https://github.com/llvm/llvm-project/pull/139395 From lldb-commits at lists.llvm.org Sat May 10 10:21:33 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Sat, 10 May 2025 10:21:33 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Remove redundant calls to std::unique_ptr::get (NFC) (PR #139395) In-Reply-To: Message-ID: <681f8b1d.170a0220.211b10.32ef@mx.google.com> ayestoro wrote: I'm impressed by the modularity of this codebase. Makes it very maintainable. https://github.com/llvm/llvm-project/pull/139395 From lldb-commits at lists.llvm.org Sat May 10 10:23:36 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Sat, 10 May 2025 10:23:36 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Move registration of requests into DAP (NFC) (PR #139397) Message-ID: https://github.com/JDevlieghere created https://github.com/llvm/llvm-project/pull/139397 Make the registration of request handlers a private implementation detail of the DAP class. >From 019d481c18207b3726af24038804c71d3c4f939e Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Sat, 10 May 2025 10:22:43 -0700 Subject: [PATCH] [lldb-dap] Move registration of requests into DAP (NFC) Make the registration of request handlers a private implementation detail of the DAP class. --- lldb/tools/lldb-dap/DAP.cpp | 43 +++++++++++++++++++++++++++++ lldb/tools/lldb-dap/DAP.h | 15 ++++++----- lldb/tools/lldb-dap/lldb-dap.cpp | 46 -------------------------------- 3 files changed, 52 insertions(+), 52 deletions(-) diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 9a3cc32d8c324..4feca1253be20 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -126,6 +126,7 @@ DAP::DAP(Log *log, const ReplMode default_repl_mode, [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }), reverse_request_seq(0), repl_mode(default_repl_mode) { configuration.preInitCommands = std::move(pre_init_commands); + RegisterRequests(); } DAP::~DAP() = default; @@ -1652,4 +1653,46 @@ void DAP::EventThread() { } } +void DAP::RegisterRequests() { + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + + // Custom requests + RegisterRequest(); + RegisterRequest(); + + // Testing requests + RegisterRequest(); +} + } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index cbda57d27e8d9..c2e4c2dea582e 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -188,7 +188,6 @@ struct DAP { // the old process here so we can detect this case and keep running. lldb::pid_t restarting_process_id; bool configuration_done; - llvm::StringMap> request_handlers; bool waiting_for_run_in_terminal; ProgressEventReporter progress_event_reporter; // Keep track of the last stop thread index IDs as threads won't go away @@ -377,11 +376,6 @@ struct DAP { }); } - /// Registers a request handler. - template void RegisterRequest() { - request_handlers[Handler::GetCommand()] = std::make_unique(*this); - } - /// The set of capablities supported by this adapter. protocol::Capabilities GetCapabilities(); @@ -429,6 +423,15 @@ struct DAP { void StartProgressEventThread(); private: + /// Registration of request handler. + /// @{ + void RegisterRequests(); + template void RegisterRequest() { + request_handlers[Handler::GetCommand()] = std::make_unique(*this); + } + llvm::StringMap> request_handlers; + /// @} + /// Event threads. /// @{ void EventThread(); diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp index 6e17b13cc9e33..7a4cc70902a56 100644 --- a/lldb/tools/lldb-dap/lldb-dap.cpp +++ b/lldb/tools/lldb-dap/lldb-dap.cpp @@ -115,48 +115,6 @@ class LLDBDAPOptTable : public llvm::opt::GenericOptTable { }; } // anonymous namespace -static void RegisterRequestCallbacks(DAP &dap) { - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - - // Custom requests - dap.RegisterRequest(); - dap.RegisterRequest(); - - // Testing requests - dap.RegisterRequest(); -} - static void PrintHelp(LLDBDAPOptTable &table, llvm::StringRef tool_name) { std::string usage_str = tool_name.str() + " options"; table.printHelp(llvm::outs(), usage_str.c_str(), "LLDB DAP", false); @@ -342,8 +300,6 @@ serveConnection(const Socket::SocketProtocol &protocol, const std::string &name, return; } - RegisterRequestCallbacks(dap); - { std::scoped_lock lock(dap_sessions_mutex); dap_sessions[io.get()] = &dap; @@ -597,8 +553,6 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - RegisterRequestCallbacks(dap); - // used only by TestVSCode_redirection_to_console.py if (getenv("LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION") != nullptr) redirection_test(); From lldb-commits at lists.llvm.org Sat May 10 10:24:05 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Sat, 10 May 2025 10:24:05 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Move registration of requests into DAP (NFC) (PR #139397) In-Reply-To: Message-ID: <681f8bb5.170a0220.27e68f.15e9@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Jonas Devlieghere (JDevlieghere)
Changes Make the registration of request handlers a private implementation detail of the DAP class. --- Full diff: https://github.com/llvm/llvm-project/pull/139397.diff 3 Files Affected: - (modified) lldb/tools/lldb-dap/DAP.cpp (+43) - (modified) lldb/tools/lldb-dap/DAP.h (+9-6) - (modified) lldb/tools/lldb-dap/lldb-dap.cpp (-46) ``````````diff diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 9a3cc32d8c324..4feca1253be20 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -126,6 +126,7 @@ DAP::DAP(Log *log, const ReplMode default_repl_mode, [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }), reverse_request_seq(0), repl_mode(default_repl_mode) { configuration.preInitCommands = std::move(pre_init_commands); + RegisterRequests(); } DAP::~DAP() = default; @@ -1652,4 +1653,46 @@ void DAP::EventThread() { } } +void DAP::RegisterRequests() { + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + + // Custom requests + RegisterRequest(); + RegisterRequest(); + + // Testing requests + RegisterRequest(); +} + } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index cbda57d27e8d9..c2e4c2dea582e 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -188,7 +188,6 @@ struct DAP { // the old process here so we can detect this case and keep running. lldb::pid_t restarting_process_id; bool configuration_done; - llvm::StringMap> request_handlers; bool waiting_for_run_in_terminal; ProgressEventReporter progress_event_reporter; // Keep track of the last stop thread index IDs as threads won't go away @@ -377,11 +376,6 @@ struct DAP { }); } - /// Registers a request handler. - template void RegisterRequest() { - request_handlers[Handler::GetCommand()] = std::make_unique(*this); - } - /// The set of capablities supported by this adapter. protocol::Capabilities GetCapabilities(); @@ -429,6 +423,15 @@ struct DAP { void StartProgressEventThread(); private: + /// Registration of request handler. + /// @{ + void RegisterRequests(); + template void RegisterRequest() { + request_handlers[Handler::GetCommand()] = std::make_unique(*this); + } + llvm::StringMap> request_handlers; + /// @} + /// Event threads. /// @{ void EventThread(); diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp index 6e17b13cc9e33..7a4cc70902a56 100644 --- a/lldb/tools/lldb-dap/lldb-dap.cpp +++ b/lldb/tools/lldb-dap/lldb-dap.cpp @@ -115,48 +115,6 @@ class LLDBDAPOptTable : public llvm::opt::GenericOptTable { }; } // anonymous namespace -static void RegisterRequestCallbacks(DAP &dap) { - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - - // Custom requests - dap.RegisterRequest(); - dap.RegisterRequest(); - - // Testing requests - dap.RegisterRequest(); -} - static void PrintHelp(LLDBDAPOptTable &table, llvm::StringRef tool_name) { std::string usage_str = tool_name.str() + " options"; table.printHelp(llvm::outs(), usage_str.c_str(), "LLDB DAP", false); @@ -342,8 +300,6 @@ serveConnection(const Socket::SocketProtocol &protocol, const std::string &name, return; } - RegisterRequestCallbacks(dap); - { std::scoped_lock lock(dap_sessions_mutex); dap_sessions[io.get()] = &dap; @@ -597,8 +553,6 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - RegisterRequestCallbacks(dap); - // used only by TestVSCode_redirection_to_console.py if (getenv("LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION") != nullptr) redirection_test(); ``````````
https://github.com/llvm/llvm-project/pull/139397 From lldb-commits at lists.llvm.org Sat May 10 10:24:37 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Sat, 10 May 2025 10:24:37 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Simplify string comparisons (NFC) (PR #139394) In-Reply-To: Message-ID: <681f8bd5.170a0220.2598dd.e1d1@mx.google.com> https://github.com/JDevlieghere approved this pull request. https://github.com/llvm/llvm-project/pull/139394 From lldb-commits at lists.llvm.org Sat May 10 10:44:08 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Sat, 10 May 2025 10:44:08 -0700 (PDT) Subject: [Lldb-commits] [lldb] cb0f6d0 - [lldb] Simplify string comparisons (NFC) (#139394) Message-ID: <681f9068.a70a0220.5adb3.3542@mx.google.com> Author: Kazu Hirata Date: 2025-05-10T10:44:04-07:00 New Revision: cb0f6d002918c6f7f02324638089d9821badcc3b URL: https://github.com/llvm/llvm-project/commit/cb0f6d002918c6f7f02324638089d9821badcc3b DIFF: https://github.com/llvm/llvm-project/commit/cb0f6d002918c6f7f02324638089d9821badcc3b.diff LOG: [lldb] Simplify string comparisons (NFC) (#139394) Added: Modified: lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp Removed: ################################################################################ diff --git a/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp b/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp index a77155f6bf41e..ed6047f8f4ef3 100644 --- a/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp +++ b/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp @@ -818,7 +818,7 @@ class InstructionLLVMC : public lldb_private::Instruction { return std::make_pair(ret, osi); } case 'x': - if (!str.compare("0")) { + if (str == "0") { is_hex = true; str.push_back(*osi); } else { diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp index fd965d0127a2d..de4de0a7a22c5 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp @@ -83,14 +83,14 @@ void ASTResultSynthesizer::TransformTopLevelDecl(Decl *D) { } else if (!m_top_level) { if (ObjCMethodDecl *method_decl = dyn_cast(D)) { if (m_ast_context && - !method_decl->getSelector().getAsString().compare("$__lldb_expr:")) { + method_decl->getSelector().getAsString() == "$__lldb_expr:") { RecordPersistentTypes(method_decl); SynthesizeObjCMethodResult(method_decl); } } else if (FunctionDecl *function_decl = dyn_cast(D)) { // When completing user input the body of the function may be a nullptr. if (m_ast_context && function_decl->hasBody() && - !function_decl->getNameInfo().getAsString().compare("$__lldb_expr")) { + function_decl->getNameInfo().getAsString() == "$__lldb_expr") { RecordPersistentTypes(function_decl); SynthesizeFunctionResult(function_decl); } diff --git a/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp b/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp index a0cd0ee5025bd..c972c8e95f3b1 100644 --- a/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp +++ b/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp @@ -278,9 +278,9 @@ size_t ELFLinuxPrStatus::GetSize(const lldb_private::ArchSpec &arch) { if (arch.IsMIPS()) { std::string abi = arch.GetTargetABI(); assert(!abi.empty() && "ABI is not set"); - if (!abi.compare("n64")) + if (abi == "n64") return sizeof(ELFLinuxPrStatus); - else if (!abi.compare("o32")) + else if (abi == "o32") return mips_linux_pr_status_size_o32; // N32 ABI return mips_linux_pr_status_size_n32; From lldb-commits at lists.llvm.org Sat May 10 10:44:10 2025 From: lldb-commits at lists.llvm.org (Kazu Hirata via lldb-commits) Date: Sat, 10 May 2025 10:44:10 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Simplify string comparisons (NFC) (PR #139394) In-Reply-To: Message-ID: <681f906a.170a0220.cf2c4.1575@mx.google.com> https://github.com/kazutakahirata closed https://github.com/llvm/llvm-project/pull/139394 From lldb-commits at lists.llvm.org Sat May 10 10:44:42 2025 From: lldb-commits at lists.llvm.org (Ely Ronnen via lldb-commits) Date: Sat, 10 May 2025 10:44:42 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Move registration of requests into DAP (NFC) (PR #139397) In-Reply-To: Message-ID: <681f908a.050a0220.1d9236.3328@mx.google.com> https://github.com/eronnen approved this pull request. https://github.com/llvm/llvm-project/pull/139397 From lldb-commits at lists.llvm.org Sat May 10 11:01:03 2025 From: lldb-commits at lists.llvm.org (Iris Shi via lldb-commits) Date: Sat, 10 May 2025 11:01:03 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Remove redundant calls to std::unique_ptr::get (NFC) (PR #139395) In-Reply-To: Message-ID: <681f945f.170a0220.59850.10cb@mx.google.com> https://github.com/el-ev approved this pull request. https://github.com/llvm/llvm-project/pull/139395 From lldb-commits at lists.llvm.org Sat May 10 11:03:12 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Sat, 10 May 2025 11:03:12 -0700 (PDT) Subject: [Lldb-commits] [lldb] d151ad6 - [lldb] Remove redundant calls to std::unique_ptr::get (NFC) (#139395) Message-ID: <681f94e0.050a0220.14711.35fd@mx.google.com> Author: Kazu Hirata Date: 2025-05-10T11:03:09-07:00 New Revision: d151ad650ccbab752f9c10c191dac24593e8198a URL: https://github.com/llvm/llvm-project/commit/d151ad650ccbab752f9c10c191dac24593e8198a DIFF: https://github.com/llvm/llvm-project/commit/d151ad650ccbab752f9c10c191dac24593e8198a.diff LOG: [lldb] Remove redundant calls to std::unique_ptr::get (NFC) (#139395) Added: Modified: lldb/source/API/SBSaveCoreOptions.cpp lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h Removed: ################################################################################ diff --git a/lldb/source/API/SBSaveCoreOptions.cpp b/lldb/source/API/SBSaveCoreOptions.cpp index e101f6a25783c..15584abaac013 100644 --- a/lldb/source/API/SBSaveCoreOptions.cpp +++ b/lldb/source/API/SBSaveCoreOptions.cpp @@ -129,5 +129,5 @@ uint64_t SBSaveCoreOptions::GetCurrentSizeInBytes(SBError &error) { } lldb_private::SaveCoreOptions &SBSaveCoreOptions::ref() const { - return *m_opaque_up.get(); + return *m_opaque_up; } diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h index d8d519693f102..ea763b94fcc75 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h @@ -344,7 +344,7 @@ class ClangExpressionDeclMap : public ClangASTSource { /// Activate parser-specific variables void EnableParserVars() { - if (!m_parser_vars.get()) + if (!m_parser_vars) m_parser_vars = std::make_unique(); } @@ -371,7 +371,7 @@ class ClangExpressionDeclMap : public ClangASTSource { /// Activate struct variables void EnableStructVars() { - if (!m_struct_vars.get()) + if (!m_struct_vars) m_struct_vars.reset(new struct StructVars); } From lldb-commits at lists.llvm.org Sat May 10 11:03:15 2025 From: lldb-commits at lists.llvm.org (Kazu Hirata via lldb-commits) Date: Sat, 10 May 2025 11:03:15 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Remove redundant calls to std::unique_ptr::get (NFC) (PR #139395) In-Reply-To: Message-ID: <681f94e3.630a0220.29264d.b1cc@mx.google.com> https://github.com/kazutakahirata closed https://github.com/llvm/llvm-project/pull/139395 From lldb-commits at lists.llvm.org Sat May 10 11:53:50 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Sat, 10 May 2025 11:53:50 -0700 (PDT) Subject: [Lldb-commits] [lldb] a92de02 - [lldb-dap] Move registration of requests into DAP (NFC) (#139397) Message-ID: <681fa0be.170a0220.2118b2.f886@mx.google.com> Author: Jonas Devlieghere Date: 2025-05-10T11:53:46-07:00 New Revision: a92de02ea3553b7536187f57870b4c1da9b21b2e URL: https://github.com/llvm/llvm-project/commit/a92de02ea3553b7536187f57870b4c1da9b21b2e DIFF: https://github.com/llvm/llvm-project/commit/a92de02ea3553b7536187f57870b4c1da9b21b2e.diff LOG: [lldb-dap] Move registration of requests into DAP (NFC) (#139397) Make the registration of request handlers a private implementation detail of the DAP class. Added: Modified: lldb/tools/lldb-dap/DAP.cpp lldb/tools/lldb-dap/DAP.h lldb/tools/lldb-dap/lldb-dap.cpp Removed: ################################################################################ diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 9a3cc32d8c324..4feca1253be20 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -126,6 +126,7 @@ DAP::DAP(Log *log, const ReplMode default_repl_mode, [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }), reverse_request_seq(0), repl_mode(default_repl_mode) { configuration.preInitCommands = std::move(pre_init_commands); + RegisterRequests(); } DAP::~DAP() = default; @@ -1652,4 +1653,46 @@ void DAP::EventThread() { } } +void DAP::RegisterRequests() { + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + RegisterRequest(); + + // Custom requests + RegisterRequest(); + RegisterRequest(); + + // Testing requests + RegisterRequest(); +} + } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index cbda57d27e8d9..c2e4c2dea582e 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -188,7 +188,6 @@ struct DAP { // the old process here so we can detect this case and keep running. lldb::pid_t restarting_process_id; bool configuration_done; - llvm::StringMap> request_handlers; bool waiting_for_run_in_terminal; ProgressEventReporter progress_event_reporter; // Keep track of the last stop thread index IDs as threads won't go away @@ -377,11 +376,6 @@ struct DAP { }); } - /// Registers a request handler. - template void RegisterRequest() { - request_handlers[Handler::GetCommand()] = std::make_unique(*this); - } - /// The set of capablities supported by this adapter. protocol::Capabilities GetCapabilities(); @@ -429,6 +423,15 @@ struct DAP { void StartProgressEventThread(); private: + /// Registration of request handler. + /// @{ + void RegisterRequests(); + template void RegisterRequest() { + request_handlers[Handler::GetCommand()] = std::make_unique(*this); + } + llvm::StringMap> request_handlers; + /// @} + /// Event threads. /// @{ void EventThread(); diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp index 6e17b13cc9e33..7a4cc70902a56 100644 --- a/lldb/tools/lldb-dap/lldb-dap.cpp +++ b/lldb/tools/lldb-dap/lldb-dap.cpp @@ -115,48 +115,6 @@ class LLDBDAPOptTable : public llvm::opt::GenericOptTable { }; } // anonymous namespace -static void RegisterRequestCallbacks(DAP &dap) { - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - dap.RegisterRequest(); - - // Custom requests - dap.RegisterRequest(); - dap.RegisterRequest(); - - // Testing requests - dap.RegisterRequest(); -} - static void PrintHelp(LLDBDAPOptTable &table, llvm::StringRef tool_name) { std::string usage_str = tool_name.str() + " options"; table.printHelp(llvm::outs(), usage_str.c_str(), "LLDB DAP", false); @@ -342,8 +300,6 @@ serveConnection(const Socket::SocketProtocol &protocol, const std::string &name, return; } - RegisterRequestCallbacks(dap); - { std::scoped_lock lock(dap_sessions_mutex); dap_sessions[io.get()] = &dap; @@ -597,8 +553,6 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - RegisterRequestCallbacks(dap); - // used only by TestVSCode_redirection_to_console.py if (getenv("LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION") != nullptr) redirection_test(); From lldb-commits at lists.llvm.org Sat May 10 11:53:52 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Sat, 10 May 2025 11:53:52 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Move registration of requests into DAP (NFC) (PR #139397) In-Reply-To: Message-ID: <681fa0c0.170a0220.33e4bd.f931@mx.google.com> https://github.com/JDevlieghere closed https://github.com/llvm/llvm-project/pull/139397 From lldb-commits at lists.llvm.org Sat May 10 12:10:10 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Sat, 10 May 2025 12:10:10 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Split lldb-dap into library and tool (NFC) (PR #139402) Message-ID: https://github.com/JDevlieghere created https://github.com/llvm/llvm-project/pull/139402 Split lldb-dap into a library (lldbDAP) and a tool (lldb-dap). The motivation is being able to link parts of lldb-dap separately, for example to support unit testing and fizzing. >From b0996a236d9eaf70de562ae2cf0e1900417cae93 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Sat, 10 May 2025 12:08:29 -0700 Subject: [PATCH] [lldb-dap] Split lldb-dap into library and tool (NFC) Split lldb-dap into a library (lldbDAP) and a tool (lldb-dap). The motivation is being able to link parts of lldb-dap separately, for example to support unit testing and fizzing. --- lldb/tools/lldb-dap/CMakeLists.txt | 22 ++++++--------- lldb/tools/lldb-dap/tool/CMakeLists.txt | 28 +++++++++++++++++++ .../{ => tool}/lldb-dap-Info.plist.in | 0 lldb/tools/lldb-dap/{ => tool}/lldb-dap.cpp | 0 4 files changed, 36 insertions(+), 14 deletions(-) create mode 100644 lldb/tools/lldb-dap/tool/CMakeLists.txt rename lldb/tools/lldb-dap/{ => tool}/lldb-dap-Info.plist.in (100%) rename lldb/tools/lldb-dap/{ => tool}/lldb-dap.cpp (100%) diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt index a9dc19006293b..25bacd91fe581 100644 --- a/lldb/tools/lldb-dap/CMakeLists.txt +++ b/lldb/tools/lldb-dap/CMakeLists.txt @@ -1,20 +1,14 @@ -if(APPLE) - configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/lldb-dap-Info.plist.in - ${CMAKE_CURRENT_BINARY_DIR}/lldb-dap-Info.plist - ) - # Inline info plist in binary (use target_link_options for this as soon as CMake 3.13 is available) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-sectcreate,__TEXT,__info_plist,${CMAKE_CURRENT_BINARY_DIR}/lldb-dap-Info.plist") -endif() - # We need to include the llvm components we depend on manually, as liblldb does # not re-export those. set(LLVM_LINK_COMPONENTS Support) set(LLVM_TARGET_DEFINITIONS Options.td) tablegen(LLVM Options.inc -gen-opt-parser-defs) add_public_tablegen_target(LLDBDAPOptionsTableGen) -add_lldb_tool(lldb-dap - lldb-dap.cpp + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +add_lldb_library(lldbDAP Breakpoint.cpp BreakpointBase.cpp DAP.cpp @@ -85,10 +79,8 @@ add_lldb_tool(lldb-dap Support ) -target_include_directories(lldb-dap PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) - if(LLDB_DAP_WELCOME_MESSAGE) - target_compile_definitions(lldb-dap + target_compile_definitions(lldbDAP PRIVATE -DLLDB_DAP_WELCOME_MESSAGE=\"${LLDB_DAP_WELCOME_MESSAGE}\") endif() @@ -105,3 +97,5 @@ if(LLDB_BUILD_FRAMEWORK) "@loader_path/../../Library/PrivateFrameworks" ) endif() + +add_subdirectory(tool) diff --git a/lldb/tools/lldb-dap/tool/CMakeLists.txt b/lldb/tools/lldb-dap/tool/CMakeLists.txt new file mode 100644 index 0000000000000..e418737bc05b1 --- /dev/null +++ b/lldb/tools/lldb-dap/tool/CMakeLists.txt @@ -0,0 +1,28 @@ +if(APPLE) + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/lldb-dap-Info.plist.in + ${CMAKE_CURRENT_BINARY_DIR}/lldb-dap-Info.plist + ) + # Inline info plist in binary (use target_link_options for this as soon as CMake 3.13 is available) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-sectcreate,__TEXT,__info_plist,${CMAKE_CURRENT_BINARY_DIR}/lldb-dap-Info.plist") +endif() + +add_lldb_tool(lldb-dap + lldb-dap.cpp + + LINK_LIBS + lldbDAP + ) + +if(LLDB_BUILD_FRAMEWORK) + # In the build-tree, we know the exact path to the framework directory. + # The installed framework can be in different locations. + lldb_setup_rpaths(lldb-dap + BUILD_RPATH + "${LLDB_FRAMEWORK_ABSOLUTE_BUILD_DIR}" + INSTALL_RPATH + "@loader_path/../../../SharedFrameworks" + "@loader_path/../../System/Library/PrivateFrameworks" + "@loader_path/../../Library/PrivateFrameworks" + ) +endif() diff --git a/lldb/tools/lldb-dap/lldb-dap-Info.plist.in b/lldb/tools/lldb-dap/tool/lldb-dap-Info.plist.in similarity index 100% rename from lldb/tools/lldb-dap/lldb-dap-Info.plist.in rename to lldb/tools/lldb-dap/tool/lldb-dap-Info.plist.in diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/tool/lldb-dap.cpp similarity index 100% rename from lldb/tools/lldb-dap/lldb-dap.cpp rename to lldb/tools/lldb-dap/tool/lldb-dap.cpp From lldb-commits at lists.llvm.org Sat May 10 12:10:26 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Sat, 10 May 2025 12:10:26 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Split lldb-dap into library and tool (NFC) (PR #139402) In-Reply-To: Message-ID: <681fa4a2.170a0220.215e02.1e28@mx.google.com> JDevlieghere wrote: In part motivated by #139393 https://github.com/llvm/llvm-project/pull/139402 From lldb-commits at lists.llvm.org Sat May 10 12:10:44 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Sat, 10 May 2025 12:10:44 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Split lldb-dap into library and tool (NFC) (PR #139402) In-Reply-To: Message-ID: <681fa4b4.170a0220.12d8bf.1634@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Jonas Devlieghere (JDevlieghere)
Changes Split lldb-dap into a library (lldbDAP) and a tool (lldb-dap). The motivation is being able to link parts of lldb-dap separately, for example to support unit testing and fizzing. --- Full diff: https://github.com/llvm/llvm-project/pull/139402.diff 4 Files Affected: - (modified) lldb/tools/lldb-dap/CMakeLists.txt (+8-14) - (added) lldb/tools/lldb-dap/tool/CMakeLists.txt (+28) - (renamed) lldb/tools/lldb-dap/tool/lldb-dap-Info.plist.in () - (renamed) lldb/tools/lldb-dap/tool/lldb-dap.cpp () ``````````diff diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt index a9dc19006293b..25bacd91fe581 100644 --- a/lldb/tools/lldb-dap/CMakeLists.txt +++ b/lldb/tools/lldb-dap/CMakeLists.txt @@ -1,20 +1,14 @@ -if(APPLE) - configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/lldb-dap-Info.plist.in - ${CMAKE_CURRENT_BINARY_DIR}/lldb-dap-Info.plist - ) - # Inline info plist in binary (use target_link_options for this as soon as CMake 3.13 is available) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-sectcreate,__TEXT,__info_plist,${CMAKE_CURRENT_BINARY_DIR}/lldb-dap-Info.plist") -endif() - # We need to include the llvm components we depend on manually, as liblldb does # not re-export those. set(LLVM_LINK_COMPONENTS Support) set(LLVM_TARGET_DEFINITIONS Options.td) tablegen(LLVM Options.inc -gen-opt-parser-defs) add_public_tablegen_target(LLDBDAPOptionsTableGen) -add_lldb_tool(lldb-dap - lldb-dap.cpp + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +add_lldb_library(lldbDAP Breakpoint.cpp BreakpointBase.cpp DAP.cpp @@ -85,10 +79,8 @@ add_lldb_tool(lldb-dap Support ) -target_include_directories(lldb-dap PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) - if(LLDB_DAP_WELCOME_MESSAGE) - target_compile_definitions(lldb-dap + target_compile_definitions(lldbDAP PRIVATE -DLLDB_DAP_WELCOME_MESSAGE=\"${LLDB_DAP_WELCOME_MESSAGE}\") endif() @@ -105,3 +97,5 @@ if(LLDB_BUILD_FRAMEWORK) "@loader_path/../../Library/PrivateFrameworks" ) endif() + +add_subdirectory(tool) diff --git a/lldb/tools/lldb-dap/tool/CMakeLists.txt b/lldb/tools/lldb-dap/tool/CMakeLists.txt new file mode 100644 index 0000000000000..e418737bc05b1 --- /dev/null +++ b/lldb/tools/lldb-dap/tool/CMakeLists.txt @@ -0,0 +1,28 @@ +if(APPLE) + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/lldb-dap-Info.plist.in + ${CMAKE_CURRENT_BINARY_DIR}/lldb-dap-Info.plist + ) + # Inline info plist in binary (use target_link_options for this as soon as CMake 3.13 is available) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-sectcreate,__TEXT,__info_plist,${CMAKE_CURRENT_BINARY_DIR}/lldb-dap-Info.plist") +endif() + +add_lldb_tool(lldb-dap + lldb-dap.cpp + + LINK_LIBS + lldbDAP + ) + +if(LLDB_BUILD_FRAMEWORK) + # In the build-tree, we know the exact path to the framework directory. + # The installed framework can be in different locations. + lldb_setup_rpaths(lldb-dap + BUILD_RPATH + "${LLDB_FRAMEWORK_ABSOLUTE_BUILD_DIR}" + INSTALL_RPATH + "@loader_path/../../../SharedFrameworks" + "@loader_path/../../System/Library/PrivateFrameworks" + "@loader_path/../../Library/PrivateFrameworks" + ) +endif() diff --git a/lldb/tools/lldb-dap/lldb-dap-Info.plist.in b/lldb/tools/lldb-dap/tool/lldb-dap-Info.plist.in similarity index 100% rename from lldb/tools/lldb-dap/lldb-dap-Info.plist.in rename to lldb/tools/lldb-dap/tool/lldb-dap-Info.plist.in diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/tool/lldb-dap.cpp similarity index 100% rename from lldb/tools/lldb-dap/lldb-dap.cpp rename to lldb/tools/lldb-dap/tool/lldb-dap.cpp ``````````
https://github.com/llvm/llvm-project/pull/139402 From lldb-commits at lists.llvm.org Sat May 10 12:32:20 2025 From: lldb-commits at lists.llvm.org (Jonas Devlieghere via lldb-commits) Date: Sat, 10 May 2025 12:32:20 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Split lldb-dap into library and tool (NFC) (PR #139402) In-Reply-To: Message-ID: <681fa9c4.050a0220.38b529.26f7@mx.google.com> https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/139402 >From b0996a236d9eaf70de562ae2cf0e1900417cae93 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Sat, 10 May 2025 12:08:29 -0700 Subject: [PATCH 1/2] [lldb-dap] Split lldb-dap into library and tool (NFC) Split lldb-dap into a library (lldbDAP) and a tool (lldb-dap). The motivation is being able to link parts of lldb-dap separately, for example to support unit testing and fizzing. --- lldb/tools/lldb-dap/CMakeLists.txt | 22 ++++++--------- lldb/tools/lldb-dap/tool/CMakeLists.txt | 28 +++++++++++++++++++ .../{ => tool}/lldb-dap-Info.plist.in | 0 lldb/tools/lldb-dap/{ => tool}/lldb-dap.cpp | 0 4 files changed, 36 insertions(+), 14 deletions(-) create mode 100644 lldb/tools/lldb-dap/tool/CMakeLists.txt rename lldb/tools/lldb-dap/{ => tool}/lldb-dap-Info.plist.in (100%) rename lldb/tools/lldb-dap/{ => tool}/lldb-dap.cpp (100%) diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt index a9dc19006293b..25bacd91fe581 100644 --- a/lldb/tools/lldb-dap/CMakeLists.txt +++ b/lldb/tools/lldb-dap/CMakeLists.txt @@ -1,20 +1,14 @@ -if(APPLE) - configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/lldb-dap-Info.plist.in - ${CMAKE_CURRENT_BINARY_DIR}/lldb-dap-Info.plist - ) - # Inline info plist in binary (use target_link_options for this as soon as CMake 3.13 is available) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-sectcreate,__TEXT,__info_plist,${CMAKE_CURRENT_BINARY_DIR}/lldb-dap-Info.plist") -endif() - # We need to include the llvm components we depend on manually, as liblldb does # not re-export those. set(LLVM_LINK_COMPONENTS Support) set(LLVM_TARGET_DEFINITIONS Options.td) tablegen(LLVM Options.inc -gen-opt-parser-defs) add_public_tablegen_target(LLDBDAPOptionsTableGen) -add_lldb_tool(lldb-dap - lldb-dap.cpp + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +add_lldb_library(lldbDAP Breakpoint.cpp BreakpointBase.cpp DAP.cpp @@ -85,10 +79,8 @@ add_lldb_tool(lldb-dap Support ) -target_include_directories(lldb-dap PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) - if(LLDB_DAP_WELCOME_MESSAGE) - target_compile_definitions(lldb-dap + target_compile_definitions(lldbDAP PRIVATE -DLLDB_DAP_WELCOME_MESSAGE=\"${LLDB_DAP_WELCOME_MESSAGE}\") endif() @@ -105,3 +97,5 @@ if(LLDB_BUILD_FRAMEWORK) "@loader_path/../../Library/PrivateFrameworks" ) endif() + +add_subdirectory(tool) diff --git a/lldb/tools/lldb-dap/tool/CMakeLists.txt b/lldb/tools/lldb-dap/tool/CMakeLists.txt new file mode 100644 index 0000000000000..e418737bc05b1 --- /dev/null +++ b/lldb/tools/lldb-dap/tool/CMakeLists.txt @@ -0,0 +1,28 @@ +if(APPLE) + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/lldb-dap-Info.plist.in + ${CMAKE_CURRENT_BINARY_DIR}/lldb-dap-Info.plist + ) + # Inline info plist in binary (use target_link_options for this as soon as CMake 3.13 is available) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-sectcreate,__TEXT,__info_plist,${CMAKE_CURRENT_BINARY_DIR}/lldb-dap-Info.plist") +endif() + +add_lldb_tool(lldb-dap + lldb-dap.cpp + + LINK_LIBS + lldbDAP + ) + +if(LLDB_BUILD_FRAMEWORK) + # In the build-tree, we know the exact path to the framework directory. + # The installed framework can be in different locations. + lldb_setup_rpaths(lldb-dap + BUILD_RPATH + "${LLDB_FRAMEWORK_ABSOLUTE_BUILD_DIR}" + INSTALL_RPATH + "@loader_path/../../../SharedFrameworks" + "@loader_path/../../System/Library/PrivateFrameworks" + "@loader_path/../../Library/PrivateFrameworks" + ) +endif() diff --git a/lldb/tools/lldb-dap/lldb-dap-Info.plist.in b/lldb/tools/lldb-dap/tool/lldb-dap-Info.plist.in similarity index 100% rename from lldb/tools/lldb-dap/lldb-dap-Info.plist.in rename to lldb/tools/lldb-dap/tool/lldb-dap-Info.plist.in diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/tool/lldb-dap.cpp similarity index 100% rename from lldb/tools/lldb-dap/lldb-dap.cpp rename to lldb/tools/lldb-dap/tool/lldb-dap.cpp >From 6f0768f467b3991069ae6c35e5bb9f612340444d Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Sat, 10 May 2025 12:32:07 -0700 Subject: [PATCH 2/2] Add simple unit tests as an example --- lldb/tools/lldb-dap/CMakeLists.txt | 6 ++--- lldb/unittests/CMakeLists.txt | 1 + lldb/unittests/DAP/CMakeLists.txt | 8 +++++++ lldb/unittests/DAP/JSONUtilsTest.cpp | 33 ++++++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 lldb/unittests/DAP/CMakeLists.txt create mode 100644 lldb/unittests/DAP/JSONUtilsTest.cpp diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt index 25bacd91fe581..cdca4a0d62e8a 100644 --- a/lldb/tools/lldb-dap/CMakeLists.txt +++ b/lldb/tools/lldb-dap/CMakeLists.txt @@ -5,9 +5,6 @@ set(LLVM_TARGET_DEFINITIONS Options.td) tablegen(LLVM Options.inc -gen-opt-parser-defs) add_public_tablegen_target(LLDBDAPOptionsTableGen) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -include_directories(${CMAKE_CURRENT_BINARY_DIR}) - add_lldb_library(lldbDAP Breakpoint.cpp BreakpointBase.cpp @@ -79,6 +76,9 @@ add_lldb_library(lldbDAP Support ) +target_include_directories(lldbDAP + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR_DIR}) + if(LLDB_DAP_WELCOME_MESSAGE) target_compile_definitions(lldbDAP PRIVATE diff --git a/lldb/unittests/CMakeLists.txt b/lldb/unittests/CMakeLists.txt index cc9d45ebf981d..fa59c00a3f0c8 100644 --- a/lldb/unittests/CMakeLists.txt +++ b/lldb/unittests/CMakeLists.txt @@ -54,6 +54,7 @@ endif() add_subdirectory(Breakpoint) add_subdirectory(Callback) add_subdirectory(Core) +add_subdirectory(DAP) add_subdirectory(DataFormatter) add_subdirectory(Disassembler) add_subdirectory(Editline) diff --git a/lldb/unittests/DAP/CMakeLists.txt b/lldb/unittests/DAP/CMakeLists.txt new file mode 100644 index 0000000000000..f61c4006072a3 --- /dev/null +++ b/lldb/unittests/DAP/CMakeLists.txt @@ -0,0 +1,8 @@ +add_lldb_unittest(DAPTests + JSONUtilsTest.cpp + + LINK_LIBS + lldbDAP + LINK_COMPONENTS + Support + ) diff --git a/lldb/unittests/DAP/JSONUtilsTest.cpp b/lldb/unittests/DAP/JSONUtilsTest.cpp new file mode 100644 index 0000000000000..0cf42a1c08870 --- /dev/null +++ b/lldb/unittests/DAP/JSONUtilsTest.cpp @@ -0,0 +1,33 @@ +//===-- JSONUtilsTest.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 "JSONUtils.h" +#include "lldb/API/SBModule.h" +#include "lldb/API/SBTarget.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace lldb; +using namespace lldb_dap; + +TEST(JSONUtilsTest, GetAsString) { + StringRef str = "foo"; + json::Value value("foo"); + EXPECT_EQ(str, GetAsString(value)); +} + +TEST(JSONUtilsTest, CreateModule) { + SBTarget target; + SBModule module; + + json::Value value = CreateModule(target, module); + json::Object *object = value.getAsObject(); + + ASSERT_NE(object, nullptr); + EXPECT_EQ(object->size(), 0UL); +} From lldb-commits at lists.llvm.org Sat May 10 16:43:42 2025 From: lldb-commits at lists.llvm.org (John Harrison via lldb-commits) Date: Sat, 10 May 2025 16:43:42 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb-dap] Split lldb-dap into library and tool (NFC) (PR #139402) In-Reply-To: Message-ID: <681fe4ae.620a0220.3bdce.3dea@mx.google.com> https://github.com/ashgti approved this pull request. Looks good to me, I started something similar to write unit tests for the c++ code a while ago but never made it to a PR. Looks good to start having more direct tests of the c++ parts. https://github.com/llvm/llvm-project/pull/139402 From lldb-commits at lists.llvm.org Sat May 10 18:11:18 2025 From: lldb-commits at lists.llvm.org (Kazu Hirata via lldb-commits) Date: Sat, 10 May 2025 18:11:18 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Remove redundant calls to std::unique_ptr::get (NFC) (PR #139428) Message-ID: https://github.com/kazutakahirata created https://github.com/llvm/llvm-project/pull/139428 None >From 4001252c4cd0cc8fedfb1773ef3a53befe2774f6 Mon Sep 17 00:00:00 2001 From: Kazu Hirata Date: Tue, 25 Mar 2025 00:24:37 -0700 Subject: [PATCH] [lldb] Remove redundant calls to std::unique_ptr::get (NFC) --- lldb/source/Target/RegisterContextUnwind.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 4c760b84e45a5..f56dda187e12a 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -279,7 +279,7 @@ void RegisterContextUnwind::InitializeZerothFrame() { call_site_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite( process->GetTarget(), m_thread); - if (call_site_unwind_plan.get() != nullptr) { + if (call_site_unwind_plan != nullptr) { m_fallback_unwind_plan_sp = call_site_unwind_plan; if (TryFallbackUnwindPlan()) cfa_status = true; @@ -1722,10 +1722,10 @@ RegisterContextUnwind::SavedLocationForRegister( // tricky frame and our usual techniques can continue to be used. bool RegisterContextUnwind::TryFallbackUnwindPlan() { - if (m_fallback_unwind_plan_sp.get() == nullptr) + if (m_fallback_unwind_plan_sp == nullptr) return false; - if (m_full_unwind_plan_sp.get() == nullptr) + if (m_full_unwind_plan_sp == nullptr) return false; if (m_full_unwind_plan_sp.get() == m_fallback_unwind_plan_sp.get() || @@ -1773,7 +1773,7 @@ bool RegisterContextUnwind::TryFallbackUnwindPlan() { // fallback UnwindPlan. We checked if m_fallback_unwind_plan_sp was nullptr // at the top -- the only way it became nullptr since then is via // SavedLocationForRegister(). - if (m_fallback_unwind_plan_sp.get() == nullptr) + if (m_fallback_unwind_plan_sp == nullptr) return true; // Switch the full UnwindPlan to be the fallback UnwindPlan. If we decide @@ -1862,10 +1862,10 @@ bool RegisterContextUnwind::TryFallbackUnwindPlan() { } bool RegisterContextUnwind::ForceSwitchToFallbackUnwindPlan() { - if (m_fallback_unwind_plan_sp.get() == nullptr) + if (m_fallback_unwind_plan_sp == nullptr) return false; - if (m_full_unwind_plan_sp.get() == nullptr) + if (m_full_unwind_plan_sp == nullptr) return false; if (m_full_unwind_plan_sp.get() == m_fallback_unwind_plan_sp.get() || From lldb-commits at lists.llvm.org Sat May 10 18:11:51 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Sat, 10 May 2025 18:11:51 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Remove redundant calls to std::unique_ptr::get (NFC) (PR #139428) In-Reply-To: Message-ID: <681ff957.170a0220.3980c7.cb5c@mx.google.com> llvmbot wrote: @llvm/pr-subscribers-lldb Author: Kazu Hirata (kazutakahirata)
Changes --- Full diff: https://github.com/llvm/llvm-project/pull/139428.diff 1 Files Affected: - (modified) lldb/source/Target/RegisterContextUnwind.cpp (+6-6) ``````````diff diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 4c760b84e45a5..f56dda187e12a 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -279,7 +279,7 @@ void RegisterContextUnwind::InitializeZerothFrame() { call_site_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite( process->GetTarget(), m_thread); - if (call_site_unwind_plan.get() != nullptr) { + if (call_site_unwind_plan != nullptr) { m_fallback_unwind_plan_sp = call_site_unwind_plan; if (TryFallbackUnwindPlan()) cfa_status = true; @@ -1722,10 +1722,10 @@ RegisterContextUnwind::SavedLocationForRegister( // tricky frame and our usual techniques can continue to be used. bool RegisterContextUnwind::TryFallbackUnwindPlan() { - if (m_fallback_unwind_plan_sp.get() == nullptr) + if (m_fallback_unwind_plan_sp == nullptr) return false; - if (m_full_unwind_plan_sp.get() == nullptr) + if (m_full_unwind_plan_sp == nullptr) return false; if (m_full_unwind_plan_sp.get() == m_fallback_unwind_plan_sp.get() || @@ -1773,7 +1773,7 @@ bool RegisterContextUnwind::TryFallbackUnwindPlan() { // fallback UnwindPlan. We checked if m_fallback_unwind_plan_sp was nullptr // at the top -- the only way it became nullptr since then is via // SavedLocationForRegister(). - if (m_fallback_unwind_plan_sp.get() == nullptr) + if (m_fallback_unwind_plan_sp == nullptr) return true; // Switch the full UnwindPlan to be the fallback UnwindPlan. If we decide @@ -1862,10 +1862,10 @@ bool RegisterContextUnwind::TryFallbackUnwindPlan() { } bool RegisterContextUnwind::ForceSwitchToFallbackUnwindPlan() { - if (m_fallback_unwind_plan_sp.get() == nullptr) + if (m_fallback_unwind_plan_sp == nullptr) return false; - if (m_full_unwind_plan_sp.get() == nullptr) + if (m_full_unwind_plan_sp == nullptr) return false; if (m_full_unwind_plan_sp.get() == m_fallback_unwind_plan_sp.get() || ``````````
https://github.com/llvm/llvm-project/pull/139428 From lldb-commits at lists.llvm.org Sat May 10 19:22:13 2025 From: lldb-commits at lists.llvm.org (Alex Langford via lldb-commits) Date: Sat, 10 May 2025 19:22:13 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Remove redundant calls to std::unique_ptr::get (NFC) (PR #139428) In-Reply-To: Message-ID: <682009d5.170a0220.1ff63a.27bd@mx.google.com> https://github.com/bulbazord approved this pull request. https://github.com/llvm/llvm-project/pull/139428 From lldb-commits at lists.llvm.org Sat May 10 19:31:39 2025 From: lldb-commits at lists.llvm.org (via lldb-commits) Date: Sat, 10 May 2025 19:31:39 -0700 (PDT) Subject: [Lldb-commits] [lldb] fadd427 - [lldb] Remove redundant calls to std::unique_ptr::get (NFC) (#139428) Message-ID: <68200c0b.170a0220.9828a.9496@mx.google.com> Author: Kazu Hirata Date: 2025-05-10T19:31:35-07:00 New Revision: fadd427777b23a63248d57745126a3a30db71b02 URL: https://github.com/llvm/llvm-project/commit/fadd427777b23a63248d57745126a3a30db71b02 DIFF: https://github.com/llvm/llvm-project/commit/fadd427777b23a63248d57745126a3a30db71b02.diff LOG: [lldb] Remove redundant calls to std::unique_ptr::get (NFC) (#139428) Added: Modified: lldb/source/Target/RegisterContextUnwind.cpp Removed: ################################################################################ diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 4c760b84e45a5..f56dda187e12a 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -279,7 +279,7 @@ void RegisterContextUnwind::InitializeZerothFrame() { call_site_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite( process->GetTarget(), m_thread); - if (call_site_unwind_plan.get() != nullptr) { + if (call_site_unwind_plan != nullptr) { m_fallback_unwind_plan_sp = call_site_unwind_plan; if (TryFallbackUnwindPlan()) cfa_status = true; @@ -1722,10 +1722,10 @@ RegisterContextUnwind::SavedLocationForRegister( // tricky frame and our usual techniques can continue to be used. bool RegisterContextUnwind::TryFallbackUnwindPlan() { - if (m_fallback_unwind_plan_sp.get() == nullptr) + if (m_fallback_unwind_plan_sp == nullptr) return false; - if (m_full_unwind_plan_sp.get() == nullptr) + if (m_full_unwind_plan_sp == nullptr) return false; if (m_full_unwind_plan_sp.get() == m_fallback_unwind_plan_sp.get() || @@ -1773,7 +1773,7 @@ bool RegisterContextUnwind::TryFallbackUnwindPlan() { // fallback UnwindPlan. We checked if m_fallback_unwind_plan_sp was nullptr // at the top -- the only way it became nullptr since then is via // SavedLocationForRegister(). - if (m_fallback_unwind_plan_sp.get() == nullptr) + if (m_fallback_unwind_plan_sp == nullptr) return true; // Switch the full UnwindPlan to be the fallback UnwindPlan. If we decide @@ -1862,10 +1862,10 @@ bool RegisterContextUnwind::TryFallbackUnwindPlan() { } bool RegisterContextUnwind::ForceSwitchToFallbackUnwindPlan() { - if (m_fallback_unwind_plan_sp.get() == nullptr) + if (m_fallback_unwind_plan_sp == nullptr) return false; - if (m_full_unwind_plan_sp.get() == nullptr) + if (m_full_unwind_plan_sp == nullptr) return false; if (m_full_unwind_plan_sp.get() == m_fallback_unwind_plan_sp.get() || From lldb-commits at lists.llvm.org Sat May 10 19:31:40 2025 From: lldb-commits at lists.llvm.org (Kazu Hirata via lldb-commits) Date: Sat, 10 May 2025 19:31:40 -0700 (PDT) Subject: [Lldb-commits] [lldb] [lldb] Remove redundant calls to std::unique_ptr::get (NFC) (PR #139428) In-Reply-To: Message-ID: <68200c0c.170a0220.7e312.183a@mx.google.com> https://github.com/kazutakahirata closed https://github.com/llvm/llvm-project/pull/139428