diff options
Diffstat (limited to 'packages/Python/lldbsuite/test/api/multiple-debuggers')
5 files changed, 348 insertions, 0 deletions
| diff --git a/packages/Python/lldbsuite/test/api/multiple-debuggers/.categories b/packages/Python/lldbsuite/test/api/multiple-debuggers/.categories new file mode 100644 index 0000000000000..6e70196ccd707 --- /dev/null +++ b/packages/Python/lldbsuite/test/api/multiple-debuggers/.categories @@ -0,0 +1 @@ +stresstest diff --git a/packages/Python/lldbsuite/test/api/multiple-debuggers/Makefile b/packages/Python/lldbsuite/test/api/multiple-debuggers/Makefile new file mode 100644 index 0000000000000..08f8850e3e8f3 --- /dev/null +++ b/packages/Python/lldbsuite/test/api/multiple-debuggers/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../make + +MAKE_DSYM := NO + +ENABLE_THREADS := YES +CXX_SOURCES := multi-process-driver.cpp testprog.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/api/multiple-debuggers/TestMultipleDebuggers.py b/packages/Python/lldbsuite/test/api/multiple-debuggers/TestMultipleDebuggers.py new file mode 100644 index 0000000000000..67dca2273d8ab --- /dev/null +++ b/packages/Python/lldbsuite/test/api/multiple-debuggers/TestMultipleDebuggers.py @@ -0,0 +1,44 @@ +"""Test the lldb public C++ api when doing multiple debug sessions simultaneously.""" + +from __future__ import print_function + + + +import os, re +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil +import lldb +import subprocess + +class TestMultipleSimultaneousDebuggers(TestBase): + +    mydir = TestBase.compute_mydir(__file__) + +    @skipIfi386 +    @skipIfNoSBHeaders +    @expectedFailureFreeBSD("llvm.org/pr20282") +    @expectedFailureLinux("llvm.org/pr20282") +    @expectedFailureWindows # Test crashes +    @expectedFlakeyDarwin() +    def test_multiple_debuggers(self): +        env = {self.dylibPath : self.getLLDBLibraryEnvVal()} + +        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.signBinary(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)) + +# 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) diff --git a/packages/Python/lldbsuite/test/api/multiple-debuggers/multi-process-driver.cpp b/packages/Python/lldbsuite/test/api/multiple-debuggers/multi-process-driver.cpp new file mode 100644 index 0000000000000..1c2689ff6c4f6 --- /dev/null +++ b/packages/Python/lldbsuite/test/api/multiple-debuggers/multi-process-driver.cpp @@ -0,0 +1,283 @@ + +// This program creates NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS of pthreads,  +// creates an lldb Debugger on each thread, creates targets, inserts two +// breakpoints, runs to the first breakpoint, backtraces, runs to the second +// breakpoint, backtraces, kills the inferior process, closes down the +// debugger. + +// The main thread keeps track of which pthreads have completed and which  +// pthreads have completed successfully, and exits when all pthreads have +// completed successfully, or our time limit has been exceeded. + +// This test file helps to uncover race conditions and locking mistakes +// that are hit when lldb is being used to debug multiple processes +// simultaneously. + +#include <stdio.h> +#include <stdlib.h> + + +#include "lldb/API/LLDB.h" +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBDebugger.h" + +#include <chrono> +#include <thread> + +#define NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS 20 + +#define DEBUG 0 + +using namespace lldb; + +bool *completed_threads_array = 0; +bool *successful_threads_array  = 0; + +const char *inferior_process_name = "testprog"; + +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(inferior_process_name, "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 (int argc, char **argv) +{ +    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); + +    if (argc > 1 && argv[1] != NULL) +    { +        inferior_process_name = argv[1]; +    } + +    std::thread threads[NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS]; +    for (uint64_t i = 0; i< NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS; i++) +    { +        threads[i] = std::move(std::thread(do_one_debugger, (void*)i)); +    } + + +    int max_time_to_wait = 20;  // 20 iterations, or 60 seconds +    int iter = 0; +    while (1) +    { +        std::this_thread::sleep_for(std::chrono::seconds(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); +} diff --git a/packages/Python/lldbsuite/test/api/multiple-debuggers/testprog.cpp b/packages/Python/lldbsuite/test/api/multiple-debuggers/testprog.cpp new file mode 100644 index 0000000000000..c9d1ea14f17b9 --- /dev/null +++ b/packages/Python/lldbsuite/test/api/multiple-debuggers/testprog.cpp @@ -0,0 +1,12 @@ +int bar () +{ +    return 5; +} +int foo () +{ +    return bar() + 5; +} +int main () +{ +    return foo(); +} | 
