1 //===-- CommandObjectPlatform.cpp -----------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "CommandObjectPlatform.h" 10 #include "CommandOptionsProcessAttach.h" 11 #include "CommandOptionsProcessLaunch.h" 12 #include "lldb/Core/Debugger.h" 13 #include "lldb/Core/Module.h" 14 #include "lldb/Core/PluginManager.h" 15 #include "lldb/Host/OptionParser.h" 16 #include "lldb/Interpreter/CommandInterpreter.h" 17 #include "lldb/Interpreter/CommandOptionArgumentTable.h" 18 #include "lldb/Interpreter/CommandOptionValidators.h" 19 #include "lldb/Interpreter/CommandReturnObject.h" 20 #include "lldb/Interpreter/OptionGroupFile.h" 21 #include "lldb/Interpreter/OptionGroupPlatform.h" 22 #include "lldb/Interpreter/OptionGroupPythonClassWithDict.h" 23 #include "lldb/Target/ExecutionContext.h" 24 #include "lldb/Target/Platform.h" 25 #include "lldb/Target/Process.h" 26 #include "lldb/Utility/Args.h" 27 #include "lldb/Utility/ScriptedMetadata.h" 28 #include "lldb/Utility/State.h" 29 30 #include "llvm/ADT/SmallString.h" 31 32 using namespace lldb; 33 using namespace lldb_private; 34 35 static mode_t ParsePermissionString(const char *) = delete; 36 37 static mode_t ParsePermissionString(llvm::StringRef permissions) { 38 if (permissions.size() != 9) 39 return (mode_t)(-1); 40 bool user_r, user_w, user_x, group_r, group_w, group_x, world_r, world_w, 41 world_x; 42 43 user_r = (permissions[0] == 'r'); 44 user_w = (permissions[1] == 'w'); 45 user_x = (permissions[2] == 'x'); 46 47 group_r = (permissions[3] == 'r'); 48 group_w = (permissions[4] == 'w'); 49 group_x = (permissions[5] == 'x'); 50 51 world_r = (permissions[6] == 'r'); 52 world_w = (permissions[7] == 'w'); 53 world_x = (permissions[8] == 'x'); 54 55 mode_t user, group, world; 56 user = (user_r ? 4 : 0) | (user_w ? 2 : 0) | (user_x ? 1 : 0); 57 group = (group_r ? 4 : 0) | (group_w ? 2 : 0) | (group_x ? 1 : 0); 58 world = (world_r ? 4 : 0) | (world_w ? 2 : 0) | (world_x ? 1 : 0); 59 60 return user | group | world; 61 } 62 63 #define LLDB_OPTIONS_permissions 64 #include "CommandOptions.inc" 65 66 class OptionPermissions : public OptionGroup { 67 public: 68 OptionPermissions() = default; 69 70 ~OptionPermissions() override = default; 71 72 lldb_private::Status 73 SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 74 ExecutionContext *execution_context) override { 75 Status error; 76 char short_option = (char)GetDefinitions()[option_idx].short_option; 77 switch (short_option) { 78 case 'v': { 79 if (option_arg.getAsInteger(8, m_permissions)) { 80 m_permissions = 0777; 81 error.SetErrorStringWithFormat("invalid value for permissions: %s", 82 option_arg.str().c_str()); 83 } 84 85 } break; 86 case 's': { 87 mode_t perms = ParsePermissionString(option_arg); 88 if (perms == (mode_t)-1) 89 error.SetErrorStringWithFormat("invalid value for permissions: %s", 90 option_arg.str().c_str()); 91 else 92 m_permissions = perms; 93 } break; 94 case 'r': 95 m_permissions |= lldb::eFilePermissionsUserRead; 96 break; 97 case 'w': 98 m_permissions |= lldb::eFilePermissionsUserWrite; 99 break; 100 case 'x': 101 m_permissions |= lldb::eFilePermissionsUserExecute; 102 break; 103 case 'R': 104 m_permissions |= lldb::eFilePermissionsGroupRead; 105 break; 106 case 'W': 107 m_permissions |= lldb::eFilePermissionsGroupWrite; 108 break; 109 case 'X': 110 m_permissions |= lldb::eFilePermissionsGroupExecute; 111 break; 112 case 'd': 113 m_permissions |= lldb::eFilePermissionsWorldRead; 114 break; 115 case 't': 116 m_permissions |= lldb::eFilePermissionsWorldWrite; 117 break; 118 case 'e': 119 m_permissions |= lldb::eFilePermissionsWorldExecute; 120 break; 121 default: 122 llvm_unreachable("Unimplemented option"); 123 } 124 125 return error; 126 } 127 128 void OptionParsingStarting(ExecutionContext *execution_context) override { 129 m_permissions = 0; 130 } 131 132 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 133 return llvm::ArrayRef(g_permissions_options); 134 } 135 136 // Instance variables to hold the values for command options. 137 138 uint32_t m_permissions; 139 140 private: 141 OptionPermissions(const OptionPermissions &) = delete; 142 const OptionPermissions &operator=(const OptionPermissions &) = delete; 143 }; 144 145 // "platform select <platform-name>" 146 class CommandObjectPlatformSelect : public CommandObjectParsed { 147 public: 148 CommandObjectPlatformSelect(CommandInterpreter &interpreter) 149 : CommandObjectParsed(interpreter, "platform select", 150 "Create a platform if needed and select it as the " 151 "current platform.", 152 "platform select <platform-name>", 0), 153 m_platform_options( 154 false) // Don't include the "--platform" option by passing false 155 { 156 m_option_group.Append(&m_platform_options, LLDB_OPT_SET_ALL, 1); 157 m_option_group.Finalize(); 158 CommandArgumentData platform_arg{eArgTypePlatform, eArgRepeatPlain}; 159 m_arguments.push_back({platform_arg}); 160 } 161 162 ~CommandObjectPlatformSelect() override = default; 163 164 void HandleCompletion(CompletionRequest &request) override { 165 lldb_private::CommandCompletions::PlatformPluginNames( 166 GetCommandInterpreter(), request, nullptr); 167 } 168 169 Options *GetOptions() override { return &m_option_group; } 170 171 protected: 172 bool DoExecute(Args &args, CommandReturnObject &result) override { 173 if (args.GetArgumentCount() == 1) { 174 const char *platform_name = args.GetArgumentAtIndex(0); 175 if (platform_name && platform_name[0]) { 176 const bool select = true; 177 m_platform_options.SetPlatformName(platform_name); 178 Status error; 179 ArchSpec platform_arch; 180 PlatformSP platform_sp(m_platform_options.CreatePlatformWithOptions( 181 m_interpreter, ArchSpec(), select, error, platform_arch)); 182 if (platform_sp) { 183 GetDebugger().GetPlatformList().SetSelectedPlatform(platform_sp); 184 185 platform_sp->GetStatus(result.GetOutputStream()); 186 result.SetStatus(eReturnStatusSuccessFinishResult); 187 } else { 188 result.AppendError(error.AsCString()); 189 } 190 } else { 191 result.AppendError("invalid platform name"); 192 } 193 } else { 194 result.AppendError( 195 "platform create takes a platform name as an argument\n"); 196 } 197 return result.Succeeded(); 198 } 199 200 OptionGroupOptions m_option_group; 201 OptionGroupPlatform m_platform_options; 202 }; 203 204 // "platform list" 205 class CommandObjectPlatformList : public CommandObjectParsed { 206 public: 207 CommandObjectPlatformList(CommandInterpreter &interpreter) 208 : CommandObjectParsed(interpreter, "platform list", 209 "List all platforms that are available.", nullptr, 210 0) {} 211 212 ~CommandObjectPlatformList() override = default; 213 214 protected: 215 bool DoExecute(Args &args, CommandReturnObject &result) override { 216 Stream &ostrm = result.GetOutputStream(); 217 ostrm.Printf("Available platforms:\n"); 218 219 PlatformSP host_platform_sp(Platform::GetHostPlatform()); 220 ostrm.Format("{0}: {1}\n", host_platform_sp->GetPluginName(), 221 host_platform_sp->GetDescription()); 222 223 uint32_t idx; 224 for (idx = 0; true; ++idx) { 225 llvm::StringRef plugin_name = 226 PluginManager::GetPlatformPluginNameAtIndex(idx); 227 if (plugin_name.empty()) 228 break; 229 llvm::StringRef plugin_desc = 230 PluginManager::GetPlatformPluginDescriptionAtIndex(idx); 231 ostrm.Format("{0}: {1}\n", plugin_name, plugin_desc); 232 } 233 234 if (idx == 0) { 235 result.AppendError("no platforms are available\n"); 236 } else 237 result.SetStatus(eReturnStatusSuccessFinishResult); 238 return result.Succeeded(); 239 } 240 }; 241 242 // "platform status" 243 class CommandObjectPlatformStatus : public CommandObjectParsed { 244 public: 245 CommandObjectPlatformStatus(CommandInterpreter &interpreter) 246 : CommandObjectParsed(interpreter, "platform status", 247 "Display status for the current platform.", nullptr, 248 0) {} 249 250 ~CommandObjectPlatformStatus() override = default; 251 252 protected: 253 bool DoExecute(Args &args, CommandReturnObject &result) override { 254 Stream &ostrm = result.GetOutputStream(); 255 256 Target *target = GetDebugger().GetSelectedTarget().get(); 257 PlatformSP platform_sp; 258 if (target) { 259 platform_sp = target->GetPlatform(); 260 } 261 if (!platform_sp) { 262 platform_sp = GetDebugger().GetPlatformList().GetSelectedPlatform(); 263 } 264 if (platform_sp) { 265 platform_sp->GetStatus(ostrm); 266 result.SetStatus(eReturnStatusSuccessFinishResult); 267 } else { 268 result.AppendError("no platform is currently selected\n"); 269 } 270 return result.Succeeded(); 271 } 272 }; 273 274 // "platform connect <connect-url>" 275 class CommandObjectPlatformConnect : public CommandObjectParsed { 276 public: 277 CommandObjectPlatformConnect(CommandInterpreter &interpreter) 278 : CommandObjectParsed( 279 interpreter, "platform connect", 280 "Select the current platform by providing a connection URL.", 281 "platform connect <connect-url>", 0) { 282 CommandArgumentData platform_arg{eArgTypeConnectURL, eArgRepeatPlain}; 283 m_arguments.push_back({platform_arg}); 284 } 285 286 ~CommandObjectPlatformConnect() override = default; 287 288 protected: 289 bool DoExecute(Args &args, CommandReturnObject &result) override { 290 Stream &ostrm = result.GetOutputStream(); 291 292 PlatformSP platform_sp( 293 GetDebugger().GetPlatformList().GetSelectedPlatform()); 294 if (platform_sp) { 295 Status error(platform_sp->ConnectRemote(args)); 296 if (error.Success()) { 297 platform_sp->GetStatus(ostrm); 298 result.SetStatus(eReturnStatusSuccessFinishResult); 299 300 platform_sp->ConnectToWaitingProcesses(GetDebugger(), error); 301 if (error.Fail()) { 302 result.AppendError(error.AsCString()); 303 } 304 } else { 305 result.AppendErrorWithFormat("%s\n", error.AsCString()); 306 } 307 } else { 308 result.AppendError("no platform is currently selected\n"); 309 } 310 return result.Succeeded(); 311 } 312 313 Options *GetOptions() override { 314 PlatformSP platform_sp( 315 GetDebugger().GetPlatformList().GetSelectedPlatform()); 316 OptionGroupOptions *m_platform_options = nullptr; 317 if (platform_sp) { 318 m_platform_options = platform_sp->GetConnectionOptions(m_interpreter); 319 if (m_platform_options != nullptr && !m_platform_options->m_did_finalize) 320 m_platform_options->Finalize(); 321 } 322 return m_platform_options; 323 } 324 }; 325 326 // "platform disconnect" 327 class CommandObjectPlatformDisconnect : public CommandObjectParsed { 328 public: 329 CommandObjectPlatformDisconnect(CommandInterpreter &interpreter) 330 : CommandObjectParsed(interpreter, "platform disconnect", 331 "Disconnect from the current platform.", 332 "platform disconnect", 0) {} 333 334 ~CommandObjectPlatformDisconnect() override = default; 335 336 protected: 337 bool DoExecute(Args &args, CommandReturnObject &result) override { 338 PlatformSP platform_sp( 339 GetDebugger().GetPlatformList().GetSelectedPlatform()); 340 if (platform_sp) { 341 if (args.GetArgumentCount() == 0) { 342 Status error; 343 344 if (platform_sp->IsConnected()) { 345 // Cache the instance name if there is one since we are about to 346 // disconnect and the name might go with it. 347 const char *hostname_cstr = platform_sp->GetHostname(); 348 std::string hostname; 349 if (hostname_cstr) 350 hostname.assign(hostname_cstr); 351 352 error = platform_sp->DisconnectRemote(); 353 if (error.Success()) { 354 Stream &ostrm = result.GetOutputStream(); 355 if (hostname.empty()) 356 ostrm.Format("Disconnected from \"{0}\"\n", 357 platform_sp->GetPluginName()); 358 else 359 ostrm.Printf("Disconnected from \"%s\"\n", hostname.c_str()); 360 result.SetStatus(eReturnStatusSuccessFinishResult); 361 } else { 362 result.AppendErrorWithFormat("%s", error.AsCString()); 363 } 364 } else { 365 // Not connected... 366 result.AppendErrorWithFormatv("not connected to '{0}'", 367 platform_sp->GetPluginName()); 368 } 369 } else { 370 // Bad args 371 result.AppendError( 372 "\"platform disconnect\" doesn't take any arguments"); 373 } 374 } else { 375 result.AppendError("no platform is currently selected"); 376 } 377 return result.Succeeded(); 378 } 379 }; 380 381 // "platform settings" 382 class CommandObjectPlatformSettings : public CommandObjectParsed { 383 public: 384 CommandObjectPlatformSettings(CommandInterpreter &interpreter) 385 : CommandObjectParsed(interpreter, "platform settings", 386 "Set settings for the current target's platform.", 387 "platform settings", 0), 388 m_option_working_dir(LLDB_OPT_SET_1, false, "working-dir", 'w', 389 lldb::eRemoteDiskDirectoryCompletion, eArgTypePath, 390 "The working directory for the platform.") { 391 m_options.Append(&m_option_working_dir, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); 392 } 393 394 ~CommandObjectPlatformSettings() override = default; 395 396 protected: 397 bool DoExecute(Args &args, CommandReturnObject &result) override { 398 PlatformSP platform_sp( 399 GetDebugger().GetPlatformList().GetSelectedPlatform()); 400 if (platform_sp) { 401 if (m_option_working_dir.GetOptionValue().OptionWasSet()) 402 platform_sp->SetWorkingDirectory( 403 m_option_working_dir.GetOptionValue().GetCurrentValue()); 404 } else { 405 result.AppendError("no platform is currently selected"); 406 } 407 return result.Succeeded(); 408 } 409 410 Options *GetOptions() override { 411 if (!m_options.DidFinalize()) 412 m_options.Finalize(); 413 return &m_options; 414 } 415 416 OptionGroupOptions m_options; 417 OptionGroupFile m_option_working_dir; 418 }; 419 420 // "platform mkdir" 421 class CommandObjectPlatformMkDir : public CommandObjectParsed { 422 public: 423 CommandObjectPlatformMkDir(CommandInterpreter &interpreter) 424 : CommandObjectParsed(interpreter, "platform mkdir", 425 "Make a new directory on the remote end.", nullptr, 426 0) { 427 CommandArgumentData thread_arg{eArgTypePath, eArgRepeatPlain}; 428 m_arguments.push_back({thread_arg}); 429 } 430 431 ~CommandObjectPlatformMkDir() override = default; 432 433 bool DoExecute(Args &args, CommandReturnObject &result) override { 434 PlatformSP platform_sp( 435 GetDebugger().GetPlatformList().GetSelectedPlatform()); 436 if (platform_sp) { 437 std::string cmd_line; 438 args.GetCommandString(cmd_line); 439 uint32_t mode; 440 const OptionPermissions *options_permissions = 441 (const OptionPermissions *)m_options.GetGroupWithOption('r'); 442 if (options_permissions) 443 mode = options_permissions->m_permissions; 444 else 445 mode = lldb::eFilePermissionsUserRWX | lldb::eFilePermissionsGroupRWX | 446 lldb::eFilePermissionsWorldRX; 447 Status error = platform_sp->MakeDirectory(FileSpec(cmd_line), mode); 448 if (error.Success()) { 449 result.SetStatus(eReturnStatusSuccessFinishResult); 450 } else { 451 result.AppendError(error.AsCString()); 452 } 453 } else { 454 result.AppendError("no platform currently selected\n"); 455 } 456 return result.Succeeded(); 457 } 458 459 Options *GetOptions() override { 460 if (!m_options.DidFinalize()) { 461 m_options.Append(&m_option_permissions); 462 m_options.Finalize(); 463 } 464 return &m_options; 465 } 466 467 OptionPermissions m_option_permissions; 468 OptionGroupOptions m_options; 469 }; 470 471 // "platform fopen" 472 class CommandObjectPlatformFOpen : public CommandObjectParsed { 473 public: 474 CommandObjectPlatformFOpen(CommandInterpreter &interpreter) 475 : CommandObjectParsed(interpreter, "platform file open", 476 "Open a file on the remote end.", nullptr, 0) { 477 CommandArgumentData path_arg{eArgTypePath, eArgRepeatPlain}; 478 m_arguments.push_back({path_arg}); 479 } 480 481 ~CommandObjectPlatformFOpen() override = default; 482 483 void 484 HandleArgumentCompletion(CompletionRequest &request, 485 OptionElementVector &opt_element_vector) override { 486 if (request.GetCursorIndex() == 0) 487 lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( 488 GetCommandInterpreter(), lldb::eRemoteDiskFileCompletion, request, 489 nullptr); 490 } 491 492 bool DoExecute(Args &args, CommandReturnObject &result) override { 493 PlatformSP platform_sp( 494 GetDebugger().GetPlatformList().GetSelectedPlatform()); 495 if (platform_sp) { 496 Status error; 497 std::string cmd_line; 498 args.GetCommandString(cmd_line); 499 mode_t perms; 500 const OptionPermissions *options_permissions = 501 (const OptionPermissions *)m_options.GetGroupWithOption('r'); 502 if (options_permissions) 503 perms = options_permissions->m_permissions; 504 else 505 perms = lldb::eFilePermissionsUserRW | lldb::eFilePermissionsGroupRW | 506 lldb::eFilePermissionsWorldRead; 507 lldb::user_id_t fd = platform_sp->OpenFile( 508 FileSpec(cmd_line), 509 File::eOpenOptionReadWrite | File::eOpenOptionCanCreate, 510 perms, error); 511 if (error.Success()) { 512 result.AppendMessageWithFormat("File Descriptor = %" PRIu64 "\n", fd); 513 result.SetStatus(eReturnStatusSuccessFinishResult); 514 } else { 515 result.AppendError(error.AsCString()); 516 } 517 } else { 518 result.AppendError("no platform currently selected\n"); 519 } 520 return result.Succeeded(); 521 } 522 523 Options *GetOptions() override { 524 if (!m_options.DidFinalize()) { 525 m_options.Append(&m_option_permissions); 526 m_options.Finalize(); 527 } 528 return &m_options; 529 } 530 531 OptionPermissions m_option_permissions; 532 OptionGroupOptions m_options; 533 }; 534 535 // "platform fclose" 536 class CommandObjectPlatformFClose : public CommandObjectParsed { 537 public: 538 CommandObjectPlatformFClose(CommandInterpreter &interpreter) 539 : CommandObjectParsed(interpreter, "platform file close", 540 "Close a file on the remote end.", nullptr, 0) { 541 CommandArgumentData path_arg{eArgTypeUnsignedInteger, eArgRepeatPlain}; 542 m_arguments.push_back({path_arg}); 543 } 544 545 ~CommandObjectPlatformFClose() override = default; 546 547 bool DoExecute(Args &args, CommandReturnObject &result) override { 548 PlatformSP platform_sp( 549 GetDebugger().GetPlatformList().GetSelectedPlatform()); 550 if (platform_sp) { 551 std::string cmd_line; 552 args.GetCommandString(cmd_line); 553 lldb::user_id_t fd; 554 if (!llvm::to_integer(cmd_line, fd)) { 555 result.AppendErrorWithFormatv("'{0}' is not a valid file descriptor.\n", 556 cmd_line); 557 return result.Succeeded(); 558 } 559 Status error; 560 bool success = platform_sp->CloseFile(fd, error); 561 if (success) { 562 result.AppendMessageWithFormat("file %" PRIu64 " closed.\n", fd); 563 result.SetStatus(eReturnStatusSuccessFinishResult); 564 } else { 565 result.AppendError(error.AsCString()); 566 } 567 } else { 568 result.AppendError("no platform currently selected\n"); 569 } 570 return result.Succeeded(); 571 } 572 }; 573 574 // "platform fread" 575 576 #define LLDB_OPTIONS_platform_fread 577 #include "CommandOptions.inc" 578 579 class CommandObjectPlatformFRead : public CommandObjectParsed { 580 public: 581 CommandObjectPlatformFRead(CommandInterpreter &interpreter) 582 : CommandObjectParsed(interpreter, "platform file read", 583 "Read data from a file on the remote end.", nullptr, 584 0) { 585 CommandArgumentData path_arg{eArgTypeUnsignedInteger, eArgRepeatPlain}; 586 m_arguments.push_back({path_arg}); 587 } 588 589 ~CommandObjectPlatformFRead() override = default; 590 591 bool DoExecute(Args &args, CommandReturnObject &result) override { 592 PlatformSP platform_sp( 593 GetDebugger().GetPlatformList().GetSelectedPlatform()); 594 if (platform_sp) { 595 std::string cmd_line; 596 args.GetCommandString(cmd_line); 597 lldb::user_id_t fd; 598 if (!llvm::to_integer(cmd_line, fd)) { 599 result.AppendErrorWithFormatv("'{0}' is not a valid file descriptor.\n", 600 cmd_line); 601 return result.Succeeded(); 602 } 603 std::string buffer(m_options.m_count, 0); 604 Status error; 605 uint64_t retcode = platform_sp->ReadFile( 606 fd, m_options.m_offset, &buffer[0], m_options.m_count, error); 607 if (retcode != UINT64_MAX) { 608 result.AppendMessageWithFormat("Return = %" PRIu64 "\n", retcode); 609 result.AppendMessageWithFormat("Data = \"%s\"\n", buffer.c_str()); 610 result.SetStatus(eReturnStatusSuccessFinishResult); 611 } else { 612 result.AppendError(error.AsCString()); 613 } 614 } else { 615 result.AppendError("no platform currently selected\n"); 616 } 617 return result.Succeeded(); 618 } 619 620 Options *GetOptions() override { return &m_options; } 621 622 protected: 623 class CommandOptions : public Options { 624 public: 625 CommandOptions() = default; 626 627 ~CommandOptions() override = default; 628 629 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 630 ExecutionContext *execution_context) override { 631 Status error; 632 char short_option = (char)m_getopt_table[option_idx].val; 633 634 switch (short_option) { 635 case 'o': 636 if (option_arg.getAsInteger(0, m_offset)) 637 error.SetErrorStringWithFormat("invalid offset: '%s'", 638 option_arg.str().c_str()); 639 break; 640 case 'c': 641 if (option_arg.getAsInteger(0, m_count)) 642 error.SetErrorStringWithFormat("invalid offset: '%s'", 643 option_arg.str().c_str()); 644 break; 645 default: 646 llvm_unreachable("Unimplemented option"); 647 } 648 649 return error; 650 } 651 652 void OptionParsingStarting(ExecutionContext *execution_context) override { 653 m_offset = 0; 654 m_count = 1; 655 } 656 657 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 658 return llvm::ArrayRef(g_platform_fread_options); 659 } 660 661 // Instance variables to hold the values for command options. 662 663 uint32_t m_offset; 664 uint32_t m_count; 665 }; 666 667 CommandOptions m_options; 668 }; 669 670 // "platform fwrite" 671 672 #define LLDB_OPTIONS_platform_fwrite 673 #include "CommandOptions.inc" 674 675 class CommandObjectPlatformFWrite : public CommandObjectParsed { 676 public: 677 CommandObjectPlatformFWrite(CommandInterpreter &interpreter) 678 : CommandObjectParsed(interpreter, "platform file write", 679 "Write data to a file on the remote end.", nullptr, 680 0) { 681 CommandArgumentData path_arg{eArgTypeUnsignedInteger, eArgRepeatPlain}; 682 m_arguments.push_back({path_arg}); 683 } 684 685 ~CommandObjectPlatformFWrite() override = default; 686 687 bool DoExecute(Args &args, CommandReturnObject &result) override { 688 PlatformSP platform_sp( 689 GetDebugger().GetPlatformList().GetSelectedPlatform()); 690 if (platform_sp) { 691 std::string cmd_line; 692 args.GetCommandString(cmd_line); 693 Status error; 694 lldb::user_id_t fd; 695 if (!llvm::to_integer(cmd_line, fd)) { 696 result.AppendErrorWithFormatv("'{0}' is not a valid file descriptor.", 697 cmd_line); 698 return result.Succeeded(); 699 } 700 uint64_t retcode = 701 platform_sp->WriteFile(fd, m_options.m_offset, &m_options.m_data[0], 702 m_options.m_data.size(), error); 703 if (retcode != UINT64_MAX) { 704 result.AppendMessageWithFormat("Return = %" PRIu64 "\n", retcode); 705 result.SetStatus(eReturnStatusSuccessFinishResult); 706 } else { 707 result.AppendError(error.AsCString()); 708 } 709 } else { 710 result.AppendError("no platform currently selected\n"); 711 } 712 return result.Succeeded(); 713 } 714 715 Options *GetOptions() override { return &m_options; } 716 717 protected: 718 class CommandOptions : public Options { 719 public: 720 CommandOptions() = default; 721 722 ~CommandOptions() override = default; 723 724 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 725 ExecutionContext *execution_context) override { 726 Status error; 727 char short_option = (char)m_getopt_table[option_idx].val; 728 729 switch (short_option) { 730 case 'o': 731 if (option_arg.getAsInteger(0, m_offset)) 732 error.SetErrorStringWithFormat("invalid offset: '%s'", 733 option_arg.str().c_str()); 734 break; 735 case 'd': 736 m_data.assign(std::string(option_arg)); 737 break; 738 default: 739 llvm_unreachable("Unimplemented option"); 740 } 741 742 return error; 743 } 744 745 void OptionParsingStarting(ExecutionContext *execution_context) override { 746 m_offset = 0; 747 m_data.clear(); 748 } 749 750 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 751 return llvm::ArrayRef(g_platform_fwrite_options); 752 } 753 754 // Instance variables to hold the values for command options. 755 756 uint32_t m_offset; 757 std::string m_data; 758 }; 759 760 CommandOptions m_options; 761 }; 762 763 class CommandObjectPlatformFile : public CommandObjectMultiword { 764 public: 765 // Constructors and Destructors 766 CommandObjectPlatformFile(CommandInterpreter &interpreter) 767 : CommandObjectMultiword( 768 interpreter, "platform file", 769 "Commands to access files on the current platform.", 770 "platform file [open|close|read|write] ...") { 771 LoadSubCommand( 772 "open", CommandObjectSP(new CommandObjectPlatformFOpen(interpreter))); 773 LoadSubCommand( 774 "close", CommandObjectSP(new CommandObjectPlatformFClose(interpreter))); 775 LoadSubCommand( 776 "read", CommandObjectSP(new CommandObjectPlatformFRead(interpreter))); 777 LoadSubCommand( 778 "write", CommandObjectSP(new CommandObjectPlatformFWrite(interpreter))); 779 } 780 781 ~CommandObjectPlatformFile() override = default; 782 783 private: 784 // For CommandObjectPlatform only 785 CommandObjectPlatformFile(const CommandObjectPlatformFile &) = delete; 786 const CommandObjectPlatformFile & 787 operator=(const CommandObjectPlatformFile &) = delete; 788 }; 789 790 // "platform get-file remote-file-path host-file-path" 791 class CommandObjectPlatformGetFile : public CommandObjectParsed { 792 public: 793 CommandObjectPlatformGetFile(CommandInterpreter &interpreter) 794 : CommandObjectParsed( 795 interpreter, "platform get-file", 796 "Transfer a file from the remote end to the local host.", 797 "platform get-file <remote-file-spec> <local-file-spec>", 0) { 798 SetHelpLong( 799 R"(Examples: 800 801 (lldb) platform get-file /the/remote/file/path /the/local/file/path 802 803 Transfer a file from the remote end with file path /the/remote/file/path to the local host.)"); 804 805 CommandArgumentEntry arg1, arg2; 806 CommandArgumentData file_arg_remote, file_arg_host; 807 808 // Define the first (and only) variant of this arg. 809 file_arg_remote.arg_type = eArgTypeFilename; 810 file_arg_remote.arg_repetition = eArgRepeatPlain; 811 // There is only one variant this argument could be; put it into the 812 // argument entry. 813 arg1.push_back(file_arg_remote); 814 815 // Define the second (and only) variant of this arg. 816 file_arg_host.arg_type = eArgTypeFilename; 817 file_arg_host.arg_repetition = eArgRepeatPlain; 818 // There is only one variant this argument could be; put it into the 819 // argument entry. 820 arg2.push_back(file_arg_host); 821 822 // Push the data for the first and the second arguments into the 823 // m_arguments vector. 824 m_arguments.push_back(arg1); 825 m_arguments.push_back(arg2); 826 } 827 828 ~CommandObjectPlatformGetFile() override = default; 829 830 void 831 HandleArgumentCompletion(CompletionRequest &request, 832 OptionElementVector &opt_element_vector) override { 833 if (request.GetCursorIndex() == 0) 834 lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( 835 GetCommandInterpreter(), lldb::eRemoteDiskFileCompletion, request, 836 nullptr); 837 else if (request.GetCursorIndex() == 1) 838 lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( 839 GetCommandInterpreter(), lldb::eDiskFileCompletion, request, nullptr); 840 } 841 842 bool DoExecute(Args &args, CommandReturnObject &result) override { 843 // If the number of arguments is incorrect, issue an error message. 844 if (args.GetArgumentCount() != 2) { 845 result.AppendError("required arguments missing; specify both the " 846 "source and destination file paths"); 847 return false; 848 } 849 850 PlatformSP platform_sp( 851 GetDebugger().GetPlatformList().GetSelectedPlatform()); 852 if (platform_sp) { 853 const char *remote_file_path = args.GetArgumentAtIndex(0); 854 const char *local_file_path = args.GetArgumentAtIndex(1); 855 Status error = platform_sp->GetFile(FileSpec(remote_file_path), 856 FileSpec(local_file_path)); 857 if (error.Success()) { 858 result.AppendMessageWithFormat( 859 "successfully get-file from %s (remote) to %s (host)\n", 860 remote_file_path, local_file_path); 861 result.SetStatus(eReturnStatusSuccessFinishResult); 862 } else { 863 result.AppendMessageWithFormat("get-file failed: %s\n", 864 error.AsCString()); 865 } 866 } else { 867 result.AppendError("no platform currently selected\n"); 868 } 869 return result.Succeeded(); 870 } 871 }; 872 873 // "platform get-size remote-file-path" 874 class CommandObjectPlatformGetSize : public CommandObjectParsed { 875 public: 876 CommandObjectPlatformGetSize(CommandInterpreter &interpreter) 877 : CommandObjectParsed(interpreter, "platform get-size", 878 "Get the file size from the remote end.", 879 "platform get-size <remote-file-spec>", 0) { 880 SetHelpLong( 881 R"(Examples: 882 883 (lldb) platform get-size /the/remote/file/path 884 885 Get the file size from the remote end with path /the/remote/file/path.)"); 886 887 CommandArgumentEntry arg1; 888 CommandArgumentData file_arg_remote; 889 890 // Define the first (and only) variant of this arg. 891 file_arg_remote.arg_type = eArgTypeFilename; 892 file_arg_remote.arg_repetition = eArgRepeatPlain; 893 // There is only one variant this argument could be; put it into the 894 // argument entry. 895 arg1.push_back(file_arg_remote); 896 897 // Push the data for the first argument into the m_arguments vector. 898 m_arguments.push_back(arg1); 899 } 900 901 ~CommandObjectPlatformGetSize() override = default; 902 903 void 904 HandleArgumentCompletion(CompletionRequest &request, 905 OptionElementVector &opt_element_vector) override { 906 if (request.GetCursorIndex() != 0) 907 return; 908 909 lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( 910 GetCommandInterpreter(), lldb::eRemoteDiskFileCompletion, request, 911 nullptr); 912 } 913 914 bool DoExecute(Args &args, CommandReturnObject &result) override { 915 // If the number of arguments is incorrect, issue an error message. 916 if (args.GetArgumentCount() != 1) { 917 result.AppendError("required argument missing; specify the source file " 918 "path as the only argument"); 919 return false; 920 } 921 922 PlatformSP platform_sp( 923 GetDebugger().GetPlatformList().GetSelectedPlatform()); 924 if (platform_sp) { 925 std::string remote_file_path(args.GetArgumentAtIndex(0)); 926 user_id_t size = platform_sp->GetFileSize(FileSpec(remote_file_path)); 927 if (size != UINT64_MAX) { 928 result.AppendMessageWithFormat("File size of %s (remote): %" PRIu64 929 "\n", 930 remote_file_path.c_str(), size); 931 result.SetStatus(eReturnStatusSuccessFinishResult); 932 } else { 933 result.AppendMessageWithFormat( 934 "Error getting file size of %s (remote)\n", 935 remote_file_path.c_str()); 936 } 937 } else { 938 result.AppendError("no platform currently selected\n"); 939 } 940 return result.Succeeded(); 941 } 942 }; 943 944 // "platform get-permissions remote-file-path" 945 class CommandObjectPlatformGetPermissions : public CommandObjectParsed { 946 public: 947 CommandObjectPlatformGetPermissions(CommandInterpreter &interpreter) 948 : CommandObjectParsed(interpreter, "platform get-permissions", 949 "Get the file permission bits from the remote end.", 950 "platform get-permissions <remote-file-spec>", 0) { 951 SetHelpLong( 952 R"(Examples: 953 954 (lldb) platform get-permissions /the/remote/file/path 955 956 Get the file permissions from the remote end with path /the/remote/file/path.)"); 957 958 CommandArgumentEntry arg1; 959 CommandArgumentData file_arg_remote; 960 961 // Define the first (and only) variant of this arg. 962 file_arg_remote.arg_type = eArgTypeFilename; 963 file_arg_remote.arg_repetition = eArgRepeatPlain; 964 // There is only one variant this argument could be; put it into the 965 // argument entry. 966 arg1.push_back(file_arg_remote); 967 968 // Push the data for the first argument into the m_arguments vector. 969 m_arguments.push_back(arg1); 970 } 971 972 ~CommandObjectPlatformGetPermissions() override = default; 973 974 void 975 HandleArgumentCompletion(CompletionRequest &request, 976 OptionElementVector &opt_element_vector) override { 977 if (request.GetCursorIndex() != 0) 978 return; 979 980 lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( 981 GetCommandInterpreter(), lldb::eRemoteDiskFileCompletion, request, 982 nullptr); 983 } 984 985 bool DoExecute(Args &args, CommandReturnObject &result) override { 986 // If the number of arguments is incorrect, issue an error message. 987 if (args.GetArgumentCount() != 1) { 988 result.AppendError("required argument missing; specify the source file " 989 "path as the only argument"); 990 return false; 991 } 992 993 PlatformSP platform_sp( 994 GetDebugger().GetPlatformList().GetSelectedPlatform()); 995 if (platform_sp) { 996 std::string remote_file_path(args.GetArgumentAtIndex(0)); 997 uint32_t permissions; 998 Status error = platform_sp->GetFilePermissions(FileSpec(remote_file_path), 999 permissions); 1000 if (error.Success()) { 1001 result.AppendMessageWithFormat( 1002 "File permissions of %s (remote): 0o%04" PRIo32 "\n", 1003 remote_file_path.c_str(), permissions); 1004 result.SetStatus(eReturnStatusSuccessFinishResult); 1005 } else 1006 result.AppendError(error.AsCString()); 1007 } else { 1008 result.AppendError("no platform currently selected\n"); 1009 } 1010 return result.Succeeded(); 1011 } 1012 }; 1013 1014 // "platform file-exists remote-file-path" 1015 class CommandObjectPlatformFileExists : public CommandObjectParsed { 1016 public: 1017 CommandObjectPlatformFileExists(CommandInterpreter &interpreter) 1018 : CommandObjectParsed(interpreter, "platform file-exists", 1019 "Check if the file exists on the remote end.", 1020 "platform file-exists <remote-file-spec>", 0) { 1021 SetHelpLong( 1022 R"(Examples: 1023 1024 (lldb) platform file-exists /the/remote/file/path 1025 1026 Check if /the/remote/file/path exists on the remote end.)"); 1027 1028 CommandArgumentEntry arg1; 1029 CommandArgumentData file_arg_remote; 1030 1031 // Define the first (and only) variant of this arg. 1032 file_arg_remote.arg_type = eArgTypeFilename; 1033 file_arg_remote.arg_repetition = eArgRepeatPlain; 1034 // There is only one variant this argument could be; put it into the 1035 // argument entry. 1036 arg1.push_back(file_arg_remote); 1037 1038 // Push the data for the first argument into the m_arguments vector. 1039 m_arguments.push_back(arg1); 1040 } 1041 1042 ~CommandObjectPlatformFileExists() override = default; 1043 1044 void 1045 HandleArgumentCompletion(CompletionRequest &request, 1046 OptionElementVector &opt_element_vector) override { 1047 if (request.GetCursorIndex() != 0) 1048 return; 1049 1050 lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( 1051 GetCommandInterpreter(), lldb::eRemoteDiskFileCompletion, request, 1052 nullptr); 1053 } 1054 1055 bool DoExecute(Args &args, CommandReturnObject &result) override { 1056 // If the number of arguments is incorrect, issue an error message. 1057 if (args.GetArgumentCount() != 1) { 1058 result.AppendError("required argument missing; specify the source file " 1059 "path as the only argument"); 1060 return false; 1061 } 1062 1063 PlatformSP platform_sp( 1064 GetDebugger().GetPlatformList().GetSelectedPlatform()); 1065 if (platform_sp) { 1066 std::string remote_file_path(args.GetArgumentAtIndex(0)); 1067 bool exists = platform_sp->GetFileExists(FileSpec(remote_file_path)); 1068 result.AppendMessageWithFormat( 1069 "File %s (remote) %s\n", 1070 remote_file_path.c_str(), exists ? "exists" : "does not exist"); 1071 result.SetStatus(eReturnStatusSuccessFinishResult); 1072 } else { 1073 result.AppendError("no platform currently selected\n"); 1074 } 1075 return result.Succeeded(); 1076 } 1077 }; 1078 1079 // "platform put-file" 1080 class CommandObjectPlatformPutFile : public CommandObjectParsed { 1081 public: 1082 CommandObjectPlatformPutFile(CommandInterpreter &interpreter) 1083 : CommandObjectParsed( 1084 interpreter, "platform put-file", 1085 "Transfer a file from this system to the remote end.", 1086 "platform put-file <source> [<destination>]", 0) { 1087 SetHelpLong( 1088 R"(Examples: 1089 1090 (lldb) platform put-file /source/foo.txt /destination/bar.txt 1091 1092 (lldb) platform put-file /source/foo.txt 1093 1094 Relative source file paths are resolved against lldb's local working directory. 1095 1096 Omitting the destination places the file in the platform working directory.)"); 1097 CommandArgumentData source_arg{eArgTypePath, eArgRepeatPlain}; 1098 CommandArgumentData path_arg{eArgTypePath, eArgRepeatOptional}; 1099 m_arguments.push_back({source_arg}); 1100 m_arguments.push_back({path_arg}); 1101 } 1102 1103 ~CommandObjectPlatformPutFile() override = default; 1104 1105 void 1106 HandleArgumentCompletion(CompletionRequest &request, 1107 OptionElementVector &opt_element_vector) override { 1108 if (request.GetCursorIndex() == 0) 1109 lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( 1110 GetCommandInterpreter(), lldb::eDiskFileCompletion, request, nullptr); 1111 else if (request.GetCursorIndex() == 1) 1112 lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( 1113 GetCommandInterpreter(), lldb::eRemoteDiskFileCompletion, request, 1114 nullptr); 1115 } 1116 1117 bool DoExecute(Args &args, CommandReturnObject &result) override { 1118 const char *src = args.GetArgumentAtIndex(0); 1119 const char *dst = args.GetArgumentAtIndex(1); 1120 1121 FileSpec src_fs(src); 1122 FileSystem::Instance().Resolve(src_fs); 1123 FileSpec dst_fs(dst ? dst : src_fs.GetFilename().GetCString()); 1124 1125 PlatformSP platform_sp( 1126 GetDebugger().GetPlatformList().GetSelectedPlatform()); 1127 if (platform_sp) { 1128 Status error(platform_sp->PutFile(src_fs, dst_fs)); 1129 if (error.Success()) { 1130 result.SetStatus(eReturnStatusSuccessFinishNoResult); 1131 } else { 1132 result.AppendError(error.AsCString()); 1133 } 1134 } else { 1135 result.AppendError("no platform currently selected\n"); 1136 } 1137 return result.Succeeded(); 1138 } 1139 }; 1140 1141 // "platform process launch" 1142 class CommandObjectPlatformProcessLaunch : public CommandObjectParsed { 1143 public: 1144 CommandObjectPlatformProcessLaunch(CommandInterpreter &interpreter) 1145 : CommandObjectParsed(interpreter, "platform process launch", 1146 "Launch a new process on a remote platform.", 1147 "platform process launch program", 1148 eCommandRequiresTarget | eCommandTryTargetAPILock), 1149 m_class_options("scripted process", true, 'C', 'k', 'v', 0) { 1150 m_all_options.Append(&m_options); 1151 m_all_options.Append(&m_class_options, LLDB_OPT_SET_1 | LLDB_OPT_SET_2, 1152 LLDB_OPT_SET_ALL); 1153 m_all_options.Finalize(); 1154 CommandArgumentData run_arg_arg{eArgTypeRunArgs, eArgRepeatStar}; 1155 m_arguments.push_back({run_arg_arg}); 1156 } 1157 1158 ~CommandObjectPlatformProcessLaunch() override = default; 1159 1160 Options *GetOptions() override { return &m_all_options; } 1161 1162 protected: 1163 bool DoExecute(Args &args, CommandReturnObject &result) override { 1164 Target *target = GetDebugger().GetSelectedTarget().get(); 1165 PlatformSP platform_sp; 1166 if (target) { 1167 platform_sp = target->GetPlatform(); 1168 } 1169 if (!platform_sp) { 1170 platform_sp = GetDebugger().GetPlatformList().GetSelectedPlatform(); 1171 } 1172 1173 if (platform_sp) { 1174 Status error; 1175 const size_t argc = args.GetArgumentCount(); 1176 Target *target = m_exe_ctx.GetTargetPtr(); 1177 Module *exe_module = target->GetExecutableModulePointer(); 1178 if (exe_module) { 1179 m_options.launch_info.GetExecutableFile() = exe_module->GetFileSpec(); 1180 llvm::SmallString<128> exe_path; 1181 m_options.launch_info.GetExecutableFile().GetPath(exe_path); 1182 if (!exe_path.empty()) 1183 m_options.launch_info.GetArguments().AppendArgument(exe_path); 1184 m_options.launch_info.GetArchitecture() = exe_module->GetArchitecture(); 1185 } 1186 1187 if (!m_class_options.GetName().empty()) { 1188 m_options.launch_info.SetProcessPluginName("ScriptedProcess"); 1189 ScriptedMetadataSP metadata_sp = std::make_shared<ScriptedMetadata>( 1190 m_class_options.GetName(), m_class_options.GetStructuredData()); 1191 m_options.launch_info.SetScriptedMetadata(metadata_sp); 1192 target->SetProcessLaunchInfo(m_options.launch_info); 1193 } 1194 1195 if (argc > 0) { 1196 if (m_options.launch_info.GetExecutableFile()) { 1197 // We already have an executable file, so we will use this and all 1198 // arguments to this function are extra arguments 1199 m_options.launch_info.GetArguments().AppendArguments(args); 1200 } else { 1201 // We don't have any file yet, so the first argument is our 1202 // executable, and the rest are program arguments 1203 const bool first_arg_is_executable = true; 1204 m_options.launch_info.SetArguments(args, first_arg_is_executable); 1205 } 1206 } 1207 1208 if (m_options.launch_info.GetExecutableFile()) { 1209 Debugger &debugger = GetDebugger(); 1210 1211 if (argc == 0) 1212 target->GetRunArguments(m_options.launch_info.GetArguments()); 1213 1214 ProcessSP process_sp(platform_sp->DebugProcess( 1215 m_options.launch_info, debugger, *target, error)); 1216 1217 if (!process_sp && error.Success()) { 1218 result.AppendError("failed to launch or debug process"); 1219 return false; 1220 } else if (!error.Success()) { 1221 result.AppendError(error.AsCString()); 1222 return false; 1223 } 1224 1225 const bool synchronous_execution = 1226 debugger.GetCommandInterpreter().GetSynchronous(); 1227 auto launch_info = m_options.launch_info; 1228 bool rebroadcast_first_stop = 1229 !synchronous_execution && 1230 launch_info.GetFlags().Test(eLaunchFlagStopAtEntry); 1231 1232 assert(launch_info.GetHijackListener()); 1233 1234 EventSP first_stop_event_sp; 1235 StateType state = process_sp->WaitForProcessToStop( 1236 std::nullopt, &first_stop_event_sp, rebroadcast_first_stop, 1237 launch_info.GetHijackListener()); 1238 process_sp->RestoreProcessEvents(); 1239 1240 if (rebroadcast_first_stop) { 1241 assert(first_stop_event_sp); 1242 process_sp->BroadcastEvent(first_stop_event_sp); 1243 return true; 1244 } 1245 1246 switch (state) { 1247 case eStateStopped: { 1248 if (launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) 1249 break; 1250 if (synchronous_execution) { 1251 // Now we have handled the stop-from-attach, and we are just 1252 // switching to a synchronous resume. So we should switch to the 1253 // SyncResume hijacker. 1254 process_sp->ResumeSynchronous(&result.GetOutputStream()); 1255 } else { 1256 error = process_sp->Resume(); 1257 if (!error.Success()) { 1258 result.AppendErrorWithFormat( 1259 "process resume at entry point failed: %s", 1260 error.AsCString()); 1261 } 1262 } 1263 } break; 1264 default: 1265 result.AppendErrorWithFormat( 1266 "initial process state wasn't stopped: %s", 1267 StateAsCString(state)); 1268 break; 1269 } 1270 1271 if (process_sp && process_sp->IsAlive()) { 1272 result.SetStatus(eReturnStatusSuccessFinishNoResult); 1273 return true; 1274 } 1275 } else { 1276 result.AppendError("'platform process launch' uses the current target " 1277 "file and arguments, or the executable and its " 1278 "arguments can be specified in this command"); 1279 return false; 1280 } 1281 } else { 1282 result.AppendError("no platform is selected\n"); 1283 } 1284 return result.Succeeded(); 1285 } 1286 1287 CommandOptionsProcessLaunch m_options; 1288 OptionGroupPythonClassWithDict m_class_options; 1289 OptionGroupOptions m_all_options; 1290 }; 1291 1292 // "platform process list" 1293 1294 static PosixPlatformCommandOptionValidator posix_validator; 1295 #define LLDB_OPTIONS_platform_process_list 1296 #include "CommandOptions.inc" 1297 1298 class CommandObjectPlatformProcessList : public CommandObjectParsed { 1299 public: 1300 CommandObjectPlatformProcessList(CommandInterpreter &interpreter) 1301 : CommandObjectParsed(interpreter, "platform process list", 1302 "List processes on a remote platform by name, pid, " 1303 "or many other matching attributes.", 1304 "platform process list", 0) {} 1305 1306 ~CommandObjectPlatformProcessList() override = default; 1307 1308 Options *GetOptions() override { return &m_options; } 1309 1310 protected: 1311 bool DoExecute(Args &args, CommandReturnObject &result) override { 1312 Target *target = GetDebugger().GetSelectedTarget().get(); 1313 PlatformSP platform_sp; 1314 if (target) { 1315 platform_sp = target->GetPlatform(); 1316 } 1317 if (!platform_sp) { 1318 platform_sp = GetDebugger().GetPlatformList().GetSelectedPlatform(); 1319 } 1320 1321 if (platform_sp) { 1322 Status error; 1323 if (platform_sp) { 1324 Stream &ostrm = result.GetOutputStream(); 1325 1326 lldb::pid_t pid = m_options.match_info.GetProcessInfo().GetProcessID(); 1327 if (pid != LLDB_INVALID_PROCESS_ID) { 1328 ProcessInstanceInfo proc_info; 1329 if (platform_sp->GetProcessInfo(pid, proc_info)) { 1330 ProcessInstanceInfo::DumpTableHeader(ostrm, m_options.show_args, 1331 m_options.verbose); 1332 proc_info.DumpAsTableRow(ostrm, platform_sp->GetUserIDResolver(), 1333 m_options.show_args, m_options.verbose); 1334 result.SetStatus(eReturnStatusSuccessFinishResult); 1335 } else { 1336 result.AppendErrorWithFormat( 1337 "no process found with pid = %" PRIu64 "\n", pid); 1338 } 1339 } else { 1340 ProcessInstanceInfoList proc_infos; 1341 const uint32_t matches = 1342 platform_sp->FindProcesses(m_options.match_info, proc_infos); 1343 const char *match_desc = nullptr; 1344 const char *match_name = 1345 m_options.match_info.GetProcessInfo().GetName(); 1346 if (match_name && match_name[0]) { 1347 switch (m_options.match_info.GetNameMatchType()) { 1348 case NameMatch::Ignore: 1349 break; 1350 case NameMatch::Equals: 1351 match_desc = "matched"; 1352 break; 1353 case NameMatch::Contains: 1354 match_desc = "contained"; 1355 break; 1356 case NameMatch::StartsWith: 1357 match_desc = "started with"; 1358 break; 1359 case NameMatch::EndsWith: 1360 match_desc = "ended with"; 1361 break; 1362 case NameMatch::RegularExpression: 1363 match_desc = "matched the regular expression"; 1364 break; 1365 } 1366 } 1367 1368 if (matches == 0) { 1369 if (match_desc) 1370 result.AppendErrorWithFormatv( 1371 "no processes were found that {0} \"{1}\" on the \"{2}\" " 1372 "platform\n", 1373 match_desc, match_name, platform_sp->GetName()); 1374 else 1375 result.AppendErrorWithFormatv( 1376 "no processes were found on the \"{0}\" platform\n", 1377 platform_sp->GetName()); 1378 } else { 1379 result.AppendMessageWithFormatv( 1380 "{0} matching process{1} found on \"{2}\"", matches, 1381 matches > 1 ? "es were" : " was", platform_sp->GetName()); 1382 if (match_desc) 1383 result.AppendMessageWithFormat(" whose name %s \"%s\"", 1384 match_desc, match_name); 1385 result.AppendMessageWithFormat("\n"); 1386 ProcessInstanceInfo::DumpTableHeader(ostrm, m_options.show_args, 1387 m_options.verbose); 1388 for (uint32_t i = 0; i < matches; ++i) { 1389 proc_infos[i].DumpAsTableRow( 1390 ostrm, platform_sp->GetUserIDResolver(), m_options.show_args, 1391 m_options.verbose); 1392 } 1393 } 1394 } 1395 } 1396 } else { 1397 result.AppendError("no platform is selected\n"); 1398 } 1399 return result.Succeeded(); 1400 } 1401 1402 class CommandOptions : public Options { 1403 public: 1404 CommandOptions() = default; 1405 1406 ~CommandOptions() override = default; 1407 1408 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 1409 ExecutionContext *execution_context) override { 1410 Status error; 1411 const int short_option = m_getopt_table[option_idx].val; 1412 bool success = false; 1413 1414 uint32_t id = LLDB_INVALID_PROCESS_ID; 1415 success = !option_arg.getAsInteger(0, id); 1416 switch (short_option) { 1417 case 'p': { 1418 match_info.GetProcessInfo().SetProcessID(id); 1419 if (!success) 1420 error.SetErrorStringWithFormat("invalid process ID string: '%s'", 1421 option_arg.str().c_str()); 1422 break; 1423 } 1424 case 'P': 1425 match_info.GetProcessInfo().SetParentProcessID(id); 1426 if (!success) 1427 error.SetErrorStringWithFormat( 1428 "invalid parent process ID string: '%s'", 1429 option_arg.str().c_str()); 1430 break; 1431 1432 case 'u': 1433 match_info.GetProcessInfo().SetUserID(success ? id : UINT32_MAX); 1434 if (!success) 1435 error.SetErrorStringWithFormat("invalid user ID string: '%s'", 1436 option_arg.str().c_str()); 1437 break; 1438 1439 case 'U': 1440 match_info.GetProcessInfo().SetEffectiveUserID(success ? id 1441 : UINT32_MAX); 1442 if (!success) 1443 error.SetErrorStringWithFormat( 1444 "invalid effective user ID string: '%s'", 1445 option_arg.str().c_str()); 1446 break; 1447 1448 case 'g': 1449 match_info.GetProcessInfo().SetGroupID(success ? id : UINT32_MAX); 1450 if (!success) 1451 error.SetErrorStringWithFormat("invalid group ID string: '%s'", 1452 option_arg.str().c_str()); 1453 break; 1454 1455 case 'G': 1456 match_info.GetProcessInfo().SetEffectiveGroupID(success ? id 1457 : UINT32_MAX); 1458 if (!success) 1459 error.SetErrorStringWithFormat( 1460 "invalid effective group ID string: '%s'", 1461 option_arg.str().c_str()); 1462 break; 1463 1464 case 'a': { 1465 TargetSP target_sp = 1466 execution_context ? execution_context->GetTargetSP() : TargetSP(); 1467 DebuggerSP debugger_sp = 1468 target_sp ? target_sp->GetDebugger().shared_from_this() 1469 : DebuggerSP(); 1470 PlatformSP platform_sp = 1471 debugger_sp ? debugger_sp->GetPlatformList().GetSelectedPlatform() 1472 : PlatformSP(); 1473 match_info.GetProcessInfo().GetArchitecture() = 1474 Platform::GetAugmentedArchSpec(platform_sp.get(), option_arg); 1475 } break; 1476 1477 case 'n': 1478 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1479 option_arg, FileSpec::Style::native); 1480 match_info.SetNameMatchType(NameMatch::Equals); 1481 break; 1482 1483 case 'e': 1484 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1485 option_arg, FileSpec::Style::native); 1486 match_info.SetNameMatchType(NameMatch::EndsWith); 1487 break; 1488 1489 case 's': 1490 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1491 option_arg, FileSpec::Style::native); 1492 match_info.SetNameMatchType(NameMatch::StartsWith); 1493 break; 1494 1495 case 'c': 1496 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1497 option_arg, FileSpec::Style::native); 1498 match_info.SetNameMatchType(NameMatch::Contains); 1499 break; 1500 1501 case 'r': 1502 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1503 option_arg, FileSpec::Style::native); 1504 match_info.SetNameMatchType(NameMatch::RegularExpression); 1505 break; 1506 1507 case 'A': 1508 show_args = true; 1509 break; 1510 1511 case 'v': 1512 verbose = true; 1513 break; 1514 1515 case 'x': 1516 match_info.SetMatchAllUsers(true); 1517 break; 1518 1519 default: 1520 llvm_unreachable("Unimplemented option"); 1521 } 1522 1523 return error; 1524 } 1525 1526 void OptionParsingStarting(ExecutionContext *execution_context) override { 1527 match_info.Clear(); 1528 show_args = false; 1529 verbose = false; 1530 } 1531 1532 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 1533 return llvm::ArrayRef(g_platform_process_list_options); 1534 } 1535 1536 // Instance variables to hold the values for command options. 1537 1538 ProcessInstanceInfoMatch match_info; 1539 bool show_args = false; 1540 bool verbose = false; 1541 }; 1542 1543 CommandOptions m_options; 1544 }; 1545 1546 // "platform process info" 1547 class CommandObjectPlatformProcessInfo : public CommandObjectParsed { 1548 public: 1549 CommandObjectPlatformProcessInfo(CommandInterpreter &interpreter) 1550 : CommandObjectParsed( 1551 interpreter, "platform process info", 1552 "Get detailed information for one or more process by process ID.", 1553 "platform process info <pid> [<pid> <pid> ...]", 0) { 1554 CommandArgumentEntry arg; 1555 CommandArgumentData pid_args; 1556 1557 // Define the first (and only) variant of this arg. 1558 pid_args.arg_type = eArgTypePid; 1559 pid_args.arg_repetition = eArgRepeatStar; 1560 1561 // There is only one variant this argument could be; put it into the 1562 // argument entry. 1563 arg.push_back(pid_args); 1564 1565 // Push the data for the first argument into the m_arguments vector. 1566 m_arguments.push_back(arg); 1567 } 1568 1569 ~CommandObjectPlatformProcessInfo() override = default; 1570 1571 void 1572 HandleArgumentCompletion(CompletionRequest &request, 1573 OptionElementVector &opt_element_vector) override { 1574 lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( 1575 GetCommandInterpreter(), lldb::eProcessIDCompletion, request, nullptr); 1576 } 1577 1578 protected: 1579 bool DoExecute(Args &args, CommandReturnObject &result) override { 1580 Target *target = GetDebugger().GetSelectedTarget().get(); 1581 PlatformSP platform_sp; 1582 if (target) { 1583 platform_sp = target->GetPlatform(); 1584 } 1585 if (!platform_sp) { 1586 platform_sp = GetDebugger().GetPlatformList().GetSelectedPlatform(); 1587 } 1588 1589 if (platform_sp) { 1590 const size_t argc = args.GetArgumentCount(); 1591 if (argc > 0) { 1592 Status error; 1593 1594 if (platform_sp->IsConnected()) { 1595 Stream &ostrm = result.GetOutputStream(); 1596 for (auto &entry : args.entries()) { 1597 lldb::pid_t pid; 1598 if (entry.ref().getAsInteger(0, pid)) { 1599 result.AppendErrorWithFormat("invalid process ID argument '%s'", 1600 entry.ref().str().c_str()); 1601 break; 1602 } else { 1603 ProcessInstanceInfo proc_info; 1604 if (platform_sp->GetProcessInfo(pid, proc_info)) { 1605 ostrm.Printf("Process information for process %" PRIu64 ":\n", 1606 pid); 1607 proc_info.Dump(ostrm, platform_sp->GetUserIDResolver()); 1608 } else { 1609 ostrm.Printf("error: no process information is available for " 1610 "process %" PRIu64 "\n", 1611 pid); 1612 } 1613 ostrm.EOL(); 1614 } 1615 } 1616 } else { 1617 // Not connected... 1618 result.AppendErrorWithFormatv("not connected to '{0}'", 1619 platform_sp->GetPluginName()); 1620 } 1621 } else { 1622 // No args 1623 result.AppendError("one or more process id(s) must be specified"); 1624 } 1625 } else { 1626 result.AppendError("no platform is currently selected"); 1627 } 1628 return result.Succeeded(); 1629 } 1630 }; 1631 1632 #define LLDB_OPTIONS_platform_process_attach 1633 #include "CommandOptions.inc" 1634 1635 class CommandObjectPlatformProcessAttach : public CommandObjectParsed { 1636 public: 1637 CommandObjectPlatformProcessAttach(CommandInterpreter &interpreter) 1638 : CommandObjectParsed(interpreter, "platform process attach", 1639 "Attach to a process.", 1640 "platform process attach <cmd-options>"), 1641 m_class_options("scripted process", true, 'C', 'k', 'v', 0) { 1642 m_all_options.Append(&m_options); 1643 m_all_options.Append(&m_class_options, LLDB_OPT_SET_1 | LLDB_OPT_SET_2, 1644 LLDB_OPT_SET_ALL); 1645 m_all_options.Finalize(); 1646 } 1647 1648 ~CommandObjectPlatformProcessAttach() override = default; 1649 1650 bool DoExecute(Args &command, CommandReturnObject &result) override { 1651 PlatformSP platform_sp( 1652 GetDebugger().GetPlatformList().GetSelectedPlatform()); 1653 if (platform_sp) { 1654 1655 if (!m_class_options.GetName().empty()) { 1656 m_options.attach_info.SetProcessPluginName("ScriptedProcess"); 1657 ScriptedMetadataSP metadata_sp = std::make_shared<ScriptedMetadata>( 1658 m_class_options.GetName(), m_class_options.GetStructuredData()); 1659 m_options.attach_info.SetScriptedMetadata(metadata_sp); 1660 } 1661 1662 Status err; 1663 ProcessSP remote_process_sp = platform_sp->Attach( 1664 m_options.attach_info, GetDebugger(), nullptr, err); 1665 if (err.Fail()) { 1666 result.AppendError(err.AsCString()); 1667 } else if (!remote_process_sp) { 1668 result.AppendError("could not attach: unknown reason"); 1669 } else 1670 result.SetStatus(eReturnStatusSuccessFinishResult); 1671 } else { 1672 result.AppendError("no platform is currently selected"); 1673 } 1674 return result.Succeeded(); 1675 } 1676 1677 Options *GetOptions() override { return &m_all_options; } 1678 1679 protected: 1680 CommandOptionsProcessAttach m_options; 1681 OptionGroupPythonClassWithDict m_class_options; 1682 OptionGroupOptions m_all_options; 1683 }; 1684 1685 class CommandObjectPlatformProcess : public CommandObjectMultiword { 1686 public: 1687 // Constructors and Destructors 1688 CommandObjectPlatformProcess(CommandInterpreter &interpreter) 1689 : CommandObjectMultiword(interpreter, "platform process", 1690 "Commands to query, launch and attach to " 1691 "processes on the current platform.", 1692 "platform process [attach|launch|list] ...") { 1693 LoadSubCommand( 1694 "attach", 1695 CommandObjectSP(new CommandObjectPlatformProcessAttach(interpreter))); 1696 LoadSubCommand( 1697 "launch", 1698 CommandObjectSP(new CommandObjectPlatformProcessLaunch(interpreter))); 1699 LoadSubCommand("info", CommandObjectSP(new CommandObjectPlatformProcessInfo( 1700 interpreter))); 1701 LoadSubCommand("list", CommandObjectSP(new CommandObjectPlatformProcessList( 1702 interpreter))); 1703 } 1704 1705 ~CommandObjectPlatformProcess() override = default; 1706 1707 private: 1708 // For CommandObjectPlatform only 1709 CommandObjectPlatformProcess(const CommandObjectPlatformProcess &) = delete; 1710 const CommandObjectPlatformProcess & 1711 operator=(const CommandObjectPlatformProcess &) = delete; 1712 }; 1713 1714 // "platform shell" 1715 #define LLDB_OPTIONS_platform_shell 1716 #include "CommandOptions.inc" 1717 1718 class CommandObjectPlatformShell : public CommandObjectRaw { 1719 public: 1720 class CommandOptions : public Options { 1721 public: 1722 CommandOptions() = default; 1723 1724 ~CommandOptions() override = default; 1725 1726 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 1727 return llvm::ArrayRef(g_platform_shell_options); 1728 } 1729 1730 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 1731 ExecutionContext *execution_context) override { 1732 Status error; 1733 1734 const char short_option = (char)GetDefinitions()[option_idx].short_option; 1735 1736 switch (short_option) { 1737 case 'h': 1738 m_use_host_platform = true; 1739 break; 1740 case 't': 1741 uint32_t timeout_sec; 1742 if (option_arg.getAsInteger(10, timeout_sec)) 1743 error.SetErrorStringWithFormat( 1744 "could not convert \"%s\" to a numeric value.", 1745 option_arg.str().c_str()); 1746 else 1747 m_timeout = std::chrono::seconds(timeout_sec); 1748 break; 1749 case 's': { 1750 if (option_arg.empty()) { 1751 error.SetErrorStringWithFormat( 1752 "missing shell interpreter path for option -i|--interpreter."); 1753 return error; 1754 } 1755 1756 m_shell_interpreter = option_arg.str(); 1757 break; 1758 } 1759 default: 1760 llvm_unreachable("Unimplemented option"); 1761 } 1762 1763 return error; 1764 } 1765 1766 void OptionParsingStarting(ExecutionContext *execution_context) override { 1767 m_timeout.reset(); 1768 m_use_host_platform = false; 1769 m_shell_interpreter.clear(); 1770 } 1771 1772 Timeout<std::micro> m_timeout = std::chrono::seconds(10); 1773 bool m_use_host_platform; 1774 std::string m_shell_interpreter; 1775 }; 1776 1777 CommandObjectPlatformShell(CommandInterpreter &interpreter) 1778 : CommandObjectRaw(interpreter, "platform shell", 1779 "Run a shell command on the current platform.", 1780 "platform shell <shell-command>", 0) { 1781 CommandArgumentData thread_arg{eArgTypeNone, eArgRepeatStar}; 1782 m_arguments.push_back({thread_arg}); 1783 } 1784 1785 ~CommandObjectPlatformShell() override = default; 1786 1787 Options *GetOptions() override { return &m_options; } 1788 1789 bool DoExecute(llvm::StringRef raw_command_line, 1790 CommandReturnObject &result) override { 1791 ExecutionContext exe_ctx = GetCommandInterpreter().GetExecutionContext(); 1792 m_options.NotifyOptionParsingStarting(&exe_ctx); 1793 1794 // Print out an usage syntax on an empty command line. 1795 if (raw_command_line.empty()) { 1796 result.GetOutputStream().Printf("%s\n", this->GetSyntax().str().c_str()); 1797 return true; 1798 } 1799 1800 const bool is_alias = !raw_command_line.contains("platform"); 1801 OptionsWithRaw args(raw_command_line); 1802 1803 if (args.HasArgs()) 1804 if (!ParseOptions(args.GetArgs(), result)) 1805 return false; 1806 1807 if (args.GetRawPart().empty()) { 1808 result.GetOutputStream().Printf("%s <shell-command>\n", 1809 is_alias ? "shell" : "platform shell"); 1810 return false; 1811 } 1812 1813 llvm::StringRef cmd = args.GetRawPart(); 1814 1815 PlatformSP platform_sp( 1816 m_options.m_use_host_platform 1817 ? Platform::GetHostPlatform() 1818 : GetDebugger().GetPlatformList().GetSelectedPlatform()); 1819 Status error; 1820 if (platform_sp) { 1821 FileSpec working_dir{}; 1822 std::string output; 1823 int status = -1; 1824 int signo = -1; 1825 error = (platform_sp->RunShellCommand(m_options.m_shell_interpreter, cmd, 1826 working_dir, &status, &signo, 1827 &output, m_options.m_timeout)); 1828 if (!output.empty()) 1829 result.GetOutputStream().PutCString(output); 1830 if (status > 0) { 1831 if (signo > 0) { 1832 const char *signo_cstr = Host::GetSignalAsCString(signo); 1833 if (signo_cstr) 1834 result.GetOutputStream().Printf( 1835 "error: command returned with status %i and signal %s\n", 1836 status, signo_cstr); 1837 else 1838 result.GetOutputStream().Printf( 1839 "error: command returned with status %i and signal %i\n", 1840 status, signo); 1841 } else 1842 result.GetOutputStream().Printf( 1843 "error: command returned with status %i\n", status); 1844 } 1845 } else { 1846 result.GetOutputStream().Printf( 1847 "error: cannot run remote shell commands without a platform\n"); 1848 error.SetErrorString( 1849 "error: cannot run remote shell commands without a platform"); 1850 } 1851 1852 if (error.Fail()) { 1853 result.AppendError(error.AsCString()); 1854 } else { 1855 result.SetStatus(eReturnStatusSuccessFinishResult); 1856 } 1857 return true; 1858 } 1859 1860 CommandOptions m_options; 1861 }; 1862 1863 // "platform install" - install a target to a remote end 1864 class CommandObjectPlatformInstall : public CommandObjectParsed { 1865 public: 1866 CommandObjectPlatformInstall(CommandInterpreter &interpreter) 1867 : CommandObjectParsed( 1868 interpreter, "platform target-install", 1869 "Install a target (bundle or executable file) to the remote end.", 1870 "platform target-install <local-thing> <remote-sandbox>", 0) { 1871 CommandArgumentData local_arg{eArgTypePath, eArgRepeatPlain}; 1872 CommandArgumentData remote_arg{eArgTypePath, eArgRepeatPlain}; 1873 m_arguments.push_back({local_arg}); 1874 m_arguments.push_back({remote_arg}); 1875 } 1876 1877 ~CommandObjectPlatformInstall() override = default; 1878 1879 void 1880 HandleArgumentCompletion(CompletionRequest &request, 1881 OptionElementVector &opt_element_vector) override { 1882 if (request.GetCursorIndex()) 1883 return; 1884 lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( 1885 GetCommandInterpreter(), lldb::eDiskFileCompletion, request, nullptr); 1886 } 1887 1888 bool DoExecute(Args &args, CommandReturnObject &result) override { 1889 if (args.GetArgumentCount() != 2) { 1890 result.AppendError("platform target-install takes two arguments"); 1891 return false; 1892 } 1893 // TODO: move the bulk of this code over to the platform itself 1894 FileSpec src(args.GetArgumentAtIndex(0)); 1895 FileSystem::Instance().Resolve(src); 1896 FileSpec dst(args.GetArgumentAtIndex(1)); 1897 if (!FileSystem::Instance().Exists(src)) { 1898 result.AppendError("source location does not exist or is not accessible"); 1899 return false; 1900 } 1901 PlatformSP platform_sp( 1902 GetDebugger().GetPlatformList().GetSelectedPlatform()); 1903 if (!platform_sp) { 1904 result.AppendError("no platform currently selected"); 1905 return false; 1906 } 1907 1908 Status error = platform_sp->Install(src, dst); 1909 if (error.Success()) { 1910 result.SetStatus(eReturnStatusSuccessFinishNoResult); 1911 } else { 1912 result.AppendErrorWithFormat("install failed: %s", error.AsCString()); 1913 } 1914 return result.Succeeded(); 1915 } 1916 }; 1917 1918 CommandObjectPlatform::CommandObjectPlatform(CommandInterpreter &interpreter) 1919 : CommandObjectMultiword( 1920 interpreter, "platform", "Commands to manage and create platforms.", 1921 "platform [connect|disconnect|info|list|status|select] ...") { 1922 LoadSubCommand("select", 1923 CommandObjectSP(new CommandObjectPlatformSelect(interpreter))); 1924 LoadSubCommand("list", 1925 CommandObjectSP(new CommandObjectPlatformList(interpreter))); 1926 LoadSubCommand("status", 1927 CommandObjectSP(new CommandObjectPlatformStatus(interpreter))); 1928 LoadSubCommand("connect", CommandObjectSP( 1929 new CommandObjectPlatformConnect(interpreter))); 1930 LoadSubCommand( 1931 "disconnect", 1932 CommandObjectSP(new CommandObjectPlatformDisconnect(interpreter))); 1933 LoadSubCommand("settings", CommandObjectSP(new CommandObjectPlatformSettings( 1934 interpreter))); 1935 LoadSubCommand("mkdir", 1936 CommandObjectSP(new CommandObjectPlatformMkDir(interpreter))); 1937 LoadSubCommand("file", 1938 CommandObjectSP(new CommandObjectPlatformFile(interpreter))); 1939 LoadSubCommand("file-exists", 1940 CommandObjectSP(new CommandObjectPlatformFileExists(interpreter))); 1941 LoadSubCommand("get-file", CommandObjectSP(new CommandObjectPlatformGetFile( 1942 interpreter))); 1943 LoadSubCommand("get-permissions", 1944 CommandObjectSP(new CommandObjectPlatformGetPermissions(interpreter))); 1945 LoadSubCommand("get-size", CommandObjectSP(new CommandObjectPlatformGetSize( 1946 interpreter))); 1947 LoadSubCommand("put-file", CommandObjectSP(new CommandObjectPlatformPutFile( 1948 interpreter))); 1949 LoadSubCommand("process", CommandObjectSP( 1950 new CommandObjectPlatformProcess(interpreter))); 1951 LoadSubCommand("shell", 1952 CommandObjectSP(new CommandObjectPlatformShell(interpreter))); 1953 LoadSubCommand( 1954 "target-install", 1955 CommandObjectSP(new CommandObjectPlatformInstall(interpreter))); 1956 } 1957 1958 CommandObjectPlatform::~CommandObjectPlatform() = default; 1959