[Lldb-commits] [lldb] r212671 - Add a new test in api/multiple-debuggers which tries to create 50

Jason Molenda jmolenda at apple.com
Wed Jul 9 19:17:32 PDT 2014


Author: jmolenda
Date: Wed Jul  9 21:17:31 2014
New Revision: 212671

URL: http://llvm.org/viewvc/llvm-project?rev=212671&view=rev
Log:
Add a new test in api/multiple-debuggers which tries to create 50
debug sessions simultaneously to expose race conditoin/locking
issues.

This directory has an inferior program, testprog.cpp that has a
couple of functions we can put breakpoints on.

It has a driver program, multi-process-driver.cpp, which links
against the LLDB solib and uses the SB APIs.  It creates 50 pthreads,
creates a debugger on all of them, launches a debug session of the
inferior testprog, hits a couple breakpoints, walks the stack,
continues, etc., and then kills the inferior and ends the debug
session.

A pass is if all fifty debug sessions complete successfully
in the alloted time (~60 seconds).

We may need to tweak this one to work correctly on different
platforms/targets but I wanted to get it checked in to start.


Added:
    lldb/trunk/test/api/multiple-debuggers/
    lldb/trunk/test/api/multiple-debuggers/Makefile
    lldb/trunk/test/api/multiple-debuggers/TestMultipleDebuggers.py
    lldb/trunk/test/api/multiple-debuggers/multi-process-driver.cpp
    lldb/trunk/test/api/multiple-debuggers/testprog.cpp

Added: lldb/trunk/test/api/multiple-debuggers/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/api/multiple-debuggers/Makefile?rev=212671&view=auto
==============================================================================
--- lldb/trunk/test/api/multiple-debuggers/Makefile (added)
+++ lldb/trunk/test/api/multiple-debuggers/Makefile Wed Jul  9 21:17:31 2014
@@ -0,0 +1,7 @@
+LEVEL = ../../make
+
+MAKE_DSYM := NO
+
+CXX_SOURCES := multi-process-driver.cpp testprog.cpp
+
+include $(LEVEL)/Makefile.rules

Added: lldb/trunk/test/api/multiple-debuggers/TestMultipleDebuggers.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/api/multiple-debuggers/TestMultipleDebuggers.py?rev=212671&view=auto
==============================================================================
--- lldb/trunk/test/api/multiple-debuggers/TestMultipleDebuggers.py (added)
+++ lldb/trunk/test/api/multiple-debuggers/TestMultipleDebuggers.py Wed Jul  9 21:17:31 2014
@@ -0,0 +1,46 @@
+"""Test the lldb public C++ api when doing multiple debug sessions simultaneously."""
+
+import os, re, StringIO
+import unittest2
+from lldbtest import *
+import lldbutil
+import lldb
+import subprocess
+
+class TestMultipleSimultaneousDebuggers(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    def setUp(self):
+        TestBase.setUp(self)
+        self.lib_dir = os.environ["LLDB_LIB_DIR"]
+
+    @skipIfi386
+    def test_whatever(self):
+
+        self.driver_exe = os.path.join(os.getcwd(), "multi-process-driver")
+        self.buildDriver('multi-process-driver.cpp', self.driver_exe)
+        self.addTearDownHook(lambda: os.remove(self.driver_exe))
+
+        self.inferior_exe = os.path.join(os.getcwd(), "testprog")
+        self.buildDriver('testprog.cpp', self.inferior_exe)
+        self.addTearDownHook(lambda: os.remove(self.inferior_exe))
+
+        env = {self.dylibPath : self.getLLDBLibraryEnvVal()}
+
+# check_call will raise a CalledProcessError if multi-process-driver doesn't return
+# exit code 0 to indicate success.  We can let this exception go - the test harness
+# will recognize it as a test failure.
+
+        if self.TraceOn():
+            print "Running test %s" % self.driver_exe
+            check_call([self.driver_exe, self.inferior_exe], env=env)
+        else:
+            with open(os.devnull, 'w') as fnull:
+                check_call([self.driver_exe, self.inferior_exe], env=env, stdout=fnull, stderr=fnull)
+
+if __name__ == '__main__':
+    import atexit
+    lldb.SBDebugger.Initialize()
+    atexit.register(lambda: lldb.SBDebugger.Terminate())
+    unittest2.main()

Added: lldb/trunk/test/api/multiple-debuggers/multi-process-driver.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/api/multiple-debuggers/multi-process-driver.cpp?rev=212671&view=auto
==============================================================================
--- lldb/trunk/test/api/multiple-debuggers/multi-process-driver.cpp (added)
+++ lldb/trunk/test/api/multiple-debuggers/multi-process-driver.cpp Wed Jul  9 21:17:31 2014
@@ -0,0 +1,265 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+
+#ifdef __APPLE__
+#include <LLDB/LLDB.h>
+#include <LLDB/SBCommandInterpreter.h>
+#include <LLDB/SBCommandReturnObject.h>
+#include <LLDB/SBDebugger.h>
+#else
+#include "lldb/API/LLDB.h"
+#include <lldb/API/SBCommandInterpreter.h>
+#include <lldb/API/SBCommandReturnObject.h>
+#include <lldb/API/SBDebugger.h>
+#endif
+
+#define NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS 50
+
+#define DEBUG 0
+
+using namespace lldb;
+
+bool *completed_threads_array = 0;
+bool *successful_threads_array  = 0;
+
+bool
+wait_for_stop_event (SBProcess process, SBListener listener)
+{
+    bool stopped = false;
+    while (!stopped)
+    {
+        SBEvent event;
+        bool waitfor_ret = listener.WaitForEvent (2, event);
+        if (event.GetType() == SBProcess::eBroadcastBitStateChanged)
+        {
+            if (process.GetState() == StateType::eStateStopped
+                || process.GetState() == StateType::eStateCrashed
+                || process.GetState() == StateType::eStateDetached
+                || process.GetState() == StateType::eStateExited)
+            {
+                stopped = true;
+            }
+        }
+    }
+    return stopped;
+}
+
+bool
+walk_stack_to_main (SBThread thread)
+{
+    if (thread.IsValid() == 0)
+    {
+        return false;
+    }
+
+    bool found_main = false;
+    uint32_t curr_frame = 0;
+    const uint32_t framecount = thread.GetNumFrames();
+    while (!found_main && curr_frame < framecount)
+    {
+        SBFrame frame = thread.GetFrameAtIndex (curr_frame);
+        if (strcmp (frame.GetFunctionName(), "main") == 0)
+        {
+            found_main = true;
+            break;
+        }
+        curr_frame += 1;
+    }
+    return found_main;
+}
+
+void *do_one_debugger (void *in)
+{
+    uint64_t threadnum = (uint64_t) in;
+
+#if defined (__APPLE__)
+    char *threadname;
+    asprintf (&threadname, "thread #%lld", threadnum);
+    pthread_setname_np (threadname);
+    free (threadname);
+#endif
+
+#if DEBUG == 1
+    printf ("#%lld: Starting debug session\n", threadnum);
+#endif
+
+    SBDebugger debugger = lldb::SBDebugger::Create (false);
+    if (debugger.IsValid ())
+    {
+        debugger.SetAsync (true);
+        SBTarget target = debugger.CreateTargetWithFileAndArch("testprog", "x86_64");
+        SBCommandInterpreter command_interp = debugger.GetCommandInterpreter();
+        if (target.IsValid())
+        {
+            SBBreakpoint bar_br = target.BreakpointCreateByName ("bar", "testprog");
+            if (!bar_br.IsValid())
+            {
+                printf ("#%lld: failed to set breakpoint on bar, exiting.\n", threadnum);
+                exit (1);
+            }
+            SBBreakpoint foo_br = target.BreakpointCreateByName ("foo", "testprog");
+            if (!foo_br.IsValid())
+            {
+                printf ("#%lld: Failed to set breakpoint on foo()\n", threadnum);
+            }
+
+            SBLaunchInfo launch_info (NULL);
+            SBError error;
+            SBProcess process = target.Launch (launch_info, error);
+            if (process.IsValid())
+            {
+                SBListener listener = debugger.GetListener();
+                SBBroadcaster broadcaster = process.GetBroadcaster();
+                uint32_t rc = broadcaster.AddListener (listener, SBProcess::eBroadcastBitStateChanged);
+                if (rc == 0)
+                {
+                    printf ("adding listener failed\n");
+                    exit (1);
+                }
+
+                wait_for_stop_event (process, listener);
+
+                if (!walk_stack_to_main (process.GetThreadAtIndex(0)))
+                {
+                    printf ("#%lld: backtrace while @ foo() failed\n", threadnum);
+                    completed_threads_array[threadnum] = true;
+                    return (void *) 1;
+                }
+
+                if (strcmp (process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetFunctionName(), "foo") != 0)
+                {
+#if DEBUG == 1
+                    printf ("#%lld: First breakpoint did not stop at foo(), instead stopped at '%s'\n", threadnum, process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetFunctionName());
+#endif
+                    completed_threads_array[threadnum] = true;
+                    return (void*) 1;
+                }
+
+                process.Continue();
+
+                wait_for_stop_event (process, listener);
+
+                if (process.GetState() == StateType::eStateExited)
+                {
+                    printf ("#%lld: Process exited\n", threadnum);
+                    completed_threads_array[threadnum] = true;
+                    return (void *) 1;
+                }
+
+
+                if (!walk_stack_to_main (process.GetThreadAtIndex(0)))
+                {
+                    printf ("#%lld: backtrace while @ bar() failed\n", threadnum);
+                    completed_threads_array[threadnum] = true;
+                    return (void *) 1;
+                }
+
+                if (strcmp (process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetFunctionName(), "bar") != 0)
+                {
+                    printf ("#%lld: First breakpoint did not stop at bar()\n", threadnum);
+                    completed_threads_array[threadnum] = true;
+                    return (void*) 1;
+                }
+
+                process.Kill();
+
+                wait_for_stop_event (process, listener);
+
+                SBDebugger::Destroy(debugger);
+
+#if DEBUG == 1
+                printf ("#%lld: All good!\n", threadnum);
+#endif
+                successful_threads_array[threadnum] = true;
+                completed_threads_array[threadnum] = true;
+                return (void*) 0;
+            }
+            else
+            {
+                printf("#%lld: process failed to launch\n", threadnum);
+                successful_threads_array[threadnum] = false;
+                completed_threads_array[threadnum] = true;
+                return (void*) 0;
+            }
+        }
+        else
+        {
+            printf ("#%lld: did not get valid target\n", threadnum);
+            successful_threads_array[threadnum] = false;
+            completed_threads_array[threadnum] = true;
+            return (void*) 0;
+        }
+    }
+    else
+    {
+        printf ("#%lld: did not get debugger\n", threadnum);
+        successful_threads_array[threadnum] = false;
+        completed_threads_array[threadnum] = true;
+        return (void*) 0;
+    }
+    completed_threads_array[threadnum] = true;
+    return (void*) 1;
+}
+
+int main ()
+{
+    SBDebugger::Initialize();
+
+    completed_threads_array = (bool *) malloc (sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
+    memset (completed_threads_array, 0, sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
+    successful_threads_array = (bool *) malloc (sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
+    memset (successful_threads_array, 0, sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
+
+    for (uint64_t i = 0; i< NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS; i++)
+    {
+        pthread_t thread;
+        pthread_create (&thread, NULL, do_one_debugger, (void*) i);
+    }
+
+
+    int max_time_to_wait = 20;  // 20 iterations, or 60 seconds
+    int iter = 0;
+    while (1)
+    {
+        sleep (3);
+        bool all_done = true;
+        int successful_threads = 0;
+        int total_completed_threads = 0;
+        for (uint64_t i = 0; i < NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS; i++)
+        {
+            if (successful_threads_array[i] == true)
+                successful_threads++;
+            if (completed_threads_array[i] == true)
+                total_completed_threads++;
+            if (completed_threads_array[i] == false)
+            {
+                all_done = false;
+            }
+        }
+        if (all_done)
+        {
+#if DEBUG == 1
+            printf ("All threads completed.\n");
+            printf ("%d threads completed successfully out of %d\n", successful_threads, NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
+#endif
+            SBDebugger::Terminate();
+            exit(0);
+        }
+        else
+        {
+#if DEBUG == 1
+            printf ("%d threads completed so far (%d successfully), out of %d\n", total_completed_threads, successful_threads, NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
+#endif
+        }
+        if (iter++ == max_time_to_wait)
+        {
+            printf ("reached maximum timeout but only %d threads have completed so far (%d successfully), out of %d.  Exiting.\n", total_completed_threads, successful_threads, NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
+            break;
+        }
+    }
+
+
+    SBDebugger::Terminate();
+    exit (1);
+}

Added: lldb/trunk/test/api/multiple-debuggers/testprog.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/api/multiple-debuggers/testprog.cpp?rev=212671&view=auto
==============================================================================
--- lldb/trunk/test/api/multiple-debuggers/testprog.cpp (added)
+++ lldb/trunk/test/api/multiple-debuggers/testprog.cpp Wed Jul  9 21:17:31 2014
@@ -0,0 +1,12 @@
+int bar ()
+{
+    return 5;
+}
+int foo ()
+{
+    return bar() + 5;
+}
+int main ()
+{
+    return foo();
+}





More information about the lldb-commits mailing list