This revision was automatically updated to reflect the committed changes.
Closed by commit rG59237bb52c94: [lldb] Use a time-based timeout in 
IRInterpreter (authored by JDevlieghere).
Herald added a subscriber: lldb-commits.

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D102762/new/

https://reviews.llvm.org/D102762

Files:
  lldb/include/lldb/Expression/IRInterpreter.h
  lldb/include/lldb/Target/Target.h
  lldb/source/Expression/IRInterpreter.cpp
  lldb/source/Expression/LLVMUserExpression.cpp
  lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py

Index: lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py
===================================================================
--- lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py
+++ lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py
@@ -12,6 +12,41 @@
 class IRInterpreterTestCase(TestBase):
     NO_DEBUG_INFO_TESTCASE = True
 
+    def time_expression(self, expr, options):
+        start = time.time()
+        res = self.target.EvaluateExpression(expr, options)
+        return res, time.time() - start
+
+    def test_interpreter_timeout(self):
+        """Test the timeout logic in the IRInterpreter."""
+        self.build()
+        self.target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
+        self.assertTrue(self.target, VALID_TARGET)
+
+        # A non-trivial infinite loop.
+        inf_loop = "for (unsigned i = 0; i < 100; ++i) --i; 1"
+        timeout_error = "Reached timeout while interpreting expression"
+        options = lldb.SBExpressionOptions()
+
+        # This is an IRInterpreter specific test, so disable the JIT.
+        options.SetAllowJIT(False)
+
+        # No timeout means a 500ms.
+        options.SetTimeoutInMicroSeconds(0)
+        res, duration_sec = self.time_expression(inf_loop, options)
+        self.assertIn(timeout_error, str(res.GetError()))
+
+        # Depending on the machine load the expression might take quite some
+        # time, so give the time a generous upper bound.
+        self.assertLess(duration_sec, 15)
+
+        # Try a simple one second timeout.
+        options.SetTimeoutInMicroSeconds(1000000)
+        res, duration_sec = self.time_expression(inf_loop, options)
+        self.assertIn(timeout_error, str(res.GetError()))
+        self.assertGreaterEqual(duration_sec, 1)
+        self.assertLess(duration_sec, 30)
+
     def setUp(self):
         # Call super's setUp().
         TestBase.setUp(self)
Index: lldb/source/Expression/LLVMUserExpression.cpp
===================================================================
--- lldb/source/Expression/LLVMUserExpression.cpp
+++ lldb/source/Expression/LLVMUserExpression.cpp
@@ -120,7 +120,7 @@
 
     IRInterpreter::Interpret(*module, *function, args, *m_execution_unit_sp,
                              interpreter_error, function_stack_bottom,
-                             function_stack_top, exe_ctx);
+                             function_stack_top, exe_ctx, options.GetTimeout());
 
     if (!interpreter_error.Success()) {
       diagnostic_manager.Printf(eDiagnosticSeverityError,
@@ -233,7 +233,7 @@
           eDiagnosticSeverityError,
           "Couldn't complete execution; the thread "
           "on which the expression was being run: 0x%" PRIx64
-          " exited during its execution.", 
+          " exited during its execution.",
           expr_thread_id);
       return execution_result;
     } else if (execution_result != lldb::eExpressionCompleted) {
Index: lldb/source/Expression/IRInterpreter.cpp
===================================================================
--- lldb/source/Expression/IRInterpreter.cpp
+++ lldb/source/Expression/IRInterpreter.cpp
@@ -470,7 +470,8 @@
     "Interpreter couldn't allocate memory";
 static const char *memory_write_error = "Interpreter couldn't write to memory";
 static const char *memory_read_error = "Interpreter couldn't read from memory";
-static const char *infinite_loop_error = "Interpreter ran for too many cycles";
+static const char *timeout_error =
+    "Reached timeout while interpreting expression";
 static const char *too_many_functions_error =
     "Interpreter doesn't handle modules with multiple function bodies.";
 
@@ -683,7 +684,8 @@
                               lldb_private::Status &error,
                               lldb::addr_t stack_frame_bottom,
                               lldb::addr_t stack_frame_top,
-                              lldb_private::ExecutionContext &exe_ctx) {
+                              lldb_private::ExecutionContext &exe_ctx,
+                              lldb_private::Timeout<std::micro> timeout) {
   lldb_private::Log *log(GetLog(LLDBLog::Expressions));
 
   if (log) {
@@ -722,11 +724,23 @@
     frame.MakeArgument(&*ai, ptr);
   }
 
-  uint32_t num_insts = 0;
-
   frame.Jump(&function.front());
 
-  while (frame.m_ii != frame.m_ie && (++num_insts < 4096)) {
+  using clock = std::chrono::steady_clock;
+
+  // Compute the time at which the timeout has been exceeded.
+  std::optional<clock::time_point> end_time;
+  if (timeout && timeout->count() > 0)
+    end_time = clock::now() + *timeout;
+
+  while (frame.m_ii != frame.m_ie) {
+    // Timeout reached: stop interpreting.
+    if (end_time && clock::now() >= *end_time) {
+      error.SetErrorToGenericError();
+      error.SetErrorString(timeout_error);
+      return false;
+    }
+
     const Instruction *inst = &*frame.m_ii;
 
     LLDB_LOGF(log, "Interpreting %s", PrintValue(inst).c_str());
@@ -1571,11 +1585,5 @@
     ++frame.m_ii;
   }
 
-  if (num_insts >= 4096) {
-    error.SetErrorToGenericError();
-    error.SetErrorString(infinite_loop_error);
-    return false;
-  }
-
   return false;
 }
Index: lldb/include/lldb/Target/Target.h
===================================================================
--- lldb/include/lldb/Target/Target.h
+++ lldb/include/lldb/Target/Target.h
@@ -346,9 +346,16 @@
     m_use_dynamic = dynamic;
   }
 
-  const Timeout<std::micro> &GetTimeout() const { return m_timeout; }
+  const Timeout<std::micro> &GetTimeout() const {
+    assert(m_timeout && m_timeout->count() > 0);
+    return m_timeout;
+  }
 
-  void SetTimeout(const Timeout<std::micro> &timeout) { m_timeout = timeout; }
+  void SetTimeout(const Timeout<std::micro> &timeout) {
+    // Disallow setting a non-zero timeout.
+    if (timeout && timeout->count() > 0)
+      m_timeout = timeout;
+  }
 
   const Timeout<std::micro> &GetOneThreadTimeout() const {
     return m_one_thread_timeout;
Index: lldb/include/lldb/Expression/IRInterpreter.h
===================================================================
--- lldb/include/lldb/Expression/IRInterpreter.h
+++ lldb/include/lldb/Expression/IRInterpreter.h
@@ -11,6 +11,7 @@
 
 #include "lldb/Utility/ConstString.h"
 #include "lldb/Utility/Stream.h"
+#include "lldb/Utility/Timeout.h"
 #include "lldb/lldb-public.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/Pass.h"
@@ -44,7 +45,8 @@
                         lldb_private::Status &error,
                         lldb::addr_t stack_frame_bottom,
                         lldb::addr_t stack_frame_top,
-                        lldb_private::ExecutionContext &exe_ctx);
+                        lldb_private::ExecutionContext &exe_ctx,
+                        lldb_private::Timeout<std::micro> timeout);
 
 private:
   static bool supportsFunction(llvm::Function &llvm_function,
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to