diff --git a/lldb/include/lldb/Host/ProcessLaunchInfo.h b/lldb/include/lldb/Host/ProcessLaunchInfo.h index 25762bc65295d..50a5af604ee26 100644 --- a/lldb/include/lldb/Host/ProcessLaunchInfo.h +++ b/lldb/include/lldb/Host/ProcessLaunchInfo.h @@ -118,7 +118,7 @@ class ProcessLaunchInfo : public ProcessInfo { bool MonitorProcess() const; - PseudoTerminal &GetPTY() { return *m_pty; } + std::shared_ptr GetPTY() const { return m_pty; } void SetLaunchEventData(const char *data) { m_event_data.assign(data); } diff --git a/lldb/include/lldb/Host/PseudoTerminal.h b/lldb/include/lldb/Host/PseudoTerminal.h index bd1e2f56241b2..b61c213e138cb 100644 --- a/lldb/include/lldb/Host/PseudoTerminal.h +++ b/lldb/include/lldb/Host/PseudoTerminal.h @@ -35,11 +35,14 @@ class PseudoTerminal { /// Destructor /// - /// The destructor will close the primary and secondary file descriptors if - /// they are valid and ownership has not been released using one of: @li - /// PseudoTerminal::ReleasePrimaryFileDescriptor() @li - /// PseudoTerminal::ReleaseSaveFileDescriptor() - ~PseudoTerminal(); + /// The destructor will close the primary and secondary file + /// descriptor/HANDLEs if they are valid and ownership has not been released + /// using PseudoTerminal::Close(). + virtual ~PseudoTerminal(); + + /// Close all the file descriptors or Handles of the PseudoTerminal if they + /// are valid. + virtual void Close(); /// Close the primary file descriptor if it is valid. void ClosePrimaryFileDescriptor(); @@ -59,8 +62,7 @@ class PseudoTerminal { /// /// This class will close the file descriptors for the primary/secondary when /// the destructor is called. The file handles can be released using either: - /// @li PseudoTerminal::ReleasePrimaryFileDescriptor() @li - /// PseudoTerminal::ReleaseSaveFileDescriptor() + /// @li PseudoTerminal::ReleasePrimaryFileDescriptor() /// /// \return /// \b Parent process: a child process ID that is greater @@ -82,6 +84,16 @@ class PseudoTerminal { /// \see PseudoTerminal::ReleasePrimaryFileDescriptor() int GetPrimaryFileDescriptor() const; + /// The primary HANDLE accessor. + /// + /// This object retains ownership of the primary HANDLE when this + /// accessor is used. + /// + /// \return + /// The primary HANDLE, or INVALID_HANDLE_VALUE if the primary HANDLE is + /// not currently valid. + virtual void *GetPrimaryHandle() const { return ((void *)(long long)-1); }; + /// The secondary file descriptor accessor. /// /// This object retains ownership of the secondary file descriptor when this @@ -105,7 +117,17 @@ class PseudoTerminal { /// The name of the secondary pseudo terminal. /// /// \see PseudoTerminal::OpenFirstAvailablePrimary() - std::string GetSecondaryName() const; + virtual std::string GetSecondaryName() const; + + /// The underlying Windows Pseudo Terminal HANDLE's accessor. + /// + /// This object retains ownership of the ConPTY's HANDLE when this + /// accessor is used. + /// + /// \return + /// The primary HANDLE, or INVALID_HANDLE_VALUE if the primary HANDLE is + /// not currently valid. + virtual void *GetPseudoTerminalHandle() { return ((void *)(long long)-1); }; /// Open the first available pseudo terminal. /// @@ -126,7 +148,7 @@ class PseudoTerminal { /// /// \see PseudoTerminal::GetPrimaryFileDescriptor() @see /// PseudoTerminal::ReleasePrimaryFileDescriptor() - llvm::Error OpenFirstAvailablePrimary(int oflag); + virtual llvm::Error OpenFirstAvailablePrimary(int oflag); /// Open the secondary for the current primary pseudo terminal. /// diff --git a/lldb/include/lldb/Host/windows/ConnectionPseudoTerminalWindows.h b/lldb/include/lldb/Host/windows/ConnectionPseudoTerminalWindows.h new file mode 100644 index 0000000000000..6283312db7b8d --- /dev/null +++ b/lldb/include/lldb/Host/windows/ConnectionPseudoTerminalWindows.h @@ -0,0 +1,61 @@ +//===-- ConnectionPseudoTerminalWindows.h ------------------------*- C++ +//-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Host_windows_ConnectionPseudoConsoleWindows_h_ +#define liblldb_Host_windows_ConnectionPseudoConsoleWindows_h_ + +#include "lldb/Host/windows/PseudoTerminalWindows.h" +#include "lldb/Host/windows/windows.h" +#include "lldb/Utility/Connection.h" +#include "lldb/Utility/Status.h" +#include "lldb/lldb-types.h" + +namespace lldb_private { + +class ConnectionPseudoTerminal : public lldb_private::Connection { +public: + ConnectionPseudoTerminal(); + + ConnectionPseudoTerminal(std::shared_ptr pty, bool owns_file); + + ~ConnectionPseudoTerminal() override; + + bool IsConnected() const override; + + lldb::ConnectionStatus Connect(llvm::StringRef url, + Status *error_ptr) override; + + lldb::ConnectionStatus Disconnect(Status *error_ptr) override; + + size_t Read(void *dst, size_t dst_len, const Timeout &timeout, + lldb::ConnectionStatus &status, Status *error_ptr) override; + + size_t Write(const void *src, size_t src_len, lldb::ConnectionStatus &status, + Status *error_ptr) override; + + std::string GetURI() override { return ""; }; + + bool InterruptRead() override; + +protected: + std::shared_ptr m_pty; + OVERLAPPED m_overlapped; + bool m_owns_file; + HANDLE m_event_handles[2]; + + enum { kBytesAvailableEvent, kInterruptEvent }; + +private: + ConnectionPseudoTerminal(const ConnectionPseudoTerminal &) = delete; + const ConnectionPseudoTerminal & + operator=(const ConnectionPseudoTerminal &) = delete; +}; +} // namespace lldb_private + +#endif diff --git a/lldb/include/lldb/Host/windows/ProcessLauncherWindows.h b/lldb/include/lldb/Host/windows/ProcessLauncherWindows.h index 81aea5b2022a5..a04077a5e1d03 100644 --- a/lldb/include/lldb/Host/windows/ProcessLauncherWindows.h +++ b/lldb/include/lldb/Host/windows/ProcessLauncherWindows.h @@ -11,6 +11,9 @@ #include "lldb/Host/ProcessLauncher.h" #include "lldb/Host/windows/windows.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/Environment.h" +#include "llvm/Support/ErrorOr.h" namespace lldb_private { @@ -23,6 +26,32 @@ class ProcessLauncherWindows : public ProcessLauncher { protected: HANDLE GetStdioHandle(const ProcessLaunchInfo &launch_info, int fd); + + /// Create a UTF-16 environment block to use with CreateProcessW. + /// + /// The buffer is a sequence of null-terminated UTF-16 strings, followed by an + /// extra L'\0' (two bytes of 0). An empty environment must have one + /// empty string, followed by an extra L'\0'. + /// + /// The keys are sorted to comply with the CreateProcess' calling convention. + /// + /// Ensure that the resulting buffer is used in conjunction with + /// CreateProcessW and be sure that dwCreationFlags includes + /// CREATE_UNICODE_ENVIRONMENT. + /// + /// \param env The Environment object to convert. + /// \returns The sorted sequence of environment variables and their values, + /// separated by null terminators. + static std::vector CreateEnvironmentBufferW(const Environment &env); + + /// Flattens an Args object into a Windows command-line wide string. + /// + /// Returns an empty string if args is empty. + /// + /// \param args The Args object to flatten. + /// \returns A wide string containing the flattened command line. + static llvm::ErrorOr + GetFlattenedWindowsCommandStringW(Args args); }; } diff --git a/lldb/include/lldb/Host/windows/PseudoTerminalWindows.h b/lldb/include/lldb/Host/windows/PseudoTerminalWindows.h new file mode 100644 index 0000000000000..8698ae2f8b804 --- /dev/null +++ b/lldb/include/lldb/Host/windows/PseudoTerminalWindows.h @@ -0,0 +1,37 @@ +//===-- PseudoTerminalWindows.h ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Host_Windows_PseudoTerminalWindows_H_ +#define liblldb_Host_Windows_PseudoTerminalWindows_H_ + +#include "lldb/Host/PseudoTerminal.h" +#include "lldb/Host/windows/windows.h" + +namespace lldb_private { + +class PseudoTerminalWindows : public PseudoTerminal { + +public: + void Close() override; + + HPCON GetPseudoTerminalHandle() override { return m_conpty_handle; }; + + HANDLE GetPrimaryHandle() const override { return m_conpty_output; }; + + std::string GetSecondaryName() const override { return ""; }; + + llvm::Error OpenFirstAvailablePrimary(int oflag) override; + +protected: + HANDLE m_conpty_handle = INVALID_HANDLE_VALUE; + HANDLE m_conpty_output = INVALID_HANDLE_VALUE; + HANDLE m_conpty_input = INVALID_HANDLE_VALUE; +}; +}; // namespace lldb_private + +#endif // liblldb_Host_Windows_PseudoTerminalWindows_H_ \ No newline at end of file diff --git a/lldb/include/lldb/Host/windows/windows.h b/lldb/include/lldb/Host/windows/windows.h index d53d4b9967268..2047e602291fc 100644 --- a/lldb/include/lldb/Host/windows/windows.h +++ b/lldb/include/lldb/Host/windows/windows.h @@ -9,9 +9,9 @@ #ifndef LLDB_lldb_windows_h_ #define LLDB_lldb_windows_h_ -#define NTDDI_VERSION NTDDI_VISTA +// #define NTDDI_VERSION NTDDI_VISTA #undef _WIN32_WINNT // undef a previous definition to avoid warning -#define _WIN32_WINNT _WIN32_WINNT_VISTA +// #define _WIN32_WINNT _WIN32_WINNT_VISTA #define WIN32_LEAN_AND_MEAN #define NOGDI #undef NOMINMAX // undef a previous definition to avoid warning @@ -26,6 +26,7 @@ #undef near #undef FAR #undef NEAR +#undef WIN32_MEMORY_INFORMATION_CLASS #define FAR #define NEAR diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index c1f9785e76f90..17a8ce713302a 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -2534,8 +2534,38 @@ void PruneThreadPlans(); void CalculateExecutionContext(ExecutionContext &exe_ctx) override; + /// Associates a file descriptor with the process's STDIO handling + /// and configures an asynchronous reading of that descriptor. + /// + /// This method installs a ConnectionFileDescriptor for the passed file + /// descriptor and starts a dedicated read thread. If the read thread starts + /// successfully, the method also ensures that an IOHandlerProcessSTDIO is + /// created to manage user input to the process. + /// + /// The descriptor's ownership is transferred to the underlying + /// ConnectionFileDescriptor. + /// + /// \param[in] fd + /// The file descriptor to use for process STDIO communication. It's + /// assumed to be valid and will be managed by the newly created + /// connection. + /// + /// \see lldb_private::Process::STDIOReadThreadBytesReceived() + /// \see lldb_private::IOHandlerProcessSTDIO + /// \see lldb_private::ConnectionFileDescriptor void SetSTDIOFileDescriptor(int file_descriptor); + /// Windows equivalent of Process::SetSTDIOFileDescriptor, with a + /// PseudoTerminalWindows instead of a file descriptor. + /// + /// \param pty + /// The PseudoTerminal to use for process STDIO communication. It is not + /// managed by the created read thread. + /// + /// \see lldb_private::ConnectionPseudoTerminalWindows + virtual void + SetPseudoTerminalHandle(const std::shared_ptr &pty) {}; + // Add a permanent region of memory that should never be read or written to. // This can be used to ensure that memory reads or writes to certain areas of // memory never end up being sent to the DoReadMemory or DoWriteMemory diff --git a/lldb/source/Host/CMakeLists.txt b/lldb/source/Host/CMakeLists.txt index c9e8afe48fcde..1657dadf5ffe7 100644 --- a/lldb/source/Host/CMakeLists.txt +++ b/lldb/source/Host/CMakeLists.txt @@ -66,6 +66,7 @@ add_host_subdirectory(posix if (CMAKE_SYSTEM_NAME MATCHES "Windows") add_host_subdirectory(windows windows/ConnectionGenericFileWindows.cpp + windows/ConnectionPseudoTerminalWindows.cpp windows/FileSystem.cpp windows/Host.cpp windows/HostInfoWindows.cpp @@ -75,6 +76,7 @@ if (CMAKE_SYSTEM_NAME MATCHES "Windows") windows/MainLoopWindows.cpp windows/PipeWindows.cpp windows/ProcessLauncherWindows.cpp + windows/PseudoTerminalWindows.cpp windows/ProcessRunLock.cpp ) else() diff --git a/lldb/source/Host/common/ProcessLaunchInfo.cpp b/lldb/source/Host/common/ProcessLaunchInfo.cpp index 49159cca9c57c..c3beef7031f18 100644 --- a/lldb/source/Host/common/ProcessLaunchInfo.cpp +++ b/lldb/source/Host/common/ProcessLaunchInfo.cpp @@ -20,7 +20,9 @@ #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/FileSystem.h" -#if !defined(_WIN32) +#ifdef _WIN32 +#include "lldb/Host/windows/PseudoTerminalWindows.h" +#else #include #endif @@ -31,7 +33,12 @@ using namespace lldb_private; ProcessLaunchInfo::ProcessLaunchInfo() : ProcessInfo(), m_working_dir(), m_plugin_name(), m_flags(0), - m_file_actions(), m_pty(new PseudoTerminal), m_monitor_callback(nullptr) { + m_file_actions(), m_monitor_callback(nullptr) { +#ifdef _WIN32 + m_pty = std::make_shared(); +#else + m_pty = std::make_shared(); +#endif } ProcessLaunchInfo::ProcessLaunchInfo(const FileSpec &stdin_file_spec, @@ -40,7 +47,13 @@ ProcessLaunchInfo::ProcessLaunchInfo(const FileSpec &stdin_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) { + m_file_actions() { +#ifdef _WIN32 + m_pty = std::make_shared(); +#else + m_pty = std::make_shared(); +#endif + if (stdin_file_spec) { FileAction file_action; const bool read = true; diff --git a/lldb/source/Host/common/PseudoTerminal.cpp b/lldb/source/Host/common/PseudoTerminal.cpp index 53e91aff212a4..4d062c3618523 100644 --- a/lldb/source/Host/common/PseudoTerminal.cpp +++ b/lldb/source/Host/common/PseudoTerminal.cpp @@ -9,6 +9,7 @@ #include "lldb/Host/PseudoTerminal.h" #include "lldb/Host/Config.h" #include "lldb/Host/FileSystem.h" +#include "lldb/Host/windows/windows.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Errno.h" #include @@ -29,16 +30,11 @@ 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() { +PseudoTerminal::~PseudoTerminal() { Close(); } + +void PseudoTerminal::Close() { ClosePrimaryFileDescriptor(); CloseSecondaryFileDescriptor(); } diff --git a/lldb/source/Host/windows/ConnectionPseudoTerminalWindows.cpp b/lldb/source/Host/windows/ConnectionPseudoTerminalWindows.cpp new file mode 100644 index 0000000000000..b00fd745bc409 --- /dev/null +++ b/lldb/source/Host/windows/ConnectionPseudoTerminalWindows.cpp @@ -0,0 +1,191 @@ +//===-- ConnectionPseudoConsoleWindowsWindows.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/windows/ConnectionPseudoTerminalWindows.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Timeout.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ConvertUTF.h" + +using namespace lldb; +using namespace lldb_private; + +class ReturnInfo { +public: + void Set(size_t bytes, ConnectionStatus status, DWORD error_code) { + m_error = Status(error_code, eErrorTypeWin32); + m_bytes = bytes; + m_status = status; + } + + void Set(size_t bytes, ConnectionStatus status, llvm::StringRef error_msg) { + m_error = Status::FromErrorString(error_msg.data()); + m_bytes = bytes; + m_status = status; + } + + size_t GetBytes() const { return m_bytes; } + ConnectionStatus GetStatus() const { return m_status; } + const Status &GetError() const { return m_error; } + +private: + Status m_error; + size_t m_bytes; + ConnectionStatus m_status; +}; + +ConnectionPseudoTerminal::ConnectionPseudoTerminal() + : m_pty(nullptr), m_owns_file(false) { + ::ZeroMemory(&m_overlapped, sizeof(m_overlapped)); +} + +ConnectionPseudoTerminal::ConnectionPseudoTerminal( + std::shared_ptr pty, bool owns_file) + : m_pty(pty), m_owns_file(owns_file) { + ::ZeroMemory(&m_overlapped, sizeof(m_overlapped)); +} + +ConnectionPseudoTerminal::~ConnectionPseudoTerminal() {} + +lldb::ConnectionStatus ConnectionPseudoTerminal::Connect(llvm::StringRef url, + Status *error_ptr) { + if (IsConnected()) + return eConnectionStatusSuccess; + return eConnectionStatusNoConnection; +} + +bool ConnectionPseudoTerminal::IsConnected() const { + return m_pty && (m_pty->GetPrimaryHandle() != INVALID_HANDLE_VALUE); +} + +lldb::ConnectionStatus ConnectionPseudoTerminal::Disconnect(Status *error_ptr) { + Log *log = GetLog(LLDBLog::Connection); + LLDB_LOGF(log, "%p ConnectionPseudoTerminal::Disconnect ()", + static_cast(this)); + + if (!IsConnected()) + return eConnectionStatusSuccess; + + m_pty->Close(); + return eConnectionStatusSuccess; +} + +size_t ConnectionPseudoTerminal::Read(void *dst, size_t dst_len, + const Timeout &timeout, + lldb::ConnectionStatus &status, + Status *error_ptr) { + ReturnInfo return_info; + DWORD bytes_read = 0; + BOOL result = false; + + if (error_ptr) + error_ptr->Clear(); + + HANDLE hInput = m_pty->GetPrimaryHandle(); + + if (!IsConnected()) { + return_info.Set(0, eConnectionStatusNoConnection, ERROR_INVALID_HANDLE); + goto finish; + } + + // Setup OVERLAPPED event + m_overlapped.hEvent = m_event_handles[kBytesAvailableEvent]; + + result = + ::ReadFile(hInput, dst, static_cast(dst_len), NULL, &m_overlapped); + if (result || ::GetLastError() == ERROR_IO_PENDING) { + if (!result) { + // Operation pending: wait for completion or interrupt + DWORD milliseconds = + timeout ? static_cast( + std::chrono::duration_cast( + *timeout) + .count()) + : INFINITE; + + DWORD wait_result = ::WaitForMultipleObjects( + static_cast(std::size(m_event_handles)), m_event_handles, + FALSE, milliseconds); + + switch (wait_result) { + case WAIT_OBJECT_0 + kBytesAvailableEvent: + break; // Data ready + case WAIT_OBJECT_0 + kInterruptEvent: + return_info.Set(0, eConnectionStatusInterrupted, 0); + goto finish; + case WAIT_TIMEOUT: + return_info.Set(0, eConnectionStatusTimedOut, 0); + goto finish; + case WAIT_FAILED: + return_info.Set(0, eConnectionStatusError, ::GetLastError()); + goto finish; + } + } + + // Get actual number of bytes read + if (!::GetOverlappedResult(hInput, &m_overlapped, &bytes_read, FALSE)) { + DWORD err = ::GetLastError(); + if (err == ERROR_HANDLE_EOF || err == ERROR_OPERATION_ABORTED || + err == ERROR_BROKEN_PIPE) + return_info.Set(bytes_read, eConnectionStatusEndOfFile, 0); + else + return_info.Set(bytes_read, eConnectionStatusError, err); + } else if (bytes_read == 0) { + return_info.Set(bytes_read, eConnectionStatusEndOfFile, 0); + } else { + return_info.Set(bytes_read, eConnectionStatusSuccess, 0); + } + goto finish; + } else if (::GetLastError() == ERROR_BROKEN_PIPE) { + return_info.Set(0, eConnectionStatusEndOfFile, 0); + } else { + return_info.Set(0, eConnectionStatusError, ::GetLastError()); + } + +finish: + status = return_info.GetStatus(); + if (error_ptr) + *error_ptr = return_info.GetError().Clone(); + + // Reset manual-reset event for next read + ResetEvent(m_event_handles[kBytesAvailableEvent]); + + // IncrementFilePointer(return_info.GetBytes()); + + Log *log = GetLog(LLDBLog::Connection); + LLDB_LOGF(log, + "%p ConnectionPseudoTerminal::Read() handle=%p, dst=%p, " + "dst_len=%zu => %zu, error=%s", + static_cast(this), hInput, dst, dst_len, + return_info.GetBytes(), return_info.GetError().AsCString()); + + return return_info.GetBytes(); +} + +size_t ConnectionPseudoTerminal::Write(const void *src, size_t src_len, + lldb::ConnectionStatus &status, + Status *error_ptr) { + // TODO: + DWORD bytes_written = 0; + + if (error_ptr) + error_ptr->Clear(); + + if (!IsConnected()) { + } + return 0; +} + +bool ConnectionPseudoTerminal::InterruptRead() { + return ::SetEvent(m_event_handles[kInterruptEvent]); +} \ No newline at end of file diff --git a/lldb/source/Host/windows/ProcessLauncherWindows.cpp b/lldb/source/Host/windows/ProcessLauncherWindows.cpp index f5adadaf061bf..dc7f864b0543d 100644 --- a/lldb/source/Host/windows/ProcessLauncherWindows.cpp +++ b/lldb/source/Host/windows/ProcessLauncherWindows.cpp @@ -21,42 +21,41 @@ using namespace lldb; using namespace lldb_private; -static void CreateEnvironmentBuffer(const Environment &env, - std::vector &buffer) { - // The buffer is a list of null-terminated UTF-16 strings, followed by an - // extra L'\0' (two bytes of 0). An empty environment must have one - // empty string, followed by an extra L'\0'. +std::vector +ProcessLauncherWindows::CreateEnvironmentBufferW(const Environment &env) { + std::vector env_entries; for (const auto &KV : env) { - std::wstring warg; - if (llvm::ConvertUTF8toWide(Environment::compose(KV), warg)) { - buffer.insert( - buffer.end(), reinterpret_cast(warg.c_str()), - reinterpret_cast(warg.c_str() + warg.size() + 1)); + std::wstring wentry; + if (llvm::ConvertUTF8toWide(Environment::compose(KV), wentry)) { + env_entries.push_back(std::move(wentry)); } } - // One null wchar_t (to end the block) is two null bytes - buffer.push_back(0); - buffer.push_back(0); - // Insert extra two bytes, just in case the environment was empty. - buffer.push_back(0); - buffer.push_back(0); + std::sort(env_entries.begin(), env_entries.end(), + [](const std::wstring &a, const std::wstring &b) { + return _wcsicmp(a.c_str(), b.c_str()) < 0; + }); + + std::vector buffer; + buffer.clear(); + for (const auto &env_entry : env_entries) { + buffer.insert(buffer.end(), env_entry.begin(), env_entry.end()); + buffer.push_back(L'\0'); + } + buffer.push_back(L'\0'); + + return buffer; } -static bool GetFlattenedWindowsCommandString(Args args, std::wstring &command) { +llvm::ErrorOr +ProcessLauncherWindows::GetFlattenedWindowsCommandStringW(Args args) { if (args.empty()) - return false; + return L""; std::vector args_ref; for (auto &entry : args.entries()) args_ref.push_back(entry.ref()); - llvm::ErrorOr result = - llvm::sys::flattenWindowsCommandLine(args_ref); - if (result.getError()) - return false; - - command = *result; - return true; + return llvm::sys::flattenWindowsCommandLine(args_ref); } HostProcess @@ -64,12 +63,13 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, Status &error) { error.Clear(); - std::string executable; - std::vector environment; - STARTUPINFOEX startupinfoex = {}; - STARTUPINFO &startupinfo = startupinfoex.StartupInfo; + std::vector inherited_handles; + STARTUPINFOEXW startupinfoex = {}; + STARTUPINFOW &startupinfo = startupinfoex.StartupInfo; PROCESS_INFORMATION pi = {}; + HPCON hPC = launch_info.GetPTY()->GetPseudoTerminalHandle(); + HANDLE stdin_handle = GetStdioHandle(launch_info, STDIN_FILENO); HANDLE stdout_handle = GetStdioHandle(launch_info, STDOUT_FILENO); HANDLE stderr_handle = GetStdioHandle(launch_info, STDERR_FILENO); @@ -82,32 +82,34 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, ::CloseHandle(stderr_handle); }); - startupinfo.cb = sizeof(startupinfoex); + startupinfo.cb = sizeof(STARTUPINFOEXW); startupinfo.dwFlags |= STARTF_USESTDHANDLES; - startupinfo.hStdError = - stderr_handle ? stderr_handle : ::GetStdHandle(STD_ERROR_HANDLE); - startupinfo.hStdInput = - stdin_handle ? stdin_handle : ::GetStdHandle(STD_INPUT_HANDLE); - startupinfo.hStdOutput = - stdout_handle ? stdout_handle : ::GetStdHandle(STD_OUTPUT_HANDLE); - std::vector inherited_handles; - if (startupinfo.hStdError) - inherited_handles.push_back(startupinfo.hStdError); - if (startupinfo.hStdInput) - inherited_handles.push_back(startupinfo.hStdInput); - if (startupinfo.hStdOutput) - inherited_handles.push_back(startupinfo.hStdOutput); + if (hPC == INVALID_HANDLE_VALUE) { + startupinfo.hStdError = + stderr_handle ? stderr_handle : ::GetStdHandle(STD_ERROR_HANDLE); + startupinfo.hStdInput = + stdin_handle ? stdin_handle : ::GetStdHandle(STD_INPUT_HANDLE); + startupinfo.hStdOutput = + stdout_handle ? stdout_handle : ::GetStdHandle(STD_OUTPUT_HANDLE); + + if (startupinfo.hStdError) + inherited_handles.push_back(startupinfo.hStdError); + if (startupinfo.hStdInput) + inherited_handles.push_back(startupinfo.hStdInput); + if (startupinfo.hStdOutput) + inherited_handles.push_back(startupinfo.hStdOutput); + } SIZE_T attributelist_size = 0; InitializeProcThreadAttributeList(/*lpAttributeList=*/nullptr, /*dwAttributeCount=*/1, /*dwFlags=*/0, &attributelist_size); - startupinfoex.lpAttributeList = - static_cast(malloc(attributelist_size)); - auto free_attributelist = - llvm::make_scope_exit([&] { free(startupinfoex.lpAttributeList); }); + startupinfoex.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc( + GetProcessHeap(), 0, attributelist_size); + auto free_attributelist = llvm::make_scope_exit( + [&] { HeapFree(GetProcessHeap(), 0, startupinfoex.lpAttributeList); }); if (!InitializeProcThreadAttributeList(startupinfoex.lpAttributeList, /*dwAttributeCount=*/1, /*dwFlags=*/0, &attributelist_size)) { @@ -116,21 +118,31 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, } auto delete_attributelist = llvm::make_scope_exit( [&] { DeleteProcThreadAttributeList(startupinfoex.lpAttributeList); }); - for (size_t i = 0; i < launch_info.GetNumFileActions(); ++i) { - const FileAction *act = launch_info.GetFileActionAtIndex(i); - if (act->GetAction() == FileAction::eFileActionDuplicate && - act->GetFD() == act->GetActionArgument()) - inherited_handles.push_back(reinterpret_cast(act->GetFD())); - } - if (!inherited_handles.empty()) { - if (!UpdateProcThreadAttribute( - startupinfoex.lpAttributeList, /*dwFlags=*/0, - PROC_THREAD_ATTRIBUTE_HANDLE_LIST, inherited_handles.data(), - inherited_handles.size() * sizeof(HANDLE), - /*lpPreviousValue=*/nullptr, /*lpReturnSize=*/nullptr)) { + + if (hPC != INVALID_HANDLE_VALUE) { + if (!UpdateProcThreadAttribute(startupinfoex.lpAttributeList, 0, + PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, hPC, + sizeof(hPC), NULL, NULL)) { error = Status(::GetLastError(), eErrorTypeWin32); return HostProcess(); } + } else { + for (size_t i = 0; i < launch_info.GetNumFileActions(); ++i) { + const FileAction *act = launch_info.GetFileActionAtIndex(i); + if (act->GetAction() == FileAction::eFileActionDuplicate && + act->GetFD() == act->GetActionArgument()) + inherited_handles.push_back(reinterpret_cast(act->GetFD())); + } + if (!inherited_handles.empty()) { + if (!UpdateProcThreadAttribute( + startupinfoex.lpAttributeList, /*dwFlags=*/0, + PROC_THREAD_ATTRIBUTE_HANDLE_LIST, inherited_handles.data(), + inherited_handles.size() * sizeof(HANDLE), + /*lpPreviousValue=*/nullptr, /*lpReturnSize=*/nullptr)) { + error = Status(::GetLastError(), eErrorTypeWin32); + return HostProcess(); + } + } } const char *hide_console_var = @@ -146,31 +158,37 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, if (launch_info.GetFlags().Test(eLaunchFlagDebug)) flags |= DEBUG_ONLY_THIS_PROCESS; - if (launch_info.GetFlags().Test(eLaunchFlagDisableSTDIO)) + if (launch_info.GetFlags().Test(eLaunchFlagDisableSTDIO) || + hPC != INVALID_HANDLE_VALUE) flags &= ~CREATE_NEW_CONSOLE; - LPVOID env_block = nullptr; - ::CreateEnvironmentBuffer(launch_info.GetEnvironment(), environment); - env_block = environment.data(); + std::vector environment = + CreateEnvironmentBufferW(launch_info.GetEnvironment()); + LPVOID env_block = environment.empty() ? nullptr : environment.data(); - executable = launch_info.GetExecutableFile().GetPath(); - std::wstring wcommandLine; - GetFlattenedWindowsCommandString(launch_info.GetArguments(), wcommandLine); - - std::wstring wexecutable, wworkingDirectory; - llvm::ConvertUTF8toWide(executable, wexecutable); - llvm::ConvertUTF8toWide(launch_info.GetWorkingDirectory().GetPath(), - wworkingDirectory); + auto wcommandLineOrErr = + GetFlattenedWindowsCommandStringW(launch_info.GetArguments()); + if (!wcommandLineOrErr) { + error = Status(wcommandLineOrErr.getError()); + return HostProcess(); + } + std::wstring wcommandLine = *wcommandLineOrErr; // If the command line is empty, it's best to pass a null pointer to tell // CreateProcessW to use the executable name as the command line. If the // command line is not empty, its contents may be modified by CreateProcessW. WCHAR *pwcommandLine = wcommandLine.empty() ? nullptr : &wcommandLine[0]; + std::wstring wexecutable, wworkingDirectory; + llvm::ConvertUTF8toWide(launch_info.GetExecutableFile().GetPath(), + wexecutable); + llvm::ConvertUTF8toWide(launch_info.GetWorkingDirectory().GetPath(), + wworkingDirectory); + BOOL result = ::CreateProcessW( wexecutable.c_str(), pwcommandLine, NULL, NULL, /*bInheritHandles=*/!inherited_handles.empty(), flags, env_block, wworkingDirectory.size() == 0 ? NULL : wworkingDirectory.c_str(), - reinterpret_cast(&startupinfoex), &pi); + reinterpret_cast(&startupinfoex), &pi); if (!result) { // Call GetLastError before we make any other system calls. @@ -223,4 +241,4 @@ ProcessLauncherWindows::GetStdioHandle(const ProcessLaunchInfo &launch_info, HANDLE result = ::CreateFileW(wpath.c_str(), access, share, &secattr, create, flags, NULL); return (result == INVALID_HANDLE_VALUE) ? NULL : result; -} +} \ No newline at end of file diff --git a/lldb/source/Host/windows/PseudoTerminalWindows.cpp b/lldb/source/Host/windows/PseudoTerminalWindows.cpp new file mode 100644 index 0000000000000..38ebbbce84cfe --- /dev/null +++ b/lldb/source/Host/windows/PseudoTerminalWindows.cpp @@ -0,0 +1,68 @@ +#include "lldb/Host/windows/PseudoTerminalWindows.h" + +#include "llvm/Support/Errc.h" +#include "llvm/Support/Errno.h" + +using namespace lldb_private; + +void PseudoTerminalWindows::Close() { + if (m_conpty_handle != INVALID_HANDLE_VALUE) + ClosePseudoConsole(m_conpty_handle); + CloseHandle(m_conpty_input); + CloseHandle(m_conpty_output); + m_conpty_handle = INVALID_HANDLE_VALUE; + m_conpty_input = INVALID_HANDLE_VALUE; + m_conpty_output = INVALID_HANDLE_VALUE; +} + +llvm::Error PseudoTerminalWindows::OpenFirstAvailablePrimary(int oflag) { + HRESULT hr; + HANDLE hInputRead = INVALID_HANDLE_VALUE; + HANDLE hInputWrite = INVALID_HANDLE_VALUE; + HANDLE hOutputRead = INVALID_HANDLE_VALUE; + HANDLE hOutputWrite = INVALID_HANDLE_VALUE; + + wchar_t pipe_name[MAX_PATH]; + swprintf(pipe_name, MAX_PATH, L"\\\\.\\pipe\\conpty-%d-%p", + GetCurrentProcessId(), this); + + hOutputRead = + CreateNamedPipeW(pipe_name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_WAIT, 1, 4096, 4096, 0, NULL); + hOutputWrite = CreateFileW(pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + + if (!CreatePipe(&hInputRead, &hInputWrite, NULL, 0)) + return llvm::errorCodeToError( + std::error_code(GetLastError(), std::system_category())); + + // if (!CreatePipe(&hOutputRead, &hOutputWrite, NULL, 0)) { + // CloseHandle(hInputRead); + // CloseHandle(hInputWrite); + // return llvm::errorCodeToError( + // std::error_code(GetLastError(), std::system_category())); + // } + + COORD consoleSize{80, 25}; + HPCON hPC = INVALID_HANDLE_VALUE; + hr = CreatePseudoConsole(consoleSize, hInputRead, hOutputWrite, 0, &hPC); + CloseHandle(hInputRead); + CloseHandle(hOutputWrite); + + if (FAILED(hr)) { + CloseHandle(hInputWrite); + CloseHandle(hOutputRead); + return llvm::make_error( + "Failed to create Windows ConPTY pseudo terminal", + llvm::errc::io_error); + } + + DWORD mode = PIPE_NOWAIT; + SetNamedPipeHandleState(hOutputRead, &mode, NULL, NULL); + + m_conpty_handle = hPC; + m_conpty_output = hOutputRead; + m_conpty_input = hInputWrite; + + return llvm::Error::success(); +} \ No newline at end of file diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp index 211868b51facb..d2fd372bfe9e3 100644 --- a/lldb/source/Interpreter/ScriptInterpreter.cpp +++ b/lldb/source/Interpreter/ScriptInterpreter.cpp @@ -136,7 +136,7 @@ SymbolContext ScriptInterpreter::GetOpaqueTypeFromSBSymbolContext( return {}; } -std::optional +std::optional ScriptInterpreter::GetOpaqueTypeFromSBMemoryRegionInfo( const lldb::SBMemoryRegionInfo &mem_region) const { if (!mem_region.m_opaque_up) diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.cpp index 47111c97927c1..8eeb3d61ba78b 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.cpp @@ -215,7 +215,7 @@ PlatformAppleSimulator::DebugProcess(ProcessLaunchInfo &launch_info, // been used where the secondary side was given as the file to open for // stdin/out/err after we have already opened the primary so we can // read/write stdin/out/err. - int pty_fd = launch_info.GetPTY().ReleasePrimaryFileDescriptor(); + int pty_fd = launch_info.GetPTY()->ReleasePrimaryFileDescriptor(); if (pty_fd != PseudoTerminal::invalid_fd) { process_sp->SetSTDIOFileDescriptor(pty_fd); } diff --git a/lldb/source/Plugins/Platform/MacOSX/objcxx/PlatformiOSSimulatorCoreSimulatorSupport.mm b/lldb/source/Plugins/Platform/MacOSX/objcxx/PlatformiOSSimulatorCoreSimulatorSupport.mm index f3e79d3d56154..297fcd9b17f0a 100644 --- a/lldb/source/Plugins/Platform/MacOSX/objcxx/PlatformiOSSimulatorCoreSimulatorSupport.mm +++ b/lldb/source/Plugins/Platform/MacOSX/objcxx/PlatformiOSSimulatorCoreSimulatorSupport.mm @@ -399,18 +399,18 @@ static Status HandleFileAction(ProcessLaunchInfo &launch_info, case FileAction::eFileActionOpen: { FileSpec file_spec = file_action->GetFileSpec(); if (file_spec) { - const int primary_fd = launch_info.GetPTY().GetPrimaryFileDescriptor(); + const int primary_fd = launch_info.GetPTY()->GetPrimaryFileDescriptor(); if (primary_fd != PseudoTerminal::invalid_fd) { // Check in case our file action open wants to open the secondary - FileSpec secondary_spec(launch_info.GetPTY().GetSecondaryName()); + FileSpec secondary_spec(launch_info.GetPTY()->GetSecondaryName()); if (file_spec == secondary_spec) { int secondary_fd = - launch_info.GetPTY().GetSecondaryFileDescriptor(); + launch_info.GetPTY()->GetSecondaryFileDescriptor(); if (secondary_fd == PseudoTerminal::invalid_fd) { - if (llvm::Error Err = launch_info.GetPTY().OpenSecondary(O_RDWR)) + if (llvm::Error Err = launch_info.GetPTY()->OpenSecondary(O_RDWR)) return Status::FromError(std::move(Err)); } - secondary_fd = launch_info.GetPTY().GetSecondaryFileDescriptor(); + secondary_fd = launch_info.GetPTY()->GetSecondaryFileDescriptor(); assert(secondary_fd != PseudoTerminal::invalid_fd); [options setValue:[NSNumber numberWithInteger:secondary_fd] forKey:key]; diff --git a/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp b/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp index befc28b09d185..193ea3ca5d219 100644 --- a/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp +++ b/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp @@ -488,7 +488,7 @@ lldb::ProcessSP PlatformPOSIX::DebugProcess(ProcessLaunchInfo &launch_info, if (error.Success()) { // Hook up process PTY if we have one (which we should for local debugging // with llgs). - int pty_fd = launch_info.GetPTY().ReleasePrimaryFileDescriptor(); + int pty_fd = launch_info.GetPTY()->ReleasePrimaryFileDescriptor(); if (pty_fd != PseudoTerminal::invalid_fd) { process_sp->SetSTDIOFileDescriptor(pty_fd); LLDB_LOG(log, "hooked up STDIO pty to process"); diff --git a/lldb/source/Plugins/Platform/QemuUser/PlatformQemuUser.cpp b/lldb/source/Plugins/Platform/QemuUser/PlatformQemuUser.cpp index c182d3d862269..ced709aef6f8d 100644 --- a/lldb/source/Plugins/Platform/QemuUser/PlatformQemuUser.cpp +++ b/lldb/source/Plugins/Platform/QemuUser/PlatformQemuUser.cpp @@ -235,10 +235,10 @@ lldb::ProcessSP PlatformQemuUser::DebugProcess(ProcessLaunchInfo &launch_info, if (error.Fail()) return nullptr; - if (launch_info.GetPTY().GetPrimaryFileDescriptor() != + if (launch_info.GetPTY()->GetPrimaryFileDescriptor() != PseudoTerminal::invalid_fd) process_sp->SetSTDIOFileDescriptor( - launch_info.GetPTY().ReleasePrimaryFileDescriptor()); + launch_info.GetPTY()->ReleasePrimaryFileDescriptor()); return process_sp; } diff --git a/lldb/source/Plugins/Platform/Windows/PlatformWindows.cpp b/lldb/source/Plugins/Platform/Windows/PlatformWindows.cpp index c0c26cc5f1954..917e8be9ea60a 100644 --- a/lldb/source/Plugins/Platform/Windows/PlatformWindows.cpp +++ b/lldb/source/Plugins/Platform/Windows/PlatformWindows.cpp @@ -496,6 +496,7 @@ ProcessSP PlatformWindows::DebugProcess(ProcessLaunchInfo &launch_info, // plugin, and PlatformWindows::DebugProcess is just a pass-through to get to // the process plugin. + Log *log = GetLog(LLDBLog::Platform); if (IsRemote()) { if (m_remote_platform_sp) return m_remote_platform_sp->DebugProcess(launch_info, debugger, target, @@ -519,8 +520,15 @@ ProcessSP PlatformWindows::DebugProcess(ProcessLaunchInfo &launch_info, // We need to launch and attach to the process. launch_info.GetFlags().Set(eLaunchFlagDebug); - if (process_sp) - error = process_sp->Launch(launch_info); + if (!process_sp) + return process_sp; + error = process_sp->Launch(launch_info); + int zzzz = 1; + if (error.Success() && zzzz) + process_sp->SetPseudoTerminalHandle(launch_info.GetPTY()); + else + LLDB_LOGF(log, "Platform::%s LaunchProcess() failed: %s", __FUNCTION__, + error.AsCString()); return process_sp; } diff --git a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp index cd5e3458e60e8..bbf10960f55ae 100644 --- a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp +++ b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp @@ -86,8 +86,9 @@ NativeProcessAIX::Manager::Launch(ProcessLaunchInfo &launch_info, LLDB_LOG(log, "inferior started, now in stopped state"); return std::unique_ptr(new NativeProcessAIX( - pid, launch_info.GetPTY().ReleasePrimaryFileDescriptor(), native_delegate, - HostInfo::GetArchitecture(HostInfo::eArchKind64), *this, {pid})); + pid, launch_info.GetPTY()->ReleasePrimaryFileDescriptor(), + native_delegate, HostInfo::GetArchitecture(HostInfo::eArchKind64), *this, + {pid})); } llvm::Expected> diff --git a/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp b/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp index a22083a8a0903..1b63b01693840 100644 --- a/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp +++ b/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp @@ -108,8 +108,8 @@ NativeProcessFreeBSD::Manager::Launch(ProcessLaunchInfo &launch_info, Info.GetArchitecture().GetArchitectureName()); std::unique_ptr process_up(new NativeProcessFreeBSD( - pid, launch_info.GetPTY().ReleasePrimaryFileDescriptor(), native_delegate, - Info.GetArchitecture(), m_mainloop)); + pid, launch_info.GetPTY()->ReleasePrimaryFileDescriptor(), + native_delegate, Info.GetArchitecture(), m_mainloop)); status = process_up->SetupTrace(); if (status.Fail()) diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp index 7ef50da3641b4..c603c42cf09bf 100644 --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -304,8 +304,8 @@ NativeProcessLinux::Manager::Launch(ProcessLaunchInfo &launch_info, return arch_or.takeError(); return std::unique_ptr(new NativeProcessLinux( - pid, launch_info.GetPTY().ReleasePrimaryFileDescriptor(), native_delegate, - *arch_or, *this, {pid})); + pid, launch_info.GetPTY()->ReleasePrimaryFileDescriptor(), + native_delegate, *arch_or, *this, {pid})); } llvm::Expected> diff --git a/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp b/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp index 8fb15d83117f4..4f874d33f361a 100644 --- a/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp +++ b/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp @@ -94,8 +94,8 @@ NativeProcessNetBSD::Manager::Launch(ProcessLaunchInfo &launch_info, Info.GetArchitecture().GetArchitectureName()); std::unique_ptr process_up(new NativeProcessNetBSD( - pid, launch_info.GetPTY().ReleasePrimaryFileDescriptor(), native_delegate, - Info.GetArchitecture(), m_mainloop)); + pid, launch_info.GetPTY()->ReleasePrimaryFileDescriptor(), + native_delegate, Info.GetArchitecture(), m_mainloop)); status = process_up->SetupTrace(); if (status.Fail()) diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp index 79dd46ba319d6..600f2203c63e1 100644 --- a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp @@ -47,9 +47,9 @@ namespace lldb_private { NativeProcessWindows::NativeProcessWindows(ProcessLaunchInfo &launch_info, NativeDelegate &delegate, llvm::Error &E) - : NativeProcessProtocol(LLDB_INVALID_PROCESS_ID, - launch_info.GetPTY().ReleasePrimaryFileDescriptor(), - delegate), + : NativeProcessProtocol( + LLDB_INVALID_PROCESS_ID, + launch_info.GetPTY()->ReleasePrimaryFileDescriptor(), delegate), ProcessDebugger(), m_arch(launch_info.GetArchitecture()) { ErrorAsOutParameter EOut(&E); DebugDelegateSP delegate_sp(new NativeDebugDelegate(*this)); diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp index 0fecefe23b88e..d25ac7ada5d43 100644 --- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp @@ -21,6 +21,9 @@ #include "lldb/Host/HostInfo.h" #include "lldb/Host/HostNativeProcessBase.h" #include "lldb/Host/HostProcess.h" +#include "lldb/Host/PseudoTerminal.h" +#include "lldb/Host/windows/ConnectionGenericFileWindows.h" +#include "lldb/Host/windows/ConnectionPseudoTerminalWindows.h" #include "lldb/Host/windows/HostThreadWindows.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/DynamicLoader.h" @@ -121,22 +124,6 @@ ProcessWindows::ProcessWindows(lldb::TargetSP target_sp, ProcessWindows::~ProcessWindows() {} -size_t ProcessWindows::GetSTDOUT(char *buf, size_t buf_size, Status &error) { - error = Status::FromErrorString("GetSTDOUT unsupported on Windows"); - return 0; -} - -size_t ProcessWindows::GetSTDERR(char *buf, size_t buf_size, Status &error) { - error = Status::FromErrorString("GetSTDERR unsupported on Windows"); - return 0; -} - -size_t ProcessWindows::PutSTDIN(const char *buf, size_t buf_size, - Status &error) { - error = Status::FromErrorString("PutSTDIN unsupported on Windows"); - return 0; -} - Status ProcessWindows::EnableBreakpointSite(BreakpointSite *bp_site) { if (bp_site->HardwareRequired()) return Status::FromErrorString("Hardware breakpoints are not supported."); @@ -659,6 +646,7 @@ void ProcessWindows::OnExitProcess(uint32_t exit_code) { LLDB_LOG(log, "Process {0} exited with code {1}", GetID(), exit_code); TargetSP target = CalculateTarget(); + target->GetProcessLaunchInfo().GetPTY()->Close(); if (target) { ModuleSP executable_module = target->GetExecutableModule(); ModuleList unloaded_modules; @@ -954,4 +942,17 @@ Status ProcessWindows::DisableWatchpoint(WatchpointSP wp_sp, bool notify) { return error; } + +void ProcessWindows::SetPseudoTerminalHandle( + const std::shared_ptr &pty) { + m_stdio_communication.SetConnection( + std::make_unique(pty->GetPrimaryHandle(), false)); + if (m_stdio_communication.IsConnected()) { + m_stdio_communication.SetReadThreadBytesReceivedCallback( + STDIOReadThreadBytesReceived, this); + m_stdio_communication.StartReadThread(); + + // TODO: Now read thread is set up, set up input reader. + } +} } // namespace lldb_private diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h index 97284b7cd1436..49596e49a9f82 100644 --- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h +++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h @@ -9,6 +9,7 @@ #ifndef liblldb_Plugins_Process_Windows_Common_ProcessWindows_H_ #define liblldb_Plugins_Process_Windows_Common_ProcessWindows_H_ +#include "lldb/Host/windows/PseudoTerminalWindows.h" #include "lldb/Target/Process.h" #include "lldb/Utility/Status.h" #include "lldb/lldb-forward.h" @@ -38,10 +39,6 @@ class ProcessWindows : public Process, public ProcessDebugger { ~ProcessWindows(); - size_t GetSTDOUT(char *buf, size_t buf_size, Status &error) override; - size_t GetSTDERR(char *buf, size_t buf_size, Status &error) override; - size_t PutSTDIN(const char *buf, size_t buf_size, Status &error) override; - llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } Status EnableBreakpointSite(BreakpointSite *bp_site) override; @@ -101,6 +98,9 @@ class ProcessWindows : public Process, public ProcessDebugger { Status DisableWatchpoint(lldb::WatchpointSP wp_sp, bool notify = true) override; + void + SetPseudoTerminalHandle(const std::shared_ptr &pty) override; + protected: ProcessWindows(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp); diff --git a/lldb/source/Target/Platform.cpp b/lldb/source/Target/Platform.cpp index 5b0930cf26b77..2c569039624e6 100644 --- a/lldb/source/Target/Platform.cpp +++ b/lldb/source/Target/Platform.cpp @@ -1054,7 +1054,7 @@ lldb::ProcessSP Platform::DebugProcess(ProcessLaunchInfo &launch_info, // been used where the secondary side was given as the file to open for // stdin/out/err after we have already opened the primary so we can // read/write stdin/out/err. - int pty_fd = launch_info.GetPTY().ReleasePrimaryFileDescriptor(); + int pty_fd = launch_info.GetPTY()->ReleasePrimaryFileDescriptor(); if (pty_fd != PseudoTerminal::invalid_fd) { process_sp->SetSTDIOFileDescriptor(pty_fd); } diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index 69edea503002e..7059726671e05 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -6545,7 +6545,7 @@ Status Process::WriteMemoryTags(lldb::addr_t addr, size_t len, // Create a CoreFileMemoryRange from a MemoryRegionInfo static CoreFileMemoryRange -CreateCoreFileMemoryRange(const MemoryRegionInfo ®ion) { +CreateCoreFileMemoryRange(const lldb_private::MemoryRegionInfo ®ion) { const addr_t addr = region.GetRange().GetRangeBase(); llvm::AddressRange range(addr, addr + region.GetRange().GetByteSize()); return {range, region.GetLLDBPermissions()}; @@ -6554,7 +6554,7 @@ CreateCoreFileMemoryRange(const MemoryRegionInfo ®ion) { // Add dirty pages to the core file ranges and return true if dirty pages // were added. Return false if the dirty page information is not valid or in // the region. -static bool AddDirtyPages(const MemoryRegionInfo ®ion, +static bool AddDirtyPages(const lldb_private::MemoryRegionInfo ®ion, CoreFileMemoryRanges &ranges) { const auto &dirty_page_list = region.GetDirtyPageList(); if (!dirty_page_list) @@ -6593,8 +6593,8 @@ static bool AddDirtyPages(const MemoryRegionInfo ®ion, // given region. If the region has dirty page information, only dirty pages // will be added to \a ranges, else the entire range will be added to \a // ranges. -static void AddRegion(const MemoryRegionInfo ®ion, bool try_dirty_pages, - CoreFileMemoryRanges &ranges) { +static void AddRegion(const lldb_private::MemoryRegionInfo ®ion, + bool try_dirty_pages, CoreFileMemoryRanges &ranges) { // Don't add empty ranges. if (region.GetRange().GetByteSize() == 0) return; @@ -6617,7 +6617,7 @@ static void SaveDynamicLoaderSections(Process &process, if (!dyld) return; - std::vector dynamic_loader_mem_regions; + std::vector dynamic_loader_mem_regions; std::function save_thread_predicate = [&](const lldb_private::Thread &t) -> bool { return options.ShouldThreadBeSaved(t.GetID()); @@ -6742,10 +6742,11 @@ static void GetCoreFileSaveRangesStackOnly(Process &process, // TODO: We should refactor CoreFileMemoryRanges to use the lldb range type, and // then add an intersect method on it, or MemoryRegionInfo. -static MemoryRegionInfo Intersect(const MemoryRegionInfo &lhs, - const MemoryRegionInfo::RangeType &rhs) { +static lldb_private::MemoryRegionInfo +Intersect(const lldb_private::MemoryRegionInfo &lhs, + const MemoryRegionInfo::RangeType &rhs) { - MemoryRegionInfo region_info; + lldb_private::MemoryRegionInfo region_info; region_info.SetLLDBPermissions(lhs.GetLLDBPermissions()); region_info.GetRange() = lhs.GetRange().Intersect(rhs); diff --git a/lldb/test/API/commands/platform/basic/TestPlatformCommand.py b/lldb/test/API/commands/platform/basic/TestPlatformCommand.py index 1e1a476c44a92..9becd03438c68 100644 --- a/lldb/test/API/commands/platform/basic/TestPlatformCommand.py +++ b/lldb/test/API/commands/platform/basic/TestPlatformCommand.py @@ -100,6 +100,7 @@ def test_shell_timeout(self): def test_host_shell_interpreter(self): """Test the host platform shell with a different interpreter""" self.build() + breakpoint() exe = self.getBuildArtifact("a.out") self.expect( "platform shell -h -s " + exe + " -- 'echo $0'", diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py index ca881f1d817c5..4196c8be24fc8 100644 --- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py +++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py @@ -16,7 +16,6 @@ class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase): - @skipIfWindows def test_default(self): """ Tests the default launch of a simple program. No arguments, diff --git a/lldb/tools/lldb-dap/tool/lldb-dap.cpp b/lldb/tools/lldb-dap/tool/lldb-dap.cpp index f10ed12344cbd..dc016638d4223 100644 --- a/lldb/tools/lldb-dap/tool/lldb-dap.cpp +++ b/lldb/tools/lldb-dap/tool/lldb-dap.cpp @@ -542,6 +542,9 @@ static llvm::Error serveConnection( } int main(int argc, char *argv[]) { + int zzz = 0; + while (zzz) { + } llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false); #if !defined(__APPLE__) llvm::setBugReportMsg("PLEASE submit a bug report to " LLDB_BUG_REPORT_URL diff --git a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp index 012eae02d5857..966b37e09ee55 100644 --- a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp +++ b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp @@ -326,7 +326,7 @@ TEST_F(GDBRemoteCommunicationClientTest, SendSignalsToIgnore) { TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfo) { const lldb::addr_t addr = 0xa000; - MemoryRegionInfo region_info; + lldb_private::MemoryRegionInfo region_info; std::future result = std::async(std::launch::async, [&] { return client.GetMemoryRegionInfo(addr, region_info); }); @@ -343,13 +343,16 @@ TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfo) { EXPECT_TRUE(result.get().Success()); EXPECT_EQ(addr, region_info.GetRange().GetRangeBase()); EXPECT_EQ(0x2000u, region_info.GetRange().GetByteSize()); - EXPECT_EQ(MemoryRegionInfo::eYes, region_info.GetReadable()); - EXPECT_EQ(MemoryRegionInfo::eNo, region_info.GetWritable()); - EXPECT_EQ(MemoryRegionInfo::eYes, region_info.GetExecutable()); + EXPECT_EQ(lldb_private::MemoryRegionInfo::eYes, region_info.GetReadable()); + EXPECT_EQ(lldb_private::MemoryRegionInfo::eNo, region_info.GetWritable()); + EXPECT_EQ(lldb_private::MemoryRegionInfo::eYes, region_info.GetExecutable()); EXPECT_EQ("/foo/bar.so", region_info.GetName().GetStringRef()); - EXPECT_EQ(MemoryRegionInfo::eDontKnow, region_info.GetMemoryTagged()); - EXPECT_EQ(MemoryRegionInfo::eDontKnow, region_info.IsStackMemory()); - EXPECT_EQ(MemoryRegionInfo::eDontKnow, region_info.IsShadowStack()); + EXPECT_EQ(lldb_private::MemoryRegionInfo::eDontKnow, + region_info.GetMemoryTagged()); + EXPECT_EQ(lldb_private::MemoryRegionInfo::eDontKnow, + region_info.IsStackMemory()); + EXPECT_EQ(lldb_private::MemoryRegionInfo::eDontKnow, + region_info.IsShadowStack()); result = std::async(std::launch::async, [&] { return client.GetMemoryRegionInfo(addr, region_info); @@ -358,9 +361,9 @@ TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfo) { HandlePacket(server, "qMemoryRegionInfo:a000", "start:a000;size:2000;flags:;type:stack;"); EXPECT_TRUE(result.get().Success()); - EXPECT_EQ(MemoryRegionInfo::eNo, region_info.GetMemoryTagged()); - EXPECT_EQ(MemoryRegionInfo::eYes, region_info.IsStackMemory()); - EXPECT_EQ(MemoryRegionInfo::eNo, region_info.IsShadowStack()); + EXPECT_EQ(lldb_private::MemoryRegionInfo::eNo, region_info.GetMemoryTagged()); + EXPECT_EQ(lldb_private::MemoryRegionInfo::eYes, region_info.IsStackMemory()); + EXPECT_EQ(lldb_private::MemoryRegionInfo::eNo, region_info.IsShadowStack()); result = std::async(std::launch::async, [&] { return client.GetMemoryRegionInfo(addr, region_info); @@ -369,9 +372,10 @@ TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfo) { HandlePacket(server, "qMemoryRegionInfo:a000", "start:a000;size:2000;flags: mt zz mt ss ;type:ha,ha,stack;"); EXPECT_TRUE(result.get().Success()); - EXPECT_EQ(MemoryRegionInfo::eYes, region_info.GetMemoryTagged()); - EXPECT_EQ(MemoryRegionInfo::eYes, region_info.IsStackMemory()); - EXPECT_EQ(MemoryRegionInfo::eYes, region_info.IsShadowStack()); + EXPECT_EQ(lldb_private::MemoryRegionInfo::eYes, + region_info.GetMemoryTagged()); + EXPECT_EQ(lldb_private::MemoryRegionInfo::eYes, region_info.IsStackMemory()); + EXPECT_EQ(lldb_private::MemoryRegionInfo::eYes, region_info.IsShadowStack()); result = std::async(std::launch::async, [&] { return client.GetMemoryRegionInfo(addr, region_info); @@ -380,12 +384,12 @@ TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfo) { HandlePacket(server, "qMemoryRegionInfo:a000", "start:a000;size:2000;type:heap;"); EXPECT_TRUE(result.get().Success()); - EXPECT_EQ(MemoryRegionInfo::eNo, region_info.IsStackMemory()); + EXPECT_EQ(lldb_private::MemoryRegionInfo::eNo, region_info.IsStackMemory()); } TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfoInvalidResponse) { const lldb::addr_t addr = 0x4000; - MemoryRegionInfo region_info; + lldb_private::MemoryRegionInfo region_info; std::future result = std::async(std::launch::async, [&] { return client.GetMemoryRegionInfo(addr, region_info); }); diff --git a/llvm/utils/gn/secondary/lldb/source/Host/BUILD.gn b/llvm/utils/gn/secondary/lldb/source/Host/BUILD.gn index 10c5f95edf5fd..40bad5afe5beb 100644 --- a/llvm/utils/gn/secondary/lldb/source/Host/BUILD.gn +++ b/llvm/utils/gn/secondary/lldb/source/Host/BUILD.gn @@ -65,6 +65,7 @@ static_library("Host") { if (current_os == "win") { sources += [ "windows/ConnectionGenericFileWindows.cpp", + "windows/ConnectionPseudoTerminalWindows.cpp", "windows/FileSystem.cpp", "windows/Host.cpp", "windows/HostInfoWindows.cpp", @@ -74,6 +75,7 @@ static_library("Host") { "windows/MainLoopWindows.cpp", "windows/PipeWindows.cpp", "windows/ProcessLauncherWindows.cpp", + "windows/PseudoTerminalWindows.cpp", "windows/ProcessRunLock.cpp", ] } else {