to reflect the new license. We understand that people may be surprised that we're moving the header entirely to discuss the new license. We checked this carefully with the Foundation's lawyer and we believe this is the correct approach. Essentially, all code in the project is now made available by the LLVM project under our new license, so you will see that the license headers include that license only. Some of our contributors have contributed code under our old license, and accordingly, we have retained a copy of our old license notice in the top-level files in each project and repository. llvm-svn: 351636
308 lines
9.0 KiB
C++
308 lines
9.0 KiB
C++
//===-- TestCase.cpp --------------------------------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#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;
|
|
}
|