xref: /llvm-project/lldb/source/Commands/CommandObjectThread.cpp (revision 09b263e05cfa6ee42e7213c48114cc1458bccd09)
1 //===-- CommandObjectThread.cpp ---------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "CommandObjectThread.h"
11 
12 // C Includes
13 // C++ Includes
14 // Other libraries and framework includes
15 // Project includes
16 #include "lldb/Interpreter/Options.h"
17 #include "lldb/Core/State.h"
18 #include "lldb/Core/SourceManager.h"
19 
20 #include "lldb/Interpreter/CommandInterpreter.h"
21 #include "lldb/Interpreter/CommandReturnObject.h"
22 
23 #include "lldb/Target/Process.h"
24 #include "lldb/Target/RegisterContext.h"
25 #include "lldb/Target/Target.h"
26 #include "lldb/Target/Thread.h"
27 #include "lldb/Target/ThreadPlan.h"
28 #include "lldb/Target/ThreadPlanStepInstruction.h"
29 #include "lldb/Target/ThreadPlanStepOut.h"
30 #include "lldb/Target/ThreadPlanStepRange.h"
31 #include "lldb/Target/ThreadPlanStepInRange.h"
32 #include "lldb/Symbol/LineTable.h"
33 #include "lldb/Symbol/LineEntry.h"
34 
35 using namespace lldb;
36 using namespace lldb_private;
37 
38 
39 bool
40 lldb_private::DisplayThreadInfo
41 (
42     CommandInterpreter &interpreter,
43     Stream &strm,
44     Thread *thread,
45     bool only_threads_with_stop_reason,
46     bool show_source
47 )
48 {
49     if (thread)
50     {
51         if (only_threads_with_stop_reason)
52         {
53             if (thread->GetStopInfo() == NULL)
54                 return false;
55         }
56 
57         strm.Indent();
58         strm.Printf("%c ", thread->GetProcess().GetThreadList().GetSelectedThread().get() == thread ? '*' : ' ');
59 
60         // Show one frame with only the first showing source
61         if (show_source)
62         {
63             DisplayFramesForExecutionContext (thread,
64                                               interpreter,
65                                               strm,
66                                               0,    // Start at first frame
67                                               1,    // Number of frames to show
68                                               false,// Don't show the frame info since we already displayed most of it above...
69                                               1,    // Show source for the first frame
70                                               3,    // lines of source context before
71                                               3);   // lines of source context after
72         }
73         else
74         {
75             thread->DumpInfo (strm,
76                               true, // Dump the stop reason?
77                               true, // Dump the thread name?
78                               true, // Dump the queue name?
79                               0);   // Display context info for stack frame zero
80 
81             strm.EOL();
82         }
83 
84         return true;
85     }
86     return false;
87 }
88 
89 size_t
90 lldb_private::DisplayThreadsInfo
91 (
92     CommandInterpreter &interpreter,
93     ExecutionContext *exe_ctx,
94     CommandReturnObject &result,
95     bool only_threads_with_stop_reason,
96     bool show_source
97 )
98 {
99     StreamString strm;
100 
101     size_t num_thread_infos_dumped = 0;
102 
103     if (!exe_ctx->process)
104         return 0;
105 
106     const size_t num_threads = exe_ctx->process->GetThreadList().GetSize();
107     if (num_threads > 0)
108     {
109 
110         for (uint32_t i = 0; i < num_threads; i++)
111         {
112             Thread *thread = exe_ctx->process->GetThreadList().GetThreadAtIndex(i).get();
113             if (thread)
114             {
115                 if (DisplayThreadInfo (interpreter,
116                                        strm,
117                                        thread,
118                                        only_threads_with_stop_reason,
119                                        show_source))
120                     ++num_thread_infos_dumped;
121             }
122         }
123     }
124 
125     if (num_thread_infos_dumped > 0)
126     {
127         if (num_thread_infos_dumped < num_threads)
128             result.GetOutputStream().Printf("%u of %u threads stopped with reasons:\n", num_thread_infos_dumped, num_threads);
129 
130         result.GetOutputStream().GetString().append(strm.GetString());
131         result.SetStatus (eReturnStatusSuccessFinishNoResult);
132     }
133     return num_thread_infos_dumped;
134 }
135 
136 
137 size_t
138 lldb_private::DisplayFramesForExecutionContext
139 (
140     Thread *thread,
141     CommandInterpreter &interpreter,
142     Stream& strm,
143     uint32_t first_frame,
144     uint32_t num_frames,
145     bool show_frame_info,
146     uint32_t num_frames_with_source,
147     uint32_t source_lines_before,
148     uint32_t source_lines_after
149 )
150 {
151     if (thread == NULL)
152         return 0;
153 
154     size_t num_frames_displayed = 0;
155 
156     if (num_frames == 0)
157         return 0;
158 
159     thread->DumpInfo (strm,
160                       true,     // Dump the stop reason?
161                       true,     // Dump the thread name?
162                       true,     // Dump the queue name?
163                       num_frames > 1 ? UINT32_MAX : first_frame);  // Dump info for the first stack frame if we are showing only on frame
164     strm.EOL();
165     strm.IndentMore();
166 
167     StackFrameSP frame_sp;
168     uint32_t frame_idx = 0;
169     uint32_t last_frame;
170 
171     // Don't let the last frame wrap around...
172     if (num_frames == UINT32_MAX)
173         last_frame = UINT32_MAX;
174     else
175         last_frame = first_frame + num_frames;
176 
177     for (frame_idx = first_frame; frame_idx < last_frame; ++frame_idx)
178     {
179         frame_sp = thread->GetStackFrameAtIndex (frame_idx);
180         if (frame_sp.get() == NULL)
181             break;
182 
183         if (DisplayFrameForExecutionContext (thread,
184                                              frame_sp.get(),
185                                              interpreter,
186                                              strm,
187                                              show_frame_info,
188                                              num_frames_with_source > first_frame - frame_idx,
189                                              source_lines_before,
190                                              source_lines_after) == false)
191             break;
192 
193         ++num_frames_displayed;
194     }
195 
196     strm.IndentLess();
197     return num_frames_displayed;
198 }
199 
200 bool
201 lldb_private::DisplayFrameForExecutionContext
202 (
203     Thread *thread,
204     StackFrame *frame,
205     CommandInterpreter &interpreter,
206     Stream& strm,
207     bool show_frame_info,
208     bool show_source,
209     uint32_t source_lines_before,
210     uint32_t source_lines_after
211 )
212 {
213     // thread and frame must be filled in prior to calling this function
214     if (thread && frame)
215     {
216         if (show_frame_info)
217         {
218             strm.Indent();
219             frame->Dump (&strm, true);
220             strm.EOL();
221         }
222 
223         SymbolContext sc (frame->GetSymbolContext(eSymbolContextCompUnit | eSymbolContextLineEntry));
224 
225         if (show_source && sc.comp_unit && sc.line_entry.IsValid())
226         {
227             interpreter.GetDebugger().GetSourceManager().DisplaySourceLinesWithLineNumbers (
228                     sc.line_entry.file,
229                     sc.line_entry.line,
230                     3,
231                     3,
232                     "->",
233                     &strm);
234 
235         }
236         return true;
237     }
238     return false;
239 }
240 
241 
242 //-------------------------------------------------------------------------
243 // CommandObjectThreadBacktrace
244 //-------------------------------------------------------------------------
245 
246 class CommandObjectThreadBacktrace : public CommandObject
247 {
248 public:
249 
250     class CommandOptions : public Options
251     {
252     public:
253 
254         CommandOptions () :
255             Options()
256         {
257             // Keep default values of all options in one place: ResetOptionValues ()
258             ResetOptionValues ();
259         }
260 
261         virtual
262         ~CommandOptions ()
263         {
264         }
265 
266         virtual Error
267         SetOptionValue (int option_idx, const char *option_arg)
268         {
269             Error error;
270             char short_option = (char) m_getopt_table[option_idx].val;
271 
272             switch (short_option)
273             {
274                 case 'c':
275                 {
276                     bool success;
277                     int32_t input_count =  Args::StringToSInt32 (option_arg, -1, 0, &success);
278                     if (!success)
279                         error.SetErrorStringWithFormat("Invalid integer value for option '%c'.\n", short_option);
280                     if (input_count < -1)
281                         m_count = UINT32_MAX;
282                     else
283                         m_count = input_count;
284                 }
285                 break;
286                 case 's':
287                 {
288                     bool success;
289                     m_start =  Args::StringToUInt32 (option_arg, 0, 0, &success);
290                     if (!success)
291                         error.SetErrorStringWithFormat("Invalid integer value for option '%c'.\n", short_option);
292                 }
293                 break;
294                 default:
295                     error.SetErrorStringWithFormat("Invalid short option character '%c'.\n", short_option);
296                     break;
297 
298             }
299             return error;
300         }
301 
302         void
303         ResetOptionValues ()
304         {
305             Options::ResetOptionValues();
306             m_count = -1;
307             m_start = 0;
308         }
309 
310         const lldb::OptionDefinition*
311         GetDefinitions ()
312         {
313             return g_option_table;
314         }
315 
316         // Options table: Required for subclasses of Options.
317 
318         static lldb::OptionDefinition g_option_table[];
319 
320         // Instance variables to hold the values for command options.
321         uint32_t m_count;
322         uint32_t m_start;
323     };
324 
325     CommandObjectThreadBacktrace () :
326         CommandObject ("thread backtrace",
327                        "Shows the stack for one or more threads.  If no threads are specified, shows the currently selected thread.  \nUse the thread-index \"all\" to see all threads.",
328                        "thread backtrace [<thread-index>] ...",
329                        eFlagProcessMustBeLaunched | eFlagProcessMustBePaused),
330         m_options()
331     {
332     }
333 
334     ~CommandObjectThreadBacktrace()
335     {
336     }
337 
338     virtual Options *
339     GetOptions ()
340     {
341         return &m_options;
342     }
343 
344     virtual bool
345     Execute
346     (
347         CommandInterpreter &interpreter,
348         Args& command,
349         CommandReturnObject &result
350     )
351     {
352 
353         bool show_frame_info = true;
354         uint32_t num_frames_with_source = 0; // Don't show any frames with source when backtracing
355 
356         result.SetStatus (eReturnStatusSuccessFinishResult);
357 
358         if (command.GetArgumentCount() == 0)
359         {
360             ExecutionContext exe_ctx(interpreter.GetDebugger().GetExecutionContext());
361             if (exe_ctx.thread)
362             {
363                 if (DisplayFramesForExecutionContext (exe_ctx.thread,
364                                                       interpreter,
365                                                       result.GetOutputStream(),
366                                                       m_options.m_start,
367                                                       m_options.m_count,
368                                                       show_frame_info,
369                                                       num_frames_with_source,
370                                                       3,
371                                                       3))
372                 {
373                     result.SetStatus (eReturnStatusSuccessFinishResult);
374                 }
375             }
376             else
377             {
378                 result.AppendError ("invalid thread");
379                 result.SetStatus (eReturnStatusFailed);
380             }
381         }
382         else if (command.GetArgumentCount() == 1 && ::strcmp (command.GetArgumentAtIndex(0), "all") == 0)
383         {
384             Process *process = interpreter.GetDebugger().GetExecutionContext().process;
385             uint32_t num_threads = process->GetThreadList().GetSize();
386             for (uint32_t i = 0; i < num_threads; i++)
387             {
388                 ThreadSP thread_sp = process->GetThreadList().GetThreadAtIndex(i);
389                 if (!DisplayFramesForExecutionContext (thread_sp.get(),
390                                                       interpreter,
391                                                       result.GetOutputStream(),
392                                                       m_options.m_start,
393                                                       m_options.m_count,
394                                                       show_frame_info,
395                                                       num_frames_with_source,
396                                                       3,
397                                                       3))
398                 {
399                     result.AppendErrorWithFormat ("error displaying backtrace for thread: \"%d\"\n", i);
400                     result.SetStatus (eReturnStatusFailed);
401                     return false;
402                 }
403                 if (i < num_threads - 1)
404                     result.AppendMessage("");
405             }
406         }
407         else
408         {
409             uint32_t num_args = command.GetArgumentCount();
410             Process *process = interpreter.GetDebugger().GetExecutionContext().process;
411             std::vector<ThreadSP> thread_sps;
412 
413             for (uint32_t i = 0; i < num_args; i++)
414             {
415                 bool success;
416 
417                 uint32_t thread_idx = Args::StringToUInt32(command.GetArgumentAtIndex(i), 0, 0, &success);
418                 if (!success)
419                 {
420                     result.AppendErrorWithFormat ("invalid thread specification: \"%s\"\n", command.GetArgumentAtIndex(i));
421                     result.SetStatus (eReturnStatusFailed);
422                     return false;
423                 }
424 
425                 thread_sps.push_back(process->GetThreadList().FindThreadByIndexID(thread_idx));
426 
427                 if (!thread_sps[i])
428                 {
429                     result.AppendErrorWithFormat ("no thread with index: \"%s\"\n", command.GetArgumentAtIndex(i));
430                     result.SetStatus (eReturnStatusFailed);
431                     return false;
432                 }
433 
434             }
435 
436             for (uint32_t i = 0; i < num_args; i++)
437             {
438                 if (!DisplayFramesForExecutionContext (thread_sps[i].get(),
439                                                       interpreter,
440                                                       result.GetOutputStream(),
441                                                       m_options.m_start,
442                                                       m_options.m_count,
443                                                       show_frame_info,
444                                                       num_frames_with_source,
445                                                       3,
446                                                       3))
447                 {
448                     result.AppendErrorWithFormat ("error displaying backtrace for thread: \"%s\"\n", command.GetArgumentAtIndex(i));
449                     result.SetStatus (eReturnStatusFailed);
450                     return false;
451                 }
452 
453                 if (i < num_args - 1)
454                     result.AppendMessage("");
455             }
456         }
457         return result.Succeeded();
458     }
459 protected:
460     CommandOptions m_options;
461 };
462 
463 lldb::OptionDefinition
464 CommandObjectThreadBacktrace::CommandOptions::g_option_table[] =
465 {
466 { LLDB_OPT_SET_1, false, "count", 'c', required_argument, NULL,               0, "<count>", "How many frames to display (-1 for all)"},
467 { LLDB_OPT_SET_1, false, "start",       's', required_argument, NULL, 0, "<start>",       "Where to start the backtrace"},
468 { 0, false, NULL, 0, 0, NULL, 0, NULL, NULL }
469 };
470 
471 enum StepScope
472 {
473     eStepScopeSource,
474     eStepScopeInstruction
475 };
476 
477 class CommandObjectThreadStepWithTypeAndScope : public CommandObject
478 {
479 public:
480 
481     class CommandOptions : public Options
482     {
483     public:
484 
485         CommandOptions () :
486             Options()
487         {
488             // Keep default values of all options in one place: ResetOptionValues ()
489             ResetOptionValues ();
490         }
491 
492         virtual
493         ~CommandOptions ()
494         {
495         }
496 
497         virtual Error
498         SetOptionValue (int option_idx, const char *option_arg)
499         {
500             Error error;
501             char short_option = (char) m_getopt_table[option_idx].val;
502 
503             switch (short_option)
504             {
505                 case 'a':
506                 {
507                     bool success;
508                     m_avoid_no_debug =  Args::StringToBoolean (option_arg, true, &success);
509                     if (!success)
510                         error.SetErrorStringWithFormat("Invalid boolean value for option '%c'.\n", short_option);
511                 }
512                 break;
513                 case 'm':
514                 {
515                     bool found_one = false;
516                     OptionEnumValueElement *enum_values = g_option_table[option_idx].enum_values;
517                     m_run_mode = (lldb::RunMode) Args::StringToOptionEnum(option_arg, enum_values, eOnlyDuringStepping, &found_one);
518                     if (!found_one)
519                         error.SetErrorStringWithFormat("Invalid enumeration value for option '%c'.\n", short_option);
520                 }
521                 break;
522                 case 'r':
523                 {
524                     m_avoid_regexp.clear();
525                     m_avoid_regexp.assign(option_arg);
526                 }
527                 break;
528                 default:
529                     error.SetErrorStringWithFormat("Invalid short option character '%c'.\n", short_option);
530                     break;
531 
532             }
533             return error;
534         }
535 
536         void
537         ResetOptionValues ()
538         {
539             Options::ResetOptionValues();
540             m_avoid_no_debug = true;
541             m_run_mode = eOnlyDuringStepping;
542             m_avoid_regexp.clear();
543         }
544 
545         const lldb::OptionDefinition*
546         GetDefinitions ()
547         {
548             return g_option_table;
549         }
550 
551         // Options table: Required for subclasses of Options.
552 
553         static lldb::OptionDefinition g_option_table[];
554 
555         // Instance variables to hold the values for command options.
556         bool m_avoid_no_debug;
557         RunMode m_run_mode;
558         std::string m_avoid_regexp;
559     };
560 
561     CommandObjectThreadStepWithTypeAndScope (const char *name,
562                          const char *help,
563                          const char *syntax,
564                          uint32_t flags,
565                          StepType step_type,
566                          StepScope step_scope) :
567         CommandObject (name, help, syntax, flags),
568         m_step_type (step_type),
569         m_step_scope (step_scope),
570         m_options ()
571     {
572     }
573 
574     virtual
575     ~CommandObjectThreadStepWithTypeAndScope ()
576     {
577     }
578 
579     virtual
580     Options *
581     GetOptions ()
582     {
583         return &m_options;
584     }
585 
586     virtual bool
587     Execute
588     (
589         CommandInterpreter &interpreter,
590         Args& command,
591         CommandReturnObject &result
592     )
593     {
594         Process *process = interpreter.GetDebugger().GetExecutionContext().process;
595         bool synchronous_execution = interpreter.GetSynchronous();
596 
597         if (process == NULL)
598         {
599             result.AppendError ("need a valid process to step");
600             result.SetStatus (eReturnStatusFailed);
601 
602         }
603         else
604         {
605             const uint32_t num_threads = process->GetThreadList().GetSize();
606             Thread *thread = NULL;
607 
608             if (command.GetArgumentCount() == 0)
609             {
610                 thread = process->GetThreadList().GetSelectedThread().get();
611                 if (thread == NULL)
612                 {
613                     result.AppendError ("no selected thread in process");
614                     result.SetStatus (eReturnStatusFailed);
615                     return false;
616                 }
617             }
618             else
619             {
620                 const char *thread_idx_cstr = command.GetArgumentAtIndex(0);
621                 uint32_t step_thread_idx = Args::StringToUInt32 (thread_idx_cstr, LLDB_INVALID_INDEX32);
622                 if (step_thread_idx == LLDB_INVALID_INDEX32)
623                 {
624                     result.AppendErrorWithFormat ("Invalid thread index '%s'.\n", thread_idx_cstr);
625                     result.SetStatus (eReturnStatusFailed);
626                     return false;
627                 }
628                 thread = process->GetThreadList().FindThreadByIndexID(step_thread_idx).get();
629                 if (thread == NULL)
630                 {
631                     result.AppendErrorWithFormat ("Thread index %u is out of range (valid values are 0 - %u).\n",
632                                                   step_thread_idx, 0, num_threads);
633                     result.SetStatus (eReturnStatusFailed);
634                     return false;
635                 }
636             }
637 
638             const bool abort_other_plans = false;
639             const lldb::RunMode stop_other_threads = m_options.m_run_mode;
640 
641             // This is a bit unfortunate, but not all the commands in this command object support
642             // only while stepping, so I use the bool for them.
643             bool bool_stop_other_threads;
644             if (m_options.m_run_mode == eAllThreads)
645                 bool_stop_other_threads = false;
646             else
647                 bool_stop_other_threads = true;
648 
649             if (m_step_type == eStepTypeInto)
650             {
651                 StackFrame *frame = thread->GetStackFrameAtIndex(0).get();
652                 ThreadPlan *new_plan;
653 
654                 if (frame->HasDebugInformation ())
655                 {
656                     new_plan = thread->QueueThreadPlanForStepRange (abort_other_plans, m_step_type,
657                                                                     frame->GetSymbolContext(eSymbolContextEverything).line_entry.range,
658                                                                     frame->GetSymbolContext(eSymbolContextEverything),
659                                                                     stop_other_threads,
660                                                                     m_options.m_avoid_no_debug);
661                     if (new_plan && !m_options.m_avoid_regexp.empty())
662                     {
663                         ThreadPlanStepInRange *step_in_range_plan = static_cast<ThreadPlanStepInRange *> (new_plan);
664                         step_in_range_plan->SetAvoidRegexp(m_options.m_avoid_regexp.c_str());
665                     }
666                 }
667                 else
668                     new_plan = thread->QueueThreadPlanForStepSingleInstruction (false, abort_other_plans, bool_stop_other_threads);
669 
670                 process->GetThreadList().SetSelectedThreadByID (thread->GetID());
671                 process->Resume ();
672             }
673             else if (m_step_type == eStepTypeOver)
674             {
675                 StackFrame *frame = thread->GetStackFrameAtIndex(0).get();
676                 ThreadPlan *new_plan;
677 
678                 if (frame->HasDebugInformation())
679                     new_plan = thread->QueueThreadPlanForStepRange (abort_other_plans,
680                                                                     m_step_type,
681                                                                     frame->GetSymbolContext(eSymbolContextEverything).line_entry.range,
682                                                                     frame->GetSymbolContext(eSymbolContextEverything),
683                                                                     stop_other_threads,
684                                                                     false);
685                 else
686                     new_plan = thread->QueueThreadPlanForStepSingleInstruction (true,
687                                                                                 abort_other_plans,
688                                                                                 bool_stop_other_threads);
689 
690                 // FIXME: This will keep the step plan on the thread stack when we hit a breakpoint while stepping over.
691                 // Maybe there should be a parameter to control this.
692                 new_plan->SetOkayToDiscard(false);
693 
694                 process->GetThreadList().SetSelectedThreadByID (thread->GetID());
695                 process->Resume ();
696             }
697             else if (m_step_type == eStepTypeTrace)
698             {
699                 thread->QueueThreadPlanForStepSingleInstruction (false, abort_other_plans, bool_stop_other_threads);
700                 process->GetThreadList().SetSelectedThreadByID (thread->GetID());
701                 process->Resume ();
702             }
703             else if (m_step_type == eStepTypeTraceOver)
704             {
705                 thread->QueueThreadPlanForStepSingleInstruction (true, abort_other_plans, bool_stop_other_threads);
706                 process->GetThreadList().SetSelectedThreadByID (thread->GetID());
707                 process->Resume ();
708             }
709             else if (m_step_type == eStepTypeOut)
710             {
711                 ThreadPlan *new_plan;
712 
713                 new_plan = thread->QueueThreadPlanForStepOut (abort_other_plans, NULL, false, bool_stop_other_threads, eVoteYes, eVoteNoOpinion);
714                 // FIXME: This will keep the step plan on the thread stack when we hit a breakpoint while stepping over.
715                 // Maybe there should be a parameter to control this.
716                 new_plan->SetOkayToDiscard(false);
717 
718                 process->GetThreadList().SetSelectedThreadByID (thread->GetID());
719                 process->Resume ();
720             }
721             else
722             {
723                 result.AppendError ("step type is not supported");
724                 result.SetStatus (eReturnStatusFailed);
725             }
726             if (synchronous_execution)
727             {
728                 StateType state = process->WaitForProcessToStop (NULL);
729 
730                 //EventSP event_sp;
731                 //StateType state = process->WaitForStateChangedEvents (NULL, event_sp);
732                 //while (! StateIsStoppedState (state))
733                 //  {
734                 //    state = process->WaitForStateChangedEvents (NULL, event_sp);
735                 //  }
736                 process->GetThreadList().SetSelectedThreadByID (thread->GetID());
737                 result.SetDidChangeProcessState (true);
738                 result.AppendMessageWithFormat ("Process %i %s\n", process->GetID(), StateAsCString (state));
739                 result.SetStatus (eReturnStatusSuccessFinishNoResult);
740             }
741         }
742         return result.Succeeded();
743     }
744 
745 protected:
746     StepType m_step_type;
747     StepScope m_step_scope;
748     CommandOptions m_options;
749 };
750 
751 static lldb::OptionEnumValueElement
752 g_tri_running_mode[] =
753 {
754 { eOnlyThisThread,     "thisThread",    "Run only this thread"},
755 { eAllThreads,         "allThreads",    "Run all threads"},
756 { eOnlyDuringStepping, "whileStepping", "Run only this thread while stepping"},
757 { 0, NULL, NULL }
758 };
759 
760 static lldb::OptionEnumValueElement
761 g_duo_running_mode[] =
762 {
763 { eOnlyThisThread,     "thisThread",    "Run only this thread"},
764 { eAllThreads,         "allThreads",    "Run all threads"},
765 { 0, NULL, NULL }
766 };
767 
768 lldb::OptionDefinition
769 CommandObjectThreadStepWithTypeAndScope::CommandOptions::g_option_table[] =
770 {
771 { LLDB_OPT_SET_1, false, "avoid_no_debug", 'a', required_argument, NULL,               0, "<avoid_no_debug>", "Should step-in step over functions with no debug information"},
772 { LLDB_OPT_SET_1, false, "run_mode",       'm', required_argument, g_tri_running_mode, 0, "<run_mode>",       "Determine how to run other threads while stepping this one"},
773 { LLDB_OPT_SET_1, false, "regexp_to_avoid",'r', required_argument, NULL, 0, "<avoid_regexp>",       "Should step-in step over functions matching this regexp"},
774 { 0, false, NULL, 0, 0, NULL, 0, NULL, NULL }
775 };
776 
777 
778 //-------------------------------------------------------------------------
779 // CommandObjectThreadContinue
780 //-------------------------------------------------------------------------
781 
782 class CommandObjectThreadContinue : public CommandObject
783 {
784 public:
785 
786     CommandObjectThreadContinue () :
787         CommandObject ("thread continue",
788                        "Continues execution of one or more threads in an active process.",
789                        "thread continue <thread-index> [<thread-index> ...]",
790                        eFlagProcessMustBeLaunched | eFlagProcessMustBePaused)
791     {
792     }
793 
794 
795     virtual
796     ~CommandObjectThreadContinue ()
797     {
798     }
799 
800     virtual bool
801     Execute
802     (
803         CommandInterpreter &interpreter,
804         Args& command,
805         CommandReturnObject &result
806     )
807     {
808         bool synchronous_execution = interpreter.GetSynchronous ();
809 
810         if (!interpreter.GetDebugger().GetSelectedTarget().get())
811         {
812             result.AppendError ("invalid target, set executable file using 'file' command");
813             result.SetStatus (eReturnStatusFailed);
814             return false;
815         }
816 
817         Process *process = interpreter.GetDebugger().GetExecutionContext().process;
818         if (process == NULL)
819         {
820             result.AppendError ("no process exists. Cannot continue");
821             result.SetStatus (eReturnStatusFailed);
822             return false;
823         }
824 
825         StateType state = process->GetState();
826         if ((state == eStateCrashed) || (state == eStateStopped) || (state == eStateSuspended))
827         {
828             const uint32_t num_threads = process->GetThreadList().GetSize();
829             uint32_t idx;
830             const size_t argc = command.GetArgumentCount();
831             if (argc > 0)
832             {
833                 std::vector<uint32_t> resume_thread_indexes;
834                 for (uint32_t i=0; i<argc; ++i)
835                 {
836                     idx = Args::StringToUInt32 (command.GetArgumentAtIndex(0), LLDB_INVALID_INDEX32);
837                     if (idx < num_threads)
838                         resume_thread_indexes.push_back(idx);
839                     else
840                         result.AppendWarningWithFormat("Thread index %u out of range.\n", idx);
841                 }
842 
843                 if (resume_thread_indexes.empty())
844                 {
845                     result.AppendError ("no valid thread indexes were specified");
846                     result.SetStatus (eReturnStatusFailed);
847                     return false;
848                 }
849                 else
850                 {
851                     result.AppendMessage ("Resuming thread ");
852                     for (idx=0; idx<num_threads; ++idx)
853                     {
854                         Thread *thread = process->GetThreadList().GetThreadAtIndex(idx).get();
855                         if (find(resume_thread_indexes.begin(), resume_thread_indexes.end(), idx) != resume_thread_indexes.end())
856                         {
857                             result.AppendMessageWithFormat ("%u ", idx);
858                             thread->SetResumeState (eStateRunning);
859                         }
860                         else
861                         {
862                             thread->SetResumeState (eStateSuspended);
863                         }
864                     }
865                     result.AppendMessageWithFormat ("in process %i\n", process->GetID());
866                 }
867             }
868             else
869             {
870                 Thread *current_thread = process->GetThreadList().GetSelectedThread().get();
871                 if (current_thread == NULL)
872                 {
873                     result.AppendError ("the process doesn't have a current thread");
874                     result.SetStatus (eReturnStatusFailed);
875                     return false;
876                 }
877                 // Set the actions that the threads should each take when resuming
878                 for (idx=0; idx<num_threads; ++idx)
879                 {
880                     Thread *thread = process->GetThreadList().GetThreadAtIndex(idx).get();
881                     if (thread == current_thread)
882                     {
883                         result.AppendMessageWithFormat ("Resuming thread 0x%4.4x in process %i\n", thread->GetID(), process->GetID());
884                         thread->SetResumeState (eStateRunning);
885                     }
886                     else
887                     {
888                         thread->SetResumeState (eStateSuspended);
889                     }
890                 }
891             }
892 
893             Error error (process->Resume());
894             if (error.Success())
895             {
896                 result.AppendMessageWithFormat ("Resuming process %i\n", process->GetID());
897                 if (synchronous_execution)
898                 {
899                     state = process->WaitForProcessToStop (NULL);
900 
901                     result.SetDidChangeProcessState (true);
902                     result.AppendMessageWithFormat ("Process %i %s\n", process->GetID(), StateAsCString (state));
903                     result.SetStatus (eReturnStatusSuccessFinishNoResult);
904                 }
905                 else
906                 {
907                     result.SetStatus (eReturnStatusSuccessContinuingNoResult);
908                 }
909             }
910             else
911             {
912                 result.AppendErrorWithFormat("Failed to resume process: %s\n", error.AsCString());
913                 result.SetStatus (eReturnStatusFailed);
914             }
915         }
916         else
917         {
918             result.AppendErrorWithFormat ("Process cannot be continued from its current state (%s).\n",
919                                           StateAsCString(state));
920             result.SetStatus (eReturnStatusFailed);
921         }
922 
923         return result.Succeeded();
924     }
925 
926 };
927 
928 //-------------------------------------------------------------------------
929 // CommandObjectThreadUntil
930 //-------------------------------------------------------------------------
931 
932 class CommandObjectThreadUntil : public CommandObject
933 {
934 public:
935 
936     class CommandOptions : public Options
937     {
938     public:
939         uint32_t m_thread_idx;
940         uint32_t m_frame_idx;
941 
942         CommandOptions () :
943             Options(),
944             m_thread_idx(LLDB_INVALID_THREAD_ID),
945             m_frame_idx(LLDB_INVALID_FRAME_ID)
946         {
947             // Keep default values of all options in one place: ResetOptionValues ()
948             ResetOptionValues ();
949         }
950 
951         virtual
952         ~CommandOptions ()
953         {
954         }
955 
956         virtual Error
957         SetOptionValue (int option_idx, const char *option_arg)
958         {
959             Error error;
960             char short_option = (char) m_getopt_table[option_idx].val;
961 
962             switch (short_option)
963             {
964                 case 't':
965                 {
966                     m_thread_idx = Args::StringToUInt32 (option_arg, LLDB_INVALID_INDEX32);
967                     if (m_thread_idx == LLDB_INVALID_INDEX32)
968                     {
969                         error.SetErrorStringWithFormat ("Invalid thread index '%s'.\n", option_arg);
970                     }
971                 }
972                 break;
973                 case 'f':
974                 {
975                     m_frame_idx = Args::StringToUInt32 (option_arg, LLDB_INVALID_FRAME_ID);
976                     if (m_frame_idx == LLDB_INVALID_FRAME_ID)
977                     {
978                         error.SetErrorStringWithFormat ("Invalid frame index '%s'.\n", option_arg);
979                     }
980                 }
981                 break;
982                 case 'm':
983                 {
984                     bool found_one = false;
985                     OptionEnumValueElement *enum_values = g_option_table[option_idx].enum_values;
986                     lldb::RunMode run_mode = (lldb::RunMode) Args::StringToOptionEnum(option_arg, enum_values, eOnlyDuringStepping, &found_one);
987 
988                     if (!found_one)
989                         error.SetErrorStringWithFormat("Invalid enumeration value for option '%c'.\n", short_option);
990                     else if (run_mode == eAllThreads)
991                         m_stop_others = false;
992                     else
993                         m_stop_others = true;
994 
995                 }
996                 break;
997                 default:
998                     error.SetErrorStringWithFormat("Invalid short option character '%c'.\n", short_option);
999                     break;
1000 
1001             }
1002             return error;
1003         }
1004 
1005         void
1006         ResetOptionValues ()
1007         {
1008             Options::ResetOptionValues();
1009             m_thread_idx = LLDB_INVALID_THREAD_ID;
1010             m_frame_idx = 0;
1011             m_stop_others = false;
1012         }
1013 
1014         const lldb::OptionDefinition*
1015         GetDefinitions ()
1016         {
1017             return g_option_table;
1018         }
1019 
1020         uint32_t m_step_thread_idx;
1021         bool m_stop_others;
1022 
1023         // Options table: Required for subclasses of Options.
1024 
1025         static lldb::OptionDefinition g_option_table[];
1026 
1027         // Instance variables to hold the values for command options.
1028     };
1029 
1030     CommandObjectThreadUntil () :
1031         CommandObject ("thread until",
1032                        "Runs the current or specified thread until it reaches a given line number or leaves the current function.",
1033                        "thread until [<cmd-options>] <line-number>",
1034                        eFlagProcessMustBeLaunched | eFlagProcessMustBePaused),
1035         m_options ()
1036     {
1037     }
1038 
1039 
1040     virtual
1041     ~CommandObjectThreadUntil ()
1042     {
1043     }
1044 
1045     virtual
1046     Options *
1047     GetOptions ()
1048     {
1049         return &m_options;
1050     }
1051 
1052     virtual bool
1053     Execute
1054     (
1055         CommandInterpreter &interpreter,
1056         Args& command,
1057         CommandReturnObject &result
1058     )
1059     {
1060         bool synchronous_execution = interpreter.GetSynchronous ();
1061 
1062         if (!interpreter.GetDebugger().GetSelectedTarget().get())
1063         {
1064             result.AppendError ("invalid target, set executable file using 'file' command");
1065             result.SetStatus (eReturnStatusFailed);
1066             return false;
1067         }
1068 
1069         Process *process = interpreter.GetDebugger().GetExecutionContext().process;
1070         if (process == NULL)
1071         {
1072             result.AppendError ("need a valid process to step");
1073             result.SetStatus (eReturnStatusFailed);
1074 
1075         }
1076         else
1077         {
1078             Thread *thread = NULL;
1079             uint32_t line_number;
1080 
1081             if (command.GetArgumentCount() != 1)
1082             {
1083                 result.AppendErrorWithFormat ("No line number provided:\n%s", GetSyntax());
1084                 result.SetStatus (eReturnStatusFailed);
1085                 return false;
1086             }
1087 
1088             line_number = Args::StringToUInt32 (command.GetArgumentAtIndex(0), UINT32_MAX);
1089             if (line_number == UINT32_MAX)
1090             {
1091                 result.AppendErrorWithFormat ("Invalid line number: '%s'.\n", command.GetArgumentAtIndex(0));
1092                 result.SetStatus (eReturnStatusFailed);
1093                 return false;
1094             }
1095 
1096             if (m_options.m_thread_idx == LLDB_INVALID_THREAD_ID)
1097             {
1098                 thread = process->GetThreadList().GetSelectedThread().get();
1099             }
1100             else
1101             {
1102                 thread = process->GetThreadList().GetThreadAtIndex(m_options.m_thread_idx).get();
1103             }
1104 
1105             if (thread == NULL)
1106             {
1107                 const uint32_t num_threads = process->GetThreadList().GetSize();
1108                 result.AppendErrorWithFormat ("Thread index %u is out of range (valid values are 0 - %u).\n", m_options.m_thread_idx, 0, num_threads);
1109                 result.SetStatus (eReturnStatusFailed);
1110                 return false;
1111             }
1112 
1113             const bool abort_other_plans = true;
1114 
1115             StackFrame *frame = thread->GetStackFrameAtIndex(m_options.m_frame_idx).get();
1116             if (frame == NULL)
1117             {
1118 
1119                 result.AppendErrorWithFormat ("Frame index %u is out of range for thread %u.\n", m_options.m_frame_idx, m_options.m_thread_idx);
1120                 result.SetStatus (eReturnStatusFailed);
1121                 return false;
1122             }
1123 
1124             ThreadPlan *new_plan;
1125 
1126             if (frame->HasDebugInformation ())
1127             {
1128                 // Finally we got here...  Translate the given line number to a bunch of addresses:
1129                 SymbolContext sc(frame->GetSymbolContext (eSymbolContextCompUnit));
1130                 LineTable *line_table = NULL;
1131                 if (sc.comp_unit)
1132                     line_table = sc.comp_unit->GetLineTable();
1133 
1134                 if (line_table == NULL)
1135                 {
1136                     result.AppendErrorWithFormat ("Failed to resolve the line table for frame %u of thread index %u.\n",
1137                                                  m_options.m_frame_idx, m_options.m_thread_idx);
1138                     result.SetStatus (eReturnStatusFailed);
1139                     return false;
1140                 }
1141 
1142                 LineEntry function_start;
1143                 uint32_t index_ptr = 0, end_ptr;
1144                 std::vector<addr_t> address_list;
1145 
1146                 // Find the beginning & end index of the
1147                 AddressRange fun_addr_range = sc.function->GetAddressRange();
1148                 Address fun_start_addr = fun_addr_range.GetBaseAddress();
1149                 line_table->FindLineEntryByAddress (fun_start_addr, function_start, &index_ptr);
1150 
1151                 Address fun_end_addr(fun_start_addr.GetSection(), fun_start_addr.GetOffset() + fun_addr_range.GetByteSize());
1152                 line_table->FindLineEntryByAddress (fun_end_addr, function_start, &end_ptr);
1153 
1154                 while (index_ptr <= end_ptr)
1155                 {
1156                     LineEntry line_entry;
1157                     index_ptr = sc.comp_unit->FindLineEntry(index_ptr, line_number, sc.comp_unit, &line_entry);
1158                     if (index_ptr == UINT32_MAX)
1159                         break;
1160 
1161                     addr_t address = line_entry.range.GetBaseAddress().GetLoadAddress(process);
1162                     if (address != LLDB_INVALID_ADDRESS)
1163                         address_list.push_back (address);
1164                     index_ptr++;
1165                 }
1166 
1167                 new_plan = thread->QueueThreadPlanForStepUntil (abort_other_plans, &address_list.front(), address_list.size(), m_options.m_stop_others);
1168                 new_plan->SetOkayToDiscard(false);
1169             }
1170             else
1171             {
1172                 result.AppendErrorWithFormat ("Frame index %u of thread %u has no debug information.\n", m_options.m_frame_idx, m_options.m_thread_idx);
1173                 result.SetStatus (eReturnStatusFailed);
1174                 return false;
1175 
1176             }
1177 
1178             process->GetThreadList().SetSelectedThreadByID (m_options.m_thread_idx);
1179             Error error (process->Resume ());
1180             if (error.Success())
1181             {
1182                 result.AppendMessageWithFormat ("Resuming process %i\n", process->GetID());
1183                 if (synchronous_execution)
1184                 {
1185                     StateType state = process->WaitForProcessToStop (NULL);
1186 
1187                     result.SetDidChangeProcessState (true);
1188                     result.AppendMessageWithFormat ("Process %i %s\n", process->GetID(), StateAsCString (state));
1189                     result.SetStatus (eReturnStatusSuccessFinishNoResult);
1190                 }
1191                 else
1192                 {
1193                     result.SetStatus (eReturnStatusSuccessContinuingNoResult);
1194                 }
1195             }
1196             else
1197             {
1198                 result.AppendErrorWithFormat("Failed to resume process: %s.\n", error.AsCString());
1199                 result.SetStatus (eReturnStatusFailed);
1200             }
1201 
1202         }
1203         return result.Succeeded();
1204     }
1205 protected:
1206     CommandOptions m_options;
1207 
1208 };
1209 
1210 lldb::OptionDefinition
1211 CommandObjectThreadUntil::CommandOptions::g_option_table[] =
1212 {
1213 { LLDB_OPT_SET_1, false, "frame",   'f', required_argument, NULL,               0, "<frame>",   "Frame index for until operation - defaults to 0"},
1214 { LLDB_OPT_SET_1, false, "thread",  't', required_argument, NULL,               0, "<thread>",  "Thread index for the thread for until operation"},
1215 { LLDB_OPT_SET_1, false, "run_mode",'m', required_argument, g_duo_running_mode, 0, "<run_mode>","Determine how to run other threads while stepping this one"},
1216 { 0, false, NULL, 0, 0, NULL, 0, NULL, NULL }
1217 };
1218 
1219 
1220 //-------------------------------------------------------------------------
1221 // CommandObjectThreadSelect
1222 //-------------------------------------------------------------------------
1223 
1224 class CommandObjectThreadSelect : public CommandObject
1225 {
1226 public:
1227 
1228     CommandObjectThreadSelect () :
1229         CommandObject ("thread select",
1230                          "Selects a threads as the currently active thread.",
1231                          "thread select <thread-index>",
1232                          eFlagProcessMustBeLaunched | eFlagProcessMustBePaused)
1233     {
1234     }
1235 
1236 
1237     virtual
1238     ~CommandObjectThreadSelect ()
1239     {
1240     }
1241 
1242     virtual bool
1243     Execute
1244     (
1245         CommandInterpreter &interpreter,
1246         Args& command,
1247         CommandReturnObject &result
1248     )
1249     {
1250         Process *process = interpreter.GetDebugger().GetExecutionContext().process;
1251         if (process == NULL)
1252         {
1253             result.AppendError ("no process");
1254             result.SetStatus (eReturnStatusFailed);
1255             return false;
1256         }
1257         else if (command.GetArgumentCount() != 1)
1258         {
1259             result.AppendErrorWithFormat("'%s' takes exactly one thread index argument:\nUsage: \n", m_cmd_name.c_str(), m_cmd_syntax.c_str());
1260             result.SetStatus (eReturnStatusFailed);
1261             return false;
1262         }
1263 
1264         uint32_t index_id = Args::StringToUInt32(command.GetArgumentAtIndex(0), 0, 0);
1265 
1266         Thread *new_thread = process->GetThreadList().FindThreadByIndexID(index_id).get();
1267         if (new_thread == NULL)
1268         {
1269             result.AppendErrorWithFormat ("Invalid thread #%s.\n", command.GetArgumentAtIndex(0));
1270             result.SetStatus (eReturnStatusFailed);
1271             return false;
1272         }
1273 
1274         process->GetThreadList().SetSelectedThreadByID(new_thread->GetID());
1275 
1276         DisplayThreadInfo (interpreter,
1277                            result.GetOutputStream(),
1278                            new_thread,
1279                            false,
1280                            true);
1281 
1282         return result.Succeeded();
1283     }
1284 
1285 };
1286 
1287 
1288 //-------------------------------------------------------------------------
1289 // CommandObjectThreadList
1290 //-------------------------------------------------------------------------
1291 
1292 class CommandObjectThreadList : public CommandObject
1293 {
1294 public:
1295 
1296 
1297     CommandObjectThreadList ():
1298         CommandObject ("thread list",
1299                        "Shows a summary of all current threads in a process.",
1300                        "thread list",
1301                        eFlagProcessMustBeLaunched | eFlagProcessMustBePaused)
1302     {
1303     }
1304 
1305     ~CommandObjectThreadList()
1306     {
1307     }
1308 
1309     bool
1310     Execute
1311     (
1312         CommandInterpreter &interpreter,
1313         Args& command,
1314         CommandReturnObject &result
1315     )
1316     {
1317         StreamString &strm = result.GetOutputStream();
1318         result.SetStatus (eReturnStatusSuccessFinishNoResult);
1319         ExecutionContext exe_ctx(interpreter.GetDebugger().GetExecutionContext());
1320         if (exe_ctx.process)
1321         {
1322             const StateType state = exe_ctx.process->GetState();
1323 
1324             if (StateIsStoppedState(state))
1325             {
1326                 if (state == eStateExited)
1327                 {
1328                     int exit_status = exe_ctx.process->GetExitStatus();
1329                     const char *exit_description = exe_ctx.process->GetExitDescription();
1330                     strm.Printf ("Process %d exited with status = %i (0x%8.8x) %s\n",
1331                                           exe_ctx.process->GetID(),
1332                                           exit_status,
1333                                           exit_status,
1334                                           exit_description ? exit_description : "");
1335                 }
1336                 else
1337                 {
1338                     strm.Printf ("Process %d state is %s\n", exe_ctx.process->GetID(), StateAsCString (state));
1339                     if (exe_ctx.thread == NULL)
1340                         exe_ctx.thread = exe_ctx.process->GetThreadList().GetThreadAtIndex(0).get();
1341                     if (exe_ctx.thread != NULL)
1342                     {
1343                         DisplayThreadsInfo (interpreter, &exe_ctx, result, false, false);
1344                     }
1345                     else
1346                     {
1347                         result.AppendError ("no valid thread found in current process");
1348                         result.SetStatus (eReturnStatusFailed);
1349                     }
1350                 }
1351             }
1352             else
1353             {
1354                 result.AppendError ("process is currently running");
1355                 result.SetStatus (eReturnStatusFailed);
1356             }
1357         }
1358         else
1359         {
1360             result.AppendError ("no current location or status available");
1361             result.SetStatus (eReturnStatusFailed);
1362         }
1363         return result.Succeeded();
1364     }
1365 };
1366 
1367 //-------------------------------------------------------------------------
1368 // CommandObjectMultiwordThread
1369 //-------------------------------------------------------------------------
1370 
1371 CommandObjectMultiwordThread::CommandObjectMultiwordThread (CommandInterpreter &interpreter) :
1372     CommandObjectMultiword ("thread",
1373                             "A set of commands for operating on one or more thread within a running process.",
1374                             "thread <subcommand> [<subcommand-options>]")
1375 {
1376     LoadSubCommand (interpreter, "backtrace",  CommandObjectSP (new CommandObjectThreadBacktrace ()));
1377     LoadSubCommand (interpreter, "continue",   CommandObjectSP (new CommandObjectThreadContinue ()));
1378     LoadSubCommand (interpreter, "list",       CommandObjectSP (new CommandObjectThreadList ()));
1379     LoadSubCommand (interpreter, "select",     CommandObjectSP (new CommandObjectThreadSelect ()));
1380     LoadSubCommand (interpreter, "until",      CommandObjectSP (new CommandObjectThreadUntil ()));
1381     LoadSubCommand (interpreter, "step-in",    CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope (
1382                                                     "thread step-in",
1383                                                      "Source level single step in in specified thread (current thread, if none specified).",
1384                                                      "thread step-in [<thread-id>]",
1385                                                      eFlagProcessMustBeLaunched | eFlagProcessMustBePaused,
1386                                                      eStepTypeInto,
1387                                                      eStepScopeSource)));
1388 
1389     LoadSubCommand (interpreter, "step-out",    CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ("thread step-out",
1390                                                                                       "Source level single step out in specified thread (current thread, if none specified).",
1391                                                                                       "thread step-out [<thread-id>]",
1392                                                                                       eFlagProcessMustBeLaunched | eFlagProcessMustBePaused,
1393                                                                                       eStepTypeOut,
1394                                                                                       eStepScopeSource)));
1395 
1396     LoadSubCommand (interpreter, "step-over",   CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ("thread step-over",
1397                                                                                       "Source level single step over in specified thread (current thread, if none specified).",
1398                                                                                       "thread step-over [<thread-id>]",
1399                                                                                       eFlagProcessMustBeLaunched | eFlagProcessMustBePaused,
1400                                                                                       eStepTypeOver,
1401                                                                                       eStepScopeSource)));
1402 
1403     LoadSubCommand (interpreter, "step-inst",   CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ("thread step-inst",
1404                                                                                       "Single step one instruction in specified thread (current thread, if none specified).",
1405                                                                                       "thread step-inst [<thread-id>]",
1406                                                                                       eFlagProcessMustBeLaunched | eFlagProcessMustBePaused,
1407                                                                                       eStepTypeTrace,
1408                                                                                       eStepScopeInstruction)));
1409 
1410     LoadSubCommand (interpreter, "step-inst-over", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ("thread step-inst-over",
1411                                                                                       "Single step one instruction in specified thread (current thread, if none specified), stepping over calls.",
1412                                                                                       "thread step-inst-over [<thread-id>]",
1413                                                                                       eFlagProcessMustBeLaunched | eFlagProcessMustBePaused,
1414                                                                                       eStepTypeTraceOver,
1415                                                                                       eStepScopeInstruction)));
1416 }
1417 
1418 CommandObjectMultiwordThread::~CommandObjectMultiwordThread ()
1419 {
1420 }
1421 
1422 
1423