JDevlieghere created this revision.
JDevlieghere added reviewers: teemperor, labath, mib, jingham.
Herald added a subscriber: pengfei.
JDevlieghere requested review of this revision.

When executing a `script` command in `HandleCommand(s)` we currently print its 
output twice: once directly to the debugger's output stream, and once as part 
of `HandleCommand`'s result. You can see this issue in action when adding a 
breakpoint command:

  (lldb) b main
  Breakpoint 1: where = main.out`main + 13 at main.cpp:2:3, address = 
0x0000000100003fad
  (lldb) break command add 1 -o "script print(\"Hey!\")"
  (lldb) r
  Process 76041 launched: '/tmp/main.out' (x86_64)
  Hey!
  (lldb)  script print("Hey!")
  Hey!
  
  Process 76041 stopped
  * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
      frame #0: 0x0000000100003fad main.out`main at main.cpp:2:3

The issue is that `HandleCommands` uses a temporary `CommandReturnObject`. When 
running a one-liner with `script`, the script interpreter redirects the I/O 
from to the command object through `ScriptInterpreterIORedirect` which sets the 
immediate output on the temporary result and causes the result to be printed 
the first time. HandleCommand will then copy the result's output into its own 
result and print it a second time, not knowing that it has already been printed.

I'm not entirely sure why `ScriptInterpreterIORedirect` relies on an immediate 
output file, but there are several parts of LLDB that rely on it, so I'm 
hesitant to change that. We could check in `HandleCommands` if someone set 
immediate mode on our temporary result and not copy it into the final result 
when that's the case. A better solution in my opinion, which I implemented in 
this patch, is a "hermetic" mode for the `CommandReturnObject` which prevents 
anyone from setting and immediate stream on the temporary result by the command 
interpreter. This should prevent similar bugs if there are other places that 
try to do this.

I added a new test using the breakpoint command and fixed a Lua test that 
already suffered from this issue.


https://reviews.llvm.org/D103349

Files:
  lldb/include/lldb/Interpreter/CommandReturnObject.h
  lldb/source/Interpreter/CommandInterpreter.cpp
  lldb/source/Interpreter/CommandReturnObject.cpp
  lldb/test/Shell/Breakpoint/breakpoint-command.test
  lldb/test/Shell/ScriptInterpreter/Lua/nested_sessions.test

Index: lldb/test/Shell/ScriptInterpreter/Lua/nested_sessions.test
===================================================================
--- lldb/test/Shell/ScriptInterpreter/Lua/nested_sessions.test
+++ lldb/test/Shell/ScriptInterpreter/Lua/nested_sessions.test
@@ -7,6 +7,5 @@
 # CHECK-NEXT: foo foo
 # CHECK-NEXT: foo bar
 # CHECK-NEXT: foo bar
-# CHECK-NEXT: foo bar
 # CHECK: script
 # CHECK-NEXT: bar bar
Index: lldb/test/Shell/Breakpoint/breakpoint-command.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/Breakpoint/breakpoint-command.test
@@ -0,0 +1,5 @@
+# RUN: %build %p/Inputs/dummy-target.c -o %t.out
+# RUN: %lldb %t.out -o 'b main' -o 'break command add 1 -o "script print(95000 + 126)"' -o 'r'
+
+# CHECK: 95125
+# CHECK-NOT: 95126
Index: lldb/source/Interpreter/CommandReturnObject.cpp
===================================================================
--- lldb/source/Interpreter/CommandReturnObject.cpp
+++ lldb/source/Interpreter/CommandReturnObject.cpp
@@ -43,7 +43,7 @@
 CommandReturnObject::CommandReturnObject(bool colors)
     : m_out_stream(colors), m_err_stream(colors),
       m_status(eReturnStatusStarted), m_did_change_process_state(false),
-      m_interactive(true) {}
+      m_interactive(true), m_hermetic(false) {}
 
 void CommandReturnObject::AppendErrorWithFormat(const char *format, ...) {
   if (!format)
@@ -152,6 +152,7 @@
   m_status = eReturnStatusStarted;
   m_did_change_process_state = false;
   m_interactive = true;
+  m_hermetic = false;
 }
 
 bool CommandReturnObject::GetDidChangeProcessState() {
@@ -165,3 +166,7 @@
 bool CommandReturnObject::GetInteractive() const { return m_interactive; }
 
 void CommandReturnObject::SetInteractive(bool b) { m_interactive = b; }
+
+bool CommandReturnObject::GetHermetic() const { return m_hermetic; }
+
+void CommandReturnObject::SetHermetic(bool b) { m_hermetic = b; }
Index: lldb/source/Interpreter/CommandInterpreter.cpp
===================================================================
--- lldb/source/Interpreter/CommandInterpreter.cpp
+++ lldb/source/Interpreter/CommandInterpreter.cpp
@@ -2306,6 +2306,7 @@
 
     CommandReturnObject tmp_result(m_debugger.GetUseColor());
     tmp_result.SetInteractive(result.GetInteractive());
+    tmp_result.SetHermetic(true);
 
     // We might call into a regex or alias command, in which case the
     // add_to_history will get lost.  This m_command_source_depth dingus is the
Index: lldb/include/lldb/Interpreter/CommandReturnObject.h
===================================================================
--- lldb/include/lldb/Interpreter/CommandReturnObject.h
+++ lldb/include/lldb/Interpreter/CommandReturnObject.h
@@ -63,20 +63,28 @@
   }
 
   void SetImmediateOutputFile(lldb::FileSP file_sp) {
+    if (m_hermetic)
+      return;
     lldb::StreamSP stream_sp(new StreamFile(file_sp));
     m_out_stream.SetStreamAtIndex(eImmediateStreamIndex, stream_sp);
   }
 
   void SetImmediateErrorFile(lldb::FileSP file_sp) {
+    if (m_hermetic)
+      return;
     lldb::StreamSP stream_sp(new StreamFile(file_sp));
     m_err_stream.SetStreamAtIndex(eImmediateStreamIndex, stream_sp);
   }
 
   void SetImmediateOutputStream(const lldb::StreamSP &stream_sp) {
+    if (m_hermetic)
+      return;
     m_out_stream.SetStreamAtIndex(eImmediateStreamIndex, stream_sp);
   }
 
   void SetImmediateErrorStream(const lldb::StreamSP &stream_sp) {
+    if (m_hermetic)
+      return;
     m_err_stream.SetStreamAtIndex(eImmediateStreamIndex, stream_sp);
   }
 
@@ -142,6 +150,10 @@
 
   void SetInteractive(bool b);
 
+  bool GetHermetic() const;
+
+  void SetHermetic(bool b);
+
 private:
   enum { eStreamStringIndex = 0, eImmediateStreamIndex = 1 };
 
@@ -150,8 +162,11 @@
 
   lldb::ReturnStatus m_status;
   bool m_did_change_process_state;
-  bool m_interactive; // If true, then the input handle from the debugger will
-                      // be hooked up
+  /// If true, then the input handle from the debugger will be hooked up.
+  bool m_interactive;
+  /// If true, disallow immediate streams. Useful when using this return object
+  /// as a temporary.
+  bool m_hermetic;
 };
 
 } // namespace lldb_private
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to