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(new OptionPermissions()); 460 m_options.Finalize(); 461 } 462 return &m_options; 463 } 464 465 OptionGroupOptions m_options; 466 }; 467 468 // "platform fopen" 469 class CommandObjectPlatformFOpen : public CommandObjectParsed { 470 public: 471 CommandObjectPlatformFOpen(CommandInterpreter &interpreter) 472 : CommandObjectParsed(interpreter, "platform file open", 473 "Open a file on the remote end.", nullptr, 0) { 474 CommandArgumentData path_arg{eArgTypePath, eArgRepeatPlain}; 475 m_arguments.push_back({path_arg}); 476 } 477 478 ~CommandObjectPlatformFOpen() override = default; 479 480 void 481 HandleArgumentCompletion(CompletionRequest &request, 482 OptionElementVector &opt_element_vector) override { 483 if (request.GetCursorIndex() == 0) 484 CommandCompletions::InvokeCommonCompletionCallbacks( 485 GetCommandInterpreter(), 486 CommandCompletions::eRemoteDiskFileCompletion, request, nullptr); 487 } 488 489 bool DoExecute(Args &args, CommandReturnObject &result) override { 490 PlatformSP platform_sp( 491 GetDebugger().GetPlatformList().GetSelectedPlatform()); 492 if (platform_sp) { 493 Status error; 494 std::string cmd_line; 495 args.GetCommandString(cmd_line); 496 mode_t perms; 497 const OptionPermissions *options_permissions = 498 (const OptionPermissions *)m_options.GetGroupWithOption('r'); 499 if (options_permissions) 500 perms = options_permissions->m_permissions; 501 else 502 perms = lldb::eFilePermissionsUserRW | lldb::eFilePermissionsGroupRW | 503 lldb::eFilePermissionsWorldRead; 504 lldb::user_id_t fd = platform_sp->OpenFile( 505 FileSpec(cmd_line), 506 File::eOpenOptionReadWrite | File::eOpenOptionCanCreate, 507 perms, error); 508 if (error.Success()) { 509 result.AppendMessageWithFormat("File Descriptor = %" PRIu64 "\n", fd); 510 result.SetStatus(eReturnStatusSuccessFinishResult); 511 } else { 512 result.AppendError(error.AsCString()); 513 } 514 } else { 515 result.AppendError("no platform currently selected\n"); 516 } 517 return result.Succeeded(); 518 } 519 520 Options *GetOptions() override { 521 if (!m_options.DidFinalize()) { 522 m_options.Append(new OptionPermissions()); 523 m_options.Finalize(); 524 } 525 return &m_options; 526 } 527 528 OptionGroupOptions m_options; 529 }; 530 531 // "platform fclose" 532 class CommandObjectPlatformFClose : public CommandObjectParsed { 533 public: 534 CommandObjectPlatformFClose(CommandInterpreter &interpreter) 535 : CommandObjectParsed(interpreter, "platform file close", 536 "Close a file on the remote end.", nullptr, 0) { 537 CommandArgumentData path_arg{eArgTypeUnsignedInteger, eArgRepeatPlain}; 538 m_arguments.push_back({path_arg}); 539 } 540 541 ~CommandObjectPlatformFClose() override = default; 542 543 bool DoExecute(Args &args, CommandReturnObject &result) override { 544 PlatformSP platform_sp( 545 GetDebugger().GetPlatformList().GetSelectedPlatform()); 546 if (platform_sp) { 547 std::string cmd_line; 548 args.GetCommandString(cmd_line); 549 lldb::user_id_t fd; 550 if (!llvm::to_integer(cmd_line, fd)) { 551 result.AppendErrorWithFormatv("'{0}' is not a valid file descriptor.\n", 552 cmd_line); 553 return result.Succeeded(); 554 } 555 Status error; 556 bool success = platform_sp->CloseFile(fd, error); 557 if (success) { 558 result.AppendMessageWithFormat("file %" PRIu64 " closed.\n", fd); 559 result.SetStatus(eReturnStatusSuccessFinishResult); 560 } else { 561 result.AppendError(error.AsCString()); 562 } 563 } else { 564 result.AppendError("no platform currently selected\n"); 565 } 566 return result.Succeeded(); 567 } 568 }; 569 570 // "platform fread" 571 572 #define LLDB_OPTIONS_platform_fread 573 #include "CommandOptions.inc" 574 575 class CommandObjectPlatformFRead : public CommandObjectParsed { 576 public: 577 CommandObjectPlatformFRead(CommandInterpreter &interpreter) 578 : CommandObjectParsed(interpreter, "platform file read", 579 "Read data from a file on the remote end.", nullptr, 580 0) { 581 CommandArgumentData path_arg{eArgTypeUnsignedInteger, eArgRepeatPlain}; 582 m_arguments.push_back({path_arg}); 583 } 584 585 ~CommandObjectPlatformFRead() override = default; 586 587 bool DoExecute(Args &args, CommandReturnObject &result) override { 588 PlatformSP platform_sp( 589 GetDebugger().GetPlatformList().GetSelectedPlatform()); 590 if (platform_sp) { 591 std::string cmd_line; 592 args.GetCommandString(cmd_line); 593 lldb::user_id_t fd; 594 if (!llvm::to_integer(cmd_line, fd)) { 595 result.AppendErrorWithFormatv("'{0}' is not a valid file descriptor.\n", 596 cmd_line); 597 return result.Succeeded(); 598 } 599 std::string buffer(m_options.m_count, 0); 600 Status error; 601 uint64_t retcode = platform_sp->ReadFile( 602 fd, m_options.m_offset, &buffer[0], m_options.m_count, error); 603 if (retcode != UINT64_MAX) { 604 result.AppendMessageWithFormat("Return = %" PRIu64 "\n", retcode); 605 result.AppendMessageWithFormat("Data = \"%s\"\n", buffer.c_str()); 606 result.SetStatus(eReturnStatusSuccessFinishResult); 607 } else { 608 result.AppendError(error.AsCString()); 609 } 610 } else { 611 result.AppendError("no platform currently selected\n"); 612 } 613 return result.Succeeded(); 614 } 615 616 Options *GetOptions() override { return &m_options; } 617 618 protected: 619 class CommandOptions : public Options { 620 public: 621 CommandOptions() = default; 622 623 ~CommandOptions() override = default; 624 625 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 626 ExecutionContext *execution_context) override { 627 Status error; 628 char short_option = (char)m_getopt_table[option_idx].val; 629 630 switch (short_option) { 631 case 'o': 632 if (option_arg.getAsInteger(0, m_offset)) 633 error.SetErrorStringWithFormat("invalid offset: '%s'", 634 option_arg.str().c_str()); 635 break; 636 case 'c': 637 if (option_arg.getAsInteger(0, m_count)) 638 error.SetErrorStringWithFormat("invalid offset: '%s'", 639 option_arg.str().c_str()); 640 break; 641 default: 642 llvm_unreachable("Unimplemented option"); 643 } 644 645 return error; 646 } 647 648 void OptionParsingStarting(ExecutionContext *execution_context) override { 649 m_offset = 0; 650 m_count = 1; 651 } 652 653 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 654 return llvm::makeArrayRef(g_platform_fread_options); 655 } 656 657 // Instance variables to hold the values for command options. 658 659 uint32_t m_offset; 660 uint32_t m_count; 661 }; 662 663 CommandOptions m_options; 664 }; 665 666 // "platform fwrite" 667 668 #define LLDB_OPTIONS_platform_fwrite 669 #include "CommandOptions.inc" 670 671 class CommandObjectPlatformFWrite : public CommandObjectParsed { 672 public: 673 CommandObjectPlatformFWrite(CommandInterpreter &interpreter) 674 : CommandObjectParsed(interpreter, "platform file write", 675 "Write data to a file on the remote end.", nullptr, 676 0) { 677 CommandArgumentData path_arg{eArgTypeUnsignedInteger, eArgRepeatPlain}; 678 m_arguments.push_back({path_arg}); 679 } 680 681 ~CommandObjectPlatformFWrite() override = default; 682 683 bool DoExecute(Args &args, CommandReturnObject &result) override { 684 PlatformSP platform_sp( 685 GetDebugger().GetPlatformList().GetSelectedPlatform()); 686 if (platform_sp) { 687 std::string cmd_line; 688 args.GetCommandString(cmd_line); 689 Status error; 690 lldb::user_id_t fd; 691 if (!llvm::to_integer(cmd_line, fd)) { 692 result.AppendErrorWithFormatv("'{0}' is not a valid file descriptor.", 693 cmd_line); 694 return result.Succeeded(); 695 } 696 uint64_t retcode = 697 platform_sp->WriteFile(fd, m_options.m_offset, &m_options.m_data[0], 698 m_options.m_data.size(), error); 699 if (retcode != UINT64_MAX) { 700 result.AppendMessageWithFormat("Return = %" PRIu64 "\n", retcode); 701 result.SetStatus(eReturnStatusSuccessFinishResult); 702 } else { 703 result.AppendError(error.AsCString()); 704 } 705 } else { 706 result.AppendError("no platform currently selected\n"); 707 } 708 return result.Succeeded(); 709 } 710 711 Options *GetOptions() override { return &m_options; } 712 713 protected: 714 class CommandOptions : public Options { 715 public: 716 CommandOptions() = default; 717 718 ~CommandOptions() override = default; 719 720 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 721 ExecutionContext *execution_context) override { 722 Status error; 723 char short_option = (char)m_getopt_table[option_idx].val; 724 725 switch (short_option) { 726 case 'o': 727 if (option_arg.getAsInteger(0, m_offset)) 728 error.SetErrorStringWithFormat("invalid offset: '%s'", 729 option_arg.str().c_str()); 730 break; 731 case 'd': 732 m_data.assign(std::string(option_arg)); 733 break; 734 default: 735 llvm_unreachable("Unimplemented option"); 736 } 737 738 return error; 739 } 740 741 void OptionParsingStarting(ExecutionContext *execution_context) override { 742 m_offset = 0; 743 m_data.clear(); 744 } 745 746 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 747 return llvm::makeArrayRef(g_platform_fwrite_options); 748 } 749 750 // Instance variables to hold the values for command options. 751 752 uint32_t m_offset; 753 std::string m_data; 754 }; 755 756 CommandOptions m_options; 757 }; 758 759 class CommandObjectPlatformFile : public CommandObjectMultiword { 760 public: 761 // Constructors and Destructors 762 CommandObjectPlatformFile(CommandInterpreter &interpreter) 763 : CommandObjectMultiword( 764 interpreter, "platform file", 765 "Commands to access files on the current platform.", 766 "platform file [open|close|read|write] ...") { 767 LoadSubCommand( 768 "open", CommandObjectSP(new CommandObjectPlatformFOpen(interpreter))); 769 LoadSubCommand( 770 "close", CommandObjectSP(new CommandObjectPlatformFClose(interpreter))); 771 LoadSubCommand( 772 "read", CommandObjectSP(new CommandObjectPlatformFRead(interpreter))); 773 LoadSubCommand( 774 "write", CommandObjectSP(new CommandObjectPlatformFWrite(interpreter))); 775 } 776 777 ~CommandObjectPlatformFile() override = default; 778 779 private: 780 // For CommandObjectPlatform only 781 CommandObjectPlatformFile(const CommandObjectPlatformFile &) = delete; 782 const CommandObjectPlatformFile & 783 operator=(const CommandObjectPlatformFile &) = delete; 784 }; 785 786 // "platform get-file remote-file-path host-file-path" 787 class CommandObjectPlatformGetFile : public CommandObjectParsed { 788 public: 789 CommandObjectPlatformGetFile(CommandInterpreter &interpreter) 790 : CommandObjectParsed( 791 interpreter, "platform get-file", 792 "Transfer a file from the remote end to the local host.", 793 "platform get-file <remote-file-spec> <local-file-spec>", 0) { 794 SetHelpLong( 795 R"(Examples: 796 797 (lldb) platform get-file /the/remote/file/path /the/local/file/path 798 799 Transfer a file from the remote end with file path /the/remote/file/path to the local host.)"); 800 801 CommandArgumentEntry arg1, arg2; 802 CommandArgumentData file_arg_remote, file_arg_host; 803 804 // Define the first (and only) variant of this arg. 805 file_arg_remote.arg_type = eArgTypeFilename; 806 file_arg_remote.arg_repetition = eArgRepeatPlain; 807 // There is only one variant this argument could be; put it into the 808 // argument entry. 809 arg1.push_back(file_arg_remote); 810 811 // Define the second (and only) variant of this arg. 812 file_arg_host.arg_type = eArgTypeFilename; 813 file_arg_host.arg_repetition = eArgRepeatPlain; 814 // There is only one variant this argument could be; put it into the 815 // argument entry. 816 arg2.push_back(file_arg_host); 817 818 // Push the data for the first and the second arguments into the 819 // m_arguments vector. 820 m_arguments.push_back(arg1); 821 m_arguments.push_back(arg2); 822 } 823 824 ~CommandObjectPlatformGetFile() override = default; 825 826 void 827 HandleArgumentCompletion(CompletionRequest &request, 828 OptionElementVector &opt_element_vector) override { 829 if (request.GetCursorIndex() == 0) 830 CommandCompletions::InvokeCommonCompletionCallbacks( 831 GetCommandInterpreter(), 832 CommandCompletions::eRemoteDiskFileCompletion, request, nullptr); 833 else if (request.GetCursorIndex() == 1) 834 CommandCompletions::InvokeCommonCompletionCallbacks( 835 GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion, 836 request, nullptr); 837 } 838 839 bool DoExecute(Args &args, CommandReturnObject &result) override { 840 // If the number of arguments is incorrect, issue an error message. 841 if (args.GetArgumentCount() != 2) { 842 result.AppendError("required arguments missing; specify both the " 843 "source and destination file paths"); 844 return false; 845 } 846 847 PlatformSP platform_sp( 848 GetDebugger().GetPlatformList().GetSelectedPlatform()); 849 if (platform_sp) { 850 const char *remote_file_path = args.GetArgumentAtIndex(0); 851 const char *local_file_path = args.GetArgumentAtIndex(1); 852 Status error = platform_sp->GetFile(FileSpec(remote_file_path), 853 FileSpec(local_file_path)); 854 if (error.Success()) { 855 result.AppendMessageWithFormat( 856 "successfully get-file from %s (remote) to %s (host)\n", 857 remote_file_path, local_file_path); 858 result.SetStatus(eReturnStatusSuccessFinishResult); 859 } else { 860 result.AppendMessageWithFormat("get-file failed: %s\n", 861 error.AsCString()); 862 } 863 } else { 864 result.AppendError("no platform currently selected\n"); 865 } 866 return result.Succeeded(); 867 } 868 }; 869 870 // "platform get-size remote-file-path" 871 class CommandObjectPlatformGetSize : public CommandObjectParsed { 872 public: 873 CommandObjectPlatformGetSize(CommandInterpreter &interpreter) 874 : CommandObjectParsed(interpreter, "platform get-size", 875 "Get the file size from the remote end.", 876 "platform get-size <remote-file-spec>", 0) { 877 SetHelpLong( 878 R"(Examples: 879 880 (lldb) platform get-size /the/remote/file/path 881 882 Get the file size from the remote end with path /the/remote/file/path.)"); 883 884 CommandArgumentEntry arg1; 885 CommandArgumentData file_arg_remote; 886 887 // Define the first (and only) variant of this arg. 888 file_arg_remote.arg_type = eArgTypeFilename; 889 file_arg_remote.arg_repetition = eArgRepeatPlain; 890 // There is only one variant this argument could be; put it into the 891 // argument entry. 892 arg1.push_back(file_arg_remote); 893 894 // Push the data for the first argument into the m_arguments vector. 895 m_arguments.push_back(arg1); 896 } 897 898 ~CommandObjectPlatformGetSize() override = default; 899 900 void 901 HandleArgumentCompletion(CompletionRequest &request, 902 OptionElementVector &opt_element_vector) override { 903 if (request.GetCursorIndex() != 0) 904 return; 905 906 CommandCompletions::InvokeCommonCompletionCallbacks( 907 GetCommandInterpreter(), CommandCompletions::eRemoteDiskFileCompletion, 908 request, nullptr); 909 } 910 911 bool DoExecute(Args &args, CommandReturnObject &result) override { 912 // If the number of arguments is incorrect, issue an error message. 913 if (args.GetArgumentCount() != 1) { 914 result.AppendError("required argument missing; specify the source file " 915 "path as the only argument"); 916 return false; 917 } 918 919 PlatformSP platform_sp( 920 GetDebugger().GetPlatformList().GetSelectedPlatform()); 921 if (platform_sp) { 922 std::string remote_file_path(args.GetArgumentAtIndex(0)); 923 user_id_t size = platform_sp->GetFileSize(FileSpec(remote_file_path)); 924 if (size != UINT64_MAX) { 925 result.AppendMessageWithFormat("File size of %s (remote): %" PRIu64 926 "\n", 927 remote_file_path.c_str(), size); 928 result.SetStatus(eReturnStatusSuccessFinishResult); 929 } else { 930 result.AppendMessageWithFormat( 931 "Error getting file size of %s (remote)\n", 932 remote_file_path.c_str()); 933 } 934 } else { 935 result.AppendError("no platform currently selected\n"); 936 } 937 return result.Succeeded(); 938 } 939 }; 940 941 // "platform get-permissions remote-file-path" 942 class CommandObjectPlatformGetPermissions : public CommandObjectParsed { 943 public: 944 CommandObjectPlatformGetPermissions(CommandInterpreter &interpreter) 945 : CommandObjectParsed(interpreter, "platform get-permissions", 946 "Get the file permission bits from the remote end.", 947 "platform get-permissions <remote-file-spec>", 0) { 948 SetHelpLong( 949 R"(Examples: 950 951 (lldb) platform get-permissions /the/remote/file/path 952 953 Get the file permissions from the remote end with path /the/remote/file/path.)"); 954 955 CommandArgumentEntry arg1; 956 CommandArgumentData file_arg_remote; 957 958 // Define the first (and only) variant of this arg. 959 file_arg_remote.arg_type = eArgTypeFilename; 960 file_arg_remote.arg_repetition = eArgRepeatPlain; 961 // There is only one variant this argument could be; put it into the 962 // argument entry. 963 arg1.push_back(file_arg_remote); 964 965 // Push the data for the first argument into the m_arguments vector. 966 m_arguments.push_back(arg1); 967 } 968 969 ~CommandObjectPlatformGetPermissions() override = default; 970 971 void 972 HandleArgumentCompletion(CompletionRequest &request, 973 OptionElementVector &opt_element_vector) override { 974 if (request.GetCursorIndex() != 0) 975 return; 976 977 CommandCompletions::InvokeCommonCompletionCallbacks( 978 GetCommandInterpreter(), CommandCompletions::eRemoteDiskFileCompletion, 979 request, nullptr); 980 } 981 982 bool DoExecute(Args &args, CommandReturnObject &result) override { 983 // If the number of arguments is incorrect, issue an error message. 984 if (args.GetArgumentCount() != 1) { 985 result.AppendError("required argument missing; specify the source file " 986 "path as the only argument"); 987 return false; 988 } 989 990 PlatformSP platform_sp( 991 GetDebugger().GetPlatformList().GetSelectedPlatform()); 992 if (platform_sp) { 993 std::string remote_file_path(args.GetArgumentAtIndex(0)); 994 uint32_t permissions; 995 Status error = platform_sp->GetFilePermissions(FileSpec(remote_file_path), 996 permissions); 997 if (error.Success()) { 998 result.AppendMessageWithFormat( 999 "File permissions of %s (remote): 0o%04" PRIo32 "\n", 1000 remote_file_path.c_str(), permissions); 1001 result.SetStatus(eReturnStatusSuccessFinishResult); 1002 } else 1003 result.AppendError(error.AsCString()); 1004 } else { 1005 result.AppendError("no platform currently selected\n"); 1006 } 1007 return result.Succeeded(); 1008 } 1009 }; 1010 1011 // "platform file-exists remote-file-path" 1012 class CommandObjectPlatformFileExists : public CommandObjectParsed { 1013 public: 1014 CommandObjectPlatformFileExists(CommandInterpreter &interpreter) 1015 : CommandObjectParsed(interpreter, "platform file-exists", 1016 "Check if the file exists on the remote end.", 1017 "platform file-exists <remote-file-spec>", 0) { 1018 SetHelpLong( 1019 R"(Examples: 1020 1021 (lldb) platform file-exists /the/remote/file/path 1022 1023 Check if /the/remote/file/path exists on the remote end.)"); 1024 1025 CommandArgumentEntry arg1; 1026 CommandArgumentData file_arg_remote; 1027 1028 // Define the first (and only) variant of this arg. 1029 file_arg_remote.arg_type = eArgTypeFilename; 1030 file_arg_remote.arg_repetition = eArgRepeatPlain; 1031 // There is only one variant this argument could be; put it into the 1032 // argument entry. 1033 arg1.push_back(file_arg_remote); 1034 1035 // Push the data for the first argument into the m_arguments vector. 1036 m_arguments.push_back(arg1); 1037 } 1038 1039 ~CommandObjectPlatformFileExists() override = default; 1040 1041 void 1042 HandleArgumentCompletion(CompletionRequest &request, 1043 OptionElementVector &opt_element_vector) override { 1044 if (request.GetCursorIndex() != 0) 1045 return; 1046 1047 CommandCompletions::InvokeCommonCompletionCallbacks( 1048 GetCommandInterpreter(), CommandCompletions::eRemoteDiskFileCompletion, 1049 request, nullptr); 1050 } 1051 1052 bool DoExecute(Args &args, CommandReturnObject &result) override { 1053 // If the number of arguments is incorrect, issue an error message. 1054 if (args.GetArgumentCount() != 1) { 1055 result.AppendError("required argument missing; specify the source file " 1056 "path as the only argument"); 1057 return false; 1058 } 1059 1060 PlatformSP platform_sp( 1061 GetDebugger().GetPlatformList().GetSelectedPlatform()); 1062 if (platform_sp) { 1063 std::string remote_file_path(args.GetArgumentAtIndex(0)); 1064 bool exists = platform_sp->GetFileExists(FileSpec(remote_file_path)); 1065 result.AppendMessageWithFormat( 1066 "File %s (remote) %s\n", 1067 remote_file_path.c_str(), exists ? "exists" : "does not exist"); 1068 result.SetStatus(eReturnStatusSuccessFinishResult); 1069 } else { 1070 result.AppendError("no platform currently selected\n"); 1071 } 1072 return result.Succeeded(); 1073 } 1074 }; 1075 1076 // "platform put-file" 1077 class CommandObjectPlatformPutFile : public CommandObjectParsed { 1078 public: 1079 CommandObjectPlatformPutFile(CommandInterpreter &interpreter) 1080 : CommandObjectParsed( 1081 interpreter, "platform put-file", 1082 "Transfer a file from this system to the remote end.", 1083 "platform put-file <source> [<destination>]", 0) { 1084 SetHelpLong( 1085 R"(Examples: 1086 1087 (lldb) platform put-file /source/foo.txt /destination/bar.txt 1088 1089 (lldb) platform put-file /source/foo.txt 1090 1091 Relative source file paths are resolved against lldb's local working directory. 1092 1093 Omitting the destination places the file in the platform working directory.)"); 1094 CommandArgumentData source_arg{eArgTypePath, eArgRepeatPlain}; 1095 CommandArgumentData path_arg{eArgTypePath, eArgRepeatOptional}; 1096 m_arguments.push_back({source_arg}); 1097 m_arguments.push_back({path_arg}); 1098 } 1099 1100 ~CommandObjectPlatformPutFile() override = default; 1101 1102 void 1103 HandleArgumentCompletion(CompletionRequest &request, 1104 OptionElementVector &opt_element_vector) override { 1105 if (request.GetCursorIndex() == 0) 1106 CommandCompletions::InvokeCommonCompletionCallbacks( 1107 GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion, 1108 request, nullptr); 1109 else if (request.GetCursorIndex() == 1) 1110 CommandCompletions::InvokeCommonCompletionCallbacks( 1111 GetCommandInterpreter(), 1112 CommandCompletions::eRemoteDiskFileCompletion, request, nullptr); 1113 } 1114 1115 bool DoExecute(Args &args, CommandReturnObject &result) override { 1116 const char *src = args.GetArgumentAtIndex(0); 1117 const char *dst = args.GetArgumentAtIndex(1); 1118 1119 FileSpec src_fs(src); 1120 FileSystem::Instance().Resolve(src_fs); 1121 FileSpec dst_fs(dst ? dst : src_fs.GetFilename().GetCString()); 1122 1123 PlatformSP platform_sp( 1124 GetDebugger().GetPlatformList().GetSelectedPlatform()); 1125 if (platform_sp) { 1126 Status error(platform_sp->PutFile(src_fs, dst_fs)); 1127 if (error.Success()) { 1128 result.SetStatus(eReturnStatusSuccessFinishNoResult); 1129 } else { 1130 result.AppendError(error.AsCString()); 1131 } 1132 } else { 1133 result.AppendError("no platform currently selected\n"); 1134 } 1135 return result.Succeeded(); 1136 } 1137 }; 1138 1139 // "platform process launch" 1140 class CommandObjectPlatformProcessLaunch : public CommandObjectParsed { 1141 public: 1142 CommandObjectPlatformProcessLaunch(CommandInterpreter &interpreter) 1143 : CommandObjectParsed(interpreter, "platform process launch", 1144 "Launch a new process on a remote platform.", 1145 "platform process launch program", 1146 eCommandRequiresTarget | eCommandTryTargetAPILock) { 1147 m_all_options.Append(&m_options); 1148 m_all_options.Finalize(); 1149 CommandArgumentData run_arg_arg{eArgTypeRunArgs, eArgRepeatStar}; 1150 m_arguments.push_back({run_arg_arg}); 1151 } 1152 1153 ~CommandObjectPlatformProcessLaunch() override = default; 1154 1155 Options *GetOptions() override { return &m_all_options; } 1156 1157 protected: 1158 bool DoExecute(Args &args, CommandReturnObject &result) override { 1159 Target *target = GetDebugger().GetSelectedTarget().get(); 1160 PlatformSP platform_sp; 1161 if (target) { 1162 platform_sp = target->GetPlatform(); 1163 } 1164 if (!platform_sp) { 1165 platform_sp = GetDebugger().GetPlatformList().GetSelectedPlatform(); 1166 } 1167 1168 if (platform_sp) { 1169 Status error; 1170 const size_t argc = args.GetArgumentCount(); 1171 Target *target = m_exe_ctx.GetTargetPtr(); 1172 Module *exe_module = target->GetExecutableModulePointer(); 1173 if (exe_module) { 1174 m_options.launch_info.GetExecutableFile() = exe_module->GetFileSpec(); 1175 llvm::SmallString<128> exe_path; 1176 m_options.launch_info.GetExecutableFile().GetPath(exe_path); 1177 if (!exe_path.empty()) 1178 m_options.launch_info.GetArguments().AppendArgument(exe_path); 1179 m_options.launch_info.GetArchitecture() = exe_module->GetArchitecture(); 1180 } 1181 1182 if (argc > 0) { 1183 if (m_options.launch_info.GetExecutableFile()) { 1184 // We already have an executable file, so we will use this and all 1185 // arguments to this function are extra arguments 1186 m_options.launch_info.GetArguments().AppendArguments(args); 1187 } else { 1188 // We don't have any file yet, so the first argument is our 1189 // executable, and the rest are program arguments 1190 const bool first_arg_is_executable = true; 1191 m_options.launch_info.SetArguments(args, first_arg_is_executable); 1192 } 1193 } 1194 1195 if (m_options.launch_info.GetExecutableFile()) { 1196 Debugger &debugger = GetDebugger(); 1197 1198 if (argc == 0) 1199 target->GetRunArguments(m_options.launch_info.GetArguments()); 1200 1201 ProcessSP process_sp(platform_sp->DebugProcess( 1202 m_options.launch_info, debugger, *target, error)); 1203 if (process_sp && process_sp->IsAlive()) { 1204 result.SetStatus(eReturnStatusSuccessFinishNoResult); 1205 return true; 1206 } 1207 1208 if (error.Success()) 1209 result.AppendError("process launch failed"); 1210 else 1211 result.AppendError(error.AsCString()); 1212 } else { 1213 result.AppendError("'platform process launch' uses the current target " 1214 "file and arguments, or the executable and its " 1215 "arguments can be specified in this command"); 1216 return false; 1217 } 1218 } else { 1219 result.AppendError("no platform is selected\n"); 1220 } 1221 return result.Succeeded(); 1222 } 1223 1224 CommandOptionsProcessLaunch m_options; 1225 OptionGroupOptions m_all_options; 1226 }; 1227 1228 // "platform process list" 1229 1230 static PosixPlatformCommandOptionValidator posix_validator; 1231 #define LLDB_OPTIONS_platform_process_list 1232 #include "CommandOptions.inc" 1233 1234 class CommandObjectPlatformProcessList : public CommandObjectParsed { 1235 public: 1236 CommandObjectPlatformProcessList(CommandInterpreter &interpreter) 1237 : CommandObjectParsed(interpreter, "platform process list", 1238 "List processes on a remote platform by name, pid, " 1239 "or many other matching attributes.", 1240 "platform process list", 0) {} 1241 1242 ~CommandObjectPlatformProcessList() override = default; 1243 1244 Options *GetOptions() override { return &m_options; } 1245 1246 protected: 1247 bool DoExecute(Args &args, CommandReturnObject &result) override { 1248 Target *target = GetDebugger().GetSelectedTarget().get(); 1249 PlatformSP platform_sp; 1250 if (target) { 1251 platform_sp = target->GetPlatform(); 1252 } 1253 if (!platform_sp) { 1254 platform_sp = GetDebugger().GetPlatformList().GetSelectedPlatform(); 1255 } 1256 1257 if (platform_sp) { 1258 Status error; 1259 if (platform_sp) { 1260 Stream &ostrm = result.GetOutputStream(); 1261 1262 lldb::pid_t pid = m_options.match_info.GetProcessInfo().GetProcessID(); 1263 if (pid != LLDB_INVALID_PROCESS_ID) { 1264 ProcessInstanceInfo proc_info; 1265 if (platform_sp->GetProcessInfo(pid, proc_info)) { 1266 ProcessInstanceInfo::DumpTableHeader(ostrm, m_options.show_args, 1267 m_options.verbose); 1268 proc_info.DumpAsTableRow(ostrm, platform_sp->GetUserIDResolver(), 1269 m_options.show_args, m_options.verbose); 1270 result.SetStatus(eReturnStatusSuccessFinishResult); 1271 } else { 1272 result.AppendErrorWithFormat( 1273 "no process found with pid = %" PRIu64 "\n", pid); 1274 } 1275 } else { 1276 ProcessInstanceInfoList proc_infos; 1277 const uint32_t matches = 1278 platform_sp->FindProcesses(m_options.match_info, proc_infos); 1279 const char *match_desc = nullptr; 1280 const char *match_name = 1281 m_options.match_info.GetProcessInfo().GetName(); 1282 if (match_name && match_name[0]) { 1283 switch (m_options.match_info.GetNameMatchType()) { 1284 case NameMatch::Ignore: 1285 break; 1286 case NameMatch::Equals: 1287 match_desc = "matched"; 1288 break; 1289 case NameMatch::Contains: 1290 match_desc = "contained"; 1291 break; 1292 case NameMatch::StartsWith: 1293 match_desc = "started with"; 1294 break; 1295 case NameMatch::EndsWith: 1296 match_desc = "ended with"; 1297 break; 1298 case NameMatch::RegularExpression: 1299 match_desc = "matched the regular expression"; 1300 break; 1301 } 1302 } 1303 1304 if (matches == 0) { 1305 if (match_desc) 1306 result.AppendErrorWithFormatv( 1307 "no processes were found that {0} \"{1}\" on the \"{2}\" " 1308 "platform\n", 1309 match_desc, match_name, platform_sp->GetName()); 1310 else 1311 result.AppendErrorWithFormatv( 1312 "no processes were found on the \"{0}\" platform\n", 1313 platform_sp->GetName()); 1314 } else { 1315 result.AppendMessageWithFormatv( 1316 "{0} matching process{1} found on \"{2}\"", matches, 1317 matches > 1 ? "es were" : " was", platform_sp->GetName()); 1318 if (match_desc) 1319 result.AppendMessageWithFormat(" whose name %s \"%s\"", 1320 match_desc, match_name); 1321 result.AppendMessageWithFormat("\n"); 1322 ProcessInstanceInfo::DumpTableHeader(ostrm, m_options.show_args, 1323 m_options.verbose); 1324 for (uint32_t i = 0; i < matches; ++i) { 1325 proc_infos[i].DumpAsTableRow( 1326 ostrm, platform_sp->GetUserIDResolver(), m_options.show_args, 1327 m_options.verbose); 1328 } 1329 } 1330 } 1331 } 1332 } else { 1333 result.AppendError("no platform is selected\n"); 1334 } 1335 return result.Succeeded(); 1336 } 1337 1338 class CommandOptions : public Options { 1339 public: 1340 CommandOptions() = default; 1341 1342 ~CommandOptions() override = default; 1343 1344 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 1345 ExecutionContext *execution_context) override { 1346 Status error; 1347 const int short_option = m_getopt_table[option_idx].val; 1348 bool success = false; 1349 1350 uint32_t id = LLDB_INVALID_PROCESS_ID; 1351 success = !option_arg.getAsInteger(0, id); 1352 switch (short_option) { 1353 case 'p': { 1354 match_info.GetProcessInfo().SetProcessID(id); 1355 if (!success) 1356 error.SetErrorStringWithFormat("invalid process ID string: '%s'", 1357 option_arg.str().c_str()); 1358 break; 1359 } 1360 case 'P': 1361 match_info.GetProcessInfo().SetParentProcessID(id); 1362 if (!success) 1363 error.SetErrorStringWithFormat( 1364 "invalid parent process ID string: '%s'", 1365 option_arg.str().c_str()); 1366 break; 1367 1368 case 'u': 1369 match_info.GetProcessInfo().SetUserID(success ? id : UINT32_MAX); 1370 if (!success) 1371 error.SetErrorStringWithFormat("invalid user ID string: '%s'", 1372 option_arg.str().c_str()); 1373 break; 1374 1375 case 'U': 1376 match_info.GetProcessInfo().SetEffectiveUserID(success ? id 1377 : UINT32_MAX); 1378 if (!success) 1379 error.SetErrorStringWithFormat( 1380 "invalid effective user ID string: '%s'", 1381 option_arg.str().c_str()); 1382 break; 1383 1384 case 'g': 1385 match_info.GetProcessInfo().SetGroupID(success ? id : UINT32_MAX); 1386 if (!success) 1387 error.SetErrorStringWithFormat("invalid group ID string: '%s'", 1388 option_arg.str().c_str()); 1389 break; 1390 1391 case 'G': 1392 match_info.GetProcessInfo().SetEffectiveGroupID(success ? id 1393 : UINT32_MAX); 1394 if (!success) 1395 error.SetErrorStringWithFormat( 1396 "invalid effective group ID string: '%s'", 1397 option_arg.str().c_str()); 1398 break; 1399 1400 case 'a': { 1401 TargetSP target_sp = 1402 execution_context ? execution_context->GetTargetSP() : TargetSP(); 1403 DebuggerSP debugger_sp = 1404 target_sp ? target_sp->GetDebugger().shared_from_this() 1405 : DebuggerSP(); 1406 PlatformSP platform_sp = 1407 debugger_sp ? debugger_sp->GetPlatformList().GetSelectedPlatform() 1408 : PlatformSP(); 1409 match_info.GetProcessInfo().GetArchitecture() = 1410 Platform::GetAugmentedArchSpec(platform_sp.get(), option_arg); 1411 } break; 1412 1413 case 'n': 1414 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1415 option_arg, FileSpec::Style::native); 1416 match_info.SetNameMatchType(NameMatch::Equals); 1417 break; 1418 1419 case 'e': 1420 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1421 option_arg, FileSpec::Style::native); 1422 match_info.SetNameMatchType(NameMatch::EndsWith); 1423 break; 1424 1425 case 's': 1426 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1427 option_arg, FileSpec::Style::native); 1428 match_info.SetNameMatchType(NameMatch::StartsWith); 1429 break; 1430 1431 case 'c': 1432 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1433 option_arg, FileSpec::Style::native); 1434 match_info.SetNameMatchType(NameMatch::Contains); 1435 break; 1436 1437 case 'r': 1438 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1439 option_arg, FileSpec::Style::native); 1440 match_info.SetNameMatchType(NameMatch::RegularExpression); 1441 break; 1442 1443 case 'A': 1444 show_args = true; 1445 break; 1446 1447 case 'v': 1448 verbose = true; 1449 break; 1450 1451 case 'x': 1452 match_info.SetMatchAllUsers(true); 1453 break; 1454 1455 default: 1456 llvm_unreachable("Unimplemented option"); 1457 } 1458 1459 return error; 1460 } 1461 1462 void OptionParsingStarting(ExecutionContext *execution_context) override { 1463 match_info.Clear(); 1464 show_args = false; 1465 verbose = false; 1466 } 1467 1468 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 1469 return llvm::makeArrayRef(g_platform_process_list_options); 1470 } 1471 1472 // Instance variables to hold the values for command options. 1473 1474 ProcessInstanceInfoMatch match_info; 1475 bool show_args = false; 1476 bool verbose = false; 1477 }; 1478 1479 CommandOptions m_options; 1480 }; 1481 1482 // "platform process info" 1483 class CommandObjectPlatformProcessInfo : public CommandObjectParsed { 1484 public: 1485 CommandObjectPlatformProcessInfo(CommandInterpreter &interpreter) 1486 : CommandObjectParsed( 1487 interpreter, "platform process info", 1488 "Get detailed information for one or more process by process ID.", 1489 "platform process info <pid> [<pid> <pid> ...]", 0) { 1490 CommandArgumentEntry arg; 1491 CommandArgumentData pid_args; 1492 1493 // Define the first (and only) variant of this arg. 1494 pid_args.arg_type = eArgTypePid; 1495 pid_args.arg_repetition = eArgRepeatStar; 1496 1497 // There is only one variant this argument could be; put it into the 1498 // argument entry. 1499 arg.push_back(pid_args); 1500 1501 // Push the data for the first argument into the m_arguments vector. 1502 m_arguments.push_back(arg); 1503 } 1504 1505 ~CommandObjectPlatformProcessInfo() override = default; 1506 1507 void 1508 HandleArgumentCompletion(CompletionRequest &request, 1509 OptionElementVector &opt_element_vector) override { 1510 CommandCompletions::InvokeCommonCompletionCallbacks( 1511 GetCommandInterpreter(), CommandCompletions::eProcessIDCompletion, 1512 request, nullptr); 1513 } 1514 1515 protected: 1516 bool DoExecute(Args &args, CommandReturnObject &result) override { 1517 Target *target = GetDebugger().GetSelectedTarget().get(); 1518 PlatformSP platform_sp; 1519 if (target) { 1520 platform_sp = target->GetPlatform(); 1521 } 1522 if (!platform_sp) { 1523 platform_sp = GetDebugger().GetPlatformList().GetSelectedPlatform(); 1524 } 1525 1526 if (platform_sp) { 1527 const size_t argc = args.GetArgumentCount(); 1528 if (argc > 0) { 1529 Status error; 1530 1531 if (platform_sp->IsConnected()) { 1532 Stream &ostrm = result.GetOutputStream(); 1533 for (auto &entry : args.entries()) { 1534 lldb::pid_t pid; 1535 if (entry.ref().getAsInteger(0, pid)) { 1536 result.AppendErrorWithFormat("invalid process ID argument '%s'", 1537 entry.ref().str().c_str()); 1538 break; 1539 } else { 1540 ProcessInstanceInfo proc_info; 1541 if (platform_sp->GetProcessInfo(pid, proc_info)) { 1542 ostrm.Printf("Process information for process %" PRIu64 ":\n", 1543 pid); 1544 proc_info.Dump(ostrm, platform_sp->GetUserIDResolver()); 1545 } else { 1546 ostrm.Printf("error: no process information is available for " 1547 "process %" PRIu64 "\n", 1548 pid); 1549 } 1550 ostrm.EOL(); 1551 } 1552 } 1553 } else { 1554 // Not connected... 1555 result.AppendErrorWithFormatv("not connected to '{0}'", 1556 platform_sp->GetPluginName()); 1557 } 1558 } else { 1559 // No args 1560 result.AppendError("one or more process id(s) must be specified"); 1561 } 1562 } else { 1563 result.AppendError("no platform is currently selected"); 1564 } 1565 return result.Succeeded(); 1566 } 1567 }; 1568 1569 #define LLDB_OPTIONS_platform_process_attach 1570 #include "CommandOptions.inc" 1571 1572 class CommandObjectPlatformProcessAttach : public CommandObjectParsed { 1573 public: 1574 class CommandOptions : public Options { 1575 public: 1576 CommandOptions() { 1577 // Keep default values of all options in one place: OptionParsingStarting 1578 // () 1579 OptionParsingStarting(nullptr); 1580 } 1581 1582 ~CommandOptions() override = default; 1583 1584 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 1585 ExecutionContext *execution_context) override { 1586 Status error; 1587 char short_option = (char)m_getopt_table[option_idx].val; 1588 switch (short_option) { 1589 case 'p': { 1590 lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; 1591 if (option_arg.getAsInteger(0, pid)) { 1592 error.SetErrorStringWithFormat("invalid process ID '%s'", 1593 option_arg.str().c_str()); 1594 } else { 1595 attach_info.SetProcessID(pid); 1596 } 1597 } break; 1598 1599 case 'P': 1600 attach_info.SetProcessPluginName(option_arg); 1601 break; 1602 1603 case 'n': 1604 attach_info.GetExecutableFile().SetFile(option_arg, 1605 FileSpec::Style::native); 1606 break; 1607 1608 case 'w': 1609 attach_info.SetWaitForLaunch(true); 1610 break; 1611 1612 default: 1613 llvm_unreachable("Unimplemented option"); 1614 } 1615 return error; 1616 } 1617 1618 void OptionParsingStarting(ExecutionContext *execution_context) override { 1619 attach_info.Clear(); 1620 } 1621 1622 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 1623 return llvm::makeArrayRef(g_platform_process_attach_options); 1624 } 1625 1626 // Options table: Required for subclasses of Options. 1627 1628 static OptionDefinition g_option_table[]; 1629 1630 // Instance variables to hold the values for command options. 1631 1632 ProcessAttachInfo attach_info; 1633 }; 1634 1635 CommandObjectPlatformProcessAttach(CommandInterpreter &interpreter) 1636 : CommandObjectParsed(interpreter, "platform process attach", 1637 "Attach to a process.", 1638 "platform process attach <cmd-options>") {} 1639 1640 ~CommandObjectPlatformProcessAttach() override = default; 1641 1642 bool DoExecute(Args &command, CommandReturnObject &result) override { 1643 PlatformSP platform_sp( 1644 GetDebugger().GetPlatformList().GetSelectedPlatform()); 1645 if (platform_sp) { 1646 Status err; 1647 ProcessSP remote_process_sp = platform_sp->Attach( 1648 m_options.attach_info, GetDebugger(), nullptr, err); 1649 if (err.Fail()) { 1650 result.AppendError(err.AsCString()); 1651 } else if (!remote_process_sp) { 1652 result.AppendError("could not attach: unknown reason"); 1653 } else 1654 result.SetStatus(eReturnStatusSuccessFinishResult); 1655 } else { 1656 result.AppendError("no platform is currently selected"); 1657 } 1658 return result.Succeeded(); 1659 } 1660 1661 Options *GetOptions() override { return &m_options; } 1662 1663 protected: 1664 CommandOptions m_options; 1665 }; 1666 1667 class CommandObjectPlatformProcess : public CommandObjectMultiword { 1668 public: 1669 // Constructors and Destructors 1670 CommandObjectPlatformProcess(CommandInterpreter &interpreter) 1671 : CommandObjectMultiword(interpreter, "platform process", 1672 "Commands to query, launch and attach to " 1673 "processes on the current platform.", 1674 "platform process [attach|launch|list] ...") { 1675 LoadSubCommand( 1676 "attach", 1677 CommandObjectSP(new CommandObjectPlatformProcessAttach(interpreter))); 1678 LoadSubCommand( 1679 "launch", 1680 CommandObjectSP(new CommandObjectPlatformProcessLaunch(interpreter))); 1681 LoadSubCommand("info", CommandObjectSP(new CommandObjectPlatformProcessInfo( 1682 interpreter))); 1683 LoadSubCommand("list", CommandObjectSP(new CommandObjectPlatformProcessList( 1684 interpreter))); 1685 } 1686 1687 ~CommandObjectPlatformProcess() override = default; 1688 1689 private: 1690 // For CommandObjectPlatform only 1691 CommandObjectPlatformProcess(const CommandObjectPlatformProcess &) = delete; 1692 const CommandObjectPlatformProcess & 1693 operator=(const CommandObjectPlatformProcess &) = delete; 1694 }; 1695 1696 // "platform shell" 1697 #define LLDB_OPTIONS_platform_shell 1698 #include "CommandOptions.inc" 1699 1700 class CommandObjectPlatformShell : public CommandObjectRaw { 1701 public: 1702 class CommandOptions : public Options { 1703 public: 1704 CommandOptions() = default; 1705 1706 ~CommandOptions() override = default; 1707 1708 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 1709 return llvm::makeArrayRef(g_platform_shell_options); 1710 } 1711 1712 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 1713 ExecutionContext *execution_context) override { 1714 Status error; 1715 1716 const char short_option = (char)GetDefinitions()[option_idx].short_option; 1717 1718 switch (short_option) { 1719 case 'h': 1720 m_use_host_platform = true; 1721 break; 1722 case 't': 1723 uint32_t timeout_sec; 1724 if (option_arg.getAsInteger(10, timeout_sec)) 1725 error.SetErrorStringWithFormat( 1726 "could not convert \"%s\" to a numeric value.", 1727 option_arg.str().c_str()); 1728 else 1729 m_timeout = std::chrono::seconds(timeout_sec); 1730 break; 1731 case 's': { 1732 if (option_arg.empty()) { 1733 error.SetErrorStringWithFormat( 1734 "missing shell interpreter path for option -i|--interpreter."); 1735 return error; 1736 } 1737 1738 m_shell_interpreter = option_arg.str(); 1739 break; 1740 } 1741 default: 1742 llvm_unreachable("Unimplemented option"); 1743 } 1744 1745 return error; 1746 } 1747 1748 void OptionParsingStarting(ExecutionContext *execution_context) override { 1749 m_timeout.reset(); 1750 m_use_host_platform = false; 1751 m_shell_interpreter.clear(); 1752 } 1753 1754 Timeout<std::micro> m_timeout = std::chrono::seconds(10); 1755 bool m_use_host_platform; 1756 std::string m_shell_interpreter; 1757 }; 1758 1759 CommandObjectPlatformShell(CommandInterpreter &interpreter) 1760 : CommandObjectRaw(interpreter, "platform shell", 1761 "Run a shell command on the current platform.", 1762 "platform shell <shell-command>", 0) { 1763 CommandArgumentData thread_arg{eArgTypeNone, eArgRepeatStar}; 1764 m_arguments.push_back({thread_arg}); 1765 } 1766 1767 ~CommandObjectPlatformShell() override = default; 1768 1769 Options *GetOptions() override { return &m_options; } 1770 1771 bool DoExecute(llvm::StringRef raw_command_line, 1772 CommandReturnObject &result) override { 1773 ExecutionContext exe_ctx = GetCommandInterpreter().GetExecutionContext(); 1774 m_options.NotifyOptionParsingStarting(&exe_ctx); 1775 1776 // Print out an usage syntax on an empty command line. 1777 if (raw_command_line.empty()) { 1778 result.GetOutputStream().Printf("%s\n", this->GetSyntax().str().c_str()); 1779 return true; 1780 } 1781 1782 const bool is_alias = !raw_command_line.contains("platform"); 1783 OptionsWithRaw args(raw_command_line); 1784 1785 if (args.HasArgs()) 1786 if (!ParseOptions(args.GetArgs(), result)) 1787 return false; 1788 1789 if (args.GetRawPart().empty()) { 1790 result.GetOutputStream().Printf("%s <shell-command>\n", 1791 is_alias ? "shell" : "platform shell"); 1792 return false; 1793 } 1794 1795 llvm::StringRef cmd = args.GetRawPart(); 1796 1797 PlatformSP platform_sp( 1798 m_options.m_use_host_platform 1799 ? Platform::GetHostPlatform() 1800 : GetDebugger().GetPlatformList().GetSelectedPlatform()); 1801 Status error; 1802 if (platform_sp) { 1803 FileSpec working_dir{}; 1804 std::string output; 1805 int status = -1; 1806 int signo = -1; 1807 error = (platform_sp->RunShellCommand(m_options.m_shell_interpreter, cmd, 1808 working_dir, &status, &signo, 1809 &output, m_options.m_timeout)); 1810 if (!output.empty()) 1811 result.GetOutputStream().PutCString(output); 1812 if (status > 0) { 1813 if (signo > 0) { 1814 const char *signo_cstr = Host::GetSignalAsCString(signo); 1815 if (signo_cstr) 1816 result.GetOutputStream().Printf( 1817 "error: command returned with status %i and signal %s\n", 1818 status, signo_cstr); 1819 else 1820 result.GetOutputStream().Printf( 1821 "error: command returned with status %i and signal %i\n", 1822 status, signo); 1823 } else 1824 result.GetOutputStream().Printf( 1825 "error: command returned with status %i\n", status); 1826 } 1827 } else { 1828 result.GetOutputStream().Printf( 1829 "error: cannot run remote shell commands without a platform\n"); 1830 error.SetErrorString( 1831 "error: cannot run remote shell commands without a platform"); 1832 } 1833 1834 if (error.Fail()) { 1835 result.AppendError(error.AsCString()); 1836 } else { 1837 result.SetStatus(eReturnStatusSuccessFinishResult); 1838 } 1839 return true; 1840 } 1841 1842 CommandOptions m_options; 1843 }; 1844 1845 // "platform install" - install a target to a remote end 1846 class CommandObjectPlatformInstall : public CommandObjectParsed { 1847 public: 1848 CommandObjectPlatformInstall(CommandInterpreter &interpreter) 1849 : CommandObjectParsed( 1850 interpreter, "platform target-install", 1851 "Install a target (bundle or executable file) to the remote end.", 1852 "platform target-install <local-thing> <remote-sandbox>", 0) { 1853 CommandArgumentData local_arg{eArgTypePath, eArgRepeatPlain}; 1854 CommandArgumentData remote_arg{eArgTypePath, eArgRepeatPlain}; 1855 m_arguments.push_back({local_arg}); 1856 m_arguments.push_back({remote_arg}); 1857 } 1858 1859 ~CommandObjectPlatformInstall() override = default; 1860 1861 void 1862 HandleArgumentCompletion(CompletionRequest &request, 1863 OptionElementVector &opt_element_vector) override { 1864 if (request.GetCursorIndex()) 1865 return; 1866 CommandCompletions::InvokeCommonCompletionCallbacks( 1867 GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion, 1868 request, nullptr); 1869 } 1870 1871 bool DoExecute(Args &args, CommandReturnObject &result) override { 1872 if (args.GetArgumentCount() != 2) { 1873 result.AppendError("platform target-install takes two arguments"); 1874 return false; 1875 } 1876 // TODO: move the bulk of this code over to the platform itself 1877 FileSpec src(args.GetArgumentAtIndex(0)); 1878 FileSystem::Instance().Resolve(src); 1879 FileSpec dst(args.GetArgumentAtIndex(1)); 1880 if (!FileSystem::Instance().Exists(src)) { 1881 result.AppendError("source location does not exist or is not accessible"); 1882 return false; 1883 } 1884 PlatformSP platform_sp( 1885 GetDebugger().GetPlatformList().GetSelectedPlatform()); 1886 if (!platform_sp) { 1887 result.AppendError("no platform currently selected"); 1888 return false; 1889 } 1890 1891 Status error = platform_sp->Install(src, dst); 1892 if (error.Success()) { 1893 result.SetStatus(eReturnStatusSuccessFinishNoResult); 1894 } else { 1895 result.AppendErrorWithFormat("install failed: %s", error.AsCString()); 1896 } 1897 return result.Succeeded(); 1898 } 1899 }; 1900 1901 CommandObjectPlatform::CommandObjectPlatform(CommandInterpreter &interpreter) 1902 : CommandObjectMultiword( 1903 interpreter, "platform", "Commands to manage and create platforms.", 1904 "platform [connect|disconnect|info|list|status|select] ...") { 1905 LoadSubCommand("select", 1906 CommandObjectSP(new CommandObjectPlatformSelect(interpreter))); 1907 LoadSubCommand("list", 1908 CommandObjectSP(new CommandObjectPlatformList(interpreter))); 1909 LoadSubCommand("status", 1910 CommandObjectSP(new CommandObjectPlatformStatus(interpreter))); 1911 LoadSubCommand("connect", CommandObjectSP( 1912 new CommandObjectPlatformConnect(interpreter))); 1913 LoadSubCommand( 1914 "disconnect", 1915 CommandObjectSP(new CommandObjectPlatformDisconnect(interpreter))); 1916 LoadSubCommand("settings", CommandObjectSP(new CommandObjectPlatformSettings( 1917 interpreter))); 1918 LoadSubCommand("mkdir", 1919 CommandObjectSP(new CommandObjectPlatformMkDir(interpreter))); 1920 LoadSubCommand("file", 1921 CommandObjectSP(new CommandObjectPlatformFile(interpreter))); 1922 LoadSubCommand("file-exists", 1923 CommandObjectSP(new CommandObjectPlatformFileExists(interpreter))); 1924 LoadSubCommand("get-file", CommandObjectSP(new CommandObjectPlatformGetFile( 1925 interpreter))); 1926 LoadSubCommand("get-permissions", 1927 CommandObjectSP(new CommandObjectPlatformGetPermissions(interpreter))); 1928 LoadSubCommand("get-size", CommandObjectSP(new CommandObjectPlatformGetSize( 1929 interpreter))); 1930 LoadSubCommand("put-file", CommandObjectSP(new CommandObjectPlatformPutFile( 1931 interpreter))); 1932 LoadSubCommand("process", CommandObjectSP( 1933 new CommandObjectPlatformProcess(interpreter))); 1934 LoadSubCommand("shell", 1935 CommandObjectSP(new CommandObjectPlatformShell(interpreter))); 1936 LoadSubCommand( 1937 "target-install", 1938 CommandObjectSP(new CommandObjectPlatformInstall(interpreter))); 1939 } 1940 1941 CommandObjectPlatform::~CommandObjectPlatform() = default; 1942