diff options
Diffstat (limited to 'tools/lldb-perf/lib/TestCase.cpp')
-rw-r--r-- | tools/lldb-perf/lib/TestCase.cpp | 358 |
1 files changed, 358 insertions, 0 deletions
diff --git a/tools/lldb-perf/lib/TestCase.cpp b/tools/lldb-perf/lib/TestCase.cpp new file mode 100644 index 0000000000000..c23a5e519773c --- /dev/null +++ b/tools/lldb-perf/lib/TestCase.cpp @@ -0,0 +1,358 @@ +//===-- TestCase.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TestCase.h" +#include "Results.h" +#include "Xcode.h" + +using namespace lldb_perf; + +TestCase::TestCase () : + m_debugger(), + m_target(), + m_process(), + m_thread(), + m_listener(), + m_verbose(false), + m_step(0) +{ + SBDebugger::Initialize(); + SBHostOS::ThreadCreated ("<lldb-tester.app.main>"); + m_debugger = SBDebugger::Create(false); + m_listener = m_debugger.GetListener(); + m_listener.StartListeningForEventClass (m_debugger, SBProcess::GetBroadcasterClass(), SBProcess::eBroadcastBitStateChanged | SBProcess::eBroadcastBitInterrupt); +} + +static std::string +GetShortOptionString (struct option *long_options) +{ + std::string option_string; + for (int i = 0; long_options[i].name != NULL; ++i) + { + if (long_options[i].flag == NULL) + { + option_string.push_back ((char) long_options[i].val); + switch (long_options[i].has_arg) + { + default: + case no_argument: + break; + case required_argument: + option_string.push_back (':'); + break; + case optional_argument: + option_string.append (2, ':'); + break; + } + } + } + return option_string; +} + +bool +TestCase::Setup (int& argc, const char**& argv) +{ + bool done = false; + + struct option* long_options = GetLongOptions(); + + if (long_options) + { + std::string short_option_string (GetShortOptionString(long_options)); + + #if __GLIBC__ + optind = 0; + #else + optreset = 1; + optind = 1; + #endif + while (!done) + { + int long_options_index = -1; + const int short_option = ::getopt_long_only (argc, + const_cast<char **>(argv), + short_option_string.c_str(), + long_options, + &long_options_index); + + switch (short_option) + { + case 0: + // Already handled + break; + + case -1: + done = true; + break; + + default: + done = !ParseOption(short_option, optarg); + break; + } + } + argc -= optind; + argv += optind; + } + + return false; +} + +bool +TestCase::Launch (lldb::SBLaunchInfo &launch_info) +{ + lldb::SBError error; + m_process = m_target.Launch (launch_info, error); + if (!error.Success()) + fprintf (stderr, "error: %s\n", error.GetCString()); + if (m_process.IsValid()) + return true; + return false; +} + +bool +TestCase::Launch (std::initializer_list<const char*> args) +{ + std::vector<const char*> args_vect(args); + args_vect.push_back(NULL); + lldb::SBLaunchInfo launch_info((const char**)&args_vect[0]); + return Launch(launch_info); +} + +void +TestCase::SetVerbose (bool b) +{ + m_verbose = b; +} + +bool +TestCase::GetVerbose () +{ + return m_verbose; +} + +void +TestCase::Loop () +{ + while (true) + { + bool call_test_step = false; + if (m_process.IsValid()) + { + SBEvent evt; + m_listener.WaitForEvent (UINT32_MAX, evt); + StateType state = SBProcess::GetStateFromEvent (evt); + if (m_verbose) + printf("event = %s\n",SBDebugger::StateAsCString(state)); + if (SBProcess::GetRestartedFromEvent(evt)) + { + if (m_verbose) + { + const uint32_t num_threads = m_process.GetNumThreads(); + for (auto thread_index = 0; thread_index < num_threads; thread_index++) + { + SBThread thread(m_process.GetThreadAtIndex(thread_index)); + SBFrame frame(thread.GetFrameAtIndex(0)); + SBStream strm; + strm.RedirectToFileHandle(stdout, false); + frame.GetDescription(strm); + } + puts("restarted"); + } + call_test_step = false; + } + else + { + switch (state) + { + case eStateInvalid: + case eStateDetached: + case eStateCrashed: + case eStateUnloaded: + break; + case eStateExited: + return; + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + call_test_step = false; + break; + + case eStateStopped: + case eStateSuspended: + { + call_test_step = true; + bool fatal = false; + bool selected_thread = false; + const uint32_t num_threads = m_process.GetNumThreads(); + for (auto thread_index = 0; thread_index < num_threads; thread_index++) + { + SBThread thread(m_process.GetThreadAtIndex(thread_index)); + SBFrame frame(thread.GetFrameAtIndex(0)); + SBStream strm; + strm.RedirectToFileHandle(stdout, false); + frame.GetDescription(strm); + bool select_thread = false; + StopReason stop_reason = thread.GetStopReason(); + if (m_verbose) printf("tid = 0x%llx pc = 0x%llx ",thread.GetThreadID(),frame.GetPC()); + switch (stop_reason) + { + case eStopReasonNone: + if (m_verbose) + printf("none\n"); + break; + + case eStopReasonTrace: + select_thread = true; + if (m_verbose) + printf("trace\n"); + break; + + case eStopReasonPlanComplete: + select_thread = true; + if (m_verbose) + printf("plan complete\n"); + break; + case eStopReasonThreadExiting: + if (m_verbose) + printf("thread exiting\n"); + break; + case eStopReasonExec: + if (m_verbose) + printf("exec\n"); + break; + case eStopReasonInvalid: + if (m_verbose) + printf("invalid\n"); + break; + case eStopReasonException: + select_thread = true; + if (m_verbose) + printf("exception\n"); + fatal = true; + break; + case eStopReasonBreakpoint: + select_thread = true; + if (m_verbose) + printf("breakpoint id = %lld.%lld\n",thread.GetStopReasonDataAtIndex(0),thread.GetStopReasonDataAtIndex(1)); + break; + case eStopReasonWatchpoint: + select_thread = true; + if (m_verbose) + printf("watchpoint id = %lld\n",thread.GetStopReasonDataAtIndex(0)); + break; + case eStopReasonSignal: + select_thread = true; + if (m_verbose) + printf("signal %d\n",(int)thread.GetStopReasonDataAtIndex(0)); + break; + } + if (select_thread && !selected_thread) + { + m_thread = thread; + selected_thread = m_process.SetSelectedThread(thread); + } + } + if (fatal) + { + if (m_verbose) Xcode::RunCommand(m_debugger,"bt all",true); + exit(1); + } + } + break; + } + } + } + else + { + call_test_step = true; + } + + if (call_test_step) + { + do_the_call: + if (m_verbose) + printf("RUNNING STEP %d\n",m_step); + ActionWanted action; + TestStep(m_step, action); + m_step++; + SBError err; + switch (action.type) + { + case ActionWanted::Type::eNone: + // Just exit and wait for the next event + break; + case ActionWanted::Type::eContinue: + err = m_process.Continue(); + break; + case ActionWanted::Type::eStepOut: + if (action.thread.IsValid() == false) + { + if (m_verbose) + { + Xcode::RunCommand(m_debugger,"bt all",true); + printf("error: invalid thread for step out on step %d\n", m_step); + } + exit(501); + } + m_process.SetSelectedThread(action.thread); + action.thread.StepOut(); + break; + case ActionWanted::Type::eStepOver: + if (action.thread.IsValid() == false) + { + if (m_verbose) + { + Xcode::RunCommand(m_debugger,"bt all",true); + printf("error: invalid thread for step over %d\n",m_step); + } + exit(500); + } + m_process.SetSelectedThread(action.thread); + action.thread.StepOver(); + break; + case ActionWanted::Type::eRelaunch: + if (m_process.IsValid()) + { + m_process.Kill(); + m_process.Clear(); + } + Launch(action.launch_info); + break; + case ActionWanted::Type::eKill: + if (m_verbose) + printf("kill\n"); + m_process.Kill(); + return; + case ActionWanted::Type::eCallNext: + goto do_the_call; + break; + } + } + + } + + if (GetVerbose()) printf("I am gonna die at step %d\n",m_step); +} + +int +TestCase::Run (TestCase& test, int argc, const char** argv) +{ + if (test.Setup(argc, argv)) + { + test.Loop(); + Results results; + test.WriteResults(results); + return RUN_SUCCESS; + } + else + return RUN_SETUP_ERROR; +} + |