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
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to