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