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