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