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