1 //===-- CommandObjectProcess.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 "CommandObjectProcess.h"
11 
12 // C Includes
13 // C++ Includes
14 // Other libraries and framework includes
15 // Project includes
16 #include "lldb/Interpreter/Args.h"
17 #include "lldb/Interpreter/Options.h"
18 #include "lldb/Core/State.h"
19 #include "lldb/Interpreter/CommandInterpreter.h"
20 #include "lldb/Interpreter/CommandReturnObject.h"
21 #include "./CommandObjectThread.h"
22 #include "lldb/Target/Process.h"
23 #include "lldb/Target/Target.h"
24 #include "lldb/Target/Thread.h"
25 
26 using namespace lldb;
27 using namespace lldb_private;
28 
29 //-------------------------------------------------------------------------
30 // CommandObjectProcessLaunch
31 //-------------------------------------------------------------------------
32 
33 class CommandObjectProcessLaunch : public CommandObject
34 {
35 public:
36 
37     class CommandOptions : public Options
38     {
39     public:
40 
41         CommandOptions () :
42             Options()
43         {
44             // Keep default values of all options in one place: ResetOptionValues ()
45             ResetOptionValues ();
46         }
47 
48         ~CommandOptions ()
49         {
50         }
51 
52         Error
53         SetOptionValue (int option_idx, const char *option_arg)
54         {
55             Error error;
56             char short_option = (char) m_getopt_table[option_idx].val;
57 
58             switch (short_option)
59             {
60                 case 's':   stop_at_entry = true;       break;
61                 case 'e':   stderr_path = option_arg;   break;
62                 case 'i':   stdin_path  = option_arg;   break;
63                 case 'o':   stdout_path = option_arg;   break;
64                 case 'p':   plugin_name = option_arg;   break;
65                 default:
66                     error.SetErrorStringWithFormat("Invalid short option character '%c'.\n", short_option);
67                     break;
68 
69             }
70             return error;
71         }
72 
73         void
74         ResetOptionValues ()
75         {
76             Options::ResetOptionValues();
77             stop_at_entry = false;
78             stdin_path.clear();
79             stdout_path.clear();
80             stderr_path.clear();
81             plugin_name.clear();
82         }
83 
84         const lldb::OptionDefinition*
85         GetDefinitions ()
86         {
87             return g_option_table;
88         }
89 
90         // Options table: Required for subclasses of Options.
91 
92         static lldb::OptionDefinition g_option_table[];
93 
94         // Instance variables to hold the values for command options.
95 
96         bool stop_at_entry;
97         std::string stderr_path;
98         std::string stdin_path;
99         std::string stdout_path;
100         std::string plugin_name;
101 
102     };
103 
104     CommandObjectProcessLaunch () :
105         CommandObject ("process launch",
106                        "Launches the executable in the debugger.",
107                        "process launch [<cmd-options>] [<arguments-for-running-the-program>]")
108     {
109     }
110 
111 
112     ~CommandObjectProcessLaunch ()
113     {
114     }
115 
116     Options *
117     GetOptions ()
118     {
119         return &m_options;
120     }
121 
122     bool
123     Execute (CommandInterpreter &interpreter,
124              Args& launch_args,
125              CommandReturnObject &result)
126     {
127         Target *target = interpreter.GetDebugger().GetCurrentTarget().get();
128         bool synchronous_execution = interpreter.GetSynchronous ();
129     //    bool launched = false;
130     //    bool stopped_after_launch = false;
131 
132         if (target == NULL)
133         {
134             result.AppendError ("invalid target, set executable file using 'file' command");
135             result.SetStatus (eReturnStatusFailed);
136             return false;
137         }
138 
139         // If our listener is NULL, users aren't allows to launch
140         char filename[PATH_MAX];
141         Module *exe_module = target->GetExecutableModule().get();
142         exe_module->GetFileSpec().GetPath(filename, sizeof(filename));
143 
144         Process *process = interpreter.GetDebugger().GetExecutionContext().process;
145         if (process)
146         {
147             if (process->IsAlive())
148             {
149                result.AppendErrorWithFormat ("Process %u is currently being debugged, kill the process before running again.\n",
150                                             process->GetID());
151                 result.SetStatus (eReturnStatusFailed);
152                 return false;
153             }
154         }
155 
156         const char *plugin_name;
157         if (!m_options.plugin_name.empty())
158             plugin_name = m_options.plugin_name.c_str();
159         else
160             plugin_name = NULL;
161 
162         process = target->CreateProcess (interpreter.GetDebugger().GetListener(), plugin_name).get();
163 
164         const Args *environment = interpreter.GetEnvironmentVariables();
165         const Args *run_args = interpreter.GetProgramArguments();
166 
167         // There are two possible sources of args to be passed to the process upon launching:  Those the user
168         // typed at the run command (launch_args); or those the user pre-set in the run-args variable (run_args).
169 
170         // If launch_args is empty, use run_args.
171         if (launch_args.GetArgumentCount() == 0)
172         {
173             if (run_args != NULL)
174                 launch_args.AppendArguments (*run_args);
175         }
176         else
177         {
178             // launch-args was not empty; use that, AND re-set run-args to contains launch-args values.
179             StateVariable *run_args_var = interpreter.GetStateVariable ("run-args");
180             if (run_args_var != NULL)
181             {
182                 run_args_var->ArrayClearValues();
183                 run_args_var->GetArgs().AppendArguments (launch_args);
184             }
185         }
186 
187 
188         if (process)
189         {
190             const char *archname = exe_module->GetArchitecture().AsCString();
191 
192             const char * stdin_path = NULL;
193             const char * stdout_path = NULL;
194             const char * stderr_path = NULL;
195 
196             if (!(m_options.stdin_path.empty() &&
197                 m_options.stdout_path.empty() &&
198                 m_options.stderr_path.empty()))
199             {
200                 stdin_path =    m_options.stdin_path.empty()  ? "/dev/null" : m_options.stdin_path.c_str();
201                 stdout_path =   m_options.stdout_path.empty() ? "/dev/null" : m_options.stdout_path.c_str();
202                 stderr_path =   m_options.stderr_path.empty() ? "/dev/null" : m_options.stderr_path.c_str();
203             }
204 
205             Error error (process->Launch (launch_args.GetConstArgumentVector(),
206                                           environment ? environment->GetConstArgumentVector() : NULL,
207                                           stdin_path,
208                                           stdout_path,
209                                           stderr_path));
210 
211             if (error.Success())
212             {
213                 result.AppendMessageWithFormat ("Launching '%s'  (%s)\n", filename, archname);
214                 result.SetStatus (eReturnStatusSuccessContinuingNoResult);
215                 if (m_options.stop_at_entry == false)
216                 {
217                     StateType state = process->WaitForProcessToStop (NULL);
218 
219                     if (state == eStateStopped)
220                     {
221                         // Call continue_command.
222                         CommandReturnObject continue_result;
223                         interpreter.HandleCommand("process continue", false, continue_result);
224                     }
225 
226                     if (synchronous_execution)
227                     {
228                         result.SetDidChangeProcessState (true);
229                         result.SetStatus (eReturnStatusSuccessFinishNoResult);
230                     }
231                 }
232             }
233             else
234             {
235                 result.AppendErrorWithFormat ("Process launch failed: %s",
236                                               error.AsCString());
237                 result.SetStatus (eReturnStatusFailed);
238             }
239         }
240         else
241         {
242             result.AppendErrorWithFormat ("Process launch failed: unable to create a process object.\n");
243             result.SetStatus (eReturnStatusFailed);
244             return false;
245         }
246 
247         return result.Succeeded();
248     }
249 
250 protected:
251 
252     CommandOptions m_options;
253 };
254 
255 
256 lldb::OptionDefinition
257 CommandObjectProcessLaunch::CommandOptions::g_option_table[] =
258 {
259 { LLDB_OPT_SET_1, false, "stop-at-entry", 's', no_argument,       NULL, 0, NULL,        "Stop at the entry point of the program when launching a process."},
260 { LLDB_OPT_SET_1, false, "stdin",         'i', required_argument, NULL, 0, "<path>",    "Redirect stdin for the process to <path>."},
261 { LLDB_OPT_SET_1, false, "stdout",        'o', required_argument, NULL, 0, "<path>",    "Redirect stdout for the process to <path>."},
262 { LLDB_OPT_SET_1, false, "stderr",        'e', required_argument, NULL, 0, "<path>",    "Redirect stderr for the process to <path>."},
263 { LLDB_OPT_SET_1, false, "plugin",        'p', required_argument, NULL, 0, "<plugin>",  "Name of the process plugin you want to use."},
264 { 0, false, NULL, 0, 0, NULL, 0, NULL, NULL }
265 };
266 
267 
268 //-------------------------------------------------------------------------
269 // CommandObjectProcessAttach
270 //-------------------------------------------------------------------------
271 
272 class CommandObjectProcessAttach : public CommandObject
273 {
274 public:
275 
276     CommandObjectProcessAttach () :
277         CommandObject ("process attach",
278                        "Attaches to a process.",
279                        "process attach <cmd-options>")
280     {
281         SetHelpLong("Currently, you must set the executable file before you can attach "
282                     "to a process.\n");
283     }
284 
285     ~CommandObjectProcessAttach ()
286     {
287     }
288 
289     bool
290     Execute (CommandInterpreter &interpreter,
291              Args& command,
292              CommandReturnObject &result)
293     {
294         Target *target = interpreter.GetDebugger().GetCurrentTarget().get();
295         if (target == NULL)
296         {
297             result.AppendError ("invalid target, set executable file using 'file' command");
298             result.SetStatus (eReturnStatusFailed);
299             return false;
300         }
301 
302         // If our listener is NULL, users aren't allows to launch
303 
304         Process *process = interpreter.GetDebugger().GetExecutionContext().process;
305         if (process)
306         {
307             if (process->IsAlive())
308             {
309                 result.AppendErrorWithFormat ("Process %u is currently being debugged, kill the process before attaching.\n", process->GetID());
310                 result.SetStatus (eReturnStatusFailed);
311                 return false;
312             }
313         }
314 
315         if (command.GetArgumentCount())
316         {
317             result.AppendErrorWithFormat("Invalid arguments for '%s'.\nUsage: \n", m_cmd_name.c_str(), m_cmd_syntax.c_str());
318             result.SetStatus (eReturnStatusFailed);
319         }
320         else
321         {
322             const char *plugin_name = NULL;
323 
324             if (!m_options.plugin_name.empty())
325                 plugin_name = m_options.plugin_name.c_str();
326 
327             process = target->CreateProcess (interpreter.GetDebugger().GetListener(), plugin_name).get();
328 
329             if (process)
330             {
331                 Error error;
332                 int attach_pid = m_options.pid;
333 
334                 if (attach_pid != LLDB_INVALID_PROCESS_ID)
335                 {
336                     error = process->Attach (attach_pid);
337                     if (error.Success())
338                     {
339                         result.SetStatus (eReturnStatusSuccessContinuingNoResult);
340                     }
341                     else
342                     {
343                         result.AppendErrorWithFormat ("Attaching to process %i failed: %s.\n",
344                                                      attach_pid,
345                                                      error.AsCString());
346                         result.SetStatus (eReturnStatusFailed);
347                     }
348                 }
349                 else if (!m_options.name.empty())
350                 {
351                     error = process->Attach (m_options.name.c_str(), m_options.waitfor);
352                     if (error.Success())
353                     {
354                         result.SetStatus (eReturnStatusSuccessContinuingNoResult);
355                     }
356                     else
357                     {
358                         if (m_options.waitfor)
359                             result.AppendErrorWithFormat ("Waiting for a process to launch named '%s': %s\n",
360                                                          m_options.name.c_str(),
361                                                          error.AsCString());
362                         else
363                             result.AppendErrorWithFormat ("Failed to a process named '%s': %s\n",
364                                                          m_options.name.c_str(),
365                                                          error.AsCString());
366                         result.SetStatus (eReturnStatusFailed);
367                     }
368                 }
369             }
370         }
371         return result.Succeeded();
372     }
373 
374     Options *
375     GetOptions ()
376     {
377         return &m_options;
378     }
379 
380     class CommandOptions : public Options
381     {
382     public:
383 
384         CommandOptions () :
385             Options()
386         {
387             // Keep default values of all options in one place: ResetOptionValues ()
388             ResetOptionValues ();
389         }
390 
391         ~CommandOptions ()
392         {
393         }
394 
395         Error
396         SetOptionValue (int option_idx, const char *option_arg)
397         {
398             Error error;
399             char short_option = (char) m_getopt_table[option_idx].val;
400             bool success = false;
401             switch (short_option)
402             {
403                 case 'p':
404                     pid = Args::StringToUInt32 (option_arg, LLDB_INVALID_PROCESS_ID, 0, &success);
405                     if (!success || pid == LLDB_INVALID_PROCESS_ID)
406                     {
407                         error.SetErrorStringWithFormat("Invalid process ID '%s'.\n", option_arg);
408                     }
409                     break;
410 
411                 case 'P':
412                     plugin_name = option_arg;
413                     break;
414 
415                 case 'n':
416                     name.assign(option_arg);
417                     break;
418 
419                 case 'w':
420                     waitfor = true;
421                     break;
422 
423                 default:
424                     error.SetErrorStringWithFormat("Invalid short option character '%c'.\n", short_option);
425                     break;
426             }
427             return error;
428         }
429 
430         void
431         ResetOptionValues ()
432         {
433             Options::ResetOptionValues();
434             pid = LLDB_INVALID_PROCESS_ID;
435             name.clear();
436             waitfor = false;
437         }
438 
439         const lldb::OptionDefinition*
440         GetDefinitions ()
441         {
442             return g_option_table;
443         }
444 
445         // Options table: Required for subclasses of Options.
446 
447         static lldb::OptionDefinition g_option_table[];
448 
449         // Instance variables to hold the values for command options.
450 
451         lldb::pid_t pid;
452         std::string plugin_name;
453         std::string name;
454         bool waitfor;
455     };
456 
457 protected:
458 
459     CommandOptions m_options;
460 };
461 
462 
463 lldb::OptionDefinition
464 CommandObjectProcessAttach::CommandOptions::g_option_table[] =
465 {
466 { LLDB_OPT_SET_ALL, false, "plugin",       'P', required_argument, NULL, 0, "<plugin>",        "Name of the process plugin you want to use."},
467 { LLDB_OPT_SET_1, false, "pid",          'p', required_argument, NULL, 0, "<pid>",           "The process ID of an existing process to attach to."},
468 { LLDB_OPT_SET_2, true,  "name",         'n', required_argument, NULL, 0, "<process-name>",  "The name of the process to attach to."},
469 { LLDB_OPT_SET_2, false, "waitfor",      'w', no_argument,       NULL, 0, NULL,              "Wait for the the process with <process-name> to launch."},
470 { 0, false, NULL, 0, 0, NULL, 0, NULL, NULL }
471 };
472 
473 //-------------------------------------------------------------------------
474 // CommandObjectProcessContinue
475 //-------------------------------------------------------------------------
476 
477 class CommandObjectProcessContinue : public CommandObject
478 {
479 public:
480 
481     CommandObjectProcessContinue () :
482         CommandObject ("process continue",
483                        "Continues execution all threads in the current process.",
484                        "process continue",
485                        eFlagProcessMustBeLaunched | eFlagProcessMustBePaused)
486     {
487     }
488 
489 
490     ~CommandObjectProcessContinue ()
491     {
492     }
493 
494     bool
495     Execute (CommandInterpreter &interpreter,
496              Args& command,
497              CommandReturnObject &result)
498     {
499         Process *process = interpreter.GetDebugger().GetExecutionContext().process;
500         bool synchronous_execution = interpreter.GetSynchronous ();
501 
502         if (process == NULL)
503         {
504             result.AppendError ("no process to continue");
505             result.SetStatus (eReturnStatusFailed);
506             return false;
507          }
508 
509         StateType state = process->GetState();
510         if (state == eStateStopped)
511         {
512             if (command.GetArgumentCount() != 0)
513             {
514                 result.AppendErrorWithFormat ("The '%s' command does not take any arguments.\n", m_cmd_name.c_str());
515                 result.SetStatus (eReturnStatusFailed);
516                 return false;
517             }
518 
519             const uint32_t num_threads = process->GetThreadList().GetSize();
520 
521             // Set the actions that the threads should each take when resuming
522             for (uint32_t idx=0; idx<num_threads; ++idx)
523             {
524                 process->GetThreadList().GetThreadAtIndex(idx)->SetResumeState (eStateRunning);
525             }
526 
527             Error error(process->Resume());
528             if (error.Success())
529             {
530                 result.AppendMessageWithFormat ("Resuming process %i\n", process->GetID());
531                 if (synchronous_execution)
532                 {
533                     StateType state = process->WaitForProcessToStop (NULL);
534 
535                     result.SetDidChangeProcessState (true);
536                     result.AppendMessageWithFormat ("Process %i %s\n", process->GetID(), StateAsCString (state));
537                     result.SetStatus (eReturnStatusSuccessFinishNoResult);
538                 }
539                 else
540                 {
541                     result.SetStatus (eReturnStatusSuccessContinuingNoResult);
542                 }
543             }
544             else
545             {
546                 result.AppendErrorWithFormat("Failed to resume process: %s.\n", error.AsCString());
547                 result.SetStatus (eReturnStatusFailed);
548             }
549         }
550         else
551         {
552             result.AppendErrorWithFormat ("Process cannot be continued from its current state (%s).\n",
553                                          StateAsCString(state));
554             result.SetStatus (eReturnStatusFailed);
555         }
556         return result.Succeeded();
557     }
558 };
559 
560 //-------------------------------------------------------------------------
561 // CommandObjectProcessDetach
562 //-------------------------------------------------------------------------
563 
564 class CommandObjectProcessDetach : public CommandObject
565 {
566 public:
567 
568     CommandObjectProcessDetach () :
569         CommandObject ("process detach",
570                        "Detaches from the current process being debugged.",
571                        "process detach",
572                        eFlagProcessMustBeLaunched)
573     {
574     }
575 
576     ~CommandObjectProcessDetach ()
577     {
578     }
579 
580     bool
581     Execute (CommandInterpreter &interpreter,
582              Args& command,
583              CommandReturnObject &result)
584     {
585         Process *process = interpreter.GetDebugger().GetExecutionContext().process;
586         if (process == NULL)
587         {
588             result.AppendError ("must have a valid process in order to detach");
589             result.SetStatus (eReturnStatusFailed);
590             return false;
591         }
592 
593         Error error (process->Detach());
594         if (error.Success())
595         {
596             result.SetStatus (eReturnStatusSuccessFinishResult);
597         }
598         else
599         {
600             result.AppendErrorWithFormat ("Detach failed: %s\n", error.AsCString());
601             result.SetStatus (eReturnStatusFailed);
602             return false;
603         }
604         return result.Succeeded();
605     }
606 };
607 
608 //-------------------------------------------------------------------------
609 // CommandObjectProcessSignal
610 //-------------------------------------------------------------------------
611 
612 class CommandObjectProcessSignal : public CommandObject
613 {
614 public:
615 
616     CommandObjectProcessSignal () :
617         CommandObject ("process signal",
618                        "Sends a UNIX signal to the current process being debugged.",
619                        "process signal <unix-signal-number>")
620     {
621     }
622 
623     ~CommandObjectProcessSignal ()
624     {
625     }
626 
627     bool
628     Execute (CommandInterpreter &interpreter,
629              Args& command,
630              CommandReturnObject &result)
631     {
632         Process *process = interpreter.GetDebugger().GetExecutionContext().process;
633         if (process == NULL)
634         {
635             result.AppendError ("no process to signal");
636             result.SetStatus (eReturnStatusFailed);
637             return false;
638         }
639 
640         if (command.GetArgumentCount() == 1)
641         {
642             int signo = Args::StringToSInt32(command.GetArgumentAtIndex(0), -1, 0);
643             if (signo == -1)
644             {
645                 result.AppendErrorWithFormat ("Invalid signal argument '%s'.\n", command.GetArgumentAtIndex(0));
646                 result.SetStatus (eReturnStatusFailed);
647             }
648             else
649             {
650                 Error error (process->Signal (signo));
651                 if (error.Success())
652                 {
653                     result.SetStatus (eReturnStatusSuccessFinishResult);
654                 }
655                 else
656                 {
657                     result.AppendErrorWithFormat ("Failed to send signal %i: %s\n", signo, error.AsCString());
658                     result.SetStatus (eReturnStatusFailed);
659                 }
660             }
661         }
662         else
663         {
664             result.AppendErrorWithFormat("'%s' takes exactly one signal number argument:\nUsage: \n", m_cmd_name.c_str(),
665                                         m_cmd_syntax.c_str());
666             result.SetStatus (eReturnStatusFailed);
667         }
668         return result.Succeeded();
669     }
670 };
671 
672 
673 //-------------------------------------------------------------------------
674 // CommandObjectProcessInterrupt
675 //-------------------------------------------------------------------------
676 
677 class CommandObjectProcessInterrupt : public CommandObject
678 {
679 public:
680 
681 
682     CommandObjectProcessInterrupt () :
683     CommandObject ("process interrupt",
684                    "Interrupts the current process being debugged.",
685                    "process interrupt",
686                    eFlagProcessMustBeLaunched)
687     {
688     }
689 
690     ~CommandObjectProcessInterrupt ()
691     {
692     }
693 
694     bool
695     Execute (CommandInterpreter &interpreter,
696              Args& command,
697              CommandReturnObject &result)
698     {
699         Process *process = interpreter.GetDebugger().GetExecutionContext().process;
700         if (process == NULL)
701         {
702             result.AppendError ("no process to halt");
703             result.SetStatus (eReturnStatusFailed);
704             return false;
705         }
706 
707         if (command.GetArgumentCount() == 0)
708         {
709             Error error(process->Halt ());
710             if (error.Success())
711             {
712                 result.SetStatus (eReturnStatusSuccessFinishResult);
713 
714                 // Maybe we should add a "SuspendThreadPlans so we
715                 // can halt, and keep in place all the current thread plans.
716                 process->GetThreadList().DiscardThreadPlans();
717             }
718             else
719             {
720                 result.AppendErrorWithFormat ("Failed to halt process: %s\n", error.AsCString());
721                 result.SetStatus (eReturnStatusFailed);
722             }
723         }
724         else
725         {
726             result.AppendErrorWithFormat("'%s' takes no arguments:\nUsage: \n",
727                                         m_cmd_name.c_str(),
728                                         m_cmd_syntax.c_str());
729             result.SetStatus (eReturnStatusFailed);
730         }
731         return result.Succeeded();
732     }
733 };
734 
735 //-------------------------------------------------------------------------
736 // CommandObjectProcessKill
737 //-------------------------------------------------------------------------
738 
739 class CommandObjectProcessKill : public CommandObject
740 {
741 public:
742 
743     CommandObjectProcessKill () :
744     CommandObject ("process kill",
745                    "Terminates the current process being debugged.",
746                    "process kill",
747                    eFlagProcessMustBeLaunched)
748     {
749     }
750 
751     ~CommandObjectProcessKill ()
752     {
753     }
754 
755     bool
756     Execute (CommandInterpreter &interpreter,
757              Args& command,
758              CommandReturnObject &result)
759     {
760         Process *process = interpreter.GetDebugger().GetExecutionContext().process;
761         if (process == NULL)
762         {
763             result.AppendError ("no process to kill");
764             result.SetStatus (eReturnStatusFailed);
765             return false;
766         }
767 
768         if (command.GetArgumentCount() == 0)
769         {
770             Error error (process->Destroy());
771             if (error.Success())
772             {
773                 result.SetStatus (eReturnStatusSuccessFinishResult);
774             }
775             else
776             {
777                 result.AppendErrorWithFormat ("Failed to kill process: %s\n", error.AsCString());
778                 result.SetStatus (eReturnStatusFailed);
779             }
780         }
781         else
782         {
783             result.AppendErrorWithFormat("'%s' takes no arguments:\nUsage: \n",
784                                         m_cmd_name.c_str(),
785                                         m_cmd_syntax.c_str());
786             result.SetStatus (eReturnStatusFailed);
787         }
788         return result.Succeeded();
789     }
790 };
791 
792 //-------------------------------------------------------------------------
793 // CommandObjectProcessStatus
794 //-------------------------------------------------------------------------
795 class CommandObjectProcessStatus : public CommandObject
796 {
797 public:
798     CommandObjectProcessStatus () :
799     CommandObject ("status",
800                    "Shows the current status and location of executing process.",
801                    "status",
802                    0)
803     {
804     }
805 
806     ~CommandObjectProcessStatus()
807     {
808     }
809 
810 
811     bool
812     Execute
813     (
814         CommandInterpreter &interpreter,
815         Args& command,
816         CommandReturnObject &result
817     )
818     {
819         StreamString &output_stream = result.GetOutputStream();
820         result.SetStatus (eReturnStatusSuccessFinishNoResult);
821         ExecutionContext exe_ctx(interpreter.GetDebugger().GetExecutionContext());
822         if (exe_ctx.process)
823         {
824             const StateType state = exe_ctx.process->GetState();
825             if (StateIsStoppedState(state))
826             {
827                 if (state == eStateExited)
828                 {
829                     int exit_status = exe_ctx.process->GetExitStatus();
830                     const char *exit_description = exe_ctx.process->GetExitDescription();
831                     output_stream.Printf ("Process %d exited with status = %i (0x%8.8x) %s\n",
832                                           exe_ctx.process->GetID(),
833                                           exit_status,
834                                           exit_status,
835                                           exit_description ? exit_description : "");
836                 }
837                 else
838                 {
839                     output_stream.Printf ("Process %d %s\n", exe_ctx.process->GetID(), StateAsCString (state));
840                     if (exe_ctx.thread == NULL)
841                         exe_ctx.thread = exe_ctx.process->GetThreadList().GetThreadAtIndex(0).get();
842                     if (exe_ctx.thread != NULL)
843                     {
844                         DisplayThreadsInfo (interpreter, &exe_ctx, result, true, true);
845                     }
846                     else
847                     {
848                         result.AppendError ("No valid thread found in current process.");
849                         result.SetStatus (eReturnStatusFailed);
850                     }
851                 }
852             }
853             else
854             {
855                 output_stream.Printf ("Process %d is running.\n",
856                                           exe_ctx.process->GetID());
857             }
858         }
859         else
860         {
861             result.AppendError ("No current location or status available.");
862             result.SetStatus (eReturnStatusFailed);
863         }
864         return result.Succeeded();
865     }
866 };
867 
868 //-------------------------------------------------------------------------
869 // CommandObjectMultiwordProcess
870 //-------------------------------------------------------------------------
871 
872 CommandObjectMultiwordProcess::CommandObjectMultiwordProcess (CommandInterpreter &interpreter) :
873     CommandObjectMultiword ("process",
874                               "A set of commands for operating on a process.",
875                               "process <subcommand> [<subcommand-options>]")
876 {
877     LoadSubCommand (interpreter, "attach",      CommandObjectSP (new CommandObjectProcessAttach ()));
878     LoadSubCommand (interpreter, "launch",      CommandObjectSP (new CommandObjectProcessLaunch ()));
879     LoadSubCommand (interpreter, "continue",    CommandObjectSP (new CommandObjectProcessContinue ()));
880     LoadSubCommand (interpreter, "detach",      CommandObjectSP (new CommandObjectProcessDetach ()));
881     LoadSubCommand (interpreter, "signal",      CommandObjectSP (new CommandObjectProcessSignal ()));
882     LoadSubCommand (interpreter, "status",      CommandObjectSP (new CommandObjectProcessStatus ()));
883     LoadSubCommand (interpreter, "interrupt",   CommandObjectSP (new CommandObjectProcessInterrupt ()));
884     LoadSubCommand (interpreter, "kill",        CommandObjectSP (new CommandObjectProcessKill ()));
885 }
886 
887 CommandObjectMultiwordProcess::~CommandObjectMultiwordProcess ()
888 {
889 }
890 
891