summaryrefslogtreecommitdiffstats
path: root/lldb/source
diff options
context:
space:
mode:
authorJonas Devlieghere <jonas@devlieghere.com>2019-03-02 00:20:26 +0000
committerJonas Devlieghere <jonas@devlieghere.com>2019-03-02 00:20:26 +0000
commitd77c2e09266304864ca95434bb8f4eb1605f057a (patch)
tree88c19efefcc36015f00e954d592c6f313f6f40ff /lldb/source
parent70f5fc13691d503df28f885f8742f30d83a83307 (diff)
downloadbcm5719-llvm-d77c2e09266304864ca95434bb8f4eb1605f057a.tar.gz
bcm5719-llvm-d77c2e09266304864ca95434bb8f4eb1605f057a.zip
[Reproducers] Capture and replay interpreter commands.
This patch adds the necessary logic to capture and replay commands entered into the command interpreter. A DataRecorder shadows the input and writes its data to a know file. During replay this file is used as the command interpreter's input. It's possible to the command interpreter more than once, with a different input source. We support this scenario by using multiple buffers. The synchronization for this takes place at the SB layer, where we create a new recorder every time the debugger input is changed. During replay we use the corresponding buffer as input. Differential revision: https://reviews.llvm.org/D58564 llvm-svn: 355249
Diffstat (limited to 'lldb/source')
-rw-r--r--lldb/source/API/SBDebugger.cpp53
-rw-r--r--lldb/source/Commands/CommandObjectCommands.cpp2
-rw-r--r--lldb/source/Commands/CommandObjectExpression.cpp4
-rw-r--r--lldb/source/Core/Debugger.cpp7
-rw-r--r--lldb/source/Core/IOHandler.cpp31
-rw-r--r--lldb/source/Expression/REPL.cpp14
-rw-r--r--lldb/source/Interpreter/CommandInterpreter.cpp17
-rw-r--r--lldb/source/Utility/Reproducer.cpp46
8 files changed, 143 insertions, 31 deletions
diff --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp
index d402bb0b48e..535dcf67582 100644
--- a/lldb/source/API/SBDebugger.cpp
+++ b/lldb/source/API/SBDebugger.cpp
@@ -57,6 +57,45 @@
using namespace lldb;
using namespace lldb_private;
+/// Helper class for replaying commands through the reproducer.
+class CommandLoader {
+public:
+ CommandLoader(std::vector<std::string> files) : m_files(files) {}
+
+ static std::unique_ptr<CommandLoader> Create() {
+ repro::Loader *loader = repro::Reproducer::Instance().GetLoader();
+ if (!loader)
+ return {};
+
+ FileSpec file = loader->GetFile<repro::CommandInfo>();
+ if (!file)
+ return {};
+
+ auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath());
+ if (auto err = error_or_file.getError())
+ return {};
+
+ std::vector<std::string> files;
+ llvm::yaml::Input yin((*error_or_file)->getBuffer());
+ yin >> files;
+
+ if (auto err = yin.error())
+ return {};
+
+ return llvm::make_unique<CommandLoader>(std::move(files));
+ }
+
+ FILE *GetNextFile() {
+ if (m_index >= m_files.size())
+ return nullptr;
+ return FileSystem::Instance().Fopen(m_files[m_index++].c_str(), "r");
+ }
+
+private:
+ std::vector<std::string> m_files;
+ unsigned m_index = 0;
+};
+
static llvm::sys::DynamicLibrary LoadPlugin(const lldb::DebuggerSP &debugger_sp,
const FileSpec &spec,
Status &error) {
@@ -269,8 +308,18 @@ void SBDebugger::SetInputFileHandle(FILE *fh, bool transfer_ownership) {
static_cast<void *>(m_opaque_sp.get()), static_cast<void *>(fh),
transfer_ownership);
- if (m_opaque_sp)
- m_opaque_sp->SetInputFileHandle(fh, transfer_ownership);
+ if (!m_opaque_sp)
+ return;
+
+ repro::DataRecorder *recorder = nullptr;
+ if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator())
+ recorder = g->GetOrCreate<repro::CommandProvider>().GetNewDataRecorder();
+
+ static std::unique_ptr<CommandLoader> loader = CommandLoader::Create();
+ if (loader)
+ fh = loader->GetNextFile();
+
+ m_opaque_sp->SetInputFileHandle(fh, transfer_ownership, recorder);
}
void SBDebugger::SetOutputFileHandle(FILE *fh, bool transfer_ownership) {
diff --git a/lldb/source/Commands/CommandObjectCommands.cpp b/lldb/source/Commands/CommandObjectCommands.cpp
index df6ec6492d7..6c92493150b 100644
--- a/lldb/source/Commands/CommandObjectCommands.cpp
+++ b/lldb/source/Commands/CommandObjectCommands.cpp
@@ -1040,7 +1040,7 @@ protected:
llvm::StringRef(), // Continuation prompt
multiple_lines, color_prompt,
0, // Don't show line numbers
- *this));
+ *this, nullptr));
if (io_handler_sp) {
debugger.PushIOHandler(io_handler_sp);
diff --git a/lldb/source/Commands/CommandObjectExpression.cpp b/lldb/source/Commands/CommandObjectExpression.cpp
index c85e54cd36f..948a874895f 100644
--- a/lldb/source/Commands/CommandObjectExpression.cpp
+++ b/lldb/source/Commands/CommandObjectExpression.cpp
@@ -234,7 +234,7 @@ Single and multi-line expressions:
with no newlines. To evaluate a multi-line expression, \
hit a return after an empty expression, and lldb will enter the multi-line expression editor. \
Hit return on an empty line to end the multi-line expression."
-
+
R"(
Timeouts:
@@ -560,7 +560,7 @@ void CommandObjectExpression::GetMultilineExpression() {
llvm::StringRef(), // Continuation prompt
multiple_lines, color_prompt,
1, // Show line numbers starting at 1
- *this));
+ *this, nullptr));
StreamFileSP output_sp(io_handler_sp->GetOutputStreamFile());
if (output_sp) {
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index 459a712fd5c..00b88a71685 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -760,6 +760,7 @@ Debugger::Debugger(lldb::LogOutputCallback log_callback, void *baton)
m_input_file_sp(std::make_shared<StreamFile>(stdin, false)),
m_output_file_sp(std::make_shared<StreamFile>(stdout, false)),
m_error_file_sp(std::make_shared<StreamFile>(stderr, false)),
+ m_input_recorder(nullptr),
m_broadcaster_manager_sp(BroadcasterManager::MakeBroadcasterManager()),
m_terminal_state(), m_target_list(*this), m_platform_list(),
m_listener_sp(Listener::MakeListener("lldb.Debugger")),
@@ -877,7 +878,11 @@ void Debugger::SetAsyncExecution(bool async_execution) {
m_command_interpreter_up->SetSynchronous(!async_execution);
}
-void Debugger::SetInputFileHandle(FILE *fh, bool tranfer_ownership) {
+repro::DataRecorder *Debugger::GetInputRecorder() { return m_input_recorder; }
+
+void Debugger::SetInputFileHandle(FILE *fh, bool tranfer_ownership,
+ repro::DataRecorder *recorder) {
+ m_input_recorder = recorder;
if (m_input_file_sp)
m_input_file_sp->GetFile().SetStream(fh, tranfer_ownership);
else
diff --git a/lldb/source/Core/IOHandler.cpp b/lldb/source/Core/IOHandler.cpp
index 1e647bb884e..4a6fb0c8a78 100644
--- a/lldb/source/Core/IOHandler.cpp
+++ b/lldb/source/Core/IOHandler.cpp
@@ -76,16 +76,19 @@ IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type)
StreamFileSP(), // Adopt STDIN from top input reader
StreamFileSP(), // Adopt STDOUT from top input reader
StreamFileSP(), // Adopt STDERR from top input reader
- 0) // Flags
-{}
+ 0, // Flags
+ nullptr // Shadow file recorder
+ ) {}
IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type,
const lldb::StreamFileSP &input_sp,
const lldb::StreamFileSP &output_sp,
- const lldb::StreamFileSP &error_sp, uint32_t flags)
+ const lldb::StreamFileSP &error_sp, uint32_t flags,
+ repro::DataRecorder *data_recorder)
: m_debugger(debugger), m_input_sp(input_sp), m_output_sp(output_sp),
- m_error_sp(error_sp), m_popped(false), m_flags(flags), m_type(type),
- m_user_data(nullptr), m_done(false), m_active(false) {
+ m_error_sp(error_sp), m_data_recorder(data_recorder), m_popped(false),
+ m_flags(flags), m_type(type), m_user_data(nullptr), m_done(false),
+ m_active(false) {
// If any files are not specified, then adopt them from the top input reader.
if (!m_input_sp || !m_output_sp || !m_error_sp)
debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_sp, m_output_sp,
@@ -153,7 +156,7 @@ IOHandlerConfirm::IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt,
llvm::StringRef(), // No continuation prompt
false, // Multi-line
false, // Don't colorize the prompt (i.e. the confirm message.)
- 0, *this),
+ 0, *this, nullptr),
m_default_response(default_response), m_user_response(default_response) {
StreamString prompt_stream;
prompt_stream.PutCString(prompt);
@@ -264,7 +267,7 @@ IOHandlerEditline::IOHandlerEditline(
const char *editline_name, // Used for saving history files
llvm::StringRef prompt, llvm::StringRef continuation_prompt,
bool multi_line, bool color_prompts, uint32_t line_number_start,
- IOHandlerDelegate &delegate)
+ IOHandlerDelegate &delegate, repro::DataRecorder *data_recorder)
: IOHandlerEditline(debugger, type,
StreamFileSP(), // Inherit input from top input reader
StreamFileSP(), // Inherit output from top input reader
@@ -272,7 +275,7 @@ IOHandlerEditline::IOHandlerEditline(
0, // Flags
editline_name, // Used for saving history files
prompt, continuation_prompt, multi_line, color_prompts,
- line_number_start, delegate) {}
+ line_number_start, delegate, data_recorder) {}
IOHandlerEditline::IOHandlerEditline(
Debugger &debugger, IOHandler::Type type,
@@ -281,8 +284,9 @@ IOHandlerEditline::IOHandlerEditline(
const char *editline_name, // Used for saving history files
llvm::StringRef prompt, llvm::StringRef continuation_prompt,
bool multi_line, bool color_prompts, uint32_t line_number_start,
- IOHandlerDelegate &delegate)
- : IOHandler(debugger, type, input_sp, output_sp, error_sp, flags),
+ IOHandlerDelegate &delegate, repro::DataRecorder *data_recorder)
+ : IOHandler(debugger, type, input_sp, output_sp, error_sp, flags,
+ data_recorder),
#ifndef LLDB_DISABLE_LIBEDIT
m_editline_up(),
#endif
@@ -338,7 +342,10 @@ void IOHandlerEditline::Deactivate() {
bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) {
#ifndef LLDB_DISABLE_LIBEDIT
if (m_editline_up) {
- return m_editline_up->GetLine(line, interrupted);
+ bool b = m_editline_up->GetLine(line, interrupted);
+ if (m_data_recorder)
+ m_data_recorder->Record(line, true);
+ return b;
} else {
#endif
line.clear();
@@ -394,6 +401,8 @@ bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) {
}
}
m_editing = false;
+ if (m_data_recorder && got_line)
+ m_data_recorder->Record(line, true);
// We might have gotten a newline on a line by itself make sure to return
// true in this case.
return got_line;
diff --git a/lldb/source/Expression/REPL.cpp b/lldb/source/Expression/REPL.cpp
index 08c3ce1f063..4136146cb48 100644
--- a/lldb/source/Expression/REPL.cpp
+++ b/lldb/source/Expression/REPL.cpp
@@ -75,13 +75,13 @@ lldb::IOHandlerSP REPL::GetIOHandler() {
Debugger &debugger = m_target.GetDebugger();
m_io_handler_sp = std::make_shared<IOHandlerEditline>(
debugger, IOHandler::Type::REPL,
- "lldb-repl", // Name of input reader for history
- llvm::StringRef("> "), // prompt
- llvm::StringRef(". "), // Continuation prompt
- true, // Multi-line
- true, // The REPL prompt is always colored
- 1, // Line number
- *this);
+ "lldb-repl", // Name of input reader for history
+ llvm::StringRef("> "), // prompt
+ llvm::StringRef(". "), // Continuation prompt
+ true, // Multi-line
+ true, // The REPL prompt is always colored
+ 1, // Line number
+ *this, nullptr);
// Don't exit if CTRL+C is pressed
static_cast<IOHandlerEditline *>(m_io_handler_sp.get())
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index 55b96af7208..cccaf3693f3 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -2483,7 +2483,7 @@ void CommandInterpreter::HandleCommandsFromFile(
// or written
debugger.GetPrompt(), llvm::StringRef(),
false, // Not multi-line
- debugger.GetUseColor(), 0, *this));
+ debugger.GetUseColor(), 0, *this, nullptr));
const bool old_async_execution = debugger.GetAsyncExecution();
// Set synchronous execution if we are not stopping on continue
@@ -2905,8 +2905,9 @@ void CommandInterpreter::GetLLDBCommandsFromIOHandler(
llvm::StringRef(), // Continuation prompt
true, // Get multiple lines
debugger.GetUseColor(),
- 0, // Don't show line numbers
- delegate)); // IOHandlerDelegate
+ 0, // Don't show line numbers
+ delegate, // IOHandlerDelegate
+ nullptr)); // FileShadowCollector
if (io_handler_sp) {
io_handler_sp->SetUserData(baton);
@@ -2928,8 +2929,9 @@ void CommandInterpreter::GetPythonCommandsFromIOHandler(
llvm::StringRef(), // Continuation prompt
true, // Get multiple lines
debugger.GetUseColor(),
- 0, // Don't show line numbers
- delegate)); // IOHandlerDelegate
+ 0, // Don't show line numbers
+ delegate, // IOHandlerDelegate
+ nullptr)); // FileShadowCollector
if (io_handler_sp) {
io_handler_sp->SetUserData(baton);
@@ -2980,8 +2982,9 @@ CommandInterpreter::GetIOHandler(bool force_create,
llvm::StringRef(), // Continuation prompt
false, // Don't enable multiple line input, just single line commands
m_debugger.GetUseColor(),
- 0, // Don't show line numbers
- *this);
+ 0, // Don't show line numbers
+ *this, // IOHandlerDelegate
+ GetDebugger().GetInputRecorder());
}
return m_command_io_handler_sp;
}
diff --git a/lldb/source/Utility/Reproducer.cpp b/lldb/source/Utility/Reproducer.cpp
index 9f749aac36a..b09248061d1 100644
--- a/lldb/source/Utility/Reproducer.cpp
+++ b/lldb/source/Utility/Reproducer.cpp
@@ -220,8 +220,54 @@ bool Loader::HasFile(StringRef file) {
return (it != m_files.end()) && (*it == file);
}
+llvm::Expected<std::unique_ptr<DataRecorder>>
+DataRecorder::Create(FileSpec filename) {
+ std::error_code ec;
+ auto recorder = llvm::make_unique<DataRecorder>(std::move(filename), ec);
+ if (ec)
+ return llvm::errorCodeToError(ec);
+ return recorder;
+}
+
+DataRecorder *CommandProvider::GetNewDataRecorder() {
+ std::size_t i = m_data_recorders.size() + 1;
+ std::string filename = (llvm::Twine(info::name) + llvm::Twine("-") +
+ llvm::Twine(i) + llvm::Twine(".txt"))
+ .str();
+ auto recorder_or_error =
+ DataRecorder::Create(GetRoot().CopyByAppendingPathComponent(filename));
+ if (!recorder_or_error) {
+ llvm::consumeError(recorder_or_error.takeError());
+ return nullptr;
+ }
+
+ m_data_recorders.push_back(std::move(*recorder_or_error));
+ return m_data_recorders.back().get();
+}
+
+void CommandProvider::Keep() {
+ std::vector<std::string> files;
+ for (auto &recorder : m_data_recorders)
+ files.push_back(recorder->GetFilename().GetPath());
+
+ FileSpec file = GetRoot().CopyByAppendingPathComponent(info::file);
+ std::error_code ec;
+ llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::F_Text);
+ if (ec)
+ return;
+ yaml::Output yout(os);
+ yout << files;
+
+ m_data_recorders.clear();
+}
+
+void CommandProvider::Discard() { m_data_recorders.clear(); }
+
void ProviderBase::anchor() {}
char ProviderBase::ID = 0;
char FileProvider::ID = 0;
+char CommandProvider::ID = 0;
const char *FileInfo::name = "files";
const char *FileInfo::file = "files.yaml";
+const char *CommandInfo::name = "command-interpreter";
+const char *CommandInfo::file = "command-interpreter.yaml";
OpenPOWER on IntegriCloud