//===-- 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(""); 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(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 args) { std::vector 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; }