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