JDevlieghere updated this revision to Diff 177950.
JDevlieghere added a comment.
When sourcing a file we should also ignore the individual commands otherwise
they end up getting executed twice, once as part of the `command source` and
once for every individual command in the file.
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D55582/new/
https://reviews.llvm.org/D55582
Files:
include/lldb/Core/Debugger.h
include/lldb/Interpreter/CommandInterpreter.h
lit/Reproducer/Inputs/CommandCapture.in
lit/Reproducer/TestCommandRepro.test
source/API/SBDebugger.cpp
source/Commands/CommandObjectCommands.cpp
source/Commands/CommandObjectReproducer.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
@@ -286,6 +286,10 @@
m_option_data.m_core_file = arg_value;
}
+ if (args.hasArg(OPT_replay)) {
+ m_option_data.m_replay = true;
+ }
+
if (args.hasArg(OPT_editor)) {
m_option_data.m_use_external_editor = true;
}
@@ -677,6 +681,8 @@
else
WithColor::error() << error.GetError() << '\n';
}
+ } else if (m_option_data.m_replay) {
+ m_debugger.RunCommandInterpreter(handle_events, spawn_thread);
} 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,42 @@
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) {
+ 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 +179,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 {
@@ -1589,7 +1630,8 @@
CommandReturnObject &result,
ExecutionContext *override_context,
bool repeat_on_empty_command,
- bool no_context_switching)
+ bool no_context_switching,
+ bool add_to_reproducer)
{
@@ -1679,6 +1721,9 @@
Status error(PreprocessCommand(command_string));
+ if (m_provider && add_to_reproducer)
+ m_provider->CaptureCommand(original_command_string);
+
if (error.Fail()) {
result.AppendError(error.AsCString());
result.SetStatus(eReturnStatusFailed);
@@ -2074,6 +2119,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(false);
+ 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;
@@ -2160,6 +2244,7 @@
options.SetSilent(true);
options.SetStopOnError(false);
options.SetStopOnContinue(true);
+ options.SetAddToReproducer(false);
HandleCommandsFromFile(init_file,
nullptr, // Execution context
@@ -2237,7 +2322,8 @@
HandleCommand(cmd, options.m_add_to_history, tmp_result,
nullptr, /* override_context */
true, /* repeat_on_empty_command */
- override_context != nullptr /* no_context_switching */);
+ override_context != nullptr /* no_context_switching */,
+ options.GetAddToReproducer());
if (!options.GetAddToHistory())
m_command_source_depth--;
@@ -2349,7 +2435,8 @@
eHandleCommandFlagEchoCommand = (1u << 2),
eHandleCommandFlagEchoCommentCommand = (1u << 3),
eHandleCommandFlagPrintResult = (1u << 4),
- eHandleCommandFlagStopOnCrash = (1u << 5)
+ eHandleCommandFlagStopOnCrash = (1u << 5),
+ eHandleCommandFlagAddToReproducer = (1u << 6)
};
void CommandInterpreter::HandleCommandsFromFile(
@@ -2438,6 +2525,10 @@
flags |= eHandleCommandFlagPrintResult;
}
+ if (options.GetAddToReproducer()) {
+ flags |= eHandleCommandFlagAddToReproducer;
+ }
+
if (flags & eHandleCommandFlagPrintResult) {
debugger.GetOutputFile()->Printf("Executing commands in '%s'.\n",
cmd_file_path.c_str());
@@ -2788,7 +2879,8 @@
StartHandlingCommand();
lldb_private::CommandReturnObject result;
- HandleCommand(line.c_str(), eLazyBoolCalculate, result);
+ HandleCommand(line.c_str(), eLazyBoolCalculate, result, nullptr, true, false,
+ io_handler.GetFlags().Test(eHandleCommandFlagAddToReproducer));
// Now emit the command output text from the command we just executed
if (io_handler.GetFlags().Test(eHandleCommandFlagPrintResult)) {
@@ -2958,6 +3050,8 @@
flags |= eHandleCommandFlagEchoCommentCommand;
if (options->m_print_results != eLazyBoolNo)
flags |= eHandleCommandFlagPrintResult;
+ if (options->m_add_to_reproducer != eLazyBoolNo)
+ flags |= eHandleCommandFlagAddToReproducer;
} else {
flags = eHandleCommandFlagEchoCommand | eHandleCommandFlagPrintResult;
}
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,16 @@
return GetDummyTarget();
}
+void 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);
+}
+
Status Debugger::RunREPL(LanguageType language, const char *repl_options) {
Status err;
FileSpec repl_executable;
Index: source/Commands/CommandObjectReproducer.cpp
===================================================================
--- source/Commands/CommandObjectReproducer.cpp
+++ source/Commands/CommandObjectReproducer.cpp
@@ -38,8 +38,13 @@
auto &r = repro::Reproducer::Instance();
if (auto generator = r.GetGenerator()) {
generator->Keep();
+ } else if (r.GetLoader()) {
+ // Make this operation a NOP in replay mode.
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
} else {
result.AppendErrorWithFormat("Unable to get the reproducer generator");
+ result.SetStatus(eReturnStatusFailed);
return false;
}
Index: source/Commands/CommandObjectCommands.cpp
===================================================================
--- source/Commands/CommandObjectCommands.cpp
+++ source/Commands/CommandObjectCommands.cpp
@@ -316,6 +316,7 @@
CommandInterpreterRunOptions options;
options.SetStopOnContinue(m_options.m_stop_on_continue.GetCurrentValue());
options.SetStopOnError(m_options.m_stop_on_error.GetCurrentValue());
+ options.SetAddToReproducer(false);
// Individual silent setting is override for global command echo settings.
if (m_options.m_silent_run.GetCurrentValue()) {
@@ -331,6 +332,7 @@
// No options were set, inherit any settings from nested "command source"
// commands, or set to sane default settings...
CommandInterpreterRunOptions options;
+ options.SetAddToReproducer(false);
m_interpreter.HandleCommandsFromFile(cmd_file, exe_ctx, options, result);
}
return result.Succeeded();
Index: source/API/SBDebugger.cpp
===================================================================
--- source/API/SBDebugger.cpp
+++ source/API/SBDebugger.cpp
@@ -48,6 +48,7 @@
#include "lldb/Target/Process.h"
#include "lldb/Target/TargetList.h"
#include "lldb/Utility/Args.h"
+#include "lldb/Utility/Reproducer.h"
#include "lldb/Utility/State.h"
#include "llvm/ADT/STLExtras.h"
@@ -929,8 +930,13 @@
void SBDebugger::RunCommandInterpreter(bool auto_handle_events,
bool spawn_thread) {
if (m_opaque_sp) {
- CommandInterpreterRunOptions options;
+ // If there's a loader we're in replay mode.
+ if (repro::Reproducer::Instance().GetLoader()) {
+ m_opaque_sp->RunReplay();
+ return;
+ }
+ CommandInterpreterRunOptions options;
m_opaque_sp->GetCommandInterpreter().RunCommandInterpreter(
auto_handle_events, spawn_thread, options);
}
@@ -945,6 +951,13 @@
{
if (m_opaque_sp) {
CommandInterpreter &interp = m_opaque_sp->GetCommandInterpreter();
+
+ // If there's a loader we're in replay mode.
+ if (repro::Reproducer::Instance().GetLoader()) {
+ m_opaque_sp->RunReplay();
+ return;
+ }
+
interp.RunCommandInterpreter(auto_handle_events, spawn_thread,
options.ref());
num_errors = interp.GetNumErrors();
Index: lit/Reproducer/TestCommandRepro.test
===================================================================
--- /dev/null
+++ lit/Reproducer/TestCommandRepro.test
@@ -0,0 +1,39 @@
+# UNSUPPORTED: system-windows, system-freebsd
+
+# RUN: rm -rf %T/commands
+# RUN: %clang %S/Inputs/simple.c -g -o %t.out
+
+# This tests the replaying of commands.
+#
+# 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/commands -- %t.out | FileCheck %s --check-prefix CHECK --check-prefix CAPTURE
+# RUN: %lldb -x -b --replay %T/commands -- %t.out | FileCheck %s --check-prefix CHECK --check-prefix REPLAY
+# RUN: %lldb -x -b --replay %T/commands | FileCheck %s --check-prefix CHECK --check-prefix REPLAY
+
+# RUN: rm -rf %T/commands
+
+# Test that sourced files are not captured double.
+#
+# RUN: %lldb -x -b -s %S/Inputs/CommandCapture.in --capture %T/commands -- %t.out | FileCheck %s --check-prefix CHECK --check-prefix CAPTURE
+# RUN: %lldb -x -b --replay %T/commands -- %t.out | FileCheck %s --check-prefix CHECK --check-prefix REPLAY
+
+# This tests that errors are printed.
+#
+# RUN: echo "bogus" >> %T/commands/command-interpreter.txt
+# RUN: %lldb -x -b --replay %T/commands 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: lit/Reproducer/Inputs/CommandCapture.in
===================================================================
--- /dev/null
+++ lit/Reproducer/Inputs/CommandCapture.in
@@ -0,0 +1,6 @@
+breakpoint set -f simple.c -l 13
+run
+bt
+cont
+reproducer status
+reproducer generate
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:
//------------------------------------------------------------------
@@ -136,6 +137,12 @@
m_add_to_history = add_to_history ? eLazyBoolYes : eLazyBoolNo;
}
+ bool GetAddToReproducer() const { return DefaultToYes(m_add_to_reproducer); }
+
+ void SetAddToReproducer(bool add_to_reproducer) {
+ m_add_to_reproducer = add_to_reproducer ? eLazyBoolYes : eLazyBoolNo;
+ }
+
LazyBool m_stop_on_continue;
LazyBool m_stop_on_error;
LazyBool m_stop_on_crash;
@@ -143,6 +150,7 @@
LazyBool m_echo_comment_commands;
LazyBool m_print_results;
LazyBool m_add_to_history;
+ LazyBool m_add_to_reproducer;
private:
static bool DefaultToYes(LazyBool flag) {
@@ -204,6 +212,8 @@
return GetStaticBroadcasterClass();
}
+ void ReplayCommands(CommandReturnObject &result);
+
void SourceInitFile(bool in_cwd, CommandReturnObject &result);
bool AddCommand(llvm::StringRef name, const lldb::CommandObjectSP &cmd_sp,
@@ -251,7 +261,8 @@
CommandReturnObject &result,
ExecutionContext *override_context = nullptr,
bool repeat_on_empty_command = true,
- bool no_context_switching = false);
+ bool no_context_switching = false,
+ bool add_to_reproducer = false);
bool WasInterrupted() const;
@@ -607,6 +618,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(); }
+ void RunReplay();
+
Status RunREPL(lldb::LanguageType language, const char *repl_options);
// This is for use in the command interpreter, when you either want the
_______________________________________________
lldb-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits