summaryrefslogtreecommitdiffstats
path: root/lldb/source/Commands/CommandObjectThread.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/source/Commands/CommandObjectThread.cpp')
-rw-r--r--lldb/source/Commands/CommandObjectThread.cpp1277
1 files changed, 1277 insertions, 0 deletions
diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp
new file mode 100644
index 00000000000..07777a19cfe
--- /dev/null
+++ b/lldb/source/Commands/CommandObjectThread.cpp
@@ -0,0 +1,1277 @@
+//===-- CommandObjectThread.cpp ---------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectThread.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Core/Options.h"
+#include "lldb/Core/State.h"
+#include "lldb/Core/SourceManager.h"
+
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/ThreadPlanContinue.h"
+#include "lldb/Target/ThreadPlanStepInstruction.h"
+#include "lldb/Target/ThreadPlanStepOut.h"
+#include "lldb/Target/ThreadPlanStepRange.h"
+#include "lldb/Target/ThreadPlanStepInRange.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Symbol/LineEntry.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+
+bool
+lldb_private::DisplayThreadInfo
+(
+ CommandInterpreter *interpreter,
+ Stream &strm,
+ Thread *thread,
+ bool only_threads_with_stop_reason,
+ bool show_source
+)
+{
+ if (thread)
+ {
+ if (only_threads_with_stop_reason)
+ {
+ StopReason thread_stop_reason = eStopReasonNone;
+ Thread::StopInfo thread_stop_info;
+ if (thread->GetStopInfo(&thread_stop_info))
+ {
+ thread_stop_reason = thread_stop_info.GetStopReason();
+ if (thread_stop_reason == eStopReasonNone)
+ return false;
+ }
+ }
+
+ strm.Indent();
+ strm.Printf("%c ", thread->GetProcess().GetThreadList().GetCurrentThread().get() == thread ? '*' : ' ');
+
+ // Show one frame with only the first showing source
+ if (show_source)
+ {
+ DisplayFramesForExecutionContext (thread,
+ interpreter,
+ strm,
+ true,
+ 0, // Start at first frame
+ 1, // Number of frames to show
+ false,// Don't show the frame info since we already displayed most of it above...
+ 1, // Show source for the first frame
+ 3, // lines of source context before
+ 3); // lines of source context after
+ }
+ else
+ {
+ thread->DumpInfo (strm,
+ true, // Dump the stop reason?
+ true, // Dump the thread name?
+ true, // Dump the queue name?
+ 0); // Display context info for stack frame zero
+
+ strm.EOL();
+ }
+
+ return true;
+ }
+ return false;
+}
+
+size_t
+lldb_private::DisplayThreadsInfo
+(
+ CommandInterpreter *interpreter,
+ ExecutionContext *exe_ctx,
+ CommandReturnObject &result,
+ bool only_threads_with_stop_reason,
+ bool show_source
+)
+{
+ StreamString strm;
+
+ size_t num_thread_infos_dumped = 0;
+
+ if (!exe_ctx->process)
+ return 0;
+
+ const size_t num_threads = exe_ctx->process->GetThreadList().GetSize();
+ if (num_threads > 0)
+ {
+
+ for (uint32_t i = 0; i < num_threads; i++)
+ {
+ Thread *thread = exe_ctx->process->GetThreadList().GetThreadAtIndex(i).get();
+ if (thread)
+ {
+ if (DisplayThreadInfo (interpreter,
+ strm,
+ thread,
+ only_threads_with_stop_reason,
+ show_source))
+ ++num_thread_infos_dumped;
+ }
+ }
+ }
+
+ if (num_thread_infos_dumped > 0)
+ {
+ if (num_thread_infos_dumped < num_threads)
+ result.GetOutputStream().Printf("%u of %u threads stopped with reasons:\n", num_thread_infos_dumped, num_threads);
+
+ result.GetOutputStream().GetString().append(strm.GetString());
+ result.SetStatus (eReturnStatusSuccessFinishNoResult);
+ }
+ return num_thread_infos_dumped;
+}
+
+
+size_t
+lldb_private::DisplayFramesForExecutionContext
+(
+ Thread *thread,
+ CommandInterpreter *interpreter,
+ Stream& strm,
+ bool ascending,
+ uint32_t first_frame,
+ uint32_t num_frames,
+ bool show_frame_info,
+ uint32_t num_frames_with_source,
+ uint32_t source_lines_before,
+ uint32_t source_lines_after
+)
+{
+ if (thread == NULL)
+ return 0;
+
+ size_t num_frames_displayed = 0;
+
+ if (num_frames == 0)
+ return 0;
+
+ thread->DumpInfo (strm,
+ true, // Dump the stop reason?
+ true, // Dump the thread name?
+ true, // Dump the queue name?
+ 0); // Dump info for stack frame zero
+ strm.EOL();
+ strm.IndentMore();
+
+ StackFrameSP frame_sp;
+ int frame_idx = 0;
+
+ if (ascending)
+ {
+ for (frame_idx = first_frame; frame_idx < first_frame + num_frames; ++frame_idx)
+ {
+ frame_sp = thread->GetStackFrameAtIndex (frame_idx);
+ if (frame_sp.get() == NULL)
+ break;
+
+ if (DisplayFrameForExecutionContext (thread,
+ frame_sp.get(),
+ interpreter,
+ strm,
+ show_frame_info,
+ num_frames_with_source > first_frame - frame_idx,
+ source_lines_before,
+ source_lines_after) == false)
+ break;
+
+ ++num_frames_displayed;
+ }
+ }
+ else
+ {
+ for (frame_idx = first_frame + num_frames - 1; frame_idx >= first_frame; --frame_idx)
+ {
+ frame_sp = thread->GetStackFrameAtIndex (frame_idx);
+ if (frame_sp == NULL)
+ break;
+
+ if (DisplayFrameForExecutionContext (thread,
+ frame_sp.get(),
+ interpreter,
+ strm,
+ show_frame_info,
+ num_frames_with_source > first_frame - frame_idx,
+ source_lines_before,
+ source_lines_after) == false)
+ break;
+
+ ++num_frames_displayed;
+ }
+ }
+ strm.IndentLess();
+ return num_frames_displayed;
+}
+
+bool
+lldb_private::DisplayFrameForExecutionContext
+(
+ Thread *thread,
+ StackFrame *frame,
+ CommandInterpreter *interpreter,
+ Stream& strm,
+ bool show_frame_info,
+ bool show_source,
+ uint32_t source_lines_before,
+ uint32_t source_lines_after
+)
+{
+ // thread and frame must be filled in prior to calling this function
+ if (thread && frame)
+ {
+ if (show_frame_info)
+ {
+ strm.Indent();
+ frame->Dump (&strm, true);
+ strm.EOL();
+ }
+
+ SymbolContext sc (frame->GetSymbolContext(eSymbolContextCompUnit | eSymbolContextLineEntry));
+
+ if (show_source && sc.comp_unit && sc.line_entry.IsValid())
+ {
+ interpreter->GetSourceManager().DisplaySourceLinesWithLineNumbers (
+ sc.line_entry.file,
+ sc.line_entry.line,
+ 3,
+ 3,
+ "->",
+ &strm);
+
+ }
+ return true;
+ }
+ return false;
+}
+
+
+//-------------------------------------------------------------------------
+// CommandObjectThreadBacktrace
+//-------------------------------------------------------------------------
+
+class CommandObjectThreadBacktrace : public CommandObject
+{
+public:
+
+ CommandObjectThreadBacktrace () :
+ CommandObject ("thread backtrace",
+ "Shows the stack for one or more threads.",
+ "thread backtrace [<thread-idx>] ...",
+ eFlagProcessMustBeLaunched | eFlagProcessMustBePaused),
+ m_ascending (true)
+ {
+ }
+
+ ~CommandObjectThreadBacktrace()
+ {
+ }
+
+
+ bool
+ Execute
+ (
+ Args& command,
+ CommandContext *context,
+ CommandInterpreter *interpreter,
+ CommandReturnObject &result
+ )
+ {
+ if (command.GetArgumentCount() == 0)
+ {
+ ExecutionContext exe_ctx(context->GetExecutionContext());
+ if (exe_ctx.thread)
+ {
+ bool show_frame_info = true;
+ uint32_t num_frames_with_source = 0; // Don't show any frasmes with source when backtracing
+ if (DisplayFramesForExecutionContext (exe_ctx.thread,
+ interpreter,
+ result.GetOutputStream(),
+ m_ascending,
+ 0,
+ UINT32_MAX,
+ show_frame_info,
+ num_frames_with_source,
+ 3,
+ 3))
+ {
+ result.SetStatus (eReturnStatusSuccessFinishResult);
+ }
+ }
+ else
+ {
+ result.AppendError ("invalid thread");
+ result.SetStatus (eReturnStatusFailed);
+ }
+ }
+ else
+ {
+ result.AppendError ("backtrace doesn't take arguments (for now)");
+ result.SetStatus (eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+protected:
+ bool m_ascending;
+};
+
+
+typedef enum StepScope
+{
+ eStepScopeSource,
+ eStepScopeInstruction
+};
+
+class CommandObjectThreadStepWithTypeAndScope : public CommandObject
+{
+public:
+
+ class CommandOptions : public Options
+ {
+ public:
+
+ CommandOptions () :
+ Options()
+ {
+ // Keep default values of all options in one place: ResetOptionValues ()
+ ResetOptionValues ();
+ }
+
+ virtual
+ ~CommandOptions ()
+ {
+ }
+
+ virtual Error
+ SetOptionValue (int option_idx, const char *option_arg)
+ {
+ Error error;
+ char short_option = (char) m_getopt_table[option_idx].val;
+
+ switch (short_option)
+ {
+ case 'a':
+ {
+ bool success;
+ m_avoid_no_debug = Args::StringToBoolean (option_arg, true, &success);
+ if (!success)
+ error.SetErrorStringWithFormat("Invalid boolean value for option '%c'.\n", short_option);
+ }
+ break;
+ case 'm':
+ {
+ bool found_one = false;
+ OptionEnumValueElement *enum_values = g_option_table[option_idx].enum_values;
+ m_run_mode = (lldb::RunMode) Args::StringToOptionEnum(option_arg, enum_values, eOnlyDuringStepping, &found_one);
+ if (!found_one)
+ error.SetErrorStringWithFormat("Invalid enumeration value for option '%c'.\n", short_option);
+ }
+ break;
+ default:
+ error.SetErrorStringWithFormat("Invalid short option character '%c'.\n", short_option);
+ break;
+
+ }
+ return error;
+ }
+
+ void
+ ResetOptionValues ()
+ {
+ Options::ResetOptionValues();
+ m_avoid_no_debug = true;
+ m_run_mode = eOnlyDuringStepping;
+ }
+
+ const lldb::OptionDefinition*
+ GetDefinitions ()
+ {
+ return g_option_table;
+ }
+
+ // Options table: Required for subclasses of Options.
+
+ static lldb::OptionDefinition g_option_table[];
+
+ // Instance variables to hold the values for command options.
+ bool m_avoid_no_debug;
+ RunMode m_run_mode;
+ };
+
+ CommandObjectThreadStepWithTypeAndScope (const char *name,
+ const char *help,
+ const char *syntax,
+ uint32_t flags,
+ StepType step_type,
+ StepScope step_scope) :
+ CommandObject (name, help, syntax, flags),
+ m_step_type (step_type),
+ m_step_scope (step_scope),
+ m_options ()
+ {
+ }
+
+ virtual
+ ~CommandObjectThreadStepWithTypeAndScope ()
+ {
+ }
+
+ virtual
+ Options *
+ GetOptions ()
+ {
+ return &m_options;
+ }
+
+ virtual bool
+ Execute (Args& command,
+ CommandContext *context,
+ CommandInterpreter *interpreter,
+ CommandReturnObject &result)
+ {
+ Process *process = context->GetExecutionContext().process;
+ bool synchronous_execution = interpreter->GetSynchronous();
+
+ if (process == NULL)
+ {
+ result.AppendError ("need a valid process to step");
+ result.SetStatus (eReturnStatusFailed);
+
+ }
+ else
+ {
+ const uint32_t num_threads = process->GetThreadList().GetSize();
+ Thread *thread = NULL;
+
+ if (command.GetArgumentCount() == 0)
+ {
+ thread = process->GetThreadList().GetCurrentThread().get();
+ if (thread == NULL)
+ {
+ result.AppendError ("no current thread in process");
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
+ }
+ else
+ {
+ const char *thread_idx_cstr = command.GetArgumentAtIndex(0);
+ uint32_t step_thread_idx = Args::StringToUInt32 (thread_idx_cstr, LLDB_INVALID_INDEX32);
+ if (step_thread_idx == LLDB_INVALID_INDEX32)
+ {
+ result.AppendErrorWithFormat ("Invalid thread index '%s'.\n", thread_idx_cstr);
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
+ thread = process->GetThreadList().FindThreadByIndexID(step_thread_idx).get();
+ if (thread == NULL)
+ {
+ result.AppendErrorWithFormat ("Thread index %u is out of range (valid values are 0 - %u).\n",
+ step_thread_idx, 0, num_threads);
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ const bool abort_other_plans = false;
+ const lldb::RunMode stop_other_threads = m_options.m_run_mode;
+
+ // This is a bit unfortunate, but not all the commands in this command object support
+ // only while stepping, so I use the bool for them.
+ bool bool_stop_other_threads;
+ if (m_options.m_run_mode == eAllThreads)
+ bool_stop_other_threads = false;
+ else
+ bool_stop_other_threads = true;
+
+ if (m_step_type == eStepTypeInto)
+ {
+ StackFrame *frame = thread->GetStackFrameAtIndex(0).get();
+ ThreadPlan *new_plan;
+
+ if (frame->HasDebugInformation ())
+ {
+ new_plan = thread->QueueThreadPlanForStepRange (abort_other_plans, m_step_type,
+ frame->GetSymbolContext(eSymbolContextEverything).line_entry.range,
+ frame->GetSymbolContext(eSymbolContextEverything),
+ stop_other_threads);
+ if (new_plan)
+ {
+ ThreadPlanStepInRange *real_plan = dynamic_cast<ThreadPlanStepInRange *> (new_plan);
+ if (real_plan)
+ {
+ if (m_options.m_avoid_no_debug)
+ {
+ real_plan->GetFlags().Set (ThreadPlanShouldStopHere::eAvoidNoDebug);
+ }
+ else
+ {
+ real_plan->GetFlags().Clear (ThreadPlanShouldStopHere::eAvoidNoDebug);
+ }
+ }
+ }
+ }
+ else
+ new_plan = thread->QueueThreadPlanForStepSingleInstruction (false, abort_other_plans, bool_stop_other_threads);
+
+ process->GetThreadList().SetCurrentThreadByID (thread->GetID());
+ process->Resume ();
+ }
+ else if (m_step_type == eStepTypeOver)
+ {
+ StackFrame *frame = thread->GetStackFrameAtIndex(0).get();
+ ThreadPlan *new_plan;
+
+ if (frame->HasDebugInformation())
+ new_plan = thread->QueueThreadPlanForStepRange (abort_other_plans,
+ m_step_type,
+ frame->GetSymbolContext(eSymbolContextEverything).line_entry.range,
+ frame->GetSymbolContext(eSymbolContextEverything),
+ stop_other_threads);
+ else
+ new_plan = thread->QueueThreadPlanForStepSingleInstruction (true,
+ abort_other_plans,
+ bool_stop_other_threads);
+
+ // FIXME: This will keep the step plan on the thread stack when we hit a breakpoint while stepping over.
+ // Maybe there should be a parameter to control this.
+ new_plan->SetOkayToDiscard(false);
+
+ process->GetThreadList().SetCurrentThreadByID (thread->GetID());
+ process->Resume ();
+ }
+ else if (m_step_type == eStepTypeTrace)
+ {
+ thread->QueueThreadPlanForStepSingleInstruction (false, abort_other_plans, bool_stop_other_threads);
+ process->GetThreadList().SetCurrentThreadByID (thread->GetID());
+ process->Resume ();
+ }
+ else if (m_step_type == eStepTypeTraceOver)
+ {
+ thread->QueueThreadPlanForStepSingleInstruction (true, abort_other_plans, bool_stop_other_threads);
+ process->GetThreadList().SetCurrentThreadByID (thread->GetID());
+ process->Resume ();
+ }
+ else if (m_step_type == eStepTypeOut)
+ {
+ ThreadPlan *new_plan;
+
+ new_plan = thread->QueueThreadPlanForStepOut (abort_other_plans, NULL, false, bool_stop_other_threads, eVoteYes, eVoteNoOpinion);
+ // FIXME: This will keep the step plan on the thread stack when we hit a breakpoint while stepping over.
+ // Maybe there should be a parameter to control this.
+ new_plan->SetOkayToDiscard(false);
+
+ process->GetThreadList().SetCurrentThreadByID (thread->GetID());
+ process->Resume ();
+ }
+ else
+ {
+ result.AppendError ("step type is not supported");
+ result.SetStatus (eReturnStatusFailed);
+ }
+ if (synchronous_execution)
+ {
+ StateType state = process->WaitForProcessToStop (NULL);
+
+ //EventSP event_sp;
+ //StateType state = process->WaitForStateChangedEvents (NULL, event_sp);
+ //while (! StateIsStoppedState (state))
+ // {
+ // state = process->WaitForStateChangedEvents (NULL, event_sp);
+ // }
+ process->GetThreadList().SetCurrentThreadByID (thread->GetID());
+ result.SetDidChangeProcessState (true);
+ result.AppendMessageWithFormat ("Process %i %s\n", process->GetID(), StateAsCString (state));
+ result.SetStatus (eReturnStatusSuccessFinishNoResult);
+ }
+ }
+ return result.Succeeded();
+ }
+
+protected:
+ StepType m_step_type;
+ StepScope m_step_scope;
+ CommandOptions m_options;
+};
+
+static lldb::OptionEnumValueElement
+g_tri_running_mode[] =
+{
+{ eOnlyThisThread, "thisThread", "Run only this thread"},
+{ eAllThreads, "allThreads", "Run all threads"},
+{ eOnlyDuringStepping, "whileStepping", "Run only this thread while stepping"},
+{ 0, NULL, NULL }
+};
+
+static lldb::OptionEnumValueElement
+g_duo_running_mode[] =
+{
+{ eOnlyThisThread, "thisThread", "Run only this thread"},
+{ eAllThreads, "allThreads", "Run all threads"},
+{ 0, NULL, NULL }
+};
+
+lldb::OptionDefinition
+CommandObjectThreadStepWithTypeAndScope::CommandOptions::g_option_table[] =
+{
+{ 0, true, "avoid_no_debug", 'a', required_argument, NULL, 0, "<avoid_no_debug>", "Should step-in step over functions with no debug information"},
+{ 0, true, "run_mode", 'm', required_argument, g_tri_running_mode, 0, "<run_mode>", "Determine how to run other threads while stepping this one"},
+{ 0, false, NULL, 0, 0, NULL, 0, NULL, NULL }
+};
+
+
+//-------------------------------------------------------------------------
+// CommandObjectThreadContinue
+//-------------------------------------------------------------------------
+
+class CommandObjectThreadContinue : public CommandObject
+{
+public:
+
+ CommandObjectThreadContinue () :
+ CommandObject ("thread continue",
+ "Continues execution of one or more threads in an active process.",
+ "thread continue <thread-index> [<thread-index> ...]",
+ eFlagProcessMustBeLaunched | eFlagProcessMustBePaused)
+ {
+ }
+
+
+ virtual
+ ~CommandObjectThreadContinue ()
+ {
+ }
+
+ virtual bool
+ Execute (Args& command,
+ CommandContext *context,
+ CommandInterpreter *interpreter,
+ CommandReturnObject &result)
+ {
+ bool synchronous_execution = interpreter->GetSynchronous ();
+
+ if (!context->GetTarget())
+ {
+ result.AppendError ("invalid target, set executable file using 'file' command");
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
+
+ Process *process = context->GetExecutionContext().process;
+ if (process == NULL)
+ {
+ result.AppendError ("no process exists. Cannot continue");
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
+
+ StateType state = process->GetState();
+ if ((state == eStateCrashed) || (state == eStateStopped) || (state == eStateSuspended))
+ {
+ const uint32_t num_threads = process->GetThreadList().GetSize();
+ uint32_t idx;
+ const size_t argc = command.GetArgumentCount();
+ if (argc > 0)
+ {
+ std::vector<uint32_t> resume_thread_indexes;
+ for (uint32_t i=0; i<argc; ++i)
+ {
+ idx = Args::StringToUInt32 (command.GetArgumentAtIndex(0), LLDB_INVALID_INDEX32);
+ if (idx < num_threads)
+ resume_thread_indexes.push_back(idx);
+ else
+ result.AppendWarningWithFormat("Thread index %u out of range.\n", idx);
+ }
+
+ if (resume_thread_indexes.empty())
+ {
+ result.AppendError ("no valid thread indexes were specified");
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
+ else
+ {
+ result.AppendMessage ("Resuming thread ");
+ for (idx=0; idx<num_threads; ++idx)
+ {
+ Thread *thread = process->GetThreadList().GetThreadAtIndex(idx).get();
+ if (find(resume_thread_indexes.begin(), resume_thread_indexes.end(), idx) != resume_thread_indexes.end())
+ {
+ result.AppendMessageWithFormat ("%u ", idx);
+ thread->SetResumeState (eStateRunning);
+ }
+ else
+ {
+ thread->SetResumeState (eStateSuspended);
+ }
+ }
+ result.AppendMessageWithFormat ("in process %i\n", process->GetID());
+ }
+ }
+ else
+ {
+ Thread *current_thread = process->GetThreadList().GetCurrentThread().get();
+ if (current_thread == NULL)
+ {
+ result.AppendError ("the process doesn't have a current thread");
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
+ // Set the actions that the threads should each take when resuming
+ for (idx=0; idx<num_threads; ++idx)
+ {
+ Thread *thread = process->GetThreadList().GetThreadAtIndex(idx).get();
+ if (thread == current_thread)
+ {
+ result.AppendMessageWithFormat ("Resuming thread 0x%4.4x in process %i\n", thread->GetID(), process->GetID());
+ thread->SetResumeState (eStateRunning);
+ }
+ else
+ {
+ thread->SetResumeState (eStateSuspended);
+ }
+ }
+ }
+
+ Error error (process->Resume());
+ if (error.Success())
+ {
+ result.AppendMessageWithFormat ("Resuming process %i\n", process->GetID());
+ if (synchronous_execution)
+ {
+ StateType state = process->WaitForProcessToStop (NULL);
+
+ result.SetDidChangeProcessState (true);
+ result.AppendMessageWithFormat ("Process %i %s\n", process->GetID(), StateAsCString (state));
+ result.SetStatus (eReturnStatusSuccessFinishNoResult);
+ }
+ else
+ {
+ result.SetStatus (eReturnStatusSuccessContinuingNoResult);
+ }
+ }
+ else
+ {
+ result.AppendErrorWithFormat("Failed to resume process: %s\n", error.AsCString());
+ result.SetStatus (eReturnStatusFailed);
+ }
+ }
+ else
+ {
+ result.AppendErrorWithFormat ("Process cannot be continued from its current state (%s).\n",
+ StateAsCString(state));
+ result.SetStatus (eReturnStatusFailed);
+ }
+
+ return result.Succeeded();
+ }
+
+};
+
+//-------------------------------------------------------------------------
+// CommandObjectThreadUntil
+//-------------------------------------------------------------------------
+
+class CommandObjectThreadUntil : public CommandObject
+{
+public:
+
+ class CommandOptions : public Options
+ {
+ public:
+ uint32_t m_thread_idx;
+ uint32_t m_frame_idx;
+
+ CommandOptions () :
+ Options(),
+ m_thread_idx(LLDB_INVALID_THREAD_ID),
+ m_frame_idx(LLDB_INVALID_FRAME_ID)
+ {
+ // Keep default values of all options in one place: ResetOptionValues ()
+ ResetOptionValues ();
+ }
+
+ virtual
+ ~CommandOptions ()
+ {
+ }
+
+ virtual Error
+ SetOptionValue (int option_idx, const char *option_arg)
+ {
+ Error error;
+ char short_option = (char) m_getopt_table[option_idx].val;
+
+ switch (short_option)
+ {
+ case 't':
+ {
+ uint32_t m_thread_idx = Args::StringToUInt32 (option_arg, LLDB_INVALID_INDEX32);
+ if (m_thread_idx == LLDB_INVALID_INDEX32)
+ {
+ error.SetErrorStringWithFormat ("Invalid thread index '%s'.\n", option_arg);
+ }
+ }
+ break;
+ case 'f':
+ {
+ m_frame_idx = Args::StringToUInt32 (option_arg, LLDB_INVALID_FRAME_ID);
+ if (m_frame_idx == LLDB_INVALID_FRAME_ID)
+ {
+ error.SetErrorStringWithFormat ("Invalid frame index '%s'.\n", option_arg);
+ }
+ }
+ break;
+ case 'm':
+ {
+ bool found_one = false;
+ OptionEnumValueElement *enum_values = g_option_table[option_idx].enum_values;
+ lldb::RunMode run_mode = (lldb::RunMode) Args::StringToOptionEnum(option_arg, enum_values, eOnlyDuringStepping, &found_one);
+
+ if (!found_one)
+ error.SetErrorStringWithFormat("Invalid enumeration value for option '%c'.\n", short_option);
+ else if (run_mode == eAllThreads)
+ m_stop_others = false;
+ else
+ m_stop_others = true;
+
+ }
+ break;
+ default:
+ error.SetErrorStringWithFormat("Invalid short option character '%c'.\n", short_option);
+ break;
+
+ }
+ return error;
+ }
+
+ void
+ ResetOptionValues ()
+ {
+ Options::ResetOptionValues();
+ m_thread_idx = LLDB_INVALID_THREAD_ID;
+ m_frame_idx = 0;
+ m_stop_others = false;
+ }
+
+ const lldb::OptionDefinition*
+ GetDefinitions ()
+ {
+ return g_option_table;
+ }
+
+ uint32_t m_step_thread_idx;
+ bool m_stop_others;
+
+ // Options table: Required for subclasses of Options.
+
+ static lldb::OptionDefinition g_option_table[];
+
+ // Instance variables to hold the values for command options.
+ };
+
+ CommandObjectThreadUntil () :
+ CommandObject ("thread until",
+ "Runs the current or specified thread until it reaches a given line number or leaves the current function.",
+ "thread until [<cmd-options>] <line-number>",
+ eFlagProcessMustBeLaunched | eFlagProcessMustBePaused),
+ m_options ()
+ {
+ }
+
+
+ virtual
+ ~CommandObjectThreadUntil ()
+ {
+ }
+
+ virtual
+ Options *
+ GetOptions ()
+ {
+ return &m_options;
+ }
+
+ virtual bool
+ Execute (Args& command,
+ CommandContext *context,
+ CommandInterpreter *interpreter,
+ CommandReturnObject &result)
+ {
+ bool synchronous_execution = interpreter->GetSynchronous ();
+
+ if (!context->GetTarget())
+ {
+ result.AppendError ("invalid target, set executable file using 'file' command");
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
+
+ Process *process = context->GetExecutionContext().process;
+ if (process == NULL)
+ {
+ result.AppendError ("need a valid process to step");
+ result.SetStatus (eReturnStatusFailed);
+
+ }
+ else
+ {
+ Thread *thread = NULL;
+ uint32_t line_number;
+
+ if (command.GetArgumentCount() != 1)
+ {
+ result.AppendErrorWithFormat ("No line number provided:\n%s", GetSyntax());
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
+
+ line_number = Args::StringToUInt32 (command.GetArgumentAtIndex(0), UINT32_MAX);
+ if (line_number == UINT32_MAX)
+ {
+ result.AppendErrorWithFormat ("Invalid line number: '%s'.\n", command.GetArgumentAtIndex(0));
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
+
+ if (m_options.m_thread_idx == LLDB_INVALID_THREAD_ID)
+ {
+ thread = process->GetThreadList().GetCurrentThread().get();
+ }
+ else
+ {
+ thread = process->GetThreadList().GetThreadAtIndex(m_options.m_thread_idx).get();
+ }
+
+ if (thread == NULL)
+ {
+ const uint32_t num_threads = process->GetThreadList().GetSize();
+ result.AppendErrorWithFormat ("Thread index %u is out of range (valid values are 0 - %u).\n", m_options.m_thread_idx, 0, num_threads);
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
+
+ const bool abort_other_plans = true;
+
+ StackFrame *frame = thread->GetStackFrameAtIndex(m_options.m_frame_idx).get();
+ if (frame == NULL)
+ {
+
+ result.AppendErrorWithFormat ("Frame index %u is out of range for thread %u.\n", m_options.m_frame_idx, m_options.m_thread_idx);
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
+
+ ThreadPlan *new_plan;
+
+ if (frame->HasDebugInformation ())
+ {
+ // Finally we got here... Translate the given line number to a bunch of addresses:
+ SymbolContext sc(frame->GetSymbolContext (eSymbolContextCompUnit));
+ LineTable *line_table = NULL;
+ if (sc.comp_unit)
+ line_table = sc.comp_unit->GetLineTable();
+
+ if (line_table == NULL)
+ {
+ result.AppendErrorWithFormat ("Failed to resolve the line table for frame %u of thread index %u.\n",
+ m_options.m_frame_idx, m_options.m_thread_idx);
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
+
+ LineEntry function_start;
+ uint32_t index_ptr = 0, end_ptr;
+ std::vector<addr_t> address_list;
+
+ // Find the beginning & end index of the
+ AddressRange fun_addr_range = sc.function->GetAddressRange();
+ Address fun_start_addr = fun_addr_range.GetBaseAddress();
+ line_table->FindLineEntryByAddress (fun_start_addr, function_start, &index_ptr);
+
+ Address fun_end_addr(fun_start_addr.GetSection(), fun_start_addr.GetOffset() + fun_addr_range.GetByteSize());
+ line_table->FindLineEntryByAddress (fun_end_addr, function_start, &end_ptr);
+
+ while (index_ptr <= end_ptr)
+ {
+ LineEntry line_entry;
+ index_ptr = sc.comp_unit->FindLineEntry(index_ptr, line_number, sc.comp_unit, &line_entry);
+ if (index_ptr == UINT32_MAX)
+ break;
+
+ addr_t address = line_entry.range.GetBaseAddress().GetLoadAddress(process);
+ if (address != LLDB_INVALID_ADDRESS)
+ address_list.push_back (address);
+ index_ptr++;
+ }
+
+ new_plan = thread->QueueThreadPlanForStepUntil (abort_other_plans, address_list.data(), address_list.size(), m_options.m_stop_others);
+ new_plan->SetOkayToDiscard(false);
+ }
+ else
+ {
+ result.AppendErrorWithFormat ("Frame index %u of thread %u has no debug information.\n", m_options.m_frame_idx, m_options.m_thread_idx);
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+
+ }
+
+ process->GetThreadList().SetCurrentThreadByID (m_options.m_thread_idx);
+ Error error (process->Resume ());
+ if (error.Success())
+ {
+ result.AppendMessageWithFormat ("Resuming process %i\n", process->GetID());
+ if (synchronous_execution)
+ {
+ StateType state = process->WaitForProcessToStop (NULL);
+
+ result.SetDidChangeProcessState (true);
+ result.AppendMessageWithFormat ("Process %i %s\n", process->GetID(), StateAsCString (state));
+ result.SetStatus (eReturnStatusSuccessFinishNoResult);
+ }
+ else
+ {
+ result.SetStatus (eReturnStatusSuccessContinuingNoResult);
+ }
+ }
+ else
+ {
+ result.AppendErrorWithFormat("Failed to resume process: %s.\n", error.AsCString());
+ result.SetStatus (eReturnStatusFailed);
+ }
+
+ }
+ return result.Succeeded();
+ }
+protected:
+ CommandOptions m_options;
+
+};
+
+lldb::OptionDefinition
+CommandObjectThreadUntil::CommandOptions::g_option_table[] =
+{
+{ 0, true, "frame", 'f', required_argument, NULL, 0, "<frame>", "Frame index for until operation - defaults to 0"},
+{ 0, true, "thread", 't', required_argument, NULL, 0, "<thread>", "Thread index for the thread for until operation"},
+{ 0, true, "run_mode", 'm', required_argument, g_duo_running_mode, 0, "<run_mode>", "Determine how to run other threads while stepping this one"},
+{ 0, false, NULL, 0, 0, NULL, 0, NULL, NULL }
+};
+
+
+//-------------------------------------------------------------------------
+// CommandObjectThreadSelect
+//-------------------------------------------------------------------------
+
+class CommandObjectThreadSelect : public CommandObject
+{
+public:
+
+ CommandObjectThreadSelect () :
+ CommandObject ("thread select",
+ "Selects a threads as the currently active thread.",
+ "thread select <thread-index>",
+ eFlagProcessMustBeLaunched | eFlagProcessMustBePaused)
+ {
+ }
+
+
+ virtual
+ ~CommandObjectThreadSelect ()
+ {
+ }
+
+ virtual bool
+ Execute (Args& command,
+ CommandContext *context,
+ CommandInterpreter *interpreter,
+ CommandReturnObject &result)
+ {
+ Process *process = context->GetExecutionContext().process;
+ if (process == NULL)
+ {
+ result.AppendError ("no process");
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
+ else if (command.GetArgumentCount() != 1)
+ {
+ result.AppendErrorWithFormat("'%s' takes exactly one thread index argument:\nUsage: \n", m_cmd_name.c_str(), m_cmd_syntax.c_str());
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
+
+ uint32_t index_id = Args::StringToUInt32(command.GetArgumentAtIndex(0), 0, 0);
+
+ Thread *new_thread = process->GetThreadList().FindThreadByIndexID(index_id).get();
+ if (new_thread == NULL)
+ {
+ result.AppendErrorWithFormat ("Invalid thread #%s.\n", command.GetArgumentAtIndex(0));
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
+
+ process->GetThreadList().SetCurrentThreadByID(new_thread->GetID());
+
+ DisplayThreadInfo (interpreter,
+ result.GetOutputStream(),
+ new_thread,
+ false,
+ true);
+
+ return result.Succeeded();
+ }
+
+};
+
+
+//-------------------------------------------------------------------------
+// CommandObjectThreadList
+//-------------------------------------------------------------------------
+
+CommandObjectThreadList::CommandObjectThreadList ():
+ CommandObject ("thread list",
+ "Shows a summary of all current threads in a process.",
+ "thread list",
+ eFlagProcessMustBeLaunched | eFlagProcessMustBePaused)
+{
+}
+
+CommandObjectThreadList::~CommandObjectThreadList()
+{
+}
+
+bool
+CommandObjectThreadList::Execute
+(
+ Args& command,
+ CommandContext *context,
+ CommandInterpreter *interpreter,
+ CommandReturnObject &result
+)
+{
+ StreamString &strm = result.GetOutputStream();
+ result.SetStatus (eReturnStatusSuccessFinishNoResult);
+ ExecutionContext exe_ctx(context->GetExecutionContext());
+ if (exe_ctx.process)
+ {
+ const StateType state = exe_ctx.process->GetState();
+
+ if (StateIsStoppedState(state))
+ {
+ if (state == eStateExited)
+ {
+ int exit_status = exe_ctx.process->GetExitStatus();
+ const char *exit_description = exe_ctx.process->GetExitDescription();
+ strm.Printf ("Process %d exited with status = %i (0x%8.8x) %s\n",
+ exe_ctx.process->GetID(),
+ exit_status,
+ exit_status,
+ exit_description ? exit_description : "");
+ }
+ else
+ {
+ strm.Printf ("Process %d state is %s\n", exe_ctx.process->GetID(), StateAsCString (state));
+ if (exe_ctx.thread == NULL)
+ exe_ctx.thread = exe_ctx.process->GetThreadList().GetThreadAtIndex(0).get();
+ if (exe_ctx.thread != NULL)
+ {
+ DisplayThreadsInfo (interpreter, &exe_ctx, result, false, false);
+ }
+ else
+ {
+ result.AppendError ("no valid thread found in current process");
+ result.SetStatus (eReturnStatusFailed);
+ }
+ }
+ }
+ else
+ {
+ result.AppendError ("process is currently running");
+ result.SetStatus (eReturnStatusFailed);
+ }
+ }
+ else
+ {
+ result.AppendError ("no current location or status available");
+ result.SetStatus (eReturnStatusFailed);
+ }
+ return result.Succeeded();
+}
+
+//-------------------------------------------------------------------------
+// CommandObjectMultiwordThread
+//-------------------------------------------------------------------------
+
+CommandObjectMultiwordThread::CommandObjectMultiwordThread (CommandInterpreter *interpreter) :
+ CommandObjectMultiword ("thread",
+ "A set of commands for operating on one or more thread within a running process.",
+ "thread <subcommand> [<subcommand-options>]")
+{
+ LoadSubCommand (CommandObjectSP (new CommandObjectThreadBacktrace ()), "backtrace", interpreter);
+ LoadSubCommand (CommandObjectSP (new CommandObjectThreadContinue ()), "continue", interpreter);
+ LoadSubCommand (CommandObjectSP (new CommandObjectThreadList ()), "list", interpreter);
+ LoadSubCommand (CommandObjectSP (new CommandObjectThreadSelect ()), "select", interpreter);
+ LoadSubCommand (CommandObjectSP (new CommandObjectThreadUntil ()), "until", interpreter);
+ LoadSubCommand (CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ("thread step-in",
+ "Source level single step in in specified thread (current thread, if none specified).",
+ "thread step-in [<thread-id>]",
+ eFlagProcessMustBeLaunched | eFlagProcessMustBePaused,
+ eStepTypeInto,
+ eStepScopeSource)),
+ "step-in", interpreter);
+
+ LoadSubCommand (CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ("thread step-out",
+ "Source level single step out in specified thread (current thread, if none specified).",
+ "thread step-out [<thread-id>]",
+ eFlagProcessMustBeLaunched | eFlagProcessMustBePaused,
+ eStepTypeOut,
+ eStepScopeSource)),
+ "step-out", interpreter);
+
+ LoadSubCommand (CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ("thread step-over",
+ "Source level single step over in specified thread (current thread, if none specified).",
+ "thread step-over [<thread-id>]",
+ eFlagProcessMustBeLaunched | eFlagProcessMustBePaused,
+ eStepTypeOver,
+ eStepScopeSource)),
+ "step-over", interpreter);
+
+ LoadSubCommand (CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ("thread step-inst",
+ "Single step one instruction in specified thread (current thread, if none specified).",
+ "thread step-inst [<thread-id>]",
+ eFlagProcessMustBeLaunched | eFlagProcessMustBePaused,
+ eStepTypeTrace,
+ eStepScopeInstruction)),
+ "step-inst", interpreter);
+ LoadSubCommand (CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ("thread step-inst-over",
+ "Single step one instruction in specified thread (current thread, if none specified), stepping over calls.",
+ "thread step-inst-over [<thread-id>]",
+ eFlagProcessMustBeLaunched | eFlagProcessMustBePaused,
+ eStepTypeTraceOver,
+ eStepScopeInstruction)),
+ "step-inst-over", interpreter);
+}
+
+CommandObjectMultiwordThread::~CommandObjectMultiwordThread ()
+{
+}
+
+
OpenPOWER on IntegriCloud