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