aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/source/Host
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lldb/source/Host')
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/Alarm.cpp222
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/Editline.cpp1603
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/File.cpp907
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/FileAction.cpp89
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/FileCache.cpp114
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/FileSystem.cpp470
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/GetOptInc.cpp451
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/Host.cpp655
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/HostInfoBase.cpp352
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/HostNativeThreadBase.cpp63
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/HostProcess.cpp42
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/HostThread.cpp46
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/LZMA.cpp146
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/LockFileBase.cpp78
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/MainLoopBase.cpp33
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/MonitoringProcessLauncher.cpp70
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/NativeProcessProtocol.cpp766
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/NativeRegisterContext.cpp458
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/NativeThreadProtocol.cpp19
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/NativeWatchpointList.cpp30
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/OptionParser.cpp84
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/PipeBase.cpp24
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/ProcessLaunchInfo.cpp342
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/ProcessRunLock.cpp65
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/PseudoTerminal.cpp221
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/Socket.cpp376
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/SocketAddress.cpp324
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/StreamFile.cpp54
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/TCPSocket.cpp326
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/Terminal.cpp474
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/ThreadLauncher.cpp79
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/UDPSocket.cpp138
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/XML.cpp519
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/ZipFileResolver.cpp72
-rw-r--r--contrib/llvm-project/lldb/source/Host/freebsd/Host.cpp248
-rw-r--r--contrib/llvm-project/lldb/source/Host/freebsd/HostInfoFreeBSD.cpp55
-rw-r--r--contrib/llvm-project/lldb/source/Host/netbsd/HostInfoNetBSD.cpp73
-rw-r--r--contrib/llvm-project/lldb/source/Host/netbsd/HostNetBSD.cpp273
-rw-r--r--contrib/llvm-project/lldb/source/Host/openbsd/Host.cpp219
-rw-r--r--contrib/llvm-project/lldb/source/Host/openbsd/HostInfoOpenBSD.cpp52
-rw-r--r--contrib/llvm-project/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp812
-rw-r--r--contrib/llvm-project/lldb/source/Host/posix/DomainSocket.cpp157
-rw-r--r--contrib/llvm-project/lldb/source/Host/posix/FileSystemPosix.cpp84
-rw-r--r--contrib/llvm-project/lldb/source/Host/posix/HostInfoPosix.cpp159
-rw-r--r--contrib/llvm-project/lldb/source/Host/posix/HostProcessPosix.cpp66
-rw-r--r--contrib/llvm-project/lldb/source/Host/posix/HostThreadPosix.cpp61
-rw-r--r--contrib/llvm-project/lldb/source/Host/posix/LockFilePosix.cpp58
-rw-r--r--contrib/llvm-project/lldb/source/Host/posix/MainLoopPosix.cpp411
-rw-r--r--contrib/llvm-project/lldb/source/Host/posix/PipePosix.cpp367
-rw-r--r--contrib/llvm-project/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp305
50 files changed, 13112 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Host/common/Alarm.cpp b/contrib/llvm-project/lldb/source/Host/common/Alarm.cpp
new file mode 100644
index 000000000000..afc770d20d7b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/Alarm.cpp
@@ -0,0 +1,222 @@
+//===-- Alarm.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/Alarm.h"
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+Alarm::Alarm(Duration timeout, bool run_callback_on_exit)
+ : m_timeout(timeout), m_run_callbacks_on_exit(run_callback_on_exit) {
+ StartAlarmThread();
+}
+
+Alarm::~Alarm() { StopAlarmThread(); }
+
+Alarm::Handle Alarm::Create(std::function<void()> callback) {
+ // Gracefully deal with the unlikely event that the alarm thread failed to
+ // launch.
+ if (!AlarmThreadRunning())
+ return INVALID_HANDLE;
+
+ // Compute the next expiration before we take the lock. This ensures that
+ // waiting on the lock doesn't eat into the timeout.
+ const TimePoint expiration = GetNextExpiration();
+
+ Handle handle = INVALID_HANDLE;
+
+ {
+ std::lock_guard<std::mutex> alarm_guard(m_alarm_mutex);
+
+ // Create a new unique entry and remember its handle.
+ m_entries.emplace_back(callback, expiration);
+ handle = m_entries.back().handle;
+
+ // Tell the alarm thread we need to recompute the next alarm.
+ m_recompute_next_alarm = true;
+ }
+
+ m_alarm_cv.notify_one();
+ return handle;
+}
+
+bool Alarm::Restart(Handle handle) {
+ // Gracefully deal with the unlikely event that the alarm thread failed to
+ // launch.
+ if (!AlarmThreadRunning())
+ return false;
+
+ // Compute the next expiration before we take the lock. This ensures that
+ // waiting on the lock doesn't eat into the timeout.
+ const TimePoint expiration = GetNextExpiration();
+
+ {
+ std::lock_guard<std::mutex> alarm_guard(m_alarm_mutex);
+
+ // Find the entry corresponding to the given handle.
+ const auto it =
+ std::find_if(m_entries.begin(), m_entries.end(),
+ [handle](Entry &entry) { return entry.handle == handle; });
+ if (it == m_entries.end())
+ return false;
+
+ // Update the expiration.
+ it->expiration = expiration;
+
+ // Tell the alarm thread we need to recompute the next alarm.
+ m_recompute_next_alarm = true;
+ }
+
+ m_alarm_cv.notify_one();
+ return true;
+}
+
+bool Alarm::Cancel(Handle handle) {
+ // Gracefully deal with the unlikely event that the alarm thread failed to
+ // launch.
+ if (!AlarmThreadRunning())
+ return false;
+
+ {
+ std::lock_guard<std::mutex> alarm_guard(m_alarm_mutex);
+
+ const auto it =
+ std::find_if(m_entries.begin(), m_entries.end(),
+ [handle](Entry &entry) { return entry.handle == handle; });
+
+ if (it == m_entries.end())
+ return false;
+
+ m_entries.erase(it);
+ }
+
+ // No need to notify the alarm thread. This only affects the alarm thread if
+ // we removed the entry that corresponds to the next alarm. If that's the
+ // case, the thread will wake up as scheduled, find no expired events, and
+ // recompute the next alarm time.
+ return true;
+}
+
+Alarm::Entry::Entry(Alarm::Callback callback, Alarm::TimePoint expiration)
+ : handle(Alarm::GetNextUniqueHandle()), callback(std::move(callback)),
+ expiration(std::move(expiration)) {}
+
+void Alarm::StartAlarmThread() {
+ if (!m_alarm_thread.IsJoinable()) {
+ llvm::Expected<HostThread> alarm_thread = ThreadLauncher::LaunchThread(
+ "lldb.debugger.alarm-thread", [this] { return AlarmThread(); },
+ 8 * 1024 * 1024); // Use larger 8MB stack for this thread
+ if (alarm_thread) {
+ m_alarm_thread = *alarm_thread;
+ } else {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::Host), alarm_thread.takeError(),
+ "failed to launch host thread: {0}");
+ }
+ }
+}
+
+void Alarm::StopAlarmThread() {
+ if (m_alarm_thread.IsJoinable()) {
+ {
+ std::lock_guard<std::mutex> alarm_guard(m_alarm_mutex);
+ m_exit = true;
+ }
+ m_alarm_cv.notify_one();
+ m_alarm_thread.Join(nullptr);
+ }
+}
+
+bool Alarm::AlarmThreadRunning() { return m_alarm_thread.IsJoinable(); }
+
+lldb::thread_result_t Alarm::AlarmThread() {
+ bool exit = false;
+ std::optional<TimePoint> next_alarm;
+
+ const auto predicate = [this] { return m_exit || m_recompute_next_alarm; };
+
+ while (!exit) {
+ // Synchronization between the main thread and the alarm thread using a
+ // mutex and condition variable. There are 2 reasons the thread can wake up:
+ //
+ // 1. The timeout for the next alarm expired.
+ //
+ // 2. The condition variable is notified that one of our shared variables
+ // (see predicate) was modified. Either the thread is asked to shut down
+ // or a new alarm came in and we need to recompute the next timeout.
+ //
+ // Below we only deal with the timeout expiring and fall through for dealing
+ // with the rest.
+ llvm::SmallVector<Callback, 1> callbacks;
+ {
+ std::unique_lock<std::mutex> alarm_lock(m_alarm_mutex);
+ if (next_alarm) {
+ if (!m_alarm_cv.wait_until(alarm_lock, *next_alarm, predicate)) {
+ // The timeout for the next alarm expired.
+
+ // Clear the next timeout to signal that we need to recompute the next
+ // timeout.
+ next_alarm.reset();
+
+ // Iterate over all the callbacks. Call the ones that have expired
+ // and remove them from the list.
+ const TimePoint now = std::chrono::system_clock::now();
+ auto it = m_entries.begin();
+ while (it != m_entries.end()) {
+ if (it->expiration <= now) {
+ callbacks.emplace_back(std::move(it->callback));
+ it = m_entries.erase(it);
+ } else {
+ it++;
+ }
+ }
+ }
+ } else {
+ m_alarm_cv.wait(alarm_lock, predicate);
+ }
+
+ // Fall through after waiting on the condition variable. At this point
+ // either the predicate is true or we woke up because an alarm expired.
+
+ // The alarm thread is shutting down.
+ if (m_exit) {
+ exit = true;
+ if (m_run_callbacks_on_exit) {
+ for (Entry &entry : m_entries)
+ callbacks.emplace_back(std::move(entry.callback));
+ }
+ }
+
+ // A new alarm was added or an alarm expired. Either way we need to
+ // recompute when this thread should wake up for the next alarm.
+ if (m_recompute_next_alarm || !next_alarm) {
+ for (Entry &entry : m_entries) {
+ if (!next_alarm || entry.expiration < *next_alarm)
+ next_alarm = entry.expiration;
+ }
+ m_recompute_next_alarm = false;
+ }
+ }
+
+ // Outside the lock, call the callbacks.
+ for (Callback &callback : callbacks)
+ callback();
+ }
+ return {};
+}
+
+Alarm::TimePoint Alarm::GetNextExpiration() const {
+ return std::chrono::system_clock::now() + m_timeout;
+}
+
+Alarm::Handle Alarm::GetNextUniqueHandle() {
+ static std::atomic<Handle> g_next_handle = 1;
+ return g_next_handle++;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/Editline.cpp b/contrib/llvm-project/lldb/source/Host/common/Editline.cpp
new file mode 100644
index 000000000000..561ec228cdb2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/Editline.cpp
@@ -0,0 +1,1603 @@
+//===-- Editline.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 <climits>
+#include <iomanip>
+#include <optional>
+
+#include "lldb/Host/Editline.h"
+
+#include "lldb/Host/ConnectionFileDescriptor.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Utility/CompletionRequest.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/SelectHelper.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StringList.h"
+#include "lldb/Utility/Timeout.h"
+
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Locale.h"
+#include "llvm/Support/Threading.h"
+
+using namespace lldb_private;
+using namespace lldb_private::line_editor;
+
+// Editline uses careful cursor management to achieve the illusion of editing a
+// multi-line block of text with a single line editor. Preserving this
+// illusion requires fairly careful management of cursor state. Read and
+// understand the relationship between DisplayInput(), MoveCursor(),
+// SetCurrentLine(), and SaveEditedLine() before making changes.
+
+/// https://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf
+#define ESCAPE "\x1b"
+#define ANSI_CLEAR_BELOW ESCAPE "[J"
+#define ANSI_CLEAR_RIGHT ESCAPE "[K"
+#define ANSI_SET_COLUMN_N ESCAPE "[%dG"
+#define ANSI_UP_N_ROWS ESCAPE "[%dA"
+#define ANSI_DOWN_N_ROWS ESCAPE "[%dB"
+
+#if LLDB_EDITLINE_USE_WCHAR
+
+#define EditLineConstString(str) L##str
+#define EditLineStringFormatSpec "%ls"
+
+#else
+
+#define EditLineConstString(str) str
+#define EditLineStringFormatSpec "%s"
+
+// use #defines so wide version functions and structs will resolve to old
+// versions for case of libedit not built with wide char support
+#define history_w history
+#define history_winit history_init
+#define history_wend history_end
+#define HistoryW History
+#define HistEventW HistEvent
+#define LineInfoW LineInfo
+
+#define el_wgets el_gets
+#define el_wgetc el_getc
+#define el_wpush el_push
+#define el_wparse el_parse
+#define el_wset el_set
+#define el_wget el_get
+#define el_wline el_line
+#define el_winsertstr el_insertstr
+#define el_wdeletestr el_deletestr
+
+#endif // #if LLDB_EDITLINE_USE_WCHAR
+
+bool IsOnlySpaces(const EditLineStringType &content) {
+ for (wchar_t ch : content) {
+ if (ch != EditLineCharType(' '))
+ return false;
+ }
+ return true;
+}
+
+static size_t ColumnWidth(llvm::StringRef str) {
+ return llvm::sys::locale::columnWidth(str);
+}
+
+static int GetOperation(HistoryOperation op) {
+ // The naming used by editline for the history operations is counter
+ // intuitive to how it's used in LLDB's editline implementation.
+ //
+ // - The H_LAST returns the oldest entry in the history.
+ //
+ // - The H_PREV operation returns the previous element in the history, which
+ // is newer than the current one.
+ //
+ // - The H_CURR returns the current entry in the history.
+ //
+ // - The H_NEXT operation returns the next element in the history, which is
+ // older than the current one.
+ //
+ // - The H_FIRST returns the most recent entry in the history.
+ //
+ // The naming of the enum entries match the semantic meaning.
+ switch(op) {
+ case HistoryOperation::Oldest:
+ return H_LAST;
+ case HistoryOperation::Older:
+ return H_NEXT;
+ case HistoryOperation::Current:
+ return H_CURR;
+ case HistoryOperation::Newer:
+ return H_PREV;
+ case HistoryOperation::Newest:
+ return H_FIRST;
+ }
+ llvm_unreachable("Fully covered switch!");
+}
+
+
+EditLineStringType CombineLines(const std::vector<EditLineStringType> &lines) {
+ EditLineStringStreamType combined_stream;
+ for (EditLineStringType line : lines) {
+ combined_stream << line.c_str() << "\n";
+ }
+ return combined_stream.str();
+}
+
+std::vector<EditLineStringType> SplitLines(const EditLineStringType &input) {
+ std::vector<EditLineStringType> result;
+ size_t start = 0;
+ while (start < input.length()) {
+ size_t end = input.find('\n', start);
+ if (end == std::string::npos) {
+ result.push_back(input.substr(start));
+ break;
+ }
+ result.push_back(input.substr(start, end - start));
+ start = end + 1;
+ }
+ // Treat an empty history session as a single command of zero-length instead
+ // of returning an empty vector.
+ if (result.empty()) {
+ result.emplace_back();
+ }
+ return result;
+}
+
+EditLineStringType FixIndentation(const EditLineStringType &line,
+ int indent_correction) {
+ if (indent_correction == 0)
+ return line;
+ if (indent_correction < 0)
+ return line.substr(-indent_correction);
+ return EditLineStringType(indent_correction, EditLineCharType(' ')) + line;
+}
+
+int GetIndentation(const EditLineStringType &line) {
+ int space_count = 0;
+ for (EditLineCharType ch : line) {
+ if (ch != EditLineCharType(' '))
+ break;
+ ++space_count;
+ }
+ return space_count;
+}
+
+bool IsInputPending(FILE *file) {
+ // FIXME: This will be broken on Windows if we ever re-enable Editline. You
+ // can't use select
+ // on something that isn't a socket. This will have to be re-written to not
+ // use a FILE*, but instead use some kind of yet-to-be-created abstraction
+ // that select-like functionality on non-socket objects.
+ const int fd = fileno(file);
+ SelectHelper select_helper;
+ select_helper.SetTimeout(std::chrono::microseconds(0));
+ select_helper.FDSetRead(fd);
+ return select_helper.Select().Success();
+}
+
+namespace lldb_private {
+namespace line_editor {
+typedef std::weak_ptr<EditlineHistory> EditlineHistoryWP;
+
+// EditlineHistory objects are sometimes shared between multiple Editline
+// instances with the same program name.
+
+class EditlineHistory {
+private:
+ // Use static GetHistory() function to get a EditlineHistorySP to one of
+ // these objects
+ EditlineHistory(const std::string &prefix, uint32_t size, bool unique_entries)
+ : m_prefix(prefix) {
+ m_history = history_winit();
+ history_w(m_history, &m_event, H_SETSIZE, size);
+ if (unique_entries)
+ history_w(m_history, &m_event, H_SETUNIQUE, 1);
+ }
+
+ const char *GetHistoryFilePath() {
+ // Compute the history path lazily.
+ if (m_path.empty() && m_history && !m_prefix.empty()) {
+ llvm::SmallString<128> lldb_history_file;
+ FileSystem::Instance().GetHomeDirectory(lldb_history_file);
+ llvm::sys::path::append(lldb_history_file, ".lldb");
+
+ // LLDB stores its history in ~/.lldb/. If for some reason this directory
+ // isn't writable or cannot be created, history won't be available.
+ if (!llvm::sys::fs::create_directory(lldb_history_file)) {
+#if LLDB_EDITLINE_USE_WCHAR
+ std::string filename = m_prefix + "-widehistory";
+#else
+ std::string filename = m_prefix + "-history";
+#endif
+ llvm::sys::path::append(lldb_history_file, filename);
+ m_path = std::string(lldb_history_file.str());
+ }
+ }
+
+ if (m_path.empty())
+ return nullptr;
+
+ return m_path.c_str();
+ }
+
+public:
+ ~EditlineHistory() {
+ Save();
+
+ if (m_history) {
+ history_wend(m_history);
+ m_history = nullptr;
+ }
+ }
+
+ static EditlineHistorySP GetHistory(const std::string &prefix) {
+ typedef std::map<std::string, EditlineHistoryWP> WeakHistoryMap;
+ static std::recursive_mutex g_mutex;
+ static WeakHistoryMap g_weak_map;
+ std::lock_guard<std::recursive_mutex> guard(g_mutex);
+ WeakHistoryMap::const_iterator pos = g_weak_map.find(prefix);
+ EditlineHistorySP history_sp;
+ if (pos != g_weak_map.end()) {
+ history_sp = pos->second.lock();
+ if (history_sp)
+ return history_sp;
+ g_weak_map.erase(pos);
+ }
+ history_sp.reset(new EditlineHistory(prefix, 800, true));
+ g_weak_map[prefix] = history_sp;
+ return history_sp;
+ }
+
+ bool IsValid() const { return m_history != nullptr; }
+
+ HistoryW *GetHistoryPtr() { return m_history; }
+
+ void Enter(const EditLineCharType *line_cstr) {
+ if (m_history)
+ history_w(m_history, &m_event, H_ENTER, line_cstr);
+ }
+
+ bool Load() {
+ if (m_history) {
+ const char *path = GetHistoryFilePath();
+ if (path) {
+ history_w(m_history, &m_event, H_LOAD, path);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool Save() {
+ if (m_history) {
+ const char *path = GetHistoryFilePath();
+ if (path) {
+ history_w(m_history, &m_event, H_SAVE, path);
+ return true;
+ }
+ }
+ return false;
+ }
+
+protected:
+ /// The history object.
+ HistoryW *m_history = nullptr;
+ /// The history event needed to contain all history events.
+ HistEventW m_event;
+ /// The prefix name (usually the editline program name) to use when
+ /// loading/saving history.
+ std::string m_prefix;
+ /// Path to the history file.
+ std::string m_path;
+};
+}
+}
+
+// Editline private methods
+
+void Editline::SetBaseLineNumber(int line_number) {
+ m_base_line_number = line_number;
+ m_line_number_digits =
+ std::max<int>(3, std::to_string(line_number).length() + 1);
+}
+
+std::string Editline::PromptForIndex(int line_index) {
+ bool use_line_numbers = m_multiline_enabled && m_base_line_number > 0;
+ std::string prompt = m_set_prompt;
+ if (use_line_numbers && prompt.length() == 0)
+ prompt = ": ";
+ std::string continuation_prompt = prompt;
+ if (m_set_continuation_prompt.length() > 0) {
+ continuation_prompt = m_set_continuation_prompt;
+ // Ensure that both prompts are the same length through space padding
+ const size_t prompt_width = ColumnWidth(prompt);
+ const size_t cont_prompt_width = ColumnWidth(continuation_prompt);
+ const size_t padded_prompt_width =
+ std::max(prompt_width, cont_prompt_width);
+ if (prompt_width < padded_prompt_width)
+ prompt += std::string(padded_prompt_width - prompt_width, ' ');
+ else if (cont_prompt_width < padded_prompt_width)
+ continuation_prompt +=
+ std::string(padded_prompt_width - cont_prompt_width, ' ');
+ }
+
+ if (use_line_numbers) {
+ StreamString prompt_stream;
+ prompt_stream.Printf(
+ "%*d%s", m_line_number_digits, m_base_line_number + line_index,
+ (line_index == 0) ? prompt.c_str() : continuation_prompt.c_str());
+ return std::string(std::move(prompt_stream.GetString()));
+ }
+ return (line_index == 0) ? prompt : continuation_prompt;
+}
+
+void Editline::SetCurrentLine(int line_index) {
+ m_current_line_index = line_index;
+ m_current_prompt = PromptForIndex(line_index);
+}
+
+size_t Editline::GetPromptWidth() { return ColumnWidth(PromptForIndex(0)); }
+
+bool Editline::IsEmacs() {
+ const char *editor;
+ el_get(m_editline, EL_EDITOR, &editor);
+ return editor[0] == 'e';
+}
+
+bool Editline::IsOnlySpaces() {
+ const LineInfoW *info = el_wline(m_editline);
+ for (const EditLineCharType *character = info->buffer;
+ character < info->lastchar; character++) {
+ if (*character != ' ')
+ return false;
+ }
+ return true;
+}
+
+int Editline::GetLineIndexForLocation(CursorLocation location, int cursor_row) {
+ int line = 0;
+ if (location == CursorLocation::EditingPrompt ||
+ location == CursorLocation::BlockEnd ||
+ location == CursorLocation::EditingCursor) {
+ for (unsigned index = 0; index < m_current_line_index; index++) {
+ line += CountRowsForLine(m_input_lines[index]);
+ }
+ if (location == CursorLocation::EditingCursor) {
+ line += cursor_row;
+ } else if (location == CursorLocation::BlockEnd) {
+ for (unsigned index = m_current_line_index; index < m_input_lines.size();
+ index++) {
+ line += CountRowsForLine(m_input_lines[index]);
+ }
+ --line;
+ }
+ }
+ return line;
+}
+
+void Editline::MoveCursor(CursorLocation from, CursorLocation to) {
+ const LineInfoW *info = el_wline(m_editline);
+ int editline_cursor_position =
+ (int)((info->cursor - info->buffer) + GetPromptWidth());
+ int editline_cursor_row = editline_cursor_position / m_terminal_width;
+
+ // Determine relative starting and ending lines
+ int fromLine = GetLineIndexForLocation(from, editline_cursor_row);
+ int toLine = GetLineIndexForLocation(to, editline_cursor_row);
+ if (toLine != fromLine) {
+ fprintf(m_output_file,
+ (toLine > fromLine) ? ANSI_DOWN_N_ROWS : ANSI_UP_N_ROWS,
+ std::abs(toLine - fromLine));
+ }
+
+ // Determine target column
+ int toColumn = 1;
+ if (to == CursorLocation::EditingCursor) {
+ toColumn =
+ editline_cursor_position - (editline_cursor_row * m_terminal_width) + 1;
+ } else if (to == CursorLocation::BlockEnd && !m_input_lines.empty()) {
+ toColumn =
+ ((m_input_lines[m_input_lines.size() - 1].length() + GetPromptWidth()) %
+ 80) +
+ 1;
+ }
+ fprintf(m_output_file, ANSI_SET_COLUMN_N, toColumn);
+}
+
+void Editline::DisplayInput(int firstIndex) {
+ fprintf(m_output_file, ANSI_SET_COLUMN_N ANSI_CLEAR_BELOW, 1);
+ int line_count = (int)m_input_lines.size();
+ for (int index = firstIndex; index < line_count; index++) {
+ fprintf(m_output_file,
+ "%s"
+ "%s"
+ "%s" EditLineStringFormatSpec " ",
+ m_prompt_ansi_prefix.c_str(), PromptForIndex(index).c_str(),
+ m_prompt_ansi_suffix.c_str(), m_input_lines[index].c_str());
+ if (index < line_count - 1)
+ fprintf(m_output_file, "\n");
+ }
+}
+
+int Editline::CountRowsForLine(const EditLineStringType &content) {
+ std::string prompt =
+ PromptForIndex(0); // Prompt width is constant during an edit session
+ int line_length = (int)(content.length() + ColumnWidth(prompt));
+ return (line_length / m_terminal_width) + 1;
+}
+
+void Editline::SaveEditedLine() {
+ const LineInfoW *info = el_wline(m_editline);
+ m_input_lines[m_current_line_index] =
+ EditLineStringType(info->buffer, info->lastchar - info->buffer);
+}
+
+StringList Editline::GetInputAsStringList(int line_count) {
+ StringList lines;
+ for (EditLineStringType line : m_input_lines) {
+ if (line_count == 0)
+ break;
+#if LLDB_EDITLINE_USE_WCHAR
+ lines.AppendString(m_utf8conv.to_bytes(line));
+#else
+ lines.AppendString(line);
+#endif
+ --line_count;
+ }
+ return lines;
+}
+
+unsigned char Editline::RecallHistory(HistoryOperation op) {
+ assert(op == HistoryOperation::Older || op == HistoryOperation::Newer);
+ if (!m_history_sp || !m_history_sp->IsValid())
+ return CC_ERROR;
+
+ HistoryW *pHistory = m_history_sp->GetHistoryPtr();
+ HistEventW history_event;
+ std::vector<EditLineStringType> new_input_lines;
+
+ // Treat moving from the "live" entry differently
+ if (!m_in_history) {
+ switch (op) {
+ case HistoryOperation::Newer:
+ return CC_ERROR; // Can't go newer than the "live" entry
+ case HistoryOperation::Older: {
+ if (history_w(pHistory, &history_event,
+ GetOperation(HistoryOperation::Newest)) == -1)
+ return CC_ERROR;
+ // Save any edits to the "live" entry in case we return by moving forward
+ // in history (it would be more bash-like to save over any current entry,
+ // but libedit doesn't offer the ability to add entries anywhere except
+ // the end.)
+ SaveEditedLine();
+ m_live_history_lines = m_input_lines;
+ m_in_history = true;
+ } break;
+ default:
+ llvm_unreachable("unsupported history direction");
+ }
+ } else {
+ if (history_w(pHistory, &history_event, GetOperation(op)) == -1) {
+ switch (op) {
+ case HistoryOperation::Older:
+ // Can't move earlier than the earliest entry.
+ return CC_ERROR;
+ case HistoryOperation::Newer:
+ // Moving to newer-than-the-newest entry yields the "live" entry.
+ new_input_lines = m_live_history_lines;
+ m_in_history = false;
+ break;
+ default:
+ llvm_unreachable("unsupported history direction");
+ }
+ }
+ }
+
+ // If we're pulling the lines from history, split them apart
+ if (m_in_history)
+ new_input_lines = SplitLines(history_event.str);
+
+ // Erase the current edit session and replace it with a new one
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
+ m_input_lines = new_input_lines;
+ DisplayInput();
+
+ // Prepare to edit the last line when moving to previous entry, or the first
+ // line when moving to next entry
+ switch (op) {
+ case HistoryOperation::Older:
+ m_current_line_index = (int)m_input_lines.size() - 1;
+ break;
+ case HistoryOperation::Newer:
+ m_current_line_index = 0;
+ break;
+ default:
+ llvm_unreachable("unsupported history direction");
+ }
+ SetCurrentLine(m_current_line_index);
+ MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
+ return CC_NEWLINE;
+}
+
+int Editline::GetCharacter(EditLineGetCharType *c) {
+ const LineInfoW *info = el_wline(m_editline);
+
+ // Paint a ANSI formatted version of the desired prompt over the version
+ // libedit draws. (will only be requested if colors are supported)
+ if (m_needs_prompt_repaint) {
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
+ fprintf(m_output_file,
+ "%s"
+ "%s"
+ "%s",
+ m_prompt_ansi_prefix.c_str(), Prompt(),
+ m_prompt_ansi_suffix.c_str());
+ MoveCursor(CursorLocation::EditingPrompt, CursorLocation::EditingCursor);
+ m_needs_prompt_repaint = false;
+ }
+
+ if (m_multiline_enabled) {
+ // Detect when the number of rows used for this input line changes due to
+ // an edit
+ int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth());
+ int new_line_rows = (lineLength / m_terminal_width) + 1;
+ if (m_current_line_rows != -1 && new_line_rows != m_current_line_rows) {
+ // Respond by repainting the current state from this line on
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
+ SaveEditedLine();
+ DisplayInput(m_current_line_index);
+ MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
+ }
+ m_current_line_rows = new_line_rows;
+ }
+
+ // Read an actual character
+ while (true) {
+ lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess;
+ char ch = 0;
+
+ if (m_terminal_size_has_changed)
+ ApplyTerminalSizeChange();
+
+ // This mutex is locked by our caller (GetLine). Unlock it while we read a
+ // character (blocking operation), so we do not hold the mutex
+ // indefinitely. This gives a chance for someone to interrupt us. After
+ // Read returns, immediately lock the mutex again and check if we were
+ // interrupted.
+ m_output_mutex.unlock();
+ int read_count =
+ m_input_connection.Read(&ch, 1, std::nullopt, status, nullptr);
+ m_output_mutex.lock();
+ if (m_editor_status == EditorStatus::Interrupted) {
+ while (read_count > 0 && status == lldb::eConnectionStatusSuccess)
+ read_count =
+ m_input_connection.Read(&ch, 1, std::nullopt, status, nullptr);
+ lldbassert(status == lldb::eConnectionStatusInterrupted);
+ return 0;
+ }
+
+ if (read_count) {
+ if (CompleteCharacter(ch, *c))
+ return 1;
+ } else {
+ switch (status) {
+ case lldb::eConnectionStatusSuccess: // Success
+ break;
+
+ case lldb::eConnectionStatusInterrupted:
+ llvm_unreachable("Interrupts should have been handled above.");
+
+ case lldb::eConnectionStatusError: // Check GetError() for details
+ case lldb::eConnectionStatusTimedOut: // Request timed out
+ case lldb::eConnectionStatusEndOfFile: // End-of-file encountered
+ case lldb::eConnectionStatusNoConnection: // No connection
+ case lldb::eConnectionStatusLostConnection: // Lost connection while
+ // connected to a valid
+ // connection
+ m_editor_status = EditorStatus::EndOfInput;
+ return 0;
+ }
+ }
+ }
+}
+
+const char *Editline::Prompt() {
+ if (!m_prompt_ansi_prefix.empty() || !m_prompt_ansi_suffix.empty())
+ m_needs_prompt_repaint = true;
+ return m_current_prompt.c_str();
+}
+
+unsigned char Editline::BreakLineCommand(int ch) {
+ // Preserve any content beyond the cursor, truncate and save the current line
+ const LineInfoW *info = el_wline(m_editline);
+ auto current_line =
+ EditLineStringType(info->buffer, info->cursor - info->buffer);
+ auto new_line_fragment =
+ EditLineStringType(info->cursor, info->lastchar - info->cursor);
+ m_input_lines[m_current_line_index] = current_line;
+
+ // Ignore whitespace-only extra fragments when breaking a line
+ if (::IsOnlySpaces(new_line_fragment))
+ new_line_fragment = EditLineConstString("");
+
+ // Establish the new cursor position at the start of a line when inserting a
+ // line break
+ m_revert_cursor_index = 0;
+
+ // Don't perform automatic formatting when pasting
+ if (!IsInputPending(m_input_file)) {
+ // Apply smart indentation
+ if (m_fix_indentation_callback) {
+ StringList lines = GetInputAsStringList(m_current_line_index + 1);
+#if LLDB_EDITLINE_USE_WCHAR
+ lines.AppendString(m_utf8conv.to_bytes(new_line_fragment));
+#else
+ lines.AppendString(new_line_fragment);
+#endif
+
+ int indent_correction = m_fix_indentation_callback(this, lines, 0);
+ new_line_fragment = FixIndentation(new_line_fragment, indent_correction);
+ m_revert_cursor_index = GetIndentation(new_line_fragment);
+ }
+ }
+
+ // Insert the new line and repaint everything from the split line on down
+ m_input_lines.insert(m_input_lines.begin() + m_current_line_index + 1,
+ new_line_fragment);
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
+ DisplayInput(m_current_line_index);
+
+ // Reposition the cursor to the right line and prepare to edit the new line
+ SetCurrentLine(m_current_line_index + 1);
+ MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
+ return CC_NEWLINE;
+}
+
+unsigned char Editline::EndOrAddLineCommand(int ch) {
+ // Don't perform end of input detection when pasting, always treat this as a
+ // line break
+ if (IsInputPending(m_input_file)) {
+ return BreakLineCommand(ch);
+ }
+
+ // Save any edits to this line
+ SaveEditedLine();
+
+ // If this is the end of the last line, consider whether to add a line
+ // instead
+ const LineInfoW *info = el_wline(m_editline);
+ if (m_current_line_index == m_input_lines.size() - 1 &&
+ info->cursor == info->lastchar) {
+ if (m_is_input_complete_callback) {
+ auto lines = GetInputAsStringList();
+ if (!m_is_input_complete_callback(this, lines)) {
+ return BreakLineCommand(ch);
+ }
+
+ // The completion test is allowed to change the input lines when complete
+ m_input_lines.clear();
+ for (unsigned index = 0; index < lines.GetSize(); index++) {
+#if LLDB_EDITLINE_USE_WCHAR
+ m_input_lines.insert(m_input_lines.end(),
+ m_utf8conv.from_bytes(lines[index]));
+#else
+ m_input_lines.insert(m_input_lines.end(), lines[index]);
+#endif
+ }
+ }
+ }
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd);
+ fprintf(m_output_file, "\n");
+ m_editor_status = EditorStatus::Complete;
+ return CC_NEWLINE;
+}
+
+unsigned char Editline::DeleteNextCharCommand(int ch) {
+ LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
+
+ // Just delete the next character normally if possible
+ if (info->cursor < info->lastchar) {
+ info->cursor++;
+ el_deletestr(m_editline, 1);
+ return CC_REFRESH;
+ }
+
+ // Fail when at the end of the last line, except when ^D is pressed on the
+ // line is empty, in which case it is treated as EOF
+ if (m_current_line_index == m_input_lines.size() - 1) {
+ if (ch == 4 && info->buffer == info->lastchar) {
+ fprintf(m_output_file, "^D\n");
+ m_editor_status = EditorStatus::EndOfInput;
+ return CC_EOF;
+ }
+ return CC_ERROR;
+ }
+
+ // Prepare to combine this line with the one below
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
+
+ // Insert the next line of text at the cursor and restore the cursor position
+ const EditLineCharType *cursor = info->cursor;
+ el_winsertstr(m_editline, m_input_lines[m_current_line_index + 1].c_str());
+ info->cursor = cursor;
+ SaveEditedLine();
+
+ // Delete the extra line
+ m_input_lines.erase(m_input_lines.begin() + m_current_line_index + 1);
+
+ // Clear and repaint from this line on down
+ DisplayInput(m_current_line_index);
+ MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
+ return CC_REFRESH;
+}
+
+unsigned char Editline::DeletePreviousCharCommand(int ch) {
+ LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
+
+ // Just delete the previous character normally when not at the start of a
+ // line
+ if (info->cursor > info->buffer) {
+ el_deletestr(m_editline, 1);
+ return CC_REFRESH;
+ }
+
+ // No prior line and no prior character? Let the user know
+ if (m_current_line_index == 0)
+ return CC_ERROR;
+
+ // No prior character, but prior line? Combine with the line above
+ SaveEditedLine();
+ SetCurrentLine(m_current_line_index - 1);
+ auto priorLine = m_input_lines[m_current_line_index];
+ m_input_lines.erase(m_input_lines.begin() + m_current_line_index);
+ m_input_lines[m_current_line_index] =
+ priorLine + m_input_lines[m_current_line_index];
+
+ // Repaint from the new line down
+ fprintf(m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N,
+ CountRowsForLine(priorLine), 1);
+ DisplayInput(m_current_line_index);
+
+ // Put the cursor back where libedit expects it to be before returning to
+ // editing by telling libedit about the newly inserted text
+ MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
+ el_winsertstr(m_editline, priorLine.c_str());
+ return CC_REDISPLAY;
+}
+
+unsigned char Editline::PreviousLineCommand(int ch) {
+ SaveEditedLine();
+
+ if (m_current_line_index == 0) {
+ return RecallHistory(HistoryOperation::Older);
+ }
+
+ // Start from a known location
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
+
+ // Treat moving up from a blank last line as a deletion of that line
+ if (m_current_line_index == m_input_lines.size() - 1 && IsOnlySpaces()) {
+ m_input_lines.erase(m_input_lines.begin() + m_current_line_index);
+ fprintf(m_output_file, ANSI_CLEAR_BELOW);
+ }
+
+ SetCurrentLine(m_current_line_index - 1);
+ fprintf(m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N,
+ CountRowsForLine(m_input_lines[m_current_line_index]), 1);
+ return CC_NEWLINE;
+}
+
+unsigned char Editline::NextLineCommand(int ch) {
+ SaveEditedLine();
+
+ // Handle attempts to move down from the last line
+ if (m_current_line_index == m_input_lines.size() - 1) {
+ // Don't add an extra line if the existing last line is blank, move through
+ // history instead
+ if (IsOnlySpaces()) {
+ return RecallHistory(HistoryOperation::Newer);
+ }
+
+ // Determine indentation for the new line
+ int indentation = 0;
+ if (m_fix_indentation_callback) {
+ StringList lines = GetInputAsStringList();
+ lines.AppendString("");
+ indentation = m_fix_indentation_callback(this, lines, 0);
+ }
+ m_input_lines.insert(
+ m_input_lines.end(),
+ EditLineStringType(indentation, EditLineCharType(' ')));
+ }
+
+ // Move down past the current line using newlines to force scrolling if
+ // needed
+ SetCurrentLine(m_current_line_index + 1);
+ const LineInfoW *info = el_wline(m_editline);
+ int cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth());
+ int cursor_row = cursor_position / m_terminal_width;
+ for (int line_count = 0; line_count < m_current_line_rows - cursor_row;
+ line_count++) {
+ fprintf(m_output_file, "\n");
+ }
+ return CC_NEWLINE;
+}
+
+unsigned char Editline::PreviousHistoryCommand(int ch) {
+ SaveEditedLine();
+
+ return RecallHistory(HistoryOperation::Older);
+}
+
+unsigned char Editline::NextHistoryCommand(int ch) {
+ SaveEditedLine();
+
+ return RecallHistory(HistoryOperation::Newer);
+}
+
+unsigned char Editline::FixIndentationCommand(int ch) {
+ if (!m_fix_indentation_callback)
+ return CC_NORM;
+
+ // Insert the character typed before proceeding
+ EditLineCharType inserted[] = {(EditLineCharType)ch, 0};
+ el_winsertstr(m_editline, inserted);
+ LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
+ int cursor_position = info->cursor - info->buffer;
+
+ // Save the edits and determine the correct indentation level
+ SaveEditedLine();
+ StringList lines = GetInputAsStringList(m_current_line_index + 1);
+ int indent_correction =
+ m_fix_indentation_callback(this, lines, cursor_position);
+
+ // If it is already correct no special work is needed
+ if (indent_correction == 0)
+ return CC_REFRESH;
+
+ // Change the indentation level of the line
+ std::string currentLine = lines.GetStringAtIndex(m_current_line_index);
+ if (indent_correction > 0) {
+ currentLine = currentLine.insert(0, indent_correction, ' ');
+ } else {
+ currentLine = currentLine.erase(0, -indent_correction);
+ }
+#if LLDB_EDITLINE_USE_WCHAR
+ m_input_lines[m_current_line_index] = m_utf8conv.from_bytes(currentLine);
+#else
+ m_input_lines[m_current_line_index] = currentLine;
+#endif
+
+ // Update the display to reflect the change
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
+ DisplayInput(m_current_line_index);
+
+ // Reposition the cursor back on the original line and prepare to restart
+ // editing with a new cursor position
+ SetCurrentLine(m_current_line_index);
+ MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
+ m_revert_cursor_index = cursor_position + indent_correction;
+ return CC_NEWLINE;
+}
+
+unsigned char Editline::RevertLineCommand(int ch) {
+ el_winsertstr(m_editline, m_input_lines[m_current_line_index].c_str());
+ if (m_revert_cursor_index >= 0) {
+ LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
+ info->cursor = info->buffer + m_revert_cursor_index;
+ if (info->cursor > info->lastchar) {
+ info->cursor = info->lastchar;
+ }
+ m_revert_cursor_index = -1;
+ }
+ return CC_REFRESH;
+}
+
+unsigned char Editline::BufferStartCommand(int ch) {
+ SaveEditedLine();
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
+ SetCurrentLine(0);
+ m_revert_cursor_index = 0;
+ return CC_NEWLINE;
+}
+
+unsigned char Editline::BufferEndCommand(int ch) {
+ SaveEditedLine();
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd);
+ SetCurrentLine((int)m_input_lines.size() - 1);
+ MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
+ return CC_NEWLINE;
+}
+
+/// Prints completions and their descriptions to the given file. Only the
+/// completions in the interval [start, end) are printed.
+static void
+PrintCompletion(FILE *output_file,
+ llvm::ArrayRef<CompletionResult::Completion> results,
+ size_t max_len) {
+ for (const CompletionResult::Completion &c : results) {
+ fprintf(output_file, "\t%-*s", (int)max_len, c.GetCompletion().c_str());
+ if (!c.GetDescription().empty())
+ fprintf(output_file, " -- %s", c.GetDescription().c_str());
+ fprintf(output_file, "\n");
+ }
+}
+
+void Editline::DisplayCompletions(
+ Editline &editline, llvm::ArrayRef<CompletionResult::Completion> results) {
+ assert(!results.empty());
+
+ fprintf(editline.m_output_file,
+ "\n" ANSI_CLEAR_BELOW "Available completions:\n");
+ const size_t page_size = 40;
+ bool all = false;
+
+ auto longest =
+ std::max_element(results.begin(), results.end(), [](auto &c1, auto &c2) {
+ return c1.GetCompletion().size() < c2.GetCompletion().size();
+ });
+
+ const size_t max_len = longest->GetCompletion().size();
+
+ if (results.size() < page_size) {
+ PrintCompletion(editline.m_output_file, results, max_len);
+ return;
+ }
+
+ size_t cur_pos = 0;
+ while (cur_pos < results.size()) {
+ size_t remaining = results.size() - cur_pos;
+ size_t next_size = all ? remaining : std::min(page_size, remaining);
+
+ PrintCompletion(editline.m_output_file, results.slice(cur_pos, next_size),
+ max_len);
+
+ cur_pos += next_size;
+
+ if (cur_pos >= results.size())
+ break;
+
+ fprintf(editline.m_output_file, "More (Y/n/a): ");
+ // The type for the output and the type for the parameter are different,
+ // to allow interoperability with older versions of libedit. The container
+ // for the reply must be as wide as what our implementation is using,
+ // but libedit may use a narrower type depending on the build
+ // configuration.
+ EditLineGetCharType reply = L'n';
+ int got_char = el_wgetc(editline.m_editline,
+ reinterpret_cast<EditLineCharType *>(&reply));
+ // Check for a ^C or other interruption.
+ if (editline.m_editor_status == EditorStatus::Interrupted) {
+ editline.m_editor_status = EditorStatus::Editing;
+ fprintf(editline.m_output_file, "^C\n");
+ break;
+ }
+
+ fprintf(editline.m_output_file, "\n");
+ if (got_char == -1 || reply == 'n')
+ break;
+ if (reply == 'a')
+ all = true;
+ }
+}
+
+unsigned char Editline::TabCommand(int ch) {
+ if (!m_completion_callback)
+ return CC_ERROR;
+
+ const LineInfo *line_info = el_line(m_editline);
+
+ llvm::StringRef line(line_info->buffer,
+ line_info->lastchar - line_info->buffer);
+ unsigned cursor_index = line_info->cursor - line_info->buffer;
+ CompletionResult result;
+ CompletionRequest request(line, cursor_index, result);
+
+ m_completion_callback(request);
+
+ llvm::ArrayRef<CompletionResult::Completion> results = result.GetResults();
+
+ StringList completions;
+ result.GetMatches(completions);
+
+ if (results.size() == 0)
+ return CC_ERROR;
+
+ if (results.size() == 1) {
+ CompletionResult::Completion completion = results.front();
+ switch (completion.GetMode()) {
+ case CompletionMode::Normal: {
+ std::string to_add = completion.GetCompletion();
+ // Terminate the current argument with a quote if it started with a quote.
+ Args &parsedLine = request.GetParsedLine();
+ if (!parsedLine.empty() && request.GetCursorIndex() < parsedLine.size() &&
+ request.GetParsedArg().IsQuoted()) {
+ to_add.push_back(request.GetParsedArg().GetQuoteChar());
+ }
+ to_add.push_back(' ');
+ el_deletestr(m_editline, request.GetCursorArgumentPrefix().size());
+ el_insertstr(m_editline, to_add.c_str());
+ // Clear all the autosuggestion parts if the only single space can be completed.
+ if (to_add == " ")
+ return CC_REDISPLAY;
+ return CC_REFRESH;
+ }
+ case CompletionMode::Partial: {
+ std::string to_add = completion.GetCompletion();
+ to_add = to_add.substr(request.GetCursorArgumentPrefix().size());
+ el_insertstr(m_editline, to_add.c_str());
+ break;
+ }
+ case CompletionMode::RewriteLine: {
+ el_deletestr(m_editline, line_info->cursor - line_info->buffer);
+ el_insertstr(m_editline, completion.GetCompletion().c_str());
+ break;
+ }
+ }
+ return CC_REDISPLAY;
+ }
+
+ // If we get a longer match display that first.
+ std::string longest_prefix = completions.LongestCommonPrefix();
+ if (!longest_prefix.empty())
+ longest_prefix =
+ longest_prefix.substr(request.GetCursorArgumentPrefix().size());
+ if (!longest_prefix.empty()) {
+ el_insertstr(m_editline, longest_prefix.c_str());
+ return CC_REDISPLAY;
+ }
+
+ DisplayCompletions(*this, results);
+
+ DisplayInput();
+ MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
+ return CC_REDISPLAY;
+}
+
+unsigned char Editline::ApplyAutosuggestCommand(int ch) {
+ if (!m_suggestion_callback) {
+ return CC_REDISPLAY;
+ }
+
+ const LineInfo *line_info = el_line(m_editline);
+ llvm::StringRef line(line_info->buffer,
+ line_info->lastchar - line_info->buffer);
+
+ if (std::optional<std::string> to_add = m_suggestion_callback(line))
+ el_insertstr(m_editline, to_add->c_str());
+
+ return CC_REDISPLAY;
+}
+
+unsigned char Editline::TypedCharacter(int ch) {
+ std::string typed = std::string(1, ch);
+ el_insertstr(m_editline, typed.c_str());
+
+ if (!m_suggestion_callback) {
+ return CC_REDISPLAY;
+ }
+
+ const LineInfo *line_info = el_line(m_editline);
+ llvm::StringRef line(line_info->buffer,
+ line_info->lastchar - line_info->buffer);
+
+ if (std::optional<std::string> to_add = m_suggestion_callback(line)) {
+ std::string to_add_color =
+ m_suggestion_ansi_prefix + to_add.value() + m_suggestion_ansi_suffix;
+ fputs(typed.c_str(), m_output_file);
+ fputs(to_add_color.c_str(), m_output_file);
+ size_t new_autosuggestion_size = line.size() + to_add->length();
+ // Print spaces to hide any remains of a previous longer autosuggestion.
+ if (new_autosuggestion_size < m_previous_autosuggestion_size) {
+ size_t spaces_to_print =
+ m_previous_autosuggestion_size - new_autosuggestion_size;
+ std::string spaces = std::string(spaces_to_print, ' ');
+ fputs(spaces.c_str(), m_output_file);
+ }
+ m_previous_autosuggestion_size = new_autosuggestion_size;
+
+ int editline_cursor_position =
+ (int)((line_info->cursor - line_info->buffer) + GetPromptWidth());
+ int editline_cursor_row = editline_cursor_position / m_terminal_width;
+ int toColumn =
+ editline_cursor_position - (editline_cursor_row * m_terminal_width);
+ fprintf(m_output_file, ANSI_SET_COLUMN_N, toColumn);
+ return CC_REFRESH;
+ }
+
+ return CC_REDISPLAY;
+}
+
+void Editline::AddFunctionToEditLine(const EditLineCharType *command,
+ const EditLineCharType *helptext,
+ EditlineCommandCallbackType callbackFn) {
+ el_wset(m_editline, EL_ADDFN, command, helptext, callbackFn);
+}
+
+void Editline::SetEditLinePromptCallback(
+ EditlinePromptCallbackType callbackFn) {
+ el_set(m_editline, EL_PROMPT, callbackFn);
+}
+
+void Editline::SetGetCharacterFunction(EditlineGetCharCallbackType callbackFn) {
+ el_wset(m_editline, EL_GETCFN, callbackFn);
+}
+
+void Editline::ConfigureEditor(bool multiline) {
+ if (m_editline && m_multiline_enabled == multiline)
+ return;
+ m_multiline_enabled = multiline;
+
+ if (m_editline) {
+ // Disable edit mode to stop the terminal from flushing all input during
+ // the call to el_end() since we expect to have multiple editline instances
+ // in this program.
+ el_set(m_editline, EL_EDITMODE, 0);
+ el_end(m_editline);
+ }
+
+ m_editline =
+ el_init(m_editor_name.c_str(), m_input_file, m_output_file, m_error_file);
+ ApplyTerminalSizeChange();
+
+ if (m_history_sp && m_history_sp->IsValid()) {
+ if (!m_history_sp->Load()) {
+ fputs("Could not load history file\n.", m_output_file);
+ }
+ el_wset(m_editline, EL_HIST, history, m_history_sp->GetHistoryPtr());
+ }
+ el_set(m_editline, EL_CLIENTDATA, this);
+ el_set(m_editline, EL_SIGNAL, 0);
+ el_set(m_editline, EL_EDITOR, "emacs");
+
+ SetGetCharacterFunction([](EditLine *editline, EditLineGetCharType *c) {
+ return Editline::InstanceFor(editline)->GetCharacter(c);
+ });
+
+ SetEditLinePromptCallback([](EditLine *editline) {
+ return Editline::InstanceFor(editline)->Prompt();
+ });
+
+ // Commands used for multiline support, registered whether or not they're
+ // used
+ AddFunctionToEditLine(
+ EditLineConstString("lldb-break-line"),
+ EditLineConstString("Insert a line break"),
+ [](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->BreakLineCommand(ch);
+ });
+
+ AddFunctionToEditLine(
+ EditLineConstString("lldb-end-or-add-line"),
+ EditLineConstString("End editing or continue when incomplete"),
+ [](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->EndOrAddLineCommand(ch);
+ });
+ AddFunctionToEditLine(
+ EditLineConstString("lldb-delete-next-char"),
+ EditLineConstString("Delete next character"),
+ [](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->DeleteNextCharCommand(ch);
+ });
+ AddFunctionToEditLine(
+ EditLineConstString("lldb-delete-previous-char"),
+ EditLineConstString("Delete previous character"),
+ [](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->DeletePreviousCharCommand(ch);
+ });
+ AddFunctionToEditLine(
+ EditLineConstString("lldb-previous-line"),
+ EditLineConstString("Move to previous line"),
+ [](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->PreviousLineCommand(ch);
+ });
+ AddFunctionToEditLine(
+ EditLineConstString("lldb-next-line"),
+ EditLineConstString("Move to next line"), [](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->NextLineCommand(ch);
+ });
+ AddFunctionToEditLine(
+ EditLineConstString("lldb-previous-history"),
+ EditLineConstString("Move to previous history"),
+ [](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->PreviousHistoryCommand(ch);
+ });
+ AddFunctionToEditLine(
+ EditLineConstString("lldb-next-history"),
+ EditLineConstString("Move to next history"),
+ [](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->NextHistoryCommand(ch);
+ });
+ AddFunctionToEditLine(
+ EditLineConstString("lldb-buffer-start"),
+ EditLineConstString("Move to start of buffer"),
+ [](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->BufferStartCommand(ch);
+ });
+ AddFunctionToEditLine(
+ EditLineConstString("lldb-buffer-end"),
+ EditLineConstString("Move to end of buffer"),
+ [](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->BufferEndCommand(ch);
+ });
+ AddFunctionToEditLine(
+ EditLineConstString("lldb-fix-indentation"),
+ EditLineConstString("Fix line indentation"),
+ [](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->FixIndentationCommand(ch);
+ });
+
+ // Register the complete callback under two names for compatibility with
+ // older clients using custom .editrc files (largely because libedit has a
+ // bad bug where if you have a bind command that tries to bind to a function
+ // name that doesn't exist, it can corrupt the heap and crash your process
+ // later.)
+ EditlineCommandCallbackType complete_callback = [](EditLine *editline,
+ int ch) {
+ return Editline::InstanceFor(editline)->TabCommand(ch);
+ };
+ AddFunctionToEditLine(EditLineConstString("lldb-complete"),
+ EditLineConstString("Invoke completion"),
+ complete_callback);
+ AddFunctionToEditLine(EditLineConstString("lldb_complete"),
+ EditLineConstString("Invoke completion"),
+ complete_callback);
+
+ // General bindings we don't mind being overridden
+ if (!multiline) {
+ el_set(m_editline, EL_BIND, "^r", "em-inc-search-prev",
+ NULL); // Cycle through backwards search, entering string
+
+ if (m_suggestion_callback) {
+ AddFunctionToEditLine(
+ EditLineConstString("lldb-apply-complete"),
+ EditLineConstString("Adopt autocompletion"),
+ [](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->ApplyAutosuggestCommand(ch);
+ });
+
+ el_set(m_editline, EL_BIND, "^f", "lldb-apply-complete",
+ NULL); // Apply a part that is suggested automatically
+
+ AddFunctionToEditLine(
+ EditLineConstString("lldb-typed-character"),
+ EditLineConstString("Typed character"),
+ [](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->TypedCharacter(ch);
+ });
+
+ char bind_key[2] = {0, 0};
+ llvm::StringRef ascii_chars =
+ "abcdefghijklmnopqrstuvwxzyABCDEFGHIJKLMNOPQRSTUVWXZY1234567890!\"#$%"
+ "&'()*+,./:;<=>?@[]_`{|}~ ";
+ for (char c : ascii_chars) {
+ bind_key[0] = c;
+ el_set(m_editline, EL_BIND, bind_key, "lldb-typed-character", NULL);
+ }
+ el_set(m_editline, EL_BIND, "\\-", "lldb-typed-character", NULL);
+ el_set(m_editline, EL_BIND, "\\^", "lldb-typed-character", NULL);
+ el_set(m_editline, EL_BIND, "\\\\", "lldb-typed-character", NULL);
+ }
+ }
+
+ el_set(m_editline, EL_BIND, "^w", "ed-delete-prev-word",
+ NULL); // Delete previous word, behave like bash in emacs mode
+ el_set(m_editline, EL_BIND, "\t", "lldb-complete",
+ NULL); // Bind TAB to auto complete
+
+ // Allow ctrl-left-arrow and ctrl-right-arrow for navigation, behave like
+ // bash in emacs mode.
+ el_set(m_editline, EL_BIND, ESCAPE "[1;5C", "em-next-word", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE "[1;5D", "ed-prev-word", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE "[5C", "em-next-word", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE "[5D", "ed-prev-word", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[C", "em-next-word", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[D", "ed-prev-word", NULL);
+
+ // Allow user-specific customization prior to registering bindings we
+ // absolutely require
+ el_source(m_editline, nullptr);
+
+ // Register an internal binding that external developers shouldn't use
+ AddFunctionToEditLine(
+ EditLineConstString("lldb-revert-line"),
+ EditLineConstString("Revert line to saved state"),
+ [](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->RevertLineCommand(ch);
+ });
+
+ // Register keys that perform auto-indent correction
+ if (m_fix_indentation_callback && m_fix_indentation_callback_chars) {
+ char bind_key[2] = {0, 0};
+ const char *indent_chars = m_fix_indentation_callback_chars;
+ while (*indent_chars) {
+ bind_key[0] = *indent_chars;
+ el_set(m_editline, EL_BIND, bind_key, "lldb-fix-indentation", NULL);
+ ++indent_chars;
+ }
+ }
+
+ // Multi-line editor bindings
+ if (multiline) {
+ el_set(m_editline, EL_BIND, "\n", "lldb-end-or-add-line", NULL);
+ el_set(m_editline, EL_BIND, "\r", "lldb-end-or-add-line", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE "\n", "lldb-break-line", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE "\r", "lldb-break-line", NULL);
+ el_set(m_editline, EL_BIND, "^p", "lldb-previous-line", NULL);
+ el_set(m_editline, EL_BIND, "^n", "lldb-next-line", NULL);
+ el_set(m_editline, EL_BIND, "^?", "lldb-delete-previous-char", NULL);
+ el_set(m_editline, EL_BIND, "^d", "lldb-delete-next-char", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE "[3~", "lldb-delete-next-char", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE "[\\^", "lldb-revert-line", NULL);
+
+ // Editor-specific bindings
+ if (IsEmacs()) {
+ el_set(m_editline, EL_BIND, ESCAPE "<", "lldb-buffer-start", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE ">", "lldb-buffer-end", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE "[A", "lldb-previous-line", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE "[B", "lldb-next-line", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[A", "lldb-previous-history",
+ NULL);
+ el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[B", "lldb-next-history",
+ NULL);
+ el_set(m_editline, EL_BIND, ESCAPE "[1;3A", "lldb-previous-history",
+ NULL);
+ el_set(m_editline, EL_BIND, ESCAPE "[1;3B", "lldb-next-history", NULL);
+ } else {
+ el_set(m_editline, EL_BIND, "^H", "lldb-delete-previous-char", NULL);
+
+ el_set(m_editline, EL_BIND, "-a", ESCAPE "[A", "lldb-previous-line",
+ NULL);
+ el_set(m_editline, EL_BIND, "-a", ESCAPE "[B", "lldb-next-line", NULL);
+ el_set(m_editline, EL_BIND, "-a", "x", "lldb-delete-next-char", NULL);
+ el_set(m_editline, EL_BIND, "-a", "^H", "lldb-delete-previous-char",
+ NULL);
+ el_set(m_editline, EL_BIND, "-a", "^?", "lldb-delete-previous-char",
+ NULL);
+
+ // Escape is absorbed exiting edit mode, so re-register important
+ // sequences without the prefix
+ el_set(m_editline, EL_BIND, "-a", "[A", "lldb-previous-line", NULL);
+ el_set(m_editline, EL_BIND, "-a", "[B", "lldb-next-line", NULL);
+ el_set(m_editline, EL_BIND, "-a", "[\\^", "lldb-revert-line", NULL);
+ }
+ }
+}
+
+// Editline public methods
+
+Editline *Editline::InstanceFor(EditLine *editline) {
+ Editline *editor;
+ el_get(editline, EL_CLIENTDATA, &editor);
+ return editor;
+}
+
+Editline::Editline(const char *editline_name, FILE *input_file,
+ FILE *output_file, FILE *error_file,
+ std::recursive_mutex &output_mutex)
+ : m_editor_status(EditorStatus::Complete), m_input_file(input_file),
+ m_output_file(output_file), m_error_file(error_file),
+ m_input_connection(fileno(input_file), false),
+ m_output_mutex(output_mutex) {
+ // Get a shared history instance
+ m_editor_name = (editline_name == nullptr) ? "lldb-tmp" : editline_name;
+ m_history_sp = EditlineHistory::GetHistory(m_editor_name);
+}
+
+Editline::~Editline() {
+ if (m_editline) {
+ // Disable edit mode to stop the terminal from flushing all input during
+ // the call to el_end() since we expect to have multiple editline instances
+ // in this program.
+ el_set(m_editline, EL_EDITMODE, 0);
+ el_end(m_editline);
+ m_editline = nullptr;
+ }
+
+ // EditlineHistory objects are sometimes shared between multiple Editline
+ // instances with the same program name. So just release our shared pointer
+ // and if we are the last owner, it will save the history to the history save
+ // file automatically.
+ m_history_sp.reset();
+}
+
+void Editline::SetPrompt(const char *prompt) {
+ m_set_prompt = prompt == nullptr ? "" : prompt;
+}
+
+void Editline::SetContinuationPrompt(const char *continuation_prompt) {
+ m_set_continuation_prompt =
+ continuation_prompt == nullptr ? "" : continuation_prompt;
+}
+
+void Editline::TerminalSizeChanged() { m_terminal_size_has_changed = 1; }
+
+void Editline::ApplyTerminalSizeChange() {
+ if (!m_editline)
+ return;
+
+ m_terminal_size_has_changed = 0;
+ el_resize(m_editline);
+ int columns;
+ // This function is documenting as taking (const char *, void *) for the
+ // vararg part, but in reality in was consuming arguments until the first
+ // null pointer. This was fixed in libedit in April 2019
+ // <http://mail-index.netbsd.org/source-changes/2019/04/26/msg105454.html>,
+ // but we're keeping the workaround until a version with that fix is more
+ // widely available.
+ if (el_get(m_editline, EL_GETTC, "co", &columns, nullptr) == 0) {
+ m_terminal_width = columns;
+ if (m_current_line_rows != -1) {
+ const LineInfoW *info = el_wline(m_editline);
+ int lineLength =
+ (int)((info->lastchar - info->buffer) + GetPromptWidth());
+ m_current_line_rows = (lineLength / columns) + 1;
+ }
+ } else {
+ m_terminal_width = INT_MAX;
+ m_current_line_rows = 1;
+ }
+}
+
+const char *Editline::GetPrompt() { return m_set_prompt.c_str(); }
+
+uint32_t Editline::GetCurrentLine() { return m_current_line_index; }
+
+bool Editline::Interrupt() {
+ bool result = true;
+ std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
+ if (m_editor_status == EditorStatus::Editing) {
+ fprintf(m_output_file, "^C\n");
+ result = m_input_connection.InterruptRead();
+ }
+ m_editor_status = EditorStatus::Interrupted;
+ return result;
+}
+
+bool Editline::Cancel() {
+ bool result = true;
+ std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
+ if (m_editor_status == EditorStatus::Editing) {
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
+ fprintf(m_output_file, ANSI_CLEAR_BELOW);
+ result = m_input_connection.InterruptRead();
+ }
+ m_editor_status = EditorStatus::Interrupted;
+ return result;
+}
+
+bool Editline::GetLine(std::string &line, bool &interrupted) {
+ ConfigureEditor(false);
+ m_input_lines = std::vector<EditLineStringType>();
+ m_input_lines.insert(m_input_lines.begin(), EditLineConstString(""));
+
+ std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
+
+ lldbassert(m_editor_status != EditorStatus::Editing);
+ if (m_editor_status == EditorStatus::Interrupted) {
+ m_editor_status = EditorStatus::Complete;
+ interrupted = true;
+ return true;
+ }
+
+ SetCurrentLine(0);
+ m_in_history = false;
+ m_editor_status = EditorStatus::Editing;
+ m_revert_cursor_index = -1;
+
+ int count;
+ auto input = el_wgets(m_editline, &count);
+
+ interrupted = m_editor_status == EditorStatus::Interrupted;
+ if (!interrupted) {
+ if (input == nullptr) {
+ fprintf(m_output_file, "\n");
+ m_editor_status = EditorStatus::EndOfInput;
+ } else {
+ m_history_sp->Enter(input);
+#if LLDB_EDITLINE_USE_WCHAR
+ line = m_utf8conv.to_bytes(SplitLines(input)[0]);
+#else
+ line = SplitLines(input)[0];
+#endif
+ m_editor_status = EditorStatus::Complete;
+ }
+ }
+ return m_editor_status != EditorStatus::EndOfInput;
+}
+
+bool Editline::GetLines(int first_line_number, StringList &lines,
+ bool &interrupted) {
+ ConfigureEditor(true);
+
+ // Print the initial input lines, then move the cursor back up to the start
+ // of input
+ SetBaseLineNumber(first_line_number);
+ m_input_lines = std::vector<EditLineStringType>();
+ m_input_lines.insert(m_input_lines.begin(), EditLineConstString(""));
+
+ std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
+ // Begin the line editing loop
+ DisplayInput();
+ SetCurrentLine(0);
+ MoveCursor(CursorLocation::BlockEnd, CursorLocation::BlockStart);
+ m_editor_status = EditorStatus::Editing;
+ m_in_history = false;
+
+ m_revert_cursor_index = -1;
+ while (m_editor_status == EditorStatus::Editing) {
+ int count;
+ m_current_line_rows = -1;
+ el_wpush(m_editline, EditLineConstString(
+ "\x1b[^")); // Revert to the existing line content
+ el_wgets(m_editline, &count);
+ }
+
+ interrupted = m_editor_status == EditorStatus::Interrupted;
+ if (!interrupted) {
+ // Save the completed entry in history before returning. Don't save empty
+ // input as that just clutters the command history.
+ if (!m_input_lines.empty())
+ m_history_sp->Enter(CombineLines(m_input_lines).c_str());
+
+ lines = GetInputAsStringList();
+ }
+ return m_editor_status != EditorStatus::EndOfInput;
+}
+
+void Editline::PrintAsync(Stream *stream, const char *s, size_t len) {
+ std::lock_guard<std::recursive_mutex> guard(m_output_mutex);
+ if (m_editor_status == EditorStatus::Editing) {
+ SaveEditedLine();
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
+ fprintf(m_output_file, ANSI_CLEAR_BELOW);
+ }
+ stream->Write(s, len);
+ stream->Flush();
+ if (m_editor_status == EditorStatus::Editing) {
+ DisplayInput();
+ MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
+ }
+}
+
+bool Editline::CompleteCharacter(char ch, EditLineGetCharType &out) {
+#if !LLDB_EDITLINE_USE_WCHAR
+ if (ch == (char)EOF)
+ return false;
+
+ out = (unsigned char)ch;
+ return true;
+#else
+ std::codecvt_utf8<wchar_t> cvt;
+ llvm::SmallString<4> input;
+ for (;;) {
+ const char *from_next;
+ wchar_t *to_next;
+ std::mbstate_t state = std::mbstate_t();
+ input.push_back(ch);
+ switch (cvt.in(state, input.begin(), input.end(), from_next, &out, &out + 1,
+ to_next)) {
+ case std::codecvt_base::ok:
+ return out != (EditLineGetCharType)WEOF;
+
+ case std::codecvt_base::error:
+ case std::codecvt_base::noconv:
+ return false;
+
+ case std::codecvt_base::partial:
+ lldb::ConnectionStatus status;
+ size_t read_count = m_input_connection.Read(
+ &ch, 1, std::chrono::seconds(0), status, nullptr);
+ if (read_count == 0)
+ return false;
+ break;
+ }
+ }
+#endif
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/File.cpp b/contrib/llvm-project/lldb/source/Host/common/File.cpp
new file mode 100644
index 000000000000..174feecf2de0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/File.cpp
@@ -0,0 +1,907 @@
+//===-- File.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/File.h"
+
+#include <cerrno>
+#include <climits>
+#include <cstdarg>
+#include <cstdio>
+#include <fcntl.h>
+#include <optional>
+
+#ifdef _WIN32
+#include "lldb/Host/windows/windows.h"
+#else
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <unistd.h>
+#endif
+
+#include "lldb/Host/Config.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/VASPrintf.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/Errno.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Process.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using llvm::Expected;
+
+Expected<const char *>
+File::GetStreamOpenModeFromOptions(File::OpenOptions options) {
+ File::OpenOptions rw =
+ options & (File::eOpenOptionReadOnly | File::eOpenOptionWriteOnly |
+ File::eOpenOptionReadWrite);
+
+ if (options & File::eOpenOptionAppend) {
+ if (rw == File::eOpenOptionReadWrite) {
+ if (options & File::eOpenOptionCanCreateNewOnly)
+ return "a+x";
+ else
+ return "a+";
+ } else if (rw == File::eOpenOptionWriteOnly) {
+ if (options & File::eOpenOptionCanCreateNewOnly)
+ return "ax";
+ else
+ return "a";
+ }
+ } else if (rw == File::eOpenOptionReadWrite) {
+ if (options & File::eOpenOptionCanCreate) {
+ if (options & File::eOpenOptionCanCreateNewOnly)
+ return "w+x";
+ else
+ return "w+";
+ } else
+ return "r+";
+ } else if (rw == File::eOpenOptionWriteOnly) {
+ return "w";
+ } else if (rw == File::eOpenOptionReadOnly) {
+ return "r";
+ }
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "invalid options, cannot convert to mode string");
+}
+
+Expected<File::OpenOptions> File::GetOptionsFromMode(llvm::StringRef mode) {
+ OpenOptions opts =
+ llvm::StringSwitch<OpenOptions>(mode)
+ .Cases("r", "rb", eOpenOptionReadOnly)
+ .Cases("w", "wb", eOpenOptionWriteOnly)
+ .Cases("a", "ab",
+ eOpenOptionWriteOnly | eOpenOptionAppend |
+ eOpenOptionCanCreate)
+ .Cases("r+", "rb+", "r+b", eOpenOptionReadWrite)
+ .Cases("w+", "wb+", "w+b",
+ eOpenOptionReadWrite | eOpenOptionCanCreate |
+ eOpenOptionTruncate)
+ .Cases("a+", "ab+", "a+b",
+ eOpenOptionReadWrite | eOpenOptionAppend |
+ eOpenOptionCanCreate)
+ .Default(eOpenOptionInvalid);
+ if (opts != eOpenOptionInvalid)
+ return opts;
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "invalid mode, cannot convert to File::OpenOptions");
+}
+
+int File::kInvalidDescriptor = -1;
+FILE *File::kInvalidStream = nullptr;
+
+Status File::Read(void *buf, size_t &num_bytes) {
+ return std::error_code(ENOTSUP, std::system_category());
+}
+Status File::Write(const void *buf, size_t &num_bytes) {
+ return std::error_code(ENOTSUP, std::system_category());
+}
+
+bool File::IsValid() const { return false; }
+
+Status File::Close() { return Flush(); }
+
+IOObject::WaitableHandle File::GetWaitableHandle() {
+ return IOObject::kInvalidHandleValue;
+}
+
+Status File::GetFileSpec(FileSpec &file_spec) const {
+ file_spec.Clear();
+ return std::error_code(ENOTSUP, std::system_category());
+}
+
+int File::GetDescriptor() const { return kInvalidDescriptor; }
+
+FILE *File::GetStream() { return nullptr; }
+
+off_t File::SeekFromStart(off_t offset, Status *error_ptr) {
+ if (error_ptr)
+ *error_ptr = std::error_code(ENOTSUP, std::system_category());
+ return -1;
+}
+
+off_t File::SeekFromCurrent(off_t offset, Status *error_ptr) {
+ if (error_ptr)
+ *error_ptr = std::error_code(ENOTSUP, std::system_category());
+ return -1;
+}
+
+off_t File::SeekFromEnd(off_t offset, Status *error_ptr) {
+ if (error_ptr)
+ *error_ptr = std::error_code(ENOTSUP, std::system_category());
+ return -1;
+}
+
+Status File::Read(void *dst, size_t &num_bytes, off_t &offset) {
+ return std::error_code(ENOTSUP, std::system_category());
+}
+
+Status File::Write(const void *src, size_t &num_bytes, off_t &offset) {
+ return std::error_code(ENOTSUP, std::system_category());
+}
+
+Status File::Flush() { return Status(); }
+
+Status File::Sync() { return Flush(); }
+
+void File::CalculateInteractiveAndTerminal() {
+ const int fd = GetDescriptor();
+ if (!DescriptorIsValid(fd)) {
+ m_is_interactive = eLazyBoolNo;
+ m_is_real_terminal = eLazyBoolNo;
+ m_supports_colors = eLazyBoolNo;
+ return;
+ }
+ m_is_interactive = eLazyBoolNo;
+ m_is_real_terminal = eLazyBoolNo;
+#if defined(_WIN32)
+ if (_isatty(fd)) {
+ m_is_interactive = eLazyBoolYes;
+ m_is_real_terminal = eLazyBoolYes;
+#if defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING)
+ m_supports_colors = eLazyBoolYes;
+#endif
+ }
+#else
+ if (isatty(fd)) {
+ m_is_interactive = eLazyBoolYes;
+ struct winsize window_size;
+ if (::ioctl(fd, TIOCGWINSZ, &window_size) == 0) {
+ if (window_size.ws_col > 0) {
+ m_is_real_terminal = eLazyBoolYes;
+ if (llvm::sys::Process::FileDescriptorHasColors(fd))
+ m_supports_colors = eLazyBoolYes;
+ }
+ }
+ }
+#endif
+}
+
+bool File::GetIsInteractive() {
+ if (m_is_interactive == eLazyBoolCalculate)
+ CalculateInteractiveAndTerminal();
+ return m_is_interactive == eLazyBoolYes;
+}
+
+bool File::GetIsRealTerminal() {
+ if (m_is_real_terminal == eLazyBoolCalculate)
+ CalculateInteractiveAndTerminal();
+ return m_is_real_terminal == eLazyBoolYes;
+}
+
+bool File::GetIsTerminalWithColors() {
+ if (m_supports_colors == eLazyBoolCalculate)
+ CalculateInteractiveAndTerminal();
+ return m_supports_colors == eLazyBoolYes;
+}
+
+size_t File::Printf(const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ size_t result = PrintfVarArg(format, args);
+ va_end(args);
+ return result;
+}
+
+size_t File::PrintfVarArg(const char *format, va_list args) {
+ llvm::SmallString<0> s;
+ if (VASprintf(s, format, args)) {
+ size_t written = s.size();
+ Write(s.data(), written);
+ return written;
+ }
+ return 0;
+}
+
+Expected<File::OpenOptions> File::GetOptions() const {
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "GetOptions() not implemented for this File class");
+}
+
+uint32_t File::GetPermissions(Status &error) const {
+ int fd = GetDescriptor();
+ if (!DescriptorIsValid(fd)) {
+ error = std::error_code(ENOTSUP, std::system_category());
+ return 0;
+ }
+ struct stat file_stats;
+ if (::fstat(fd, &file_stats) == -1) {
+ error.SetErrorToErrno();
+ return 0;
+ }
+ error.Clear();
+ return file_stats.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
+}
+
+bool NativeFile::IsValid() const {
+ std::scoped_lock<std::mutex, std::mutex> lock(m_descriptor_mutex, m_stream_mutex);
+ return DescriptorIsValidUnlocked() || StreamIsValidUnlocked();
+}
+
+Expected<File::OpenOptions> NativeFile::GetOptions() const { return m_options; }
+
+int NativeFile::GetDescriptor() const {
+ if (ValueGuard descriptor_guard = DescriptorIsValid()) {
+ return m_descriptor;
+ }
+
+ // Don't open the file descriptor if we don't need to, just get it from the
+ // stream if we have one.
+ if (ValueGuard stream_guard = StreamIsValid()) {
+#if defined(_WIN32)
+ return _fileno(m_stream);
+#else
+ return fileno(m_stream);
+#endif
+ }
+
+ // Invalid descriptor and invalid stream, return invalid descriptor.
+ return kInvalidDescriptor;
+}
+
+IOObject::WaitableHandle NativeFile::GetWaitableHandle() {
+ return GetDescriptor();
+}
+
+FILE *NativeFile::GetStream() {
+ ValueGuard stream_guard = StreamIsValid();
+ if (!stream_guard) {
+ if (ValueGuard descriptor_guard = DescriptorIsValid()) {
+ auto mode = GetStreamOpenModeFromOptions(m_options);
+ if (!mode)
+ llvm::consumeError(mode.takeError());
+ else {
+ if (!m_own_descriptor) {
+// We must duplicate the file descriptor if we don't own it because when you
+// call fdopen, the stream will own the fd
+#ifdef _WIN32
+ m_descriptor = ::_dup(m_descriptor);
+#else
+ m_descriptor = dup(m_descriptor);
+#endif
+ m_own_descriptor = true;
+ }
+
+ m_stream = llvm::sys::RetryAfterSignal(nullptr, ::fdopen, m_descriptor,
+ mode.get());
+
+ // If we got a stream, then we own the stream and should no longer own
+ // the descriptor because fclose() will close it for us
+
+ if (m_stream) {
+ m_own_stream = true;
+ m_own_descriptor = false;
+ }
+ }
+ }
+ }
+ return m_stream;
+}
+
+Status NativeFile::Close() {
+ std::scoped_lock<std::mutex, std::mutex> lock(m_descriptor_mutex, m_stream_mutex);
+
+ Status error;
+
+ if (StreamIsValidUnlocked()) {
+ if (m_own_stream) {
+ if (::fclose(m_stream) == EOF)
+ error.SetErrorToErrno();
+ } else {
+ File::OpenOptions rw =
+ m_options & (File::eOpenOptionReadOnly | File::eOpenOptionWriteOnly |
+ File::eOpenOptionReadWrite);
+
+ if (rw == eOpenOptionWriteOnly || rw == eOpenOptionReadWrite) {
+ if (::fflush(m_stream) == EOF)
+ error.SetErrorToErrno();
+ }
+ }
+ }
+
+ if (DescriptorIsValidUnlocked() && m_own_descriptor) {
+ if (::close(m_descriptor) != 0)
+ error.SetErrorToErrno();
+ }
+
+ m_stream = kInvalidStream;
+ m_own_stream = false;
+ m_descriptor = kInvalidDescriptor;
+ m_own_descriptor = false;
+ m_options = OpenOptions(0);
+ m_is_interactive = eLazyBoolCalculate;
+ m_is_real_terminal = eLazyBoolCalculate;
+ return error;
+}
+
+Status NativeFile::GetFileSpec(FileSpec &file_spec) const {
+ Status error;
+#ifdef F_GETPATH
+ if (IsValid()) {
+ char path[PATH_MAX];
+ if (::fcntl(GetDescriptor(), F_GETPATH, path) == -1)
+ error.SetErrorToErrno();
+ else
+ file_spec.SetFile(path, FileSpec::Style::native);
+ } else {
+ error.SetErrorString("invalid file handle");
+ }
+#elif defined(__linux__)
+ char proc[64];
+ char path[PATH_MAX];
+ if (::snprintf(proc, sizeof(proc), "/proc/self/fd/%d", GetDescriptor()) < 0)
+ error.SetErrorString("cannot resolve file descriptor");
+ else {
+ ssize_t len;
+ if ((len = ::readlink(proc, path, sizeof(path) - 1)) == -1)
+ error.SetErrorToErrno();
+ else {
+ path[len] = '\0';
+ file_spec.SetFile(path, FileSpec::Style::native);
+ }
+ }
+#else
+ error.SetErrorString(
+ "NativeFile::GetFileSpec is not supported on this platform");
+#endif
+
+ if (error.Fail())
+ file_spec.Clear();
+ return error;
+}
+
+off_t NativeFile::SeekFromStart(off_t offset, Status *error_ptr) {
+ off_t result = 0;
+ if (ValueGuard descriptor_guard = DescriptorIsValid()) {
+ result = ::lseek(m_descriptor, offset, SEEK_SET);
+
+ if (error_ptr) {
+ if (result == -1)
+ error_ptr->SetErrorToErrno();
+ else
+ error_ptr->Clear();
+ }
+ return result;
+ }
+
+ if (ValueGuard stream_guard = StreamIsValid()) {
+ result = ::fseek(m_stream, offset, SEEK_SET);
+
+ if (error_ptr) {
+ if (result == -1)
+ error_ptr->SetErrorToErrno();
+ else
+ error_ptr->Clear();
+ }
+ return result;
+ }
+
+ if (error_ptr)
+ error_ptr->SetErrorString("invalid file handle");
+ return result;
+}
+
+off_t NativeFile::SeekFromCurrent(off_t offset, Status *error_ptr) {
+ off_t result = -1;
+ if (ValueGuard descriptor_guard = DescriptorIsValid()) {
+ result = ::lseek(m_descriptor, offset, SEEK_CUR);
+
+ if (error_ptr) {
+ if (result == -1)
+ error_ptr->SetErrorToErrno();
+ else
+ error_ptr->Clear();
+ }
+ return result;
+ }
+
+ if (ValueGuard stream_guard = StreamIsValid()) {
+ result = ::fseek(m_stream, offset, SEEK_CUR);
+
+ if (error_ptr) {
+ if (result == -1)
+ error_ptr->SetErrorToErrno();
+ else
+ error_ptr->Clear();
+ }
+ return result;
+ }
+
+ if (error_ptr)
+ error_ptr->SetErrorString("invalid file handle");
+ return result;
+}
+
+off_t NativeFile::SeekFromEnd(off_t offset, Status *error_ptr) {
+ off_t result = -1;
+ if (ValueGuard descriptor_guard = DescriptorIsValid()) {
+ result = ::lseek(m_descriptor, offset, SEEK_END);
+
+ if (error_ptr) {
+ if (result == -1)
+ error_ptr->SetErrorToErrno();
+ else
+ error_ptr->Clear();
+ }
+ return result;
+ }
+
+ if (ValueGuard stream_guard = StreamIsValid()) {
+ result = ::fseek(m_stream, offset, SEEK_END);
+
+ if (error_ptr) {
+ if (result == -1)
+ error_ptr->SetErrorToErrno();
+ else
+ error_ptr->Clear();
+ }
+ }
+
+ if (error_ptr)
+ error_ptr->SetErrorString("invalid file handle");
+ return result;
+}
+
+Status NativeFile::Flush() {
+ Status error;
+ if (ValueGuard stream_guard = StreamIsValid()) {
+ if (llvm::sys::RetryAfterSignal(EOF, ::fflush, m_stream) == EOF)
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ {
+ ValueGuard descriptor_guard = DescriptorIsValid();
+ if (!descriptor_guard)
+ error.SetErrorString("invalid file handle");
+ }
+ return error;
+}
+
+Status NativeFile::Sync() {
+ Status error;
+ if (ValueGuard descriptor_guard = DescriptorIsValid()) {
+#ifdef _WIN32
+ int err = FlushFileBuffers((HANDLE)_get_osfhandle(m_descriptor));
+ if (err == 0)
+ error.SetErrorToGenericError();
+#else
+ if (llvm::sys::RetryAfterSignal(-1, ::fsync, m_descriptor) == -1)
+ error.SetErrorToErrno();
+#endif
+ } else {
+ error.SetErrorString("invalid file handle");
+ }
+ return error;
+}
+
+#if defined(__APPLE__)
+// Darwin kernels only can read/write <= INT_MAX bytes
+#define MAX_READ_SIZE INT_MAX
+#define MAX_WRITE_SIZE INT_MAX
+#endif
+
+Status NativeFile::Read(void *buf, size_t &num_bytes) {
+ Status error;
+
+#if defined(MAX_READ_SIZE)
+ if (num_bytes > MAX_READ_SIZE) {
+ uint8_t *p = (uint8_t *)buf;
+ size_t bytes_left = num_bytes;
+ // Init the num_bytes read to zero
+ num_bytes = 0;
+
+ while (bytes_left > 0) {
+ size_t curr_num_bytes;
+ if (bytes_left > MAX_READ_SIZE)
+ curr_num_bytes = MAX_READ_SIZE;
+ else
+ curr_num_bytes = bytes_left;
+
+ error = Read(p + num_bytes, curr_num_bytes);
+
+ // Update how many bytes were read
+ num_bytes += curr_num_bytes;
+ if (bytes_left < curr_num_bytes)
+ bytes_left = 0;
+ else
+ bytes_left -= curr_num_bytes;
+
+ if (error.Fail())
+ break;
+ }
+ return error;
+ }
+#endif
+
+ ssize_t bytes_read = -1;
+ if (ValueGuard descriptor_guard = DescriptorIsValid()) {
+ bytes_read =
+ llvm::sys::RetryAfterSignal(-1, ::read, m_descriptor, buf, num_bytes);
+ if (bytes_read == -1) {
+ error.SetErrorToErrno();
+ num_bytes = 0;
+ } else
+ num_bytes = bytes_read;
+ return error;
+ }
+
+ if (ValueGuard file_lock = StreamIsValid()) {
+ bytes_read = ::fread(buf, 1, num_bytes, m_stream);
+
+ if (bytes_read == 0) {
+ if (::feof(m_stream))
+ error.SetErrorString("feof");
+ else if (::ferror(m_stream))
+ error.SetErrorString("ferror");
+ num_bytes = 0;
+ } else
+ num_bytes = bytes_read;
+ return error;
+ }
+
+ num_bytes = 0;
+ error.SetErrorString("invalid file handle");
+ return error;
+}
+
+Status NativeFile::Write(const void *buf, size_t &num_bytes) {
+ Status error;
+
+#if defined(MAX_WRITE_SIZE)
+ if (num_bytes > MAX_WRITE_SIZE) {
+ const uint8_t *p = (const uint8_t *)buf;
+ size_t bytes_left = num_bytes;
+ // Init the num_bytes written to zero
+ num_bytes = 0;
+
+ while (bytes_left > 0) {
+ size_t curr_num_bytes;
+ if (bytes_left > MAX_WRITE_SIZE)
+ curr_num_bytes = MAX_WRITE_SIZE;
+ else
+ curr_num_bytes = bytes_left;
+
+ error = Write(p + num_bytes, curr_num_bytes);
+
+ // Update how many bytes were read
+ num_bytes += curr_num_bytes;
+ if (bytes_left < curr_num_bytes)
+ bytes_left = 0;
+ else
+ bytes_left -= curr_num_bytes;
+
+ if (error.Fail())
+ break;
+ }
+ return error;
+ }
+#endif
+
+ ssize_t bytes_written = -1;
+ if (ValueGuard descriptor_guard = DescriptorIsValid()) {
+ bytes_written =
+ llvm::sys::RetryAfterSignal(-1, ::write, m_descriptor, buf, num_bytes);
+ if (bytes_written == -1) {
+ error.SetErrorToErrno();
+ num_bytes = 0;
+ } else
+ num_bytes = bytes_written;
+ return error;
+ }
+
+ if (ValueGuard stream_guard = StreamIsValid()) {
+ bytes_written = ::fwrite(buf, 1, num_bytes, m_stream);
+
+ if (bytes_written == 0) {
+ if (::feof(m_stream))
+ error.SetErrorString("feof");
+ else if (::ferror(m_stream))
+ error.SetErrorString("ferror");
+ num_bytes = 0;
+ } else
+ num_bytes = bytes_written;
+ return error;
+ }
+
+ num_bytes = 0;
+ error.SetErrorString("invalid file handle");
+ return error;
+}
+
+Status NativeFile::Read(void *buf, size_t &num_bytes, off_t &offset) {
+ Status error;
+
+#if defined(MAX_READ_SIZE)
+ if (num_bytes > MAX_READ_SIZE) {
+ uint8_t *p = (uint8_t *)buf;
+ size_t bytes_left = num_bytes;
+ // Init the num_bytes read to zero
+ num_bytes = 0;
+
+ while (bytes_left > 0) {
+ size_t curr_num_bytes;
+ if (bytes_left > MAX_READ_SIZE)
+ curr_num_bytes = MAX_READ_SIZE;
+ else
+ curr_num_bytes = bytes_left;
+
+ error = Read(p + num_bytes, curr_num_bytes, offset);
+
+ // Update how many bytes were read
+ num_bytes += curr_num_bytes;
+ if (bytes_left < curr_num_bytes)
+ bytes_left = 0;
+ else
+ bytes_left -= curr_num_bytes;
+
+ if (error.Fail())
+ break;
+ }
+ return error;
+ }
+#endif
+
+#ifndef _WIN32
+ int fd = GetDescriptor();
+ if (fd != kInvalidDescriptor) {
+ ssize_t bytes_read =
+ llvm::sys::RetryAfterSignal(-1, ::pread, fd, buf, num_bytes, offset);
+ if (bytes_read < 0) {
+ num_bytes = 0;
+ error.SetErrorToErrno();
+ } else {
+ offset += bytes_read;
+ num_bytes = bytes_read;
+ }
+ } else {
+ num_bytes = 0;
+ error.SetErrorString("invalid file handle");
+ }
+#else
+ std::lock_guard<std::mutex> guard(offset_access_mutex);
+ long cur = ::lseek(m_descriptor, 0, SEEK_CUR);
+ SeekFromStart(offset);
+ error = Read(buf, num_bytes);
+ if (!error.Fail())
+ SeekFromStart(cur);
+#endif
+ return error;
+}
+
+Status NativeFile::Write(const void *buf, size_t &num_bytes, off_t &offset) {
+ Status error;
+
+#if defined(MAX_WRITE_SIZE)
+ if (num_bytes > MAX_WRITE_SIZE) {
+ const uint8_t *p = (const uint8_t *)buf;
+ size_t bytes_left = num_bytes;
+ // Init the num_bytes written to zero
+ num_bytes = 0;
+
+ while (bytes_left > 0) {
+ size_t curr_num_bytes;
+ if (bytes_left > MAX_WRITE_SIZE)
+ curr_num_bytes = MAX_WRITE_SIZE;
+ else
+ curr_num_bytes = bytes_left;
+
+ error = Write(p + num_bytes, curr_num_bytes, offset);
+
+ // Update how many bytes were read
+ num_bytes += curr_num_bytes;
+ if (bytes_left < curr_num_bytes)
+ bytes_left = 0;
+ else
+ bytes_left -= curr_num_bytes;
+
+ if (error.Fail())
+ break;
+ }
+ return error;
+ }
+#endif
+
+ int fd = GetDescriptor();
+ if (fd != kInvalidDescriptor) {
+#ifndef _WIN32
+ ssize_t bytes_written =
+ llvm::sys::RetryAfterSignal(-1, ::pwrite, m_descriptor, buf, num_bytes, offset);
+ if (bytes_written < 0) {
+ num_bytes = 0;
+ error.SetErrorToErrno();
+ } else {
+ offset += bytes_written;
+ num_bytes = bytes_written;
+ }
+#else
+ std::lock_guard<std::mutex> guard(offset_access_mutex);
+ long cur = ::lseek(m_descriptor, 0, SEEK_CUR);
+ SeekFromStart(offset);
+ error = Write(buf, num_bytes);
+ long after = ::lseek(m_descriptor, 0, SEEK_CUR);
+
+ if (!error.Fail())
+ SeekFromStart(cur);
+
+ offset = after;
+#endif
+ } else {
+ num_bytes = 0;
+ error.SetErrorString("invalid file handle");
+ }
+ return error;
+}
+
+size_t NativeFile::PrintfVarArg(const char *format, va_list args) {
+ if (StreamIsValid()) {
+ return ::vfprintf(m_stream, format, args);
+ } else {
+ return File::PrintfVarArg(format, args);
+ }
+}
+
+mode_t File::ConvertOpenOptionsForPOSIXOpen(OpenOptions open_options) {
+ mode_t mode = 0;
+ File::OpenOptions rw =
+ open_options & (File::eOpenOptionReadOnly | File::eOpenOptionWriteOnly |
+ File::eOpenOptionReadWrite);
+ if (rw == eOpenOptionReadWrite)
+ mode |= O_RDWR;
+ else if (rw == eOpenOptionWriteOnly)
+ mode |= O_WRONLY;
+ else if (rw == eOpenOptionReadOnly)
+ mode |= O_RDONLY;
+
+ if (open_options & eOpenOptionAppend)
+ mode |= O_APPEND;
+
+ if (open_options & eOpenOptionTruncate)
+ mode |= O_TRUNC;
+
+ if (open_options & eOpenOptionNonBlocking)
+ mode |= O_NONBLOCK;
+
+ if (open_options & eOpenOptionCanCreateNewOnly)
+ mode |= O_CREAT | O_EXCL;
+ else if (open_options & eOpenOptionCanCreate)
+ mode |= O_CREAT;
+
+ return mode;
+}
+
+llvm::Expected<SerialPort::Options>
+SerialPort::OptionsFromURL(llvm::StringRef urlqs) {
+ SerialPort::Options serial_options;
+ for (llvm::StringRef x : llvm::split(urlqs, '&')) {
+ if (x.consume_front("baud=")) {
+ unsigned int baud_rate;
+ if (!llvm::to_integer(x, baud_rate, 10))
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "Invalid baud rate: %s",
+ x.str().c_str());
+ serial_options.BaudRate = baud_rate;
+ } else if (x.consume_front("parity=")) {
+ serial_options.Parity =
+ llvm::StringSwitch<std::optional<Terminal::Parity>>(x)
+ .Case("no", Terminal::Parity::No)
+ .Case("even", Terminal::Parity::Even)
+ .Case("odd", Terminal::Parity::Odd)
+ .Case("mark", Terminal::Parity::Mark)
+ .Case("space", Terminal::Parity::Space)
+ .Default(std::nullopt);
+ if (!serial_options.Parity)
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Invalid parity (must be no, even, odd, mark or space): %s",
+ x.str().c_str());
+ } else if (x.consume_front("parity-check=")) {
+ serial_options.ParityCheck =
+ llvm::StringSwitch<std::optional<Terminal::ParityCheck>>(x)
+ .Case("no", Terminal::ParityCheck::No)
+ .Case("replace", Terminal::ParityCheck::ReplaceWithNUL)
+ .Case("ignore", Terminal::ParityCheck::Ignore)
+ // "mark" mode is not currently supported as it requires special
+ // input processing
+ // .Case("mark", Terminal::ParityCheck::Mark)
+ .Default(std::nullopt);
+ if (!serial_options.ParityCheck)
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Invalid parity-check (must be no, replace, ignore or mark): %s",
+ x.str().c_str());
+ } else if (x.consume_front("stop-bits=")) {
+ unsigned int stop_bits;
+ if (!llvm::to_integer(x, stop_bits, 10) ||
+ (stop_bits != 1 && stop_bits != 2))
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Invalid stop bit number (must be 1 or 2): %s", x.str().c_str());
+ serial_options.StopBits = stop_bits;
+ } else
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "Unknown parameter: %s", x.str().c_str());
+ }
+ return serial_options;
+}
+
+llvm::Expected<std::unique_ptr<SerialPort>>
+SerialPort::Create(int fd, OpenOptions options, Options serial_options,
+ bool transfer_ownership) {
+ std::unique_ptr<SerialPort> out{
+ new SerialPort(fd, options, serial_options, transfer_ownership)};
+
+ if (!out->GetIsInteractive())
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "the specified file is not a teletype");
+
+ Terminal term{fd};
+ if (llvm::Error error = term.SetRaw())
+ return std::move(error);
+ if (serial_options.BaudRate) {
+ if (llvm::Error error = term.SetBaudRate(*serial_options.BaudRate))
+ return std::move(error);
+ }
+ if (serial_options.Parity) {
+ if (llvm::Error error = term.SetParity(*serial_options.Parity))
+ return std::move(error);
+ }
+ if (serial_options.ParityCheck) {
+ if (llvm::Error error = term.SetParityCheck(*serial_options.ParityCheck))
+ return std::move(error);
+ }
+ if (serial_options.StopBits) {
+ if (llvm::Error error = term.SetStopBits(*serial_options.StopBits))
+ return std::move(error);
+ }
+
+ return std::move(out);
+}
+
+SerialPort::SerialPort(int fd, OpenOptions options,
+ SerialPort::Options serial_options,
+ bool transfer_ownership)
+ : NativeFile(fd, options, transfer_ownership), m_state(fd) {}
+
+Status SerialPort::Close() {
+ m_state.Restore();
+ return NativeFile::Close();
+}
+
+char File::ID = 0;
+char NativeFile::ID = 0;
+char SerialPort::ID = 0;
diff --git a/contrib/llvm-project/lldb/source/Host/common/FileAction.cpp b/contrib/llvm-project/lldb/source/Host/common/FileAction.cpp
new file mode 100644
index 000000000000..f980d3224640
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/FileAction.cpp
@@ -0,0 +1,89 @@
+//===-- FileAction.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 <fcntl.h>
+
+#include "lldb/Host/FileAction.h"
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb_private;
+
+// FileAction member functions
+
+FileAction::FileAction() : m_file_spec() {}
+
+void FileAction::Clear() {
+ m_action = eFileActionNone;
+ m_fd = -1;
+ m_arg = -1;
+ m_file_spec.Clear();
+}
+
+llvm::StringRef FileAction::GetPath() const {
+ return m_file_spec.GetPathAsConstString().AsCString();
+}
+
+const FileSpec &FileAction::GetFileSpec() const { return m_file_spec; }
+
+bool FileAction::Open(int fd, const FileSpec &file_spec, bool read,
+ bool write) {
+ if ((read || write) && fd >= 0 && file_spec) {
+ m_action = eFileActionOpen;
+ m_fd = fd;
+ if (read && write)
+ m_arg = O_NOCTTY | O_CREAT | O_RDWR;
+ else if (read)
+ m_arg = O_NOCTTY | O_RDONLY;
+ else
+ m_arg = O_NOCTTY | O_CREAT | O_WRONLY;
+ m_file_spec = file_spec;
+ return true;
+ } else {
+ Clear();
+ }
+ return false;
+}
+
+bool FileAction::Close(int fd) {
+ Clear();
+ if (fd >= 0) {
+ m_action = eFileActionClose;
+ m_fd = fd;
+ }
+ return m_fd >= 0;
+}
+
+bool FileAction::Duplicate(int fd, int dup_fd) {
+ Clear();
+ if (fd >= 0 && dup_fd >= 0) {
+ m_action = eFileActionDuplicate;
+ m_fd = fd;
+ m_arg = dup_fd;
+ }
+ return m_fd >= 0;
+}
+
+void FileAction::Dump(Stream &stream) const {
+ stream.PutCString("file action: ");
+ switch (m_action) {
+ case eFileActionClose:
+ stream.Printf("close fd %d", m_fd);
+ break;
+ case eFileActionDuplicate:
+ stream.Printf("duplicate fd %d to %d", m_fd, m_arg);
+ break;
+ case eFileActionNone:
+ stream.PutCString("no action");
+ break;
+ case eFileActionOpen:
+ stream.Printf("open fd %d with '%s', OFLAGS = 0x%x", m_fd,
+ m_file_spec.GetPath().c_str(), m_arg);
+ break;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/FileCache.cpp b/contrib/llvm-project/lldb/source/Host/common/FileCache.cpp
new file mode 100644
index 000000000000..da9a748e2f13
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/FileCache.cpp
@@ -0,0 +1,114 @@
+//===-- FileCache.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/FileCache.h"
+
+#include "lldb/Host/File.h"
+#include "lldb/Host/FileSystem.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+FileCache *FileCache::m_instance = nullptr;
+
+FileCache &FileCache::GetInstance() {
+ if (m_instance == nullptr)
+ m_instance = new FileCache();
+
+ return *m_instance;
+}
+
+lldb::user_id_t FileCache::OpenFile(const FileSpec &file_spec,
+ File::OpenOptions flags, uint32_t mode,
+ Status &error) {
+ if (!file_spec) {
+ error.SetErrorString("empty path");
+ return UINT64_MAX;
+ }
+ auto file = FileSystem::Instance().Open(file_spec, flags, mode);
+ if (!file) {
+ error = file.takeError();
+ return UINT64_MAX;
+ }
+ lldb::user_id_t fd = file.get()->GetDescriptor();
+ m_cache[fd] = std::move(file.get());
+ return fd;
+}
+
+bool FileCache::CloseFile(lldb::user_id_t fd, Status &error) {
+ if (fd == UINT64_MAX) {
+ error.SetErrorString("invalid file descriptor");
+ return false;
+ }
+ FDToFileMap::iterator pos = m_cache.find(fd);
+ if (pos == m_cache.end()) {
+ error.SetErrorStringWithFormat("invalid host file descriptor %" PRIu64, fd);
+ return false;
+ }
+ FileUP &file_up = pos->second;
+ if (!file_up) {
+ error.SetErrorString("invalid host backing file");
+ return false;
+ }
+ error = file_up->Close();
+ m_cache.erase(pos);
+ return error.Success();
+}
+
+uint64_t FileCache::WriteFile(lldb::user_id_t fd, uint64_t offset,
+ const void *src, uint64_t src_len,
+ Status &error) {
+ if (fd == UINT64_MAX) {
+ error.SetErrorString("invalid file descriptor");
+ return UINT64_MAX;
+ }
+ FDToFileMap::iterator pos = m_cache.find(fd);
+ if (pos == m_cache.end()) {
+ error.SetErrorStringWithFormat("invalid host file descriptor %" PRIu64, fd);
+ return false;
+ }
+ FileUP &file_up = pos->second;
+ if (!file_up) {
+ error.SetErrorString("invalid host backing file");
+ return UINT64_MAX;
+ }
+ if (static_cast<uint64_t>(file_up->SeekFromStart(offset, &error)) != offset ||
+ error.Fail())
+ return UINT64_MAX;
+ size_t bytes_written = src_len;
+ error = file_up->Write(src, bytes_written);
+ if (error.Fail())
+ return UINT64_MAX;
+ return bytes_written;
+}
+
+uint64_t FileCache::ReadFile(lldb::user_id_t fd, uint64_t offset, void *dst,
+ uint64_t dst_len, Status &error) {
+ if (fd == UINT64_MAX) {
+ error.SetErrorString("invalid file descriptor");
+ return UINT64_MAX;
+ }
+ FDToFileMap::iterator pos = m_cache.find(fd);
+ if (pos == m_cache.end()) {
+ error.SetErrorStringWithFormat("invalid host file descriptor %" PRIu64, fd);
+ return false;
+ }
+ FileUP &file_up = pos->second;
+ if (!file_up) {
+ error.SetErrorString("invalid host backing file");
+ return UINT64_MAX;
+ }
+ if (static_cast<uint64_t>(file_up->SeekFromStart(offset, &error)) != offset ||
+ error.Fail())
+ return UINT64_MAX;
+ size_t bytes_read = dst_len;
+ error = file_up->Read(dst, bytes_read);
+ if (error.Fail())
+ return UINT64_MAX;
+ return bytes_read;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/FileSystem.cpp b/contrib/llvm-project/lldb/source/Host/common/FileSystem.cpp
new file mode 100644
index 000000000000..5153a0a9ec51
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/FileSystem.cpp
@@ -0,0 +1,470 @@
+//===-- FileSystem.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/FileSystem.h"
+
+#include "lldb/Utility/DataBufferLLVM.h"
+
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Errno.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/Threading.h"
+
+#include <cerrno>
+#include <climits>
+#include <cstdarg>
+#include <cstdio>
+#include <fcntl.h>
+
+#ifdef _WIN32
+#include "lldb/Host/windows/windows.h"
+#else
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <unistd.h>
+#endif
+
+#include <algorithm>
+#include <fstream>
+#include <optional>
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm;
+
+FileSystem &FileSystem::Instance() { return *InstanceImpl(); }
+
+void FileSystem::Terminate() {
+ lldbassert(InstanceImpl() && "Already terminated.");
+ InstanceImpl().reset();
+}
+
+std::optional<FileSystem> &FileSystem::InstanceImpl() {
+ static std::optional<FileSystem> g_fs;
+ return g_fs;
+}
+
+vfs::directory_iterator FileSystem::DirBegin(const FileSpec &file_spec,
+ std::error_code &ec) {
+ if (!file_spec) {
+ ec = std::error_code(static_cast<int>(errc::no_such_file_or_directory),
+ std::system_category());
+ return {};
+ }
+ return DirBegin(file_spec.GetPath(), ec);
+}
+
+vfs::directory_iterator FileSystem::DirBegin(const Twine &dir,
+ std::error_code &ec) {
+ return m_fs->dir_begin(dir, ec);
+}
+
+llvm::ErrorOr<vfs::Status>
+FileSystem::GetStatus(const FileSpec &file_spec) const {
+ if (!file_spec)
+ return std::error_code(static_cast<int>(errc::no_such_file_or_directory),
+ std::system_category());
+ return GetStatus(file_spec.GetPath());
+}
+
+llvm::ErrorOr<vfs::Status> FileSystem::GetStatus(const Twine &path) const {
+ return m_fs->status(path);
+}
+
+sys::TimePoint<>
+FileSystem::GetModificationTime(const FileSpec &file_spec) const {
+ if (!file_spec)
+ return sys::TimePoint<>();
+ return GetModificationTime(file_spec.GetPath());
+}
+
+sys::TimePoint<> FileSystem::GetModificationTime(const Twine &path) const {
+ ErrorOr<vfs::Status> status = m_fs->status(path);
+ if (!status)
+ return sys::TimePoint<>();
+ return status->getLastModificationTime();
+}
+
+uint64_t FileSystem::GetByteSize(const FileSpec &file_spec) const {
+ if (!file_spec)
+ return 0;
+ return GetByteSize(file_spec.GetPath());
+}
+
+uint64_t FileSystem::GetByteSize(const Twine &path) const {
+ ErrorOr<vfs::Status> status = m_fs->status(path);
+ if (!status)
+ return 0;
+ return status->getSize();
+}
+
+uint32_t FileSystem::GetPermissions(const FileSpec &file_spec) const {
+ return GetPermissions(file_spec.GetPath());
+}
+
+uint32_t FileSystem::GetPermissions(const FileSpec &file_spec,
+ std::error_code &ec) const {
+ if (!file_spec)
+ return sys::fs::perms::perms_not_known;
+ return GetPermissions(file_spec.GetPath(), ec);
+}
+
+uint32_t FileSystem::GetPermissions(const Twine &path) const {
+ std::error_code ec;
+ return GetPermissions(path, ec);
+}
+
+uint32_t FileSystem::GetPermissions(const Twine &path,
+ std::error_code &ec) const {
+ ErrorOr<vfs::Status> status = m_fs->status(path);
+ if (!status) {
+ ec = status.getError();
+ return sys::fs::perms::perms_not_known;
+ }
+ return status->getPermissions();
+}
+
+bool FileSystem::Exists(const Twine &path) const { return m_fs->exists(path); }
+
+bool FileSystem::Exists(const FileSpec &file_spec) const {
+ return file_spec && Exists(file_spec.GetPath());
+}
+
+bool FileSystem::Readable(const Twine &path) const {
+ return GetPermissions(path) & sys::fs::perms::all_read;
+}
+
+bool FileSystem::Readable(const FileSpec &file_spec) const {
+ return file_spec && Readable(file_spec.GetPath());
+}
+
+bool FileSystem::IsDirectory(const Twine &path) const {
+ ErrorOr<vfs::Status> status = m_fs->status(path);
+ if (!status)
+ return false;
+ return status->isDirectory();
+}
+
+bool FileSystem::IsDirectory(const FileSpec &file_spec) const {
+ return file_spec && IsDirectory(file_spec.GetPath());
+}
+
+bool FileSystem::IsLocal(const Twine &path) const {
+ bool b = false;
+ m_fs->isLocal(path, b);
+ return b;
+}
+
+bool FileSystem::IsLocal(const FileSpec &file_spec) const {
+ return file_spec && IsLocal(file_spec.GetPath());
+}
+
+void FileSystem::EnumerateDirectory(Twine path, bool find_directories,
+ bool find_files, bool find_other,
+ EnumerateDirectoryCallbackType callback,
+ void *callback_baton) {
+ std::error_code EC;
+ vfs::recursive_directory_iterator Iter(*m_fs, path, EC);
+ vfs::recursive_directory_iterator End;
+ for (; Iter != End && !EC; Iter.increment(EC)) {
+ const auto &Item = *Iter;
+ ErrorOr<vfs::Status> Status = m_fs->status(Item.path());
+ if (!Status)
+ continue;
+ if (!find_files && Status->isRegularFile())
+ continue;
+ if (!find_directories && Status->isDirectory())
+ continue;
+ if (!find_other && Status->isOther())
+ continue;
+
+ auto Result = callback(callback_baton, Status->getType(), Item.path());
+ if (Result == eEnumerateDirectoryResultQuit)
+ return;
+ if (Result == eEnumerateDirectoryResultNext) {
+ // Default behavior is to recurse. Opt out if the callback doesn't want
+ // this behavior.
+ Iter.no_push();
+ }
+ }
+}
+
+std::error_code FileSystem::MakeAbsolute(SmallVectorImpl<char> &path) const {
+ return m_fs->makeAbsolute(path);
+}
+
+std::error_code FileSystem::MakeAbsolute(FileSpec &file_spec) const {
+ SmallString<128> path;
+ file_spec.GetPath(path, false);
+
+ auto EC = MakeAbsolute(path);
+ if (EC)
+ return EC;
+
+ FileSpec new_file_spec(path, file_spec.GetPathStyle());
+ file_spec = new_file_spec;
+ return {};
+}
+
+std::error_code FileSystem::GetRealPath(const Twine &path,
+ SmallVectorImpl<char> &output) const {
+ return m_fs->getRealPath(path, output);
+}
+
+void FileSystem::Resolve(SmallVectorImpl<char> &path) {
+ if (path.empty())
+ return;
+
+ // Resolve tilde in path.
+ SmallString<128> resolved(path.begin(), path.end());
+ assert(m_tilde_resolver && "must initialize tilde resolver in constructor");
+ m_tilde_resolver->ResolveFullPath(llvm::StringRef(path.begin(), path.size()),
+ resolved);
+
+ // Try making the path absolute if it exists.
+ SmallString<128> absolute(resolved.begin(), resolved.end());
+ MakeAbsolute(absolute);
+
+ path.clear();
+ if (Exists(absolute)) {
+ path.append(absolute.begin(), absolute.end());
+ } else {
+ path.append(resolved.begin(), resolved.end());
+ }
+}
+
+void FileSystem::Resolve(FileSpec &file_spec) {
+ if (!file_spec)
+ return;
+
+ // Extract path from the FileSpec.
+ SmallString<128> path;
+ file_spec.GetPath(path);
+
+ // Resolve the path.
+ Resolve(path);
+
+ // Update the FileSpec with the resolved path.
+ if (file_spec.GetFilename().IsEmpty())
+ file_spec.SetDirectory(path);
+ else
+ file_spec.SetPath(path);
+}
+
+template <typename T>
+static std::unique_ptr<T> GetMemoryBuffer(const llvm::Twine &path,
+ uint64_t size, uint64_t offset,
+ bool is_volatile) {
+ std::unique_ptr<T> buffer;
+ if (size == 0) {
+ auto buffer_or_error = T::getFile(path, is_volatile);
+ if (!buffer_or_error)
+ return nullptr;
+ buffer = std::move(*buffer_or_error);
+ } else {
+ auto buffer_or_error = T::getFileSlice(path, size, offset, is_volatile);
+ if (!buffer_or_error)
+ return nullptr;
+ buffer = std::move(*buffer_or_error);
+ }
+ return buffer;
+}
+
+std::shared_ptr<WritableDataBuffer>
+FileSystem::CreateWritableDataBuffer(const llvm::Twine &path, uint64_t size,
+ uint64_t offset) {
+ const bool is_volatile = !IsLocal(path);
+ auto buffer = GetMemoryBuffer<llvm::WritableMemoryBuffer>(path, size, offset,
+ is_volatile);
+ if (!buffer)
+ return {};
+ return std::shared_ptr<WritableDataBufferLLVM>(
+ new WritableDataBufferLLVM(std::move(buffer)));
+}
+
+std::shared_ptr<DataBuffer>
+FileSystem::CreateDataBuffer(const llvm::Twine &path, uint64_t size,
+ uint64_t offset) {
+ const bool is_volatile = !IsLocal(path);
+ auto buffer =
+ GetMemoryBuffer<llvm::MemoryBuffer>(path, size, offset, is_volatile);
+ if (!buffer)
+ return {};
+ return std::shared_ptr<DataBufferLLVM>(new DataBufferLLVM(std::move(buffer)));
+}
+
+std::shared_ptr<WritableDataBuffer>
+FileSystem::CreateWritableDataBuffer(const FileSpec &file_spec, uint64_t size,
+ uint64_t offset) {
+ return CreateWritableDataBuffer(file_spec.GetPath(), size, offset);
+}
+
+std::shared_ptr<DataBuffer>
+FileSystem::CreateDataBuffer(const FileSpec &file_spec, uint64_t size,
+ uint64_t offset) {
+ return CreateDataBuffer(file_spec.GetPath(), size, offset);
+}
+
+bool FileSystem::ResolveExecutableLocation(FileSpec &file_spec) {
+ // If the directory is set there's nothing to do.
+ ConstString directory = file_spec.GetDirectory();
+ if (directory)
+ return false;
+
+ // We cannot look for a file if there's no file name.
+ ConstString filename = file_spec.GetFilename();
+ if (!filename)
+ return false;
+
+ // Search for the file on the host.
+ const std::string filename_str(filename.GetCString());
+ llvm::ErrorOr<std::string> error_or_path =
+ llvm::sys::findProgramByName(filename_str);
+ if (!error_or_path)
+ return false;
+
+ // findProgramByName returns "." if it can't find the file.
+ llvm::StringRef path = *error_or_path;
+ llvm::StringRef parent = llvm::sys::path::parent_path(path);
+ if (parent.empty() || parent == ".")
+ return false;
+
+ // Make sure that the result exists.
+ FileSpec result(*error_or_path);
+ if (!Exists(result))
+ return false;
+
+ file_spec = result;
+ return true;
+}
+
+bool FileSystem::GetHomeDirectory(SmallVectorImpl<char> &path) const {
+ if (!m_home_directory.empty()) {
+ path.assign(m_home_directory.begin(), m_home_directory.end());
+ return true;
+ }
+ return llvm::sys::path::home_directory(path);
+}
+
+bool FileSystem::GetHomeDirectory(FileSpec &file_spec) const {
+ SmallString<128> home_dir;
+ if (!GetHomeDirectory(home_dir))
+ return false;
+ file_spec.SetPath(home_dir);
+ return true;
+}
+
+static int OpenWithFS(const FileSystem &fs, const char *path, int flags,
+ int mode) {
+ return const_cast<FileSystem &>(fs).Open(path, flags, mode);
+}
+
+static int GetOpenFlags(File::OpenOptions options) {
+ int open_flags = 0;
+ File::OpenOptions rw =
+ options & (File::eOpenOptionReadOnly | File::eOpenOptionWriteOnly |
+ File::eOpenOptionReadWrite);
+ if (rw == File::eOpenOptionWriteOnly || rw == File::eOpenOptionReadWrite) {
+ if (rw == File::eOpenOptionReadWrite)
+ open_flags |= O_RDWR;
+ else
+ open_flags |= O_WRONLY;
+
+ if (options & File::eOpenOptionAppend)
+ open_flags |= O_APPEND;
+
+ if (options & File::eOpenOptionTruncate)
+ open_flags |= O_TRUNC;
+
+ if (options & File::eOpenOptionCanCreate)
+ open_flags |= O_CREAT;
+
+ if (options & File::eOpenOptionCanCreateNewOnly)
+ open_flags |= O_CREAT | O_EXCL;
+ } else if (rw == File::eOpenOptionReadOnly) {
+ open_flags |= O_RDONLY;
+
+#ifndef _WIN32
+ if (options & File::eOpenOptionDontFollowSymlinks)
+ open_flags |= O_NOFOLLOW;
+#endif
+ }
+
+#ifndef _WIN32
+ if (options & File::eOpenOptionNonBlocking)
+ open_flags |= O_NONBLOCK;
+ if (options & File::eOpenOptionCloseOnExec)
+ open_flags |= O_CLOEXEC;
+#else
+ open_flags |= O_BINARY;
+#endif
+
+ return open_flags;
+}
+
+static mode_t GetOpenMode(uint32_t permissions) {
+ mode_t mode = 0;
+ if (permissions & lldb::eFilePermissionsUserRead)
+ mode |= S_IRUSR;
+ if (permissions & lldb::eFilePermissionsUserWrite)
+ mode |= S_IWUSR;
+ if (permissions & lldb::eFilePermissionsUserExecute)
+ mode |= S_IXUSR;
+ if (permissions & lldb::eFilePermissionsGroupRead)
+ mode |= S_IRGRP;
+ if (permissions & lldb::eFilePermissionsGroupWrite)
+ mode |= S_IWGRP;
+ if (permissions & lldb::eFilePermissionsGroupExecute)
+ mode |= S_IXGRP;
+ if (permissions & lldb::eFilePermissionsWorldRead)
+ mode |= S_IROTH;
+ if (permissions & lldb::eFilePermissionsWorldWrite)
+ mode |= S_IWOTH;
+ if (permissions & lldb::eFilePermissionsWorldExecute)
+ mode |= S_IXOTH;
+ return mode;
+}
+
+Expected<FileUP> FileSystem::Open(const FileSpec &file_spec,
+ File::OpenOptions options,
+ uint32_t permissions, bool should_close_fd) {
+ const int open_flags = GetOpenFlags(options);
+ const mode_t open_mode =
+ (open_flags & O_CREAT) ? GetOpenMode(permissions) : 0;
+
+ auto path = file_spec.GetPath();
+
+ int descriptor = llvm::sys::RetryAfterSignal(
+ -1, OpenWithFS, *this, path.c_str(), open_flags, open_mode);
+
+ if (!File::DescriptorIsValid(descriptor))
+ return llvm::errorCodeToError(
+ std::error_code(errno, std::system_category()));
+
+ auto file = std::unique_ptr<File>(
+ new NativeFile(descriptor, options, should_close_fd));
+ assert(file->IsValid());
+ return std::move(file);
+}
+
+void FileSystem::SetHomeDirectory(std::string home_directory) {
+ m_home_directory = std::move(home_directory);
+}
+
+Status FileSystem::RemoveFile(const FileSpec &file_spec) {
+ return RemoveFile(file_spec.GetPath());
+}
+
+Status FileSystem::RemoveFile(const llvm::Twine &path) {
+ return Status(llvm::sys::fs::remove(path));
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/GetOptInc.cpp b/contrib/llvm-project/lldb/source/Host/common/GetOptInc.cpp
new file mode 100644
index 000000000000..c2044b687322
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/GetOptInc.cpp
@@ -0,0 +1,451 @@
+//===-- GetOptInc.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/common/GetOptInc.h"
+
+#if defined(REPLACE_GETOPT) || defined(REPLACE_GETOPT_LONG) || \
+ defined(REPLACE_GETOPT_LONG_ONLY)
+
+// getopt.cpp
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+
+#if defined(REPLACE_GETOPT)
+int opterr = 1; /* if error message should be printed */
+int optind = 1; /* index into parent argv vector */
+int optopt = '?'; /* character checked for validity */
+int optreset; /* reset getopt */
+char *optarg; /* argument associated with option */
+#endif
+
+#define PRINT_ERROR ((opterr) && (*options != ':'))
+
+#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
+#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
+#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
+
+/* return values */
+#define BADCH (int)'?'
+#define BADARG ((*options == ':') ? (int)':' : (int)'?')
+#define INORDER (int)1
+
+#define EMSG ""
+
+static int getopt_internal(int, char *const *, const char *,
+ const struct option *, int *, int);
+static int parse_long_options(char *const *, const char *,
+ const struct option *, int *, int);
+static int gcd(int, int);
+static void permute_args(int, int, int, char *const *);
+
+static const char *place = EMSG; /* option letter processing */
+
+/* XXX: set optreset to 1 rather than these two */
+static int nonopt_start = -1; /* first non option argument (for permute) */
+static int nonopt_end = -1; /* first option after non options (for permute) */
+
+/*
+* Compute the greatest common divisor of a and b.
+*/
+static int gcd(int a, int b) {
+ int c;
+
+ c = a % b;
+ while (c != 0) {
+ a = b;
+ b = c;
+ c = a % b;
+ }
+
+ return (b);
+}
+
+static void pass() {}
+#define warnx(a, ...) pass();
+
+/*
+* Exchange the block from nonopt_start to nonopt_end with the block
+* from nonopt_end to opt_end (keeping the same order of arguments
+* in each block).
+*/
+static void permute_args(int panonopt_start, int panonopt_end, int opt_end,
+ char *const *nargv) {
+ int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
+ char *swap;
+
+ /*
+ * compute lengths of blocks and number and size of cycles
+ */
+ nnonopts = panonopt_end - panonopt_start;
+ nopts = opt_end - panonopt_end;
+ ncycle = gcd(nnonopts, nopts);
+ cyclelen = (opt_end - panonopt_start) / ncycle;
+
+ for (i = 0; i < ncycle; i++) {
+ cstart = panonopt_end + i;
+ pos = cstart;
+ for (j = 0; j < cyclelen; j++) {
+ if (pos >= panonopt_end)
+ pos -= nnonopts;
+ else
+ pos += nopts;
+ swap = nargv[pos];
+ /* LINTED const cast */
+ const_cast<char **>(nargv)[pos] = nargv[cstart];
+ /* LINTED const cast */
+ const_cast<char **>(nargv)[cstart] = swap;
+ }
+ }
+}
+
+/*
+* parse_long_options --
+* Parse long options in argc/argv argument vector.
+* Returns -1 if short_too is set and the option does not match long_options.
+*/
+static int parse_long_options(char *const *nargv, const char *options,
+ const struct option *long_options, int *idx,
+ int short_too) {
+ char *current_argv, *has_equal;
+ size_t current_argv_len;
+ int i, match;
+
+ current_argv = const_cast<char *>(place);
+ match = -1;
+
+ optind++;
+
+ if ((has_equal = strchr(current_argv, '=')) != NULL) {
+ /* argument found (--option=arg) */
+ current_argv_len = has_equal - current_argv;
+ has_equal++;
+ } else
+ current_argv_len = strlen(current_argv);
+
+ for (i = 0; long_options[i].name; i++) {
+ /* find matching long option */
+ if (strncmp(current_argv, long_options[i].name, current_argv_len))
+ continue;
+
+ if (strlen(long_options[i].name) == current_argv_len) {
+ /* exact match */
+ match = i;
+ break;
+ }
+ /*
+ * If this is a known short option, don't allow
+ * a partial match of a single character.
+ */
+ if (short_too && current_argv_len == 1)
+ continue;
+
+ if (match == -1) /* partial match */
+ match = i;
+ else {
+ /* ambiguous abbreviation */
+ if (PRINT_ERROR)
+ warnx(ambig, (int)current_argv_len, current_argv);
+ optopt = 0;
+ return (BADCH);
+ }
+ }
+ if (match != -1) { /* option found */
+ if (long_options[match].has_arg == no_argument && has_equal) {
+ if (PRINT_ERROR)
+ warnx(noarg, (int)current_argv_len, current_argv);
+ /*
+ * XXX: GNU sets optopt to val regardless of flag
+ */
+ if (long_options[match].flag == NULL)
+ optopt = long_options[match].val;
+ else
+ optopt = 0;
+ return (BADARG);
+ }
+ if (long_options[match].has_arg == required_argument ||
+ long_options[match].has_arg == optional_argument) {
+ if (has_equal)
+ optarg = has_equal;
+ else if (long_options[match].has_arg == required_argument) {
+ /*
+ * optional argument doesn't use next nargv
+ */
+ optarg = nargv[optind++];
+ }
+ }
+ if ((long_options[match].has_arg == required_argument) &&
+ (optarg == NULL)) {
+ /*
+ * Missing argument; leading ':' indicates no error
+ * should be generated.
+ */
+ if (PRINT_ERROR)
+ warnx(recargstring, current_argv);
+ /*
+ * XXX: GNU sets optopt to val regardless of flag
+ */
+ if (long_options[match].flag == NULL)
+ optopt = long_options[match].val;
+ else
+ optopt = 0;
+ --optind;
+ return (BADARG);
+ }
+ } else { /* unknown option */
+ if (short_too) {
+ --optind;
+ return (-1);
+ }
+ if (PRINT_ERROR)
+ warnx(illoptstring, current_argv);
+ optopt = 0;
+ return (BADCH);
+ }
+ if (idx)
+ *idx = match;
+ if (long_options[match].flag) {
+ *long_options[match].flag = long_options[match].val;
+ return (0);
+ } else
+ return (long_options[match].val);
+}
+
+/*
+* getopt_internal --
+* Parse argc/argv argument vector. Called by user level routines.
+*/
+static int getopt_internal(int nargc, char *const *nargv, const char *options,
+ const struct option *long_options, int *idx,
+ int flags) {
+ const char *oli; /* option letter list index */
+ int optchar, short_too;
+ static int posixly_correct = -1;
+
+ if (options == NULL)
+ return (-1);
+
+ /*
+ * XXX Some GNU programs (like cvs) set optind to 0 instead of
+ * XXX using optreset. Work around this braindamage.
+ */
+ if (optind == 0)
+ optind = optreset = 1;
+
+ /*
+ * Disable GNU extensions if POSIXLY_CORRECT is set or options
+ * string begins with a '+'.
+ */
+ if (posixly_correct == -1 || optreset)
+ posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
+ if (*options == '-')
+ flags |= FLAG_ALLARGS;
+ else if (posixly_correct || *options == '+')
+ flags &= ~FLAG_PERMUTE;
+ if (*options == '+' || *options == '-')
+ options++;
+
+ optarg = NULL;
+ if (optreset)
+ nonopt_start = nonopt_end = -1;
+start:
+ if (optreset || !*place) { /* update scanning pointer */
+ optreset = 0;
+ if (optind >= nargc) { /* end of argument vector */
+ place = EMSG;
+ if (nonopt_end != -1) {
+ /* do permutation, if we have to */
+ permute_args(nonopt_start, nonopt_end, optind, nargv);
+ optind -= nonopt_end - nonopt_start;
+ } else if (nonopt_start != -1) {
+ /*
+ * If we skipped non-options, set optind
+ * to the first of them.
+ */
+ optind = nonopt_start;
+ }
+ nonopt_start = nonopt_end = -1;
+ return (-1);
+ }
+ if (*(place = nargv[optind]) != '-' ||
+ (place[1] == '\0' && strchr(options, '-') == NULL)) {
+ place = EMSG; /* found non-option */
+ if (flags & FLAG_ALLARGS) {
+ /*
+ * GNU extension:
+ * return non-option as argument to option 1
+ */
+ optarg = nargv[optind++];
+ return (INORDER);
+ }
+ if (!(flags & FLAG_PERMUTE)) {
+ /*
+ * If no permutation wanted, stop parsing
+ * at first non-option.
+ */
+ return (-1);
+ }
+ /* do permutation */
+ if (nonopt_start == -1)
+ nonopt_start = optind;
+ else if (nonopt_end != -1) {
+ permute_args(nonopt_start, nonopt_end, optind, nargv);
+ nonopt_start = optind - (nonopt_end - nonopt_start);
+ nonopt_end = -1;
+ }
+ optind++;
+ /* process next argument */
+ goto start;
+ }
+ if (nonopt_start != -1 && nonopt_end == -1)
+ nonopt_end = optind;
+
+ /*
+ * If we have "-" do nothing, if "--" we are done.
+ */
+ if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
+ optind++;
+ place = EMSG;
+ /*
+ * We found an option (--), so if we skipped
+ * non-options, we have to permute.
+ */
+ if (nonopt_end != -1) {
+ permute_args(nonopt_start, nonopt_end, optind, nargv);
+ optind -= nonopt_end - nonopt_start;
+ }
+ nonopt_start = nonopt_end = -1;
+ return (-1);
+ }
+ }
+
+ /*
+ * Check long options if:
+ * 1) we were passed some
+ * 2) the arg is not just "-"
+ * 3) either the arg starts with -- we are getopt_long_only()
+ */
+ if (long_options != NULL && place != nargv[optind] &&
+ (*place == '-' || (flags & FLAG_LONGONLY))) {
+ short_too = 0;
+ if (*place == '-')
+ place++; /* --foo long option */
+ else if (*place != ':' && strchr(options, *place) != NULL)
+ short_too = 1; /* could be short option too */
+
+ optchar = parse_long_options(nargv, options, long_options, idx, short_too);
+ if (optchar != -1) {
+ place = EMSG;
+ return (optchar);
+ }
+ }
+
+ if ((optchar = (int)*place++) == (int)':' ||
+ (optchar == (int)'-' && *place != '\0') ||
+ (oli = strchr(options, optchar)) == NULL) {
+ /*
+ * If the user specified "-" and '-' isn't listed in
+ * options, return -1 (non-option) as per POSIX.
+ * Otherwise, it is an unknown option character (or ':').
+ */
+ if (optchar == (int)'-' && *place == '\0')
+ return (-1);
+ if (!*place)
+ ++optind;
+ if (PRINT_ERROR)
+ warnx(illoptchar, optchar);
+ optopt = optchar;
+ return (BADCH);
+ }
+ if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
+ /* -W long-option */
+ if (*place) /* no space */
+ /* NOTHING */;
+ else if (++optind >= nargc) { /* no arg */
+ place = EMSG;
+ if (PRINT_ERROR)
+ warnx(recargchar, optchar);
+ optopt = optchar;
+ return (BADARG);
+ } else /* white space */
+ place = nargv[optind];
+ optchar = parse_long_options(nargv, options, long_options, idx, 0);
+ place = EMSG;
+ return (optchar);
+ }
+ if (*++oli != ':') { /* doesn't take argument */
+ if (!*place)
+ ++optind;
+ } else { /* takes (optional) argument */
+ optarg = NULL;
+ if (*place) /* no white space */
+ optarg = const_cast<char *>(place);
+ else if (oli[1] != ':') { /* arg not optional */
+ if (++optind >= nargc) { /* no arg */
+ place = EMSG;
+ if (PRINT_ERROR)
+ warnx(recargchar, optchar);
+ optopt = optchar;
+ return (BADARG);
+ } else
+ optarg = nargv[optind];
+ }
+ place = EMSG;
+ ++optind;
+ }
+ /* dump back option letter */
+ return (optchar);
+}
+
+/*
+* getopt --
+* Parse argc/argv argument vector.
+*
+* [eventually this will replace the BSD getopt]
+*/
+#if defined(REPLACE_GETOPT)
+int getopt(int nargc, char *const *nargv, const char *options) {
+
+ /*
+ * We don't pass FLAG_PERMUTE to getopt_internal() since
+ * the BSD getopt(3) (unlike GNU) has never done this.
+ *
+ * Furthermore, since many privileged programs call getopt()
+ * before dropping privileges it makes sense to keep things
+ * as simple (and bug-free) as possible.
+ */
+ return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
+}
+#endif
+
+/*
+* getopt_long --
+* Parse argc/argv argument vector.
+*/
+#if defined(REPLACE_GETOPT_LONG)
+int getopt_long(int nargc, char *const *nargv, const char *options,
+ const struct option *long_options, int *idx) {
+ return (
+ getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE));
+}
+#endif
+
+/*
+* getopt_long_only --
+* Parse argc/argv argument vector.
+*/
+#if defined(REPLACE_GETOPT_LONG_ONLY)
+int getopt_long_only(int nargc, char *const *nargv, const char *options,
+ const struct option *long_options, int *idx) {
+
+ return (getopt_internal(nargc, nargv, options, long_options, idx,
+ FLAG_PERMUTE | FLAG_LONGONLY));
+}
+#endif
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Host/common/Host.cpp b/contrib/llvm-project/lldb/source/Host/common/Host.cpp
new file mode 100644
index 000000000000..06ccc0e2b342
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/Host.cpp
@@ -0,0 +1,655 @@
+//===-- 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
+//
+//===----------------------------------------------------------------------===//
+
+// C includes
+#include <cerrno>
+#include <climits>
+#include <cstdlib>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <dlfcn.h>
+#include <grp.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#endif
+
+#if defined(__APPLE__)
+#include <mach-o/dyld.h>
+#include <mach/mach_init.h>
+#include <mach/mach_port.h>
+#endif
+
+#if defined(__linux__) || defined(__FreeBSD__) || \
+ defined(__FreeBSD_kernel__) || defined(__APPLE__) || \
+ defined(__NetBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__)
+#if !defined(__ANDROID__)
+#include <spawn.h>
+#endif
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#endif
+
+#if defined(__FreeBSD__)
+#include <pthread_np.h>
+#endif
+
+#if defined(__NetBSD__)
+#include <lwp.h>
+#endif
+
+#include <csignal>
+
+#include "lldb/Host/FileAction.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/HostProcess.h"
+#include "lldb/Host/MonitoringProcessLauncher.h"
+#include "lldb/Host/ProcessLaunchInfo.h"
+#include "lldb/Host/ProcessLauncher.h"
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Predicate.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-private-forward.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Errno.h"
+#include "llvm/Support/FileSystem.h"
+
+#if defined(_WIN32)
+#include "lldb/Host/windows/ConnectionGenericFileWindows.h"
+#include "lldb/Host/windows/ProcessLauncherWindows.h"
+#else
+#include "lldb/Host/posix/ProcessLauncherPosixFork.h"
+#endif
+
+#if defined(__APPLE__)
+#ifndef _POSIX_SPAWN_DISABLE_ASLR
+#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
+#endif
+
+extern "C" {
+int __pthread_chdir(const char *path);
+int __pthread_fchdir(int fildes);
+}
+
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+
+#if !defined(__APPLE__)
+#if !defined(_WIN32)
+#include <syslog.h>
+void Host::SystemLog(Severity severity, llvm::StringRef message) {
+ static llvm::once_flag g_openlog_once;
+ llvm::call_once(g_openlog_once, [] {
+ openlog("lldb", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_USER);
+ });
+ int level = LOG_DEBUG;
+ switch (severity) {
+ case lldb::eSeverityInfo:
+ level = LOG_INFO;
+ break;
+ case lldb::eSeverityWarning:
+ level = LOG_WARNING;
+ break;
+ case lldb::eSeverityError:
+ level = LOG_ERR;
+ break;
+ }
+ syslog(level, "%s", message.data());
+}
+#else
+void Host::SystemLog(Severity severity, llvm::StringRef message) {
+ switch (severity) {
+ case lldb::eSeverityInfo:
+ case lldb::eSeverityWarning:
+ llvm::outs() << message;
+ break;
+ case lldb::eSeverityError:
+ llvm::errs() << message;
+ break;
+ }
+}
+#endif
+#endif
+
+#if !defined(__APPLE__) && !defined(_WIN32)
+static thread_result_t
+MonitorChildProcessThreadFunction(::pid_t pid,
+ Host::MonitorChildProcessCallback callback);
+
+llvm::Expected<HostThread> Host::StartMonitoringChildProcess(
+ const Host::MonitorChildProcessCallback &callback, lldb::pid_t pid) {
+ char thread_name[256];
+ ::snprintf(thread_name, sizeof(thread_name),
+ "<lldb.host.wait4(pid=%" PRIu64 ")>", pid);
+ assert(pid <= UINT32_MAX);
+ return ThreadLauncher::LaunchThread(thread_name, [pid, callback] {
+ return MonitorChildProcessThreadFunction(pid, callback);
+ });
+}
+
+#ifndef __linux__
+// Scoped class that will disable thread canceling when it is constructed, and
+// exception safely restore the previous value it when it goes out of scope.
+class ScopedPThreadCancelDisabler {
+public:
+ ScopedPThreadCancelDisabler() {
+ // Disable the ability for this thread to be cancelled
+ int err = ::pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &m_old_state);
+ if (err != 0)
+ m_old_state = -1;
+ }
+
+ ~ScopedPThreadCancelDisabler() {
+ // Restore the ability for this thread to be cancelled to what it
+ // previously was.
+ if (m_old_state != -1)
+ ::pthread_setcancelstate(m_old_state, 0);
+ }
+
+private:
+ int m_old_state; // Save the old cancelability state.
+};
+#endif // __linux__
+
+#ifdef __linux__
+static thread_local volatile sig_atomic_t g_usr1_called;
+
+static void SigUsr1Handler(int) { g_usr1_called = 1; }
+#endif // __linux__
+
+static bool CheckForMonitorCancellation() {
+#ifdef __linux__
+ if (g_usr1_called) {
+ g_usr1_called = 0;
+ return true;
+ }
+#else
+ ::pthread_testcancel();
+#endif
+ return false;
+}
+
+static thread_result_t
+MonitorChildProcessThreadFunction(::pid_t pid,
+ Host::MonitorChildProcessCallback callback) {
+ Log *log = GetLog(LLDBLog::Process);
+ LLDB_LOG(log, "pid = {0}", pid);
+
+ int status = -1;
+
+#ifdef __linux__
+ // This signal is only used to interrupt the thread from waitpid
+ struct sigaction sigUsr1Action;
+ memset(&sigUsr1Action, 0, sizeof(sigUsr1Action));
+ sigUsr1Action.sa_handler = SigUsr1Handler;
+ ::sigaction(SIGUSR1, &sigUsr1Action, nullptr);
+#endif // __linux__
+
+ while (true) {
+ log = GetLog(LLDBLog::Process);
+ LLDB_LOG(log, "::waitpid({0}, &status, 0)...", pid);
+
+ if (CheckForMonitorCancellation())
+ return nullptr;
+
+ const ::pid_t wait_pid = ::waitpid(pid, &status, 0);
+
+ LLDB_LOG(log, "::waitpid({0}, &status, 0) => pid = {1}, status = {2:x}", pid,
+ wait_pid, status);
+
+ if (CheckForMonitorCancellation())
+ return nullptr;
+
+ if (wait_pid != -1)
+ break;
+ if (errno != EINTR) {
+ LLDB_LOG(log, "pid = {0}, thread exiting because waitpid failed ({1})...",
+ pid, llvm::sys::StrError());
+ return nullptr;
+ }
+ }
+
+ int signal = 0;
+ int exit_status = 0;
+ if (WIFEXITED(status)) {
+ exit_status = WEXITSTATUS(status);
+ } else if (WIFSIGNALED(status)) {
+ signal = WTERMSIG(status);
+ exit_status = -1;
+ } else {
+ llvm_unreachable("Unknown status");
+ }
+
+ // Scope for pthread_cancel_disabler
+ {
+#ifndef __linux__
+ ScopedPThreadCancelDisabler pthread_cancel_disabler;
+#endif
+
+ if (callback)
+ callback(pid, signal, exit_status);
+ }
+
+ LLDB_LOG(GetLog(LLDBLog::Process), "pid = {0} thread exiting...", pid);
+ return nullptr;
+}
+
+#endif // #if !defined (__APPLE__) && !defined (_WIN32)
+
+lldb::pid_t Host::GetCurrentProcessID() { return ::getpid(); }
+
+#ifndef _WIN32
+
+lldb::thread_t Host::GetCurrentThread() {
+ return lldb::thread_t(pthread_self());
+}
+
+const char *Host::GetSignalAsCString(int signo) {
+ switch (signo) {
+ case SIGHUP:
+ return "SIGHUP"; // 1 hangup
+ case SIGINT:
+ return "SIGINT"; // 2 interrupt
+ case SIGQUIT:
+ return "SIGQUIT"; // 3 quit
+ case SIGILL:
+ return "SIGILL"; // 4 illegal instruction (not reset when caught)
+ case SIGTRAP:
+ return "SIGTRAP"; // 5 trace trap (not reset when caught)
+ case SIGABRT:
+ return "SIGABRT"; // 6 abort()
+#if defined(SIGPOLL)
+#if !defined(SIGIO) || (SIGPOLL != SIGIO)
+ // Under some GNU/Linux, SIGPOLL and SIGIO are the same. Causing the build to
+ // fail with 'multiple define cases with same value'
+ case SIGPOLL:
+ return "SIGPOLL"; // 7 pollable event ([XSR] generated, not supported)
+#endif
+#endif
+#if defined(SIGEMT)
+ case SIGEMT:
+ return "SIGEMT"; // 7 EMT instruction
+#endif
+ case SIGFPE:
+ return "SIGFPE"; // 8 floating point exception
+ case SIGKILL:
+ return "SIGKILL"; // 9 kill (cannot be caught or ignored)
+ case SIGBUS:
+ return "SIGBUS"; // 10 bus error
+ case SIGSEGV:
+ return "SIGSEGV"; // 11 segmentation violation
+ case SIGSYS:
+ return "SIGSYS"; // 12 bad argument to system call
+ case SIGPIPE:
+ return "SIGPIPE"; // 13 write on a pipe with no one to read it
+ case SIGALRM:
+ return "SIGALRM"; // 14 alarm clock
+ case SIGTERM:
+ return "SIGTERM"; // 15 software termination signal from kill
+ case SIGURG:
+ return "SIGURG"; // 16 urgent condition on IO channel
+ case SIGSTOP:
+ return "SIGSTOP"; // 17 sendable stop signal not from tty
+ case SIGTSTP:
+ return "SIGTSTP"; // 18 stop signal from tty
+ case SIGCONT:
+ return "SIGCONT"; // 19 continue a stopped process
+ case SIGCHLD:
+ return "SIGCHLD"; // 20 to parent on child stop or exit
+ case SIGTTIN:
+ return "SIGTTIN"; // 21 to readers pgrp upon background tty read
+ case SIGTTOU:
+ return "SIGTTOU"; // 22 like TTIN for output if (tp->t_local&LTOSTOP)
+#if defined(SIGIO)
+ case SIGIO:
+ return "SIGIO"; // 23 input/output possible signal
+#endif
+ case SIGXCPU:
+ return "SIGXCPU"; // 24 exceeded CPU time limit
+ case SIGXFSZ:
+ return "SIGXFSZ"; // 25 exceeded file size limit
+ case SIGVTALRM:
+ return "SIGVTALRM"; // 26 virtual time alarm
+ case SIGPROF:
+ return "SIGPROF"; // 27 profiling time alarm
+#if defined(SIGWINCH)
+ case SIGWINCH:
+ return "SIGWINCH"; // 28 window size changes
+#endif
+#if defined(SIGINFO)
+ case SIGINFO:
+ return "SIGINFO"; // 29 information request
+#endif
+ case SIGUSR1:
+ return "SIGUSR1"; // 30 user defined signal 1
+ case SIGUSR2:
+ return "SIGUSR2"; // 31 user defined signal 2
+ default:
+ break;
+ }
+ return nullptr;
+}
+
+#endif
+
+#if !defined(__APPLE__) // see Host.mm
+
+bool Host::GetBundleDirectory(const FileSpec &file, FileSpec &bundle) {
+ bundle.Clear();
+ return false;
+}
+
+bool Host::ResolveExecutableInBundle(FileSpec &file) { return false; }
+#endif
+
+#ifndef _WIN32
+
+FileSpec Host::GetModuleFileSpecForHostAddress(const void *host_addr) {
+ FileSpec module_filespec;
+#if !defined(__ANDROID__)
+ Dl_info info;
+ if (::dladdr(host_addr, &info)) {
+ if (info.dli_fname) {
+ module_filespec.SetFile(info.dli_fname, FileSpec::Style::native);
+ FileSystem::Instance().Resolve(module_filespec);
+ }
+ }
+#endif
+ return module_filespec;
+}
+
+#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) {}
+
+ lldb_private::Predicate<bool> process_reaped;
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+ int signo = -1;
+ int status = -1;
+};
+
+static void
+MonitorShellCommand(std::shared_ptr<ShellInfo> shell_info, lldb::pid_t pid,
+ int signo, // Zero for no signal
+ int status) // Exit value of process if signal is zero
+{
+ shell_info->pid = pid;
+ shell_info->signo = signo;
+ shell_info->status = status;
+ // Let the thread running Host::RunShellCommand() know that the process
+ // exited and that ShellInfo has been filled in by broadcasting to it
+ shell_info->process_reaped.SetValue(true, eBroadcastAlways);
+}
+
+Status Host::RunShellCommand(llvm::StringRef command,
+ const FileSpec &working_dir, int *status_ptr,
+ int *signo_ptr, std::string *command_output_ptr,
+ const Timeout<std::micro> &timeout,
+ bool run_in_shell, bool hide_stderr) {
+ return RunShellCommand(llvm::StringRef(), Args(command), working_dir,
+ status_ptr, signo_ptr, command_output_ptr, timeout,
+ run_in_shell, hide_stderr);
+}
+
+Status Host::RunShellCommand(llvm::StringRef shell_path,
+ llvm::StringRef command,
+ const FileSpec &working_dir, int *status_ptr,
+ int *signo_ptr, std::string *command_output_ptr,
+ const Timeout<std::micro> &timeout,
+ bool run_in_shell, bool hide_stderr) {
+ return RunShellCommand(shell_path, Args(command), working_dir, status_ptr,
+ signo_ptr, command_output_ptr, timeout, run_in_shell,
+ hide_stderr);
+}
+
+Status Host::RunShellCommand(const Args &args, const FileSpec &working_dir,
+ int *status_ptr, int *signo_ptr,
+ std::string *command_output_ptr,
+ const Timeout<std::micro> &timeout,
+ bool run_in_shell, bool hide_stderr) {
+ return RunShellCommand(llvm::StringRef(), args, working_dir, status_ptr,
+ signo_ptr, command_output_ptr, timeout, run_in_shell,
+ hide_stderr);
+}
+
+Status Host::RunShellCommand(llvm::StringRef shell_path, const Args &args,
+ const FileSpec &working_dir, int *status_ptr,
+ int *signo_ptr, std::string *command_output_ptr,
+ const Timeout<std::micro> &timeout,
+ bool run_in_shell, bool hide_stderr) {
+ Status error;
+ ProcessLaunchInfo launch_info;
+ launch_info.SetArchitecture(HostInfo::GetArchitecture());
+ if (run_in_shell) {
+ // Run the command in a shell
+ FileSpec shell = HostInfo::GetDefaultShell();
+ if (!shell_path.empty())
+ shell.SetPath(shell_path);
+
+ launch_info.SetShell(shell);
+ launch_info.GetArguments().AppendArguments(args);
+ const bool will_debug = false;
+ const bool first_arg_is_full_shell_command = false;
+ launch_info.ConvertArgumentsForLaunchingInShell(
+ error, will_debug, first_arg_is_full_shell_command, 0);
+ } else {
+ // No shell, just run it
+ const bool first_arg_is_executable = true;
+ launch_info.SetArguments(args, first_arg_is_executable);
+ }
+
+ launch_info.GetEnvironment() = Host::GetEnvironment();
+
+ if (working_dir)
+ launch_info.SetWorkingDirectory(working_dir);
+ llvm::SmallString<64> output_file_path;
+
+ if (command_output_ptr) {
+ // Create a temporary file to get the stdout/stderr and redirect the output
+ // of the command into this file. We will later read this file if all goes
+ // well and fill the data into "command_output_ptr"
+ if (FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir()) {
+ tmpdir_file_spec.AppendPathComponent("lldb-shell-output.%%%%%%");
+ llvm::sys::fs::createUniqueFile(tmpdir_file_spec.GetPath(),
+ output_file_path);
+ } else {
+ llvm::sys::fs::createTemporaryFile("lldb-shell-output.%%%%%%", "",
+ output_file_path);
+ }
+ }
+
+ FileSpec output_file_spec(output_file_path.str());
+ // Set up file descriptors.
+ launch_info.AppendSuppressFileAction(STDIN_FILENO, true, false);
+ if (output_file_spec)
+ launch_info.AppendOpenFileAction(STDOUT_FILENO, output_file_spec, false,
+ true);
+ else
+ launch_info.AppendSuppressFileAction(STDOUT_FILENO, false, true);
+
+ if (output_file_spec && !hide_stderr)
+ launch_info.AppendDuplicateFileAction(STDOUT_FILENO, STDERR_FILENO);
+ else
+ launch_info.AppendSuppressFileAction(STDERR_FILENO, false, true);
+
+ std::shared_ptr<ShellInfo> shell_info_sp(new ShellInfo());
+ launch_info.SetMonitorProcessCallback(
+ std::bind(MonitorShellCommand, shell_info_sp, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3));
+
+ error = LaunchProcess(launch_info);
+ const lldb::pid_t pid = launch_info.GetProcessID();
+
+ if (error.Success() && pid == LLDB_INVALID_PROCESS_ID)
+ error.SetErrorString("failed to get process ID");
+
+ if (error.Success()) {
+ if (!shell_info_sp->process_reaped.WaitForValueEqualTo(true, timeout)) {
+ error.SetErrorString("timed out waiting for shell command to complete");
+
+ // Kill the process since it didn't complete within the timeout specified
+ Kill(pid, SIGKILL);
+ // Wait for the monitor callback to get the message
+ shell_info_sp->process_reaped.WaitForValueEqualTo(
+ true, std::chrono::seconds(1));
+ } else {
+ if (status_ptr)
+ *status_ptr = shell_info_sp->status;
+
+ if (signo_ptr)
+ *signo_ptr = shell_info_sp->signo;
+
+ if (command_output_ptr) {
+ command_output_ptr->clear();
+ uint64_t file_size =
+ FileSystem::Instance().GetByteSize(output_file_spec);
+ if (file_size > 0) {
+ if (file_size > command_output_ptr->max_size()) {
+ error.SetErrorStringWithFormat(
+ "shell command output is too large to fit into a std::string");
+ } else {
+ WritableDataBufferSP Buffer =
+ FileSystem::Instance().CreateWritableDataBuffer(
+ output_file_spec);
+ if (error.Success())
+ command_output_ptr->assign(
+ reinterpret_cast<char *>(Buffer->GetBytes()),
+ Buffer->GetByteSize());
+ }
+ }
+ }
+ }
+ }
+
+ llvm::sys::fs::remove(output_file_spec.GetPath());
+ return error;
+}
+
+// The functions below implement process launching for non-Apple-based
+// platforms
+#if !defined(__APPLE__)
+Status Host::LaunchProcess(ProcessLaunchInfo &launch_info) {
+ std::unique_ptr<ProcessLauncher> delegate_launcher;
+#if defined(_WIN32)
+ delegate_launcher.reset(new ProcessLauncherWindows());
+#else
+ delegate_launcher.reset(new ProcessLauncherPosixFork());
+#endif
+ MonitoringProcessLauncher launcher(std::move(delegate_launcher));
+
+ Status error;
+ HostProcess process = launcher.LaunchProcess(launch_info, error);
+
+ // TODO(zturner): It would be better if the entire HostProcess were returned
+ // instead of writing it into this structure.
+ launch_info.SetProcessID(process.GetProcessId());
+
+ return error;
+}
+#endif // !defined(__APPLE__)
+
+#ifndef _WIN32
+void Host::Kill(lldb::pid_t pid, int signo) { ::kill(pid, signo); }
+
+#endif
+
+#if !defined(__APPLE__)
+llvm::Error Host::OpenFileInExternalEditor(llvm::StringRef editor,
+ const FileSpec &file_spec,
+ uint32_t line_no) {
+ return llvm::errorCodeToError(
+ std::error_code(ENOTSUP, std::system_category()));
+}
+
+bool Host::IsInteractiveGraphicSession() { return false; }
+#endif
+
+std::unique_ptr<Connection> Host::CreateDefaultConnection(llvm::StringRef url) {
+#if defined(_WIN32)
+ if (url.starts_with("file://"))
+ return std::unique_ptr<Connection>(new ConnectionGenericFile());
+#endif
+ return std::unique_ptr<Connection>(new ConnectionFileDescriptor());
+}
+
+#if defined(LLVM_ON_UNIX)
+WaitStatus WaitStatus::Decode(int wstatus) {
+ if (WIFEXITED(wstatus))
+ return {Exit, uint8_t(WEXITSTATUS(wstatus))};
+ else if (WIFSIGNALED(wstatus))
+ return {Signal, uint8_t(WTERMSIG(wstatus))};
+ else if (WIFSTOPPED(wstatus))
+ return {Stop, uint8_t(WSTOPSIG(wstatus))};
+ llvm_unreachable("Unknown wait status");
+}
+#endif
+
+void llvm::format_provider<WaitStatus>::format(const WaitStatus &WS,
+ raw_ostream &OS,
+ StringRef Options) {
+ if (Options == "g") {
+ char type;
+ switch (WS.type) {
+ case WaitStatus::Exit:
+ type = 'W';
+ break;
+ case WaitStatus::Signal:
+ type = 'X';
+ break;
+ case WaitStatus::Stop:
+ type = 'S';
+ break;
+ }
+ OS << formatv("{0}{1:x-2}", type, WS.status);
+ return;
+ }
+
+ assert(Options.empty());
+ const char *desc;
+ switch(WS.type) {
+ case WaitStatus::Exit:
+ desc = "Exited with status";
+ break;
+ case WaitStatus::Signal:
+ desc = "Killed by signal";
+ break;
+ case WaitStatus::Stop:
+ desc = "Stopped by signal";
+ break;
+ }
+ OS << desc << " " << int(WS.status);
+}
+
+uint32_t Host::FindProcesses(const ProcessInstanceInfoMatch &match_info,
+ ProcessInstanceInfoList &process_infos) {
+ return FindProcessesImpl(match_info, process_infos);
+}
+
+char SystemLogHandler::ID;
+
+SystemLogHandler::SystemLogHandler() {}
+
+void SystemLogHandler::Emit(llvm::StringRef message) {
+ Host::SystemLog(lldb::eSeverityInfo, message);
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/HostInfoBase.cpp b/contrib/llvm-project/lldb/source/Host/common/HostInfoBase.cpp
new file mode 100644
index 000000000000..5c44c2f38b28
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/HostInfoBase.cpp
@@ -0,0 +1,352 @@
+//===-- HostInfoBase.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/Config.h"
+
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/HostInfoBase.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/Threading.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/TargetParser/Host.h"
+#include "llvm/TargetParser/Triple.h"
+
+#include <mutex>
+#include <optional>
+#include <thread>
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+/// Contains the state of the HostInfoBase plugin.
+struct HostInfoBaseFields {
+ ~HostInfoBaseFields() {
+ if (FileSystem::Instance().Exists(m_lldb_process_tmp_dir)) {
+ // Remove the LLDB temporary directory if we have one. Set "recurse" to
+ // true to all files that were created for the LLDB process can be
+ // cleaned up.
+ llvm::sys::fs::remove_directories(m_lldb_process_tmp_dir.GetPath());
+ }
+ }
+
+ llvm::once_flag m_host_triple_once;
+ llvm::Triple m_host_triple;
+
+ llvm::once_flag m_host_arch_once;
+ ArchSpec m_host_arch_32;
+ ArchSpec m_host_arch_64;
+
+ llvm::once_flag m_lldb_so_dir_once;
+ FileSpec m_lldb_so_dir;
+ llvm::once_flag m_lldb_support_exe_dir_once;
+ FileSpec m_lldb_support_exe_dir;
+ llvm::once_flag m_lldb_headers_dir_once;
+ FileSpec m_lldb_headers_dir;
+ llvm::once_flag m_lldb_clang_resource_dir_once;
+ FileSpec m_lldb_clang_resource_dir;
+ llvm::once_flag m_lldb_system_plugin_dir_once;
+ FileSpec m_lldb_system_plugin_dir;
+ llvm::once_flag m_lldb_user_plugin_dir_once;
+ FileSpec m_lldb_user_plugin_dir;
+ llvm::once_flag m_lldb_process_tmp_dir_once;
+ FileSpec m_lldb_process_tmp_dir;
+ llvm::once_flag m_lldb_global_tmp_dir_once;
+ FileSpec m_lldb_global_tmp_dir;
+};
+} // namespace
+
+static HostInfoBaseFields *g_fields = nullptr;
+static HostInfoBase::SharedLibraryDirectoryHelper *g_shlib_dir_helper = nullptr;
+
+void HostInfoBase::Initialize(SharedLibraryDirectoryHelper *helper) {
+ g_shlib_dir_helper = helper;
+ g_fields = new HostInfoBaseFields();
+}
+
+void HostInfoBase::Terminate() {
+ g_shlib_dir_helper = nullptr;
+ delete g_fields;
+ g_fields = nullptr;
+}
+
+llvm::Triple HostInfoBase::GetTargetTriple() {
+ llvm::call_once(g_fields->m_host_triple_once, []() {
+ g_fields->m_host_triple = HostInfo::GetArchitecture().GetTriple();
+ });
+ return g_fields->m_host_triple;
+}
+
+const ArchSpec &HostInfoBase::GetArchitecture(ArchitectureKind arch_kind) {
+ llvm::call_once(g_fields->m_host_arch_once, []() {
+ HostInfo::ComputeHostArchitectureSupport(g_fields->m_host_arch_32,
+ g_fields->m_host_arch_64);
+ });
+
+ // If an explicit 32 or 64-bit architecture was requested, return that.
+ if (arch_kind == eArchKind32)
+ return g_fields->m_host_arch_32;
+ if (arch_kind == eArchKind64)
+ return g_fields->m_host_arch_64;
+
+ // Otherwise prefer the 64-bit architecture if it is valid.
+ return (g_fields->m_host_arch_64.IsValid()) ? g_fields->m_host_arch_64
+ : g_fields->m_host_arch_32;
+}
+
+std::optional<HostInfoBase::ArchitectureKind>
+HostInfoBase::ParseArchitectureKind(llvm::StringRef kind) {
+ return llvm::StringSwitch<std::optional<ArchitectureKind>>(kind)
+ .Case(LLDB_ARCH_DEFAULT, eArchKindDefault)
+ .Case(LLDB_ARCH_DEFAULT_32BIT, eArchKind32)
+ .Case(LLDB_ARCH_DEFAULT_64BIT, eArchKind64)
+ .Default(std::nullopt);
+}
+
+FileSpec HostInfoBase::GetShlibDir() {
+ llvm::call_once(g_fields->m_lldb_so_dir_once, []() {
+ if (!HostInfo::ComputeSharedLibraryDirectory(g_fields->m_lldb_so_dir))
+ g_fields->m_lldb_so_dir = FileSpec();
+ Log *log = GetLog(LLDBLog::Host);
+ LLDB_LOG(log, "shlib dir -> `{0}`", g_fields->m_lldb_so_dir);
+ });
+ return g_fields->m_lldb_so_dir;
+}
+
+FileSpec HostInfoBase::GetSupportExeDir() {
+ llvm::call_once(g_fields->m_lldb_support_exe_dir_once, []() {
+ if (!HostInfo::ComputeSupportExeDirectory(g_fields->m_lldb_support_exe_dir))
+ g_fields->m_lldb_support_exe_dir = FileSpec();
+ Log *log = GetLog(LLDBLog::Host);
+ LLDB_LOG(log, "support exe dir -> `{0}`", g_fields->m_lldb_support_exe_dir);
+ });
+ return g_fields->m_lldb_support_exe_dir;
+}
+
+FileSpec HostInfoBase::GetHeaderDir() {
+ llvm::call_once(g_fields->m_lldb_headers_dir_once, []() {
+ if (!HostInfo::ComputeHeaderDirectory(g_fields->m_lldb_headers_dir))
+ g_fields->m_lldb_headers_dir = FileSpec();
+ Log *log = GetLog(LLDBLog::Host);
+ LLDB_LOG(log, "header dir -> `{0}`", g_fields->m_lldb_headers_dir);
+ });
+ return g_fields->m_lldb_headers_dir;
+}
+
+FileSpec HostInfoBase::GetSystemPluginDir() {
+ llvm::call_once(g_fields->m_lldb_system_plugin_dir_once, []() {
+ if (!HostInfo::ComputeSystemPluginsDirectory(
+ g_fields->m_lldb_system_plugin_dir))
+ g_fields->m_lldb_system_plugin_dir = FileSpec();
+ Log *log = GetLog(LLDBLog::Host);
+ LLDB_LOG(log, "system plugin dir -> `{0}`",
+ g_fields->m_lldb_system_plugin_dir);
+ });
+ return g_fields->m_lldb_system_plugin_dir;
+}
+
+FileSpec HostInfoBase::GetUserPluginDir() {
+ llvm::call_once(g_fields->m_lldb_user_plugin_dir_once, []() {
+ if (!HostInfo::ComputeUserPluginsDirectory(
+ g_fields->m_lldb_user_plugin_dir))
+ g_fields->m_lldb_user_plugin_dir = FileSpec();
+ Log *log = GetLog(LLDBLog::Host);
+ LLDB_LOG(log, "user plugin dir -> `{0}`", g_fields->m_lldb_user_plugin_dir);
+ });
+ return g_fields->m_lldb_user_plugin_dir;
+}
+
+FileSpec HostInfoBase::GetProcessTempDir() {
+ llvm::call_once(g_fields->m_lldb_process_tmp_dir_once, []() {
+ if (!HostInfo::ComputeProcessTempFileDirectory(
+ g_fields->m_lldb_process_tmp_dir))
+ g_fields->m_lldb_process_tmp_dir = FileSpec();
+ Log *log = GetLog(LLDBLog::Host);
+ LLDB_LOG(log, "process temp dir -> `{0}`",
+ g_fields->m_lldb_process_tmp_dir);
+ });
+ return g_fields->m_lldb_process_tmp_dir;
+}
+
+FileSpec HostInfoBase::GetGlobalTempDir() {
+ llvm::call_once(g_fields->m_lldb_global_tmp_dir_once, []() {
+ if (!HostInfo::ComputeGlobalTempFileDirectory(
+ g_fields->m_lldb_global_tmp_dir))
+ g_fields->m_lldb_global_tmp_dir = FileSpec();
+
+ Log *log = GetLog(LLDBLog::Host);
+ LLDB_LOG(log, "global temp dir -> `{0}`", g_fields->m_lldb_global_tmp_dir);
+ });
+ return g_fields->m_lldb_global_tmp_dir;
+}
+
+ArchSpec HostInfoBase::GetAugmentedArchSpec(llvm::StringRef triple) {
+ if (triple.empty())
+ return ArchSpec();
+ llvm::Triple normalized_triple(llvm::Triple::normalize(triple));
+ if (!ArchSpec::ContainsOnlyArch(normalized_triple))
+ return ArchSpec(triple);
+
+ if (auto kind = HostInfo::ParseArchitectureKind(triple))
+ return HostInfo::GetArchitecture(*kind);
+
+ llvm::Triple host_triple(llvm::sys::getDefaultTargetTriple());
+
+ if (normalized_triple.getVendorName().empty())
+ normalized_triple.setVendor(host_triple.getVendor());
+ if (normalized_triple.getOSName().empty())
+ normalized_triple.setOS(host_triple.getOS());
+ if (normalized_triple.getEnvironmentName().empty() &&
+ !host_triple.getEnvironmentName().empty())
+ normalized_triple.setEnvironment(host_triple.getEnvironment());
+ return ArchSpec(normalized_triple);
+}
+
+bool HostInfoBase::ComputePathRelativeToLibrary(FileSpec &file_spec,
+ llvm::StringRef dir) {
+ Log *log = GetLog(LLDBLog::Host);
+
+ FileSpec lldb_file_spec = GetShlibDir();
+ if (!lldb_file_spec)
+ return false;
+
+ std::string raw_path = lldb_file_spec.GetPath();
+ LLDB_LOG(
+ log,
+ "Attempting to derive the path {0} relative to liblldb install path: {1}",
+ dir, raw_path);
+
+ // Drop bin (windows) or lib
+ llvm::StringRef parent_path = llvm::sys::path::parent_path(raw_path);
+ if (parent_path.empty()) {
+ LLDB_LOG(log, "Failed to find liblldb within the shared lib path");
+ return false;
+ }
+
+ raw_path = (parent_path + dir).str();
+ LLDB_LOG(log, "Derived the path as: {0}", raw_path);
+ file_spec.SetDirectory(raw_path);
+ return (bool)file_spec.GetDirectory();
+}
+
+bool HostInfoBase::ComputeSharedLibraryDirectory(FileSpec &file_spec) {
+ // To get paths related to LLDB we get the path to the executable that
+ // contains this function. On MacOSX this will be "LLDB.framework/.../LLDB".
+ // On other posix systems, we will get .../lib(64|32)?/liblldb.so.
+
+ FileSpec lldb_file_spec(Host::GetModuleFileSpecForHostAddress(
+ reinterpret_cast<void *>(HostInfoBase::ComputeSharedLibraryDirectory)));
+
+ if (g_shlib_dir_helper)
+ g_shlib_dir_helper(lldb_file_spec);
+
+ // Remove the filename so that this FileSpec only represents the directory.
+ file_spec.SetDirectory(lldb_file_spec.GetDirectory());
+
+ return (bool)file_spec.GetDirectory();
+}
+
+bool HostInfoBase::ComputeSupportExeDirectory(FileSpec &file_spec) {
+ file_spec = GetShlibDir();
+ return bool(file_spec);
+}
+
+bool HostInfoBase::ComputeProcessTempFileDirectory(FileSpec &file_spec) {
+ FileSpec temp_file_spec;
+ if (!HostInfo::ComputeGlobalTempFileDirectory(temp_file_spec))
+ return false;
+
+ std::string pid_str{llvm::to_string(Host::GetCurrentProcessID())};
+ temp_file_spec.AppendPathComponent(pid_str);
+ if (llvm::sys::fs::create_directory(temp_file_spec.GetPath()))
+ return false;
+
+ file_spec.SetDirectory(temp_file_spec.GetPathAsConstString());
+ return true;
+}
+
+bool HostInfoBase::ComputeTempFileBaseDirectory(FileSpec &file_spec) {
+ llvm::SmallVector<char, 16> tmpdir;
+ llvm::sys::path::system_temp_directory(/*ErasedOnReboot*/ true, tmpdir);
+ file_spec = FileSpec(std::string(tmpdir.data(), tmpdir.size()));
+ FileSystem::Instance().Resolve(file_spec);
+ return true;
+}
+
+bool HostInfoBase::ComputeGlobalTempFileDirectory(FileSpec &file_spec) {
+ file_spec.Clear();
+
+ FileSpec temp_file_spec;
+ if (!HostInfo::ComputeTempFileBaseDirectory(temp_file_spec))
+ return false;
+
+ temp_file_spec.AppendPathComponent("lldb");
+ if (llvm::sys::fs::create_directory(temp_file_spec.GetPath()))
+ return false;
+
+ file_spec.SetDirectory(temp_file_spec.GetPathAsConstString());
+ return true;
+}
+
+bool HostInfoBase::ComputeHeaderDirectory(FileSpec &file_spec) {
+ // TODO(zturner): Figure out how to compute the header directory for all
+ // platforms.
+ return false;
+}
+
+bool HostInfoBase::ComputeSystemPluginsDirectory(FileSpec &file_spec) {
+ // TODO(zturner): Figure out how to compute the system plugins directory for
+ // all platforms.
+ return false;
+}
+
+bool HostInfoBase::ComputeUserPluginsDirectory(FileSpec &file_spec) {
+ // TODO(zturner): Figure out how to compute the user plugins directory for
+ // all platforms.
+ return false;
+}
+
+void HostInfoBase::ComputeHostArchitectureSupport(ArchSpec &arch_32,
+ ArchSpec &arch_64) {
+ llvm::Triple triple(llvm::sys::getProcessTriple());
+
+ arch_32.Clear();
+ arch_64.Clear();
+
+ switch (triple.getArch()) {
+ default:
+ arch_32.SetTriple(triple);
+ break;
+
+ case llvm::Triple::aarch64:
+ case llvm::Triple::ppc64:
+ case llvm::Triple::ppc64le:
+ case llvm::Triple::x86_64:
+ case llvm::Triple::riscv64:
+ case llvm::Triple::loongarch64:
+ arch_64.SetTriple(triple);
+ arch_32.SetTriple(triple.get32BitArchVariant());
+ break;
+
+ case llvm::Triple::mips64:
+ case llvm::Triple::mips64el:
+ case llvm::Triple::sparcv9:
+ case llvm::Triple::systemz:
+ arch_64.SetTriple(triple);
+ break;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/HostNativeThreadBase.cpp b/contrib/llvm-project/lldb/source/Host/common/HostNativeThreadBase.cpp
new file mode 100644
index 000000000000..a9cbb69c4d98
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/HostNativeThreadBase.cpp
@@ -0,0 +1,63 @@
+//===-- HostNativeThreadBase.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/HostNativeThreadBase.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Threading.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+HostNativeThreadBase::HostNativeThreadBase(thread_t thread)
+ : m_thread(thread) {}
+
+lldb::thread_t HostNativeThreadBase::GetSystemHandle() const {
+ return m_thread;
+}
+
+lldb::thread_result_t HostNativeThreadBase::GetResult() const {
+ return m_result;
+}
+
+bool HostNativeThreadBase::IsJoinable() const {
+ return m_thread != LLDB_INVALID_HOST_THREAD;
+}
+
+void HostNativeThreadBase::Reset() {
+ m_thread = LLDB_INVALID_HOST_THREAD;
+ m_result = 0; // NOLINT(modernize-use-nullptr)
+}
+
+bool HostNativeThreadBase::EqualsThread(lldb::thread_t thread) const {
+ return m_thread == thread;
+}
+
+lldb::thread_t HostNativeThreadBase::Release() {
+ lldb::thread_t result = m_thread;
+ m_thread = LLDB_INVALID_HOST_THREAD;
+ m_result = 0; // NOLINT(modernize-use-nullptr)
+
+ return result;
+}
+
+lldb::thread_result_t
+HostNativeThreadBase::ThreadCreateTrampoline(lldb::thread_arg_t arg) {
+ std::unique_ptr<ThreadLauncher::HostThreadCreateInfo> info_up(
+ (ThreadLauncher::HostThreadCreateInfo *)arg);
+ llvm::set_thread_name(info_up->thread_name);
+
+ Log *log = GetLog(LLDBLog::Thread);
+ LLDB_LOGF(log, "thread created");
+
+ return info_up->impl();
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/HostProcess.cpp b/contrib/llvm-project/lldb/source/Host/common/HostProcess.cpp
new file mode 100644
index 000000000000..8db5a61e8796
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/HostProcess.cpp
@@ -0,0 +1,42 @@
+//===-- HostProcess.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/HostProcess.h"
+#include "lldb/Host/HostNativeProcess.h"
+#include "lldb/Host/HostThread.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+HostProcess::HostProcess() : m_native_process(new HostNativeProcess) {}
+
+HostProcess::HostProcess(lldb::process_t process)
+ : m_native_process(new HostNativeProcess(process)) {}
+
+HostProcess::~HostProcess() = default;
+
+Status HostProcess::Terminate() { return m_native_process->Terminate(); }
+
+lldb::pid_t HostProcess::GetProcessId() const {
+ return m_native_process->GetProcessId();
+}
+
+bool HostProcess::IsRunning() const { return m_native_process->IsRunning(); }
+
+llvm::Expected<HostThread> HostProcess::StartMonitoring(
+ const Host::MonitorChildProcessCallback &callback) {
+ return m_native_process->StartMonitoring(callback);
+}
+
+HostNativeProcessBase &HostProcess::GetNativeProcess() {
+ return *m_native_process;
+}
+
+const HostNativeProcessBase &HostProcess::GetNativeProcess() const {
+ return *m_native_process;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/HostThread.cpp b/contrib/llvm-project/lldb/source/Host/common/HostThread.cpp
new file mode 100644
index 000000000000..eec029be1c09
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/HostThread.cpp
@@ -0,0 +1,46 @@
+//===-- HostThread.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/HostThread.h"
+#include "lldb/Host/HostNativeThread.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+HostThread::HostThread() : m_native_thread(new HostNativeThread) {}
+
+HostThread::HostThread(lldb::thread_t thread)
+ : m_native_thread(new HostNativeThread(thread)) {}
+
+Status HostThread::Join(lldb::thread_result_t *result) {
+ return m_native_thread->Join(result);
+}
+
+Status HostThread::Cancel() { return m_native_thread->Cancel(); }
+
+void HostThread::Reset() { return m_native_thread->Reset(); }
+
+lldb::thread_t HostThread::Release() { return m_native_thread->Release(); }
+
+bool HostThread::IsJoinable() const { return m_native_thread->IsJoinable(); }
+
+HostNativeThread &HostThread::GetNativeThread() {
+ return static_cast<HostNativeThread &>(*m_native_thread);
+}
+
+const HostNativeThread &HostThread::GetNativeThread() const {
+ return static_cast<const HostNativeThread &>(*m_native_thread);
+}
+
+lldb::thread_result_t HostThread::GetResult() const {
+ return m_native_thread->GetResult();
+}
+
+bool HostThread::EqualsThread(lldb::thread_t thread) const {
+ return m_native_thread->EqualsThread(thread);
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/LZMA.cpp b/contrib/llvm-project/lldb/source/Host/common/LZMA.cpp
new file mode 100644
index 000000000000..5b457f07afca
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/LZMA.cpp
@@ -0,0 +1,146 @@
+//===-- LZMA.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/Config.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+
+#if LLDB_ENABLE_LZMA
+#include <lzma.h>
+#endif // LLDB_ENABLE_LZMA
+
+namespace lldb_private {
+
+namespace lzma {
+
+#if !LLDB_ENABLE_LZMA
+bool isAvailable() { return false; }
+llvm::Expected<uint64_t>
+getUncompressedSize(llvm::ArrayRef<uint8_t> InputBuffer) {
+ llvm_unreachable("lzma::getUncompressedSize is unavailable");
+}
+
+llvm::Error uncompress(llvm::ArrayRef<uint8_t> InputBuffer,
+ llvm::SmallVectorImpl<uint8_t> &Uncompressed) {
+ llvm_unreachable("lzma::uncompress is unavailable");
+}
+
+#else // LLDB_ENABLE_LZMA
+
+bool isAvailable() { return true; }
+
+static const char *convertLZMACodeToString(lzma_ret Code) {
+ switch (Code) {
+ case LZMA_STREAM_END:
+ return "lzma error: LZMA_STREAM_END";
+ case LZMA_NO_CHECK:
+ return "lzma error: LZMA_NO_CHECK";
+ case LZMA_UNSUPPORTED_CHECK:
+ return "lzma error: LZMA_UNSUPPORTED_CHECK";
+ case LZMA_GET_CHECK:
+ return "lzma error: LZMA_GET_CHECK";
+ case LZMA_MEM_ERROR:
+ return "lzma error: LZMA_MEM_ERROR";
+ case LZMA_MEMLIMIT_ERROR:
+ return "lzma error: LZMA_MEMLIMIT_ERROR";
+ case LZMA_FORMAT_ERROR:
+ return "lzma error: LZMA_FORMAT_ERROR";
+ case LZMA_OPTIONS_ERROR:
+ return "lzma error: LZMA_OPTIONS_ERROR";
+ case LZMA_DATA_ERROR:
+ return "lzma error: LZMA_DATA_ERROR";
+ case LZMA_BUF_ERROR:
+ return "lzma error: LZMA_BUF_ERROR";
+ case LZMA_PROG_ERROR:
+ return "lzma error: LZMA_PROG_ERROR";
+ default:
+ llvm_unreachable("unknown or unexpected lzma status code");
+ }
+}
+
+llvm::Expected<uint64_t>
+getUncompressedSize(llvm::ArrayRef<uint8_t> InputBuffer) {
+ lzma_stream_flags opts{};
+ if (InputBuffer.size() < LZMA_STREAM_HEADER_SIZE) {
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "size of xz-compressed blob (%lu bytes) is smaller than the "
+ "LZMA_STREAM_HEADER_SIZE (%lu bytes)",
+ InputBuffer.size(), LZMA_STREAM_HEADER_SIZE);
+ }
+
+ // Decode xz footer.
+ lzma_ret xzerr = lzma_stream_footer_decode(
+ &opts, InputBuffer.take_back(LZMA_STREAM_HEADER_SIZE).data());
+ if (xzerr != LZMA_OK) {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "lzma_stream_footer_decode()=%s",
+ convertLZMACodeToString(xzerr));
+ }
+ if (InputBuffer.size() < (opts.backward_size + LZMA_STREAM_HEADER_SIZE)) {
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "xz-compressed buffer size (%lu bytes) too small (required at "
+ "least %lu bytes) ",
+ InputBuffer.size(), (opts.backward_size + LZMA_STREAM_HEADER_SIZE));
+ }
+
+ // Decode xz index.
+ lzma_index *xzindex;
+ uint64_t memlimit(UINT64_MAX);
+ size_t inpos = 0;
+ xzerr = lzma_index_buffer_decode(
+ &xzindex, &memlimit, nullptr,
+ InputBuffer.take_back(LZMA_STREAM_HEADER_SIZE + opts.backward_size)
+ .data(),
+ &inpos, InputBuffer.size());
+ if (xzerr != LZMA_OK) {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "lzma_index_buffer_decode()=%s",
+ convertLZMACodeToString(xzerr));
+ }
+
+ // Get size of uncompressed file to construct an in-memory buffer of the
+ // same size on the calling end (if needed).
+ uint64_t uncompressedSize = lzma_index_uncompressed_size(xzindex);
+
+ // Deallocate xz index as it is no longer needed.
+ lzma_index_end(xzindex, nullptr);
+
+ return uncompressedSize;
+}
+
+llvm::Error uncompress(llvm::ArrayRef<uint8_t> InputBuffer,
+ llvm::SmallVectorImpl<uint8_t> &Uncompressed) {
+ llvm::Expected<uint64_t> uncompressedSize = getUncompressedSize(InputBuffer);
+
+ if (auto err = uncompressedSize.takeError())
+ return err;
+
+ Uncompressed.resize(*uncompressedSize);
+
+ // Decompress xz buffer to buffer.
+ uint64_t memlimit = UINT64_MAX;
+ size_t inpos = 0;
+ size_t outpos = 0;
+ lzma_ret ret = lzma_stream_buffer_decode(
+ &memlimit, 0, nullptr, InputBuffer.data(), &inpos, InputBuffer.size(),
+ Uncompressed.data(), &outpos, Uncompressed.size());
+ if (ret != LZMA_OK) {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "lzma_stream_buffer_decode()=%s",
+ convertLZMACodeToString(ret));
+ }
+
+ return llvm::Error::success();
+}
+
+#endif // LLDB_ENABLE_LZMA
+
+} // end of namespace lzma
+} // namespace lldb_private
diff --git a/contrib/llvm-project/lldb/source/Host/common/LockFileBase.cpp b/contrib/llvm-project/lldb/source/Host/common/LockFileBase.cpp
new file mode 100644
index 000000000000..1c0de9e04e29
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/LockFileBase.cpp
@@ -0,0 +1,78 @@
+//===-- LockFileBase.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/LockFileBase.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static Status AlreadyLocked() { return Status("Already locked"); }
+
+static Status NotLocked() { return Status("Not locked"); }
+
+LockFileBase::LockFileBase(int fd)
+ : m_fd(fd), m_locked(false), m_start(0), m_len(0) {}
+
+bool LockFileBase::IsLocked() const { return m_locked; }
+
+Status LockFileBase::WriteLock(const uint64_t start, const uint64_t len) {
+ return DoLock([&](const uint64_t start,
+ const uint64_t len) { return DoWriteLock(start, len); },
+ start, len);
+}
+
+Status LockFileBase::TryWriteLock(const uint64_t start, const uint64_t len) {
+ return DoLock([&](const uint64_t start,
+ const uint64_t len) { return DoTryWriteLock(start, len); },
+ start, len);
+}
+
+Status LockFileBase::ReadLock(const uint64_t start, const uint64_t len) {
+ return DoLock([&](const uint64_t start,
+ const uint64_t len) { return DoReadLock(start, len); },
+ start, len);
+}
+
+Status LockFileBase::TryReadLock(const uint64_t start, const uint64_t len) {
+ return DoLock([&](const uint64_t start,
+ const uint64_t len) { return DoTryReadLock(start, len); },
+ start, len);
+}
+
+Status LockFileBase::Unlock() {
+ if (!IsLocked())
+ return NotLocked();
+
+ const auto error = DoUnlock();
+ if (error.Success()) {
+ m_locked = false;
+ m_start = 0;
+ m_len = 0;
+ }
+ return error;
+}
+
+bool LockFileBase::IsValidFile() const { return m_fd != -1; }
+
+Status LockFileBase::DoLock(const Locker &locker, const uint64_t start,
+ const uint64_t len) {
+ if (!IsValidFile())
+ return Status("File is invalid");
+
+ if (IsLocked())
+ return AlreadyLocked();
+
+ const auto error = locker(start, len);
+ if (error.Success()) {
+ m_locked = true;
+ m_start = start;
+ m_len = len;
+ }
+
+ return error;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/MainLoopBase.cpp b/contrib/llvm-project/lldb/source/Host/common/MainLoopBase.cpp
new file mode 100644
index 000000000000..030a4f037168
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/MainLoopBase.cpp
@@ -0,0 +1,33 @@
+//===-- MainLoopBase.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/MainLoopBase.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void MainLoopBase::AddPendingCallback(const Callback &callback) {
+ {
+ std::lock_guard<std::mutex> lock{m_callback_mutex};
+ m_pending_callbacks.push_back(callback);
+ }
+ TriggerPendingCallbacks();
+}
+
+void MainLoopBase::ProcessPendingCallbacks() {
+ // Move the callbacks to a local vector to avoid keeping m_pending_callbacks
+ // locked throughout the calls.
+ std::vector<Callback> pending_callbacks;
+ {
+ std::lock_guard<std::mutex> lock{m_callback_mutex};
+ pending_callbacks = std::move(m_pending_callbacks);
+ }
+
+ for (const Callback &callback : pending_callbacks)
+ callback(*this);
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/MonitoringProcessLauncher.cpp b/contrib/llvm-project/lldb/source/Host/common/MonitoringProcessLauncher.cpp
new file mode 100644
index 000000000000..953934ea4a6e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/MonitoringProcessLauncher.cpp
@@ -0,0 +1,70 @@
+//===-- MonitoringProcessLauncher.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/MonitoringProcessLauncher.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostProcess.h"
+#include "lldb/Host/ProcessLaunchInfo.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+
+#include "llvm/Support/FileSystem.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+MonitoringProcessLauncher::MonitoringProcessLauncher(
+ std::unique_ptr<ProcessLauncher> delegate_launcher)
+ : m_delegate_launcher(std::move(delegate_launcher)) {}
+
+HostProcess
+MonitoringProcessLauncher::LaunchProcess(const ProcessLaunchInfo &launch_info,
+ Status &error) {
+ ProcessLaunchInfo resolved_info(launch_info);
+
+ error.Clear();
+
+ FileSystem &fs = FileSystem::Instance();
+ FileSpec exe_spec(resolved_info.GetExecutableFile());
+
+ if (!fs.Exists(exe_spec))
+ FileSystem::Instance().Resolve(exe_spec);
+
+ if (!fs.Exists(exe_spec))
+ FileSystem::Instance().ResolveExecutableLocation(exe_spec);
+
+ if (!fs.Exists(exe_spec)) {
+ error.SetErrorStringWithFormatv("executable doesn't exist: '{0}'",
+ exe_spec);
+ return HostProcess();
+ }
+
+ resolved_info.SetExecutableFile(exe_spec, false);
+ assert(!resolved_info.GetFlags().Test(eLaunchFlagLaunchInTTY));
+
+ HostProcess process =
+ m_delegate_launcher->LaunchProcess(resolved_info, error);
+
+ if (process.GetProcessId() != LLDB_INVALID_PROCESS_ID) {
+ Log *log = GetLog(LLDBLog::Process);
+
+ assert(launch_info.GetMonitorProcessCallback());
+ llvm::Expected<HostThread> maybe_thread =
+ process.StartMonitoring(launch_info.GetMonitorProcessCallback());
+ if (!maybe_thread)
+ error.SetErrorStringWithFormatv("failed to launch host thread: {}",
+ llvm::toString(maybe_thread.takeError()));
+ if (log)
+ log->PutCString("started monitoring child process.");
+ } else {
+ // Invalid process ID, something didn't go well
+ if (error.Success())
+ error.SetErrorString("process launch failed for unknown reasons");
+ }
+ return process;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/NativeProcessProtocol.cpp b/contrib/llvm-project/lldb/source/Host/common/NativeProcessProtocol.cpp
new file mode 100644
index 000000000000..b3ef8f027bcf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/NativeProcessProtocol.cpp
@@ -0,0 +1,766 @@
+//===-- NativeProcessProtocol.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/common/NativeProcessProtocol.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/common/NativeBreakpointList.h"
+#include "lldb/Host/common/NativeRegisterContext.h"
+#include "lldb/Host/common/NativeThreadProtocol.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/Support/Process.h"
+#include <optional>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// NativeProcessProtocol Members
+
+NativeProcessProtocol::NativeProcessProtocol(lldb::pid_t pid, int terminal_fd,
+ NativeDelegate &delegate)
+ : m_pid(pid), m_delegate(delegate), m_terminal_fd(terminal_fd) {
+ delegate.InitializeDelegate(this);
+}
+
+lldb_private::Status NativeProcessProtocol::Interrupt() {
+ Status error;
+#if !defined(SIGSTOP)
+ error.SetErrorString("local host does not support signaling");
+ return error;
+#else
+ return Signal(SIGSTOP);
+#endif
+}
+
+Status NativeProcessProtocol::IgnoreSignals(llvm::ArrayRef<int> signals) {
+ m_signals_to_ignore.clear();
+ m_signals_to_ignore.insert(signals.begin(), signals.end());
+ return Status();
+}
+
+lldb_private::Status
+NativeProcessProtocol::GetMemoryRegionInfo(lldb::addr_t load_addr,
+ MemoryRegionInfo &range_info) {
+ // Default: not implemented.
+ return Status("not implemented");
+}
+
+lldb_private::Status
+NativeProcessProtocol::ReadMemoryTags(int32_t type, lldb::addr_t addr,
+ size_t len, std::vector<uint8_t> &tags) {
+ return Status("not implemented");
+}
+
+lldb_private::Status
+NativeProcessProtocol::WriteMemoryTags(int32_t type, lldb::addr_t addr,
+ size_t len,
+ const std::vector<uint8_t> &tags) {
+ return Status("not implemented");
+}
+
+std::optional<WaitStatus> NativeProcessProtocol::GetExitStatus() {
+ if (m_state == lldb::eStateExited)
+ return m_exit_status;
+
+ return std::nullopt;
+}
+
+bool NativeProcessProtocol::SetExitStatus(WaitStatus status,
+ bool bNotifyStateChange) {
+ Log *log = GetLog(LLDBLog::Process);
+ LLDB_LOG(log, "status = {0}, notify = {1}", status, bNotifyStateChange);
+
+ // Exit status already set
+ if (m_state == lldb::eStateExited) {
+ if (m_exit_status)
+ LLDB_LOG(log, "exit status already set to {0}", *m_exit_status);
+ else
+ LLDB_LOG(log, "state is exited, but status not set");
+ return false;
+ }
+
+ m_state = lldb::eStateExited;
+ m_exit_status = status;
+
+ if (bNotifyStateChange)
+ SynchronouslyNotifyProcessStateChanged(lldb::eStateExited);
+
+ return true;
+}
+
+NativeThreadProtocol *NativeProcessProtocol::GetThreadAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(m_threads_mutex);
+ if (idx < m_threads.size())
+ return m_threads[idx].get();
+ return nullptr;
+}
+
+NativeThreadProtocol *
+NativeProcessProtocol::GetThreadByIDUnlocked(lldb::tid_t tid) {
+ for (const auto &thread : m_threads) {
+ if (thread->GetID() == tid)
+ return thread.get();
+ }
+ return nullptr;
+}
+
+NativeThreadProtocol *NativeProcessProtocol::GetThreadByID(lldb::tid_t tid) {
+ std::lock_guard<std::recursive_mutex> guard(m_threads_mutex);
+ return GetThreadByIDUnlocked(tid);
+}
+
+bool NativeProcessProtocol::IsAlive() const {
+ return m_state != eStateDetached && m_state != eStateExited &&
+ m_state != eStateInvalid && m_state != eStateUnloaded;
+}
+
+const NativeWatchpointList::WatchpointMap &
+NativeProcessProtocol::GetWatchpointMap() const {
+ return m_watchpoint_list.GetWatchpointMap();
+}
+
+std::optional<std::pair<uint32_t, uint32_t>>
+NativeProcessProtocol::GetHardwareDebugSupportInfo() const {
+ Log *log = GetLog(LLDBLog::Process);
+
+ // get any thread
+ NativeThreadProtocol *thread(
+ const_cast<NativeProcessProtocol *>(this)->GetThreadAtIndex(0));
+ if (!thread) {
+ LLDB_LOG(log, "failed to find a thread to grab a NativeRegisterContext!");
+ return std::nullopt;
+ }
+
+ NativeRegisterContext &reg_ctx = thread->GetRegisterContext();
+ return std::make_pair(reg_ctx.NumSupportedHardwareBreakpoints(),
+ reg_ctx.NumSupportedHardwareWatchpoints());
+}
+
+Status NativeProcessProtocol::SetWatchpoint(lldb::addr_t addr, size_t size,
+ uint32_t watch_flags,
+ bool hardware) {
+ // This default implementation assumes setting the watchpoint for the process
+ // will require setting the watchpoint for each of the threads. Furthermore,
+ // it will track watchpoints set for the process and will add them to each
+ // thread that is attached to via the (FIXME implement) OnThreadAttached ()
+ // method.
+
+ Log *log = GetLog(LLDBLog::Process);
+
+ // Update the thread list
+ UpdateThreads();
+
+ // Keep track of the threads we successfully set the watchpoint for. If one
+ // of the thread watchpoint setting operations fails, back off and remove the
+ // watchpoint for all the threads that were successfully set so we get back
+ // to a consistent state.
+ std::vector<NativeThreadProtocol *> watchpoint_established_threads;
+
+ // Tell each thread to set a watchpoint. In the event that hardware
+ // watchpoints are requested but the SetWatchpoint fails, try to set a
+ // software watchpoint as a fallback. It's conceivable that if there are
+ // more threads than hardware watchpoints available, some of the threads will
+ // fail to set hardware watchpoints while software ones may be available.
+ std::lock_guard<std::recursive_mutex> guard(m_threads_mutex);
+ for (const auto &thread : m_threads) {
+ assert(thread && "thread list should not have a NULL thread!");
+
+ Status thread_error =
+ thread->SetWatchpoint(addr, size, watch_flags, hardware);
+ if (thread_error.Fail() && hardware) {
+ // Try software watchpoints since we failed on hardware watchpoint
+ // setting and we may have just run out of hardware watchpoints.
+ thread_error = thread->SetWatchpoint(addr, size, watch_flags, false);
+ if (thread_error.Success())
+ LLDB_LOG(log,
+ "hardware watchpoint requested but software watchpoint set");
+ }
+
+ if (thread_error.Success()) {
+ // Remember that we set this watchpoint successfully in case we need to
+ // clear it later.
+ watchpoint_established_threads.push_back(thread.get());
+ } else {
+ // Unset the watchpoint for each thread we successfully set so that we
+ // get back to a consistent state of "not set" for the watchpoint.
+ for (auto unwatch_thread_sp : watchpoint_established_threads) {
+ Status remove_error = unwatch_thread_sp->RemoveWatchpoint(addr);
+ if (remove_error.Fail())
+ LLDB_LOG(log, "RemoveWatchpoint failed for pid={0}, tid={1}: {2}",
+ GetID(), unwatch_thread_sp->GetID(), remove_error);
+ }
+
+ return thread_error;
+ }
+ }
+ return m_watchpoint_list.Add(addr, size, watch_flags, hardware);
+}
+
+Status NativeProcessProtocol::RemoveWatchpoint(lldb::addr_t addr) {
+ // Update the thread list
+ UpdateThreads();
+
+ Status overall_error;
+
+ std::lock_guard<std::recursive_mutex> guard(m_threads_mutex);
+ for (const auto &thread : m_threads) {
+ assert(thread && "thread list should not have a NULL thread!");
+
+ const Status thread_error = thread->RemoveWatchpoint(addr);
+ if (thread_error.Fail()) {
+ // Keep track of the first thread error if any threads fail. We want to
+ // try to remove the watchpoint from every thread, though, even if one or
+ // more have errors.
+ if (!overall_error.Fail())
+ overall_error = thread_error;
+ }
+ }
+ const Status error = m_watchpoint_list.Remove(addr);
+ return overall_error.Fail() ? overall_error : error;
+}
+
+const HardwareBreakpointMap &
+NativeProcessProtocol::GetHardwareBreakpointMap() const {
+ return m_hw_breakpoints_map;
+}
+
+Status NativeProcessProtocol::SetHardwareBreakpoint(lldb::addr_t addr,
+ size_t size) {
+ // This default implementation assumes setting a hardware breakpoint for this
+ // process will require setting same hardware breakpoint for each of its
+ // existing threads. New thread will do the same once created.
+ Log *log = GetLog(LLDBLog::Process);
+
+ // Update the thread list
+ UpdateThreads();
+
+ // Exit here if target does not have required hardware breakpoint capability.
+ auto hw_debug_cap = GetHardwareDebugSupportInfo();
+
+ if (hw_debug_cap == std::nullopt || hw_debug_cap->first == 0 ||
+ hw_debug_cap->first <= m_hw_breakpoints_map.size())
+ return Status("Target does not have required no of hardware breakpoints");
+
+ // Vector below stores all thread pointer for which we have we successfully
+ // set this hardware breakpoint. If any of the current process threads fails
+ // to set this hardware breakpoint then roll back and remove this breakpoint
+ // for all the threads that had already set it successfully.
+ std::vector<NativeThreadProtocol *> breakpoint_established_threads;
+
+ // Request to set a hardware breakpoint for each of current process threads.
+ std::lock_guard<std::recursive_mutex> guard(m_threads_mutex);
+ for (const auto &thread : m_threads) {
+ assert(thread && "thread list should not have a NULL thread!");
+
+ Status thread_error = thread->SetHardwareBreakpoint(addr, size);
+ if (thread_error.Success()) {
+ // Remember that we set this breakpoint successfully in case we need to
+ // clear it later.
+ breakpoint_established_threads.push_back(thread.get());
+ } else {
+ // Unset the breakpoint for each thread we successfully set so that we
+ // get back to a consistent state of "not set" for this hardware
+ // breakpoint.
+ for (auto rollback_thread_sp : breakpoint_established_threads) {
+ Status remove_error =
+ rollback_thread_sp->RemoveHardwareBreakpoint(addr);
+ if (remove_error.Fail())
+ LLDB_LOG(log,
+ "RemoveHardwareBreakpoint failed for pid={0}, tid={1}: {2}",
+ GetID(), rollback_thread_sp->GetID(), remove_error);
+ }
+
+ return thread_error;
+ }
+ }
+
+ // Register new hardware breakpoint into hardware breakpoints map of current
+ // process.
+ m_hw_breakpoints_map[addr] = {addr, size};
+
+ return Status();
+}
+
+Status NativeProcessProtocol::RemoveHardwareBreakpoint(lldb::addr_t addr) {
+ // Update the thread list
+ UpdateThreads();
+
+ Status error;
+
+ std::lock_guard<std::recursive_mutex> guard(m_threads_mutex);
+ for (const auto &thread : m_threads) {
+ assert(thread && "thread list should not have a NULL thread!");
+ error = thread->RemoveHardwareBreakpoint(addr);
+ }
+
+ // Also remove from hardware breakpoint map of current process.
+ m_hw_breakpoints_map.erase(addr);
+
+ return error;
+}
+
+void NativeProcessProtocol::SynchronouslyNotifyProcessStateChanged(
+ lldb::StateType state) {
+ Log *log = GetLog(LLDBLog::Process);
+
+ m_delegate.ProcessStateChanged(this, state);
+
+ switch (state) {
+ case eStateStopped:
+ case eStateExited:
+ case eStateCrashed:
+ NotifyTracersProcessDidStop();
+ break;
+ default:
+ break;
+ }
+
+ LLDB_LOG(log, "sent state notification [{0}] from process {1}", state,
+ GetID());
+}
+
+void NativeProcessProtocol::NotifyDidExec() {
+ Log *log = GetLog(LLDBLog::Process);
+ LLDB_LOG(log, "process {0} exec()ed", GetID());
+
+ m_software_breakpoints.clear();
+
+ m_delegate.DidExec(this);
+}
+
+Status NativeProcessProtocol::SetSoftwareBreakpoint(lldb::addr_t addr,
+ uint32_t size_hint) {
+ Log *log = GetLog(LLDBLog::Breakpoints);
+ LLDB_LOG(log, "addr = {0:x}, size_hint = {1}", addr, size_hint);
+
+ auto it = m_software_breakpoints.find(addr);
+ if (it != m_software_breakpoints.end()) {
+ ++it->second.ref_count;
+ return Status();
+ }
+ auto expected_bkpt = EnableSoftwareBreakpoint(addr, size_hint);
+ if (!expected_bkpt)
+ return Status(expected_bkpt.takeError());
+
+ m_software_breakpoints.emplace(addr, std::move(*expected_bkpt));
+ return Status();
+}
+
+Status NativeProcessProtocol::RemoveSoftwareBreakpoint(lldb::addr_t addr) {
+ Log *log = GetLog(LLDBLog::Breakpoints);
+ LLDB_LOG(log, "addr = {0:x}", addr);
+ auto it = m_software_breakpoints.find(addr);
+ if (it == m_software_breakpoints.end())
+ return Status("Breakpoint not found.");
+ assert(it->second.ref_count > 0);
+ if (--it->second.ref_count > 0)
+ return Status();
+
+ // This is the last reference. Let's remove the breakpoint.
+ Status error;
+
+ // Clear a software breakpoint instruction
+ llvm::SmallVector<uint8_t, 4> curr_break_op(
+ it->second.breakpoint_opcodes.size(), 0);
+
+ // Read the breakpoint opcode
+ size_t bytes_read = 0;
+ error =
+ ReadMemory(addr, curr_break_op.data(), curr_break_op.size(), bytes_read);
+ if (error.Fail() || bytes_read < curr_break_op.size()) {
+ return Status("addr=0x%" PRIx64
+ ": tried to read %zu bytes but only read %zu",
+ addr, curr_break_op.size(), bytes_read);
+ }
+ const auto &saved = it->second.saved_opcodes;
+ // Make sure the breakpoint opcode exists at this address
+ if (llvm::ArrayRef(curr_break_op) != it->second.breakpoint_opcodes) {
+ if (curr_break_op != it->second.saved_opcodes)
+ return Status("Original breakpoint trap is no longer in memory.");
+ LLDB_LOG(log,
+ "Saved opcodes ({0:@[x]}) have already been restored at {1:x}.",
+ llvm::make_range(saved.begin(), saved.end()), addr);
+ } else {
+ // We found a valid breakpoint opcode at this address, now restore the
+ // saved opcode.
+ size_t bytes_written = 0;
+ error = WriteMemory(addr, saved.data(), saved.size(), bytes_written);
+ if (error.Fail() || bytes_written < saved.size()) {
+ return Status("addr=0x%" PRIx64
+ ": tried to write %zu bytes but only wrote %zu",
+ addr, saved.size(), bytes_written);
+ }
+
+ // Verify that our original opcode made it back to the inferior
+ llvm::SmallVector<uint8_t, 4> verify_opcode(saved.size(), 0);
+ size_t verify_bytes_read = 0;
+ error = ReadMemory(addr, verify_opcode.data(), verify_opcode.size(),
+ verify_bytes_read);
+ if (error.Fail() || verify_bytes_read < verify_opcode.size()) {
+ return Status("addr=0x%" PRIx64
+ ": tried to read %zu verification bytes but only read %zu",
+ addr, verify_opcode.size(), verify_bytes_read);
+ }
+ if (verify_opcode != saved)
+ LLDB_LOG(log, "Restoring bytes at {0:x}: {1:@[x]}", addr,
+ llvm::make_range(saved.begin(), saved.end()));
+ }
+
+ m_software_breakpoints.erase(it);
+ return Status();
+}
+
+llvm::Expected<NativeProcessProtocol::SoftwareBreakpoint>
+NativeProcessProtocol::EnableSoftwareBreakpoint(lldb::addr_t addr,
+ uint32_t size_hint) {
+ Log *log = GetLog(LLDBLog::Breakpoints);
+
+ auto expected_trap = GetSoftwareBreakpointTrapOpcode(size_hint);
+ if (!expected_trap)
+ return expected_trap.takeError();
+
+ llvm::SmallVector<uint8_t, 4> saved_opcode_bytes(expected_trap->size(), 0);
+ // Save the original opcodes by reading them so we can restore later.
+ size_t bytes_read = 0;
+ Status error = ReadMemory(addr, saved_opcode_bytes.data(),
+ saved_opcode_bytes.size(), bytes_read);
+ if (error.Fail())
+ return error.ToError();
+
+ // Ensure we read as many bytes as we expected.
+ if (bytes_read != saved_opcode_bytes.size()) {
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Failed to read memory while attempting to set breakpoint: attempted "
+ "to read {0} bytes but only read {1}.",
+ saved_opcode_bytes.size(), bytes_read);
+ }
+
+ LLDB_LOG(
+ log, "Overwriting bytes at {0:x}: {1:@[x]}", addr,
+ llvm::make_range(saved_opcode_bytes.begin(), saved_opcode_bytes.end()));
+
+ // Write a software breakpoint in place of the original opcode.
+ size_t bytes_written = 0;
+ error = WriteMemory(addr, expected_trap->data(), expected_trap->size(),
+ bytes_written);
+ if (error.Fail())
+ return error.ToError();
+
+ // Ensure we wrote as many bytes as we expected.
+ if (bytes_written != expected_trap->size()) {
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Failed write memory while attempting to set "
+ "breakpoint: attempted to write {0} bytes but only wrote {1}",
+ expected_trap->size(), bytes_written);
+ }
+
+ llvm::SmallVector<uint8_t, 4> verify_bp_opcode_bytes(expected_trap->size(),
+ 0);
+ size_t verify_bytes_read = 0;
+ error = ReadMemory(addr, verify_bp_opcode_bytes.data(),
+ verify_bp_opcode_bytes.size(), verify_bytes_read);
+ if (error.Fail())
+ return error.ToError();
+
+ // Ensure we read as many verification bytes as we expected.
+ if (verify_bytes_read != verify_bp_opcode_bytes.size()) {
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Failed to read memory while "
+ "attempting to verify breakpoint: attempted to read {0} bytes "
+ "but only read {1}",
+ verify_bp_opcode_bytes.size(), verify_bytes_read);
+ }
+
+ if (llvm::ArrayRef(verify_bp_opcode_bytes.data(), verify_bytes_read) !=
+ *expected_trap) {
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Verification of software breakpoint "
+ "writing failed - trap opcodes not successfully read back "
+ "after writing when setting breakpoint at {0:x}",
+ addr);
+ }
+
+ LLDB_LOG(log, "addr = {0:x}: SUCCESS", addr);
+ return SoftwareBreakpoint{1, saved_opcode_bytes, *expected_trap};
+}
+
+llvm::Expected<llvm::ArrayRef<uint8_t>>
+NativeProcessProtocol::GetSoftwareBreakpointTrapOpcode(size_t size_hint) {
+ static const uint8_t g_aarch64_opcode[] = {0x00, 0x00, 0x20, 0xd4};
+ static const uint8_t g_i386_opcode[] = {0xCC};
+ static const uint8_t g_mips64_opcode[] = {0x00, 0x00, 0x00, 0x0d};
+ static const uint8_t g_mips64el_opcode[] = {0x0d, 0x00, 0x00, 0x00};
+ static const uint8_t g_msp430_opcode[] = {0x43, 0x43};
+ static const uint8_t g_s390x_opcode[] = {0x00, 0x01};
+ static const uint8_t g_ppc_opcode[] = {0x7f, 0xe0, 0x00, 0x08}; // trap
+ static const uint8_t g_ppcle_opcode[] = {0x08, 0x00, 0xe0, 0x7f}; // trap
+ static const uint8_t g_riscv_opcode[] = {0x73, 0x00, 0x10, 0x00}; // ebreak
+ static const uint8_t g_riscv_opcode_c[] = {0x02, 0x90}; // c.ebreak
+ static const uint8_t g_loongarch_opcode[] = {0x05, 0x00, 0x2a,
+ 0x00}; // break 0x5
+
+ switch (GetArchitecture().GetMachine()) {
+ case llvm::Triple::aarch64:
+ case llvm::Triple::aarch64_32:
+ return llvm::ArrayRef(g_aarch64_opcode);
+
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ return llvm::ArrayRef(g_i386_opcode);
+
+ case llvm::Triple::mips:
+ case llvm::Triple::mips64:
+ return llvm::ArrayRef(g_mips64_opcode);
+
+ case llvm::Triple::mipsel:
+ case llvm::Triple::mips64el:
+ return llvm::ArrayRef(g_mips64el_opcode);
+
+ case llvm::Triple::msp430:
+ return llvm::ArrayRef(g_msp430_opcode);
+
+ case llvm::Triple::systemz:
+ return llvm::ArrayRef(g_s390x_opcode);
+
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ return llvm::ArrayRef(g_ppc_opcode);
+
+ case llvm::Triple::ppc64le:
+ return llvm::ArrayRef(g_ppcle_opcode);
+
+ case llvm::Triple::riscv32:
+ case llvm::Triple::riscv64: {
+ return size_hint == 2 ? llvm::ArrayRef(g_riscv_opcode_c)
+ : llvm::ArrayRef(g_riscv_opcode);
+ }
+
+ case llvm::Triple::loongarch32:
+ case llvm::Triple::loongarch64:
+ return llvm::ArrayRef(g_loongarch_opcode);
+
+ default:
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "CPU type not supported!");
+ }
+}
+
+size_t NativeProcessProtocol::GetSoftwareBreakpointPCOffset() {
+ switch (GetArchitecture().GetMachine()) {
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ case llvm::Triple::systemz:
+ // These architectures report increment the PC after breakpoint is hit.
+ return cantFail(GetSoftwareBreakpointTrapOpcode(0)).size();
+
+ case llvm::Triple::arm:
+ case llvm::Triple::aarch64:
+ case llvm::Triple::aarch64_32:
+ case llvm::Triple::mips64:
+ case llvm::Triple::mips64el:
+ case llvm::Triple::mips:
+ case llvm::Triple::mipsel:
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ case llvm::Triple::ppc64le:
+ case llvm::Triple::riscv32:
+ case llvm::Triple::riscv64:
+ case llvm::Triple::loongarch32:
+ case llvm::Triple::loongarch64:
+ // On these architectures the PC doesn't get updated for breakpoint hits.
+ return 0;
+
+ default:
+ llvm_unreachable("CPU type not supported!");
+ }
+}
+
+void NativeProcessProtocol::FixupBreakpointPCAsNeeded(
+ NativeThreadProtocol &thread) {
+ Log *log = GetLog(LLDBLog::Breakpoints);
+
+ Status error;
+
+ // Find out the size of a breakpoint (might depend on where we are in the
+ // code).
+ NativeRegisterContext &context = thread.GetRegisterContext();
+
+ uint32_t breakpoint_size = GetSoftwareBreakpointPCOffset();
+ LLDB_LOG(log, "breakpoint size: {0}", breakpoint_size);
+ if (breakpoint_size == 0)
+ return;
+
+ // First try probing for a breakpoint at a software breakpoint location: PC -
+ // breakpoint size.
+ const lldb::addr_t initial_pc_addr = context.GetPCfromBreakpointLocation();
+ lldb::addr_t breakpoint_addr = initial_pc_addr;
+ // Do not allow breakpoint probe to wrap around.
+ if (breakpoint_addr >= breakpoint_size)
+ breakpoint_addr -= breakpoint_size;
+
+ if (m_software_breakpoints.count(breakpoint_addr) == 0) {
+ // We didn't find one at a software probe location. Nothing to do.
+ LLDB_LOG(log,
+ "pid {0} no lldb software breakpoint found at current pc with "
+ "adjustment: {1}",
+ GetID(), breakpoint_addr);
+ return;
+ }
+
+ //
+ // We have a software breakpoint and need to adjust the PC.
+ //
+
+ // Change the program counter.
+ LLDB_LOG(log, "pid {0} tid {1}: changing PC from {2:x} to {3:x}", GetID(),
+ thread.GetID(), initial_pc_addr, breakpoint_addr);
+
+ error = context.SetPC(breakpoint_addr);
+ if (error.Fail()) {
+ // This can happen in case the process was killed between the time we read
+ // the PC and when we are updating it. There's nothing better to do than to
+ // swallow the error.
+ LLDB_LOG(log, "pid {0} tid {1}: failed to set PC: {2}", GetID(),
+ thread.GetID(), error);
+ }
+}
+
+Status NativeProcessProtocol::RemoveBreakpoint(lldb::addr_t addr,
+ bool hardware) {
+ if (hardware)
+ return RemoveHardwareBreakpoint(addr);
+ else
+ return RemoveSoftwareBreakpoint(addr);
+}
+
+Status NativeProcessProtocol::ReadMemoryWithoutTrap(lldb::addr_t addr,
+ void *buf, size_t size,
+ size_t &bytes_read) {
+ Status error = ReadMemory(addr, buf, size, bytes_read);
+ if (error.Fail())
+ return error;
+
+ llvm::MutableArrayRef data(static_cast<uint8_t *>(buf), bytes_read);
+ for (const auto &pair : m_software_breakpoints) {
+ lldb::addr_t bp_addr = pair.first;
+ auto saved_opcodes = llvm::ArrayRef(pair.second.saved_opcodes);
+
+ if (bp_addr + saved_opcodes.size() < addr || addr + bytes_read <= bp_addr)
+ continue; // Breakpoint not in range, ignore
+
+ if (bp_addr < addr) {
+ saved_opcodes = saved_opcodes.drop_front(addr - bp_addr);
+ bp_addr = addr;
+ }
+ auto bp_data = data.drop_front(bp_addr - addr);
+ std::copy_n(saved_opcodes.begin(),
+ std::min(saved_opcodes.size(), bp_data.size()),
+ bp_data.begin());
+ }
+ return Status();
+}
+
+llvm::Expected<llvm::StringRef>
+NativeProcessProtocol::ReadCStringFromMemory(lldb::addr_t addr, char *buffer,
+ size_t max_size,
+ size_t &total_bytes_read) {
+ static const size_t cache_line_size =
+ llvm::sys::Process::getPageSizeEstimate();
+ size_t bytes_read = 0;
+ size_t bytes_left = max_size;
+ addr_t curr_addr = addr;
+ size_t string_size;
+ char *curr_buffer = buffer;
+ total_bytes_read = 0;
+ Status status;
+
+ while (bytes_left > 0 && status.Success()) {
+ addr_t cache_line_bytes_left =
+ cache_line_size - (curr_addr % cache_line_size);
+ addr_t bytes_to_read = std::min<addr_t>(bytes_left, cache_line_bytes_left);
+ status = ReadMemory(curr_addr, static_cast<void *>(curr_buffer),
+ bytes_to_read, bytes_read);
+
+ if (bytes_read == 0)
+ break;
+
+ void *str_end = std::memchr(curr_buffer, '\0', bytes_read);
+ if (str_end != nullptr) {
+ total_bytes_read =
+ static_cast<size_t>((static_cast<char *>(str_end) - buffer + 1));
+ status.Clear();
+ break;
+ }
+
+ total_bytes_read += bytes_read;
+ curr_buffer += bytes_read;
+ curr_addr += bytes_read;
+ bytes_left -= bytes_read;
+ }
+
+ string_size = total_bytes_read - 1;
+
+ // Make sure we return a null terminated string.
+ if (bytes_left == 0 && max_size > 0 && buffer[max_size - 1] != '\0') {
+ buffer[max_size - 1] = '\0';
+ total_bytes_read--;
+ }
+
+ if (!status.Success())
+ return status.ToError();
+
+ return llvm::StringRef(buffer, string_size);
+}
+
+lldb::StateType NativeProcessProtocol::GetState() const {
+ std::lock_guard<std::recursive_mutex> guard(m_state_mutex);
+ return m_state;
+}
+
+void NativeProcessProtocol::SetState(lldb::StateType state,
+ bool notify_delegates) {
+ std::lock_guard<std::recursive_mutex> guard(m_state_mutex);
+
+ if (state == m_state)
+ return;
+
+ m_state = state;
+
+ if (StateIsStoppedState(state, false)) {
+ ++m_stop_id;
+
+ // Give process a chance to do any stop id bump processing, such as
+ // clearing cached data that is invalidated each time the process runs.
+ // Note if/when we support some threads running, we'll end up needing to
+ // manage this per thread and per process.
+ DoStopIDBumped(m_stop_id);
+ }
+
+ // Optionally notify delegates of the state change.
+ if (notify_delegates)
+ SynchronouslyNotifyProcessStateChanged(state);
+}
+
+uint32_t NativeProcessProtocol::GetStopID() const {
+ std::lock_guard<std::recursive_mutex> guard(m_state_mutex);
+ return m_stop_id;
+}
+
+void NativeProcessProtocol::DoStopIDBumped(uint32_t /* newBumpId */) {
+ // Default implementation does nothing.
+}
+
+NativeProcessProtocol::Manager::~Manager() = default;
diff --git a/contrib/llvm-project/lldb/source/Host/common/NativeRegisterContext.cpp b/contrib/llvm-project/lldb/source/Host/common/NativeRegisterContext.cpp
new file mode 100644
index 000000000000..40a57f3d5c82
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/NativeRegisterContext.cpp
@@ -0,0 +1,458 @@
+//===-- NativeRegisterContext.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/common/NativeRegisterContext.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Host/common/NativeProcessProtocol.h"
+#include "lldb/Host/common/NativeThreadProtocol.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+NativeRegisterContext::NativeRegisterContext(NativeThreadProtocol &thread)
+ : m_thread(thread) {}
+
+// Destructor
+NativeRegisterContext::~NativeRegisterContext() = default;
+
+// FIXME revisit invalidation, process stop ids, etc. Right now we don't
+// support caching in NativeRegisterContext. We can do this later by utilizing
+// NativeProcessProtocol::GetStopID () and adding a stop id to
+// NativeRegisterContext.
+
+// void
+// NativeRegisterContext::InvalidateIfNeeded (bool force) {
+// ProcessSP process_sp (m_thread.GetProcess());
+// bool invalidate = force;
+// uint32_t process_stop_id = UINT32_MAX;
+
+// if (process_sp)
+// process_stop_id = process_sp->GetStopID();
+// else
+// invalidate = true;
+
+// if (!invalidate)
+// invalidate = process_stop_id != GetStopID();
+
+// if (invalidate)
+// {
+// InvalidateAllRegisters ();
+// SetStopID (process_stop_id);
+// }
+// }
+
+const RegisterInfo *
+NativeRegisterContext::GetRegisterInfoByName(llvm::StringRef reg_name,
+ uint32_t start_idx) {
+ if (reg_name.empty())
+ return nullptr;
+
+ // Generic register names take precedence over specific register names.
+ // For example, on x86 we want "sp" to refer to the complete RSP/ESP register
+ // rather than the 16-bit SP pseudo-register.
+ uint32_t generic_reg = Args::StringToGenericRegister(reg_name);
+ if (generic_reg != LLDB_INVALID_REGNUM) {
+ const RegisterInfo *reg_info =
+ GetRegisterInfo(eRegisterKindGeneric, generic_reg);
+ if (reg_info)
+ return reg_info;
+ }
+
+ const uint32_t num_registers = GetRegisterCount();
+ for (uint32_t reg = start_idx; reg < num_registers; ++reg) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg);
+
+ if (reg_name.equals_insensitive(reg_info->name) ||
+ reg_name.equals_insensitive(reg_info->alt_name))
+ return reg_info;
+ }
+
+ return nullptr;
+}
+
+const RegisterInfo *NativeRegisterContext::GetRegisterInfo(uint32_t kind,
+ uint32_t num) {
+ const uint32_t reg_num = ConvertRegisterKindToRegisterNumber(kind, num);
+ if (reg_num == LLDB_INVALID_REGNUM)
+ return nullptr;
+ return GetRegisterInfoAtIndex(reg_num);
+}
+
+const char *NativeRegisterContext::GetRegisterName(uint32_t reg) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg);
+ if (reg_info)
+ return reg_info->name;
+ return nullptr;
+}
+
+const char *NativeRegisterContext::GetRegisterSetNameForRegisterAtIndex(
+ uint32_t reg_index) const {
+ const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(reg_index);
+ if (!reg_info)
+ return nullptr;
+
+ for (uint32_t set_index = 0; set_index < GetRegisterSetCount(); ++set_index) {
+ const RegisterSet *const reg_set = GetRegisterSet(set_index);
+ if (!reg_set)
+ continue;
+
+ for (uint32_t reg_num_index = 0; reg_num_index < reg_set->num_registers;
+ ++reg_num_index) {
+ const uint32_t reg_num = reg_set->registers[reg_num_index];
+ // FIXME double check we're checking the right register kind here.
+ if (reg_info->kinds[RegisterKind::eRegisterKindLLDB] == reg_num) {
+ // The given register is a member of this register set. Return the
+ // register set name.
+ return reg_set->name;
+ }
+ }
+ }
+
+ // Didn't find it.
+ return nullptr;
+}
+
+lldb::addr_t NativeRegisterContext::GetPC(lldb::addr_t fail_value) {
+ Log *log = GetLog(LLDBLog::Thread);
+
+ uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC);
+ LLDB_LOGF(log, "Using reg index %" PRIu32 " (default %" PRIu64 ")", reg,
+ fail_value);
+
+ const uint64_t retval = ReadRegisterAsUnsigned(reg, fail_value);
+
+ LLDB_LOGF(log, PRIu32 " retval %" PRIu64, retval);
+
+ return retval;
+}
+
+lldb::addr_t
+NativeRegisterContext::GetPCfromBreakpointLocation(lldb::addr_t fail_value) {
+ return GetPC(fail_value);
+}
+
+Status NativeRegisterContext::SetPC(lldb::addr_t pc) {
+ uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC);
+ return WriteRegisterFromUnsigned(reg, pc);
+}
+
+lldb::addr_t NativeRegisterContext::GetSP(lldb::addr_t fail_value) {
+ uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_SP);
+ return ReadRegisterAsUnsigned(reg, fail_value);
+}
+
+Status NativeRegisterContext::SetSP(lldb::addr_t sp) {
+ uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_SP);
+ return WriteRegisterFromUnsigned(reg, sp);
+}
+
+lldb::addr_t NativeRegisterContext::GetFP(lldb::addr_t fail_value) {
+ uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_FP);
+ return ReadRegisterAsUnsigned(reg, fail_value);
+}
+
+Status NativeRegisterContext::SetFP(lldb::addr_t fp) {
+ uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_FP);
+ return WriteRegisterFromUnsigned(reg, fp);
+}
+
+lldb::addr_t NativeRegisterContext::GetReturnAddress(lldb::addr_t fail_value) {
+ uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_RA);
+ return ReadRegisterAsUnsigned(reg, fail_value);
+}
+
+lldb::addr_t NativeRegisterContext::GetFlags(lldb::addr_t fail_value) {
+ uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_FLAGS);
+ return ReadRegisterAsUnsigned(reg, fail_value);
+}
+
+lldb::addr_t
+NativeRegisterContext::ReadRegisterAsUnsigned(uint32_t reg,
+ lldb::addr_t fail_value) {
+ if (reg != LLDB_INVALID_REGNUM)
+ return ReadRegisterAsUnsigned(GetRegisterInfoAtIndex(reg), fail_value);
+ return fail_value;
+}
+
+uint64_t
+NativeRegisterContext::ReadRegisterAsUnsigned(const RegisterInfo *reg_info,
+ lldb::addr_t fail_value) {
+ Log *log = GetLog(LLDBLog::Thread);
+
+ if (reg_info) {
+ RegisterValue value;
+ Status error = ReadRegister(reg_info, value);
+ if (error.Success()) {
+ LLDB_LOGF(log,
+ "Read register succeeded: value "
+ "%" PRIu64,
+ value.GetAsUInt64());
+ return value.GetAsUInt64();
+ } else {
+ LLDB_LOGF(log, "Read register failed: error %s", error.AsCString());
+ }
+ } else {
+ LLDB_LOGF(log, "Read register failed: null reg_info");
+ }
+ return fail_value;
+}
+
+Status NativeRegisterContext::WriteRegisterFromUnsigned(uint32_t reg,
+ uint64_t uval) {
+ if (reg == LLDB_INVALID_REGNUM)
+ return Status("Write register failed: reg is invalid");
+ return WriteRegisterFromUnsigned(GetRegisterInfoAtIndex(reg), uval);
+}
+
+Status
+NativeRegisterContext::WriteRegisterFromUnsigned(const RegisterInfo *reg_info,
+ uint64_t uval) {
+ assert(reg_info);
+ if (!reg_info)
+ return Status("reg_info is nullptr");
+
+ RegisterValue value;
+ if (!value.SetUInt(uval, reg_info->byte_size))
+ return Status("RegisterValue::SetUInt () failed");
+
+ return WriteRegister(reg_info, value);
+}
+
+lldb::tid_t NativeRegisterContext::GetThreadID() const {
+ return m_thread.GetID();
+}
+
+uint32_t NativeRegisterContext::NumSupportedHardwareBreakpoints() { return 0; }
+
+uint32_t NativeRegisterContext::SetHardwareBreakpoint(lldb::addr_t addr,
+ size_t size) {
+ return LLDB_INVALID_INDEX32;
+}
+
+Status NativeRegisterContext::ClearAllHardwareBreakpoints() {
+ return Status("not implemented");
+}
+
+bool NativeRegisterContext::ClearHardwareBreakpoint(uint32_t hw_idx) {
+ return false;
+}
+
+Status NativeRegisterContext::GetHardwareBreakHitIndex(uint32_t &bp_index,
+ lldb::addr_t trap_addr) {
+ bp_index = LLDB_INVALID_INDEX32;
+ return Status("not implemented");
+}
+
+uint32_t NativeRegisterContext::NumSupportedHardwareWatchpoints() { return 0; }
+
+uint32_t NativeRegisterContext::SetHardwareWatchpoint(lldb::addr_t addr,
+ size_t size,
+ uint32_t watch_flags) {
+ return LLDB_INVALID_INDEX32;
+}
+
+bool NativeRegisterContext::ClearHardwareWatchpoint(uint32_t hw_index) {
+ return false;
+}
+
+Status NativeRegisterContext::ClearWatchpointHit(uint32_t hw_index) {
+ return Status("not implemented");
+}
+
+Status NativeRegisterContext::ClearAllHardwareWatchpoints() {
+ return Status("not implemented");
+}
+
+Status NativeRegisterContext::IsWatchpointHit(uint32_t wp_index, bool &is_hit) {
+ is_hit = false;
+ return Status("not implemented");
+}
+
+Status NativeRegisterContext::GetWatchpointHitIndex(uint32_t &wp_index,
+ lldb::addr_t trap_addr) {
+ wp_index = LLDB_INVALID_INDEX32;
+ return Status("not implemented");
+}
+
+Status NativeRegisterContext::IsWatchpointVacant(uint32_t wp_index,
+ bool &is_vacant) {
+ is_vacant = false;
+ return Status("not implemented");
+}
+
+lldb::addr_t NativeRegisterContext::GetWatchpointAddress(uint32_t wp_index) {
+ return LLDB_INVALID_ADDRESS;
+}
+
+lldb::addr_t NativeRegisterContext::GetWatchpointHitAddress(uint32_t wp_index) {
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool NativeRegisterContext::HardwareSingleStep(bool enable) { return false; }
+
+Status NativeRegisterContext::ReadRegisterValueFromMemory(
+ const RegisterInfo *reg_info, lldb::addr_t src_addr, size_t src_len,
+ RegisterValue &reg_value) {
+ Status error;
+ if (reg_info == nullptr) {
+ error.SetErrorString("invalid register info argument.");
+ return error;
+ }
+
+ // Moving from addr into a register
+ //
+ // Case 1: src_len == dst_len
+ //
+ // |AABBCCDD| Address contents
+ // |AABBCCDD| Register contents
+ //
+ // Case 2: src_len > dst_len
+ //
+ // Status! (The register should always be big enough to hold the data)
+ //
+ // Case 3: src_len < dst_len
+ //
+ // |AABB| Address contents
+ // |AABB0000| Register contents [on little-endian hardware]
+ // |0000AABB| Register contents [on big-endian hardware]
+ const size_t dst_len = reg_info->byte_size;
+
+ if (src_len > dst_len) {
+ error.SetErrorStringWithFormat(
+ "%" PRIu64 " bytes is too big to store in register %s (%" PRIu64
+ " bytes)",
+ static_cast<uint64_t>(src_len), reg_info->name,
+ static_cast<uint64_t>(dst_len));
+ return error;
+ }
+
+ NativeProcessProtocol &process = m_thread.GetProcess();
+ RegisterValue::BytesContainer src(src_len);
+
+ // Read the memory
+ size_t bytes_read;
+ error = process.ReadMemory(src_addr, src.data(), src_len, bytes_read);
+ if (error.Fail())
+ return error;
+
+ // Make sure the memory read succeeded...
+ if (bytes_read != src_len) {
+ // This might happen if we read _some_ bytes but not all
+ error.SetErrorStringWithFormat("read %" PRIu64 " of %" PRIu64 " bytes",
+ static_cast<uint64_t>(bytes_read),
+ static_cast<uint64_t>(src_len));
+ return error;
+ }
+
+ // We now have a memory buffer that contains the part or all of the register
+ // value. Set the register value using this memory data.
+ // TODO: we might need to add a parameter to this function in case the byte
+ // order of the memory data doesn't match the process. For now we are
+ // assuming they are the same.
+ reg_value.SetFromMemoryData(*reg_info, src.data(), src_len,
+ process.GetByteOrder(), error);
+
+ return error;
+}
+
+Status NativeRegisterContext::WriteRegisterValueToMemory(
+ const RegisterInfo *reg_info, lldb::addr_t dst_addr, size_t dst_len,
+ const RegisterValue &reg_value) {
+ Status error;
+ if (reg_info == nullptr) {
+ error.SetErrorString("Invalid register info argument.");
+ return error;
+ }
+
+ RegisterValue::BytesContainer dst(dst_len);
+ NativeProcessProtocol &process = m_thread.GetProcess();
+
+ // TODO: we might need to add a parameter to this function in case the byte
+ // order of the memory data doesn't match the process. For now we are
+ // assuming they are the same.
+ const size_t bytes_copied = reg_value.GetAsMemoryData(
+ *reg_info, dst.data(), dst_len, process.GetByteOrder(), error);
+
+ if (error.Success()) {
+ if (bytes_copied == 0) {
+ error.SetErrorString("byte copy failed.");
+ } else {
+ size_t bytes_written;
+ error = process.WriteMemory(dst_addr, dst.data(), bytes_copied,
+ bytes_written);
+ if (error.Fail())
+ return error;
+
+ if (bytes_written != bytes_copied) {
+ // This might happen if we read _some_ bytes but not all
+ error.SetErrorStringWithFormat("only wrote %" PRIu64 " of %" PRIu64
+ " bytes",
+ static_cast<uint64_t>(bytes_written),
+ static_cast<uint64_t>(bytes_copied));
+ }
+ }
+ }
+
+ return error;
+}
+
+uint32_t
+NativeRegisterContext::ConvertRegisterKindToRegisterNumber(uint32_t kind,
+ uint32_t num) const {
+ const uint32_t num_regs = GetRegisterCount();
+
+ assert(kind < kNumRegisterKinds);
+ for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_idx);
+
+ if (reg_info->kinds[kind] == num)
+ return reg_idx;
+ }
+
+ return LLDB_INVALID_REGNUM;
+}
+
+std::vector<uint32_t>
+NativeRegisterContext::GetExpeditedRegisters(ExpeditedRegs expType) const {
+ if (expType == ExpeditedRegs::Minimal) {
+ // Expedite only a minimum set of important generic registers.
+ static const uint32_t k_expedited_registers[] = {
+ LLDB_REGNUM_GENERIC_PC, LLDB_REGNUM_GENERIC_SP, LLDB_REGNUM_GENERIC_FP,
+ LLDB_REGNUM_GENERIC_RA};
+
+ std::vector<uint32_t> expedited_reg_nums;
+ for (uint32_t gen_reg : k_expedited_registers) {
+ uint32_t reg_num =
+ ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, gen_reg);
+ if (reg_num == LLDB_INVALID_REGNUM)
+ continue; // Target does not support the given register.
+ else
+ expedited_reg_nums.push_back(reg_num);
+ }
+
+ return expedited_reg_nums;
+ }
+
+ if (GetRegisterSetCount() > 0 && expType == ExpeditedRegs::Full)
+ return std::vector<uint32_t>(GetRegisterSet(0)->registers,
+ GetRegisterSet(0)->registers +
+ GetRegisterSet(0)->num_registers);
+
+ return std::vector<uint32_t>();
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/NativeThreadProtocol.cpp b/contrib/llvm-project/lldb/source/Host/common/NativeThreadProtocol.cpp
new file mode 100644
index 000000000000..16901bc9cf7b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/NativeThreadProtocol.cpp
@@ -0,0 +1,19 @@
+//===-- NativeThreadProtocol.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/common/NativeThreadProtocol.h"
+
+#include "lldb/Host/common/NativeProcessProtocol.h"
+#include "lldb/Host/common/NativeRegisterContext.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+NativeThreadProtocol::NativeThreadProtocol(NativeProcessProtocol &process,
+ lldb::tid_t tid)
+ : m_process(process), m_tid(tid) {}
diff --git a/contrib/llvm-project/lldb/source/Host/common/NativeWatchpointList.cpp b/contrib/llvm-project/lldb/source/Host/common/NativeWatchpointList.cpp
new file mode 100644
index 000000000000..6d856202d147
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/NativeWatchpointList.cpp
@@ -0,0 +1,30 @@
+//===-- NativeWatchpointList.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/common/NativeWatchpointList.h"
+
+#include "lldb/Utility/Log.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+Status NativeWatchpointList::Add(addr_t addr, size_t size, uint32_t watch_flags,
+ bool hardware) {
+ m_watchpoints[addr] = {addr, size, watch_flags, hardware};
+ return Status();
+}
+
+Status NativeWatchpointList::Remove(addr_t addr) {
+ m_watchpoints.erase(addr);
+ return Status();
+}
+
+const NativeWatchpointList::WatchpointMap &
+NativeWatchpointList::GetWatchpointMap() const {
+ return m_watchpoints;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/OptionParser.cpp b/contrib/llvm-project/lldb/source/Host/common/OptionParser.cpp
new file mode 100644
index 000000000000..0274fdc4ac3f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/OptionParser.cpp
@@ -0,0 +1,84 @@
+//===-- source/Host/common/OptionParser.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/OptionParser.h"
+#include "lldb/Host/HostGetOpt.h"
+#include "lldb/Utility/OptionDefinition.h"
+#include "lldb/lldb-private-types.h"
+
+#include <vector>
+
+using namespace lldb_private;
+
+void OptionParser::Prepare(std::unique_lock<std::mutex> &lock) {
+ static std::mutex g_mutex;
+ lock = std::unique_lock<std::mutex>(g_mutex);
+#ifdef __GLIBC__
+ optind = 0;
+#else
+ optreset = 1;
+ optind = 1;
+#endif
+}
+
+void OptionParser::EnableError(bool error) { opterr = error ? 1 : 0; }
+
+int OptionParser::Parse(llvm::MutableArrayRef<char *> argv,
+ llvm::StringRef optstring, const Option *longopts,
+ int *longindex) {
+ std::vector<option> opts;
+ while (longopts->definition != nullptr) {
+ option opt;
+ opt.flag = longopts->flag;
+ opt.val = longopts->val;
+ opt.name = longopts->definition->long_option;
+ opt.has_arg = longopts->definition->option_has_arg;
+ opts.push_back(opt);
+ ++longopts;
+ }
+ opts.push_back(option());
+ std::string opt_cstr = std::string(optstring);
+ return getopt_long_only(argv.size() - 1, argv.data(), opt_cstr.c_str(),
+ &opts[0], longindex);
+}
+
+char *OptionParser::GetOptionArgument() { return optarg; }
+
+int OptionParser::GetOptionIndex() { return optind; }
+
+int OptionParser::GetOptionErrorCause() { return optopt; }
+
+std::string OptionParser::GetShortOptionString(struct option *long_options) {
+ std::string s;
+ int i = 0;
+ bool done = false;
+ while (!done) {
+ if (long_options[i].name == nullptr && long_options[i].has_arg == 0 &&
+ long_options[i].flag == nullptr && long_options[i].val == 0) {
+ done = true;
+ } else {
+ if (long_options[i].flag == nullptr && isalpha(long_options[i].val)) {
+ s.append(1, (char)long_options[i].val);
+ switch (long_options[i].has_arg) {
+ default:
+ case no_argument:
+ break;
+
+ case optional_argument:
+ s.append(2, ':');
+ break;
+ case required_argument:
+ s.append(1, ':');
+ break;
+ }
+ }
+ ++i;
+ }
+ }
+ return s;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/PipeBase.cpp b/contrib/llvm-project/lldb/source/Host/common/PipeBase.cpp
new file mode 100644
index 000000000000..b3e0ab34a58d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/PipeBase.cpp
@@ -0,0 +1,24 @@
+//===-- source/Host/common/PipeBase.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/PipeBase.h"
+
+using namespace lldb_private;
+
+PipeBase::~PipeBase() = default;
+
+Status PipeBase::OpenAsWriter(llvm::StringRef name,
+ bool child_process_inherit) {
+ return OpenAsWriterWithTimeout(name, child_process_inherit,
+ std::chrono::microseconds::zero());
+}
+
+Status PipeBase::Read(void *buf, size_t size, size_t &bytes_read) {
+ return ReadWithTimeout(buf, size, std::chrono::microseconds::zero(),
+ bytes_read);
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/ProcessLaunchInfo.cpp b/contrib/llvm-project/lldb/source/Host/common/ProcessLaunchInfo.cpp
new file mode 100644
index 000000000000..a1866b2a99fd
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/ProcessLaunchInfo.cpp
@@ -0,0 +1,342 @@
+//===-- ProcessLaunchInfo.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 <climits>
+
+#include "lldb/Host/Config.h"
+#include "lldb/Host/FileAction.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/ProcessLaunchInfo.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/FileSystem.h"
+
+#if !defined(_WIN32)
+#include <climits>
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+
+// ProcessLaunchInfo member functions
+
+ProcessLaunchInfo::ProcessLaunchInfo()
+ : ProcessInfo(), m_working_dir(), m_plugin_name(), m_flags(0),
+ m_file_actions(), m_pty(new PseudoTerminal), m_monitor_callback(nullptr) {
+}
+
+ProcessLaunchInfo::ProcessLaunchInfo(const FileSpec &stdin_file_spec,
+ const FileSpec &stdout_file_spec,
+ const FileSpec &stderr_file_spec,
+ const FileSpec &working_directory,
+ uint32_t launch_flags)
+ : ProcessInfo(), m_working_dir(), m_plugin_name(), m_flags(launch_flags),
+ m_file_actions(), m_pty(new PseudoTerminal) {
+ if (stdin_file_spec) {
+ FileAction file_action;
+ const bool read = true;
+ const bool write = false;
+ if (file_action.Open(STDIN_FILENO, stdin_file_spec, read, write))
+ AppendFileAction(file_action);
+ }
+ if (stdout_file_spec) {
+ FileAction file_action;
+ const bool read = false;
+ const bool write = true;
+ if (file_action.Open(STDOUT_FILENO, stdout_file_spec, read, write))
+ AppendFileAction(file_action);
+ }
+ if (stderr_file_spec) {
+ FileAction file_action;
+ const bool read = false;
+ const bool write = true;
+ if (file_action.Open(STDERR_FILENO, stderr_file_spec, read, write))
+ AppendFileAction(file_action);
+ }
+ if (working_directory)
+ SetWorkingDirectory(working_directory);
+}
+
+bool ProcessLaunchInfo::AppendCloseFileAction(int fd) {
+ FileAction file_action;
+ if (file_action.Close(fd)) {
+ AppendFileAction(file_action);
+ return true;
+ }
+ return false;
+}
+
+bool ProcessLaunchInfo::AppendDuplicateFileAction(int fd, int dup_fd) {
+ FileAction file_action;
+ if (file_action.Duplicate(fd, dup_fd)) {
+ AppendFileAction(file_action);
+ return true;
+ }
+ return false;
+}
+
+bool ProcessLaunchInfo::AppendOpenFileAction(int fd, const FileSpec &file_spec,
+ bool read, bool write) {
+ FileAction file_action;
+ if (file_action.Open(fd, file_spec, read, write)) {
+ AppendFileAction(file_action);
+ return true;
+ }
+ return false;
+}
+
+bool ProcessLaunchInfo::AppendSuppressFileAction(int fd, bool read,
+ bool write) {
+ FileAction file_action;
+ if (file_action.Open(fd, FileSpec(FileSystem::DEV_NULL), read, write)) {
+ AppendFileAction(file_action);
+ return true;
+ }
+ return false;
+}
+
+const FileAction *ProcessLaunchInfo::GetFileActionAtIndex(size_t idx) const {
+ if (idx < m_file_actions.size())
+ return &m_file_actions[idx];
+ return nullptr;
+}
+
+const FileAction *ProcessLaunchInfo::GetFileActionForFD(int fd) const {
+ for (size_t idx = 0, count = m_file_actions.size(); idx < count; ++idx) {
+ if (m_file_actions[idx].GetFD() == fd)
+ return &m_file_actions[idx];
+ }
+ return nullptr;
+}
+
+const FileSpec &ProcessLaunchInfo::GetWorkingDirectory() const {
+ return m_working_dir;
+}
+
+void ProcessLaunchInfo::SetWorkingDirectory(const FileSpec &working_dir) {
+ m_working_dir = working_dir;
+}
+
+llvm::StringRef ProcessLaunchInfo::GetProcessPluginName() const {
+ return llvm::StringRef(m_plugin_name);
+}
+
+void ProcessLaunchInfo::SetProcessPluginName(llvm::StringRef plugin) {
+ m_plugin_name = std::string(plugin);
+}
+
+const FileSpec &ProcessLaunchInfo::GetShell() const { return m_shell; }
+
+void ProcessLaunchInfo::SetShell(const FileSpec &shell) {
+ m_shell = shell;
+ if (m_shell) {
+ FileSystem::Instance().ResolveExecutableLocation(m_shell);
+ m_flags.Set(lldb::eLaunchFlagLaunchInShell);
+ } else
+ m_flags.Clear(lldb::eLaunchFlagLaunchInShell);
+}
+
+void ProcessLaunchInfo::SetLaunchInSeparateProcessGroup(bool separate) {
+ if (separate)
+ m_flags.Set(lldb::eLaunchFlagLaunchInSeparateProcessGroup);
+ else
+ m_flags.Clear(lldb::eLaunchFlagLaunchInSeparateProcessGroup);
+}
+
+void ProcessLaunchInfo::SetShellExpandArguments(bool expand) {
+ if (expand)
+ m_flags.Set(lldb::eLaunchFlagShellExpandArguments);
+ else
+ m_flags.Clear(lldb::eLaunchFlagShellExpandArguments);
+}
+
+void ProcessLaunchInfo::Clear() {
+ ProcessInfo::Clear();
+ m_working_dir.Clear();
+ m_plugin_name.clear();
+ m_shell.Clear();
+ m_flags.Clear();
+ m_file_actions.clear();
+ m_resume_count = 0;
+ m_listener_sp.reset();
+ m_hijack_listener_sp.reset();
+}
+
+void ProcessLaunchInfo::NoOpMonitorCallback(lldb::pid_t pid, int signal,
+ int status) {
+ Log *log = GetLog(LLDBLog::Process);
+ LLDB_LOG(log, "pid = {0}, signal = {1}, status = {2}", pid, signal, status);
+}
+
+bool ProcessLaunchInfo::MonitorProcess() const {
+ if (m_monitor_callback && ProcessIDIsValid()) {
+ llvm::Expected<HostThread> maybe_thread =
+ Host::StartMonitoringChildProcess(m_monitor_callback, GetProcessID());
+ if (!maybe_thread)
+ LLDB_LOG_ERROR(GetLog(LLDBLog::Host), maybe_thread.takeError(),
+ "failed to launch host thread: {0}");
+ return true;
+ }
+ return false;
+}
+
+void ProcessLaunchInfo::SetDetachOnError(bool enable) {
+ if (enable)
+ m_flags.Set(lldb::eLaunchFlagDetachOnError);
+ else
+ m_flags.Clear(lldb::eLaunchFlagDetachOnError);
+}
+
+llvm::Error ProcessLaunchInfo::SetUpPtyRedirection() {
+ Log *log = GetLog(LLDBLog::Process);
+
+ bool stdin_free = GetFileActionForFD(STDIN_FILENO) == nullptr;
+ bool stdout_free = GetFileActionForFD(STDOUT_FILENO) == nullptr;
+ bool stderr_free = GetFileActionForFD(STDERR_FILENO) == nullptr;
+ bool any_free = stdin_free || stdout_free || stderr_free;
+ if (!any_free)
+ return llvm::Error::success();
+
+ LLDB_LOG(log, "Generating a pty to use for stdin/out/err");
+
+ int open_flags = O_RDWR | O_NOCTTY;
+#if !defined(_WIN32)
+ // We really shouldn't be specifying platform specific flags that are
+ // intended for a system call in generic code. But this will have to
+ // do for now.
+ open_flags |= O_CLOEXEC;
+#endif
+ if (llvm::Error Err = m_pty->OpenFirstAvailablePrimary(open_flags))
+ return Err;
+
+ const FileSpec secondary_file_spec(m_pty->GetSecondaryName());
+
+ if (stdin_free)
+ AppendOpenFileAction(STDIN_FILENO, secondary_file_spec, true, false);
+
+ if (stdout_free)
+ AppendOpenFileAction(STDOUT_FILENO, secondary_file_spec, false, true);
+
+ if (stderr_free)
+ AppendOpenFileAction(STDERR_FILENO, secondary_file_spec, false, true);
+ return llvm::Error::success();
+}
+
+bool ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell(
+ Status &error, bool will_debug, bool first_arg_is_full_shell_command,
+ uint32_t num_resumes) {
+ error.Clear();
+
+ if (GetFlags().Test(eLaunchFlagLaunchInShell)) {
+ if (m_shell) {
+ std::string shell_executable = m_shell.GetPath();
+
+ const char **argv = GetArguments().GetConstArgumentVector();
+ if (argv == nullptr || argv[0] == nullptr)
+ return false;
+ Args shell_arguments;
+ shell_arguments.AppendArgument(shell_executable);
+ const llvm::Triple &triple = GetArchitecture().GetTriple();
+ if (triple.getOS() == llvm::Triple::Win32 &&
+ !triple.isWindowsCygwinEnvironment())
+ shell_arguments.AppendArgument(llvm::StringRef("/C"));
+ else
+ shell_arguments.AppendArgument(llvm::StringRef("-c"));
+
+ StreamString shell_command;
+ if (will_debug) {
+ // Add a modified PATH environment variable in case argv[0] is a
+ // relative path.
+ const char *argv0 = argv[0];
+ FileSpec arg_spec(argv0);
+ if (arg_spec.IsRelative()) {
+ // We have a relative path to our executable which may not work if we
+ // just try to run "a.out" (without it being converted to "./a.out")
+ FileSpec working_dir = GetWorkingDirectory();
+ // Be sure to put quotes around PATH's value in case any paths have
+ // spaces...
+ std::string new_path("PATH=\"");
+ const size_t empty_path_len = new_path.size();
+
+ if (working_dir) {
+ new_path += working_dir.GetPath();
+ } else {
+ llvm::SmallString<64> cwd;
+ if (! llvm::sys::fs::current_path(cwd))
+ new_path += cwd;
+ }
+ std::string curr_path;
+ if (HostInfo::GetEnvironmentVar("PATH", curr_path)) {
+ if (new_path.size() > empty_path_len)
+ new_path += ':';
+ new_path += curr_path;
+ }
+ new_path += "\" ";
+ shell_command.PutCString(new_path);
+ }
+
+ if (triple.getOS() != llvm::Triple::Win32 ||
+ triple.isWindowsCygwinEnvironment())
+ shell_command.PutCString("exec");
+
+ // Only Apple supports /usr/bin/arch being able to specify the
+ // architecture
+ if (GetArchitecture().IsValid() && // Valid architecture
+ GetArchitecture().GetTriple().getVendor() ==
+ llvm::Triple::Apple && // Apple only
+ GetArchitecture().GetCore() !=
+ ArchSpec::eCore_x86_64_x86_64h) // Don't do this for x86_64h
+ {
+ shell_command.Printf(" /usr/bin/arch -arch %s",
+ GetArchitecture().GetArchitectureName());
+ // Set the resume count to 2:
+ // 1 - stop in shell
+ // 2 - stop in /usr/bin/arch
+ // 3 - then we will stop in our program
+ SetResumeCount(num_resumes + 1);
+ } else {
+ // Set the resume count to 1:
+ // 1 - stop in shell
+ // 2 - then we will stop in our program
+ SetResumeCount(num_resumes);
+ }
+ }
+
+ if (first_arg_is_full_shell_command) {
+ // There should only be one argument that is the shell command itself
+ // to be used as is
+ if (argv[0] && !argv[1])
+ shell_command.Printf("%s", argv[0]);
+ else
+ return false;
+ } else {
+ for (size_t i = 0; argv[i] != nullptr; ++i) {
+ std::string safe_arg = Args::GetShellSafeArgument(m_shell, argv[i]);
+ if (safe_arg.empty())
+ safe_arg = "\"\"";
+ // Add a space to separate this arg from the previous one.
+ shell_command.PutCString(" ");
+ shell_command.PutCString(safe_arg);
+ }
+ }
+ shell_arguments.AppendArgument(shell_command.GetString());
+ m_executable = m_shell;
+ m_arguments = shell_arguments;
+ return true;
+ } else {
+ error.SetErrorString("invalid shell path");
+ }
+ } else {
+ error.SetErrorString("not launching in shell");
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/ProcessRunLock.cpp b/contrib/llvm-project/lldb/source/Host/common/ProcessRunLock.cpp
new file mode 100644
index 000000000000..da59f4057697
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/ProcessRunLock.cpp
@@ -0,0 +1,65 @@
+//===-- ProcessRunLock.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _WIN32
+#include "lldb/Host/ProcessRunLock.h"
+
+namespace lldb_private {
+
+ProcessRunLock::ProcessRunLock() {
+ int err = ::pthread_rwlock_init(&m_rwlock, nullptr);
+ (void)err;
+}
+
+ProcessRunLock::~ProcessRunLock() {
+ int err = ::pthread_rwlock_destroy(&m_rwlock);
+ (void)err;
+}
+
+bool ProcessRunLock::ReadTryLock() {
+ ::pthread_rwlock_rdlock(&m_rwlock);
+ if (!m_running) {
+ // coverity[missing_unlock]
+ return true;
+ }
+ ::pthread_rwlock_unlock(&m_rwlock);
+ return false;
+}
+
+bool ProcessRunLock::ReadUnlock() {
+ return ::pthread_rwlock_unlock(&m_rwlock) == 0;
+}
+
+bool ProcessRunLock::SetRunning() {
+ ::pthread_rwlock_wrlock(&m_rwlock);
+ m_running = true;
+ ::pthread_rwlock_unlock(&m_rwlock);
+ return true;
+}
+
+bool ProcessRunLock::TrySetRunning() {
+ bool r;
+
+ if (::pthread_rwlock_trywrlock(&m_rwlock) == 0) {
+ r = !m_running;
+ m_running = true;
+ ::pthread_rwlock_unlock(&m_rwlock);
+ return r;
+ }
+ return false;
+}
+
+bool ProcessRunLock::SetStopped() {
+ ::pthread_rwlock_wrlock(&m_rwlock);
+ m_running = false;
+ ::pthread_rwlock_unlock(&m_rwlock);
+ return true;
+}
+}
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Host/common/PseudoTerminal.cpp b/contrib/llvm-project/lldb/source/Host/common/PseudoTerminal.cpp
new file mode 100644
index 000000000000..d53327973eb2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/PseudoTerminal.cpp
@@ -0,0 +1,221 @@
+//===-- PseudoTerminal.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/PseudoTerminal.h"
+#include "lldb/Host/Config.h"
+#include "lldb/Host/FileSystem.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Errno.h"
+#include <cassert>
+#include <climits>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <mutex>
+#if defined(TIOCSCTTY)
+#include <sys/ioctl.h>
+#endif
+
+#include "lldb/Host/PosixApi.h"
+
+#if defined(__APPLE__)
+#include <Availability.h>
+#endif
+
+#if defined(__ANDROID__)
+int posix_openpt(int flags);
+#endif
+
+using namespace lldb_private;
+
+// PseudoTerminal constructor
+PseudoTerminal::PseudoTerminal() = default;
+
+// Destructor
+//
+// The destructor will close the primary and secondary file descriptors if they
+// are valid and ownership has not been released using the
+// ReleasePrimaryFileDescriptor() or the ReleaseSaveFileDescriptor() member
+// functions.
+PseudoTerminal::~PseudoTerminal() {
+ ClosePrimaryFileDescriptor();
+ CloseSecondaryFileDescriptor();
+}
+
+// Close the primary file descriptor if it is valid.
+void PseudoTerminal::ClosePrimaryFileDescriptor() {
+ if (m_primary_fd >= 0) {
+ ::close(m_primary_fd);
+ m_primary_fd = invalid_fd;
+ }
+}
+
+// Close the secondary file descriptor if it is valid.
+void PseudoTerminal::CloseSecondaryFileDescriptor() {
+ if (m_secondary_fd >= 0) {
+ ::close(m_secondary_fd);
+ m_secondary_fd = invalid_fd;
+ }
+}
+
+llvm::Error PseudoTerminal::OpenFirstAvailablePrimary(int oflag) {
+#if LLDB_ENABLE_POSIX
+ // Open the primary side of a pseudo terminal
+ m_primary_fd = ::posix_openpt(oflag);
+ if (m_primary_fd < 0) {
+ return llvm::errorCodeToError(
+ std::error_code(errno, std::generic_category()));
+ }
+
+ // Grant access to the secondary pseudo terminal
+ if (::grantpt(m_primary_fd) < 0) {
+ std::error_code EC(errno, std::generic_category());
+ ClosePrimaryFileDescriptor();
+ return llvm::errorCodeToError(EC);
+ }
+
+ // Clear the lock flag on the secondary pseudo terminal
+ if (::unlockpt(m_primary_fd) < 0) {
+ std::error_code EC(errno, std::generic_category());
+ ClosePrimaryFileDescriptor();
+ return llvm::errorCodeToError(EC);
+ }
+
+ return llvm::Error::success();
+#else
+ return llvm::errorCodeToError(llvm::errc::not_supported);
+#endif
+}
+
+llvm::Error PseudoTerminal::OpenSecondary(int oflag) {
+ CloseSecondaryFileDescriptor();
+
+ std::string name = GetSecondaryName();
+ m_secondary_fd = FileSystem::Instance().Open(name.c_str(), oflag);
+ if (m_secondary_fd >= 0)
+ return llvm::Error::success();
+
+ return llvm::errorCodeToError(
+ std::error_code(errno, std::generic_category()));
+}
+
+#if !HAVE_PTSNAME_R || defined(__APPLE__)
+static std::string use_ptsname(int fd) {
+ static std::mutex mutex;
+ std::lock_guard<std::mutex> guard(mutex);
+ const char *r = ptsname(fd);
+ assert(r != nullptr);
+ return r;
+}
+#endif
+
+std::string PseudoTerminal::GetSecondaryName() const {
+ assert(m_primary_fd >= 0);
+#if HAVE_PTSNAME_R
+#if defined(__APPLE__)
+ if (__builtin_available(macos 10.13.4, iOS 11.3, tvOS 11.3, watchOS 4.4, *)) {
+#endif
+ char buf[PATH_MAX];
+ buf[0] = '\0';
+ int r = ptsname_r(m_primary_fd, buf, sizeof(buf));
+ UNUSED_IF_ASSERT_DISABLED(r);
+ assert(r == 0);
+ return buf;
+#if defined(__APPLE__)
+ } else {
+ return use_ptsname(m_primary_fd);
+ }
+#endif
+#else
+ return use_ptsname(m_primary_fd);
+#endif
+}
+
+llvm::Expected<lldb::pid_t> PseudoTerminal::Fork() {
+#if LLDB_ENABLE_POSIX
+ if (llvm::Error Err = OpenFirstAvailablePrimary(O_RDWR | O_CLOEXEC))
+ return std::move(Err);
+
+ pid_t pid = ::fork();
+ if (pid < 0) {
+ return llvm::errorCodeToError(
+ std::error_code(errno, std::generic_category()));
+ }
+ if (pid > 0) {
+ // Parent process.
+ return pid;
+ }
+
+ // Child Process
+ ::setsid();
+
+ if (llvm::Error Err = OpenSecondary(O_RDWR))
+ return std::move(Err);
+
+ // Primary FD should have O_CLOEXEC set, but let's close it just in
+ // case...
+ ClosePrimaryFileDescriptor();
+
+#if defined(TIOCSCTTY)
+ // Acquire the controlling terminal
+ if (::ioctl(m_secondary_fd, TIOCSCTTY, (char *)0) < 0) {
+ return llvm::errorCodeToError(
+ std::error_code(errno, std::generic_category()));
+ }
+#endif
+ // Duplicate all stdio file descriptors to the secondary pseudo terminal
+ for (int fd : {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}) {
+ if (::dup2(m_secondary_fd, fd) != fd) {
+ return llvm::errorCodeToError(
+ std::error_code(errno, std::generic_category()));
+ }
+ }
+#endif
+ return 0;
+}
+
+// The primary file descriptor accessor. This object retains ownership of the
+// primary file descriptor when this accessor is used. Use
+// ReleasePrimaryFileDescriptor() if you wish this object to release ownership
+// of the primary file descriptor.
+//
+// Returns the primary file descriptor, or -1 if the primary file descriptor is
+// not currently valid.
+int PseudoTerminal::GetPrimaryFileDescriptor() const { return m_primary_fd; }
+
+// The secondary file descriptor accessor.
+//
+// Returns the secondary file descriptor, or -1 if the secondary file descriptor
+// is not currently valid.
+int PseudoTerminal::GetSecondaryFileDescriptor() const {
+ return m_secondary_fd;
+}
+
+// Release ownership of the primary pseudo terminal file descriptor without
+// closing it. The destructor for this class will close the primary file
+// descriptor if the ownership isn't released using this call and the primary
+// file descriptor has been opened.
+int PseudoTerminal::ReleasePrimaryFileDescriptor() {
+ // Release ownership of the primary pseudo terminal file descriptor without
+ // closing it. (the destructor for this class will close it otherwise!)
+ int fd = m_primary_fd;
+ m_primary_fd = invalid_fd;
+ return fd;
+}
+
+// Release ownership of the secondary pseudo terminal file descriptor without
+// closing it. The destructor for this class will close the secondary file
+// descriptor if the ownership isn't released using this call and the secondary
+// file descriptor has been opened.
+int PseudoTerminal::ReleaseSecondaryFileDescriptor() {
+ // Release ownership of the secondary pseudo terminal file descriptor without
+ // closing it (the destructor for this class will close it otherwise!)
+ int fd = m_secondary_fd;
+ m_secondary_fd = invalid_fd;
+ return fd;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/Socket.cpp b/contrib/llvm-project/lldb/source/Host/common/Socket.cpp
new file mode 100644
index 000000000000..f9911cf136cb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/Socket.cpp
@@ -0,0 +1,376 @@
+//===-- Socket.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/Socket.h"
+
+#include "lldb/Host/Config.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/SocketAddress.h"
+#include "lldb/Host/common/TCPSocket.h"
+#include "lldb/Host/common/UDPSocket.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Errno.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/WindowsError.h"
+
+#if LLDB_ENABLE_POSIX
+#include "lldb/Host/posix/DomainSocket.h"
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#endif
+
+#ifdef __linux__
+#include "lldb/Host/linux/AbstractSocket.h"
+#endif
+
+#ifdef __ANDROID__
+#include <arpa/inet.h>
+#include <asm-generic/errno-base.h>
+#include <cerrno>
+#include <fcntl.h>
+#include <linux/tcp.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#endif // __ANDROID__
+
+using namespace lldb;
+using namespace lldb_private;
+
+#if defined(_WIN32)
+typedef const char *set_socket_option_arg_type;
+typedef char *get_socket_option_arg_type;
+const NativeSocket Socket::kInvalidSocketValue = INVALID_SOCKET;
+#else // #if defined(_WIN32)
+typedef const void *set_socket_option_arg_type;
+typedef void *get_socket_option_arg_type;
+const NativeSocket Socket::kInvalidSocketValue = -1;
+#endif // #if defined(_WIN32)
+
+static bool IsInterrupted() {
+#if defined(_WIN32)
+ return ::WSAGetLastError() == WSAEINTR;
+#else
+ return errno == EINTR;
+#endif
+}
+
+Socket::Socket(SocketProtocol protocol, bool should_close,
+ bool child_processes_inherit)
+ : IOObject(eFDTypeSocket), m_protocol(protocol),
+ m_socket(kInvalidSocketValue),
+ m_child_processes_inherit(child_processes_inherit),
+ m_should_close_fd(should_close) {}
+
+Socket::~Socket() { Close(); }
+
+llvm::Error Socket::Initialize() {
+#if defined(_WIN32)
+ auto wVersion = WINSOCK_VERSION;
+ WSADATA wsaData;
+ int err = ::WSAStartup(wVersion, &wsaData);
+ if (err == 0) {
+ if (wsaData.wVersion < wVersion) {
+ WSACleanup();
+ return llvm::createStringError("WSASock version is not expected.");
+ }
+ } else {
+ return llvm::errorCodeToError(llvm::mapWindowsError(::WSAGetLastError()));
+ }
+#endif
+
+ return llvm::Error::success();
+}
+
+void Socket::Terminate() {
+#if defined(_WIN32)
+ ::WSACleanup();
+#endif
+}
+
+std::unique_ptr<Socket> Socket::Create(const SocketProtocol protocol,
+ bool child_processes_inherit,
+ Status &error) {
+ error.Clear();
+
+ std::unique_ptr<Socket> socket_up;
+ switch (protocol) {
+ case ProtocolTcp:
+ socket_up =
+ std::make_unique<TCPSocket>(true, child_processes_inherit);
+ break;
+ case ProtocolUdp:
+ socket_up =
+ std::make_unique<UDPSocket>(true, child_processes_inherit);
+ break;
+ case ProtocolUnixDomain:
+#if LLDB_ENABLE_POSIX
+ socket_up =
+ std::make_unique<DomainSocket>(true, child_processes_inherit);
+#else
+ error.SetErrorString(
+ "Unix domain sockets are not supported on this platform.");
+#endif
+ break;
+ case ProtocolUnixAbstract:
+#ifdef __linux__
+ socket_up =
+ std::make_unique<AbstractSocket>(child_processes_inherit);
+#else
+ error.SetErrorString(
+ "Abstract domain sockets are not supported on this platform.");
+#endif
+ break;
+ }
+
+ if (error.Fail())
+ socket_up.reset();
+
+ return socket_up;
+}
+
+llvm::Expected<std::unique_ptr<Socket>>
+Socket::TcpConnect(llvm::StringRef host_and_port,
+ bool child_processes_inherit) {
+ Log *log = GetLog(LLDBLog::Connection);
+ LLDB_LOG(log, "host_and_port = {0}", host_and_port);
+
+ Status error;
+ std::unique_ptr<Socket> connect_socket(
+ Create(ProtocolTcp, child_processes_inherit, error));
+ if (error.Fail())
+ return error.ToError();
+
+ error = connect_socket->Connect(host_and_port);
+ if (error.Success())
+ return std::move(connect_socket);
+
+ return error.ToError();
+}
+
+llvm::Expected<std::unique_ptr<TCPSocket>>
+Socket::TcpListen(llvm::StringRef host_and_port, bool child_processes_inherit,
+ int backlog) {
+ Log *log = GetLog(LLDBLog::Connection);
+ LLDB_LOG(log, "host_and_port = {0}", host_and_port);
+
+ std::unique_ptr<TCPSocket> listen_socket(
+ new TCPSocket(true, child_processes_inherit));
+
+ Status error = listen_socket->Listen(host_and_port, backlog);
+ if (error.Fail())
+ return error.ToError();
+
+ return std::move(listen_socket);
+}
+
+llvm::Expected<std::unique_ptr<UDPSocket>>
+Socket::UdpConnect(llvm::StringRef host_and_port,
+ bool child_processes_inherit) {
+ return UDPSocket::Connect(host_and_port, child_processes_inherit);
+}
+
+llvm::Expected<Socket::HostAndPort> Socket::DecodeHostAndPort(llvm::StringRef host_and_port) {
+ static llvm::Regex g_regex("([^:]+|\\[[0-9a-fA-F:]+.*\\]):([0-9]+)");
+ HostAndPort ret;
+ llvm::SmallVector<llvm::StringRef, 3> matches;
+ if (g_regex.match(host_and_port, &matches)) {
+ ret.hostname = matches[1].str();
+ // IPv6 addresses are wrapped in [] when specified with ports
+ if (ret.hostname.front() == '[' && ret.hostname.back() == ']')
+ ret.hostname = ret.hostname.substr(1, ret.hostname.size() - 2);
+ if (to_integer(matches[2], ret.port, 10))
+ return ret;
+ } else {
+ // If this was unsuccessful, then check if it's simply an unsigned 16-bit
+ // integer, representing a port with an empty host.
+ if (to_integer(host_and_port, ret.port, 10))
+ return ret;
+ }
+
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "invalid host:port specification: '%s'",
+ host_and_port.str().c_str());
+}
+
+IOObject::WaitableHandle Socket::GetWaitableHandle() {
+ // TODO: On Windows, use WSAEventSelect
+ return m_socket;
+}
+
+Status Socket::Read(void *buf, size_t &num_bytes) {
+ Status error;
+ int bytes_received = 0;
+ do {
+ bytes_received = ::recv(m_socket, static_cast<char *>(buf), num_bytes, 0);
+ } while (bytes_received < 0 && IsInterrupted());
+
+ if (bytes_received < 0) {
+ SetLastError(error);
+ num_bytes = 0;
+ } else
+ num_bytes = bytes_received;
+
+ Log *log = GetLog(LLDBLog::Communication);
+ if (log) {
+ LLDB_LOGF(log,
+ "%p Socket::Read() (socket = %" PRIu64
+ ", src = %p, src_len = %" PRIu64 ", flags = 0) => %" PRIi64
+ " (error = %s)",
+ static_cast<void *>(this), static_cast<uint64_t>(m_socket), buf,
+ static_cast<uint64_t>(num_bytes),
+ static_cast<int64_t>(bytes_received), error.AsCString());
+ }
+
+ return error;
+}
+
+Status Socket::Write(const void *buf, size_t &num_bytes) {
+ const size_t src_len = num_bytes;
+ Status error;
+ int bytes_sent = 0;
+ do {
+ bytes_sent = Send(buf, num_bytes);
+ } while (bytes_sent < 0 && IsInterrupted());
+
+ if (bytes_sent < 0) {
+ SetLastError(error);
+ num_bytes = 0;
+ } else
+ num_bytes = bytes_sent;
+
+ Log *log = GetLog(LLDBLog::Communication);
+ if (log) {
+ LLDB_LOGF(log,
+ "%p Socket::Write() (socket = %" PRIu64
+ ", src = %p, src_len = %" PRIu64 ", flags = 0) => %" PRIi64
+ " (error = %s)",
+ static_cast<void *>(this), static_cast<uint64_t>(m_socket), buf,
+ static_cast<uint64_t>(src_len),
+ static_cast<int64_t>(bytes_sent), error.AsCString());
+ }
+
+ return error;
+}
+
+Status Socket::Close() {
+ Status error;
+ if (!IsValid() || !m_should_close_fd)
+ return error;
+
+ Log *log = GetLog(LLDBLog::Connection);
+ LLDB_LOGF(log, "%p Socket::Close (fd = %" PRIu64 ")",
+ static_cast<void *>(this), static_cast<uint64_t>(m_socket));
+
+#if defined(_WIN32)
+ bool success = closesocket(m_socket) == 0;
+#else
+ bool success = ::close(m_socket) == 0;
+#endif
+ // A reference to a FD was passed in, set it to an invalid value
+ m_socket = kInvalidSocketValue;
+ if (!success) {
+ SetLastError(error);
+ }
+
+ return error;
+}
+
+int Socket::GetOption(int level, int option_name, int &option_value) {
+ get_socket_option_arg_type option_value_p =
+ reinterpret_cast<get_socket_option_arg_type>(&option_value);
+ socklen_t option_value_size = sizeof(int);
+ return ::getsockopt(m_socket, level, option_name, option_value_p,
+ &option_value_size);
+}
+
+int Socket::SetOption(int level, int option_name, int option_value) {
+ set_socket_option_arg_type option_value_p =
+ reinterpret_cast<get_socket_option_arg_type>(&option_value);
+ return ::setsockopt(m_socket, level, option_name, option_value_p,
+ sizeof(option_value));
+}
+
+size_t Socket::Send(const void *buf, const size_t num_bytes) {
+ return ::send(m_socket, static_cast<const char *>(buf), num_bytes, 0);
+}
+
+void Socket::SetLastError(Status &error) {
+#if defined(_WIN32)
+ error.SetError(::WSAGetLastError(), lldb::eErrorTypeWin32);
+#else
+ error.SetErrorToErrno();
+#endif
+}
+
+NativeSocket Socket::CreateSocket(const int domain, const int type,
+ const int protocol,
+ bool child_processes_inherit, Status &error) {
+ error.Clear();
+ auto socket_type = type;
+#ifdef SOCK_CLOEXEC
+ if (!child_processes_inherit)
+ socket_type |= SOCK_CLOEXEC;
+#endif
+ auto sock = ::socket(domain, socket_type, protocol);
+ if (sock == kInvalidSocketValue)
+ SetLastError(error);
+
+ return sock;
+}
+
+NativeSocket Socket::AcceptSocket(NativeSocket sockfd, struct sockaddr *addr,
+ socklen_t *addrlen,
+ bool child_processes_inherit, Status &error) {
+ error.Clear();
+#if defined(ANDROID_USE_ACCEPT_WORKAROUND)
+ // Hack:
+ // This enables static linking lldb-server to an API 21 libc, but still
+ // having it run on older devices. It is necessary because API 21 libc's
+ // implementation of accept() uses the accept4 syscall(), which is not
+ // available in older kernels. Using an older libc would fix this issue, but
+ // introduce other ones, as the old libraries were quite buggy.
+ int fd = syscall(__NR_accept, sockfd, addr, addrlen);
+ if (fd >= 0 && !child_processes_inherit) {
+ int flags = ::fcntl(fd, F_GETFD);
+ if (flags != -1 && ::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != -1)
+ return fd;
+ SetLastError(error);
+ close(fd);
+ }
+ return fd;
+#elif defined(SOCK_CLOEXEC) && defined(HAVE_ACCEPT4)
+ int flags = 0;
+ if (!child_processes_inherit) {
+ flags |= SOCK_CLOEXEC;
+ }
+ NativeSocket fd = llvm::sys::RetryAfterSignal(
+ static_cast<NativeSocket>(-1), ::accept4, sockfd, addr, addrlen, flags);
+#else
+ NativeSocket fd = llvm::sys::RetryAfterSignal(
+ static_cast<NativeSocket>(-1), ::accept, sockfd, addr, addrlen);
+#endif
+ if (fd == kInvalidSocketValue)
+ SetLastError(error);
+ return fd;
+}
+
+llvm::raw_ostream &lldb_private::operator<<(llvm::raw_ostream &OS,
+ const Socket::HostAndPort &HP) {
+ return OS << '[' << HP.hostname << ']' << ':' << HP.port;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/SocketAddress.cpp b/contrib/llvm-project/lldb/source/Host/common/SocketAddress.cpp
new file mode 100644
index 000000000000..6a23c633e54b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/SocketAddress.cpp
@@ -0,0 +1,324 @@
+//===-- SocketAddress.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Note: This file is used on Darwin by debugserver, so it needs to remain as
+// self contained as possible, and devoid of references to LLVM unless
+// there is compelling reason.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(_MSC_VER)
+#define _WINSOCK_DEPRECATED_NO_WARNINGS
+#endif
+
+#include "lldb/Host/SocketAddress.h"
+#include <cstddef>
+#include <cstdio>
+
+#if !defined(_WIN32)
+#include <arpa/inet.h>
+#endif
+
+#include <cassert>
+#include <cstring>
+
+#include "lldb/Host/PosixApi.h"
+
+// WindowsXP needs an inet_ntop implementation
+#ifdef _WIN32
+
+#ifndef INET6_ADDRSTRLEN // might not be defined in older Windows SDKs
+#define INET6_ADDRSTRLEN 46
+#endif
+
+// TODO: implement shortened form "::" for runs of zeros
+const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) {
+ if (size == 0) {
+ return nullptr;
+ }
+
+ switch (af) {
+ case AF_INET: {
+ {
+ const char *formatted = inet_ntoa(*static_cast<const in_addr *>(src));
+ if (formatted && strlen(formatted) < static_cast<size_t>(size)) {
+ return ::strcpy(dst, formatted);
+ }
+ }
+ return nullptr;
+ case AF_INET6: {
+ char tmp[INET6_ADDRSTRLEN] = {0};
+ const uint16_t *src16 = static_cast<const uint16_t *>(src);
+ int full_size = ::snprintf(
+ tmp, sizeof(tmp), "%x:%x:%x:%x:%x:%x:%x:%x", ntohs(src16[0]),
+ ntohs(src16[1]), ntohs(src16[2]), ntohs(src16[3]), ntohs(src16[4]),
+ ntohs(src16[5]), ntohs(src16[6]), ntohs(src16[7]));
+ if (full_size < static_cast<int>(size)) {
+ return ::strcpy(dst, tmp);
+ }
+ return nullptr;
+ }
+ }
+ }
+ return nullptr;
+}
+#endif
+
+using namespace lldb_private;
+
+// SocketAddress constructor
+SocketAddress::SocketAddress() { Clear(); }
+
+SocketAddress::SocketAddress(const struct sockaddr &s) { m_socket_addr.sa = s; }
+
+SocketAddress::SocketAddress(const struct sockaddr_in &s) {
+ m_socket_addr.sa_ipv4 = s;
+}
+
+SocketAddress::SocketAddress(const struct sockaddr_in6 &s) {
+ m_socket_addr.sa_ipv6 = s;
+}
+
+SocketAddress::SocketAddress(const struct sockaddr_storage &s) {
+ m_socket_addr.sa_storage = s;
+}
+
+SocketAddress::SocketAddress(const struct addrinfo *addr_info) {
+ *this = addr_info;
+}
+
+// Destructor
+SocketAddress::~SocketAddress() = default;
+
+void SocketAddress::Clear() {
+ memset(&m_socket_addr, 0, sizeof(m_socket_addr));
+}
+
+bool SocketAddress::IsValid() const { return GetLength() != 0; }
+
+static socklen_t GetFamilyLength(sa_family_t family) {
+ switch (family) {
+ case AF_INET:
+ return sizeof(struct sockaddr_in);
+ case AF_INET6:
+ return sizeof(struct sockaddr_in6);
+ }
+ assert(0 && "Unsupported address family");
+ return 0;
+}
+
+socklen_t SocketAddress::GetLength() const {
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
+ defined(__OpenBSD__)
+ return m_socket_addr.sa.sa_len;
+#else
+ return GetFamilyLength(GetFamily());
+#endif
+}
+
+socklen_t SocketAddress::GetMaxLength() { return sizeof(sockaddr_t); }
+
+sa_family_t SocketAddress::GetFamily() const {
+ return m_socket_addr.sa.sa_family;
+}
+
+void SocketAddress::SetFamily(sa_family_t family) {
+ m_socket_addr.sa.sa_family = family;
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
+ defined(__OpenBSD__)
+ m_socket_addr.sa.sa_len = GetFamilyLength(family);
+#endif
+}
+
+std::string SocketAddress::GetIPAddress() const {
+ char str[INET6_ADDRSTRLEN] = {0};
+ switch (GetFamily()) {
+ case AF_INET:
+ if (inet_ntop(GetFamily(), &m_socket_addr.sa_ipv4.sin_addr, str,
+ sizeof(str)))
+ return str;
+ break;
+ case AF_INET6:
+ if (inet_ntop(GetFamily(), &m_socket_addr.sa_ipv6.sin6_addr, str,
+ sizeof(str)))
+ return str;
+ break;
+ }
+ return "";
+}
+
+uint16_t SocketAddress::GetPort() const {
+ switch (GetFamily()) {
+ case AF_INET:
+ return ntohs(m_socket_addr.sa_ipv4.sin_port);
+ case AF_INET6:
+ return ntohs(m_socket_addr.sa_ipv6.sin6_port);
+ }
+ return 0;
+}
+
+bool SocketAddress::SetPort(uint16_t port) {
+ switch (GetFamily()) {
+ case AF_INET:
+ m_socket_addr.sa_ipv4.sin_port = htons(port);
+ return true;
+
+ case AF_INET6:
+ m_socket_addr.sa_ipv6.sin6_port = htons(port);
+ return true;
+ }
+ return false;
+}
+
+// SocketAddress assignment operator
+const SocketAddress &SocketAddress::
+operator=(const struct addrinfo *addr_info) {
+ Clear();
+ if (addr_info && addr_info->ai_addr && addr_info->ai_addrlen > 0 &&
+ size_t(addr_info->ai_addrlen) <= sizeof m_socket_addr) {
+ ::memcpy(&m_socket_addr, addr_info->ai_addr, addr_info->ai_addrlen);
+ }
+ return *this;
+}
+
+const SocketAddress &SocketAddress::operator=(const struct sockaddr &s) {
+ m_socket_addr.sa = s;
+ return *this;
+}
+
+const SocketAddress &SocketAddress::operator=(const struct sockaddr_in &s) {
+ m_socket_addr.sa_ipv4 = s;
+ return *this;
+}
+
+const SocketAddress &SocketAddress::operator=(const struct sockaddr_in6 &s) {
+ m_socket_addr.sa_ipv6 = s;
+ return *this;
+}
+
+const SocketAddress &SocketAddress::
+operator=(const struct sockaddr_storage &s) {
+ m_socket_addr.sa_storage = s;
+ return *this;
+}
+
+bool SocketAddress::getaddrinfo(const char *host, const char *service,
+ int ai_family, int ai_socktype, int ai_protocol,
+ int ai_flags) {
+ Clear();
+
+ auto addresses = GetAddressInfo(host, service, ai_family, ai_socktype,
+ ai_protocol, ai_flags);
+ if (!addresses.empty())
+ *this = addresses[0];
+ return IsValid();
+}
+
+std::vector<SocketAddress>
+SocketAddress::GetAddressInfo(const char *hostname, const char *servname,
+ int ai_family, int ai_socktype, int ai_protocol,
+ int ai_flags) {
+ std::vector<SocketAddress> addr_list;
+
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = ai_family;
+ hints.ai_socktype = ai_socktype;
+ hints.ai_protocol = ai_protocol;
+ hints.ai_flags = ai_flags;
+
+ struct addrinfo *service_info_list = nullptr;
+ int err = ::getaddrinfo(hostname, servname, &hints, &service_info_list);
+ if (err == 0 && service_info_list) {
+ for (struct addrinfo *service_ptr = service_info_list;
+ service_ptr != nullptr; service_ptr = service_ptr->ai_next) {
+ addr_list.emplace_back(SocketAddress(service_ptr));
+ }
+ }
+
+ if (service_info_list)
+ ::freeaddrinfo(service_info_list);
+ return addr_list;
+}
+
+bool SocketAddress::SetToLocalhost(sa_family_t family, uint16_t port) {
+ switch (family) {
+ case AF_INET:
+ SetFamily(AF_INET);
+ if (SetPort(port)) {
+ m_socket_addr.sa_ipv4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ return true;
+ }
+ break;
+
+ case AF_INET6:
+ SetFamily(AF_INET6);
+ if (SetPort(port)) {
+ m_socket_addr.sa_ipv6.sin6_addr = in6addr_loopback;
+ return true;
+ }
+ break;
+ }
+ Clear();
+ return false;
+}
+
+bool SocketAddress::SetToAnyAddress(sa_family_t family, uint16_t port) {
+ switch (family) {
+ case AF_INET:
+ SetFamily(AF_INET);
+ if (SetPort(port)) {
+ m_socket_addr.sa_ipv4.sin_addr.s_addr = htonl(INADDR_ANY);
+ return true;
+ }
+ break;
+
+ case AF_INET6:
+ SetFamily(AF_INET6);
+ if (SetPort(port)) {
+ m_socket_addr.sa_ipv6.sin6_addr = in6addr_any;
+ return true;
+ }
+ break;
+ }
+ Clear();
+ return false;
+}
+
+bool SocketAddress::IsAnyAddr() const {
+ return (GetFamily() == AF_INET)
+ ? m_socket_addr.sa_ipv4.sin_addr.s_addr == htonl(INADDR_ANY)
+ : 0 == memcmp(&m_socket_addr.sa_ipv6.sin6_addr, &in6addr_any, 16);
+}
+
+bool SocketAddress::IsLocalhost() const {
+ return (GetFamily() == AF_INET)
+ ? m_socket_addr.sa_ipv4.sin_addr.s_addr == htonl(INADDR_LOOPBACK)
+ : 0 == memcmp(&m_socket_addr.sa_ipv6.sin6_addr, &in6addr_loopback,
+ 16);
+}
+
+bool SocketAddress::operator==(const SocketAddress &rhs) const {
+ if (GetFamily() != rhs.GetFamily())
+ return false;
+ if (GetLength() != rhs.GetLength())
+ return false;
+ switch (GetFamily()) {
+ case AF_INET:
+ return m_socket_addr.sa_ipv4.sin_addr.s_addr ==
+ rhs.m_socket_addr.sa_ipv4.sin_addr.s_addr;
+ case AF_INET6:
+ return 0 == memcmp(&m_socket_addr.sa_ipv6.sin6_addr,
+ &rhs.m_socket_addr.sa_ipv6.sin6_addr, 16);
+ }
+ return false;
+}
+
+bool SocketAddress::operator!=(const SocketAddress &rhs) const {
+ return !(*this == rhs);
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/StreamFile.cpp b/contrib/llvm-project/lldb/source/Host/common/StreamFile.cpp
new file mode 100644
index 000000000000..099980a0993c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/StreamFile.cpp
@@ -0,0 +1,54 @@
+//===-- StreamFile.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/StreamFile.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+
+#include <cstdio>
+
+using namespace lldb;
+using namespace lldb_private;
+
+StreamFile::StreamFile(uint32_t flags, uint32_t addr_size, ByteOrder byte_order)
+ : Stream(flags, addr_size, byte_order) {
+ m_file_sp = std::make_shared<File>();
+}
+
+StreamFile::StreamFile(int fd, bool transfer_ownership) : Stream() {
+ m_file_sp = std::make_shared<NativeFile>(fd, File::eOpenOptionWriteOnly,
+ transfer_ownership);
+}
+
+StreamFile::StreamFile(FILE *fh, bool transfer_ownership) : Stream() {
+ m_file_sp = std::make_shared<NativeFile>(fh, transfer_ownership);
+}
+
+StreamFile::StreamFile(const char *path, File::OpenOptions options,
+ uint32_t permissions)
+ : Stream() {
+ auto file = FileSystem::Instance().Open(FileSpec(path), options, permissions);
+ if (file)
+ m_file_sp = std::move(file.get());
+ else {
+ // TODO refactor this so the error gets popagated up instead of logged here.
+ LLDB_LOG_ERROR(GetLog(LLDBLog::Host), file.takeError(),
+ "Cannot open {1}: {0}", path);
+ m_file_sp = std::make_shared<File>();
+ }
+}
+
+StreamFile::~StreamFile() = default;
+
+void StreamFile::Flush() { m_file_sp->Flush(); }
+
+size_t StreamFile::WriteImpl(const void *s, size_t length) {
+ m_file_sp->Write(s, length);
+ return length;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/TCPSocket.cpp b/contrib/llvm-project/lldb/source/Host/common/TCPSocket.cpp
new file mode 100644
index 000000000000..df4737216ed3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/TCPSocket.cpp
@@ -0,0 +1,326 @@
+//===-- TCPSocket.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
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(_MSC_VER)
+#define _WINSOCK_DEPRECATED_NO_WARNINGS
+#endif
+
+#include "lldb/Host/common/TCPSocket.h"
+
+#include "lldb/Host/Config.h"
+#include "lldb/Host/MainLoop.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+
+#include "llvm/Config/llvm-config.h"
+#include "llvm/Support/Errno.h"
+#include "llvm/Support/WindowsError.h"
+#include "llvm/Support/raw_ostream.h"
+
+#if LLDB_ENABLE_POSIX
+#include <arpa/inet.h>
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+#endif
+
+#if defined(_WIN32)
+#include <winsock2.h>
+#endif
+
+#ifdef _WIN32
+#define CLOSE_SOCKET closesocket
+typedef const char *set_socket_option_arg_type;
+#else
+#include <unistd.h>
+#define CLOSE_SOCKET ::close
+typedef const void *set_socket_option_arg_type;
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+
+static Status GetLastSocketError() {
+ std::error_code EC;
+#ifdef _WIN32
+ EC = llvm::mapWindowsError(WSAGetLastError());
+#else
+ EC = std::error_code(errno, std::generic_category());
+#endif
+ return EC;
+}
+
+static const int kType = SOCK_STREAM;
+
+TCPSocket::TCPSocket(bool should_close, bool child_processes_inherit)
+ : Socket(ProtocolTcp, should_close, child_processes_inherit) {}
+
+TCPSocket::TCPSocket(NativeSocket socket, const TCPSocket &listen_socket)
+ : Socket(ProtocolTcp, listen_socket.m_should_close_fd,
+ listen_socket.m_child_processes_inherit) {
+ m_socket = socket;
+}
+
+TCPSocket::TCPSocket(NativeSocket socket, bool should_close,
+ bool child_processes_inherit)
+ : Socket(ProtocolTcp, should_close, child_processes_inherit) {
+ m_socket = socket;
+}
+
+TCPSocket::~TCPSocket() { CloseListenSockets(); }
+
+bool TCPSocket::IsValid() const {
+ return m_socket != kInvalidSocketValue || m_listen_sockets.size() != 0;
+}
+
+// Return the port number that is being used by the socket.
+uint16_t TCPSocket::GetLocalPortNumber() const {
+ if (m_socket != kInvalidSocketValue) {
+ SocketAddress sock_addr;
+ socklen_t sock_addr_len = sock_addr.GetMaxLength();
+ if (::getsockname(m_socket, sock_addr, &sock_addr_len) == 0)
+ return sock_addr.GetPort();
+ } else if (!m_listen_sockets.empty()) {
+ SocketAddress sock_addr;
+ socklen_t sock_addr_len = sock_addr.GetMaxLength();
+ if (::getsockname(m_listen_sockets.begin()->first, sock_addr,
+ &sock_addr_len) == 0)
+ return sock_addr.GetPort();
+ }
+ return 0;
+}
+
+std::string TCPSocket::GetLocalIPAddress() const {
+ // We bound to port zero, so we need to figure out which port we actually
+ // bound to
+ if (m_socket != kInvalidSocketValue) {
+ SocketAddress sock_addr;
+ socklen_t sock_addr_len = sock_addr.GetMaxLength();
+ if (::getsockname(m_socket, sock_addr, &sock_addr_len) == 0)
+ return sock_addr.GetIPAddress();
+ }
+ return "";
+}
+
+uint16_t TCPSocket::GetRemotePortNumber() const {
+ if (m_socket != kInvalidSocketValue) {
+ SocketAddress sock_addr;
+ socklen_t sock_addr_len = sock_addr.GetMaxLength();
+ if (::getpeername(m_socket, sock_addr, &sock_addr_len) == 0)
+ return sock_addr.GetPort();
+ }
+ return 0;
+}
+
+std::string TCPSocket::GetRemoteIPAddress() const {
+ // We bound to port zero, so we need to figure out which port we actually
+ // bound to
+ if (m_socket != kInvalidSocketValue) {
+ SocketAddress sock_addr;
+ socklen_t sock_addr_len = sock_addr.GetMaxLength();
+ if (::getpeername(m_socket, sock_addr, &sock_addr_len) == 0)
+ return sock_addr.GetIPAddress();
+ }
+ return "";
+}
+
+std::string TCPSocket::GetRemoteConnectionURI() const {
+ if (m_socket != kInvalidSocketValue) {
+ return std::string(llvm::formatv(
+ "connect://[{0}]:{1}", GetRemoteIPAddress(), GetRemotePortNumber()));
+ }
+ return "";
+}
+
+Status TCPSocket::CreateSocket(int domain) {
+ Status error;
+ if (IsValid())
+ error = Close();
+ if (error.Fail())
+ return error;
+ m_socket = Socket::CreateSocket(domain, kType, IPPROTO_TCP,
+ m_child_processes_inherit, error);
+ return error;
+}
+
+Status TCPSocket::Connect(llvm::StringRef name) {
+
+ Log *log = GetLog(LLDBLog::Communication);
+ LLDB_LOG(log, "Connect to host/port {0}", name);
+
+ Status error;
+ llvm::Expected<HostAndPort> host_port = DecodeHostAndPort(name);
+ if (!host_port)
+ return Status(host_port.takeError());
+
+ std::vector<SocketAddress> addresses =
+ SocketAddress::GetAddressInfo(host_port->hostname.c_str(), nullptr,
+ AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP);
+ for (SocketAddress &address : addresses) {
+ error = CreateSocket(address.GetFamily());
+ if (error.Fail())
+ continue;
+
+ address.SetPort(host_port->port);
+
+ if (llvm::sys::RetryAfterSignal(-1, ::connect, GetNativeSocket(),
+ &address.sockaddr(),
+ address.GetLength()) == -1) {
+ Close();
+ continue;
+ }
+
+ if (SetOptionNoDelay() == -1) {
+ Close();
+ continue;
+ }
+
+ error.Clear();
+ return error;
+ }
+
+ error.SetErrorString("Failed to connect port");
+ return error;
+}
+
+Status TCPSocket::Listen(llvm::StringRef name, int backlog) {
+ Log *log = GetLog(LLDBLog::Connection);
+ LLDB_LOG(log, "Listen to {0}", name);
+
+ Status error;
+ llvm::Expected<HostAndPort> host_port = DecodeHostAndPort(name);
+ if (!host_port)
+ return Status(host_port.takeError());
+
+ if (host_port->hostname == "*")
+ host_port->hostname = "0.0.0.0";
+ std::vector<SocketAddress> addresses = SocketAddress::GetAddressInfo(
+ host_port->hostname.c_str(), nullptr, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP);
+ for (SocketAddress &address : addresses) {
+ int fd = Socket::CreateSocket(address.GetFamily(), kType, IPPROTO_TCP,
+ m_child_processes_inherit, error);
+ if (error.Fail() || fd < 0)
+ continue;
+
+ // enable local address reuse
+ int option_value = 1;
+ set_socket_option_arg_type option_value_p =
+ reinterpret_cast<set_socket_option_arg_type>(&option_value);
+ if (::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, option_value_p,
+ sizeof(option_value)) == -1) {
+ CLOSE_SOCKET(fd);
+ continue;
+ }
+
+ SocketAddress listen_address = address;
+ if(!listen_address.IsLocalhost())
+ listen_address.SetToAnyAddress(address.GetFamily(), host_port->port);
+ else
+ listen_address.SetPort(host_port->port);
+
+ int err =
+ ::bind(fd, &listen_address.sockaddr(), listen_address.GetLength());
+ if (err != -1)
+ err = ::listen(fd, backlog);
+
+ if (err == -1) {
+ error = GetLastSocketError();
+ CLOSE_SOCKET(fd);
+ continue;
+ }
+
+ if (host_port->port == 0) {
+ socklen_t sa_len = address.GetLength();
+ if (getsockname(fd, &address.sockaddr(), &sa_len) == 0)
+ host_port->port = address.GetPort();
+ }
+ m_listen_sockets[fd] = address;
+ }
+
+ if (m_listen_sockets.empty()) {
+ assert(error.Fail());
+ return error;
+ }
+ return Status();
+}
+
+void TCPSocket::CloseListenSockets() {
+ for (auto socket : m_listen_sockets)
+ CLOSE_SOCKET(socket.first);
+ m_listen_sockets.clear();
+}
+
+Status TCPSocket::Accept(Socket *&conn_socket) {
+ Status error;
+ if (m_listen_sockets.size() == 0) {
+ error.SetErrorString("No open listening sockets!");
+ return error;
+ }
+
+ NativeSocket sock = kInvalidSocketValue;
+ NativeSocket listen_sock = kInvalidSocketValue;
+ lldb_private::SocketAddress AcceptAddr;
+ MainLoop accept_loop;
+ std::vector<MainLoopBase::ReadHandleUP> handles;
+ for (auto socket : m_listen_sockets) {
+ auto fd = socket.first;
+ auto inherit = this->m_child_processes_inherit;
+ auto io_sp = IOObjectSP(new TCPSocket(socket.first, false, inherit));
+ handles.emplace_back(accept_loop.RegisterReadObject(
+ io_sp, [fd, inherit, &sock, &AcceptAddr, &error,
+ &listen_sock](MainLoopBase &loop) {
+ socklen_t sa_len = AcceptAddr.GetMaxLength();
+ sock = AcceptSocket(fd, &AcceptAddr.sockaddr(), &sa_len, inherit,
+ error);
+ listen_sock = fd;
+ loop.RequestTermination();
+ }, error));
+ if (error.Fail())
+ return error;
+ }
+
+ bool accept_connection = false;
+ std::unique_ptr<TCPSocket> accepted_socket;
+ // Loop until we are happy with our connection
+ while (!accept_connection) {
+ accept_loop.Run();
+
+ if (error.Fail())
+ return error;
+
+ lldb_private::SocketAddress &AddrIn = m_listen_sockets[listen_sock];
+ if (!AddrIn.IsAnyAddr() && AcceptAddr != AddrIn) {
+ if (sock != kInvalidSocketValue) {
+ CLOSE_SOCKET(sock);
+ sock = kInvalidSocketValue;
+ }
+ llvm::errs() << llvm::formatv(
+ "error: rejecting incoming connection from {0} (expecting {1})",
+ AcceptAddr.GetIPAddress(), AddrIn.GetIPAddress());
+ continue;
+ }
+ accept_connection = true;
+ accepted_socket.reset(new TCPSocket(sock, *this));
+ }
+
+ if (!accepted_socket)
+ return error;
+
+ // Keep our TCP packets coming without any delays.
+ accepted_socket->SetOptionNoDelay();
+ error.Clear();
+ conn_socket = accepted_socket.release();
+ return error;
+}
+
+int TCPSocket::SetOptionNoDelay() {
+ return SetOption(IPPROTO_TCP, TCP_NODELAY, 1);
+}
+
+int TCPSocket::SetOptionReuseAddress() {
+ return SetOption(SOL_SOCKET, SO_REUSEADDR, 1);
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/Terminal.cpp b/contrib/llvm-project/lldb/source/Host/common/Terminal.cpp
new file mode 100644
index 000000000000..436dfd8130d9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/Terminal.cpp
@@ -0,0 +1,474 @@
+//===-- Terminal.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/Terminal.h"
+
+#include "lldb/Host/Config.h"
+#include "lldb/Host/PosixApi.h"
+#include "llvm/ADT/STLExtras.h"
+
+#include <csignal>
+#include <fcntl.h>
+#include <optional>
+
+#if LLDB_ENABLE_TERMIOS
+#include <termios.h>
+#endif
+
+using namespace lldb_private;
+
+struct Terminal::Data {
+#if LLDB_ENABLE_TERMIOS
+ struct termios m_termios; ///< Cached terminal state information.
+#endif
+};
+
+bool Terminal::IsATerminal() const { return m_fd >= 0 && ::isatty(m_fd); }
+
+#if !LLDB_ENABLE_TERMIOS
+static llvm::Error termiosMissingError() {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "termios support missing in LLDB");
+}
+#endif
+
+llvm::Expected<Terminal::Data> Terminal::GetData() {
+#if LLDB_ENABLE_TERMIOS
+ if (!FileDescriptorIsValid())
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "invalid fd");
+
+ if (!IsATerminal())
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "fd not a terminal");
+
+ Data data;
+ if (::tcgetattr(m_fd, &data.m_termios) != 0)
+ return llvm::createStringError(
+ std::error_code(errno, std::generic_category()),
+ "unable to get teletype attributes");
+ return data;
+#else // !LLDB_ENABLE_TERMIOS
+ return termiosMissingError();
+#endif // LLDB_ENABLE_TERMIOS
+}
+
+llvm::Error Terminal::SetData(const Terminal::Data &data) {
+#if LLDB_ENABLE_TERMIOS
+ assert(FileDescriptorIsValid());
+ assert(IsATerminal());
+
+ if (::tcsetattr(m_fd, TCSANOW, &data.m_termios) != 0)
+ return llvm::createStringError(
+ std::error_code(errno, std::generic_category()),
+ "unable to set teletype attributes");
+ return llvm::Error::success();
+#else // !LLDB_ENABLE_TERMIOS
+ return termiosMissingError();
+#endif // LLDB_ENABLE_TERMIOS
+}
+
+llvm::Error Terminal::SetEcho(bool enabled) {
+#if LLDB_ENABLE_TERMIOS
+ llvm::Expected<Data> data = GetData();
+ if (!data)
+ return data.takeError();
+
+ struct termios &fd_termios = data->m_termios;
+ fd_termios.c_lflag &= ~ECHO;
+ if (enabled)
+ fd_termios.c_lflag |= ECHO;
+ return SetData(data.get());
+#else // !LLDB_ENABLE_TERMIOS
+ return termiosMissingError();
+#endif // LLDB_ENABLE_TERMIOS
+}
+
+llvm::Error Terminal::SetCanonical(bool enabled) {
+#if LLDB_ENABLE_TERMIOS
+ llvm::Expected<Data> data = GetData();
+ if (!data)
+ return data.takeError();
+
+ struct termios &fd_termios = data->m_termios;
+ fd_termios.c_lflag &= ~ICANON;
+ if (enabled)
+ fd_termios.c_lflag |= ICANON;
+ return SetData(data.get());
+#else // !LLDB_ENABLE_TERMIOS
+ return termiosMissingError();
+#endif // LLDB_ENABLE_TERMIOS
+}
+
+llvm::Error Terminal::SetRaw() {
+#if LLDB_ENABLE_TERMIOS
+ llvm::Expected<Data> data = GetData();
+ if (!data)
+ return data.takeError();
+
+ struct termios &fd_termios = data->m_termios;
+ ::cfmakeraw(&fd_termios);
+
+ // Make sure only one character is needed to return from a read
+ // (cfmakeraw() doesn't do this on NetBSD)
+ fd_termios.c_cc[VMIN] = 1;
+ fd_termios.c_cc[VTIME] = 0;
+
+ return SetData(data.get());
+#else // !LLDB_ENABLE_TERMIOS
+ return termiosMissingError();
+#endif // LLDB_ENABLE_TERMIOS
+}
+
+#if LLDB_ENABLE_TERMIOS
+static std::optional<speed_t> baudRateToConst(unsigned int baud_rate) {
+ switch (baud_rate) {
+#if defined(B50)
+ case 50:
+ return B50;
+#endif
+#if defined(B75)
+ case 75:
+ return B75;
+#endif
+#if defined(B110)
+ case 110:
+ return B110;
+#endif
+#if defined(B134)
+ case 134:
+ return B134;
+#endif
+#if defined(B150)
+ case 150:
+ return B150;
+#endif
+#if defined(B200)
+ case 200:
+ return B200;
+#endif
+#if defined(B300)
+ case 300:
+ return B300;
+#endif
+#if defined(B600)
+ case 600:
+ return B600;
+#endif
+#if defined(B1200)
+ case 1200:
+ return B1200;
+#endif
+#if defined(B1800)
+ case 1800:
+ return B1800;
+#endif
+#if defined(B2400)
+ case 2400:
+ return B2400;
+#endif
+#if defined(B4800)
+ case 4800:
+ return B4800;
+#endif
+#if defined(B9600)
+ case 9600:
+ return B9600;
+#endif
+#if defined(B19200)
+ case 19200:
+ return B19200;
+#endif
+#if defined(B38400)
+ case 38400:
+ return B38400;
+#endif
+#if defined(B57600)
+ case 57600:
+ return B57600;
+#endif
+#if defined(B115200)
+ case 115200:
+ return B115200;
+#endif
+#if defined(B230400)
+ case 230400:
+ return B230400;
+#endif
+#if defined(B460800)
+ case 460800:
+ return B460800;
+#endif
+#if defined(B500000)
+ case 500000:
+ return B500000;
+#endif
+#if defined(B576000)
+ case 576000:
+ return B576000;
+#endif
+#if defined(B921600)
+ case 921600:
+ return B921600;
+#endif
+#if defined(B1000000)
+ case 1000000:
+ return B1000000;
+#endif
+#if defined(B1152000)
+ case 1152000:
+ return B1152000;
+#endif
+#if defined(B1500000)
+ case 1500000:
+ return B1500000;
+#endif
+#if defined(B2000000)
+ case 2000000:
+ return B2000000;
+#endif
+#if defined(B76800)
+ case 76800:
+ return B76800;
+#endif
+#if defined(B153600)
+ case 153600:
+ return B153600;
+#endif
+#if defined(B307200)
+ case 307200:
+ return B307200;
+#endif
+#if defined(B614400)
+ case 614400:
+ return B614400;
+#endif
+#if defined(B2500000)
+ case 2500000:
+ return B2500000;
+#endif
+#if defined(B3000000)
+ case 3000000:
+ return B3000000;
+#endif
+#if defined(B3500000)
+ case 3500000:
+ return B3500000;
+#endif
+#if defined(B4000000)
+ case 4000000:
+ return B4000000;
+#endif
+ default:
+ return std::nullopt;
+ }
+}
+#endif
+
+llvm::Error Terminal::SetBaudRate(unsigned int baud_rate) {
+#if LLDB_ENABLE_TERMIOS
+ llvm::Expected<Data> data = GetData();
+ if (!data)
+ return data.takeError();
+
+ struct termios &fd_termios = data->m_termios;
+ std::optional<speed_t> val = baudRateToConst(baud_rate);
+ if (!val) // invalid value
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "baud rate %d unsupported by the platform",
+ baud_rate);
+ if (::cfsetispeed(&fd_termios, *val) != 0)
+ return llvm::createStringError(
+ std::error_code(errno, std::generic_category()),
+ "setting input baud rate failed");
+ if (::cfsetospeed(&fd_termios, *val) != 0)
+ return llvm::createStringError(
+ std::error_code(errno, std::generic_category()),
+ "setting output baud rate failed");
+ return SetData(data.get());
+#else // !LLDB_ENABLE_TERMIOS
+ return termiosMissingError();
+#endif // LLDB_ENABLE_TERMIOS
+}
+
+llvm::Error Terminal::SetStopBits(unsigned int stop_bits) {
+#if LLDB_ENABLE_TERMIOS
+ llvm::Expected<Data> data = GetData();
+ if (!data)
+ return data.takeError();
+
+ struct termios &fd_termios = data->m_termios;
+ switch (stop_bits) {
+ case 1:
+ fd_termios.c_cflag &= ~CSTOPB;
+ break;
+ case 2:
+ fd_termios.c_cflag |= CSTOPB;
+ break;
+ default:
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "invalid stop bit count: %d (must be 1 or 2)", stop_bits);
+ }
+ return SetData(data.get());
+#else // !LLDB_ENABLE_TERMIOS
+ return termiosMissingError();
+#endif // LLDB_ENABLE_TERMIOS
+}
+
+llvm::Error Terminal::SetParity(Terminal::Parity parity) {
+#if LLDB_ENABLE_TERMIOS
+ llvm::Expected<Data> data = GetData();
+ if (!data)
+ return data.takeError();
+
+ struct termios &fd_termios = data->m_termios;
+ fd_termios.c_cflag &= ~(
+#if defined(CMSPAR)
+ CMSPAR |
+#endif
+ PARENB | PARODD);
+
+ if (parity != Parity::No) {
+ fd_termios.c_cflag |= PARENB;
+ if (parity == Parity::Odd || parity == Parity::Mark)
+ fd_termios.c_cflag |= PARODD;
+ if (parity == Parity::Mark || parity == Parity::Space) {
+#if defined(CMSPAR)
+ fd_termios.c_cflag |= CMSPAR;
+#else
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "space/mark parity is not supported by the platform");
+#endif
+ }
+ }
+ return SetData(data.get());
+#else // !LLDB_ENABLE_TERMIOS
+ return termiosMissingError();
+#endif // LLDB_ENABLE_TERMIOS
+}
+
+llvm::Error Terminal::SetParityCheck(Terminal::ParityCheck parity_check) {
+#if LLDB_ENABLE_TERMIOS
+ llvm::Expected<Data> data = GetData();
+ if (!data)
+ return data.takeError();
+
+ struct termios &fd_termios = data->m_termios;
+ fd_termios.c_iflag &= ~(IGNPAR | PARMRK | INPCK);
+
+ if (parity_check != ParityCheck::No) {
+ fd_termios.c_iflag |= INPCK;
+ if (parity_check == ParityCheck::Ignore)
+ fd_termios.c_iflag |= IGNPAR;
+ else if (parity_check == ParityCheck::Mark)
+ fd_termios.c_iflag |= PARMRK;
+ }
+ return SetData(data.get());
+#else // !LLDB_ENABLE_TERMIOS
+ return termiosMissingError();
+#endif // LLDB_ENABLE_TERMIOS
+}
+
+llvm::Error Terminal::SetHardwareFlowControl(bool enabled) {
+#if LLDB_ENABLE_TERMIOS
+ llvm::Expected<Data> data = GetData();
+ if (!data)
+ return data.takeError();
+
+#if defined(CRTSCTS)
+ struct termios &fd_termios = data->m_termios;
+ fd_termios.c_cflag &= ~CRTSCTS;
+ if (enabled)
+ fd_termios.c_cflag |= CRTSCTS;
+ return SetData(data.get());
+#else // !defined(CRTSCTS)
+ if (enabled)
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "hardware flow control is not supported by the platform");
+ return llvm::Error::success();
+#endif // defined(CRTSCTS)
+#else // !LLDB_ENABLE_TERMIOS
+ return termiosMissingError();
+#endif // LLDB_ENABLE_TERMIOS
+}
+
+TerminalState::TerminalState(Terminal term, bool save_process_group)
+ : m_tty(term) {
+ Save(term, save_process_group);
+}
+
+TerminalState::~TerminalState() { Restore(); }
+
+void TerminalState::Clear() {
+ m_tty.Clear();
+ m_tflags = -1;
+ m_data.reset();
+ m_process_group = -1;
+}
+
+bool TerminalState::Save(Terminal term, bool save_process_group) {
+ Clear();
+ m_tty = term;
+ if (m_tty.IsATerminal()) {
+#if LLDB_ENABLE_POSIX
+ int fd = m_tty.GetFileDescriptor();
+ m_tflags = ::fcntl(fd, F_GETFL, 0);
+#if LLDB_ENABLE_TERMIOS
+ std::unique_ptr<Terminal::Data> new_data{new Terminal::Data()};
+ if (::tcgetattr(fd, &new_data->m_termios) == 0)
+ m_data = std::move(new_data);
+#endif // LLDB_ENABLE_TERMIOS
+ if (save_process_group)
+ m_process_group = ::tcgetpgrp(fd);
+#endif // LLDB_ENABLE_POSIX
+ }
+ return IsValid();
+}
+
+bool TerminalState::Restore() const {
+#if LLDB_ENABLE_POSIX
+ if (IsValid()) {
+ const int fd = m_tty.GetFileDescriptor();
+ if (TFlagsIsValid())
+ fcntl(fd, F_SETFL, m_tflags);
+
+#if LLDB_ENABLE_TERMIOS
+ if (TTYStateIsValid())
+ tcsetattr(fd, TCSANOW, &m_data->m_termios);
+#endif // LLDB_ENABLE_TERMIOS
+
+ if (ProcessGroupIsValid()) {
+ // Save the original signal handler.
+ void (*saved_sigttou_callback)(int) = nullptr;
+ saved_sigttou_callback = (void (*)(int))signal(SIGTTOU, SIG_IGN);
+ // Set the process group
+ tcsetpgrp(fd, m_process_group);
+ // Restore the original signal handler.
+ signal(SIGTTOU, saved_sigttou_callback);
+ }
+ return true;
+ }
+#endif // LLDB_ENABLE_POSIX
+ return false;
+}
+
+bool TerminalState::IsValid() const {
+ return m_tty.FileDescriptorIsValid() &&
+ (TFlagsIsValid() || TTYStateIsValid() || ProcessGroupIsValid());
+}
+
+bool TerminalState::TFlagsIsValid() const { return m_tflags != -1; }
+
+bool TerminalState::TTYStateIsValid() const { return bool(m_data); }
+
+bool TerminalState::ProcessGroupIsValid() const {
+ return static_cast<int32_t>(m_process_group) != -1;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/ThreadLauncher.cpp b/contrib/llvm-project/lldb/source/Host/common/ThreadLauncher.cpp
new file mode 100644
index 000000000000..28c90215f874
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/ThreadLauncher.cpp
@@ -0,0 +1,79 @@
+//===-- ThreadLauncher.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
+//
+//===----------------------------------------------------------------------===//
+
+// lldb Includes
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Host/HostNativeThread.h"
+#include "lldb/Host/HostThread.h"
+#include "lldb/Utility/Log.h"
+
+#if defined(_WIN32)
+#include "lldb/Host/windows/windows.h"
+#endif
+
+#include "llvm/Support/WindowsError.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+llvm::Expected<HostThread>
+ThreadLauncher::LaunchThread(llvm::StringRef name,
+ std::function<thread_result_t()> impl,
+ size_t min_stack_byte_size) {
+ // Host::ThreadCreateTrampoline will take ownership if thread creation is
+ // successful.
+ auto info_up = std::make_unique<HostThreadCreateInfo>(name.str(), impl);
+ lldb::thread_t thread;
+#ifdef _WIN32
+ thread = (lldb::thread_t)::_beginthreadex(
+ 0, (unsigned)min_stack_byte_size,
+ HostNativeThread::ThreadCreateTrampoline, info_up.get(), 0, NULL);
+ if (thread == LLDB_INVALID_HOST_THREAD)
+ return llvm::errorCodeToError(llvm::mapWindowsError(GetLastError()));
+#else
+
+// ASAN instrumentation adds a lot of bookkeeping overhead on stack frames.
+#if __has_feature(address_sanitizer)
+ const size_t eight_megabytes = 8 * 1024 * 1024;
+ if (min_stack_byte_size < eight_megabytes) {
+ min_stack_byte_size += eight_megabytes;
+ }
+#endif
+
+ pthread_attr_t *thread_attr_ptr = nullptr;
+ pthread_attr_t thread_attr;
+ bool destroy_attr = false;
+ if (min_stack_byte_size > 0) {
+ if (::pthread_attr_init(&thread_attr) == 0) {
+ destroy_attr = true;
+ size_t default_min_stack_byte_size = 0;
+ if (::pthread_attr_getstacksize(&thread_attr,
+ &default_min_stack_byte_size) == 0) {
+ if (default_min_stack_byte_size < min_stack_byte_size) {
+ if (::pthread_attr_setstacksize(&thread_attr, min_stack_byte_size) ==
+ 0)
+ thread_attr_ptr = &thread_attr;
+ }
+ }
+ }
+ }
+ int err =
+ ::pthread_create(&thread, thread_attr_ptr,
+ HostNativeThread::ThreadCreateTrampoline, info_up.get());
+
+ if (destroy_attr)
+ ::pthread_attr_destroy(&thread_attr);
+
+ if (err)
+ return llvm::errorCodeToError(
+ std::error_code(err, std::generic_category()));
+#endif
+
+ info_up.release();
+ return HostThread(thread);
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/UDPSocket.cpp b/contrib/llvm-project/lldb/source/Host/common/UDPSocket.cpp
new file mode 100644
index 000000000000..e40066d519c6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/UDPSocket.cpp
@@ -0,0 +1,138 @@
+//===-- UDPSocket.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/common/UDPSocket.h"
+
+#include "lldb/Host/Config.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+
+#if LLDB_ENABLE_POSIX
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#endif
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+static const int kDomain = AF_INET;
+static const int kType = SOCK_DGRAM;
+
+static const char *g_not_supported_error = "Not supported";
+
+UDPSocket::UDPSocket(NativeSocket socket) : Socket(ProtocolUdp, true, true) {
+ m_socket = socket;
+}
+
+UDPSocket::UDPSocket(bool should_close, bool child_processes_inherit)
+ : Socket(ProtocolUdp, should_close, child_processes_inherit) {}
+
+size_t UDPSocket::Send(const void *buf, const size_t num_bytes) {
+ return ::sendto(m_socket, static_cast<const char *>(buf), num_bytes, 0,
+ m_sockaddr, m_sockaddr.GetLength());
+}
+
+Status UDPSocket::Connect(llvm::StringRef name) {
+ return Status("%s", g_not_supported_error);
+}
+
+Status UDPSocket::Listen(llvm::StringRef name, int backlog) {
+ return Status("%s", g_not_supported_error);
+}
+
+Status UDPSocket::Accept(Socket *&socket) {
+ return Status("%s", g_not_supported_error);
+}
+
+llvm::Expected<std::unique_ptr<UDPSocket>>
+UDPSocket::Connect(llvm::StringRef name, bool child_processes_inherit) {
+ std::unique_ptr<UDPSocket> socket;
+
+ Log *log = GetLog(LLDBLog::Connection);
+ LLDB_LOG(log, "host/port = {0}", name);
+
+ Status error;
+ llvm::Expected<HostAndPort> host_port = DecodeHostAndPort(name);
+ if (!host_port)
+ return host_port.takeError();
+
+ // At this point we have setup the receive port, now we need to setup the UDP
+ // send socket
+
+ struct addrinfo hints;
+ struct addrinfo *service_info_list = nullptr;
+
+ ::memset(&hints, 0, sizeof(hints));
+ hints.ai_family = kDomain;
+ hints.ai_socktype = kType;
+ int err = ::getaddrinfo(host_port->hostname.c_str(), std::to_string(host_port->port).c_str(), &hints,
+ &service_info_list);
+ if (err != 0) {
+ error.SetErrorStringWithFormat(
+#if defined(_WIN32) && defined(UNICODE)
+ "getaddrinfo(%s, %d, &hints, &info) returned error %i (%S)",
+#else
+ "getaddrinfo(%s, %d, &hints, &info) returned error %i (%s)",
+#endif
+ host_port->hostname.c_str(), host_port->port, err, gai_strerror(err));
+ return error.ToError();
+ }
+
+ for (struct addrinfo *service_info_ptr = service_info_list;
+ service_info_ptr != nullptr;
+ service_info_ptr = service_info_ptr->ai_next) {
+ auto send_fd = CreateSocket(
+ service_info_ptr->ai_family, service_info_ptr->ai_socktype,
+ service_info_ptr->ai_protocol, child_processes_inherit, error);
+ if (error.Success()) {
+ socket.reset(new UDPSocket(send_fd));
+ socket->m_sockaddr = service_info_ptr;
+ break;
+ } else
+ continue;
+ }
+
+ ::freeaddrinfo(service_info_list);
+
+ if (!socket)
+ return error.ToError();
+
+ SocketAddress bind_addr;
+
+ // Only bind to the loopback address if we are expecting a connection from
+ // localhost to avoid any firewall issues.
+ const bool bind_addr_success = (host_port->hostname == "127.0.0.1" || host_port->hostname == "localhost")
+ ? bind_addr.SetToLocalhost(kDomain, host_port->port)
+ : bind_addr.SetToAnyAddress(kDomain, host_port->port);
+
+ if (!bind_addr_success) {
+ error.SetErrorString("Failed to get hostspec to bind for");
+ return error.ToError();
+ }
+
+ bind_addr.SetPort(0); // Let the source port # be determined dynamically
+
+ err = ::bind(socket->GetNativeSocket(), bind_addr, bind_addr.GetLength());
+
+ struct sockaddr_in source_info;
+ socklen_t address_len = sizeof (struct sockaddr_in);
+ err = ::getsockname(socket->GetNativeSocket(),
+ (struct sockaddr *)&source_info, &address_len);
+
+ return std::move(socket);
+}
+
+std::string UDPSocket::GetRemoteConnectionURI() const {
+ if (m_socket != kInvalidSocketValue) {
+ return std::string(llvm::formatv(
+ "udp://[{0}]:{1}", m_sockaddr.GetIPAddress(), m_sockaddr.GetPort()));
+ }
+ return "";
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/XML.cpp b/contrib/llvm-project/lldb/source/Host/common/XML.cpp
new file mode 100644
index 000000000000..f480ef3166a4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/XML.cpp
@@ -0,0 +1,519 @@
+//===-- XML.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/Config.h"
+#include "lldb/Host/XML.h"
+
+#include "llvm/ADT/StringExtras.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#pragma mark-- XMLDocument
+
+XMLDocument::XMLDocument() = default;
+
+XMLDocument::~XMLDocument() { Clear(); }
+
+void XMLDocument::Clear() {
+#if LLDB_ENABLE_LIBXML2
+ if (m_document) {
+ xmlDocPtr doc = m_document;
+ m_document = nullptr;
+ xmlFreeDoc(doc);
+ }
+#endif
+}
+
+bool XMLDocument::IsValid() const { return m_document != nullptr; }
+
+void XMLDocument::ErrorCallback(void *ctx, const char *format, ...) {
+ XMLDocument *document = (XMLDocument *)ctx;
+ va_list args;
+ va_start(args, format);
+ document->m_errors.PrintfVarArg(format, args);
+ document->m_errors.EOL();
+ va_end(args);
+}
+
+bool XMLDocument::ParseFile(const char *path) {
+#if LLDB_ENABLE_LIBXML2
+ Clear();
+ xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback);
+ m_document = xmlParseFile(path);
+ xmlSetGenericErrorFunc(nullptr, nullptr);
+#endif
+ return IsValid();
+}
+
+bool XMLDocument::ParseMemory(const char *xml, size_t xml_length,
+ const char *url) {
+#if LLDB_ENABLE_LIBXML2
+ Clear();
+ xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback);
+ m_document = xmlReadMemory(xml, (int)xml_length, url, nullptr, 0);
+ xmlSetGenericErrorFunc(nullptr, nullptr);
+#endif
+ return IsValid();
+}
+
+XMLNode XMLDocument::GetRootElement(const char *required_name) {
+#if LLDB_ENABLE_LIBXML2
+ if (IsValid()) {
+ XMLNode root_node(xmlDocGetRootElement(m_document));
+ if (required_name) {
+ llvm::StringRef actual_name = root_node.GetName();
+ if (actual_name == required_name)
+ return root_node;
+ } else {
+ return root_node;
+ }
+ }
+#endif
+ return XMLNode();
+}
+
+llvm::StringRef XMLDocument::GetErrors() const { return m_errors.GetString(); }
+
+bool XMLDocument::XMLEnabled() {
+#if LLDB_ENABLE_LIBXML2
+ return true;
+#else
+ return false;
+#endif
+}
+
+#pragma mark-- XMLNode
+
+XMLNode::XMLNode() = default;
+
+XMLNode::XMLNode(XMLNodeImpl node) : m_node(node) {}
+
+XMLNode::~XMLNode() = default;
+
+void XMLNode::Clear() { m_node = nullptr; }
+
+XMLNode XMLNode::GetParent() const {
+#if LLDB_ENABLE_LIBXML2
+ if (IsValid())
+ return XMLNode(m_node->parent);
+ else
+ return XMLNode();
+#else
+ return XMLNode();
+#endif
+}
+
+XMLNode XMLNode::GetSibling() const {
+#if LLDB_ENABLE_LIBXML2
+ if (IsValid())
+ return XMLNode(m_node->next);
+ else
+ return XMLNode();
+#else
+ return XMLNode();
+#endif
+}
+
+XMLNode XMLNode::GetChild() const {
+#if LLDB_ENABLE_LIBXML2
+
+ if (IsValid())
+ return XMLNode(m_node->children);
+ else
+ return XMLNode();
+#else
+ return XMLNode();
+#endif
+}
+
+std::string XMLNode::GetAttributeValue(const char *name,
+ const char *fail_value) const {
+ std::string attr_value;
+#if LLDB_ENABLE_LIBXML2
+ if (IsValid()) {
+ xmlChar *value = xmlGetProp(m_node, (const xmlChar *)name);
+ if (value) {
+ attr_value = (const char *)value;
+ xmlFree(value);
+ }
+ } else {
+ if (fail_value)
+ attr_value = fail_value;
+ }
+#else
+ if (fail_value)
+ attr_value = fail_value;
+#endif
+ return attr_value;
+}
+
+bool XMLNode::GetAttributeValueAsUnsigned(const char *name, uint64_t &value,
+ uint64_t fail_value, int base) const {
+ value = fail_value;
+ return llvm::to_integer(GetAttributeValue(name, ""), value, base);
+}
+
+void XMLNode::ForEachChildNode(NodeCallback const &callback) const {
+#if LLDB_ENABLE_LIBXML2
+ if (IsValid())
+ GetChild().ForEachSiblingNode(callback);
+#endif
+}
+
+void XMLNode::ForEachChildElement(NodeCallback const &callback) const {
+#if LLDB_ENABLE_LIBXML2
+ XMLNode child = GetChild();
+ if (child)
+ child.ForEachSiblingElement(callback);
+#endif
+}
+
+void XMLNode::ForEachChildElementWithName(const char *name,
+ NodeCallback const &callback) const {
+#if LLDB_ENABLE_LIBXML2
+ XMLNode child = GetChild();
+ if (child)
+ child.ForEachSiblingElementWithName(name, callback);
+#endif
+}
+
+void XMLNode::ForEachAttribute(AttributeCallback const &callback) const {
+#if LLDB_ENABLE_LIBXML2
+
+ if (IsValid()) {
+ for (xmlAttrPtr attr = m_node->properties; attr != nullptr;
+ attr = attr->next) {
+ // check if name matches
+ if (attr->name) {
+ // check child is a text node
+ xmlNodePtr child = attr->children;
+ if (child->type == XML_TEXT_NODE) {
+ llvm::StringRef attr_value;
+ if (child->content)
+ attr_value = llvm::StringRef((const char *)child->content);
+ if (!callback(llvm::StringRef((const char *)attr->name), attr_value))
+ return;
+ }
+ }
+ }
+ }
+#endif
+}
+
+void XMLNode::ForEachSiblingNode(NodeCallback const &callback) const {
+#if LLDB_ENABLE_LIBXML2
+
+ if (IsValid()) {
+ // iterate through all siblings
+ for (xmlNodePtr node = m_node; node; node = node->next) {
+ if (!callback(XMLNode(node)))
+ return;
+ }
+ }
+#endif
+}
+
+void XMLNode::ForEachSiblingElement(NodeCallback const &callback) const {
+#if LLDB_ENABLE_LIBXML2
+
+ if (IsValid()) {
+ // iterate through all siblings
+ for (xmlNodePtr node = m_node; node; node = node->next) {
+ // we are looking for element nodes only
+ if (node->type != XML_ELEMENT_NODE)
+ continue;
+
+ if (!callback(XMLNode(node)))
+ return;
+ }
+ }
+#endif
+}
+
+void XMLNode::ForEachSiblingElementWithName(
+ const char *name, NodeCallback const &callback) const {
+#if LLDB_ENABLE_LIBXML2
+
+ if (IsValid()) {
+ // iterate through all siblings
+ for (xmlNodePtr node = m_node; node; node = node->next) {
+ // we are looking for element nodes only
+ if (node->type != XML_ELEMENT_NODE)
+ continue;
+
+ // If name is nullptr, we take all nodes of type "t", else just the ones
+ // whose name matches
+ if (name) {
+ if (strcmp((const char *)node->name, name) != 0)
+ continue; // Name mismatch, ignore this one
+ } else {
+ if (node->name)
+ continue; // nullptr name specified and this element has a name,
+ // ignore this one
+ }
+
+ if (!callback(XMLNode(node)))
+ return;
+ }
+ }
+#endif
+}
+
+llvm::StringRef XMLNode::GetName() const {
+#if LLDB_ENABLE_LIBXML2
+ if (IsValid()) {
+ if (m_node->name)
+ return llvm::StringRef((const char *)m_node->name);
+ }
+#endif
+ return llvm::StringRef();
+}
+
+bool XMLNode::GetElementText(std::string &text) const {
+ text.clear();
+#if LLDB_ENABLE_LIBXML2
+ if (IsValid()) {
+ bool success = false;
+ if (m_node->type == XML_ELEMENT_NODE) {
+ // check child is a text node
+ for (xmlNodePtr node = m_node->children; node != nullptr;
+ node = node->next) {
+ if (node->type == XML_TEXT_NODE) {
+ text.append((const char *)node->content);
+ success = true;
+ }
+ }
+ }
+ return success;
+ }
+#endif
+ return false;
+}
+
+bool XMLNode::GetElementTextAsUnsigned(uint64_t &value, uint64_t fail_value,
+ int base) const {
+ std::string text;
+
+ value = fail_value;
+ return GetElementText(text) && llvm::to_integer(text, value, base);
+}
+
+bool XMLNode::GetElementTextAsFloat(double &value, double fail_value) const {
+ std::string text;
+
+ value = fail_value;
+ return GetElementText(text) && llvm::to_float(text, value);
+}
+
+bool XMLNode::NameIs(const char *name) const {
+#if LLDB_ENABLE_LIBXML2
+
+ if (IsValid()) {
+ // In case we are looking for a nullptr name or an exact pointer match
+ if (m_node->name == (const xmlChar *)name)
+ return true;
+ if (m_node->name)
+ return strcmp((const char *)m_node->name, name) == 0;
+ }
+#endif
+ return false;
+}
+
+XMLNode XMLNode::FindFirstChildElementWithName(const char *name) const {
+ XMLNode result_node;
+
+#if LLDB_ENABLE_LIBXML2
+ ForEachChildElementWithName(
+ name, [&result_node](const XMLNode &node) -> bool {
+ result_node = node;
+ // Stop iterating, we found the node we wanted
+ return false;
+ });
+#endif
+
+ return result_node;
+}
+
+bool XMLNode::IsValid() const { return m_node != nullptr; }
+
+bool XMLNode::IsElement() const {
+#if LLDB_ENABLE_LIBXML2
+ if (IsValid())
+ return m_node->type == XML_ELEMENT_NODE;
+#endif
+ return false;
+}
+
+XMLNode XMLNode::GetElementForPath(const NamePath &path) {
+#if LLDB_ENABLE_LIBXML2
+
+ if (IsValid()) {
+ if (path.empty())
+ return *this;
+ else {
+ XMLNode node = FindFirstChildElementWithName(path[0].c_str());
+ const size_t n = path.size();
+ for (size_t i = 1; node && i < n; ++i)
+ node = node.FindFirstChildElementWithName(path[i].c_str());
+ return node;
+ }
+ }
+#endif
+
+ return XMLNode();
+}
+
+#pragma mark-- ApplePropertyList
+
+ApplePropertyList::ApplePropertyList() : m_xml_doc(), m_dict_node() {}
+
+ApplePropertyList::ApplePropertyList(const char *path)
+ : m_xml_doc(), m_dict_node() {
+ ParseFile(path);
+}
+
+ApplePropertyList::~ApplePropertyList() = default;
+
+llvm::StringRef ApplePropertyList::GetErrors() const {
+ return m_xml_doc.GetErrors();
+}
+
+bool ApplePropertyList::ParseFile(const char *path) {
+ if (m_xml_doc.ParseFile(path)) {
+ XMLNode plist = m_xml_doc.GetRootElement("plist");
+ if (plist) {
+ plist.ForEachChildElementWithName("dict",
+ [this](const XMLNode &dict) -> bool {
+ this->m_dict_node = dict;
+ return false; // Stop iterating
+ });
+ return (bool)m_dict_node;
+ }
+ }
+ return false;
+}
+
+bool ApplePropertyList::IsValid() const { return (bool)m_dict_node; }
+
+bool ApplePropertyList::GetValueAsString(const char *key,
+ std::string &value) const {
+ XMLNode value_node = GetValueNode(key);
+ if (value_node)
+ return ApplePropertyList::ExtractStringFromValueNode(value_node, value);
+ return false;
+}
+
+XMLNode ApplePropertyList::GetValueNode(const char *key) const {
+ XMLNode value_node;
+#if LLDB_ENABLE_LIBXML2
+
+ if (IsValid()) {
+ m_dict_node.ForEachChildElementWithName(
+ "key", [key, &value_node](const XMLNode &key_node) -> bool {
+ std::string key_name;
+ if (key_node.GetElementText(key_name)) {
+ if (key_name == key) {
+ value_node = key_node.GetSibling();
+ while (value_node && !value_node.IsElement())
+ value_node = value_node.GetSibling();
+ return false; // Stop iterating
+ }
+ }
+ return true; // Keep iterating
+ });
+ }
+#endif
+ return value_node;
+}
+
+bool ApplePropertyList::ExtractStringFromValueNode(const XMLNode &node,
+ std::string &value) {
+ value.clear();
+#if LLDB_ENABLE_LIBXML2
+ if (node.IsValid()) {
+ llvm::StringRef element_name = node.GetName();
+ if (element_name == "true" || element_name == "false") {
+ // The text value _is_ the element name itself...
+ value = element_name.str();
+ return true;
+ } else if (element_name == "dict" || element_name == "array")
+ return false; // dictionaries and arrays have no text value, so we fail
+ else
+ return node.GetElementText(value);
+ }
+#endif
+ return false;
+}
+
+#if LLDB_ENABLE_LIBXML2
+
+static StructuredData::ObjectSP CreatePlistValue(XMLNode node) {
+ llvm::StringRef element_name = node.GetName();
+ if (element_name == "array") {
+ std::shared_ptr<StructuredData::Array> array_sp(
+ new StructuredData::Array());
+ node.ForEachChildElement([&array_sp](const XMLNode &node) -> bool {
+ array_sp->AddItem(CreatePlistValue(node));
+ return true; // Keep iterating through all child elements of the array
+ });
+ return array_sp;
+ } else if (element_name == "dict") {
+ XMLNode key_node;
+ std::shared_ptr<StructuredData::Dictionary> dict_sp(
+ new StructuredData::Dictionary());
+ node.ForEachChildElement(
+ [&key_node, &dict_sp](const XMLNode &node) -> bool {
+ if (node.NameIs("key")) {
+ // This is a "key" element node
+ key_node = node;
+ } else {
+ // This is a value node
+ if (key_node) {
+ std::string key_name;
+ key_node.GetElementText(key_name);
+ dict_sp->AddItem(key_name, CreatePlistValue(node));
+ key_node.Clear();
+ }
+ }
+ return true; // Keep iterating through all child elements of the
+ // dictionary
+ });
+ return dict_sp;
+ } else if (element_name == "real") {
+ double value = 0.0;
+ node.GetElementTextAsFloat(value);
+ return StructuredData::ObjectSP(new StructuredData::Float(value));
+ } else if (element_name == "integer") {
+ uint64_t value = 0;
+ node.GetElementTextAsUnsigned(value, 0, 0);
+ return StructuredData::ObjectSP(new StructuredData::UnsignedInteger(value));
+ } else if ((element_name == "string") || (element_name == "data") ||
+ (element_name == "date")) {
+ std::string text;
+ node.GetElementText(text);
+ return StructuredData::ObjectSP(
+ new StructuredData::String(std::move(text)));
+ } else if (element_name == "true") {
+ return StructuredData::ObjectSP(new StructuredData::Boolean(true));
+ } else if (element_name == "false") {
+ return StructuredData::ObjectSP(new StructuredData::Boolean(false));
+ }
+ return StructuredData::ObjectSP(new StructuredData::Null());
+}
+#endif
+
+StructuredData::ObjectSP ApplePropertyList::GetStructuredData() {
+ StructuredData::ObjectSP root_sp;
+#if LLDB_ENABLE_LIBXML2
+ if (IsValid()) {
+ return CreatePlistValue(m_dict_node);
+ }
+#endif
+ return root_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/ZipFileResolver.cpp b/contrib/llvm-project/lldb/source/Host/common/ZipFileResolver.cpp
new file mode 100644
index 000000000000..f70ccb79d089
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/ZipFileResolver.cpp
@@ -0,0 +1,72 @@
+//===-- ZipFileResolver.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/common/ZipFileResolver.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Utility/DataBuffer.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/ZipFile.h"
+
+using namespace lldb_private;
+using namespace llvm::support;
+
+bool ZipFileResolver::ResolveSharedLibraryPath(const FileSpec &file_spec,
+ FileKind &file_kind,
+ std::string &file_path,
+ lldb::offset_t &so_file_offset,
+ lldb::offset_t &so_file_size) {
+ // When bionic loads .so file from APK or zip file, this file_spec will be
+ // "zip_path!/so_path". Otherwise it is just a normal file path.
+ static constexpr llvm::StringLiteral k_zip_separator("!/");
+ std::string path(file_spec.GetPath());
+ size_t pos = path.find(k_zip_separator);
+
+#if defined(_WIN32)
+ // When the file_spec is resolved as a Windows path, the zip .so path will be
+ // "zip_path!\so_path". Support both patterns on Windows.
+ static constexpr llvm::StringLiteral k_zip_separator_win("!\\");
+ if (pos == std::string::npos)
+ pos = path.find(k_zip_separator_win);
+#endif
+
+ if (pos == std::string::npos) {
+ // This file_spec does not contain the zip separator.
+ // Treat this file_spec as a normal file.
+ // so_file_offset and so_file_size should be 0.
+ file_kind = FileKind::eFileKindNormal;
+ file_path = path;
+ so_file_offset = 0;
+ so_file_size = 0;
+ return true;
+ }
+
+ // This file_spec is a zip .so path. Extract the zip path and the .so path.
+ std::string zip_path(path.substr(0, pos));
+ std::string so_path(path.substr(pos + k_zip_separator.size()));
+
+#if defined(_WIN32)
+ // Replace the .so path to use POSIX file separator for file searching inside
+ // the zip file.
+ std::replace(so_path.begin(), so_path.end(), '\\', '/');
+#endif
+
+ // Try to find the .so file from the zip file.
+ FileSpec zip_file_spec(zip_path);
+ uint64_t zip_file_size = FileSystem::Instance().GetByteSize(zip_file_spec);
+ lldb::DataBufferSP zip_data =
+ FileSystem::Instance().CreateDataBuffer(zip_file_spec, zip_file_size);
+ if (ZipFile::Find(zip_data, so_path, so_file_offset, so_file_size)) {
+ // Found the .so file from the zip file and got the file offset and size.
+ // Return the zip path. so_file_offset and so_file_size are already set.
+ file_kind = FileKind::eFileKindZip;
+ file_path = zip_path;
+ return true;
+ }
+
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/freebsd/Host.cpp b/contrib/llvm-project/lldb/source/Host/freebsd/Host.cpp
new file mode 100644
index 000000000000..110e803b3354
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/freebsd/Host.cpp
@@ -0,0 +1,248 @@
+//===-- source/Host/freebsd/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 <sys/types.h>
+
+#include <sys/exec.h>
+#include <sys/proc.h>
+#include <sys/ptrace.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+
+#include <machine/elf.h>
+
+#include <cstdio>
+#include <dlfcn.h>
+#include <execinfo.h>
+
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/NameMatches.h"
+#include "lldb/Utility/ProcessInfo.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "llvm/TargetParser/Host.h"
+
+extern "C" {
+extern char **environ;
+}
+
+namespace lldb_private {
+class ProcessLaunchInfo;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+static bool
+GetFreeBSDProcessArgs(const ProcessInstanceInfoMatch *match_info_ptr,
+ ProcessInstanceInfo &process_info) {
+ if (!process_info.ProcessIDIsValid())
+ return false;
+
+ int pid = process_info.GetProcessID();
+
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ARGS, pid};
+
+ char arg_data[8192];
+ size_t arg_data_size = sizeof(arg_data);
+ if (::sysctl(mib, 4, arg_data, &arg_data_size, NULL, 0) != 0)
+ return false;
+
+ DataExtractor data(arg_data, arg_data_size, endian::InlHostByteOrder(),
+ sizeof(void *));
+ lldb::offset_t offset = 0;
+ const char *cstr;
+
+ cstr = data.GetCStr(&offset);
+ if (!cstr)
+ return false;
+
+ // Get pathname for pid. If that fails fall back to argv[0].
+ char pathname[MAXPATHLEN];
+ size_t pathname_len = sizeof(pathname);
+ mib[2] = KERN_PROC_PATHNAME;
+ if (::sysctl(mib, 4, pathname, &pathname_len, NULL, 0) == 0)
+ process_info.GetExecutableFile().SetFile(pathname, FileSpec::Style::native);
+ else
+ process_info.GetExecutableFile().SetFile(cstr, FileSpec::Style::native);
+
+ if (!(match_info_ptr == NULL ||
+ NameMatches(process_info.GetExecutableFile().GetFilename().GetCString(),
+ match_info_ptr->GetNameMatchType(),
+ match_info_ptr->GetProcessInfo().GetName())))
+ return false;
+
+ process_info.SetArg0(cstr);
+ Args &proc_args = process_info.GetArguments();
+ while (1) {
+ const uint8_t *p = data.PeekData(offset, 1);
+ while ((p != NULL) && (*p == '\0') && offset < arg_data_size) {
+ ++offset;
+ p = data.PeekData(offset, 1);
+ }
+ if (p == NULL || offset >= arg_data_size)
+ break;
+
+ cstr = data.GetCStr(&offset);
+ if (!cstr)
+ break;
+
+ proc_args.AppendArgument(llvm::StringRef(cstr));
+ }
+
+ return true;
+}
+
+static bool GetFreeBSDProcessCPUType(ProcessInstanceInfo &process_info) {
+ if (process_info.ProcessIDIsValid()) {
+ process_info.GetArchitecture() =
+ HostInfo::GetArchitecture(HostInfo::eArchKindDefault);
+ return true;
+ }
+ process_info.GetArchitecture().Clear();
+ return false;
+}
+
+static bool GetFreeBSDProcessUserAndGroup(ProcessInstanceInfo &process_info) {
+ struct kinfo_proc proc_kinfo;
+ size_t proc_kinfo_size;
+ const int pid = process_info.GetProcessID();
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
+
+ if (!process_info.ProcessIDIsValid())
+ goto error;
+
+ proc_kinfo_size = sizeof(struct kinfo_proc);
+
+ if (::sysctl(mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) != 0)
+ goto error;
+
+ if (proc_kinfo_size == 0)
+ goto error;
+
+ process_info.SetParentProcessID(proc_kinfo.ki_ppid);
+ process_info.SetUserID(proc_kinfo.ki_ruid);
+ process_info.SetGroupID(proc_kinfo.ki_rgid);
+ process_info.SetEffectiveUserID(proc_kinfo.ki_uid);
+ if (proc_kinfo.ki_ngroups > 0)
+ process_info.SetEffectiveGroupID(proc_kinfo.ki_groups[0]);
+ else
+ process_info.SetEffectiveGroupID(UINT32_MAX);
+ return true;
+
+error:
+ process_info.SetParentProcessID(LLDB_INVALID_PROCESS_ID);
+ process_info.SetUserID(UINT32_MAX);
+ process_info.SetGroupID(UINT32_MAX);
+ process_info.SetEffectiveUserID(UINT32_MAX);
+ process_info.SetEffectiveGroupID(UINT32_MAX);
+ return false;
+}
+
+uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info,
+ ProcessInstanceInfoList &process_infos) {
+ const ::pid_t our_pid = ::getpid();
+ const ::uid_t our_uid = ::getuid();
+ std::vector<struct kinfo_proc> kinfos;
+ // Special case, if lldb is being run as root we can attach to anything.
+ bool all_users = match_info.GetMatchAllUsers() || (our_uid == 0);
+
+ int mib[3] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};
+
+ size_t pid_data_size = 0;
+ if (::sysctl(mib, 3, NULL, &pid_data_size, NULL, 0) != 0)
+ return 0;
+
+ // Add a few extra in case a few more show up
+ const size_t estimated_pid_count =
+ (pid_data_size / sizeof(struct kinfo_proc)) + 10;
+
+ kinfos.resize(estimated_pid_count);
+ pid_data_size = kinfos.size() * sizeof(struct kinfo_proc);
+
+ if (::sysctl(mib, 3, &kinfos[0], &pid_data_size, NULL, 0) != 0)
+ return 0;
+
+ const size_t actual_pid_count = (pid_data_size / sizeof(struct kinfo_proc));
+
+ ProcessInstanceInfoMatch match_info_noname{match_info};
+ match_info_noname.SetNameMatchType(NameMatch::Ignore);
+
+ for (size_t i = 0; i < actual_pid_count; i++) {
+ const struct kinfo_proc &kinfo = kinfos[i];
+
+ /* Make sure the user is acceptable */
+ if (!all_users && kinfo.ki_ruid != our_uid)
+ continue;
+
+ if (kinfo.ki_pid == our_pid || // Skip this process
+ kinfo.ki_pid == 0 || // Skip kernel (kernel pid is 0)
+ kinfo.ki_stat == SZOMB || // Zombies are bad
+ kinfo.ki_flag & P_TRACED || // Being debugged?
+ kinfo.ki_flag & P_WEXIT) // Working on exiting
+ continue;
+
+ // Every thread is a process in FreeBSD, but all the threads of a single
+ // process have the same pid. Do not store the process info in the result
+ // list if a process with given identifier is already registered there.
+ bool already_registered = false;
+ for (uint32_t pi = 0;
+ !already_registered && (const int)kinfo.ki_numthreads > 1 &&
+ pi < (const uint32_t)process_infos.size();
+ pi++)
+ already_registered =
+ (process_infos[pi].GetProcessID() == (uint32_t)kinfo.ki_pid);
+
+ if (already_registered)
+ continue;
+
+ ProcessInstanceInfo process_info;
+ process_info.SetProcessID(kinfo.ki_pid);
+ process_info.SetParentProcessID(kinfo.ki_ppid);
+ process_info.SetUserID(kinfo.ki_ruid);
+ process_info.SetGroupID(kinfo.ki_rgid);
+ process_info.SetEffectiveUserID(kinfo.ki_svuid);
+ process_info.SetEffectiveGroupID(kinfo.ki_svgid);
+
+ // Make sure our info matches before we go fetch the name and cpu type
+ if (match_info_noname.Matches(process_info) &&
+ GetFreeBSDProcessArgs(&match_info, process_info)) {
+ GetFreeBSDProcessCPUType(process_info);
+ if (match_info.Matches(process_info))
+ process_infos.push_back(process_info);
+ }
+ }
+
+ return process_infos.size();
+}
+
+bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) {
+ process_info.SetProcessID(pid);
+
+ if (GetFreeBSDProcessArgs(NULL, process_info)) {
+ // should use libprocstat instead of going right into sysctl?
+ GetFreeBSDProcessCPUType(process_info);
+ GetFreeBSDProcessUserAndGroup(process_info);
+ return true;
+ }
+
+ process_info.Clear();
+ return false;
+}
+
+Environment Host::GetEnvironment() { return Environment(environ); }
+
+Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
+ return Status("unimplemented");
+}
diff --git a/contrib/llvm-project/lldb/source/Host/freebsd/HostInfoFreeBSD.cpp b/contrib/llvm-project/lldb/source/Host/freebsd/HostInfoFreeBSD.cpp
new file mode 100644
index 000000000000..9832a3f07849
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/freebsd/HostInfoFreeBSD.cpp
@@ -0,0 +1,55 @@
+//===-- HostInfoFreeBSD.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/freebsd/HostInfoFreeBSD.h"
+#include "llvm/Support/FormatVariadic.h"
+#include <cstdio>
+#include <cstring>
+#include <optional>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+using namespace lldb_private;
+
+llvm::VersionTuple HostInfoFreeBSD::GetOSVersion() {
+ struct utsname un;
+
+ ::memset(&un, 0, sizeof(utsname));
+ if (uname(&un) < 0)
+ return llvm::VersionTuple();
+
+ unsigned major, minor;
+ if (2 == sscanf(un.release, "%u.%u", &major, &minor))
+ return llvm::VersionTuple(major, minor);
+ return llvm::VersionTuple();
+}
+
+std::optional<std::string> HostInfoFreeBSD::GetOSBuildString() {
+ int mib[2] = {CTL_KERN, KERN_OSREV};
+ uint32_t osrev = 0;
+ size_t osrev_len = sizeof(osrev);
+
+ if (::sysctl(mib, 2, &osrev, &osrev_len, NULL, 0) == 0)
+ return llvm::formatv("{0,8:8}", osrev).str();
+
+ return std::nullopt;
+}
+
+FileSpec HostInfoFreeBSD::GetProgramFileSpec() {
+ static FileSpec g_program_filespec;
+ if (!g_program_filespec) {
+ int exe_path_mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, getpid()};
+ char exe_path[PATH_MAX];
+ size_t exe_path_size = sizeof(exe_path);
+ if (sysctl(exe_path_mib, 4, exe_path, &exe_path_size, NULL, 0) == 0)
+ g_program_filespec.SetFile(exe_path, FileSpec::Style::native);
+ }
+ return g_program_filespec;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/netbsd/HostInfoNetBSD.cpp b/contrib/llvm-project/lldb/source/Host/netbsd/HostInfoNetBSD.cpp
new file mode 100644
index 000000000000..6a4aacc88f1e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/netbsd/HostInfoNetBSD.cpp
@@ -0,0 +1,73 @@
+//===-- HostInfoNetBSD.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/netbsd/HostInfoNetBSD.h"
+
+#include <cinttypes>
+#include <climits>
+#include <cstdio>
+#include <cstring>
+#include <optional>
+#include <pthread.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+using namespace lldb_private;
+
+llvm::VersionTuple HostInfoNetBSD::GetOSVersion() {
+ struct utsname un;
+
+ ::memset(&un, 0, sizeof(un));
+ if (::uname(&un) < 0)
+ return llvm::VersionTuple();
+
+ /* Accept versions like 7.99.21 and 6.1_STABLE */
+ uint32_t major, minor, update;
+ int status = ::sscanf(un.release, "%" PRIu32 ".%" PRIu32 ".%" PRIu32, &major,
+ &minor, &update);
+ switch (status) {
+ case 1:
+ return llvm::VersionTuple(major);
+ case 2:
+ return llvm::VersionTuple(major, minor);
+ case 3:
+ return llvm::VersionTuple(major, minor, update);
+ }
+ return llvm::VersionTuple();
+}
+
+std::optional<std::string> HostInfoNetBSD::GetOSBuildString() {
+ int mib[2] = {CTL_KERN, KERN_OSREV};
+ int osrev = 0;
+ size_t osrev_len = sizeof(osrev);
+
+ if (::sysctl(mib, 2, &osrev, &osrev_len, NULL, 0) == 0)
+ return llvm::formatv("{0,10:10}", osrev).str();
+
+ return std::nullopt;
+}
+
+FileSpec HostInfoNetBSD::GetProgramFileSpec() {
+ static FileSpec g_program_filespec;
+
+ if (!g_program_filespec) {
+ static const int name[] = {
+ CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME,
+ };
+ char path[MAXPATHLEN];
+ size_t len;
+
+ len = sizeof(path);
+ if (sysctl(name, __arraycount(name), path, &len, NULL, 0) != -1) {
+ g_program_filespec.SetFile(path, FileSpec::Style::native);
+ }
+ }
+ return g_program_filespec;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/netbsd/HostNetBSD.cpp b/contrib/llvm-project/lldb/source/Host/netbsd/HostNetBSD.cpp
new file mode 100644
index 000000000000..de8b2fd7cb0c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/netbsd/HostNetBSD.cpp
@@ -0,0 +1,273 @@
+//===-- source/Host/netbsd/HostNetBSD.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 <cstdio>
+#include <dlfcn.h>
+#include <execinfo.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include <climits>
+
+#include <kvm.h>
+#include <sys/exec.h>
+#include <sys/ptrace.h>
+
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/NameMatches.h"
+#include "lldb/Utility/ProcessInfo.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "llvm/Object/ELF.h"
+#include "llvm/TargetParser/Host.h"
+
+extern "C" {
+extern char **environ;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace lldb_private {
+class ProcessLaunchInfo;
+}
+
+Environment Host::GetEnvironment() { return Environment(environ); }
+
+static bool GetNetBSDProcessArgs(const ProcessInstanceInfoMatch *match_info_ptr,
+ ProcessInstanceInfo &process_info) {
+ if (!process_info.ProcessIDIsValid())
+ return false;
+
+ int pid = process_info.GetProcessID();
+
+ int mib[4] = {CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_ARGV};
+
+ char arg_data[8192];
+ size_t arg_data_size = sizeof(arg_data);
+ if (::sysctl(mib, 4, arg_data, &arg_data_size, NULL, 0) != 0)
+ return false;
+
+ DataExtractor data(arg_data, arg_data_size, endian::InlHostByteOrder(),
+ sizeof(void *));
+ lldb::offset_t offset = 0;
+ const char *cstr;
+
+ cstr = data.GetCStr(&offset);
+ if (!cstr)
+ return false;
+
+ process_info.GetExecutableFile().SetFile(cstr,
+ FileSpec::Style::native);
+
+ if (!(match_info_ptr == NULL ||
+ NameMatches(process_info.GetExecutableFile().GetFilename().GetCString(),
+ match_info_ptr->GetNameMatchType(),
+ match_info_ptr->GetProcessInfo().GetName())))
+ return false;
+
+ process_info.SetArg0(cstr);
+ Args &proc_args = process_info.GetArguments();
+ while (1) {
+ const uint8_t *p = data.PeekData(offset, 1);
+ while ((p != NULL) && (*p == '\0') && offset < arg_data_size) {
+ ++offset;
+ p = data.PeekData(offset, 1);
+ }
+ if (p == NULL || offset >= arg_data_size)
+ break;
+
+ cstr = data.GetCStr(&offset);
+ if (!cstr)
+ break;
+
+ proc_args.AppendArgument(llvm::StringRef(cstr));
+ }
+
+ return true;
+}
+
+static bool GetNetBSDProcessCPUType(ProcessInstanceInfo &process_info) {
+ Log *log = GetLog(LLDBLog::Host);
+
+ if (process_info.ProcessIDIsValid()) {
+ auto buffer_sp = FileSystem::Instance().CreateDataBuffer(
+ process_info.GetExecutableFile(), 0x20, 0);
+ if (buffer_sp) {
+ uint8_t exe_class =
+ llvm::object::getElfArchType(
+ {reinterpret_cast<const char *>(buffer_sp->GetBytes()),
+ size_t(buffer_sp->GetByteSize())})
+ .first;
+
+ switch (exe_class) {
+ case llvm::ELF::ELFCLASS32:
+ process_info.GetArchitecture() =
+ HostInfo::GetArchitecture(HostInfo::eArchKind32);
+ return true;
+ case llvm::ELF::ELFCLASS64:
+ process_info.GetArchitecture() =
+ HostInfo::GetArchitecture(HostInfo::eArchKind64);
+ return true;
+ default:
+ LLDB_LOG(log, "Unknown elf class ({0}) in file {1}", exe_class,
+ process_info.GetExecutableFile());
+ }
+ }
+ }
+ process_info.GetArchitecture().Clear();
+ return false;
+}
+
+static bool GetNetBSDProcessUserAndGroup(ProcessInstanceInfo &process_info) {
+ ::kvm_t *kdp;
+ char errbuf[_POSIX2_LINE_MAX]; /* XXX: error string unused */
+
+ struct ::kinfo_proc2 *proc_kinfo;
+ const int pid = process_info.GetProcessID();
+ int nproc;
+
+ if (!process_info.ProcessIDIsValid())
+ goto error;
+
+ if ((kdp = ::kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf)) == NULL)
+ goto error;
+
+ if ((proc_kinfo = ::kvm_getproc2(kdp, KERN_PROC_PID, pid,
+ sizeof(struct ::kinfo_proc2), &nproc)) ==
+ NULL) {
+ ::kvm_close(kdp);
+ goto error;
+ }
+
+ if (nproc < 1) {
+ ::kvm_close(kdp); /* XXX: we don't check for error here */
+ goto error;
+ }
+
+ process_info.SetParentProcessID(proc_kinfo->p_ppid);
+ process_info.SetUserID(proc_kinfo->p_ruid);
+ process_info.SetGroupID(proc_kinfo->p_rgid);
+ process_info.SetEffectiveUserID(proc_kinfo->p_uid);
+ process_info.SetEffectiveGroupID(proc_kinfo->p_gid);
+
+ ::kvm_close(kdp); /* XXX: we don't check for error here */
+
+ return true;
+
+error:
+ process_info.SetParentProcessID(LLDB_INVALID_PROCESS_ID);
+ process_info.SetUserID(UINT32_MAX);
+ process_info.SetGroupID(UINT32_MAX);
+ process_info.SetEffectiveUserID(UINT32_MAX);
+ process_info.SetEffectiveGroupID(UINT32_MAX);
+ return false;
+}
+
+uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info,
+ ProcessInstanceInfoList &process_infos) {
+ const ::pid_t our_pid = ::getpid();
+ const ::uid_t our_uid = ::getuid();
+
+ const bool all_users =
+ match_info.GetMatchAllUsers() ||
+ // Special case, if lldb is being run as root we can attach to anything
+ (our_uid == 0);
+
+ kvm_t *kdp;
+ char errbuf[_POSIX2_LINE_MAX]; /* XXX: error string unused */
+ if ((kdp = ::kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf)) == NULL)
+ return 0;
+
+ struct ::kinfo_proc2 *proc_kinfo;
+ int nproc;
+ if ((proc_kinfo = ::kvm_getproc2(kdp, KERN_PROC_ALL, 0,
+ sizeof(struct ::kinfo_proc2), &nproc)) ==
+ NULL) {
+ ::kvm_close(kdp);
+ return 0;
+ }
+
+ ProcessInstanceInfoMatch match_info_noname{match_info};
+ match_info_noname.SetNameMatchType(NameMatch::Ignore);
+
+ for (int i = 0; i < nproc; i++) {
+ if (proc_kinfo[i].p_pid < 1)
+ continue; /* not valid */
+ /* Make sure the user is acceptable */
+ if (!all_users && proc_kinfo[i].p_ruid != our_uid)
+ continue;
+
+ if (proc_kinfo[i].p_pid == our_pid || // Skip this process
+ proc_kinfo[i].p_pid == 0 || // Skip kernel (kernel pid is 0)
+ proc_kinfo[i].p_stat == LSZOMB || // Zombies are bad
+ proc_kinfo[i].p_flag & P_TRACED || // Being debugged?
+ proc_kinfo[i].p_flag & P_WEXIT) // Working on exiting
+ continue;
+
+ // Every thread is a process in NetBSD, but all the threads of a single
+ // process have the same pid. Do not store the process info in the result
+ // list if a process with given identifier is already registered there.
+ if (proc_kinfo[i].p_nlwps > 1) {
+ bool already_registered = false;
+ for (size_t pi = 0; pi < process_infos.size(); pi++) {
+ if ((::pid_t)process_infos[pi].GetProcessID() == proc_kinfo[i].p_pid) {
+ already_registered = true;
+ break;
+ }
+ }
+
+ if (already_registered)
+ continue;
+ }
+ ProcessInstanceInfo process_info;
+ process_info.SetProcessID(proc_kinfo[i].p_pid);
+ process_info.SetParentProcessID(proc_kinfo[i].p_ppid);
+ process_info.SetUserID(proc_kinfo[i].p_ruid);
+ process_info.SetGroupID(proc_kinfo[i].p_rgid);
+ process_info.SetEffectiveUserID(proc_kinfo[i].p_uid);
+ process_info.SetEffectiveGroupID(proc_kinfo[i].p_gid);
+ // Make sure our info matches before we go fetch the name and cpu type
+ if (match_info_noname.Matches(process_info) &&
+ GetNetBSDProcessArgs(&match_info, process_info)) {
+ GetNetBSDProcessCPUType(process_info);
+ if (match_info.Matches(process_info))
+ process_infos.push_back(process_info);
+ }
+ }
+
+ kvm_close(kdp); /* XXX: we don't check for error here */
+
+ return process_infos.size();
+}
+
+bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) {
+ process_info.SetProcessID(pid);
+
+ if (GetNetBSDProcessArgs(NULL, process_info)) {
+ GetNetBSDProcessCPUType(process_info);
+ GetNetBSDProcessUserAndGroup(process_info);
+ return true;
+ }
+
+ process_info.Clear();
+ return false;
+}
+
+Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
+ return Status("unimplemented");
+}
diff --git a/contrib/llvm-project/lldb/source/Host/openbsd/Host.cpp b/contrib/llvm-project/lldb/source/Host/openbsd/Host.cpp
new file mode 100644
index 000000000000..9a7953e2bea4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/openbsd/Host.cpp
@@ -0,0 +1,219 @@
+//===-- source/Host/openbsd/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 <sys/types.h>
+
+#include <sys/signal.h>
+#include <sys/exec.h>
+#include <sys/proc.h>
+#include <sys/ptrace.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+
+#include <cstdio>
+
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/NameMatches.h"
+#include "lldb/Utility/ProcessInfo.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "llvm/TargetParser/Host.h"
+
+extern "C" {
+extern char **environ;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace lldb_private {
+class ProcessLaunchInfo;
+}
+
+Environment Host::GetEnvironment() {
+ Environment env;
+ char *v;
+ char **var = environ;
+ for (; var != NULL && *var != NULL; ++var) {
+ v = strchr(*var, (int)'-');
+ if (v == NULL)
+ continue;
+ env.insert(v);
+ }
+ return env;
+}
+
+static bool
+GetOpenBSDProcessArgs(const ProcessInstanceInfoMatch *match_info_ptr,
+ ProcessInstanceInfo &process_info) {
+ if (process_info.ProcessIDIsValid()) {
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ARGS,
+ (int)process_info.GetProcessID()};
+
+ char arg_data[8192];
+ size_t arg_data_size = sizeof(arg_data);
+ if (::sysctl(mib, 4, arg_data, &arg_data_size, NULL, 0) == 0) {
+ DataExtractor data(arg_data, arg_data_size, endian::InlHostByteOrder(),
+ sizeof(void *));
+ lldb::offset_t offset = 0;
+ const char *cstr;
+
+ cstr = data.GetCStr(&offset);
+ if (cstr) {
+ process_info.GetExecutableFile().SetFile(cstr, FileSpec::Style::native);
+
+ if (!(match_info_ptr == NULL ||
+ NameMatches(
+ process_info.GetExecutableFile().GetFilename().GetCString(),
+ match_info_ptr->GetNameMatchType(),
+ match_info_ptr->GetProcessInfo().GetName())))
+ return false;
+
+ Args &proc_args = process_info.GetArguments();
+ while (1) {
+ const uint8_t *p = data.PeekData(offset, 1);
+ while ((p != NULL) && (*p == '\0') && offset < arg_data_size) {
+ ++offset;
+ p = data.PeekData(offset, 1);
+ }
+ if (p == NULL || offset >= arg_data_size)
+ return true;
+
+ cstr = data.GetCStr(&offset);
+ if (cstr)
+ proc_args.AppendArgument(llvm::StringRef(cstr));
+ else
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+static bool GetOpenBSDProcessCPUType(ProcessInstanceInfo &process_info) {
+ if (process_info.ProcessIDIsValid()) {
+ process_info.GetArchitecture() =
+ HostInfo::GetArchitecture(HostInfo::eArchKindDefault);
+ return true;
+ }
+ process_info.GetArchitecture().Clear();
+ return false;
+}
+
+static bool GetOpenBSDProcessUserAndGroup(ProcessInstanceInfo &process_info) {
+ struct kinfo_proc proc_kinfo;
+ size_t proc_kinfo_size;
+
+ if (process_info.ProcessIDIsValid()) {
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID,
+ (int)process_info.GetProcessID()};
+ proc_kinfo_size = sizeof(struct kinfo_proc);
+
+ if (::sysctl(mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0) {
+ if (proc_kinfo_size > 0) {
+ process_info.SetParentProcessID(proc_kinfo.p_ppid);
+ process_info.SetUserID(proc_kinfo.p_ruid);
+ process_info.SetGroupID(proc_kinfo.p_rgid);
+ process_info.SetEffectiveUserID(proc_kinfo.p_uid);
+ process_info.SetEffectiveGroupID(proc_kinfo.p_gid);
+ return true;
+ }
+ }
+ }
+ process_info.SetParentProcessID(LLDB_INVALID_PROCESS_ID);
+ process_info.SetUserID(UINT32_MAX);
+ process_info.SetGroupID(UINT32_MAX);
+ process_info.SetEffectiveUserID(UINT32_MAX);
+ process_info.SetEffectiveGroupID(UINT32_MAX);
+ return false;
+}
+
+uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info,
+ ProcessInstanceInfoList &process_infos) {
+ std::vector<struct kinfo_proc> kinfos;
+
+ int mib[3] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};
+
+ size_t pid_data_size = 0;
+ if (::sysctl(mib, 3, NULL, &pid_data_size, NULL, 0) != 0)
+ return 0;
+
+ // Add a few extra in case a few more show up
+ const size_t estimated_pid_count =
+ (pid_data_size / sizeof(struct kinfo_proc)) + 10;
+
+ kinfos.resize(estimated_pid_count);
+ pid_data_size = kinfos.size() * sizeof(struct kinfo_proc);
+
+ if (::sysctl(mib, 3, &kinfos[0], &pid_data_size, NULL, 0) != 0)
+ return 0;
+
+ const size_t actual_pid_count = (pid_data_size / sizeof(struct kinfo_proc));
+
+ bool all_users = match_info.GetMatchAllUsers();
+ const ::pid_t our_pid = getpid();
+ const uid_t our_uid = getuid();
+ for (size_t i = 0; i < actual_pid_count; i++) {
+ const struct kinfo_proc &kinfo = kinfos[i];
+ const bool kinfo_user_matches = (all_users || (kinfo.p_ruid == our_uid) ||
+ // Special case, if lldb is being run as
+ // root we can attach to anything.
+ (our_uid == 0));
+
+ if (kinfo_user_matches == false || // Make sure the user is acceptable
+ kinfo.p_pid == our_pid || // Skip this process
+ kinfo.p_pid == 0 || // Skip kernel (kernel pid is zero)
+ kinfo.p_stat == SZOMB || // Zombies are bad, they like brains...
+ kinfo.p_psflags & PS_TRACED || // Being debugged?
+ kinfo.p_flag & P_WEXIT) // Working on exiting
+ continue;
+
+ ProcessInstanceInfo process_info;
+ process_info.SetProcessID(kinfo.p_pid);
+ process_info.SetParentProcessID(kinfo.p_ppid);
+ process_info.SetUserID(kinfo.p_ruid);
+ process_info.SetGroupID(kinfo.p_rgid);
+ process_info.SetEffectiveUserID(kinfo.p_svuid);
+ process_info.SetEffectiveGroupID(kinfo.p_svgid);
+
+ // Make sure our info matches before we go fetch the name and cpu type
+ if (match_info.Matches(process_info) &&
+ GetOpenBSDProcessArgs(&match_info, process_info)) {
+ GetOpenBSDProcessCPUType(process_info);
+ if (match_info.Matches(process_info))
+ process_infos.push_back(process_info);
+ }
+ }
+
+ return process_infos.size();
+}
+
+bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) {
+ process_info.SetProcessID(pid);
+
+ if (GetOpenBSDProcessArgs(NULL, process_info)) {
+ // should use libprocstat instead of going right into sysctl?
+ GetOpenBSDProcessCPUType(process_info);
+ GetOpenBSDProcessUserAndGroup(process_info);
+ return true;
+ }
+
+ process_info.Clear();
+ return false;
+}
+
+Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
+ return Status("unimplemented");
+}
diff --git a/contrib/llvm-project/lldb/source/Host/openbsd/HostInfoOpenBSD.cpp b/contrib/llvm-project/lldb/source/Host/openbsd/HostInfoOpenBSD.cpp
new file mode 100644
index 000000000000..b30c7a83243c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/openbsd/HostInfoOpenBSD.cpp
@@ -0,0 +1,52 @@
+//===-- HostInfoOpenBSD.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/openbsd/HostInfoOpenBSD.h"
+
+#include <cstdio>
+#include <cstring>
+#include <optional>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+
+using namespace lldb_private;
+
+llvm::VersionTuple HostInfoOpenBSD::GetOSVersion() {
+ struct utsname un;
+
+ ::memset(&un, 0, sizeof(un));
+ if (::uname(&un) < 0)
+ return llvm::VersionTuple();
+
+ uint32_t major, minor;
+ int status = ::sscanf(un.release, "%" PRIu32 ".%" PRIu32, &major, &minor);
+ switch (status) {
+ case 1:
+ return llvm::VersionTuple(major);
+ case 2:
+ return llvm::VersionTuple(major, minor);
+ }
+ return llvm::VersionTuple();
+}
+
+std::optional<std::string> HostInfoOpenBSD::GetOSBuildString() {
+ int mib[2] = {CTL_KERN, KERN_OSREV};
+ uint32_t osrev = 0;
+ size_t osrev_len = sizeof(osrev);
+
+ if (::sysctl(mib, 2, &osrev, &osrev_len, NULL, 0) == 0)
+ return llvm::formatv("{0,8:8}", osrev).str();
+
+ return std::nullopt;
+}
+
+FileSpec HostInfoOpenBSD::GetProgramFileSpec() {
+ static FileSpec g_program_filespec;
+ return g_program_filespec;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp b/contrib/llvm-project/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp
new file mode 100644
index 000000000000..fceeff08ed9d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp
@@ -0,0 +1,812 @@
+//===-- ConnectionFileDescriptorPosix.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
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__APPLE__)
+// Enable this special support for Apple builds where we can have unlimited
+// select bounds. We tried switching to poll() and kqueue and we were panicing
+// the kernel, so we have to stick with select for now.
+#define _DARWIN_UNLIMITED_SELECT
+#endif
+
+#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h"
+#include "lldb/Host/Config.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Socket.h"
+#include "lldb/Host/SocketAddress.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/SelectHelper.h"
+#include "lldb/Utility/Timeout.h"
+
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include <fcntl.h>
+#include <sys/types.h>
+
+#if LLDB_ENABLE_POSIX
+#include <termios.h>
+#include <unistd.h>
+#endif
+
+#include <memory>
+#include <sstream>
+
+#include "llvm/Support/Errno.h"
+#include "llvm/Support/ErrorHandling.h"
+#if defined(__APPLE__)
+#include "llvm/ADT/SmallVector.h"
+#endif
+#include "lldb/Host/Host.h"
+#include "lldb/Host/Socket.h"
+#include "lldb/Host/common/TCPSocket.h"
+#include "lldb/Host/common/UDPSocket.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/Timer.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+ConnectionFileDescriptor::ConnectionFileDescriptor(bool child_processes_inherit)
+ : Connection(), m_pipe(), m_mutex(), m_shutting_down(false),
+
+ m_child_processes_inherit(child_processes_inherit) {
+ Log *log(GetLog(LLDBLog::Connection | LLDBLog::Object));
+ LLDB_LOGF(log, "%p ConnectionFileDescriptor::ConnectionFileDescriptor ()",
+ static_cast<void *>(this));
+}
+
+ConnectionFileDescriptor::ConnectionFileDescriptor(int fd, bool owns_fd)
+ : Connection(), m_pipe(), m_mutex(), m_shutting_down(false),
+ m_child_processes_inherit(false) {
+ m_io_sp =
+ std::make_shared<NativeFile>(fd, File::eOpenOptionReadWrite, owns_fd);
+
+ Log *log(GetLog(LLDBLog::Connection | LLDBLog::Object));
+ LLDB_LOGF(log,
+ "%p ConnectionFileDescriptor::ConnectionFileDescriptor (fd = "
+ "%i, owns_fd = %i)",
+ static_cast<void *>(this), fd, owns_fd);
+ OpenCommandPipe();
+}
+
+ConnectionFileDescriptor::ConnectionFileDescriptor(Socket *socket)
+ : Connection(), m_pipe(), m_mutex(), m_shutting_down(false),
+ m_child_processes_inherit(false) {
+ InitializeSocket(socket);
+}
+
+ConnectionFileDescriptor::~ConnectionFileDescriptor() {
+ Log *log(GetLog(LLDBLog::Connection | LLDBLog::Object));
+ LLDB_LOGF(log, "%p ConnectionFileDescriptor::~ConnectionFileDescriptor ()",
+ static_cast<void *>(this));
+ Disconnect(nullptr);
+ CloseCommandPipe();
+}
+
+void ConnectionFileDescriptor::OpenCommandPipe() {
+ CloseCommandPipe();
+
+ Log *log = GetLog(LLDBLog::Connection);
+ // Make the command file descriptor here:
+ Status result = m_pipe.CreateNew(m_child_processes_inherit);
+ if (!result.Success()) {
+ LLDB_LOGF(log,
+ "%p ConnectionFileDescriptor::OpenCommandPipe () - could not "
+ "make pipe: %s",
+ static_cast<void *>(this), result.AsCString());
+ } else {
+ LLDB_LOGF(log,
+ "%p ConnectionFileDescriptor::OpenCommandPipe() - success "
+ "readfd=%d writefd=%d",
+ static_cast<void *>(this), m_pipe.GetReadFileDescriptor(),
+ m_pipe.GetWriteFileDescriptor());
+ }
+}
+
+void ConnectionFileDescriptor::CloseCommandPipe() {
+ Log *log = GetLog(LLDBLog::Connection);
+ LLDB_LOGF(log, "%p ConnectionFileDescriptor::CloseCommandPipe()",
+ static_cast<void *>(this));
+
+ m_pipe.Close();
+}
+
+bool ConnectionFileDescriptor::IsConnected() const {
+ return m_io_sp && m_io_sp->IsValid();
+}
+
+ConnectionStatus ConnectionFileDescriptor::Connect(llvm::StringRef path,
+ Status *error_ptr) {
+ return Connect(
+ path, [](llvm::StringRef) {}, error_ptr);
+}
+
+ConnectionStatus
+ConnectionFileDescriptor::Connect(llvm::StringRef path,
+ socket_id_callback_type socket_id_callback,
+ Status *error_ptr) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ Log *log = GetLog(LLDBLog::Connection);
+ LLDB_LOGF(log, "%p ConnectionFileDescriptor::Connect (url = '%s')",
+ static_cast<void *>(this), path.str().c_str());
+
+ OpenCommandPipe();
+
+ if (path.empty()) {
+ if (error_ptr)
+ error_ptr->SetErrorString("invalid connect arguments");
+ return eConnectionStatusError;
+ }
+
+ llvm::StringRef scheme;
+ std::tie(scheme, path) = path.split("://");
+
+ if (!path.empty()) {
+ auto method =
+ llvm::StringSwitch<ConnectionStatus (ConnectionFileDescriptor::*)(
+ llvm::StringRef, socket_id_callback_type, Status *)>(scheme)
+ .Case("listen", &ConnectionFileDescriptor::AcceptTCP)
+ .Cases("accept", "unix-accept",
+ &ConnectionFileDescriptor::AcceptNamedSocket)
+ .Case("unix-abstract-accept",
+ &ConnectionFileDescriptor::AcceptAbstractSocket)
+ .Cases("connect", "tcp-connect",
+ &ConnectionFileDescriptor::ConnectTCP)
+ .Case("udp", &ConnectionFileDescriptor::ConnectUDP)
+ .Case("unix-connect", &ConnectionFileDescriptor::ConnectNamedSocket)
+ .Case("unix-abstract-connect",
+ &ConnectionFileDescriptor::ConnectAbstractSocket)
+#if LLDB_ENABLE_POSIX
+ .Case("fd", &ConnectionFileDescriptor::ConnectFD)
+ .Case("file", &ConnectionFileDescriptor::ConnectFile)
+ .Case("serial", &ConnectionFileDescriptor::ConnectSerialPort)
+#endif
+ .Default(nullptr);
+
+ if (method) {
+ if (error_ptr)
+ *error_ptr = Status();
+ return (this->*method)(path, socket_id_callback, error_ptr);
+ }
+ }
+
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat("unsupported connection URL: '%s'",
+ path.str().c_str());
+ return eConnectionStatusError;
+}
+
+bool ConnectionFileDescriptor::InterruptRead() {
+ size_t bytes_written = 0;
+ Status result = m_pipe.Write("i", 1, bytes_written);
+ return result.Success();
+}
+
+ConnectionStatus ConnectionFileDescriptor::Disconnect(Status *error_ptr) {
+ Log *log = GetLog(LLDBLog::Connection);
+ LLDB_LOGF(log, "%p ConnectionFileDescriptor::Disconnect ()",
+ static_cast<void *>(this));
+
+ ConnectionStatus status = eConnectionStatusSuccess;
+
+ if (!IsConnected()) {
+ LLDB_LOGF(
+ log, "%p ConnectionFileDescriptor::Disconnect(): Nothing to disconnect",
+ static_cast<void *>(this));
+ return eConnectionStatusSuccess;
+ }
+
+ // Try to get the ConnectionFileDescriptor's mutex. If we fail, that is
+ // quite likely because somebody is doing a blocking read on our file
+ // descriptor. If that's the case, then send the "q" char to the command
+ // file channel so the read will wake up and the connection will then know to
+ // shut down.
+ std::unique_lock<std::recursive_mutex> locker(m_mutex, std::defer_lock);
+ if (!locker.try_lock()) {
+ if (m_pipe.CanWrite()) {
+ size_t bytes_written = 0;
+ Status result = m_pipe.Write("q", 1, bytes_written);
+ LLDB_LOGF(log,
+ "%p ConnectionFileDescriptor::Disconnect(): Couldn't get "
+ "the lock, sent 'q' to %d, error = '%s'.",
+ static_cast<void *>(this), m_pipe.GetWriteFileDescriptor(),
+ result.AsCString());
+ } else if (log) {
+ LLDB_LOGF(log,
+ "%p ConnectionFileDescriptor::Disconnect(): Couldn't get the "
+ "lock, but no command pipe is available.",
+ static_cast<void *>(this));
+ }
+ locker.lock();
+ }
+
+ // Prevents reads and writes during shutdown.
+ m_shutting_down = true;
+
+ Status error = m_io_sp->Close();
+ if (error.Fail())
+ status = eConnectionStatusError;
+ if (error_ptr)
+ *error_ptr = error;
+
+ // Close any pipes we were using for async interrupts
+ m_pipe.Close();
+
+ m_uri.clear();
+ m_shutting_down = false;
+ return status;
+}
+
+size_t ConnectionFileDescriptor::Read(void *dst, size_t dst_len,
+ const Timeout<std::micro> &timeout,
+ ConnectionStatus &status,
+ Status *error_ptr) {
+ Log *log = GetLog(LLDBLog::Connection);
+
+ std::unique_lock<std::recursive_mutex> locker(m_mutex, std::defer_lock);
+ if (!locker.try_lock()) {
+ LLDB_LOGF(log,
+ "%p ConnectionFileDescriptor::Read () failed to get the "
+ "connection lock.",
+ static_cast<void *>(this));
+ if (error_ptr)
+ error_ptr->SetErrorString("failed to get the connection lock for read.");
+
+ status = eConnectionStatusTimedOut;
+ return 0;
+ }
+
+ if (m_shutting_down) {
+ if (error_ptr)
+ error_ptr->SetErrorString("shutting down");
+ status = eConnectionStatusError;
+ return 0;
+ }
+
+ status = BytesAvailable(timeout, error_ptr);
+ if (status != eConnectionStatusSuccess)
+ return 0;
+
+ Status error;
+ size_t bytes_read = dst_len;
+ error = m_io_sp->Read(dst, bytes_read);
+
+ if (log) {
+ LLDB_LOGF(log,
+ "%p ConnectionFileDescriptor::Read() fd = %" PRIu64
+ ", dst = %p, dst_len = %" PRIu64 ") => %" PRIu64 ", error = %s",
+ static_cast<void *>(this),
+ static_cast<uint64_t>(m_io_sp->GetWaitableHandle()),
+ static_cast<void *>(dst), static_cast<uint64_t>(dst_len),
+ static_cast<uint64_t>(bytes_read), error.AsCString());
+ }
+
+ if (bytes_read == 0) {
+ error.Clear(); // End-of-file. Do not automatically close; pass along for
+ // the end-of-file handlers.
+ status = eConnectionStatusEndOfFile;
+ }
+
+ if (error_ptr)
+ *error_ptr = error;
+
+ if (error.Fail()) {
+ uint32_t error_value = error.GetError();
+ switch (error_value) {
+ case EAGAIN: // The file was marked for non-blocking I/O, and no data were
+ // ready to be read.
+ if (m_io_sp->GetFdType() == IOObject::eFDTypeSocket)
+ status = eConnectionStatusTimedOut;
+ else
+ status = eConnectionStatusSuccess;
+ return 0;
+
+ case EFAULT: // Buf points outside the allocated address space.
+ case EINTR: // A read from a slow device was interrupted before any data
+ // arrived by the delivery of a signal.
+ case EINVAL: // The pointer associated with fildes was negative.
+ case EIO: // An I/O error occurred while reading from the file system.
+ // The process group is orphaned.
+ // The file is a regular file, nbyte is greater than 0, the
+ // starting position is before the end-of-file, and the
+ // starting position is greater than or equal to the offset
+ // maximum established for the open file descriptor
+ // associated with fildes.
+ case EISDIR: // An attempt is made to read a directory.
+ case ENOBUFS: // An attempt to allocate a memory buffer fails.
+ case ENOMEM: // Insufficient memory is available.
+ status = eConnectionStatusError;
+ break; // Break to close....
+
+ case ENOENT: // no such file or directory
+ case EBADF: // fildes is not a valid file or socket descriptor open for
+ // reading.
+ case ENXIO: // An action is requested of a device that does not exist..
+ // A requested action cannot be performed by the device.
+ case ECONNRESET: // The connection is closed by the peer during a read
+ // attempt on a socket.
+ case ENOTCONN: // A read is attempted on an unconnected socket.
+ status = eConnectionStatusLostConnection;
+ break; // Break to close....
+
+ case ETIMEDOUT: // A transmission timeout occurs during a read attempt on a
+ // socket.
+ status = eConnectionStatusTimedOut;
+ return 0;
+
+ default:
+ LLDB_LOG(log, "this = {0}, unexpected error: {1}", this,
+ llvm::sys::StrError(error_value));
+ status = eConnectionStatusError;
+ break; // Break to close....
+ }
+
+ return 0;
+ }
+ return bytes_read;
+}
+
+size_t ConnectionFileDescriptor::Write(const void *src, size_t src_len,
+ ConnectionStatus &status,
+ Status *error_ptr) {
+ Log *log = GetLog(LLDBLog::Connection);
+ LLDB_LOGF(log,
+ "%p ConnectionFileDescriptor::Write (src = %p, src_len = %" PRIu64
+ ")",
+ static_cast<void *>(this), static_cast<const void *>(src),
+ static_cast<uint64_t>(src_len));
+
+ if (!IsConnected()) {
+ if (error_ptr)
+ error_ptr->SetErrorString("not connected");
+ status = eConnectionStatusNoConnection;
+ return 0;
+ }
+
+ if (m_shutting_down) {
+ if (error_ptr)
+ error_ptr->SetErrorString("shutting down");
+ status = eConnectionStatusError;
+ return 0;
+ }
+
+ Status error;
+
+ size_t bytes_sent = src_len;
+ error = m_io_sp->Write(src, bytes_sent);
+
+ if (log) {
+ LLDB_LOGF(log,
+ "%p ConnectionFileDescriptor::Write(fd = %" PRIu64
+ ", src = %p, src_len = %" PRIu64 ") => %" PRIu64 " (error = %s)",
+ static_cast<void *>(this),
+ static_cast<uint64_t>(m_io_sp->GetWaitableHandle()),
+ static_cast<const void *>(src), static_cast<uint64_t>(src_len),
+ static_cast<uint64_t>(bytes_sent), error.AsCString());
+ }
+
+ if (error_ptr)
+ *error_ptr = error;
+
+ if (error.Fail()) {
+ switch (error.GetError()) {
+ case EAGAIN:
+ case EINTR:
+ status = eConnectionStatusSuccess;
+ return 0;
+
+ case ECONNRESET: // The connection is closed by the peer during a read
+ // attempt on a socket.
+ case ENOTCONN: // A read is attempted on an unconnected socket.
+ status = eConnectionStatusLostConnection;
+ break; // Break to close....
+
+ default:
+ status = eConnectionStatusError;
+ break; // Break to close....
+ }
+
+ return 0;
+ }
+
+ status = eConnectionStatusSuccess;
+ return bytes_sent;
+}
+
+std::string ConnectionFileDescriptor::GetURI() { return m_uri; }
+
+// This ConnectionFileDescriptor::BytesAvailable() uses select() via
+// SelectHelper
+//
+// PROS:
+// - select is consistent across most unix platforms
+// - The Apple specific version allows for unlimited fds in the fd_sets by
+// setting the _DARWIN_UNLIMITED_SELECT define prior to including the
+// required header files.
+// CONS:
+// - on non-Apple platforms, only supports file descriptors up to FD_SETSIZE.
+// This implementation will assert if it runs into that hard limit to let
+// users know that another ConnectionFileDescriptor::BytesAvailable() should
+// be used or a new version of ConnectionFileDescriptor::BytesAvailable()
+// should be written for the system that is running into the limitations.
+
+ConnectionStatus
+ConnectionFileDescriptor::BytesAvailable(const Timeout<std::micro> &timeout,
+ Status *error_ptr) {
+ // Don't need to take the mutex here separately since we are only called from
+ // Read. If we ever get used more generally we will need to lock here as
+ // well.
+
+ Log *log = GetLog(LLDBLog::Connection);
+ LLDB_LOG(log, "this = {0}, timeout = {1}", this, timeout);
+
+ // Make a copy of the file descriptors to make sure we don't have another
+ // thread change these values out from under us and cause problems in the
+ // loop below where like in FS_SET()
+ const IOObject::WaitableHandle handle = m_io_sp->GetWaitableHandle();
+ const int pipe_fd = m_pipe.GetReadFileDescriptor();
+
+ if (handle != IOObject::kInvalidHandleValue) {
+ SelectHelper select_helper;
+ if (timeout)
+ select_helper.SetTimeout(*timeout);
+
+ select_helper.FDSetRead(handle);
+#if defined(_WIN32)
+ // select() won't accept pipes on Windows. The entire Windows codepath
+ // needs to be converted over to using WaitForMultipleObjects and event
+ // HANDLEs, but for now at least this will allow ::select() to not return
+ // an error.
+ const bool have_pipe_fd = false;
+#else
+ const bool have_pipe_fd = pipe_fd >= 0;
+#endif
+ if (have_pipe_fd)
+ select_helper.FDSetRead(pipe_fd);
+
+ while (handle == m_io_sp->GetWaitableHandle()) {
+
+ Status error = select_helper.Select();
+
+ if (error_ptr)
+ *error_ptr = error;
+
+ if (error.Fail()) {
+ switch (error.GetError()) {
+ case EBADF: // One of the descriptor sets specified an invalid
+ // descriptor.
+ return eConnectionStatusLostConnection;
+
+ case EINVAL: // The specified time limit is invalid. One of its
+ // components is negative or too large.
+ default: // Other unknown error
+ return eConnectionStatusError;
+
+ case ETIMEDOUT:
+ return eConnectionStatusTimedOut;
+
+ case EAGAIN: // The kernel was (perhaps temporarily) unable to
+ // allocate the requested number of file descriptors, or
+ // we have non-blocking IO
+ case EINTR: // A signal was delivered before the time limit
+ // expired and before any of the selected events occurred.
+ break; // Lets keep reading to until we timeout
+ }
+ } else {
+ if (select_helper.FDIsSetRead(handle))
+ return eConnectionStatusSuccess;
+
+ if (select_helper.FDIsSetRead(pipe_fd)) {
+ // There is an interrupt or exit command in the command pipe Read the
+ // data from that pipe:
+ char c;
+
+ ssize_t bytes_read =
+ llvm::sys::RetryAfterSignal(-1, ::read, pipe_fd, &c, 1);
+ assert(bytes_read == 1);
+ UNUSED_IF_ASSERT_DISABLED(bytes_read);
+ switch (c) {
+ case 'q':
+ LLDB_LOGF(log,
+ "%p ConnectionFileDescriptor::BytesAvailable() "
+ "got data: %c from the command channel.",
+ static_cast<void *>(this), c);
+ return eConnectionStatusEndOfFile;
+ case 'i':
+ // Interrupt the current read
+ return eConnectionStatusInterrupted;
+ }
+ }
+ }
+ }
+ }
+
+ if (error_ptr)
+ error_ptr->SetErrorString("not connected");
+ return eConnectionStatusLostConnection;
+}
+
+lldb::ConnectionStatus ConnectionFileDescriptor::AcceptSocket(
+ Socket::SocketProtocol socket_protocol, llvm::StringRef socket_name,
+ llvm::function_ref<void(Socket &)> post_listen_callback,
+ Status *error_ptr) {
+ Status error;
+ std::unique_ptr<Socket> listening_socket =
+ Socket::Create(socket_protocol, m_child_processes_inherit, error);
+ Socket *accepted_socket;
+
+ if (!error.Fail())
+ error = listening_socket->Listen(socket_name, 5);
+
+ if (!error.Fail()) {
+ post_listen_callback(*listening_socket);
+ error = listening_socket->Accept(accepted_socket);
+ }
+
+ if (!error.Fail()) {
+ m_io_sp.reset(accepted_socket);
+ m_uri.assign(socket_name.str());
+ return eConnectionStatusSuccess;
+ }
+
+ if (error_ptr)
+ *error_ptr = error;
+ return eConnectionStatusError;
+}
+
+lldb::ConnectionStatus
+ConnectionFileDescriptor::ConnectSocket(Socket::SocketProtocol socket_protocol,
+ llvm::StringRef socket_name,
+ Status *error_ptr) {
+ Status error;
+ std::unique_ptr<Socket> socket =
+ Socket::Create(socket_protocol, m_child_processes_inherit, error);
+
+ if (!error.Fail())
+ error = socket->Connect(socket_name);
+
+ if (!error.Fail()) {
+ m_io_sp = std::move(socket);
+ m_uri.assign(socket_name.str());
+ return eConnectionStatusSuccess;
+ }
+
+ if (error_ptr)
+ *error_ptr = error;
+ return eConnectionStatusError;
+}
+
+ConnectionStatus ConnectionFileDescriptor::AcceptNamedSocket(
+ llvm::StringRef socket_name, socket_id_callback_type socket_id_callback,
+ Status *error_ptr) {
+ return AcceptSocket(
+ Socket::ProtocolUnixDomain, socket_name,
+ [socket_id_callback, socket_name](Socket &listening_socket) {
+ socket_id_callback(socket_name);
+ },
+ error_ptr);
+}
+
+ConnectionStatus ConnectionFileDescriptor::ConnectNamedSocket(
+ llvm::StringRef socket_name, socket_id_callback_type socket_id_callback,
+ Status *error_ptr) {
+ return ConnectSocket(Socket::ProtocolUnixDomain, socket_name, error_ptr);
+}
+
+ConnectionStatus ConnectionFileDescriptor::AcceptAbstractSocket(
+ llvm::StringRef socket_name, socket_id_callback_type socket_id_callback,
+ Status *error_ptr) {
+ return AcceptSocket(
+ Socket::ProtocolUnixAbstract, socket_name,
+ [socket_id_callback, socket_name](Socket &listening_socket) {
+ socket_id_callback(socket_name);
+ },
+ error_ptr);
+}
+
+lldb::ConnectionStatus ConnectionFileDescriptor::ConnectAbstractSocket(
+ llvm::StringRef socket_name, socket_id_callback_type socket_id_callback,
+ Status *error_ptr) {
+ return ConnectSocket(Socket::ProtocolUnixAbstract, socket_name, error_ptr);
+}
+
+ConnectionStatus
+ConnectionFileDescriptor::AcceptTCP(llvm::StringRef socket_name,
+ socket_id_callback_type socket_id_callback,
+ Status *error_ptr) {
+ ConnectionStatus ret = AcceptSocket(
+ Socket::ProtocolTcp, socket_name,
+ [socket_id_callback](Socket &listening_socket) {
+ uint16_t port =
+ static_cast<TCPSocket &>(listening_socket).GetLocalPortNumber();
+ socket_id_callback(std::to_string(port));
+ },
+ error_ptr);
+ if (ret == eConnectionStatusSuccess)
+ m_uri.assign(
+ static_cast<TCPSocket *>(m_io_sp.get())->GetRemoteConnectionURI());
+ return ret;
+}
+
+ConnectionStatus
+ConnectionFileDescriptor::ConnectTCP(llvm::StringRef socket_name,
+ socket_id_callback_type socket_id_callback,
+ Status *error_ptr) {
+ return ConnectSocket(Socket::ProtocolTcp, socket_name, error_ptr);
+}
+
+ConnectionStatus
+ConnectionFileDescriptor::ConnectUDP(llvm::StringRef s,
+ socket_id_callback_type socket_id_callback,
+ Status *error_ptr) {
+ if (error_ptr)
+ *error_ptr = Status();
+ llvm::Expected<std::unique_ptr<UDPSocket>> socket =
+ Socket::UdpConnect(s, m_child_processes_inherit);
+ if (!socket) {
+ if (error_ptr)
+ *error_ptr = socket.takeError();
+ else
+ LLDB_LOG_ERROR(GetLog(LLDBLog::Connection), socket.takeError(),
+ "tcp connect failed: {0}");
+ return eConnectionStatusError;
+ }
+ m_io_sp = std::move(*socket);
+ m_uri.assign(std::string(s));
+ return eConnectionStatusSuccess;
+}
+
+ConnectionStatus
+ConnectionFileDescriptor::ConnectFD(llvm::StringRef s,
+ socket_id_callback_type socket_id_callback,
+ Status *error_ptr) {
+#if LLDB_ENABLE_POSIX
+ // Just passing a native file descriptor within this current process that
+ // is already opened (possibly from a service or other source).
+ int fd = -1;
+
+ if (!s.getAsInteger(0, fd)) {
+ // We have what looks to be a valid file descriptor, but we should make
+ // sure it is. We currently are doing this by trying to get the flags
+ // from the file descriptor and making sure it isn't a bad fd.
+ errno = 0;
+ int flags = ::fcntl(fd, F_GETFL, 0);
+ if (flags == -1 || errno == EBADF) {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat("stale file descriptor: %s",
+ s.str().c_str());
+ m_io_sp.reset();
+ return eConnectionStatusError;
+ } else {
+ // Don't take ownership of a file descriptor that gets passed to us
+ // since someone else opened the file descriptor and handed it to us.
+ // TODO: Since are using a URL to open connection we should
+ // eventually parse options using the web standard where we have
+ // "fd://123?opt1=value;opt2=value" and we can have an option be
+ // "owns=1" or "owns=0" or something like this to allow us to specify
+ // this. For now, we assume we must assume we don't own it.
+
+ std::unique_ptr<TCPSocket> tcp_socket;
+ tcp_socket = std::make_unique<TCPSocket>(fd, false, false);
+ // Try and get a socket option from this file descriptor to see if
+ // this is a socket and set m_is_socket accordingly.
+ int resuse;
+ bool is_socket =
+ !!tcp_socket->GetOption(SOL_SOCKET, SO_REUSEADDR, resuse);
+ if (is_socket)
+ m_io_sp = std::move(tcp_socket);
+ else
+ m_io_sp =
+ std::make_shared<NativeFile>(fd, File::eOpenOptionReadWrite, false);
+ m_uri = s.str();
+ return eConnectionStatusSuccess;
+ }
+ }
+
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat("invalid file descriptor: \"%s\"",
+ s.str().c_str());
+ m_io_sp.reset();
+ return eConnectionStatusError;
+#endif // LLDB_ENABLE_POSIX
+ llvm_unreachable("this function should be only called w/ LLDB_ENABLE_POSIX");
+}
+
+ConnectionStatus ConnectionFileDescriptor::ConnectFile(
+ llvm::StringRef s, socket_id_callback_type socket_id_callback,
+ Status *error_ptr) {
+#if LLDB_ENABLE_POSIX
+ std::string addr_str = s.str();
+ // file:///PATH
+ int fd = FileSystem::Instance().Open(addr_str.c_str(), O_RDWR);
+ if (fd == -1) {
+ if (error_ptr)
+ error_ptr->SetErrorToErrno();
+ return eConnectionStatusError;
+ }
+
+ if (::isatty(fd)) {
+ // Set up serial terminal emulation
+ struct termios options;
+ ::tcgetattr(fd, &options);
+
+ // Set port speed to maximum
+ ::cfsetospeed(&options, B115200);
+ ::cfsetispeed(&options, B115200);
+
+ // Raw input, disable echo and signals
+ options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
+
+ // Make sure only one character is needed to return from a read
+ options.c_cc[VMIN] = 1;
+ options.c_cc[VTIME] = 0;
+
+ llvm::sys::RetryAfterSignal(-1, ::tcsetattr, fd, TCSANOW, &options);
+ }
+
+ m_io_sp = std::make_shared<NativeFile>(fd, File::eOpenOptionReadWrite, true);
+ return eConnectionStatusSuccess;
+#endif // LLDB_ENABLE_POSIX
+ llvm_unreachable("this function should be only called w/ LLDB_ENABLE_POSIX");
+}
+
+ConnectionStatus ConnectionFileDescriptor::ConnectSerialPort(
+ llvm::StringRef s, socket_id_callback_type socket_id_callback,
+ Status *error_ptr) {
+#if LLDB_ENABLE_POSIX
+ llvm::StringRef path, qs;
+ // serial:///PATH?k1=v1&k2=v2...
+ std::tie(path, qs) = s.split('?');
+
+ llvm::Expected<SerialPort::Options> serial_options =
+ SerialPort::OptionsFromURL(qs);
+ if (!serial_options) {
+ if (error_ptr)
+ *error_ptr = serial_options.takeError();
+ else
+ llvm::consumeError(serial_options.takeError());
+ return eConnectionStatusError;
+ }
+
+ int fd = FileSystem::Instance().Open(path.str().c_str(), O_RDWR);
+ if (fd == -1) {
+ if (error_ptr)
+ error_ptr->SetErrorToErrno();
+ return eConnectionStatusError;
+ }
+
+ llvm::Expected<std::unique_ptr<SerialPort>> serial_sp = SerialPort::Create(
+ fd, File::eOpenOptionReadWrite, serial_options.get(), true);
+ if (!serial_sp) {
+ if (error_ptr)
+ *error_ptr = serial_sp.takeError();
+ else
+ llvm::consumeError(serial_sp.takeError());
+ return eConnectionStatusError;
+ }
+ m_io_sp = std::move(serial_sp.get());
+
+ return eConnectionStatusSuccess;
+#endif // LLDB_ENABLE_POSIX
+ llvm_unreachable("this function should be only called w/ LLDB_ENABLE_POSIX");
+}
+
+bool ConnectionFileDescriptor::GetChildProcessesInherit() const {
+ return m_child_processes_inherit;
+}
+
+void ConnectionFileDescriptor::SetChildProcessesInherit(
+ bool child_processes_inherit) {
+ m_child_processes_inherit = child_processes_inherit;
+}
+
+void ConnectionFileDescriptor::InitializeSocket(Socket *socket) {
+ m_io_sp.reset(socket);
+ m_uri = socket->GetRemoteConnectionURI();
+}
diff --git a/contrib/llvm-project/lldb/source/Host/posix/DomainSocket.cpp b/contrib/llvm-project/lldb/source/Host/posix/DomainSocket.cpp
new file mode 100644
index 000000000000..9b44c2a8368e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/posix/DomainSocket.cpp
@@ -0,0 +1,157 @@
+//===-- DomainSocket.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/posix/DomainSocket.h"
+
+#include "llvm/Support/Errno.h"
+#include "llvm/Support/FileSystem.h"
+
+#include <cstddef>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+#ifdef __ANDROID__
+// Android does not have SUN_LEN
+#ifndef SUN_LEN
+#define SUN_LEN(ptr) \
+ (offsetof(struct sockaddr_un, sun_path) + strlen((ptr)->sun_path))
+#endif
+#endif // #ifdef __ANDROID__
+
+static const int kDomain = AF_UNIX;
+static const int kType = SOCK_STREAM;
+
+static bool SetSockAddr(llvm::StringRef name, const size_t name_offset,
+ sockaddr_un *saddr_un, socklen_t &saddr_un_len) {
+ if (name.size() + name_offset > sizeof(saddr_un->sun_path))
+ return false;
+
+ memset(saddr_un, 0, sizeof(*saddr_un));
+ saddr_un->sun_family = kDomain;
+
+ memcpy(saddr_un->sun_path + name_offset, name.data(), name.size());
+
+ // For domain sockets we can use SUN_LEN in order to calculate size of
+ // sockaddr_un, but for abstract sockets we have to calculate size manually
+ // because of leading null symbol.
+ if (name_offset == 0)
+ saddr_un_len = SUN_LEN(saddr_un);
+ else
+ saddr_un_len =
+ offsetof(struct sockaddr_un, sun_path) + name_offset + name.size();
+
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
+ defined(__OpenBSD__)
+ saddr_un->sun_len = saddr_un_len;
+#endif
+
+ return true;
+}
+
+DomainSocket::DomainSocket(bool should_close, bool child_processes_inherit)
+ : Socket(ProtocolUnixDomain, should_close, child_processes_inherit) {}
+
+DomainSocket::DomainSocket(SocketProtocol protocol,
+ bool child_processes_inherit)
+ : Socket(protocol, true, child_processes_inherit) {}
+
+DomainSocket::DomainSocket(NativeSocket socket,
+ const DomainSocket &listen_socket)
+ : Socket(ProtocolUnixDomain, listen_socket.m_should_close_fd,
+ listen_socket.m_child_processes_inherit) {
+ m_socket = socket;
+}
+
+Status DomainSocket::Connect(llvm::StringRef name) {
+ sockaddr_un saddr_un;
+ socklen_t saddr_un_len;
+ if (!SetSockAddr(name, GetNameOffset(), &saddr_un, saddr_un_len))
+ return Status("Failed to set socket address");
+
+ Status error;
+ m_socket = CreateSocket(kDomain, kType, 0, m_child_processes_inherit, error);
+ if (error.Fail())
+ return error;
+ if (llvm::sys::RetryAfterSignal(-1, ::connect, GetNativeSocket(),
+ (struct sockaddr *)&saddr_un, saddr_un_len) < 0)
+ SetLastError(error);
+
+ return error;
+}
+
+Status DomainSocket::Listen(llvm::StringRef name, int backlog) {
+ sockaddr_un saddr_un;
+ socklen_t saddr_un_len;
+ if (!SetSockAddr(name, GetNameOffset(), &saddr_un, saddr_un_len))
+ return Status("Failed to set socket address");
+
+ DeleteSocketFile(name);
+
+ Status error;
+ m_socket = CreateSocket(kDomain, kType, 0, m_child_processes_inherit, error);
+ if (error.Fail())
+ return error;
+ if (::bind(GetNativeSocket(), (struct sockaddr *)&saddr_un, saddr_un_len) ==
+ 0)
+ if (::listen(GetNativeSocket(), backlog) == 0)
+ return error;
+
+ SetLastError(error);
+ return error;
+}
+
+Status DomainSocket::Accept(Socket *&socket) {
+ Status error;
+ auto conn_fd = AcceptSocket(GetNativeSocket(), nullptr, nullptr,
+ m_child_processes_inherit, error);
+ if (error.Success())
+ socket = new DomainSocket(conn_fd, *this);
+
+ return error;
+}
+
+size_t DomainSocket::GetNameOffset() const { return 0; }
+
+void DomainSocket::DeleteSocketFile(llvm::StringRef name) {
+ llvm::sys::fs::remove(name);
+}
+
+std::string DomainSocket::GetSocketName() const {
+ if (m_socket == kInvalidSocketValue)
+ return "";
+
+ struct sockaddr_un saddr_un;
+ saddr_un.sun_family = AF_UNIX;
+ socklen_t sock_addr_len = sizeof(struct sockaddr_un);
+ if (::getpeername(m_socket, (struct sockaddr *)&saddr_un, &sock_addr_len) !=
+ 0)
+ return "";
+
+ if (sock_addr_len <= offsetof(struct sockaddr_un, sun_path))
+ return ""; // Unnamed domain socket
+
+ llvm::StringRef name(saddr_un.sun_path + GetNameOffset(),
+ sock_addr_len - offsetof(struct sockaddr_un, sun_path) -
+ GetNameOffset());
+ name = name.rtrim('\0');
+
+ return name.str();
+}
+
+std::string DomainSocket::GetRemoteConnectionURI() const {
+ std::string name = GetSocketName();
+ if (name.empty())
+ return name;
+
+ return llvm::formatv(
+ "{0}://{1}",
+ GetNameOffset() == 0 ? "unix-connect" : "unix-abstract-connect", name);
+}
diff --git a/contrib/llvm-project/lldb/source/Host/posix/FileSystemPosix.cpp b/contrib/llvm-project/lldb/source/Host/posix/FileSystemPosix.cpp
new file mode 100644
index 000000000000..cdb76da626bc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/posix/FileSystemPosix.cpp
@@ -0,0 +1,84 @@
+//===-- FileSystemPosix.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/FileSystem.h"
+
+// C includes
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/mount.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#if defined(__NetBSD__)
+#include <sys/statvfs.h>
+#endif
+
+// lldb Includes
+#include "lldb/Host/Host.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "llvm/Support/Errno.h"
+#include "llvm/Support/FileSystem.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+const char *FileSystem::DEV_NULL = "/dev/null";
+
+Status FileSystem::Symlink(const FileSpec &src, const FileSpec &dst) {
+ Status error;
+ if (::symlink(dst.GetPath().c_str(), src.GetPath().c_str()) == -1)
+ error.SetErrorToErrno();
+ return error;
+}
+
+Status FileSystem::Readlink(const FileSpec &src, FileSpec &dst) {
+ Status error;
+ char buf[PATH_MAX];
+ ssize_t count = ::readlink(src.GetPath().c_str(), buf, sizeof(buf) - 1);
+ if (count < 0)
+ error.SetErrorToErrno();
+ else {
+ buf[count] = '\0'; // Success
+ dst.SetFile(buf, FileSpec::Style::native);
+ }
+ return error;
+}
+
+Status FileSystem::ResolveSymbolicLink(const FileSpec &src, FileSpec &dst) {
+ char resolved_path[PATH_MAX];
+ if (!src.GetPath(resolved_path, sizeof(resolved_path))) {
+ return Status("Couldn't get the canonical path for %s",
+ src.GetPath().c_str());
+ }
+
+ char real_path[PATH_MAX + 1];
+ if (realpath(resolved_path, real_path) == nullptr) {
+ Status err;
+ err.SetErrorToErrno();
+ return err;
+ }
+
+ dst = FileSpec(real_path);
+
+ return Status();
+}
+
+FILE *FileSystem::Fopen(const char *path, const char *mode) {
+ return llvm::sys::RetryAfterSignal(nullptr, ::fopen, path, mode);
+}
+
+int FileSystem::Open(const char *path, int flags, int mode) {
+ // Call ::open in a lambda to avoid overload resolution in RetryAfterSignal
+ // when open is overloaded, such as in Bionic.
+ auto lambda = [&]() { return ::open(path, flags, mode); };
+ return llvm::sys::RetryAfterSignal(-1, lambda);
+}
diff --git a/contrib/llvm-project/lldb/source/Host/posix/HostInfoPosix.cpp b/contrib/llvm-project/lldb/source/Host/posix/HostInfoPosix.cpp
new file mode 100644
index 000000000000..731a7dee2e62
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/posix/HostInfoPosix.cpp
@@ -0,0 +1,159 @@
+//===-- HostInfoPosix.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/posix/HostInfoPosix.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/UserIDResolver.h"
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <climits>
+#include <cstdlib>
+#include <grp.h>
+#include <mutex>
+#include <optional>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+using namespace lldb_private;
+
+size_t HostInfoPosix::GetPageSize() { return ::getpagesize(); }
+
+bool HostInfoPosix::GetHostname(std::string &s) {
+ char hostname[PATH_MAX];
+ hostname[sizeof(hostname) - 1] = '\0';
+ if (::gethostname(hostname, sizeof(hostname) - 1) == 0) {
+ s.assign(hostname);
+ return true;
+ }
+ return false;
+}
+
+std::optional<std::string> HostInfoPosix::GetOSKernelDescription() {
+ struct utsname un;
+ if (uname(&un) < 0)
+ return std::nullopt;
+
+ return std::string(un.version);
+}
+
+#ifdef __ANDROID__
+#include <android/api-level.h>
+#endif
+#if defined(__ANDROID_API__) && __ANDROID_API__ < 21
+#define USE_GETPWUID
+#endif
+
+namespace {
+class PosixUserIDResolver : public UserIDResolver {
+protected:
+ std::optional<std::string> DoGetUserName(id_t uid) override;
+ std::optional<std::string> DoGetGroupName(id_t gid) override;
+};
+} // namespace
+
+struct PasswdEntry {
+ std::string username;
+ std::string shell;
+};
+
+static std::optional<PasswdEntry> GetPassword(id_t uid) {
+#ifdef USE_GETPWUID
+ // getpwuid_r is missing from android-9
+ // The caller should provide some thread safety by making sure no one calls
+ // this function concurrently, because using getpwuid is ultimately not
+ // thread-safe as we don't know who else might be calling it.
+ if (auto *user_info_ptr = ::getpwuid(uid))
+ return PasswdEntry{user_info_ptr->pw_name, user_info_ptr->pw_shell};
+#else
+ struct passwd user_info;
+ struct passwd *user_info_ptr = &user_info;
+ char user_buffer[PATH_MAX];
+ size_t user_buffer_size = sizeof(user_buffer);
+ if (::getpwuid_r(uid, &user_info, user_buffer, user_buffer_size,
+ &user_info_ptr) == 0 &&
+ user_info_ptr) {
+ return PasswdEntry{user_info_ptr->pw_name, user_info_ptr->pw_shell};
+ }
+#endif
+ return std::nullopt;
+}
+
+std::optional<std::string> PosixUserIDResolver::DoGetUserName(id_t uid) {
+ if (std::optional<PasswdEntry> password = GetPassword(uid))
+ return password->username;
+ return std::nullopt;
+}
+
+std::optional<std::string> PosixUserIDResolver::DoGetGroupName(id_t gid) {
+#ifndef __ANDROID__
+ char group_buffer[PATH_MAX];
+ size_t group_buffer_size = sizeof(group_buffer);
+ struct group group_info;
+ struct group *group_info_ptr = &group_info;
+ // Try the threadsafe version first
+ if (::getgrgid_r(gid, &group_info, group_buffer, group_buffer_size,
+ &group_info_ptr) == 0) {
+ if (group_info_ptr)
+ return std::string(group_info_ptr->gr_name);
+ } else {
+ // The threadsafe version isn't currently working for me on darwin, but the
+ // non-threadsafe version is, so I am calling it below.
+ group_info_ptr = ::getgrgid(gid);
+ if (group_info_ptr)
+ return std::string(group_info_ptr->gr_name);
+ }
+#endif
+ return std::nullopt;
+}
+
+static llvm::ManagedStatic<PosixUserIDResolver> g_user_id_resolver;
+
+UserIDResolver &HostInfoPosix::GetUserIDResolver() {
+ return *g_user_id_resolver;
+}
+
+uint32_t HostInfoPosix::GetUserID() { return getuid(); }
+
+uint32_t HostInfoPosix::GetGroupID() { return getgid(); }
+
+uint32_t HostInfoPosix::GetEffectiveUserID() { return geteuid(); }
+
+uint32_t HostInfoPosix::GetEffectiveGroupID() { return getegid(); }
+
+FileSpec HostInfoPosix::GetDefaultShell() {
+ if (const char *v = ::getenv("SHELL"))
+ return FileSpec(v);
+ if (std::optional<PasswdEntry> password = GetPassword(::geteuid()))
+ return FileSpec(password->shell);
+ return FileSpec("/bin/sh");
+}
+
+bool HostInfoPosix::ComputeSupportExeDirectory(FileSpec &file_spec) {
+ return ComputePathRelativeToLibrary(file_spec, "/bin");
+}
+
+bool HostInfoPosix::ComputeHeaderDirectory(FileSpec &file_spec) {
+ FileSpec temp_file("/opt/local/include/lldb");
+ file_spec.SetDirectory(temp_file.GetPath());
+ return true;
+}
+
+bool HostInfoPosix::GetEnvironmentVar(const std::string &var_name,
+ std::string &var) {
+ if (const char *pvar = ::getenv(var_name.c_str())) {
+ var = std::string(pvar);
+ return true;
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/posix/HostProcessPosix.cpp b/contrib/llvm-project/lldb/source/Host/posix/HostProcessPosix.cpp
new file mode 100644
index 000000000000..064b5c7f8e80
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/posix/HostProcessPosix.cpp
@@ -0,0 +1,66 @@
+//===-- HostProcessPosix.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/Host.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/posix/HostProcessPosix.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+#include <climits>
+#include <csignal>
+#include <unistd.h>
+
+using namespace lldb_private;
+
+static const int kInvalidPosixProcess = 0;
+
+HostProcessPosix::HostProcessPosix()
+ : HostNativeProcessBase(kInvalidPosixProcess) {}
+
+HostProcessPosix::HostProcessPosix(lldb::process_t process)
+ : HostNativeProcessBase(process) {}
+
+HostProcessPosix::~HostProcessPosix() = default;
+
+Status HostProcessPosix::Signal(int signo) const {
+ if (m_process == kInvalidPosixProcess) {
+ Status error;
+ error.SetErrorString("HostProcessPosix refers to an invalid process");
+ return error;
+ }
+
+ return HostProcessPosix::Signal(m_process, signo);
+}
+
+Status HostProcessPosix::Signal(lldb::process_t process, int signo) {
+ Status error;
+
+ if (-1 == ::kill(process, signo))
+ error.SetErrorToErrno();
+
+ return error;
+}
+
+Status HostProcessPosix::Terminate() { return Signal(SIGKILL); }
+
+lldb::pid_t HostProcessPosix::GetProcessId() const { return m_process; }
+
+bool HostProcessPosix::IsRunning() const {
+ if (m_process == kInvalidPosixProcess)
+ return false;
+
+ // Send this process the null signal. If it succeeds the process is running.
+ Status error = Signal(0);
+ return error.Success();
+}
+
+llvm::Expected<HostThread> HostProcessPosix::StartMonitoring(
+ const Host::MonitorChildProcessCallback &callback) {
+ return Host::StartMonitoringChildProcess(callback, m_process);
+}
diff --git a/contrib/llvm-project/lldb/source/Host/posix/HostThreadPosix.cpp b/contrib/llvm-project/lldb/source/Host/posix/HostThreadPosix.cpp
new file mode 100644
index 000000000000..4250e07e51cc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/posix/HostThreadPosix.cpp
@@ -0,0 +1,61 @@
+//===-- HostThreadPosix.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/posix/HostThreadPosix.h"
+#include "lldb/Utility/Status.h"
+
+#include <cerrno>
+#include <pthread.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+HostThreadPosix::HostThreadPosix() = default;
+
+HostThreadPosix::HostThreadPosix(lldb::thread_t thread)
+ : HostNativeThreadBase(thread) {}
+
+HostThreadPosix::~HostThreadPosix() = default;
+
+Status HostThreadPosix::Join(lldb::thread_result_t *result) {
+ Status error;
+ if (IsJoinable()) {
+ int err = ::pthread_join(m_thread, result);
+ error.SetError(err, lldb::eErrorTypePOSIX);
+ } else {
+ if (result)
+ *result = nullptr;
+ error.SetError(EINVAL, eErrorTypePOSIX);
+ }
+
+ Reset();
+ return error;
+}
+
+Status HostThreadPosix::Cancel() {
+ Status error;
+ if (IsJoinable()) {
+#ifndef __FreeBSD__
+ llvm_unreachable("someone is calling HostThread::Cancel()");
+#else
+ int err = ::pthread_cancel(m_thread);
+ error.SetError(err, eErrorTypePOSIX);
+#endif
+ }
+ return error;
+}
+
+Status HostThreadPosix::Detach() {
+ Status error;
+ if (IsJoinable()) {
+ int err = ::pthread_detach(m_thread);
+ error.SetError(err, eErrorTypePOSIX);
+ }
+ Reset();
+ return error;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/posix/LockFilePosix.cpp b/contrib/llvm-project/lldb/source/Host/posix/LockFilePosix.cpp
new file mode 100644
index 000000000000..cb9ca5c29e5f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/posix/LockFilePosix.cpp
@@ -0,0 +1,58 @@
+//===-- LockFilePosix.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/posix/LockFilePosix.h"
+
+#include "llvm/Support/Errno.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+static Status fileLock(int fd, int cmd, int lock_type, const uint64_t start,
+ const uint64_t len) {
+ struct flock fl;
+
+ fl.l_type = lock_type;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = start;
+ fl.l_len = len;
+ fl.l_pid = ::getpid();
+
+ Status error;
+ if (llvm::sys::RetryAfterSignal(-1, ::fcntl, fd, cmd, &fl) == -1)
+ error.SetErrorToErrno();
+
+ return error;
+}
+
+LockFilePosix::LockFilePosix(int fd) : LockFileBase(fd) {}
+
+LockFilePosix::~LockFilePosix() { Unlock(); }
+
+Status LockFilePosix::DoWriteLock(const uint64_t start, const uint64_t len) {
+ return fileLock(m_fd, F_SETLKW, F_WRLCK, start, len);
+}
+
+Status LockFilePosix::DoTryWriteLock(const uint64_t start, const uint64_t len) {
+ return fileLock(m_fd, F_SETLK, F_WRLCK, start, len);
+}
+
+Status LockFilePosix::DoReadLock(const uint64_t start, const uint64_t len) {
+ return fileLock(m_fd, F_SETLKW, F_RDLCK, start, len);
+}
+
+Status LockFilePosix::DoTryReadLock(const uint64_t start, const uint64_t len) {
+ return fileLock(m_fd, F_SETLK, F_RDLCK, start, len);
+}
+
+Status LockFilePosix::DoUnlock() {
+ return fileLock(m_fd, F_SETLK, F_UNLCK, m_start, m_len);
+}
diff --git a/contrib/llvm-project/lldb/source/Host/posix/MainLoopPosix.cpp b/contrib/llvm-project/lldb/source/Host/posix/MainLoopPosix.cpp
new file mode 100644
index 000000000000..5fe4d015251c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/posix/MainLoopPosix.cpp
@@ -0,0 +1,411 @@
+//===-- MainLoopPosix.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/posix/MainLoopPosix.h"
+#include "lldb/Host/Config.h"
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Utility/Status.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/Support/Errno.h"
+#include <algorithm>
+#include <cassert>
+#include <cerrno>
+#include <csignal>
+#include <ctime>
+#include <vector>
+
+// Multiplexing is implemented using kqueue on systems that support it (BSD
+// variants including OSX). On linux we use ppoll, while android uses pselect
+// (ppoll is present but not implemented properly). On windows we use WSApoll
+// (which does not support signals).
+
+#if HAVE_SYS_EVENT_H
+#include <sys/event.h>
+#elif defined(__ANDROID__)
+#include <sys/syscall.h>
+#else
+#include <poll.h>
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+
+static sig_atomic_t g_signal_flags[NSIG];
+
+static void SignalHandler(int signo, siginfo_t *info, void *) {
+ assert(signo < NSIG);
+ g_signal_flags[signo] = 1;
+}
+
+class MainLoopPosix::RunImpl {
+public:
+ RunImpl(MainLoopPosix &loop);
+ ~RunImpl() = default;
+
+ Status Poll();
+ void ProcessEvents();
+
+private:
+ MainLoopPosix &loop;
+
+#if HAVE_SYS_EVENT_H
+ std::vector<struct kevent> in_events;
+ struct kevent out_events[4];
+ int num_events = -1;
+
+#else
+#ifdef __ANDROID__
+ fd_set read_fd_set;
+#else
+ std::vector<struct pollfd> read_fds;
+#endif
+
+ sigset_t get_sigmask();
+#endif
+};
+
+#if HAVE_SYS_EVENT_H
+MainLoopPosix::RunImpl::RunImpl(MainLoopPosix &loop) : loop(loop) {
+ in_events.reserve(loop.m_read_fds.size());
+}
+
+Status MainLoopPosix::RunImpl::Poll() {
+ in_events.resize(loop.m_read_fds.size());
+ unsigned i = 0;
+ for (auto &fd : loop.m_read_fds)
+ EV_SET(&in_events[i++], fd.first, EVFILT_READ, EV_ADD, 0, 0, 0);
+
+ num_events = kevent(loop.m_kqueue, in_events.data(), in_events.size(),
+ out_events, std::size(out_events), nullptr);
+
+ if (num_events < 0) {
+ if (errno == EINTR) {
+ // in case of EINTR, let the main loop run one iteration
+ // we need to zero num_events to avoid assertions failing
+ num_events = 0;
+ } else
+ return Status(errno, eErrorTypePOSIX);
+ }
+ return Status();
+}
+
+void MainLoopPosix::RunImpl::ProcessEvents() {
+ assert(num_events >= 0);
+ for (int i = 0; i < num_events; ++i) {
+ if (loop.m_terminate_request)
+ return;
+ switch (out_events[i].filter) {
+ case EVFILT_READ:
+ loop.ProcessReadObject(out_events[i].ident);
+ break;
+ case EVFILT_SIGNAL:
+ loop.ProcessSignal(out_events[i].ident);
+ break;
+ default:
+ llvm_unreachable("Unknown event");
+ }
+ }
+}
+#else
+MainLoopPosix::RunImpl::RunImpl(MainLoopPosix &loop) : loop(loop) {
+#ifndef __ANDROID__
+ read_fds.reserve(loop.m_read_fds.size());
+#endif
+}
+
+sigset_t MainLoopPosix::RunImpl::get_sigmask() {
+ sigset_t sigmask;
+ int ret = pthread_sigmask(SIG_SETMASK, nullptr, &sigmask);
+ assert(ret == 0);
+ UNUSED_IF_ASSERT_DISABLED(ret);
+
+ for (const auto &sig : loop.m_signals)
+ sigdelset(&sigmask, sig.first);
+ return sigmask;
+}
+
+#ifdef __ANDROID__
+Status MainLoopPosix::RunImpl::Poll() {
+ // ppoll(2) is not supported on older all android versions. Also, older
+ // versions android (API <= 19) implemented pselect in a non-atomic way, as a
+ // combination of pthread_sigmask and select. This is not sufficient for us,
+ // as we rely on the atomicity to correctly implement signal polling, so we
+ // call the underlying syscall ourselves.
+
+ FD_ZERO(&read_fd_set);
+ int nfds = 0;
+ for (const auto &fd : loop.m_read_fds) {
+ FD_SET(fd.first, &read_fd_set);
+ nfds = std::max(nfds, fd.first + 1);
+ }
+
+ union {
+ sigset_t set;
+ uint64_t pad;
+ } kernel_sigset;
+ memset(&kernel_sigset, 0, sizeof(kernel_sigset));
+ kernel_sigset.set = get_sigmask();
+
+ struct {
+ void *sigset_ptr;
+ size_t sigset_len;
+ } extra_data = {&kernel_sigset, sizeof(kernel_sigset)};
+ if (syscall(__NR_pselect6, nfds, &read_fd_set, nullptr, nullptr, nullptr,
+ &extra_data) == -1) {
+ if (errno != EINTR)
+ return Status(errno, eErrorTypePOSIX);
+ else
+ FD_ZERO(&read_fd_set);
+ }
+
+ return Status();
+}
+#else
+Status MainLoopPosix::RunImpl::Poll() {
+ read_fds.clear();
+
+ sigset_t sigmask = get_sigmask();
+
+ for (const auto &fd : loop.m_read_fds) {
+ struct pollfd pfd;
+ pfd.fd = fd.first;
+ pfd.events = POLLIN;
+ pfd.revents = 0;
+ read_fds.push_back(pfd);
+ }
+
+ if (ppoll(read_fds.data(), read_fds.size(), nullptr, &sigmask) == -1 &&
+ errno != EINTR)
+ return Status(errno, eErrorTypePOSIX);
+
+ return Status();
+}
+#endif
+
+void MainLoopPosix::RunImpl::ProcessEvents() {
+#ifdef __ANDROID__
+ // Collect first all readable file descriptors into a separate vector and
+ // then iterate over it to invoke callbacks. Iterating directly over
+ // loop.m_read_fds is not possible because the callbacks can modify the
+ // container which could invalidate the iterator.
+ std::vector<IOObject::WaitableHandle> fds;
+ for (const auto &fd : loop.m_read_fds)
+ if (FD_ISSET(fd.first, &read_fd_set))
+ fds.push_back(fd.first);
+
+ for (const auto &handle : fds) {
+#else
+ for (const auto &fd : read_fds) {
+ if ((fd.revents & (POLLIN | POLLHUP)) == 0)
+ continue;
+ IOObject::WaitableHandle handle = fd.fd;
+#endif
+ if (loop.m_terminate_request)
+ return;
+
+ loop.ProcessReadObject(handle);
+ }
+
+ std::vector<int> signals;
+ for (const auto &entry : loop.m_signals)
+ if (g_signal_flags[entry.first] != 0)
+ signals.push_back(entry.first);
+
+ for (const auto &signal : signals) {
+ if (loop.m_terminate_request)
+ return;
+ g_signal_flags[signal] = 0;
+ loop.ProcessSignal(signal);
+ }
+}
+#endif
+
+MainLoopPosix::MainLoopPosix() : m_triggering(false) {
+ Status error = m_trigger_pipe.CreateNew(/*child_process_inherit=*/false);
+ assert(error.Success());
+ const int trigger_pipe_fd = m_trigger_pipe.GetReadFileDescriptor();
+ m_read_fds.insert({trigger_pipe_fd, [trigger_pipe_fd](MainLoopBase &loop) {
+ char c;
+ ssize_t bytes_read = llvm::sys::RetryAfterSignal(
+ -1, ::read, trigger_pipe_fd, &c, 1);
+ assert(bytes_read == 1);
+ UNUSED_IF_ASSERT_DISABLED(bytes_read);
+ // NB: This implicitly causes another loop iteration
+ // and therefore the execution of pending callbacks.
+ }});
+#if HAVE_SYS_EVENT_H
+ m_kqueue = kqueue();
+ assert(m_kqueue >= 0);
+#endif
+}
+
+MainLoopPosix::~MainLoopPosix() {
+#if HAVE_SYS_EVENT_H
+ close(m_kqueue);
+#endif
+ m_read_fds.erase(m_trigger_pipe.GetReadFileDescriptor());
+ m_trigger_pipe.Close();
+ assert(m_read_fds.size() == 0);
+ assert(m_signals.size() == 0);
+}
+
+MainLoopPosix::ReadHandleUP
+MainLoopPosix::RegisterReadObject(const IOObjectSP &object_sp,
+ const Callback &callback, Status &error) {
+ if (!object_sp || !object_sp->IsValid()) {
+ error.SetErrorString("IO object is not valid.");
+ return nullptr;
+ }
+
+ const bool inserted =
+ m_read_fds.insert({object_sp->GetWaitableHandle(), callback}).second;
+ if (!inserted) {
+ error.SetErrorStringWithFormat("File descriptor %d already monitored.",
+ object_sp->GetWaitableHandle());
+ return nullptr;
+ }
+
+ return CreateReadHandle(object_sp);
+}
+
+// We shall block the signal, then install the signal handler. The signal will
+// be unblocked in the Run() function to check for signal delivery.
+MainLoopPosix::SignalHandleUP
+MainLoopPosix::RegisterSignal(int signo, const Callback &callback,
+ Status &error) {
+ auto signal_it = m_signals.find(signo);
+ if (signal_it != m_signals.end()) {
+ auto callback_it = signal_it->second.callbacks.insert(
+ signal_it->second.callbacks.end(), callback);
+ return SignalHandleUP(new SignalHandle(*this, signo, callback_it));
+ }
+
+ SignalInfo info;
+ info.callbacks.push_back(callback);
+ struct sigaction new_action;
+ new_action.sa_sigaction = &SignalHandler;
+ new_action.sa_flags = SA_SIGINFO;
+ sigemptyset(&new_action.sa_mask);
+ sigaddset(&new_action.sa_mask, signo);
+ sigset_t old_set;
+
+ g_signal_flags[signo] = 0;
+
+ // Even if using kqueue, the signal handler will still be invoked, so it's
+ // important to replace it with our "benign" handler.
+ int ret = sigaction(signo, &new_action, &info.old_action);
+ 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(HAVE_SYS_EVENT_H ? SIG_UNBLOCK : SIG_BLOCK,
+ &new_action.sa_mask, &old_set);
+ assert(ret == 0 && "pthread_sigmask failed");
+ info.was_blocked = sigismember(&old_set, signo);
+ auto insert_ret = m_signals.insert({signo, info});
+
+ return SignalHandleUP(new SignalHandle(
+ *this, signo, insert_ret.first->second.callbacks.begin()));
+}
+
+void MainLoopPosix::UnregisterReadObject(IOObject::WaitableHandle handle) {
+ bool erased = m_read_fds.erase(handle);
+ UNUSED_IF_ASSERT_DISABLED(erased);
+ assert(erased);
+}
+
+void MainLoopPosix::UnregisterSignal(
+ int signo, std::list<Callback>::iterator callback_it) {
+ auto it = m_signals.find(signo);
+ assert(it != m_signals.end());
+
+ it->second.callbacks.erase(callback_it);
+ // Do not remove the signal handler unless all callbacks have been erased.
+ if (!it->second.callbacks.empty())
+ return;
+
+ sigaction(signo, &it->second.old_action, nullptr);
+
+ sigset_t set;
+ sigemptyset(&set);
+ sigaddset(&set, signo);
+ int ret = pthread_sigmask(it->second.was_blocked ? SIG_BLOCK : SIG_UNBLOCK,
+ &set, nullptr);
+ assert(ret == 0);
+ UNUSED_IF_ASSERT_DISABLED(ret);
+
+#if HAVE_SYS_EVENT_H
+ struct kevent ev;
+ EV_SET(&ev, signo, EVFILT_SIGNAL, EV_DELETE, 0, 0, 0);
+ ret = kevent(m_kqueue, &ev, 1, nullptr, 0, nullptr);
+ assert(ret == 0);
+#endif
+
+ m_signals.erase(it);
+}
+
+Status MainLoopPosix::Run() {
+ m_terminate_request = false;
+
+ Status error;
+ RunImpl impl(*this);
+
+ // run until termination or until we run out of things to listen to
+ // (m_read_fds will always contain m_trigger_pipe fd, so check for > 1)
+ while (!m_terminate_request &&
+ (m_read_fds.size() > 1 || !m_signals.empty())) {
+ error = impl.Poll();
+ if (error.Fail())
+ return error;
+
+ impl.ProcessEvents();
+
+ m_triggering = false;
+ ProcessPendingCallbacks();
+ }
+ return Status();
+}
+
+void MainLoopPosix::ProcessReadObject(IOObject::WaitableHandle handle) {
+ auto it = m_read_fds.find(handle);
+ if (it != m_read_fds.end())
+ it->second(*this); // Do the work
+}
+
+void MainLoopPosix::ProcessSignal(int signo) {
+ auto it = m_signals.find(signo);
+ if (it != m_signals.end()) {
+ // The callback may actually register/unregister signal handlers,
+ // so we need to create a copy first.
+ llvm::SmallVector<Callback, 4> callbacks_to_run{
+ it->second.callbacks.begin(), it->second.callbacks.end()};
+ for (auto &x : callbacks_to_run)
+ x(*this); // Do the work
+ }
+}
+
+void MainLoopPosix::TriggerPendingCallbacks() {
+ if (m_triggering.exchange(true))
+ return;
+
+ char c = '.';
+ size_t bytes_written;
+ Status error = m_trigger_pipe.Write(&c, 1, bytes_written);
+ assert(error.Success());
+ UNUSED_IF_ASSERT_DISABLED(error);
+ assert(bytes_written == 1);
+}
diff --git a/contrib/llvm-project/lldb/source/Host/posix/PipePosix.cpp b/contrib/llvm-project/lldb/source/Host/posix/PipePosix.cpp
new file mode 100644
index 000000000000..f35c348990df
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/posix/PipePosix.cpp
@@ -0,0 +1,367 @@
+//===-- PipePosix.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/posix/PipePosix.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Utility/SelectHelper.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Errno.h"
+#include <functional>
+#include <thread>
+
+#include <cerrno>
+#include <climits>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+int PipePosix::kInvalidDescriptor = -1;
+
+enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE
+
+// pipe2 is supported by a limited set of platforms
+// TODO: Add more platforms that support pipe2.
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
+ defined(__OpenBSD__)
+#define PIPE2_SUPPORTED 1
+#else
+#define PIPE2_SUPPORTED 0
+#endif
+
+static constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100;
+
+#if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED
+static bool SetCloexecFlag(int fd) {
+ int flags = ::fcntl(fd, F_GETFD);
+ if (flags == -1)
+ return false;
+ return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0);
+}
+#endif
+
+static std::chrono::time_point<std::chrono::steady_clock> Now() {
+ return std::chrono::steady_clock::now();
+}
+
+PipePosix::PipePosix()
+ : m_fds{PipePosix::kInvalidDescriptor, PipePosix::kInvalidDescriptor} {}
+
+PipePosix::PipePosix(lldb::pipe_t read, lldb::pipe_t write)
+ : m_fds{read, write} {}
+
+PipePosix::PipePosix(PipePosix &&pipe_posix)
+ : PipeBase{std::move(pipe_posix)},
+ m_fds{pipe_posix.ReleaseReadFileDescriptor(),
+ pipe_posix.ReleaseWriteFileDescriptor()} {}
+
+PipePosix &PipePosix::operator=(PipePosix &&pipe_posix) {
+ std::scoped_lock<std::mutex, std::mutex, std::mutex, std::mutex> guard(
+ m_read_mutex, m_write_mutex, pipe_posix.m_read_mutex,
+ pipe_posix.m_write_mutex);
+
+ PipeBase::operator=(std::move(pipe_posix));
+ m_fds[READ] = pipe_posix.ReleaseReadFileDescriptorUnlocked();
+ m_fds[WRITE] = pipe_posix.ReleaseWriteFileDescriptorUnlocked();
+ return *this;
+}
+
+PipePosix::~PipePosix() { Close(); }
+
+Status PipePosix::CreateNew(bool child_processes_inherit) {
+ std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);
+ if (CanReadUnlocked() || CanWriteUnlocked())
+ return Status(EINVAL, eErrorTypePOSIX);
+
+ Status error;
+#if PIPE2_SUPPORTED
+ if (::pipe2(m_fds, (child_processes_inherit) ? 0 : O_CLOEXEC) == 0)
+ return error;
+#else
+ if (::pipe(m_fds) == 0) {
+#ifdef FD_CLOEXEC
+ if (!child_processes_inherit) {
+ if (!SetCloexecFlag(m_fds[0]) || !SetCloexecFlag(m_fds[1])) {
+ error.SetErrorToErrno();
+ CloseUnlocked();
+ return error;
+ }
+ }
+#endif
+ return error;
+ }
+#endif
+
+ error.SetErrorToErrno();
+ m_fds[READ] = PipePosix::kInvalidDescriptor;
+ m_fds[WRITE] = PipePosix::kInvalidDescriptor;
+ return error;
+}
+
+Status PipePosix::CreateNew(llvm::StringRef name, bool child_process_inherit) {
+ std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);
+ if (CanReadUnlocked() || CanWriteUnlocked())
+ return Status("Pipe is already opened");
+
+ Status error;
+ if (::mkfifo(name.str().c_str(), 0660) != 0)
+ error.SetErrorToErrno();
+
+ return error;
+}
+
+Status PipePosix::CreateWithUniqueName(llvm::StringRef prefix,
+ bool child_process_inherit,
+ llvm::SmallVectorImpl<char> &name) {
+ llvm::SmallString<128> named_pipe_path;
+ llvm::SmallString<128> pipe_spec((prefix + ".%%%%%%").str());
+ FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir();
+ if (!tmpdir_file_spec)
+ tmpdir_file_spec.AppendPathComponent("/tmp");
+ tmpdir_file_spec.AppendPathComponent(pipe_spec);
+
+ // It's possible that another process creates the target path after we've
+ // verified it's available but before we create it, in which case we should
+ // try again.
+ Status error;
+ do {
+ llvm::sys::fs::createUniquePath(tmpdir_file_spec.GetPath(), named_pipe_path,
+ /*MakeAbsolute=*/false);
+ error = CreateNew(named_pipe_path, child_process_inherit);
+ } while (error.GetError() == EEXIST);
+
+ if (error.Success())
+ name = named_pipe_path;
+ return error;
+}
+
+Status PipePosix::OpenAsReader(llvm::StringRef name,
+ bool child_process_inherit) {
+ std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);
+
+ if (CanReadUnlocked() || CanWriteUnlocked())
+ return Status("Pipe is already opened");
+
+ int flags = O_RDONLY | O_NONBLOCK;
+ if (!child_process_inherit)
+ flags |= O_CLOEXEC;
+
+ Status error;
+ int fd = FileSystem::Instance().Open(name.str().c_str(), flags);
+ if (fd != -1)
+ m_fds[READ] = fd;
+ else
+ error.SetErrorToErrno();
+
+ return error;
+}
+
+Status
+PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name,
+ bool child_process_inherit,
+ const std::chrono::microseconds &timeout) {
+ std::lock_guard<std::mutex> guard(m_write_mutex);
+ if (CanReadUnlocked() || CanWriteUnlocked())
+ return Status("Pipe is already opened");
+
+ int flags = O_WRONLY | O_NONBLOCK;
+ if (!child_process_inherit)
+ flags |= O_CLOEXEC;
+
+ using namespace std::chrono;
+ const auto finish_time = Now() + timeout;
+
+ while (!CanWriteUnlocked()) {
+ if (timeout != microseconds::zero()) {
+ const auto dur = duration_cast<microseconds>(finish_time - Now()).count();
+ if (dur <= 0)
+ return Status("timeout exceeded - reader hasn't opened so far");
+ }
+
+ errno = 0;
+ int fd = ::open(name.str().c_str(), flags);
+ if (fd == -1) {
+ const auto errno_copy = errno;
+ // We may get ENXIO if a reader side of the pipe hasn't opened yet.
+ if (errno_copy != ENXIO && errno_copy != EINTR)
+ return Status(errno_copy, eErrorTypePOSIX);
+
+ std::this_thread::sleep_for(
+ milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS));
+ } else {
+ m_fds[WRITE] = fd;
+ }
+ }
+
+ return Status();
+}
+
+int PipePosix::GetReadFileDescriptor() const {
+ std::lock_guard<std::mutex> guard(m_read_mutex);
+ return GetReadFileDescriptorUnlocked();
+}
+
+int PipePosix::GetReadFileDescriptorUnlocked() const {
+ return m_fds[READ];
+}
+
+int PipePosix::GetWriteFileDescriptor() const {
+ std::lock_guard<std::mutex> guard(m_write_mutex);
+ return GetWriteFileDescriptorUnlocked();
+}
+
+int PipePosix::GetWriteFileDescriptorUnlocked() const {
+ return m_fds[WRITE];
+}
+
+int PipePosix::ReleaseReadFileDescriptor() {
+ std::lock_guard<std::mutex> guard(m_read_mutex);
+ return ReleaseReadFileDescriptorUnlocked();
+}
+
+int PipePosix::ReleaseReadFileDescriptorUnlocked() {
+ const int fd = m_fds[READ];
+ m_fds[READ] = PipePosix::kInvalidDescriptor;
+ return fd;
+}
+
+int PipePosix::ReleaseWriteFileDescriptor() {
+ std::lock_guard<std::mutex> guard(m_write_mutex);
+ return ReleaseWriteFileDescriptorUnlocked();
+}
+
+int PipePosix::ReleaseWriteFileDescriptorUnlocked() {
+ const int fd = m_fds[WRITE];
+ m_fds[WRITE] = PipePosix::kInvalidDescriptor;
+ return fd;
+}
+
+void PipePosix::Close() {
+ std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);
+ CloseUnlocked();
+}
+
+void PipePosix::CloseUnlocked() {
+ CloseReadFileDescriptorUnlocked();
+ CloseWriteFileDescriptorUnlocked();
+}
+
+Status PipePosix::Delete(llvm::StringRef name) {
+ return llvm::sys::fs::remove(name);
+}
+
+bool PipePosix::CanRead() const {
+ std::lock_guard<std::mutex> guard(m_read_mutex);
+ return CanReadUnlocked();
+}
+
+bool PipePosix::CanReadUnlocked() const {
+ return m_fds[READ] != PipePosix::kInvalidDescriptor;
+}
+
+bool PipePosix::CanWrite() const {
+ std::lock_guard<std::mutex> guard(m_write_mutex);
+ return CanWriteUnlocked();
+}
+
+bool PipePosix::CanWriteUnlocked() const {
+ return m_fds[WRITE] != PipePosix::kInvalidDescriptor;
+}
+
+void PipePosix::CloseReadFileDescriptor() {
+ std::lock_guard<std::mutex> guard(m_read_mutex);
+ CloseReadFileDescriptorUnlocked();
+}
+void PipePosix::CloseReadFileDescriptorUnlocked() {
+ if (CanReadUnlocked()) {
+ close(m_fds[READ]);
+ m_fds[READ] = PipePosix::kInvalidDescriptor;
+ }
+}
+
+void PipePosix::CloseWriteFileDescriptor() {
+ std::lock_guard<std::mutex> guard(m_write_mutex);
+ CloseWriteFileDescriptorUnlocked();
+}
+
+void PipePosix::CloseWriteFileDescriptorUnlocked() {
+ if (CanWriteUnlocked()) {
+ close(m_fds[WRITE]);
+ m_fds[WRITE] = PipePosix::kInvalidDescriptor;
+ }
+}
+
+Status PipePosix::ReadWithTimeout(void *buf, size_t size,
+ const std::chrono::microseconds &timeout,
+ size_t &bytes_read) {
+ std::lock_guard<std::mutex> guard(m_read_mutex);
+ bytes_read = 0;
+ if (!CanReadUnlocked())
+ return Status(EINVAL, eErrorTypePOSIX);
+
+ const int fd = GetReadFileDescriptorUnlocked();
+
+ SelectHelper select_helper;
+ select_helper.SetTimeout(timeout);
+ select_helper.FDSetRead(fd);
+
+ Status error;
+ while (error.Success()) {
+ error = select_helper.Select();
+ if (error.Success()) {
+ auto result =
+ ::read(fd, static_cast<char *>(buf) + bytes_read, size - bytes_read);
+ if (result != -1) {
+ bytes_read += result;
+ if (bytes_read == size || result == 0)
+ break;
+ } else if (errno == EINTR) {
+ continue;
+ } else {
+ error.SetErrorToErrno();
+ break;
+ }
+ }
+ }
+ return error;
+}
+
+Status PipePosix::Write(const void *buf, size_t size, size_t &bytes_written) {
+ std::lock_guard<std::mutex> guard(m_write_mutex);
+ bytes_written = 0;
+ if (!CanWriteUnlocked())
+ return Status(EINVAL, eErrorTypePOSIX);
+
+ const int fd = GetWriteFileDescriptorUnlocked();
+ SelectHelper select_helper;
+ select_helper.SetTimeout(std::chrono::seconds(0));
+ select_helper.FDSetWrite(fd);
+
+ Status error;
+ while (error.Success()) {
+ error = select_helper.Select();
+ if (error.Success()) {
+ auto result = ::write(fd, static_cast<const char *>(buf) + bytes_written,
+ size - bytes_written);
+ if (result != -1) {
+ bytes_written += result;
+ if (bytes_written == size)
+ break;
+ } else if (errno == EINTR) {
+ continue;
+ } else {
+ error.SetErrorToErrno();
+ }
+ }
+ }
+ return error;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp b/contrib/llvm-project/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp
new file mode 100644
index 000000000000..0a832ebad13a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp
@@ -0,0 +1,305 @@
+//===-- ProcessLauncherPosixFork.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/posix/ProcessLauncherPosixFork.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostProcess.h"
+#include "lldb/Host/Pipe.h"
+#include "lldb/Host/ProcessLaunchInfo.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "llvm/Support/Errno.h"
+
+#include <climits>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <sstream>
+#include <csignal>
+
+#ifdef __ANDROID__
+#include <android/api-level.h>
+#define PT_TRACE_ME PTRACE_TRACEME
+#endif
+
+#if defined(__ANDROID_API__) && __ANDROID_API__ < 15
+#include <linux/personality.h>
+#elif defined(__linux__)
+#include <sys/personality.h>
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Begin code running in the child process
+// NB: This code needs to be async-signal safe, since we're invoking fork from
+// multithreaded contexts.
+
+static void write_string(int error_fd, const char *str) {
+ int r = write(error_fd, str, strlen(str));
+ (void)r;
+}
+
+[[noreturn]] static void ExitWithError(int error_fd,
+ const char *operation) {
+ int err = errno;
+ write_string(error_fd, operation);
+ write_string(error_fd, " failed: ");
+ // strerror is not guaranteed to be async-signal safe, but it usually is.
+ write_string(error_fd, strerror(err));
+ _exit(1);
+}
+
+static void DisableASLR(int error_fd) {
+#if defined(__linux__)
+ const unsigned long personality_get_current = 0xffffffff;
+ int value = personality(personality_get_current);
+ if (value == -1)
+ ExitWithError(error_fd, "personality get");
+
+ value = personality(ADDR_NO_RANDOMIZE | value);
+ if (value == -1)
+ ExitWithError(error_fd, "personality set");
+#endif
+}
+
+static void DupDescriptor(int error_fd, const char *file, int fd, int flags) {
+ int target_fd = FileSystem::Instance().Open(file, flags, 0666);
+
+ if (target_fd == -1)
+ ExitWithError(error_fd, "DupDescriptor-open");
+
+ if (target_fd == fd)
+ return;
+
+ if (::dup2(target_fd, fd) == -1)
+ ExitWithError(error_fd, "DupDescriptor-dup2");
+
+ ::close(target_fd);
+}
+
+namespace {
+struct ForkFileAction {
+ ForkFileAction(const FileAction &act);
+
+ FileAction::Action action;
+ int fd;
+ std::string path;
+ int arg;
+};
+
+struct ForkLaunchInfo {
+ ForkLaunchInfo(const ProcessLaunchInfo &info);
+
+ bool separate_process_group;
+ bool debug;
+ bool disable_aslr;
+ std::string wd;
+ const char **argv;
+ Environment::Envp envp;
+ std::vector<ForkFileAction> actions;
+
+ bool has_action(int fd) const {
+ for (const ForkFileAction &action : actions) {
+ if (action.fd == fd)
+ return true;
+ }
+ return false;
+ }
+};
+} // namespace
+
+[[noreturn]] static void ChildFunc(int error_fd, const ForkLaunchInfo &info) {
+ if (info.separate_process_group) {
+ if (setpgid(0, 0) != 0)
+ ExitWithError(error_fd, "setpgid");
+ }
+
+ for (const ForkFileAction &action : info.actions) {
+ switch (action.action) {
+ case FileAction::eFileActionClose:
+ if (close(action.fd) != 0)
+ ExitWithError(error_fd, "close");
+ break;
+ case FileAction::eFileActionDuplicate:
+ if (dup2(action.fd, action.arg) == -1)
+ ExitWithError(error_fd, "dup2");
+ break;
+ case FileAction::eFileActionOpen:
+ DupDescriptor(error_fd, action.path.c_str(), action.fd, action.arg);
+ break;
+ case FileAction::eFileActionNone:
+ break;
+ }
+ }
+
+ // Change working directory
+ if (!info.wd.empty() && 0 != ::chdir(info.wd.c_str()))
+ ExitWithError(error_fd, "chdir");
+
+ if (info.disable_aslr)
+ DisableASLR(error_fd);
+
+ // Clear the signal mask to prevent the child from being affected by any
+ // masking done by the parent.
+ sigset_t set;
+ if (sigemptyset(&set) != 0 ||
+ pthread_sigmask(SIG_SETMASK, &set, nullptr) != 0)
+ ExitWithError(error_fd, "pthread_sigmask");
+
+ if (info.debug) {
+ // Do not inherit setgid powers.
+ if (setgid(getgid()) != 0)
+ ExitWithError(error_fd, "setgid");
+
+ // HACK:
+ // Close everything besides stdin, stdout, and stderr that has no file
+ // action to avoid leaking. Only do this when debugging, as elsewhere we
+ // actually rely on passing open descriptors to child processes.
+ // NB: This code is not async-signal safe, but we currently do not launch
+ // processes for debugging from within multithreaded contexts.
+
+ const llvm::StringRef proc_fd_path = "/proc/self/fd";
+ std::error_code ec;
+ bool result;
+ ec = llvm::sys::fs::is_directory(proc_fd_path, result);
+ if (result) {
+ std::vector<int> files_to_close;
+ // Directory iterator doesn't ensure any sequence.
+ for (llvm::sys::fs::directory_iterator iter(proc_fd_path, ec), file_end;
+ iter != file_end && !ec; iter.increment(ec)) {
+ int fd = std::stoi(iter->path().substr(proc_fd_path.size() + 1));
+
+ // Don't close first three entries since they are stdin, stdout and
+ // stderr.
+ if (fd > 2 && !info.has_action(fd) && fd != error_fd)
+ files_to_close.push_back(fd);
+ }
+ for (int file_to_close : files_to_close)
+ close(file_to_close);
+ } else {
+ // Since /proc/self/fd didn't work, trying the slow way instead.
+ int max_fd = sysconf(_SC_OPEN_MAX);
+ for (int fd = 3; fd < max_fd; ++fd)
+ if (!info.has_action(fd) && fd != error_fd)
+ close(fd);
+ }
+
+ // Start tracing this child that is about to exec.
+ if (ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1)
+ ExitWithError(error_fd, "ptrace");
+ }
+
+ // Execute. We should never return...
+ execve(info.argv[0], const_cast<char *const *>(info.argv), info.envp);
+
+#if defined(__linux__)
+ if (errno == ETXTBSY) {
+ // On android M and earlier we can get this error because the adb daemon
+ // can hold a write handle on the executable even after it has finished
+ // uploading it. This state lasts only a short time and happens only when
+ // there are many concurrent adb commands being issued, such as when
+ // running the test suite. (The file remains open when someone does an "adb
+ // shell" command in the fork() child before it has had a chance to exec.)
+ // Since this state should clear up quickly, wait a while and then give it
+ // one more go.
+ usleep(50000);
+ execve(info.argv[0], const_cast<char *const *>(info.argv), info.envp);
+ }
+#endif
+
+ // ...unless exec fails. In which case we definitely need to end the child
+ // here.
+ ExitWithError(error_fd, "execve");
+}
+
+// End of code running in the child process.
+
+ForkFileAction::ForkFileAction(const FileAction &act)
+ : action(act.GetAction()), fd(act.GetFD()), path(act.GetPath().str()),
+ arg(act.GetActionArgument()) {}
+
+static std::vector<ForkFileAction>
+MakeForkActions(const ProcessLaunchInfo &info) {
+ std::vector<ForkFileAction> result;
+ for (size_t i = 0; i < info.GetNumFileActions(); ++i)
+ result.emplace_back(*info.GetFileActionAtIndex(i));
+ return result;
+}
+
+static Environment::Envp FixupEnvironment(Environment env) {
+#ifdef __ANDROID__
+ // If there is no PATH variable specified inside the environment then set the
+ // path to /system/bin. It is required because the default path used by
+ // execve() is wrong on android.
+ env.try_emplace("PATH", "/system/bin");
+#endif
+ return env.getEnvp();
+}
+
+ForkLaunchInfo::ForkLaunchInfo(const ProcessLaunchInfo &info)
+ : separate_process_group(
+ info.GetFlags().Test(eLaunchFlagLaunchInSeparateProcessGroup)),
+ debug(info.GetFlags().Test(eLaunchFlagDebug)),
+ disable_aslr(info.GetFlags().Test(eLaunchFlagDisableASLR)),
+ wd(info.GetWorkingDirectory().GetPath()),
+ argv(info.GetArguments().GetConstArgumentVector()),
+ envp(FixupEnvironment(info.GetEnvironment())),
+ actions(MakeForkActions(info)) {}
+
+HostProcess
+ProcessLauncherPosixFork::LaunchProcess(const ProcessLaunchInfo &launch_info,
+ Status &error) {
+ // A pipe used by the child process to report errors.
+ PipePosix pipe;
+ const bool child_processes_inherit = false;
+ error = pipe.CreateNew(child_processes_inherit);
+ if (error.Fail())
+ return HostProcess();
+
+ const ForkLaunchInfo fork_launch_info(launch_info);
+
+ ::pid_t pid = ::fork();
+ if (pid == -1) {
+ // Fork failed
+ error.SetErrorStringWithFormatv("Fork failed with error message: {0}",
+ llvm::sys::StrError());
+ return HostProcess(LLDB_INVALID_PROCESS_ID);
+ }
+ if (pid == 0) {
+ // child process
+ pipe.CloseReadFileDescriptor();
+ ChildFunc(pipe.ReleaseWriteFileDescriptor(), fork_launch_info);
+ }
+
+ // parent process
+
+ pipe.CloseWriteFileDescriptor();
+ llvm::SmallString<0> buf;
+ size_t pos = 0;
+ ssize_t r = 0;
+ do {
+ pos += r;
+ buf.resize_for_overwrite(pos + 100);
+ r = llvm::sys::RetryAfterSignal(-1, read, pipe.GetReadFileDescriptor(),
+ buf.begin() + pos, buf.size() - pos);
+ } while (r > 0);
+ assert(r != -1);
+
+ buf.resize(pos);
+ if (buf.empty())
+ return HostProcess(pid); // No error. We're done.
+
+ error.SetErrorString(buf);
+
+ llvm::sys::RetryAfterSignal(-1, waitpid, pid, nullptr, 0);
+
+ return HostProcess();
+}