[Lldb-commits] [PATCH] D82396: [lldb/ScriptInterpreter] Extract IO redirection logic and move it out of ScriptInterpreterPython (NFC)

2020-06-26 Thread Pavel Labath via Phabricator via lldb-commits
labath added inline comments.



Comment at: lldb/include/lldb/Interpreter/ScriptInterpreter.h:57-58
 private:
+  friend std::unique_ptr
+  std::make_unique();
+

labath wrote:
> If that works, I suppose it's fine. But I wouldn't be surprised if this trick 
> backfires on some STL implementations.
> 
> I think that making an exception for make_unique on classes with private 
> constructors is fine (we already have a bunch of classes like that)...
BTW, i've just thought of another reason to not do the friend thingy -- making 
std::make_unique allows _anyone_ to construct this class via std::make_unique, 
which defeats the purpose of making the constructor private.

OTOH, if we can successfully separate the fallible and infallible parts of the 
construction, then making the infallible part (constructor) public may not be 
that dangerous. For example, in this case, making the `(FileUP, FileUP)` 
constructor public might be ok.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D82396/new/

https://reviews.llvm.org/D82396



___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [PATCH] D82396: [lldb/ScriptInterpreter] Extract IO redirection logic and move it out of ScriptInterpreterPython (NFC)

2020-06-25 Thread Jonas Devlieghere via Phabricator via lldb-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGd79273c941d5: [lldb/ScriptInterpreter] Extract IO 
redirection logic (authored by JDevlieghere).
Herald added a project: LLDB.

Changed prior to commit:
  https://reviews.llvm.org/D82396?vs=273068=273429#toc

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D82396/new/

https://reviews.llvm.org/D82396

Files:
  lldb/include/lldb/Interpreter/ScriptInterpreter.h
  lldb/source/Interpreter/ScriptInterpreter.cpp
  lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp

Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
===
--- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -42,6 +42,7 @@
 
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/FormatAdapters.h"
 
@@ -895,15 +896,6 @@
   return m_run_one_line_function.IsValid();
 }
 
-static void ReadThreadBytesReceived(void *baton, const void *src,
-size_t src_len) {
-  if (src && src_len) {
-Stream *strm = (Stream *)baton;
-strm->Write(src, src_len);
-strm->Flush();
-  }
-}
-
 bool ScriptInterpreterPythonImpl::ExecuteOneLine(
 llvm::StringRef command, CommandReturnObject *result,
 const ExecuteScriptOptions ) {
@@ -919,77 +911,23 @@
 // another string to pass to PyRun_SimpleString messes up the escaping.  So
 // we use the following more complicated method to pass the command string
 // directly down to Python.
-Debugger  = m_debugger;
-
-FileSP input_file_sp;
-StreamFileSP output_file_sp;
-StreamFileSP error_file_sp;
-Communication output_comm(
-"lldb.ScriptInterpreterPythonImpl.ExecuteOneLine.comm");
-bool join_read_thread = false;
-if (options.GetEnableIO()) {
-  if (result) {
-input_file_sp = debugger.GetInputFileSP();
-// Set output to a temporary file so we can forward the results on to
-// the result object
-
-Pipe pipe;
-Status pipe_result = pipe.CreateNew(false);
-if (pipe_result.Success()) {
-#if defined(_WIN32)
-  lldb::file_t read_file = pipe.GetReadNativeHandle();
-  pipe.ReleaseReadFileDescriptor();
-  std::unique_ptr conn_up(
-  new ConnectionGenericFile(read_file, true));
-#else
-  std::unique_ptr conn_up(
-  new ConnectionFileDescriptor(pipe.ReleaseReadFileDescriptor(),
-   true));
-#endif
-  if (conn_up->IsConnected()) {
-output_comm.SetConnection(std::move(conn_up));
-output_comm.SetReadThreadBytesReceivedCallback(
-ReadThreadBytesReceived, >GetOutputStream());
-output_comm.StartReadThread();
-join_read_thread = true;
-FILE *outfile_handle =
-fdopen(pipe.ReleaseWriteFileDescriptor(), "w");
-output_file_sp = std::make_shared(outfile_handle, true);
-error_file_sp = output_file_sp;
-if (outfile_handle)
-  ::setbuf(outfile_handle, nullptr);
-
-result->SetImmediateOutputFile(
-debugger.GetOutputStream().GetFileSP());
-result->SetImmediateErrorFile(
-debugger.GetErrorStream().GetFileSP());
-  }
-}
-  }
-  if (!input_file_sp || !output_file_sp || !error_file_sp)
-debugger.AdoptTopIOHandlerFilesIfInvalid(input_file_sp, output_file_sp,
- error_file_sp);
-} else {
-  auto nullin = FileSystem::Instance().Open(
-  FileSpec(FileSystem::DEV_NULL),
-  File::eOpenOptionRead);
-  auto nullout = FileSystem::Instance().Open(
-  FileSpec(FileSystem::DEV_NULL),
-  File::eOpenOptionWrite);
-  if (!nullin) {
-result->AppendErrorWithFormatv("failed to open /dev/null: {0}\n",
-   llvm::fmt_consume(nullin.takeError()));
-return false;
-  }
-  if (!nullout) {
-result->AppendErrorWithFormatv("failed to open /dev/null: {0}\n",
-   llvm::fmt_consume(nullout.takeError()));
-return false;
-  }
-  input_file_sp = std::move(nullin.get());
-  error_file_sp = output_file_sp = std::make_shared(std::move(nullout.get()));
+llvm::Expected>
+io_redirect_or_error =
+options.GetEnableIO()
+? ScriptInterpreterIORedirect::Create(m_debugger, result)
+: ScriptInterpreterIORedirect::Create();
+ 

[Lldb-commits] [PATCH] D82396: [lldb/ScriptInterpreter] Extract IO redirection logic and move it out of ScriptInterpreterPython (NFC)

2020-06-25 Thread Pavel Labath via Phabricator via lldb-commits
labath accepted this revision.
labath added a comment.
This revision is now accepted and ready to land.

I think this looks good, though it looks like you have uploaded an partial 
patch...




Comment at: lldb/include/lldb/Interpreter/ScriptInterpreter.h:57-58
 private:
+  friend std::unique_ptr
+  std::make_unique();
+

If that works, I suppose it's fine. But I wouldn't be surprised if this trick 
backfires on some STL implementations.

I think that making an exception for make_unique on classes with private 
constructors is fine (we already have a bunch of classes like that)...



Comment at: lldb/source/Interpreter/ScriptInterpreter.cpp:128-168
   if (result) {
-m_input_file_sp = debugger.GetInputFileSP();
+redirect->m_input_file_sp = debugger.GetInputFileSP();
 
 Pipe pipe;
 Status pipe_result = pipe.CreateNew(false);
 #if defined(_WIN32)
 lldb::file_t read_file = pipe.GetReadNativeHandle();

Given that none of this fails (though maybe it should), I think it would be 
cleaner to keep it in the constructor. You can still keep the static factory 
thing as a wrapper if you want symmetry.



Comment at: lldb/source/Interpreter/ScriptInterpreter.cpp:184-190
+  std::unique_ptr redirect =
+  std::make_unique();
+  redirect->m_input_file_sp = std::move(nullin.get());
+  redirect->m_error_file_sp = redirect->m_output_file_sp =
   std::make_shared(std::move(nullout.get()));
+
+  return redirect;

Similarly, I would find this better as `return 
std::make_unique(std::move(nullin.get()), 
std::move(nullout.get()))` (with an appropriate constructor. Mainly because 
then I don't need to worry about what will the destructor of 
ScriptInterpreterIORedirect do if we return with the object in an partially 
initialized state.


Repository:
  rLLDB LLDB

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D82396/new/

https://reviews.llvm.org/D82396



___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [PATCH] D82396: [lldb/ScriptInterpreter] Extract IO redirection logic and move it out of ScriptInterpreterPython (NFC)

2020-06-24 Thread Jonas Devlieghere via Phabricator via lldb-commits
JDevlieghere updated this revision to Diff 273068.
JDevlieghere added a comment.

This now has a small function change w.r.t. the error message.


Repository:
  rLLDB LLDB

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D82396/new/

https://reviews.llvm.org/D82396

Files:
  lldb/include/lldb/Interpreter/ScriptInterpreter.h
  lldb/source/Interpreter/ScriptInterpreter.cpp
  lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp

Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
===
--- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -911,23 +911,23 @@
 // another string to pass to PyRun_SimpleString messes up the escaping.  So
 // we use the following more complicated method to pass the command string
 // directly down to Python.
-std::unique_ptr io_redirect;
-if (options.GetEnableIO()) {
-  io_redirect =
-  std::make_unique(m_debugger, result);
-} else {
-  llvm::Error error = llvm::Error::success();
-  io_redirect = std::make_unique(error);
-  if (error) {
-if (result)
-  result->AppendErrorWithFormatv("failed to open /dev/null: {0}\n",
- llvm::fmt_consume(std::move(error)));
-else
-  llvm::consumeError(std::move(error));
-return false;
-  }
+llvm::Expected>
+io_redirect_or_error =
+options.GetEnableIO()
+? ScriptInterpreterIORedirect::Create(m_debugger, result)
+: ScriptInterpreterIORedirect::Create();
+if (!io_redirect_or_error) {
+  if (result)
+result->AppendErrorWithFormatv(
+"failed to redirect I/O: {0}\n",
+llvm::fmt_consume(io_redirect_or_error.takeError()));
+  else
+llvm::consumeError(io_redirect_or_error.takeError());
+  return false;
 }
 
+ScriptInterpreterIORedirect _redirect = **io_redirect_or_error;
+
 bool success = false;
 {
   // WARNING!  It's imperative that this RAII scope be as tight as
@@ -944,8 +944,8 @@
   (options.GetSetLLDBGlobals() ? Locker::InitGlobals : 0) |
   ((result && result->GetInteractive()) ? 0 : Locker::NoSTDIN),
   Locker::FreeAcquiredLock | Locker::TearDownSession,
-  io_redirect->GetInputFile(), io_redirect->GetOutputFile(),
-  io_redirect->GetErrorFile());
+  io_redirect.GetInputFile(), io_redirect.GetOutputFile(),
+  io_redirect.GetErrorFile());
 
   // Find the correct script interpreter dictionary in the main module.
   PythonDictionary _dict = GetSessionDictionary();
@@ -971,7 +971,7 @@
 }
   }
 
-  io_redirect->Flush();
+  io_redirect.Flush();
 }
 
 if (success)
Index: lldb/source/Interpreter/ScriptInterpreter.cpp
===
--- lldb/source/Interpreter/ScriptInterpreter.cpp
+++ lldb/source/Interpreter/ScriptInterpreter.cpp
@@ -119,12 +119,14 @@
   }
 }
 
-ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
-Debugger , CommandReturnObject *result)
-: m_communication("lldb.ScriptInterpreterIORedirect.comm"),
-  m_disconnect(false) {
+llvm::Expected>
+ScriptInterpreterIORedirect::Create(Debugger ,
+CommandReturnObject *result) {
+  std::unique_ptr redirect =
+  std::make_unique();
+
   if (result) {
-m_input_file_sp = debugger.GetInputFileSP();
+redirect->m_input_file_sp = debugger.GetInputFileSP();
 
 Pipe pipe;
 Status pipe_result = pipe.CreateNew(false);
@@ -140,15 +142,16 @@
 #endif
 
 if (conn_up->IsConnected()) {
-  m_communication.SetConnection(std::move(conn_up));
-  m_communication.SetReadThreadBytesReceivedCallback(
+  redirect->m_communication.SetConnection(std::move(conn_up));
+  redirect->m_communication.SetReadThreadBytesReceivedCallback(
   ReadThreadBytesReceived, >GetOutputStream());
-  m_communication.StartReadThread();
-  m_disconnect = true;
+  redirect->m_communication.StartReadThread();
+  redirect->m_disconnect = true;
 
   FILE *outfile_handle = fdopen(pipe.ReleaseWriteFileDescriptor(), "w");
-  m_output_file_sp = std::make_shared(outfile_handle, true);
-  m_error_file_sp = m_output_file_sp;
+  redirect->m_output_file_sp =
+  std::make_shared(outfile_handle, true);
+  redirect->m_error_file_sp = redirect->m_output_file_sp;
   if (outfile_handle)
 ::setbuf(outfile_handle, nullptr);
 
@@ -157,33 +160,40 @@
 }
   }
 
-  if (!m_input_file_sp || !m_output_file_sp || !m_error_file_sp)
-debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_file_sp, m_output_file_sp,
- m_error_file_sp);
+  if 

[Lldb-commits] [PATCH] D82396: [lldb/ScriptInterpreter] Extract IO redirection logic and move it out of ScriptInterpreterPython (NFC)

2020-06-24 Thread Jonas Devlieghere via Phabricator via lldb-commits
JDevlieghere marked an inline comment as done.
JDevlieghere added inline comments.



Comment at: lldb/source/Interpreter/ScriptInterpreter.cpp:171
+  if (!nullin) {
+error = nullin.takeError();
+return;

labath wrote:
> I'm pretty sure this will trigger an assertion about overwriting an unchecked 
> error. One way to handle this would be to wrap the error in an 
> `ErrorAsOutParameter` object, but I think it's better to replace the 
> constructor with a static factory function returning an 
> `Expected`.
That's how I did it at first, but I found this more elegant and compact. Maybe 
the `ErrorAsOutParameter` would tip the scales. I'll change it back to a 
factory. 


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D82396/new/

https://reviews.llvm.org/D82396



___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [PATCH] D82396: [lldb/ScriptInterpreter] Extract IO redirection logic and move it out of ScriptInterpreterPython (NFC)

2020-06-24 Thread Pavel Labath via Phabricator via lldb-commits
labath added a comment.

Sounds like a good idea, just the error handling needs to be done carefully.




Comment at: lldb/source/Interpreter/ScriptInterpreter.cpp:171
+  if (!nullin) {
+error = nullin.takeError();
+return;

I'm pretty sure this will trigger an assertion about overwriting an unchecked 
error. One way to handle this would be to wrap the error in an 
`ErrorAsOutParameter` object, but I think it's better to replace the 
constructor with a static factory function returning an `Expected`.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D82396/new/

https://reviews.llvm.org/D82396



___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [PATCH] D82396: [lldb/ScriptInterpreter] Extract IO redirection logic and move it out of ScriptInterpreterPython (NFC)

2020-06-23 Thread Jonas Devlieghere via Phabricator via lldb-commits
JDevlieghere updated this revision to Diff 272825.
JDevlieghere added a comment.

Consume the error when the result pointer is null.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D82396/new/

https://reviews.llvm.org/D82396

Files:
  lldb/include/lldb/Interpreter/ScriptInterpreter.h
  lldb/source/Interpreter/ScriptInterpreter.cpp
  lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp

Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
===
--- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -42,6 +42,7 @@
 
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/FormatAdapters.h"
 
@@ -895,15 +896,6 @@
   return m_run_one_line_function.IsValid();
 }
 
-static void ReadThreadBytesReceived(void *baton, const void *src,
-size_t src_len) {
-  if (src && src_len) {
-Stream *strm = (Stream *)baton;
-strm->Write(src, src_len);
-strm->Flush();
-  }
-}
-
 bool ScriptInterpreterPythonImpl::ExecuteOneLine(
 llvm::StringRef command, CommandReturnObject *result,
 const ExecuteScriptOptions ) {
@@ -919,75 +911,21 @@
 // another string to pass to PyRun_SimpleString messes up the escaping.  So
 // we use the following more complicated method to pass the command string
 // directly down to Python.
-Debugger  = m_debugger;
-
-FileSP input_file_sp;
-StreamFileSP output_file_sp;
-StreamFileSP error_file_sp;
-Communication output_comm(
-"lldb.ScriptInterpreterPythonImpl.ExecuteOneLine.comm");
-bool join_read_thread = false;
+std::unique_ptr io_redirect;
 if (options.GetEnableIO()) {
-  if (result) {
-input_file_sp = debugger.GetInputFileSP();
-// Set output to a temporary file so we can forward the results on to
-// the result object
-
-Pipe pipe;
-Status pipe_result = pipe.CreateNew(false);
-if (pipe_result.Success()) {
-#if defined(_WIN32)
-  lldb::file_t read_file = pipe.GetReadNativeHandle();
-  pipe.ReleaseReadFileDescriptor();
-  std::unique_ptr conn_up(
-  new ConnectionGenericFile(read_file, true));
-#else
-  std::unique_ptr conn_up(
-  new ConnectionFileDescriptor(pipe.ReleaseReadFileDescriptor(),
-   true));
-#endif
-  if (conn_up->IsConnected()) {
-output_comm.SetConnection(std::move(conn_up));
-output_comm.SetReadThreadBytesReceivedCallback(
-ReadThreadBytesReceived, >GetOutputStream());
-output_comm.StartReadThread();
-join_read_thread = true;
-FILE *outfile_handle =
-fdopen(pipe.ReleaseWriteFileDescriptor(), "w");
-output_file_sp = std::make_shared(outfile_handle, true);
-error_file_sp = output_file_sp;
-if (outfile_handle)
-  ::setbuf(outfile_handle, nullptr);
-
-result->SetImmediateOutputFile(
-debugger.GetOutputStream().GetFileSP());
-result->SetImmediateErrorFile(
-debugger.GetErrorStream().GetFileSP());
-  }
-}
-  }
-  if (!input_file_sp || !output_file_sp || !error_file_sp)
-debugger.AdoptTopIOHandlerFilesIfInvalid(input_file_sp, output_file_sp,
- error_file_sp);
+  io_redirect =
+  std::make_unique(m_debugger, result);
 } else {
-  auto nullin = FileSystem::Instance().Open(
-  FileSpec(FileSystem::DEV_NULL),
-  File::eOpenOptionRead);
-  auto nullout = FileSystem::Instance().Open(
-  FileSpec(FileSystem::DEV_NULL),
-  File::eOpenOptionWrite);
-  if (!nullin) {
-result->AppendErrorWithFormatv("failed to open /dev/null: {0}\n",
-   llvm::fmt_consume(nullin.takeError()));
-return false;
-  }
-  if (!nullout) {
-result->AppendErrorWithFormatv("failed to open /dev/null: {0}\n",
-   llvm::fmt_consume(nullout.takeError()));
+  llvm::Error error = llvm::Error::success();
+  io_redirect = std::make_unique(error);
+  if (error) {
+if (result)
+  result->AppendErrorWithFormatv("failed to open /dev/null: {0}\n",
+ llvm::fmt_consume(std::move(error)));
+else
+  llvm::consumeError(std::move(error));
 return false;
   }
-  input_file_sp = std::move(nullin.get());
-  error_file_sp = output_file_sp = 

[Lldb-commits] [PATCH] D82396: [lldb/ScriptInterpreter] Extract IO redirection logic and move it out of ScriptInterpreterPython (NFC)

2020-06-23 Thread Jonas Devlieghere via Phabricator via lldb-commits
JDevlieghere created this revision.
JDevlieghere added reviewers: labath, LLDB.

This patch takes the IO redirection logic from ScriptInterpreterPython and 
moves it into the interpreter library so that it can be used by other script 
interpreters. I've turned it into a RAII object so that we don't have to worry 
about cleaning up in the calling code.


Repository:
  rLLDB LLDB

https://reviews.llvm.org/D82396

Files:
  lldb/include/lldb/Interpreter/ScriptInterpreter.h
  lldb/source/Interpreter/ScriptInterpreter.cpp
  lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp

Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
===
--- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -42,6 +42,7 @@
 
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/FormatAdapters.h"
 
@@ -895,15 +896,6 @@
   return m_run_one_line_function.IsValid();
 }
 
-static void ReadThreadBytesReceived(void *baton, const void *src,
-size_t src_len) {
-  if (src && src_len) {
-Stream *strm = (Stream *)baton;
-strm->Write(src, src_len);
-strm->Flush();
-  }
-}
-
 bool ScriptInterpreterPythonImpl::ExecuteOneLine(
 llvm::StringRef command, CommandReturnObject *result,
 const ExecuteScriptOptions ) {
@@ -919,75 +911,18 @@
 // another string to pass to PyRun_SimpleString messes up the escaping.  So
 // we use the following more complicated method to pass the command string
 // directly down to Python.
-Debugger  = m_debugger;
-
-FileSP input_file_sp;
-StreamFileSP output_file_sp;
-StreamFileSP error_file_sp;
-Communication output_comm(
-"lldb.ScriptInterpreterPythonImpl.ExecuteOneLine.comm");
-bool join_read_thread = false;
+std::unique_ptr io_redirect;
 if (options.GetEnableIO()) {
-  if (result) {
-input_file_sp = debugger.GetInputFileSP();
-// Set output to a temporary file so we can forward the results on to
-// the result object
-
-Pipe pipe;
-Status pipe_result = pipe.CreateNew(false);
-if (pipe_result.Success()) {
-#if defined(_WIN32)
-  lldb::file_t read_file = pipe.GetReadNativeHandle();
-  pipe.ReleaseReadFileDescriptor();
-  std::unique_ptr conn_up(
-  new ConnectionGenericFile(read_file, true));
-#else
-  std::unique_ptr conn_up(
-  new ConnectionFileDescriptor(pipe.ReleaseReadFileDescriptor(),
-   true));
-#endif
-  if (conn_up->IsConnected()) {
-output_comm.SetConnection(std::move(conn_up));
-output_comm.SetReadThreadBytesReceivedCallback(
-ReadThreadBytesReceived, >GetOutputStream());
-output_comm.StartReadThread();
-join_read_thread = true;
-FILE *outfile_handle =
-fdopen(pipe.ReleaseWriteFileDescriptor(), "w");
-output_file_sp = std::make_shared(outfile_handle, true);
-error_file_sp = output_file_sp;
-if (outfile_handle)
-  ::setbuf(outfile_handle, nullptr);
-
-result->SetImmediateOutputFile(
-debugger.GetOutputStream().GetFileSP());
-result->SetImmediateErrorFile(
-debugger.GetErrorStream().GetFileSP());
-  }
-}
-  }
-  if (!input_file_sp || !output_file_sp || !error_file_sp)
-debugger.AdoptTopIOHandlerFilesIfInvalid(input_file_sp, output_file_sp,
- error_file_sp);
+  io_redirect =
+  std::make_unique(m_debugger, result);
 } else {
-  auto nullin = FileSystem::Instance().Open(
-  FileSpec(FileSystem::DEV_NULL),
-  File::eOpenOptionRead);
-  auto nullout = FileSystem::Instance().Open(
-  FileSpec(FileSystem::DEV_NULL),
-  File::eOpenOptionWrite);
-  if (!nullin) {
+  llvm::Error error = llvm::Error::success();
+  io_redirect = std::make_unique(error);
+  if (error) {
 result->AppendErrorWithFormatv("failed to open /dev/null: {0}\n",
-   llvm::fmt_consume(nullin.takeError()));
+   llvm::fmt_consume(std::move(error)));
 return false;
   }
-  if (!nullout) {
-result->AppendErrorWithFormatv("failed to open /dev/null: {0}\n",
-   llvm::fmt_consume(nullout.takeError()));
-return false;
-  }
-  input_file_sp = std::move(nullin.get());
-  error_file_sp =