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