JDevlieghere created this revision.
JDevlieghere added reviewers: labath, jingham, sgraenitz.
JDevlieghere added a project: LLDB.
Herald added a subscriber: teemperor.
Add support for reproducing to the command interpreter. In capture mode we
capture all the command to file and in reproducer mode we replay this.
Repository:
rLLDB LLDB
https://reviews.llvm.org/D55582
Files:
include/lldb/API/SBDebugger.h
include/lldb/Core/Debugger.h
include/lldb/Interpreter/CommandInterpreter.h
lit/Reproducer/TestCommandRepro.test
source/API/SBDebugger.cpp
source/Core/Debugger.cpp
source/Interpreter/CommandInterpreter.cpp
tools/driver/Driver.cpp
tools/driver/Driver.h
Index: tools/driver/Driver.h
===================================================================
--- tools/driver/Driver.h
+++ tools/driver/Driver.h
@@ -100,6 +100,7 @@
bool m_wait_for = false;
bool m_repl = false;
bool m_batch = false;
+ bool m_replay = false;
// FIXME: When we have set/show variables we can remove this from here.
bool m_use_external_editor = false;
Index: tools/driver/Driver.cpp
===================================================================
--- tools/driver/Driver.cpp
+++ tools/driver/Driver.cpp
@@ -274,6 +274,10 @@
m_option_data.m_batch = true;
}
+ if (auto *arg = args.getLastArg(OPT_replay)) {
+ m_option_data.m_replay = true;
+ }
+
if (auto *arg = args.getLastArg(OPT_core)) {
auto arg_value = arg->getValue();
SBFileSpec file(arg_value);
@@ -677,6 +681,8 @@
else
WithColor::error() << error.GetError() << '\n';
}
+ } else if (m_option_data.m_replay) {
+ m_debugger.RunReplay();
} else {
// Check if we have any data in the commands stream, and if so, save it to a
// temp file
Index: source/Interpreter/CommandInterpreter.cpp
===================================================================
--- source/Interpreter/CommandInterpreter.cpp
+++ source/Interpreter/CommandInterpreter.cpp
@@ -45,6 +45,7 @@
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Reproducer.h"
#include "lldb/Utility/State.h"
#include "lldb/Utility/Stream.h"
#include "lldb/Utility/Timer.h"
@@ -74,6 +75,7 @@
using namespace lldb;
using namespace lldb_private;
+using namespace llvm;
static const char *k_white_space = " \t\v";
@@ -116,6 +118,46 @@
eEchoCommentCommands = 5
};
+class lldb_private::CommandProvider
+ : public repro::Provider<lldb_private::CommandProvider> {
+public:
+ CommandProvider(const FileSpec &directory) : Provider(directory) {
+ m_info.name = "command-interpreter";
+ m_info.files.push_back("command-interpreter.txt");
+ }
+
+ void CaptureCommand(std::string command) {
+ // Explicitly ignore reproducer commands.
+ if (command.find("reproducer") == 0)
+ return;
+
+ m_commands.push_back(std::move(command));
+ }
+
+ void Keep() override {
+ FileSpec file =
+ GetRoot().CopyByAppendingPathComponent("command-interpreter.txt");
+
+ std::error_code ec;
+ llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::F_Text);
+
+ if (ec)
+ return;
+
+ for (auto &command : m_commands)
+ os << command << '\n';
+ }
+
+ void Discard() override {}
+
+ static char ID;
+
+private:
+ std::vector<std::string> m_commands;
+};
+
+char CommandProvider::ID = 0;
+
ConstString &CommandInterpreter::GetStaticBroadcasterClass() {
static ConstString class_name("lldb.commandInterpreter");
return class_name;
@@ -141,6 +183,9 @@
SetEventName(eBroadcastBitQuitCommandReceived, "quit");
CheckInWithManager();
m_collection_sp->Initialize(g_properties);
+
+ if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator())
+ m_provider = &g->GetOrCreate<CommandProvider>();
}
bool CommandInterpreter::GetExpandRegexAliases() const {
@@ -1679,6 +1724,9 @@
Status error(PreprocessCommand(command_string));
+ if (m_provider)
+ m_provider->CaptureCommand(original_command_string);
+
if (error.Fail()) {
result.AppendError(error.AsCString());
result.SetStatus(eReturnStatusFailed);
@@ -2074,6 +2122,45 @@
return position;
}
+void CommandInterpreter::ReplayCommands(CommandReturnObject &result) {
+ repro::Loader *loader = repro::Reproducer::Instance().GetLoader();
+ if (!loader) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return;
+ }
+
+ auto provider_info = loader->GetProviderInfo("command-interpreter");
+ if (!provider_info) {
+ result.AppendErrorWithFormat("no provider for command interpreter.");
+ result.SetStatus(eReturnStatusFailed);
+ return;
+ }
+
+ if (provider_info->files.empty()) {
+ result.AppendErrorWithFormat(
+ "provider for command interpreter contains no files.");
+ result.SetStatus(eReturnStatusFailed);
+ return;
+ }
+
+ FileSpec command_file = loader->GetRoot().CopyByAppendingPathComponent(
+ provider_info->files.front());
+
+ // Remember current batch mode.
+ const bool saved_batch = SetBatchCommandMode(true);
+
+ CommandInterpreterRunOptions options;
+ options.SetSilent(false);
+ options.SetStopOnError(true);
+ options.SetStopOnContinue(false);
+
+ // Run the commands in a new execution context.
+ HandleCommandsFromFile(command_file, nullptr, options, result);
+
+ // Reset current batch mode.
+ SetBatchCommandMode(saved_batch);
+}
+
void CommandInterpreter::SourceInitFile(bool in_cwd,
CommandReturnObject &result) {
FileSpec init_file;
Index: source/Core/Debugger.cpp
===================================================================
--- source/Core/Debugger.cpp
+++ source/Core/Debugger.cpp
@@ -26,6 +26,7 @@
#include "lldb/Host/Terminal.h"
#include "lldb/Host/ThreadLauncher.h"
#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Interpreter/OptionValue.h"
#include "lldb/Interpreter/OptionValueProperties.h"
#include "lldb/Interpreter/OptionValueSInt64.h"
@@ -1692,6 +1693,18 @@
return GetDummyTarget();
}
+Status Debugger::RunReplay() {
+ // Create a command return object and hook up its streams.
+ CommandReturnObject result;
+ result.SetImmediateOutputStream(GetOutputFile());
+ result.SetImmediateErrorStream(GetErrorFile());
+
+ // Replay commands from reproducer.
+ GetCommandInterpreter().ReplayCommands(result);
+
+ return {};
+}
+
Status Debugger::RunREPL(LanguageType language, const char *repl_options) {
Status err;
FileSpec repl_executable;
Index: source/API/SBDebugger.cpp
===================================================================
--- source/API/SBDebugger.cpp
+++ source/API/SBDebugger.cpp
@@ -953,6 +953,15 @@
}
}
+SBError SBDebugger::RunReplay() {
+ SBError error;
+ if (m_opaque_sp)
+ error.ref() = m_opaque_sp->RunReplay();
+ else
+ error.SetErrorString("invalid debugger");
+ return error;
+}
+
SBError SBDebugger::RunREPL(lldb::LanguageType language,
const char *repl_options) {
SBError error;
Index: lit/Reproducer/TestCommandRepro.test
===================================================================
--- /dev/null
+++ lit/Reproducer/TestCommandRepro.test
@@ -0,0 +1,30 @@
+# UNSUPPORTED: system-windows, system-freebsd
+
+# This tests the replaying of commands.
+#
+# RUN: %clang %S/Inputs/simple.c -g -o %t.out
+# RUN: %lldb -x -b -o 'breakpoint set -f simple.c -l 13' -o 'run' -o 'bt' -o 'cont' -o 'reproducer status' -o 'reproducer generate' --capture %T/reproducer -- %t.out | FileCheck %s --check-prefix CHECK --check-prefix CAPTURE
+# RUN: %lldb -x -b --replay %T/reproducer -- %t.out | FileCheck %s --check-prefix CHECK --check-prefix REPLAY
+# RUN: %lldb -x -b --replay %T/reproducer | FileCheck %s --check-prefix CHECK --check-prefix REPLAY
+
+# This tests that errors are printed.
+#
+# RUN: echo "bogus" >> %T/reproducer/command-interpreter.txt
+# RUN: %lldb -x -b --replay %T/reproducer 2>&1 | FileCheck %s --check-prefix CHECK --check-prefix REPLAY --check-prefix BOGUS
+
+# CHECK: Breakpoint 1
+# CHECK: Process {{.*}} stopped
+# CHECK: Process {{.*}} launched
+# CHECK: thread {{.*}} stop reason = breakpoint
+# CHECK: frame {{.*}} simple.c
+
+# CAPTURE: testing
+# REPLAY-NOT: testing
+
+# CHECK: Process {{.*}} resuming
+# CHECK: Process {{.*}} exited
+
+# CAPTURE: Reproducer is in capture mode.
+# CAPTURE: Reproducer written
+
+# BOGUS: error: 'bogus' is not a valid command.
Index: include/lldb/Interpreter/CommandInterpreter.h
===================================================================
--- include/lldb/Interpreter/CommandInterpreter.h
+++ include/lldb/Interpreter/CommandInterpreter.h
@@ -4,7 +4,6 @@
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
-//
//===----------------------------------------------------------------------===//
#ifndef liblldb_CommandInterpreter_h_
@@ -28,6 +27,8 @@
namespace lldb_private {
+class CommandProvider;
+
class CommandInterpreterRunOptions {
public:
//------------------------------------------------------------------
@@ -204,6 +205,8 @@
return GetStaticBroadcasterClass();
}
+ void ReplayCommands(CommandReturnObject &result);
+
void SourceInitFile(bool in_cwd, CommandReturnObject &result);
bool AddCommand(llvm::StringRef name, const lldb::CommandObjectSP &cmd_sp,
@@ -607,6 +610,9 @@
bool m_quit_requested;
bool m_stopped_for_crash;
+ // Reproducer provider.
+ CommandProvider *m_provider = nullptr;
+
// The exit code the user has requested when calling the 'quit' command.
// No value means the user hasn't set a custom exit code so far.
llvm::Optional<int> m_quit_exit_code;
Index: include/lldb/Core/Debugger.h
===================================================================
--- include/lldb/Core/Debugger.h
+++ include/lldb/Core/Debugger.h
@@ -317,6 +317,8 @@
bool IsHandlingEvents() const { return m_event_handler_thread.IsJoinable(); }
+ Status RunReplay();
+
Status RunREPL(lldb::LanguageType language, const char *repl_options);
// This is for use in the command interpreter, when you either want the
Index: include/lldb/API/SBDebugger.h
===================================================================
--- include/lldb/API/SBDebugger.h
+++ include/lldb/API/SBDebugger.h
@@ -271,6 +271,7 @@
int &num_errors, bool &quit_requested,
bool &stopped_for_crash);
+ SBError RunReplay();
SBError RunREPL(lldb::LanguageType language, const char *repl_options);
private:
_______________________________________________
lldb-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits