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 // If no arguments were given to the command, use target.run-args. 1213 Args target_run_args; 1214 target->GetRunArguments(target_run_args); 1215 m_options.launch_info.GetArguments().AppendArguments(target_run_args); 1216 } 1217 1218 ProcessSP process_sp(platform_sp->DebugProcess( 1219 m_options.launch_info, debugger, *target, error)); 1220 1221 if (!process_sp && error.Success()) { 1222 result.AppendError("failed to launch or debug process"); 1223 return false; 1224 } else if (!error.Success()) { 1225 result.AppendError(error.AsCString()); 1226 return false; 1227 } 1228 1229 const bool synchronous_execution = 1230 debugger.GetCommandInterpreter().GetSynchronous(); 1231 auto launch_info = m_options.launch_info; 1232 bool rebroadcast_first_stop = 1233 !synchronous_execution && 1234 launch_info.GetFlags().Test(eLaunchFlagStopAtEntry); 1235 1236 EventSP first_stop_event_sp; 1237 StateType state = process_sp->WaitForProcessToStop( 1238 std::nullopt, &first_stop_event_sp, rebroadcast_first_stop, 1239 launch_info.GetHijackListener()); 1240 process_sp->RestoreProcessEvents(); 1241 1242 if (rebroadcast_first_stop) { 1243 assert(first_stop_event_sp); 1244 process_sp->BroadcastEvent(first_stop_event_sp); 1245 return true; 1246 } 1247 1248 switch (state) { 1249 case eStateStopped: { 1250 if (launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) 1251 break; 1252 if (synchronous_execution) { 1253 // Now we have handled the stop-from-attach, and we are just 1254 // switching to a synchronous resume. So we should switch to the 1255 // SyncResume hijacker. 1256 process_sp->ResumeSynchronous(&result.GetOutputStream()); 1257 } else { 1258 error = process_sp->Resume(); 1259 if (!error.Success()) { 1260 result.AppendErrorWithFormat( 1261 "process resume at entry point failed: %s", 1262 error.AsCString()); 1263 } 1264 } 1265 } break; 1266 default: 1267 result.AppendErrorWithFormat( 1268 "initial process state wasn't stopped: %s", 1269 StateAsCString(state)); 1270 break; 1271 } 1272 1273 if (process_sp && process_sp->IsAlive()) { 1274 result.SetStatus(eReturnStatusSuccessFinishNoResult); 1275 return true; 1276 } 1277 } else { 1278 result.AppendError("'platform process launch' uses the current target " 1279 "file and arguments, or the executable and its " 1280 "arguments can be specified in this command"); 1281 return false; 1282 } 1283 } else { 1284 result.AppendError("no platform is selected\n"); 1285 } 1286 return result.Succeeded(); 1287 } 1288 1289 CommandOptionsProcessLaunch m_options; 1290 OptionGroupPythonClassWithDict m_class_options; 1291 OptionGroupOptions m_all_options; 1292 }; 1293 1294 // "platform process list" 1295 1296 static PosixPlatformCommandOptionValidator posix_validator; 1297 #define LLDB_OPTIONS_platform_process_list 1298 #include "CommandOptions.inc" 1299 1300 class CommandObjectPlatformProcessList : public CommandObjectParsed { 1301 public: 1302 CommandObjectPlatformProcessList(CommandInterpreter &interpreter) 1303 : CommandObjectParsed(interpreter, "platform process list", 1304 "List processes on a remote platform by name, pid, " 1305 "or many other matching attributes.", 1306 "platform process list", 0) {} 1307 1308 ~CommandObjectPlatformProcessList() override = default; 1309 1310 Options *GetOptions() override { return &m_options; } 1311 1312 protected: 1313 bool DoExecute(Args &args, CommandReturnObject &result) override { 1314 Target *target = GetDebugger().GetSelectedTarget().get(); 1315 PlatformSP platform_sp; 1316 if (target) { 1317 platform_sp = target->GetPlatform(); 1318 } 1319 if (!platform_sp) { 1320 platform_sp = GetDebugger().GetPlatformList().GetSelectedPlatform(); 1321 } 1322 1323 if (platform_sp) { 1324 Status error; 1325 if (platform_sp) { 1326 Stream &ostrm = result.GetOutputStream(); 1327 1328 lldb::pid_t pid = m_options.match_info.GetProcessInfo().GetProcessID(); 1329 if (pid != LLDB_INVALID_PROCESS_ID) { 1330 ProcessInstanceInfo proc_info; 1331 if (platform_sp->GetProcessInfo(pid, proc_info)) { 1332 ProcessInstanceInfo::DumpTableHeader(ostrm, m_options.show_args, 1333 m_options.verbose); 1334 proc_info.DumpAsTableRow(ostrm, platform_sp->GetUserIDResolver(), 1335 m_options.show_args, m_options.verbose); 1336 result.SetStatus(eReturnStatusSuccessFinishResult); 1337 } else { 1338 result.AppendErrorWithFormat( 1339 "no process found with pid = %" PRIu64 "\n", pid); 1340 } 1341 } else { 1342 ProcessInstanceInfoList proc_infos; 1343 const uint32_t matches = 1344 platform_sp->FindProcesses(m_options.match_info, proc_infos); 1345 const char *match_desc = nullptr; 1346 const char *match_name = 1347 m_options.match_info.GetProcessInfo().GetName(); 1348 if (match_name && match_name[0]) { 1349 switch (m_options.match_info.GetNameMatchType()) { 1350 case NameMatch::Ignore: 1351 break; 1352 case NameMatch::Equals: 1353 match_desc = "matched"; 1354 break; 1355 case NameMatch::Contains: 1356 match_desc = "contained"; 1357 break; 1358 case NameMatch::StartsWith: 1359 match_desc = "started with"; 1360 break; 1361 case NameMatch::EndsWith: 1362 match_desc = "ended with"; 1363 break; 1364 case NameMatch::RegularExpression: 1365 match_desc = "matched the regular expression"; 1366 break; 1367 } 1368 } 1369 1370 if (matches == 0) { 1371 if (match_desc) 1372 result.AppendErrorWithFormatv( 1373 "no processes were found that {0} \"{1}\" on the \"{2}\" " 1374 "platform\n", 1375 match_desc, match_name, platform_sp->GetName()); 1376 else 1377 result.AppendErrorWithFormatv( 1378 "no processes were found on the \"{0}\" platform\n", 1379 platform_sp->GetName()); 1380 } else { 1381 result.AppendMessageWithFormatv( 1382 "{0} matching process{1} found on \"{2}\"", matches, 1383 matches > 1 ? "es were" : " was", platform_sp->GetName()); 1384 if (match_desc) 1385 result.AppendMessageWithFormat(" whose name %s \"%s\"", 1386 match_desc, match_name); 1387 result.AppendMessageWithFormat("\n"); 1388 ProcessInstanceInfo::DumpTableHeader(ostrm, m_options.show_args, 1389 m_options.verbose); 1390 for (uint32_t i = 0; i < matches; ++i) { 1391 proc_infos[i].DumpAsTableRow( 1392 ostrm, platform_sp->GetUserIDResolver(), m_options.show_args, 1393 m_options.verbose); 1394 } 1395 } 1396 } 1397 } 1398 } else { 1399 result.AppendError("no platform is selected\n"); 1400 } 1401 return result.Succeeded(); 1402 } 1403 1404 class CommandOptions : public Options { 1405 public: 1406 CommandOptions() = default; 1407 1408 ~CommandOptions() override = default; 1409 1410 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 1411 ExecutionContext *execution_context) override { 1412 Status error; 1413 const int short_option = m_getopt_table[option_idx].val; 1414 bool success = false; 1415 1416 uint32_t id = LLDB_INVALID_PROCESS_ID; 1417 success = !option_arg.getAsInteger(0, id); 1418 switch (short_option) { 1419 case 'p': { 1420 match_info.GetProcessInfo().SetProcessID(id); 1421 if (!success) 1422 error.SetErrorStringWithFormat("invalid process ID string: '%s'", 1423 option_arg.str().c_str()); 1424 break; 1425 } 1426 case 'P': 1427 match_info.GetProcessInfo().SetParentProcessID(id); 1428 if (!success) 1429 error.SetErrorStringWithFormat( 1430 "invalid parent process ID string: '%s'", 1431 option_arg.str().c_str()); 1432 break; 1433 1434 case 'u': 1435 match_info.GetProcessInfo().SetUserID(success ? id : UINT32_MAX); 1436 if (!success) 1437 error.SetErrorStringWithFormat("invalid user ID string: '%s'", 1438 option_arg.str().c_str()); 1439 break; 1440 1441 case 'U': 1442 match_info.GetProcessInfo().SetEffectiveUserID(success ? id 1443 : UINT32_MAX); 1444 if (!success) 1445 error.SetErrorStringWithFormat( 1446 "invalid effective user ID string: '%s'", 1447 option_arg.str().c_str()); 1448 break; 1449 1450 case 'g': 1451 match_info.GetProcessInfo().SetGroupID(success ? id : UINT32_MAX); 1452 if (!success) 1453 error.SetErrorStringWithFormat("invalid group ID string: '%s'", 1454 option_arg.str().c_str()); 1455 break; 1456 1457 case 'G': 1458 match_info.GetProcessInfo().SetEffectiveGroupID(success ? id 1459 : UINT32_MAX); 1460 if (!success) 1461 error.SetErrorStringWithFormat( 1462 "invalid effective group ID string: '%s'", 1463 option_arg.str().c_str()); 1464 break; 1465 1466 case 'a': { 1467 TargetSP target_sp = 1468 execution_context ? execution_context->GetTargetSP() : TargetSP(); 1469 DebuggerSP debugger_sp = 1470 target_sp ? target_sp->GetDebugger().shared_from_this() 1471 : DebuggerSP(); 1472 PlatformSP platform_sp = 1473 debugger_sp ? debugger_sp->GetPlatformList().GetSelectedPlatform() 1474 : PlatformSP(); 1475 match_info.GetProcessInfo().GetArchitecture() = 1476 Platform::GetAugmentedArchSpec(platform_sp.get(), option_arg); 1477 } break; 1478 1479 case 'n': 1480 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1481 option_arg, FileSpec::Style::native); 1482 match_info.SetNameMatchType(NameMatch::Equals); 1483 break; 1484 1485 case 'e': 1486 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1487 option_arg, FileSpec::Style::native); 1488 match_info.SetNameMatchType(NameMatch::EndsWith); 1489 break; 1490 1491 case 's': 1492 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1493 option_arg, FileSpec::Style::native); 1494 match_info.SetNameMatchType(NameMatch::StartsWith); 1495 break; 1496 1497 case 'c': 1498 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1499 option_arg, FileSpec::Style::native); 1500 match_info.SetNameMatchType(NameMatch::Contains); 1501 break; 1502 1503 case 'r': 1504 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1505 option_arg, FileSpec::Style::native); 1506 match_info.SetNameMatchType(NameMatch::RegularExpression); 1507 break; 1508 1509 case 'A': 1510 show_args = true; 1511 break; 1512 1513 case 'v': 1514 verbose = true; 1515 break; 1516 1517 case 'x': 1518 match_info.SetMatchAllUsers(true); 1519 break; 1520 1521 default: 1522 llvm_unreachable("Unimplemented option"); 1523 } 1524 1525 return error; 1526 } 1527 1528 void OptionParsingStarting(ExecutionContext *execution_context) override { 1529 match_info.Clear(); 1530 show_args = false; 1531 verbose = false; 1532 } 1533 1534 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 1535 return llvm::ArrayRef(g_platform_process_list_options); 1536 } 1537 1538 // Instance variables to hold the values for command options. 1539 1540 ProcessInstanceInfoMatch match_info; 1541 bool show_args = false; 1542 bool verbose = false; 1543 }; 1544 1545 CommandOptions m_options; 1546 }; 1547 1548 // "platform process info" 1549 class CommandObjectPlatformProcessInfo : public CommandObjectParsed { 1550 public: 1551 CommandObjectPlatformProcessInfo(CommandInterpreter &interpreter) 1552 : CommandObjectParsed( 1553 interpreter, "platform process info", 1554 "Get detailed information for one or more process by process ID.", 1555 "platform process info <pid> [<pid> <pid> ...]", 0) { 1556 CommandArgumentEntry arg; 1557 CommandArgumentData pid_args; 1558 1559 // Define the first (and only) variant of this arg. 1560 pid_args.arg_type = eArgTypePid; 1561 pid_args.arg_repetition = eArgRepeatStar; 1562 1563 // There is only one variant this argument could be; put it into the 1564 // argument entry. 1565 arg.push_back(pid_args); 1566 1567 // Push the data for the first argument into the m_arguments vector. 1568 m_arguments.push_back(arg); 1569 } 1570 1571 ~CommandObjectPlatformProcessInfo() override = default; 1572 1573 void 1574 HandleArgumentCompletion(CompletionRequest &request, 1575 OptionElementVector &opt_element_vector) override { 1576 lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( 1577 GetCommandInterpreter(), lldb::eProcessIDCompletion, request, nullptr); 1578 } 1579 1580 protected: 1581 bool DoExecute(Args &args, CommandReturnObject &result) override { 1582 Target *target = GetDebugger().GetSelectedTarget().get(); 1583 PlatformSP platform_sp; 1584 if (target) { 1585 platform_sp = target->GetPlatform(); 1586 } 1587 if (!platform_sp) { 1588 platform_sp = GetDebugger().GetPlatformList().GetSelectedPlatform(); 1589 } 1590 1591 if (platform_sp) { 1592 const size_t argc = args.GetArgumentCount(); 1593 if (argc > 0) { 1594 Status error; 1595 1596 if (platform_sp->IsConnected()) { 1597 Stream &ostrm = result.GetOutputStream(); 1598 for (auto &entry : args.entries()) { 1599 lldb::pid_t pid; 1600 if (entry.ref().getAsInteger(0, pid)) { 1601 result.AppendErrorWithFormat("invalid process ID argument '%s'", 1602 entry.ref().str().c_str()); 1603 break; 1604 } else { 1605 ProcessInstanceInfo proc_info; 1606 if (platform_sp->GetProcessInfo(pid, proc_info)) { 1607 ostrm.Printf("Process information for process %" PRIu64 ":\n", 1608 pid); 1609 proc_info.Dump(ostrm, platform_sp->GetUserIDResolver()); 1610 } else { 1611 ostrm.Printf("error: no process information is available for " 1612 "process %" PRIu64 "\n", 1613 pid); 1614 } 1615 ostrm.EOL(); 1616 } 1617 } 1618 } else { 1619 // Not connected... 1620 result.AppendErrorWithFormatv("not connected to '{0}'", 1621 platform_sp->GetPluginName()); 1622 } 1623 } else { 1624 // No args 1625 result.AppendError("one or more process id(s) must be specified"); 1626 } 1627 } else { 1628 result.AppendError("no platform is currently selected"); 1629 } 1630 return result.Succeeded(); 1631 } 1632 }; 1633 1634 #define LLDB_OPTIONS_platform_process_attach 1635 #include "CommandOptions.inc" 1636 1637 class CommandObjectPlatformProcessAttach : public CommandObjectParsed { 1638 public: 1639 CommandObjectPlatformProcessAttach(CommandInterpreter &interpreter) 1640 : CommandObjectParsed(interpreter, "platform process attach", 1641 "Attach to a process.", 1642 "platform process attach <cmd-options>"), 1643 m_class_options("scripted process", true, 'C', 'k', 'v', 0) { 1644 m_all_options.Append(&m_options); 1645 m_all_options.Append(&m_class_options, LLDB_OPT_SET_1 | LLDB_OPT_SET_2, 1646 LLDB_OPT_SET_ALL); 1647 m_all_options.Finalize(); 1648 } 1649 1650 ~CommandObjectPlatformProcessAttach() override = default; 1651 1652 bool DoExecute(Args &command, CommandReturnObject &result) override { 1653 PlatformSP platform_sp( 1654 GetDebugger().GetPlatformList().GetSelectedPlatform()); 1655 if (platform_sp) { 1656 1657 if (!m_class_options.GetName().empty()) { 1658 m_options.attach_info.SetProcessPluginName("ScriptedProcess"); 1659 ScriptedMetadataSP metadata_sp = std::make_shared<ScriptedMetadata>( 1660 m_class_options.GetName(), m_class_options.GetStructuredData()); 1661 m_options.attach_info.SetScriptedMetadata(metadata_sp); 1662 } 1663 1664 Status err; 1665 ProcessSP remote_process_sp = platform_sp->Attach( 1666 m_options.attach_info, GetDebugger(), nullptr, err); 1667 if (err.Fail()) { 1668 result.AppendError(err.AsCString()); 1669 } else if (!remote_process_sp) { 1670 result.AppendError("could not attach: unknown reason"); 1671 } else 1672 result.SetStatus(eReturnStatusSuccessFinishResult); 1673 } else { 1674 result.AppendError("no platform is currently selected"); 1675 } 1676 return result.Succeeded(); 1677 } 1678 1679 Options *GetOptions() override { return &m_all_options; } 1680 1681 protected: 1682 CommandOptionsProcessAttach m_options; 1683 OptionGroupPythonClassWithDict m_class_options; 1684 OptionGroupOptions m_all_options; 1685 }; 1686 1687 class CommandObjectPlatformProcess : public CommandObjectMultiword { 1688 public: 1689 // Constructors and Destructors 1690 CommandObjectPlatformProcess(CommandInterpreter &interpreter) 1691 : CommandObjectMultiword(interpreter, "platform process", 1692 "Commands to query, launch and attach to " 1693 "processes on the current platform.", 1694 "platform process [attach|launch|list] ...") { 1695 LoadSubCommand( 1696 "attach", 1697 CommandObjectSP(new CommandObjectPlatformProcessAttach(interpreter))); 1698 LoadSubCommand( 1699 "launch", 1700 CommandObjectSP(new CommandObjectPlatformProcessLaunch(interpreter))); 1701 LoadSubCommand("info", CommandObjectSP(new CommandObjectPlatformProcessInfo( 1702 interpreter))); 1703 LoadSubCommand("list", CommandObjectSP(new CommandObjectPlatformProcessList( 1704 interpreter))); 1705 } 1706 1707 ~CommandObjectPlatformProcess() override = default; 1708 1709 private: 1710 // For CommandObjectPlatform only 1711 CommandObjectPlatformProcess(const CommandObjectPlatformProcess &) = delete; 1712 const CommandObjectPlatformProcess & 1713 operator=(const CommandObjectPlatformProcess &) = delete; 1714 }; 1715 1716 // "platform shell" 1717 #define LLDB_OPTIONS_platform_shell 1718 #include "CommandOptions.inc" 1719 1720 class CommandObjectPlatformShell : public CommandObjectRaw { 1721 public: 1722 class CommandOptions : public Options { 1723 public: 1724 CommandOptions() = default; 1725 1726 ~CommandOptions() override = default; 1727 1728 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 1729 return llvm::ArrayRef(g_platform_shell_options); 1730 } 1731 1732 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 1733 ExecutionContext *execution_context) override { 1734 Status error; 1735 1736 const char short_option = (char)GetDefinitions()[option_idx].short_option; 1737 1738 switch (short_option) { 1739 case 'h': 1740 m_use_host_platform = true; 1741 break; 1742 case 't': 1743 uint32_t timeout_sec; 1744 if (option_arg.getAsInteger(10, timeout_sec)) 1745 error.SetErrorStringWithFormat( 1746 "could not convert \"%s\" to a numeric value.", 1747 option_arg.str().c_str()); 1748 else 1749 m_timeout = std::chrono::seconds(timeout_sec); 1750 break; 1751 case 's': { 1752 if (option_arg.empty()) { 1753 error.SetErrorStringWithFormat( 1754 "missing shell interpreter path for option -i|--interpreter."); 1755 return error; 1756 } 1757 1758 m_shell_interpreter = option_arg.str(); 1759 break; 1760 } 1761 default: 1762 llvm_unreachable("Unimplemented option"); 1763 } 1764 1765 return error; 1766 } 1767 1768 void OptionParsingStarting(ExecutionContext *execution_context) override { 1769 m_timeout.reset(); 1770 m_use_host_platform = false; 1771 m_shell_interpreter.clear(); 1772 } 1773 1774 Timeout<std::micro> m_timeout = std::chrono::seconds(10); 1775 bool m_use_host_platform; 1776 std::string m_shell_interpreter; 1777 }; 1778 1779 CommandObjectPlatformShell(CommandInterpreter &interpreter) 1780 : CommandObjectRaw(interpreter, "platform shell", 1781 "Run a shell command on the current platform.", 1782 "platform shell <shell-command>", 0) { 1783 CommandArgumentData thread_arg{eArgTypeNone, eArgRepeatStar}; 1784 m_arguments.push_back({thread_arg}); 1785 } 1786 1787 ~CommandObjectPlatformShell() override = default; 1788 1789 Options *GetOptions() override { return &m_options; } 1790 1791 bool DoExecute(llvm::StringRef raw_command_line, 1792 CommandReturnObject &result) override { 1793 ExecutionContext exe_ctx = GetCommandInterpreter().GetExecutionContext(); 1794 m_options.NotifyOptionParsingStarting(&exe_ctx); 1795 1796 // Print out an usage syntax on an empty command line. 1797 if (raw_command_line.empty()) { 1798 result.GetOutputStream().Printf("%s\n", this->GetSyntax().str().c_str()); 1799 return true; 1800 } 1801 1802 const bool is_alias = !raw_command_line.contains("platform"); 1803 OptionsWithRaw args(raw_command_line); 1804 1805 if (args.HasArgs()) 1806 if (!ParseOptions(args.GetArgs(), result)) 1807 return false; 1808 1809 if (args.GetRawPart().empty()) { 1810 result.GetOutputStream().Printf("%s <shell-command>\n", 1811 is_alias ? "shell" : "platform shell"); 1812 return false; 1813 } 1814 1815 llvm::StringRef cmd = args.GetRawPart(); 1816 1817 PlatformSP platform_sp( 1818 m_options.m_use_host_platform 1819 ? Platform::GetHostPlatform() 1820 : GetDebugger().GetPlatformList().GetSelectedPlatform()); 1821 Status error; 1822 if (platform_sp) { 1823 FileSpec working_dir{}; 1824 std::string output; 1825 int status = -1; 1826 int signo = -1; 1827 error = (platform_sp->RunShellCommand(m_options.m_shell_interpreter, cmd, 1828 working_dir, &status, &signo, 1829 &output, m_options.m_timeout)); 1830 if (!output.empty()) 1831 result.GetOutputStream().PutCString(output); 1832 if (status > 0) { 1833 if (signo > 0) { 1834 const char *signo_cstr = Host::GetSignalAsCString(signo); 1835 if (signo_cstr) 1836 result.GetOutputStream().Printf( 1837 "error: command returned with status %i and signal %s\n", 1838 status, signo_cstr); 1839 else 1840 result.GetOutputStream().Printf( 1841 "error: command returned with status %i and signal %i\n", 1842 status, signo); 1843 } else 1844 result.GetOutputStream().Printf( 1845 "error: command returned with status %i\n", status); 1846 } 1847 } else { 1848 result.GetOutputStream().Printf( 1849 "error: cannot run remote shell commands without a platform\n"); 1850 error.SetErrorString( 1851 "error: cannot run remote shell commands without a platform"); 1852 } 1853 1854 if (error.Fail()) { 1855 result.AppendError(error.AsCString()); 1856 } else { 1857 result.SetStatus(eReturnStatusSuccessFinishResult); 1858 } 1859 return true; 1860 } 1861 1862 CommandOptions m_options; 1863 }; 1864 1865 // "platform install" - install a target to a remote end 1866 class CommandObjectPlatformInstall : public CommandObjectParsed { 1867 public: 1868 CommandObjectPlatformInstall(CommandInterpreter &interpreter) 1869 : CommandObjectParsed( 1870 interpreter, "platform target-install", 1871 "Install a target (bundle or executable file) to the remote end.", 1872 "platform target-install <local-thing> <remote-sandbox>", 0) { 1873 CommandArgumentData local_arg{eArgTypePath, eArgRepeatPlain}; 1874 CommandArgumentData remote_arg{eArgTypePath, eArgRepeatPlain}; 1875 m_arguments.push_back({local_arg}); 1876 m_arguments.push_back({remote_arg}); 1877 } 1878 1879 ~CommandObjectPlatformInstall() override = default; 1880 1881 void 1882 HandleArgumentCompletion(CompletionRequest &request, 1883 OptionElementVector &opt_element_vector) override { 1884 if (request.GetCursorIndex()) 1885 return; 1886 lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( 1887 GetCommandInterpreter(), lldb::eDiskFileCompletion, request, nullptr); 1888 } 1889 1890 bool DoExecute(Args &args, CommandReturnObject &result) override { 1891 if (args.GetArgumentCount() != 2) { 1892 result.AppendError("platform target-install takes two arguments"); 1893 return false; 1894 } 1895 // TODO: move the bulk of this code over to the platform itself 1896 FileSpec src(args.GetArgumentAtIndex(0)); 1897 FileSystem::Instance().Resolve(src); 1898 FileSpec dst(args.GetArgumentAtIndex(1)); 1899 if (!FileSystem::Instance().Exists(src)) { 1900 result.AppendError("source location does not exist or is not accessible"); 1901 return false; 1902 } 1903 PlatformSP platform_sp( 1904 GetDebugger().GetPlatformList().GetSelectedPlatform()); 1905 if (!platform_sp) { 1906 result.AppendError("no platform currently selected"); 1907 return false; 1908 } 1909 1910 Status error = platform_sp->Install(src, dst); 1911 if (error.Success()) { 1912 result.SetStatus(eReturnStatusSuccessFinishNoResult); 1913 } else { 1914 result.AppendErrorWithFormat("install failed: %s", error.AsCString()); 1915 } 1916 return result.Succeeded(); 1917 } 1918 }; 1919 1920 CommandObjectPlatform::CommandObjectPlatform(CommandInterpreter &interpreter) 1921 : CommandObjectMultiword( 1922 interpreter, "platform", "Commands to manage and create platforms.", 1923 "platform [connect|disconnect|info|list|status|select] ...") { 1924 LoadSubCommand("select", 1925 CommandObjectSP(new CommandObjectPlatformSelect(interpreter))); 1926 LoadSubCommand("list", 1927 CommandObjectSP(new CommandObjectPlatformList(interpreter))); 1928 LoadSubCommand("status", 1929 CommandObjectSP(new CommandObjectPlatformStatus(interpreter))); 1930 LoadSubCommand("connect", CommandObjectSP( 1931 new CommandObjectPlatformConnect(interpreter))); 1932 LoadSubCommand( 1933 "disconnect", 1934 CommandObjectSP(new CommandObjectPlatformDisconnect(interpreter))); 1935 LoadSubCommand("settings", CommandObjectSP(new CommandObjectPlatformSettings( 1936 interpreter))); 1937 LoadSubCommand("mkdir", 1938 CommandObjectSP(new CommandObjectPlatformMkDir(interpreter))); 1939 LoadSubCommand("file", 1940 CommandObjectSP(new CommandObjectPlatformFile(interpreter))); 1941 LoadSubCommand("file-exists", 1942 CommandObjectSP(new CommandObjectPlatformFileExists(interpreter))); 1943 LoadSubCommand("get-file", CommandObjectSP(new CommandObjectPlatformGetFile( 1944 interpreter))); 1945 LoadSubCommand("get-permissions", 1946 CommandObjectSP(new CommandObjectPlatformGetPermissions(interpreter))); 1947 LoadSubCommand("get-size", CommandObjectSP(new CommandObjectPlatformGetSize( 1948 interpreter))); 1949 LoadSubCommand("put-file", CommandObjectSP(new CommandObjectPlatformPutFile( 1950 interpreter))); 1951 LoadSubCommand("process", CommandObjectSP( 1952 new CommandObjectPlatformProcess(interpreter))); 1953 LoadSubCommand("shell", 1954 CommandObjectSP(new CommandObjectPlatformShell(interpreter))); 1955 LoadSubCommand( 1956 "target-install", 1957 CommandObjectSP(new CommandObjectPlatformInstall(interpreter))); 1958 } 1959 1960 CommandObjectPlatform::~CommandObjectPlatform() = default; 1961