diff options
Diffstat (limited to 'lldb/source/Commands/CommandObjectThread.cpp')
-rw-r--r-- | lldb/source/Commands/CommandObjectThread.cpp | 1277 |
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 () +{ +} + + |