| //===-- 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; | 
 | } |