diff --git a/lldb/bindings/interface/SBDebugger.i b/lldb/bindings/interface/SBDebugger.i --- a/lldb/bindings/interface/SBDebugger.i +++ b/lldb/bindings/interface/SBDebugger.i @@ -206,6 +206,9 @@ } } + SBError + SetInputString (const char* data); + SBError SetInputFile (SBFile file); diff --git a/lldb/include/lldb/API/SBDebugger.h b/lldb/include/lldb/API/SBDebugger.h --- a/lldb/include/lldb/API/SBDebugger.h +++ b/lldb/include/lldb/API/SBDebugger.h @@ -126,6 +126,8 @@ FILE *GetErrorFileHandle(); + SBError SetInputString(const char *data); + SBError SetInputFile(SBFile file); SBError SetOutputFile(SBFile file); diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h --- a/lldb/include/lldb/Core/Debugger.h +++ b/lldb/include/lldb/Core/Debugger.h @@ -176,7 +176,13 @@ repro::DataRecorder *GetInputRecorder(); - void SetInputFile(lldb::FileSP file, repro::DataRecorder *recorder = nullptr); + Status SetInputString(const char *data); + + // This method will setup data recorder if reproducer enabled. + // On reply mode this method should take instructions from reproducer file. + Status SetInputFile(lldb::FileSP file); + + void SetInputFile(lldb::FileSP file, repro::DataRecorder *recorder); void SetOutputFile(lldb::FileSP file); diff --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp --- a/lldb/source/API/SBDebugger.cpp +++ b/lldb/source/API/SBDebugger.cpp @@ -327,12 +327,32 @@ void SBDebugger::SetInputFileHandle(FILE *fh, bool transfer_ownership) { LLDB_RECORD_METHOD(void, SBDebugger, SetInputFileHandle, (FILE *, bool), fh, transfer_ownership); - SetInputFile((FileSP)std::make_shared(fh, transfer_ownership)); + if (m_opaque_sp) + m_opaque_sp->SetInputFile( + (FileSP)std::make_shared(fh, transfer_ownership)); } -SBError SBDebugger::SetInputFile(FileSP file_sp) { - LLDB_RECORD_METHOD(SBError, SBDebugger, SetInputFile, (FileSP), file_sp); - return LLDB_RECORD_RESULT(SetInputFile(SBFile(file_sp))); +SBError SBDebugger::SetInputString(const char *data) { + LLDB_RECORD_METHOD(SBError, SBDebugger, SetInputString, (const char *), data); + SBError sb_error; + if (data == nullptr) { + sb_error.SetErrorString("String data is null"); + return LLDB_RECORD_RESULT(sb_error); + } + + size_t size = strlen(data); + if (size == 0) { + sb_error.SetErrorString("String data is empty"); + return LLDB_RECORD_RESULT(sb_error); + } + + if (!m_opaque_sp) { + sb_error.SetErrorString("invalid debugger"); + return LLDB_RECORD_RESULT(sb_error); + } + + sb_error.SetError(m_opaque_sp->SetInputString(data)); + return LLDB_RECORD_RESULT(sb_error); } // Shouldn't really be settable after initialization as this could cause lots @@ -346,36 +366,15 @@ error.ref().SetErrorString("invalid debugger"); return LLDB_RECORD_RESULT(error); } - - repro::DataRecorder *recorder = nullptr; - if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) - recorder = g->GetOrCreate().GetNewRecorder(); - - FileSP file_sp = file.m_opaque_sp; - - static std::unique_ptr> loader = - repro::MultiLoader::Create( - repro::Reproducer::Instance().GetLoader()); - if (loader) { - llvm::Optional nextfile = loader->GetNextFile(); - FILE *fh = nextfile ? FileSystem::Instance().Fopen(nextfile->c_str(), "r") - : nullptr; - // FIXME Jonas Devlieghere: shouldn't this error be propagated out to the - // reproducer somehow if fh is NULL? - if (fh) { - file_sp = std::make_shared(fh, true); - } - } - - if (!file_sp || !file_sp->IsValid()) { - error.ref().SetErrorString("invalid file"); - return LLDB_RECORD_RESULT(error); - } - - m_opaque_sp->SetInputFile(file_sp, recorder); + error.SetError(m_opaque_sp->SetInputFile(file.m_opaque_sp)); return LLDB_RECORD_RESULT(error); } +SBError SBDebugger::SetInputFile(FileSP file_sp) { + LLDB_RECORD_METHOD(SBError, SBDebugger, SetInputFile, (FileSP), file_sp); + return LLDB_RECORD_RESULT(SetInputFile(SBFile(file_sp))); +} + SBError SBDebugger::SetOutputFile(FileSP file_sp) { LLDB_RECORD_METHOD(SBError, SBDebugger, SetOutputFile, (FileSP), file_sp); return LLDB_RECORD_RESULT(SetOutputFile(SBFile(file_sp))); @@ -1771,6 +1770,7 @@ LLDB_REGISTER_METHOD(bool, SBDebugger, GetAsync, ()); LLDB_REGISTER_METHOD(void, SBDebugger, SkipLLDBInitFiles, (bool)); LLDB_REGISTER_METHOD(void, SBDebugger, SkipAppInitFiles, (bool)); + LLDB_REGISTER_METHOD(SBError, SBDebugger, SetInputString, (const char *)); LLDB_REGISTER_METHOD(void, SBDebugger, SetInputFileHandle, (FILE *, bool)); LLDB_REGISTER_METHOD(FILE *, SBDebugger, GetInputFileHandle, ()); LLDB_REGISTER_METHOD(FILE *, SBDebugger, GetOutputFileHandle, ()); diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -45,6 +45,7 @@ #include "lldb/Utility/Listener.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Reproducer.h" +#include "lldb/Utility/ReproducerProvider.h" #include "lldb/Utility/State.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/StreamCallback.h" @@ -75,6 +76,14 @@ #include #include +// Includes for pipe() +#if defined(_WIN32) +#include +#include +#else +#include +#endif + namespace lldb_private { class Address; } @@ -810,6 +819,86 @@ repro::DataRecorder *Debugger::GetInputRecorder() { return m_input_recorder; } +static inline int OpenPipe(int fds[2], std::size_t size) { +#ifdef _WIN32 + return _pipe(fds, size, O_BINARY); +#else + (void)size; + return pipe(fds); +#endif +} + +Status Debugger::SetInputString(const char *data) { + Status result; + enum PIPES { READ, WRITE }; // Indexes for the read and write fds + int fds[2] = {-1, -1}; + + if (data == nullptr) { + result.SetErrorString("String data is null"); + return result; + } + + size_t size = strlen(data); + if (size == 0) { + result.SetErrorString("String data is empty"); + return result; + } + + if (OpenPipe(fds, size) != 0) { + result.SetErrorString( + "can't create pipe file descriptors for LLDB commands"); + return result; + } + + write(fds[WRITE], data, size); + // Close the write end of the pipe, so that the command interpreter will exit + // when it consumes all the data. + llvm::sys::Process::SafelyCloseFileDescriptor(fds[WRITE]); + + // Open the read file descriptor as a FILE * that we can return as an input + // handle. + FILE *commands_file = fdopen(fds[READ], "rb"); + if (commands_file == nullptr) { + result.SetErrorStringWithFormat("fdopen(%i, \"rb\") failed (errno = %i) " + "when trying to open LLDB commands pipe", + fds[READ], errno); + llvm::sys::Process::SafelyCloseFileDescriptor(fds[READ]); + return result; + } + + return SetInputFile( + (FileSP)std::make_shared(commands_file, true)); +} + +Status Debugger::SetInputFile(FileSP file_sp) { + Status error; + repro::DataRecorder *recorder = nullptr; + if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) + recorder = g->GetOrCreate().GetNewRecorder(); + + static std::unique_ptr> loader = + repro::MultiLoader::Create( + repro::Reproducer::Instance().GetLoader()); + if (loader) { + llvm::Optional nextfile = loader->GetNextFile(); + FILE *fh = nextfile ? FileSystem::Instance().Fopen(nextfile->c_str(), "r") + : nullptr; + // FIXME Jonas Devlieghere: shouldn't this error be propagated out to the + // reproducer somehow if fh is NULL? + if (fh) { + file_sp = std::make_shared(fh, true); + } + } + + if (!file_sp || !file_sp->IsValid()) { + error.SetErrorString("invalid file"); + return error; + } + + SetInputFile(file_sp, recorder); + return error; +} + void Debugger::SetInputFile(FileSP file_sp, repro::DataRecorder *recorder) { assert(file_sp && file_sp->IsValid()); m_input_recorder = recorder; diff --git a/lldb/test/API/python_api/default-constructor/sb_debugger.py b/lldb/test/API/python_api/default-constructor/sb_debugger.py --- a/lldb/test/API/python_api/default-constructor/sb_debugger.py +++ b/lldb/test/API/python_api/default-constructor/sb_debugger.py @@ -13,6 +13,7 @@ obj.SetInputFileHandle(None, True) obj.SetOutputFileHandle(None, True) obj.SetErrorFileHandle(None, True) + obj.SetInputString("") obj.GetInputFileHandle() obj.GetOutputFileHandle() obj.GetErrorFileHandle() diff --git a/lldb/test/API/python_api/file_handle/TestFileHandle.py b/lldb/test/API/python_api/file_handle/TestFileHandle.py --- a/lldb/test/API/python_api/file_handle/TestFileHandle.py +++ b/lldb/test/API/python_api/file_handle/TestFileHandle.py @@ -852,3 +852,20 @@ self.assertTrue(f.closed) with open(self.out_filename, 'r') as f: self.assertEqual(f.read().strip(), "Frobozz") + + @skipIfReproducer # lldb::FileSP used in typemap cannot be instrumented. + def test_set_sbstream(self): + with open(self.out_filename, 'w') as outf: + outsbf = lldb.SBFile(outf.fileno(), "w", False) + status = self.dbg.SetOutputFile(outsbf) + self.assertTrue(status.Success()) + self.dbg.SetInputString("version\nhelp\n") + + opts = lldb.SBCommandInterpreterRunOptions() + self.dbg.RunCommandInterpreter(True, False, opts, 0, False, False) + self.dbg.GetOutputFile().Flush() + + with open(self.out_filename, 'r') as f: + output = f.read() + self.assertTrue(re.search(r'Show a list of all debugger commands', output)) + self.assertTrue(re.search(r'llvm revision', output)) \ No newline at end of file diff --git a/lldb/tools/driver/Driver.cpp b/lldb/tools/driver/Driver.cpp --- a/lldb/tools/driver/Driver.cpp +++ b/lldb/tools/driver/Driver.cpp @@ -24,7 +24,6 @@ #include "llvm/Support/Format.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/Path.h" -#include "llvm/Support/Process.h" #include "llvm/Support/Signals.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" @@ -44,14 +43,6 @@ #include #include -// Includes for pipe() -#if defined(_WIN32) -#include -#include -#else -#include -#endif - #if !defined(__APPLE__) #include "llvm/Support/DataTypes.h" #endif @@ -421,60 +412,6 @@ return error; } -static inline int OpenPipe(int fds[2], std::size_t size) { -#ifdef _WIN32 - return _pipe(fds, size, O_BINARY); -#else - (void)size; - return pipe(fds); -#endif -} - -static ::FILE *PrepareCommandsForSourcing(const char *commands_data, - size_t commands_size) { - enum PIPES { READ, WRITE }; // Indexes for the read and write fds - int fds[2] = {-1, -1}; - - if (OpenPipe(fds, commands_size) != 0) { - WithColor::error() - << "can't create pipe file descriptors for LLDB commands\n"; - return nullptr; - } - - ssize_t nrwr = write(fds[WRITE], commands_data, commands_size); - if (size_t(nrwr) != commands_size) { - WithColor::error() - << format( - "write(%i, %p, %" PRIu64 - ") failed (errno = %i) when trying to open LLDB commands pipe", - fds[WRITE], static_cast(commands_data), - static_cast(commands_size), errno) - << '\n'; - llvm::sys::Process::SafelyCloseFileDescriptor(fds[READ]); - llvm::sys::Process::SafelyCloseFileDescriptor(fds[WRITE]); - return nullptr; - } - - // Close the write end of the pipe, so that the command interpreter will exit - // when it consumes all the data. - llvm::sys::Process::SafelyCloseFileDescriptor(fds[WRITE]); - - // Open the read file descriptor as a FILE * that we can return as an input - // handle. - ::FILE *commands_file = fdopen(fds[READ], "rb"); - if (commands_file == nullptr) { - WithColor::error() << format("fdopen(%i, \"rb\") failed (errno = %i) " - "when trying to open LLDB commands pipe", - fds[READ], errno) - << '\n'; - llvm::sys::Process::SafelyCloseFileDescriptor(fds[READ]); - return nullptr; - } - - // 'commands_file' now owns the read descriptor. - return commands_file; -} - std::string EscapeString(std::string arg) { std::string::size_type pos = 0; while ((pos = arg.find_first_of("\"\\", pos)) != std::string::npos) { @@ -604,21 +541,15 @@ // Check if we have any data in the commands stream, and if so, save it to a // temp file // so we can then run the command interpreter using the file contents. - const char *commands_data = commands_stream.GetData(); - const size_t commands_size = commands_stream.GetSize(); - bool go_interactive = true; - if ((commands_data != nullptr) && (commands_size != 0u)) { - FILE *commands_file = - PrepareCommandsForSourcing(commands_data, commands_size); - - if (commands_file == nullptr) { - // We should have already printed an error in PrepareCommandsForSourcing. + if ((commands_stream.GetData() != nullptr) && + (commands_stream.GetSize() != 0u)) { + SBError error = m_debugger.SetInputString(commands_stream.GetData()); + if (error.Fail()) { + WithColor::error() << error.GetCString() << '\n'; return 1; } - m_debugger.SetInputFileHandle(commands_file, true); - // Set the debugger into Sync mode when running the command file. Otherwise // command files that run the target won't run in a sensible way. bool old_async = m_debugger.GetAsync(); @@ -651,12 +582,9 @@ SBStream crash_commands_stream; WriteCommandsForSourcing(eCommandPlacementAfterCrash, crash_commands_stream); - const char *crash_commands_data = crash_commands_stream.GetData(); - const size_t crash_commands_size = crash_commands_stream.GetSize(); - commands_file = - PrepareCommandsForSourcing(crash_commands_data, crash_commands_size); - if (commands_file != nullptr) { - m_debugger.SetInputFileHandle(commands_file, true); + SBError error = + m_debugger.SetInputString(crash_commands_stream.GetData()); + if (error.Success()) { SBCommandInterpreterRunResult local_results = m_debugger.RunCommandInterpreter(options); if (local_results.GetResult() ==