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