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