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

Reply via email to