forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			309 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			309 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===-- 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;
 | |
| }
 |