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