aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/source/Host/common
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lldb/source/Host/common')
-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
34 files changed, 9712 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;
+}