//===-- 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/Interpreter/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/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.GetDebugger().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 [] ...", eFlagProcessMustBeLaunched | eFlagProcessMustBePaused), m_ascending (true) { } ~CommandObjectThreadBacktrace() { } virtual bool Execute ( CommandInterpreter &interpreter, Args& command, CommandReturnObject &result ) { if (command.GetArgumentCount() == 0) { ExecutionContext exe_ctx(interpreter.GetDebugger().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; }; 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 ( CommandInterpreter &interpreter, Args& command, CommandReturnObject &result ) { Process *process = interpreter.GetDebugger().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, m_options.m_avoid_no_debug); } 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, false); 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[] = { { LLDB_OPT_SET_1, false, "avoid_no_debug", 'a', required_argument, NULL, 0, "", "Should step-in step over functions with no debug information"}, { LLDB_OPT_SET_1, false, "run_mode", 'm', required_argument, g_tri_running_mode, 0, "", "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 [ ...]", eFlagProcessMustBeLaunched | eFlagProcessMustBePaused) { } virtual ~CommandObjectThreadContinue () { } virtual bool Execute ( CommandInterpreter &interpreter, Args& command, CommandReturnObject &result ) { bool synchronous_execution = interpreter.GetSynchronous (); if (!interpreter.GetDebugger().GetCurrentTarget().get()) { result.AppendError ("invalid target, set executable file using 'file' command"); result.SetStatus (eReturnStatusFailed); return false; } Process *process = interpreter.GetDebugger().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 resume_thread_indexes; for (uint32_t i=0; iGetThreadList().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; idxGetThreadList().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 [] ", eFlagProcessMustBeLaunched | eFlagProcessMustBePaused), m_options () { } virtual ~CommandObjectThreadUntil () { } virtual Options * GetOptions () { return &m_options; } virtual bool Execute ( CommandInterpreter &interpreter, Args& command, CommandReturnObject &result ) { bool synchronous_execution = interpreter.GetSynchronous (); if (!interpreter.GetDebugger().GetCurrentTarget().get()) { result.AppendError ("invalid target, set executable file using 'file' command"); result.SetStatus (eReturnStatusFailed); return false; } Process *process = interpreter.GetDebugger().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 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[] = { { LLDB_OPT_SET_1, false, "frame", 'f', required_argument, NULL, 0, "", "Frame index for until operation - defaults to 0"}, { LLDB_OPT_SET_1, false, "thread", 't', required_argument, NULL, 0, "", "Thread index for the thread for until operation"}, { LLDB_OPT_SET_1, false, "run_mode",'m', required_argument, g_duo_running_mode, 0, "","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 ", eFlagProcessMustBeLaunched | eFlagProcessMustBePaused) { } virtual ~CommandObjectThreadSelect () { } virtual bool Execute ( CommandInterpreter &interpreter, Args& command, CommandReturnObject &result ) { Process *process = interpreter.GetDebugger().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 //------------------------------------------------------------------------- class CommandObjectThreadList : public CommandObject { public: CommandObjectThreadList (): CommandObject ("thread list", "Shows a summary of all current threads in a process.", "thread list", eFlagProcessMustBeLaunched | eFlagProcessMustBePaused) { } ~CommandObjectThreadList() { } bool Execute ( CommandInterpreter &interpreter, Args& command, CommandReturnObject &result ) { StreamString &strm = result.GetOutputStream(); result.SetStatus (eReturnStatusSuccessFinishNoResult); ExecutionContext exe_ctx(interpreter.GetDebugger().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 []") { LoadSubCommand (interpreter, "backtrace", CommandObjectSP (new CommandObjectThreadBacktrace ())); LoadSubCommand (interpreter, "continue", CommandObjectSP (new CommandObjectThreadContinue ())); LoadSubCommand (interpreter, "list", CommandObjectSP (new CommandObjectThreadList ())); LoadSubCommand (interpreter, "select", CommandObjectSP (new CommandObjectThreadSelect ())); LoadSubCommand (interpreter, "until", CommandObjectSP (new CommandObjectThreadUntil ())); LoadSubCommand (interpreter, "step-in", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ( "thread step-in", "Source level single step in in specified thread (current thread, if none specified).", "thread step-in []", eFlagProcessMustBeLaunched | eFlagProcessMustBePaused, eStepTypeInto, eStepScopeSource))); LoadSubCommand (interpreter, "step-out", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ("thread step-out", "Source level single step out in specified thread (current thread, if none specified).", "thread step-out []", eFlagProcessMustBeLaunched | eFlagProcessMustBePaused, eStepTypeOut, eStepScopeSource))); LoadSubCommand (interpreter, "step-over", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ("thread step-over", "Source level single step over in specified thread (current thread, if none specified).", "thread step-over []", eFlagProcessMustBeLaunched | eFlagProcessMustBePaused, eStepTypeOver, eStepScopeSource))); LoadSubCommand (interpreter, "step-inst", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ("thread step-inst", "Single step one instruction in specified thread (current thread, if none specified).", "thread step-inst []", eFlagProcessMustBeLaunched | eFlagProcessMustBePaused, eStepTypeTrace, eStepScopeInstruction))); LoadSubCommand (interpreter, "step-inst-over", 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 []", eFlagProcessMustBeLaunched | eFlagProcessMustBePaused, eStepTypeTraceOver, eStepScopeInstruction))); } CommandObjectMultiwordThread::~CommandObjectMultiwordThread () { }