xref: /llvm-project/lldb/source/Commands/CommandObjectProcess.cpp (revision b132097b4558cd3fd697eedc1c26581d37b6e266)
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     virtual const char *GetRepeatCommand (Args &current_command_args, uint32_t index)
251     {
252         // No repeat for "process launch"...
253         return "";
254     }
255 
256 protected:
257 
258     CommandOptions m_options;
259 };
260 
261 
262 lldb::OptionDefinition
263 CommandObjectProcessLaunch::CommandOptions::g_option_table[] =
264 {
265 { 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."},
266 { LLDB_OPT_SET_1, false, "stdin",         'i', required_argument, NULL, 0, "<path>",    "Redirect stdin for the process to <path>."},
267 { LLDB_OPT_SET_1, false, "stdout",        'o', required_argument, NULL, 0, "<path>",    "Redirect stdout for the process to <path>."},
268 { LLDB_OPT_SET_1, false, "stderr",        'e', required_argument, NULL, 0, "<path>",    "Redirect stderr for the process to <path>."},
269 { LLDB_OPT_SET_1, false, "plugin",        'p', required_argument, NULL, 0, "<plugin>",  "Name of the process plugin you want to use."},
270 { 0, false, NULL, 0, 0, NULL, 0, NULL, NULL }
271 };
272 
273 
274 //-------------------------------------------------------------------------
275 // CommandObjectProcessAttach
276 //-------------------------------------------------------------------------
277 
278 class CommandObjectProcessAttach : public CommandObject
279 {
280 public:
281 
282     CommandObjectProcessAttach () :
283         CommandObject ("process attach",
284                        "Attaches to a process.",
285                        "process attach <cmd-options>")
286     {
287         SetHelpLong("Currently, you must set the executable file before you can attach "
288                     "to a process.\n");
289     }
290 
291     ~CommandObjectProcessAttach ()
292     {
293     }
294 
295     bool
296     Execute (CommandInterpreter &interpreter,
297              Args& command,
298              CommandReturnObject &result)
299     {
300         Target *target = interpreter.GetDebugger().GetCurrentTarget().get();
301         if (target == NULL)
302         {
303             result.AppendError ("invalid target, set executable file using 'file' command");
304             result.SetStatus (eReturnStatusFailed);
305             return false;
306         }
307 
308         // If our listener is NULL, users aren't allows to launch
309 
310         Process *process = interpreter.GetDebugger().GetExecutionContext().process;
311         if (process)
312         {
313             if (process->IsAlive())
314             {
315                 result.AppendErrorWithFormat ("Process %u is currently being debugged, kill the process before attaching.\n", process->GetID());
316                 result.SetStatus (eReturnStatusFailed);
317                 return false;
318             }
319         }
320 
321         if (command.GetArgumentCount())
322         {
323             result.AppendErrorWithFormat("Invalid arguments for '%s'.\nUsage: \n", m_cmd_name.c_str(), m_cmd_syntax.c_str());
324             result.SetStatus (eReturnStatusFailed);
325         }
326         else
327         {
328             const char *plugin_name = NULL;
329 
330             if (!m_options.plugin_name.empty())
331                 plugin_name = m_options.plugin_name.c_str();
332 
333             process = target->CreateProcess (interpreter.GetDebugger().GetListener(), plugin_name).get();
334 
335             if (process)
336             {
337                 Error error;
338                 int attach_pid = m_options.pid;
339 
340                 if (attach_pid != LLDB_INVALID_PROCESS_ID)
341                 {
342                     error = process->Attach (attach_pid);
343                     if (error.Success())
344                     {
345                         result.SetStatus (eReturnStatusSuccessContinuingNoResult);
346                     }
347                     else
348                     {
349                         result.AppendErrorWithFormat ("Attaching to process %i failed: %s.\n",
350                                                      attach_pid,
351                                                      error.AsCString());
352                         result.SetStatus (eReturnStatusFailed);
353                     }
354                 }
355                 else if (!m_options.name.empty())
356                 {
357                     error = process->Attach (m_options.name.c_str(), m_options.waitfor);
358                     if (error.Success())
359                     {
360                         result.SetStatus (eReturnStatusSuccessContinuingNoResult);
361                     }
362                     else
363                     {
364                         if (m_options.waitfor)
365                             result.AppendErrorWithFormat ("Waiting for a process to launch named '%s': %s\n",
366                                                          m_options.name.c_str(),
367                                                          error.AsCString());
368                         else
369                             result.AppendErrorWithFormat ("Failed to a process named '%s': %s\n",
370                                                          m_options.name.c_str(),
371                                                          error.AsCString());
372                         result.SetStatus (eReturnStatusFailed);
373                     }
374                 }
375             }
376         }
377         return result.Succeeded();
378     }
379 
380     Options *
381     GetOptions ()
382     {
383         return &m_options;
384     }
385 
386     class CommandOptions : public Options
387     {
388     public:
389 
390         CommandOptions () :
391             Options()
392         {
393             // Keep default values of all options in one place: ResetOptionValues ()
394             ResetOptionValues ();
395         }
396 
397         ~CommandOptions ()
398         {
399         }
400 
401         Error
402         SetOptionValue (int option_idx, const char *option_arg)
403         {
404             Error error;
405             char short_option = (char) m_getopt_table[option_idx].val;
406             bool success = false;
407             switch (short_option)
408             {
409                 case 'p':
410                     pid = Args::StringToUInt32 (option_arg, LLDB_INVALID_PROCESS_ID, 0, &success);
411                     if (!success || pid == LLDB_INVALID_PROCESS_ID)
412                     {
413                         error.SetErrorStringWithFormat("Invalid process ID '%s'.\n", option_arg);
414                     }
415                     break;
416 
417                 case 'P':
418                     plugin_name = option_arg;
419                     break;
420 
421                 case 'n':
422                     name.assign(option_arg);
423                     break;
424 
425                 case 'w':
426                     waitfor = true;
427                     break;
428 
429                 default:
430                     error.SetErrorStringWithFormat("Invalid short option character '%c'.\n", short_option);
431                     break;
432             }
433             return error;
434         }
435 
436         void
437         ResetOptionValues ()
438         {
439             Options::ResetOptionValues();
440             pid = LLDB_INVALID_PROCESS_ID;
441             name.clear();
442             waitfor = false;
443         }
444 
445         const lldb::OptionDefinition*
446         GetDefinitions ()
447         {
448             return g_option_table;
449         }
450 
451         // Options table: Required for subclasses of Options.
452 
453         static lldb::OptionDefinition g_option_table[];
454 
455         // Instance variables to hold the values for command options.
456 
457         lldb::pid_t pid;
458         std::string plugin_name;
459         std::string name;
460         bool waitfor;
461     };
462 
463 protected:
464 
465     CommandOptions m_options;
466 };
467 
468 
469 lldb::OptionDefinition
470 CommandObjectProcessAttach::CommandOptions::g_option_table[] =
471 {
472 { LLDB_OPT_SET_ALL, false, "plugin",       'P', required_argument, NULL, 0, "<plugin>",        "Name of the process plugin you want to use."},
473 { LLDB_OPT_SET_1, false, "pid",          'p', required_argument, NULL, 0, "<pid>",           "The process ID of an existing process to attach to."},
474 { LLDB_OPT_SET_2, true,  "name",         'n', required_argument, NULL, 0, "<process-name>",  "The name of the process to attach to."},
475 { LLDB_OPT_SET_2, false, "waitfor",      'w', no_argument,       NULL, 0, NULL,              "Wait for the the process with <process-name> to launch."},
476 { 0, false, NULL, 0, 0, NULL, 0, NULL, NULL }
477 };
478 
479 //-------------------------------------------------------------------------
480 // CommandObjectProcessContinue
481 //-------------------------------------------------------------------------
482 
483 class CommandObjectProcessContinue : public CommandObject
484 {
485 public:
486 
487     CommandObjectProcessContinue () :
488         CommandObject ("process continue",
489                        "Continues execution all threads in the current process.",
490                        "process continue",
491                        eFlagProcessMustBeLaunched | eFlagProcessMustBePaused)
492     {
493     }
494 
495 
496     ~CommandObjectProcessContinue ()
497     {
498     }
499 
500     bool
501     Execute (CommandInterpreter &interpreter,
502              Args& command,
503              CommandReturnObject &result)
504     {
505         Process *process = interpreter.GetDebugger().GetExecutionContext().process;
506         bool synchronous_execution = interpreter.GetSynchronous ();
507 
508         if (process == NULL)
509         {
510             result.AppendError ("no process to continue");
511             result.SetStatus (eReturnStatusFailed);
512             return false;
513          }
514 
515         StateType state = process->GetState();
516         if (state == eStateStopped)
517         {
518             if (command.GetArgumentCount() != 0)
519             {
520                 result.AppendErrorWithFormat ("The '%s' command does not take any arguments.\n", m_cmd_name.c_str());
521                 result.SetStatus (eReturnStatusFailed);
522                 return false;
523             }
524 
525             const uint32_t num_threads = process->GetThreadList().GetSize();
526 
527             // Set the actions that the threads should each take when resuming
528             for (uint32_t idx=0; idx<num_threads; ++idx)
529             {
530                 process->GetThreadList().GetThreadAtIndex(idx)->SetResumeState (eStateRunning);
531             }
532 
533             Error error(process->Resume());
534             if (error.Success())
535             {
536                 result.AppendMessageWithFormat ("Resuming process %i\n", process->GetID());
537                 if (synchronous_execution)
538                 {
539                     state = process->WaitForProcessToStop (NULL);
540 
541                     result.SetDidChangeProcessState (true);
542                     result.AppendMessageWithFormat ("Process %i %s\n", process->GetID(), StateAsCString (state));
543                     result.SetStatus (eReturnStatusSuccessFinishNoResult);
544                 }
545                 else
546                 {
547                     result.SetStatus (eReturnStatusSuccessContinuingNoResult);
548                 }
549             }
550             else
551             {
552                 result.AppendErrorWithFormat("Failed to resume process: %s.\n", error.AsCString());
553                 result.SetStatus (eReturnStatusFailed);
554             }
555         }
556         else
557         {
558             result.AppendErrorWithFormat ("Process cannot be continued from its current state (%s).\n",
559                                          StateAsCString(state));
560             result.SetStatus (eReturnStatusFailed);
561         }
562         return result.Succeeded();
563     }
564 };
565 
566 //-------------------------------------------------------------------------
567 // CommandObjectProcessDetach
568 //-------------------------------------------------------------------------
569 
570 class CommandObjectProcessDetach : public CommandObject
571 {
572 public:
573 
574     CommandObjectProcessDetach () :
575         CommandObject ("process detach",
576                        "Detaches from the current process being debugged.",
577                        "process detach",
578                        eFlagProcessMustBeLaunched)
579     {
580     }
581 
582     ~CommandObjectProcessDetach ()
583     {
584     }
585 
586     bool
587     Execute (CommandInterpreter &interpreter,
588              Args& command,
589              CommandReturnObject &result)
590     {
591         Process *process = interpreter.GetDebugger().GetExecutionContext().process;
592         if (process == NULL)
593         {
594             result.AppendError ("must have a valid process in order to detach");
595             result.SetStatus (eReturnStatusFailed);
596             return false;
597         }
598 
599         Error error (process->Detach());
600         if (error.Success())
601         {
602             result.SetStatus (eReturnStatusSuccessFinishResult);
603         }
604         else
605         {
606             result.AppendErrorWithFormat ("Detach failed: %s\n", error.AsCString());
607             result.SetStatus (eReturnStatusFailed);
608             return false;
609         }
610         return result.Succeeded();
611     }
612 };
613 
614 //-------------------------------------------------------------------------
615 // CommandObjectProcessSignal
616 //-------------------------------------------------------------------------
617 
618 class CommandObjectProcessSignal : public CommandObject
619 {
620 public:
621 
622     CommandObjectProcessSignal () :
623         CommandObject ("process signal",
624                        "Sends a UNIX signal to the current process being debugged.",
625                        "process signal <unix-signal-number>")
626     {
627     }
628 
629     ~CommandObjectProcessSignal ()
630     {
631     }
632 
633     bool
634     Execute (CommandInterpreter &interpreter,
635              Args& command,
636              CommandReturnObject &result)
637     {
638         Process *process = interpreter.GetDebugger().GetExecutionContext().process;
639         if (process == NULL)
640         {
641             result.AppendError ("no process to signal");
642             result.SetStatus (eReturnStatusFailed);
643             return false;
644         }
645 
646         if (command.GetArgumentCount() == 1)
647         {
648             int signo = Args::StringToSInt32(command.GetArgumentAtIndex(0), -1, 0);
649             if (signo == -1)
650             {
651                 result.AppendErrorWithFormat ("Invalid signal argument '%s'.\n", command.GetArgumentAtIndex(0));
652                 result.SetStatus (eReturnStatusFailed);
653             }
654             else
655             {
656                 Error error (process->Signal (signo));
657                 if (error.Success())
658                 {
659                     result.SetStatus (eReturnStatusSuccessFinishResult);
660                 }
661                 else
662                 {
663                     result.AppendErrorWithFormat ("Failed to send signal %i: %s\n", signo, error.AsCString());
664                     result.SetStatus (eReturnStatusFailed);
665                 }
666             }
667         }
668         else
669         {
670             result.AppendErrorWithFormat("'%s' takes exactly one signal number argument:\nUsage: \n", m_cmd_name.c_str(),
671                                         m_cmd_syntax.c_str());
672             result.SetStatus (eReturnStatusFailed);
673         }
674         return result.Succeeded();
675     }
676 };
677 
678 
679 //-------------------------------------------------------------------------
680 // CommandObjectProcessInterrupt
681 //-------------------------------------------------------------------------
682 
683 class CommandObjectProcessInterrupt : public CommandObject
684 {
685 public:
686 
687 
688     CommandObjectProcessInterrupt () :
689     CommandObject ("process interrupt",
690                    "Interrupts the current process being debugged.",
691                    "process interrupt",
692                    eFlagProcessMustBeLaunched)
693     {
694     }
695 
696     ~CommandObjectProcessInterrupt ()
697     {
698     }
699 
700     bool
701     Execute (CommandInterpreter &interpreter,
702              Args& command,
703              CommandReturnObject &result)
704     {
705         Process *process = interpreter.GetDebugger().GetExecutionContext().process;
706         if (process == NULL)
707         {
708             result.AppendError ("no process to halt");
709             result.SetStatus (eReturnStatusFailed);
710             return false;
711         }
712 
713         if (command.GetArgumentCount() == 0)
714         {
715             Error error(process->Halt ());
716             if (error.Success())
717             {
718                 result.SetStatus (eReturnStatusSuccessFinishResult);
719 
720                 // Maybe we should add a "SuspendThreadPlans so we
721                 // can halt, and keep in place all the current thread plans.
722                 process->GetThreadList().DiscardThreadPlans();
723             }
724             else
725             {
726                 result.AppendErrorWithFormat ("Failed to halt process: %s\n", error.AsCString());
727                 result.SetStatus (eReturnStatusFailed);
728             }
729         }
730         else
731         {
732             result.AppendErrorWithFormat("'%s' takes no arguments:\nUsage: \n",
733                                         m_cmd_name.c_str(),
734                                         m_cmd_syntax.c_str());
735             result.SetStatus (eReturnStatusFailed);
736         }
737         return result.Succeeded();
738     }
739 };
740 
741 //-------------------------------------------------------------------------
742 // CommandObjectProcessKill
743 //-------------------------------------------------------------------------
744 
745 class CommandObjectProcessKill : public CommandObject
746 {
747 public:
748 
749     CommandObjectProcessKill () :
750     CommandObject ("process kill",
751                    "Terminates the current process being debugged.",
752                    "process kill",
753                    eFlagProcessMustBeLaunched)
754     {
755     }
756 
757     ~CommandObjectProcessKill ()
758     {
759     }
760 
761     bool
762     Execute (CommandInterpreter &interpreter,
763              Args& command,
764              CommandReturnObject &result)
765     {
766         Process *process = interpreter.GetDebugger().GetExecutionContext().process;
767         if (process == NULL)
768         {
769             result.AppendError ("no process to kill");
770             result.SetStatus (eReturnStatusFailed);
771             return false;
772         }
773 
774         if (command.GetArgumentCount() == 0)
775         {
776             Error error (process->Destroy());
777             if (error.Success())
778             {
779                 result.SetStatus (eReturnStatusSuccessFinishResult);
780             }
781             else
782             {
783                 result.AppendErrorWithFormat ("Failed to kill process: %s\n", error.AsCString());
784                 result.SetStatus (eReturnStatusFailed);
785             }
786         }
787         else
788         {
789             result.AppendErrorWithFormat("'%s' takes no arguments:\nUsage: \n",
790                                         m_cmd_name.c_str(),
791                                         m_cmd_syntax.c_str());
792             result.SetStatus (eReturnStatusFailed);
793         }
794         return result.Succeeded();
795     }
796 };
797 
798 //-------------------------------------------------------------------------
799 // CommandObjectProcessStatus
800 //-------------------------------------------------------------------------
801 class CommandObjectProcessStatus : public CommandObject
802 {
803 public:
804     CommandObjectProcessStatus () :
805     CommandObject ("status",
806                    "Shows the current status and location of executing process.",
807                    "status",
808                    0)
809     {
810     }
811 
812     ~CommandObjectProcessStatus()
813     {
814     }
815 
816 
817     bool
818     Execute
819     (
820         CommandInterpreter &interpreter,
821         Args& command,
822         CommandReturnObject &result
823     )
824     {
825         StreamString &output_stream = result.GetOutputStream();
826         result.SetStatus (eReturnStatusSuccessFinishNoResult);
827         ExecutionContext exe_ctx(interpreter.GetDebugger().GetExecutionContext());
828         if (exe_ctx.process)
829         {
830             const StateType state = exe_ctx.process->GetState();
831             if (StateIsStoppedState(state))
832             {
833                 if (state == eStateExited)
834                 {
835                     int exit_status = exe_ctx.process->GetExitStatus();
836                     const char *exit_description = exe_ctx.process->GetExitDescription();
837                     output_stream.Printf ("Process %d exited with status = %i (0x%8.8x) %s\n",
838                                           exe_ctx.process->GetID(),
839                                           exit_status,
840                                           exit_status,
841                                           exit_description ? exit_description : "");
842                 }
843                 else
844                 {
845                     output_stream.Printf ("Process %d %s\n", exe_ctx.process->GetID(), StateAsCString (state));
846                     if (exe_ctx.thread == NULL)
847                         exe_ctx.thread = exe_ctx.process->GetThreadList().GetThreadAtIndex(0).get();
848                     if (exe_ctx.thread != NULL)
849                     {
850                         DisplayThreadsInfo (interpreter, &exe_ctx, result, true, true);
851                     }
852                     else
853                     {
854                         result.AppendError ("No valid thread found in current process.");
855                         result.SetStatus (eReturnStatusFailed);
856                     }
857                 }
858             }
859             else
860             {
861                 output_stream.Printf ("Process %d is running.\n",
862                                           exe_ctx.process->GetID());
863             }
864         }
865         else
866         {
867             result.AppendError ("No current location or status available.");
868             result.SetStatus (eReturnStatusFailed);
869         }
870         return result.Succeeded();
871     }
872 };
873 
874 //-------------------------------------------------------------------------
875 // CommandObjectMultiwordProcess
876 //-------------------------------------------------------------------------
877 
878 CommandObjectMultiwordProcess::CommandObjectMultiwordProcess (CommandInterpreter &interpreter) :
879     CommandObjectMultiword ("process",
880                               "A set of commands for operating on a process.",
881                               "process <subcommand> [<subcommand-options>]")
882 {
883     LoadSubCommand (interpreter, "attach",      CommandObjectSP (new CommandObjectProcessAttach ()));
884     LoadSubCommand (interpreter, "launch",      CommandObjectSP (new CommandObjectProcessLaunch ()));
885     LoadSubCommand (interpreter, "continue",    CommandObjectSP (new CommandObjectProcessContinue ()));
886     LoadSubCommand (interpreter, "detach",      CommandObjectSP (new CommandObjectProcessDetach ()));
887     LoadSubCommand (interpreter, "signal",      CommandObjectSP (new CommandObjectProcessSignal ()));
888     LoadSubCommand (interpreter, "status",      CommandObjectSP (new CommandObjectProcessStatus ()));
889     LoadSubCommand (interpreter, "interrupt",   CommandObjectSP (new CommandObjectProcessInterrupt ()));
890     LoadSubCommand (interpreter, "kill",        CommandObjectSP (new CommandObjectProcessKill ()));
891 }
892 
893 CommandObjectMultiwordProcess::~CommandObjectMultiwordProcess ()
894 {
895 }
896 
897