JDevlieghere updated this revision to Diff 177906.
JDevlieghere added a comment.

Skip driver logic when replaying, otherwise commands are executed or sourced 
twice, once in the driver and once by replaying the commands.


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/TestCommandRepro.test
  source/API/SBDebugger.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 {
@@ -1679,6 +1720,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 +2118,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;
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/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"
@@ -945,6 +946,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,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(); }
 
+  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
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to