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