lawrence_danna created this revision.
lawrence_danna added reviewers: JDevlieghere, jasonmolenda, labath.
Herald added a project: LLDB.
Add new methods to SBDebugger to set IO files as SBFiles instead of
as FILE* streams.
In future commits, the FILE* methods will be deprecated and these
will become the primary way to set the debugger I/O streams.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D68181
Files:
lldb/include/lldb/API/SBDebugger.h
lldb/include/lldb/API/SBFile.h
lldb/include/lldb/Core/Debugger.h
lldb/packages/Python/lldbsuite/test/python_api/file_handle/TestFileHandle.py
lldb/scripts/interface/SBDebugger.i
lldb/source/API/SBDebugger.cpp
lldb/source/Core/Debugger.cpp
Index: lldb/source/Core/Debugger.cpp
===================================================================
--- lldb/source/Core/Debugger.cpp
+++ lldb/source/Core/Debugger.cpp
@@ -821,32 +821,42 @@
repro::DataRecorder *Debugger::GetInputRecorder() { return m_input_recorder; }
-void Debugger::SetInputFileHandle(FILE *fh, bool tranfer_ownership,
- repro::DataRecorder *recorder) {
+Status Debugger::SetInputFile(FileSP file_sp, repro::DataRecorder *recorder) {
+ Status error;
m_input_recorder = recorder;
- m_input_file_sp = std::make_shared<File>(fh, tranfer_ownership);
- if (!m_input_file_sp->IsValid())
+ if (!file_sp || !file_sp->IsValid()) {
m_input_file_sp = std::make_shared<File>(stdin, false);
+ error.SetErrorString("invalid file");
+ } else {
+ m_input_file_sp = file_sp;
+ }
// Save away the terminal state if that is relevant, so that we can restore
// it in RestoreInputState.
SaveInputTerminalState();
+
+ return error;
}
-void Debugger::SetOutputFileHandle(FILE *fh, bool tranfer_ownership) {
- FileSP file_sp = std::make_shared<File>(fh, tranfer_ownership);
- if (!file_sp->IsValid())
+Status Debugger::SetOutputFile(FileSP file_sp) {
+ Status error;
+ if (!file_sp || !file_sp->IsValid()) {
file_sp = std::make_shared<File>(stdout, false);
+ error.SetErrorString("invalid file");
+ }
m_output_stream_sp = std::make_shared<StreamFile>(file_sp);
-
+ return error;
}
-void Debugger::SetErrorFileHandle(FILE *fh, bool tranfer_ownership) {
- FileSP file_sp = std::make_shared<File>(fh, tranfer_ownership);
- if (!file_sp->IsValid())
+Status Debugger::SetErrorFile(FileSP file_sp) {
+ Status error;
+ if (!file_sp || !file_sp->IsValid()) {
file_sp = std::make_shared<File>(stderr, false);
+ error.SetErrorString("invalid file");
+ }
m_error_stream_sp = std::make_shared<StreamFile>(file_sp);
+ return error;
}
void Debugger::SaveInputTerminalState() {
Index: lldb/source/API/SBDebugger.cpp
===================================================================
--- lldb/source/API/SBDebugger.cpp
+++ lldb/source/API/SBDebugger.cpp
@@ -18,6 +18,7 @@
#include "lldb/API/SBCommandReturnObject.h"
#include "lldb/API/SBError.h"
#include "lldb/API/SBEvent.h"
+#include "lldb/API/SBFile.h"
#include "lldb/API/SBFrame.h"
#include "lldb/API/SBListener.h"
#include "lldb/API/SBProcess.h"
@@ -304,30 +305,91 @@
if (loader) {
llvm::Optional<std::string> file = loader->GetNextFile();
fh = file ? FileSystem::Instance().Fopen(file->c_str(), "r") : nullptr;
+ transfer_ownership = true;
}
- m_opaque_sp->SetInputFileHandle(fh, transfer_ownership, recorder);
+ m_opaque_sp->SetInputFile(std::make_shared<File>(fh, transfer_ownership), recorder);
+}
+
+SBError SBDebugger::SetInputFile(SBFile file) {
+ LLDB_RECORD_METHOD(SBError, SBDebugger, SetInputFile, (SBFile), file);
+
+ SBError error;
+ if (!m_opaque_sp) {
+ error.ref().SetErrorString("invalid debugger");
+ return error;
+ }
+
+ repro::DataRecorder *recorder = nullptr;
+ if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator())
+ recorder = g->GetOrCreate<repro::CommandProvider>().GetNewDataRecorder();
+
+ FileSP file_sp = file.m_opaque_sp;
+
+ static std::unique_ptr<repro::CommandLoader> loader =
+ repro::CommandLoader::Create(repro::Reproducer::Instance().GetLoader());
+ if (loader) {
+ llvm::Optional<std::string> nextfile = loader->GetNextFile();
+ FILE *fh = nextfile ? FileSystem::Instance().Fopen(nextfile->c_str(), "r")
+ : nullptr;
+ if (fh) {
+ file_sp = std::make_shared<File>(fh, true);
+ }
+ }
+
+ error.SetError(m_opaque_sp->SetInputFile(file_sp, recorder));
+ return error;
}
void SBDebugger::SetOutputFileHandle(FILE *fh, bool transfer_ownership) {
LLDB_RECORD_METHOD(void, SBDebugger, SetOutputFileHandle, (FILE *, bool), fh,
transfer_ownership);
- if (m_opaque_sp)
- m_opaque_sp->SetOutputFileHandle(fh, transfer_ownership);
+ if (m_opaque_sp) {
+ m_opaque_sp->SetOutputFile(std::make_shared<File>(fh, transfer_ownership));
+ }
+}
+
+SBError SBDebugger::SetOutputFile(SBFile file) {
+ LLDB_RECORD_METHOD(SBError, SBDebugger, SetOutputFile, (SBFile file), file);
+ SBError error;
+ if (!m_opaque_sp) {
+ error.ref().SetErrorString("invalid debugger");
+ return error;
+ }
+ if (!file) {
+ error.ref().SetErrorString("invalid file");
+ return error;
+ }
+ error.SetError(m_opaque_sp->SetOutputFile(file.m_opaque_sp));
+ return error;
}
void SBDebugger::SetErrorFileHandle(FILE *fh, bool transfer_ownership) {
LLDB_RECORD_METHOD(void, SBDebugger, SetErrorFileHandle, (FILE *, bool), fh,
transfer_ownership);
+ if (m_opaque_sp) {
+ m_opaque_sp->SetErrorFile(std::make_shared<File>(fh, transfer_ownership));
+ }
+}
- if (m_opaque_sp)
- m_opaque_sp->SetErrorFileHandle(fh, transfer_ownership);
+SBError SBDebugger::SetErrorFile(SBFile file) {
+ LLDB_RECORD_METHOD(SBError, SBDebugger, SetErrorFile, (SBFile file), file);
+ SBError error;
+ if (!m_opaque_sp) {
+ error.ref().SetErrorString("invalid debugger");
+ return error;
+ }
+ if (!file) {
+ error.ref().SetErrorString("invalid file");
+ return error;
+ }
+ error.SetError(m_opaque_sp->SetErrorFile(file.m_opaque_sp));
+ return error;
}
FILE *SBDebugger::GetInputFileHandle() {
LLDB_RECORD_METHOD_NO_ARGS(FILE *, SBDebugger, GetInputFileHandle);
-
if (m_opaque_sp) {
File &file_sp = m_opaque_sp->GetInputFile();
return LLDB_RECORD_RESULT(file_sp.GetStream());
@@ -335,9 +397,16 @@
return nullptr;
}
+SBFile SBDebugger::GetInputFile() {
+ LLDB_RECORD_METHOD_NO_ARGS(SBFile, SBDebugger, GetInputFile);
+ if (m_opaque_sp) {
+ return LLDB_RECORD_RESULT(SBFile(m_opaque_sp->GetInputFileSP()));
+ }
+ return LLDB_RECORD_RESULT(SBFile());
+}
+
FILE *SBDebugger::GetOutputFileHandle() {
LLDB_RECORD_METHOD_NO_ARGS(FILE *, SBDebugger, GetOutputFileHandle);
-
if (m_opaque_sp) {
StreamFile &stream_file = m_opaque_sp->GetOutputStream();
return LLDB_RECORD_RESULT(stream_file.GetFile().GetStream());
@@ -345,6 +414,15 @@
return nullptr;
}
+SBFile SBDebugger::GetOutputFile() {
+ LLDB_RECORD_METHOD_NO_ARGS(SBFile, SBDebugger, GetOutputFile);
+ if (m_opaque_sp) {
+ SBFile file(m_opaque_sp->GetOutputStream().GetFileSP());
+ return LLDB_RECORD_RESULT(file);
+ }
+ return LLDB_RECORD_RESULT(SBFile());
+}
+
FILE *SBDebugger::GetErrorFileHandle() {
LLDB_RECORD_METHOD_NO_ARGS(FILE *, SBDebugger, GetErrorFileHandle);
@@ -355,6 +433,16 @@
return nullptr;
}
+SBFile SBDebugger::GetErrorFile() {
+ LLDB_RECORD_METHOD_NO_ARGS(SBFile, SBDebugger, GetErrorFile);
+ SBFile file;
+ if (m_opaque_sp) {
+ SBFile file(m_opaque_sp->GetErrorStream().GetFileSP());
+ return LLDB_RECORD_RESULT(file);
+ }
+ return LLDB_RECORD_RESULT(SBFile());
+}
+
void SBDebugger::SaveInputTerminalState() {
LLDB_RECORD_METHOD_NO_ARGS(void, SBDebugger, SaveInputTerminalState);
@@ -1503,6 +1591,8 @@
// Do nothing.
}
+static SBError SetFileRedirect(SBDebugger *, SBFile file) { return SBError(); }
+
static bool GetDefaultArchitectureRedirect(char *arch_name,
size_t arch_name_len) {
// The function is writing to its argument. Without the redirect it would
@@ -1523,6 +1613,16 @@
&SBDebugger::GetDefaultArchitecture),
&GetDefaultArchitectureRedirect);
+ R.Register(&invoke<SBError (SBDebugger::*)(
+ SBFile)>::method<&SBDebugger::SetInputFile>::doit,
+ &SetFileRedirect);
+ R.Register(&invoke<SBError (SBDebugger::*)(
+ SBFile)>::method<&SBDebugger::SetOutputFile>::doit,
+ &SetFileRedirect);
+ R.Register(&invoke<SBError (SBDebugger::*)(
+ SBFile)>::method<&SBDebugger::SetErrorFile>::doit,
+ &SetFileRedirect);
+
LLDB_REGISTER_CONSTRUCTOR(SBDebugger, ());
LLDB_REGISTER_CONSTRUCTOR(SBDebugger, (const lldb::DebuggerSP &));
LLDB_REGISTER_CONSTRUCTOR(SBDebugger, (const lldb::SBDebugger &));
@@ -1547,6 +1647,9 @@
LLDB_REGISTER_METHOD(FILE *, SBDebugger, GetInputFileHandle, ());
LLDB_REGISTER_METHOD(FILE *, SBDebugger, GetOutputFileHandle, ());
LLDB_REGISTER_METHOD(FILE *, SBDebugger, GetErrorFileHandle, ());
+ LLDB_REGISTER_METHOD(SBFile, SBDebugger, GetInputFile, ());
+ LLDB_REGISTER_METHOD(SBFile, SBDebugger, GetOutputFile, ());
+ LLDB_REGISTER_METHOD(SBFile, SBDebugger, GetErrorFile, ());
LLDB_REGISTER_METHOD(void, SBDebugger, SaveInputTerminalState, ());
LLDB_REGISTER_METHOD(void, SBDebugger, RestoreInputTerminalState, ());
LLDB_REGISTER_METHOD(lldb::SBCommandInterpreter, SBDebugger,
Index: lldb/scripts/interface/SBDebugger.i
===================================================================
--- lldb/scripts/interface/SBDebugger.i
+++ lldb/scripts/interface/SBDebugger.i
@@ -183,6 +183,24 @@
FILE *
GetErrorFileHandle ();
+ SBError
+ SetInputFile (SBFile file);
+
+ SBError
+ SetOutputFile (SBFile file);
+
+ SBError
+ SetErrorFile (SBFile file);
+
+ SBFile
+ GetInputFile ();
+
+ SBFile
+ GetOutputFile ();
+
+ SBFile
+ GetErrorFile ();
+
lldb::SBCommandInterpreter
GetCommandInterpreter ();
Index: lldb/packages/Python/lldbsuite/test/python_api/file_handle/TestFileHandle.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/python_api/file_handle/TestFileHandle.py
+++ lldb/packages/Python/lldbsuite/test/python_api/file_handle/TestFileHandle.py
@@ -16,6 +16,15 @@
from lldbsuite.test import lldbutil
[email protected]
+def replace_stdout(new):
+ old = sys.stdout
+ sys.stdout = new
+ try:
+ yield
+ finally:
+ sys.stdout = old
+
def readStrippedLines(f):
def i():
for line in f:
@@ -34,6 +43,9 @@
else:
debugger.HandleCommand(cmd)
+ debugger.GetOutputFile().Flush()
+ debugger.GetErrorFile().Flush()
+
if collect_result and raise_on_fail and not ret.Succeeded():
raise Exception
@@ -76,6 +88,27 @@
lldb.SBDebugger.Destroy(debugger)
+ @add_test_categories(['pyapi'])
+ @no_debug_info_test
+ def test_legacy_file_err_with_get(self):
+
+ debugger = lldb.SBDebugger.Create()
+ try:
+ with open('output', 'w') as f:
+ debugger.SetErrorFileHandle(f, False)
+ handle_command(debugger, 'lolwut', raise_on_fail=False, collect_result=False)
+ debugger.GetErrorFileHandle().write('FOOBAR\n')
+
+ with open('output', 'r') as f:
+ errors = f.read()
+ self.assertTrue(re.search(r'error:.*lolwut', errors))
+ self.assertTrue(re.search(r'FOOBAR', errors))
+
+ finally:
+ self.RemoveTempFile('output')
+ lldb.SBDebugger.Destroy(debugger)
+
+
@add_test_categories(['pyapi'])
@no_debug_info_test
def test_sbfile_invalid(self):
@@ -126,7 +159,147 @@
self.RemoveTempFile('output')
+ @add_test_categories(['pyapi'])
+ @no_debug_info_test
+ def test_fileno_out(self):
+
+ debugger = lldb.SBDebugger.Create()
+ try:
+ with open('output', 'w') as f:
+ sbf = lldb.SBFile(f.fileno(), "w", False)
+ status = debugger.SetOutputFile(sbf)
+ if status.Fail():
+ raise Exception(status)
+ handle_command(debugger, 'script 1+2')
+ debugger.GetOutputFile().Write(b'quux')
+
+ with open('output', 'r') as f:
+ self.assertEqual(readStrippedLines(f), ['3', 'quux'])
+
+ finally:
+ self.RemoveTempFile('output')
+ lldb.SBDebugger.Destroy(debugger)
+
+ @add_test_categories(['pyapi'])
+ @no_debug_info_test
+ def test_fileno_help(self):
+
+ debugger = lldb.SBDebugger.Create()
+ try:
+ with open('output', 'w') as f:
+ sbf = lldb.SBFile(f.fileno(), "w", False)
+ status = debugger.SetOutputFile(sbf)
+ if status.Fail():
+ raise Exception(status)
+ handle_command(debugger, "help help", collect_result=False)
+
+ with open('output', 'r') as f:
+ self.assertTrue(re.search(r'Show a list of all debugger commands', f.read()))
+
+ finally:
+ self.RemoveTempFile('output')
+ lldb.SBDebugger.Destroy(debugger)
+ @add_test_categories(['pyapi'])
+ @no_debug_info_test
+ def test_immediate(self):
+
+ debugger = lldb.SBDebugger.Create()
+ try:
+ with open('output', 'w') as f:
+ ret = lldb.SBCommandReturnObject()
+ ret.SetImmediateOutputFile(f)
+ interpreter = debugger.GetCommandInterpreter()
+ interpreter.HandleCommand("help help", ret)
+ # make sure the file wasn't closed early.
+ f.write("\nQUUX\n")
+ ret = None # call destructor and flush streams
+
+ with open('output', 'r') as f:
+ output = f.read()
+ self.assertTrue(re.search(r'Show a list of all debugger commands', output))
+ self.assertTrue(re.search(r'QUUX', output))
+
+ finally:
+ self.RemoveTempFile('output')
+ lldb.SBDebugger.Destroy(debugger)
+
+
+ @add_test_categories(['pyapi'])
+ @no_debug_info_test
+ def test_fileno_inout(self):
+
+ debugger = lldb.SBDebugger.Create()
+ try:
+ with open('input', 'w') as f:
+ f.write("help help\n")
+
+ with open('output', 'w') as outf, open('input', 'r') as inf:
+
+ outsbf = lldb.SBFile(outf.fileno(), "w", False)
+ status = debugger.SetOutputFile(outsbf)
+ if status.Fail():
+ raise Exception(status)
+
+ insbf = lldb.SBFile(inf.fileno(), "r", False)
+ status = debugger.SetInputFile(insbf)
+ if status.Fail():
+ raise Exception(status)
+
+ opts = lldb.SBCommandInterpreterRunOptions()
+ debugger.RunCommandInterpreter(True, False, opts, 0, False, False)
+ debugger.GetOutputFile().Flush()
+
+ with open('output', 'r') as f:
+ self.assertTrue(re.search(r'Show a list of all debugger commands', f.read()))
+
+ finally:
+ self.RemoveTempFile('output')
+ self.RemoveTempFile('input')
+ lldb.SBDebugger.Destroy(debugger)
+
+
+ @add_test_categories(['pyapi'])
+ @no_debug_info_test
+ def test_fileno_error(self):
+
+ debugger = lldb.SBDebugger.Create()
+ try:
+ with open('output', 'w') as f:
+
+ sbf = lldb.SBFile(f.fileno(), 'w', False)
+ status = debugger.SetErrorFile(sbf)
+ if status.Fail():
+ raise Exception(status)
+
+ handle_command(debugger, 'lolwut', raise_on_fail=False, collect_result=False)
+
+ debugger.GetErrorFile().Write(b'\nzork\n')
+
+ with open('output', 'r') as f:
+ errors = f.read()
+ self.assertTrue(re.search(r'error:.*lolwut', errors))
+ self.assertTrue(re.search(r'zork', errors))
+
+ finally:
+ self.RemoveTempFile('output')
+ lldb.SBDebugger.Destroy(debugger)
+
+
+
+ @add_test_categories(['pyapi'])
+ @no_debug_info_test
+ def test_replace_stdout(self):
+ f = io.StringIO()
+ debugger = lldb.SBDebugger.Create()
+ try:
+ with replace_stdout(f):
+ self.assertEqual(sys.stdout, f)
+ handle_command(debugger, 'script sys.stdout.write("lol")',
+ collect_result=False)
+ self.assertEqual(sys.stdout, f)
+ finally:
+ lldb.SBDebugger.Destroy(debugger)
Index: lldb/include/lldb/Core/Debugger.h
===================================================================
--- lldb/include/lldb/Core/Debugger.h
+++ lldb/include/lldb/Core/Debugger.h
@@ -132,12 +132,12 @@
repro::DataRecorder *GetInputRecorder();
- void SetInputFileHandle(FILE *fh, bool tranfer_ownership,
- repro::DataRecorder *recorder = nullptr);
+ Status SetInputFile(lldb::FileSP file,
+ repro::DataRecorder *recorder = nullptr);
- void SetOutputFileHandle(FILE *fh, bool tranfer_ownership);
+ Status SetOutputFile(lldb::FileSP file);
- void SetErrorFileHandle(FILE *fh, bool tranfer_ownership);
+ Status SetErrorFile(lldb::FileSP file);
void SaveInputTerminalState();
Index: lldb/include/lldb/API/SBFile.h
===================================================================
--- lldb/include/lldb/API/SBFile.h
+++ lldb/include/lldb/API/SBFile.h
@@ -14,6 +14,8 @@
namespace lldb {
class LLDB_API SBFile {
+ friend class SBDebugger;
+
public:
SBFile();
SBFile(FILE *file, bool transfer_ownership);
@@ -31,6 +33,7 @@
private:
FileSP m_opaque_sp;
+ SBFile(FileSP file_sp) : m_opaque_sp(file_sp) {}
};
} // namespace lldb
Index: lldb/include/lldb/API/SBDebugger.h
===================================================================
--- lldb/include/lldb/API/SBDebugger.h
+++ lldb/include/lldb/API/SBDebugger.h
@@ -88,6 +88,18 @@
FILE *GetErrorFileHandle();
+ SBError SetInputFile(SBFile file);
+
+ SBError SetOutputFile(SBFile file);
+
+ SBError SetErrorFile(SBFile file);
+
+ SBFile GetInputFile();
+
+ SBFile GetOutputFile();
+
+ SBFile GetErrorFile();
+
void SaveInputTerminalState();
void RestoreInputTerminalState();
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits