[Lldb-commits] [lldb] r246623 - Address flakyness in TestAttachResume

Pavel Labath via lldb-commits lldb-commits at lists.llvm.org
Wed Sep 2 02:12:29 PDT 2015


Author: labath
Date: Wed Sep  2 04:12:28 2015
New Revision: 246623

URL: http://llvm.org/viewvc/llvm-project?rev=246623&view=rev
Log:
Address flakyness in TestAttachResume

Summary:
The test (among other things) attempts to verify that detaching works while the inferior is
running. However, this was racy since the inferior could end up stopping again before we got a
chance to detach from it (the test could be made to fail reliably by inserting a sleep just after
the last "continue" command). The reason for the stop was that we had a breakpoint set in a place
that can be hit by multiple threads, and we stop because another thread hit a breakpoint.

I fix this by moving the breakpoint to a place that is reachable from only one thread. I also
make sure that the same thread cannot hit the breakpoint if we are exceptionaly slow by flipping
a flag which makes the breakpoint unreachable (I cannot just disable or delete the breakpoint as
the test makes it a point to try detaching while breakpoints are still set).

Another source or racyness was that the test verified that the process resumes and
stops after a "continue". However, if these two events happened too fast, the initial start event
would be lost, and the test would end up confused. I have implemented this in a safer manner and
made a utility function out of it, as I know of a couple of other tests which need the same
functionality.

Reviewers: zturner, clayborg

Subscribers: lldb-commits

Differential Revision: http://reviews.llvm.org/D12527

Modified:
    lldb/trunk/test/functionalities/attach_resume/TestAttachResume.py
    lldb/trunk/test/functionalities/attach_resume/main.cpp
    lldb/trunk/test/lldbutil.py

Modified: lldb/trunk/test/functionalities/attach_resume/TestAttachResume.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/functionalities/attach_resume/TestAttachResume.py?rev=246623&r1=246622&r2=246623&view=diff
==============================================================================
--- lldb/trunk/test/functionalities/attach_resume/TestAttachResume.py (original)
+++ lldb/trunk/test/functionalities/attach_resume/TestAttachResume.py Wed Sep  2 04:12:28 2015
@@ -14,7 +14,6 @@ class AttachResumeTestCase(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    @expectedFlakeyLinux('llvm.org/pr19310')
     @expectedFailureFreeBSD('llvm.org/pr19310')
     @skipIfRemote
     @dwarf_test
@@ -23,7 +22,6 @@ class AttachResumeTestCase(TestBase):
         self.buildDwarf()
         self.process_attach_continue_interrupt_detach()
 
-    @expectedFlakeyLinux('llvm.org/pr19478') # intermittent ~2/14 runs
     @skipIfRemote
     def process_attach_continue_interrupt_detach(self):
         """Test attach/continue/interrupt/detach"""
@@ -35,67 +33,39 @@ class AttachResumeTestCase(TestBase):
 
         self.runCmd("process attach -p " + str(popen.pid))
 
-        self._state = 0
-        def process_events():
-            event = lldb.SBEvent()
-            while self.dbg.GetListener().GetNextEvent(event):
-                self._state = lldb.SBProcess.GetStateFromEvent(event)
-
-        # using process.GetState() does not work: llvm.org/pr16172
-        def wait_for_state(s, timeout=5):
-            t = 0
-            period = 0.1
-            while self._state != s:
-                process_events()
-                time.sleep(period)
-                t += period
-                if t > timeout:
-                    return False
-            return True
-
         self.setAsync(True)
+        listener = self.dbg.GetListener()
 
         self.runCmd("c")
-        self.assertTrue(wait_for_state(lldb.eStateRunning),
-            'Process not running after continue')
+        lldbutil.expect_state_changes(self, listener, [lldb.eStateRunning])
 
         self.runCmd("process interrupt")
-        self.assertTrue(wait_for_state(lldb.eStateStopped),
-            'Process not stopped after interrupt')
+        lldbutil.expect_state_changes(self, listener, [lldb.eStateStopped])
 
         # be sure to continue/interrupt/continue (r204504)
         self.runCmd("c")
-        self.assertTrue(wait_for_state(lldb.eStateRunning),
-            'Process not running after continue')
+        lldbutil.expect_state_changes(self, listener, [lldb.eStateRunning])
 
         self.runCmd("process interrupt")
-        self.assertTrue(wait_for_state(lldb.eStateStopped),
-            'Process not stopped after interrupt')
+        lldbutil.expect_state_changes(self, listener, [lldb.eStateStopped])
 
         # check that this breakpoint is auto-cleared on detach (r204752)
         self.runCmd("br set -f main.cpp -l %u" % (line_number('main.cpp', '// Set breakpoint here')))
 
         self.runCmd("c")
-        self.assertTrue(wait_for_state(lldb.eStateRunning),
-            'Process not running after continue')
-
-        self.assertTrue(wait_for_state(lldb.eStateStopped),
-            'Process not stopped after breakpoint')
-        # This test runs a bunch of threads in the same little function with this
-        # breakpoint.  We want to make sure the breakpoint got hit at least once,
-        # but more than one thread may hit it at a time.  So we really only care
-        # that is isn't 0 times, not how many times it is.
+        lldbutil.expect_state_changes(self, listener, [lldb.eStateRunning, lldb.eStateStopped])
         self.expect('br list', 'Breakpoint not hit',
-            patterns = ['hit count = [1-9]'])
+            substrs = ['hit count = 1'])
+
+        # Make sure the breakpoint is not hit again.
+        self.expect("expr debugger_flag = false", substrs=[" = false"]);
 
         self.runCmd("c")
-        self.assertTrue(wait_for_state(lldb.eStateRunning),
-            'Process not running after continue')
+        lldbutil.expect_state_changes(self, listener, [lldb.eStateRunning])
 
         # make sure to detach while in running state (r204759)
         self.runCmd("detach")
-        self.assertTrue(wait_for_state(lldb.eStateDetached),
-            'Process not detached after detach')
+        lldbutil.expect_state_changes(self, listener, [lldb.eStateDetached])
 
 if __name__ == '__main__':
     import atexit

Modified: lldb/trunk/test/functionalities/attach_resume/main.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/functionalities/attach_resume/main.cpp?rev=246623&r1=246622&r2=246623&view=diff
==============================================================================
--- lldb/trunk/test/functionalities/attach_resume/main.cpp (original)
+++ lldb/trunk/test/functionalities/attach_resume/main.cpp Wed Sep  2 04:12:28 2015
@@ -8,15 +8,17 @@
 #include <sys/prctl.h>
 #endif
 
+volatile bool debugger_flag = true; // The debugger will flip this to false
+
 void *start(void *data)
 {
     int i;
     size_t idx = (size_t)data;
     for (i=0; i<30; i++)
     {
-        if ( idx == 0 )
-            std::this_thread::sleep_for(std::chrono::microseconds(1));
-        std::this_thread::sleep_for(std::chrono::seconds(1)); // Set breakpoint here
+        if ( idx == 0 && debugger_flag)
+            std::this_thread::sleep_for(std::chrono::microseconds(1)); // Set breakpoint here
+        std::this_thread::sleep_for(std::chrono::seconds(1));
     }
     return 0;
 }

Modified: lldb/trunk/test/lldbutil.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/lldbutil.py?rev=246623&r1=246622&r2=246623&view=diff
==============================================================================
--- lldb/trunk/test/lldbutil.py (original)
+++ lldb/trunk/test/lldbutil.py Wed Sep  2 04:12:28 2015
@@ -721,6 +721,21 @@ def print_stacktraces(process, string_bu
     if string_buffer:
         return output.getvalue()
 
+def expect_state_changes(test, listener, states, timeout = 5):
+    """Listens for state changed events on the listener and makes sure they match what we
+    expect. Stop-and-restart events (where GetRestartedFromEvent() returns true) are ignored."""
+    event = lldb.SBEvent()
+    for expected_state in states:
+        if not listener.WaitForEvent(timeout, event):
+            test.Fail("Timed out while waiting for a transition to state %s" %
+                lldb.SBDebugger.StateAsCString(expected_state))
+
+        got_state = lldb.SBProcess.GetStateFromEvent(event)
+        if got_state == lldb.eStateStopped and lldb.SBProcess.GetRestartedFromEvent(event):
+            continue
+
+        test.assertEqual(expected_state, got_state)
+
 # ===================================
 # Utility functions related to Frames
 # ===================================




More information about the lldb-commits mailing list