blob: 393948989967829c4304c5cf003f5ccbe1a45fda [file] [log] [blame]
//===-- 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;
}