2013-03-21 11:39:51 +08:00
|
|
|
//===-- TestCase.cpp --------------------------------------------*- C++ -*-===//
|
2013-03-09 04:29:13 +08:00
|
|
|
//
|
2013-03-21 11:39:51 +08:00
|
|
|
// The LLVM Compiler Infrastructure
|
2013-03-09 04:29:13 +08:00
|
|
|
//
|
2013-03-21 11:39:51 +08:00
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
2013-03-09 04:29:13 +08:00
|
|
|
//
|
2013-03-21 11:39:51 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2013-03-09 04:29:13 +08:00
|
|
|
|
|
|
|
#include "TestCase.h"
|
2013-03-22 10:31:35 +08:00
|
|
|
#include "Results.h"
|
2013-03-09 04:29:13 +08:00
|
|
|
#include "Xcode.h"
|
|
|
|
|
2013-03-19 06:34:00 +08:00
|
|
|
using namespace lldb_perf;
|
2013-03-09 04:29:13 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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);
|
2013-03-09 04:29:13 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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;
|
|
|
|
}
|
2013-04-02 02:02:25 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
return option_string;
|
2013-04-02 02:02:25 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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;
|
|
|
|
}
|
2013-04-02 02:02:25 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
}
|
2013-03-15 03:00:42 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
return false;
|
2013-03-09 04:29:13 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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;
|
2013-04-03 05:31:18 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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);
|
2013-03-09 04:29:13 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void TestCase::SetVerbose(bool b) { m_verbose = b; }
|
|
|
|
|
|
|
|
bool TestCase::GetVerbose() { return m_verbose; }
|
2013-03-09 04:29:13 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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();
|
2013-03-22 10:31:35 +08:00
|
|
|
if (m_verbose)
|
2016-09-07 04:57:50 +08:00
|
|
|
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;
|
2014-02-28 05:35:49 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
if (select_thread && !selected_thread) {
|
|
|
|
m_thread = thread;
|
|
|
|
selected_thread = m_process.SetSelectedThread(thread);
|
2014-02-28 05:35:49 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
if (fatal) {
|
|
|
|
if (m_verbose)
|
|
|
|
Xcode::RunCommand(m_debugger, "bt all", true);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
} break;
|
2013-03-22 10:31:35 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
call_test_step = true;
|
|
|
|
}
|
2013-03-22 10:31:35 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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();
|
2013-03-22 10:31:35 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-03-22 10:31:35 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (GetVerbose())
|
|
|
|
printf("I am gonna die at step %d\n", m_step);
|
2013-03-09 04:29:13 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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;
|
2013-03-09 04:29:13 +08:00
|
|
|
}
|