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() {} 1013 1014 ~CommandObjectPlatformProcessLaunch() override = default; 1015 1016 Options *GetOptions() override { return &m_options; } 1017 1018 protected: 1019 bool DoExecute(Args &args, CommandReturnObject &result) override { 1020 Target *target = GetDebugger().GetSelectedTarget().get(); 1021 PlatformSP platform_sp; 1022 if (target) { 1023 platform_sp = target->GetPlatform(); 1024 } 1025 if (!platform_sp) { 1026 platform_sp = GetDebugger().GetPlatformList().GetSelectedPlatform(); 1027 } 1028 1029 if (platform_sp) { 1030 Status error; 1031 const size_t argc = args.GetArgumentCount(); 1032 Target *target = m_exe_ctx.GetTargetPtr(); 1033 Module *exe_module = target->GetExecutableModulePointer(); 1034 if (exe_module) { 1035 m_options.launch_info.GetExecutableFile() = exe_module->GetFileSpec(); 1036 llvm::SmallString<128> exe_path; 1037 m_options.launch_info.GetExecutableFile().GetPath(exe_path); 1038 if (!exe_path.empty()) 1039 m_options.launch_info.GetArguments().AppendArgument(exe_path); 1040 m_options.launch_info.GetArchitecture() = exe_module->GetArchitecture(); 1041 } 1042 1043 if (argc > 0) { 1044 if (m_options.launch_info.GetExecutableFile()) { 1045 // We already have an executable file, so we will use this and all 1046 // arguments to this function are extra arguments 1047 m_options.launch_info.GetArguments().AppendArguments(args); 1048 } else { 1049 // We don't have any file yet, so the first argument is our 1050 // executable, and the rest are program arguments 1051 const bool first_arg_is_executable = true; 1052 m_options.launch_info.SetArguments(args, first_arg_is_executable); 1053 } 1054 } 1055 1056 if (m_options.launch_info.GetExecutableFile()) { 1057 Debugger &debugger = GetDebugger(); 1058 1059 if (argc == 0) 1060 target->GetRunArguments(m_options.launch_info.GetArguments()); 1061 1062 ProcessSP process_sp(platform_sp->DebugProcess( 1063 m_options.launch_info, debugger, target, error)); 1064 if (process_sp && process_sp->IsAlive()) { 1065 result.SetStatus(eReturnStatusSuccessFinishNoResult); 1066 return true; 1067 } 1068 1069 if (error.Success()) 1070 result.AppendError("process launch failed"); 1071 else 1072 result.AppendError(error.AsCString()); 1073 result.SetStatus(eReturnStatusFailed); 1074 } else { 1075 result.AppendError("'platform process launch' uses the current target " 1076 "file and arguments, or the executable and its " 1077 "arguments can be specified in this command"); 1078 result.SetStatus(eReturnStatusFailed); 1079 return false; 1080 } 1081 } else { 1082 result.AppendError("no platform is selected\n"); 1083 } 1084 return result.Succeeded(); 1085 } 1086 1087 CommandOptionsProcessLaunch m_options; 1088 }; 1089 1090 // "platform process list" 1091 1092 static PosixPlatformCommandOptionValidator posix_validator; 1093 #define LLDB_OPTIONS_platform_process_list 1094 #include "CommandOptions.inc" 1095 1096 class CommandObjectPlatformProcessList : public CommandObjectParsed { 1097 public: 1098 CommandObjectPlatformProcessList(CommandInterpreter &interpreter) 1099 : CommandObjectParsed(interpreter, "platform process list", 1100 "List processes on a remote platform by name, pid, " 1101 "or many other matching attributes.", 1102 "platform process list", 0), 1103 m_options() {} 1104 1105 ~CommandObjectPlatformProcessList() override = default; 1106 1107 Options *GetOptions() override { return &m_options; } 1108 1109 protected: 1110 bool DoExecute(Args &args, CommandReturnObject &result) override { 1111 Target *target = GetDebugger().GetSelectedTarget().get(); 1112 PlatformSP platform_sp; 1113 if (target) { 1114 platform_sp = target->GetPlatform(); 1115 } 1116 if (!platform_sp) { 1117 platform_sp = GetDebugger().GetPlatformList().GetSelectedPlatform(); 1118 } 1119 1120 if (platform_sp) { 1121 Status error; 1122 if (args.GetArgumentCount() == 0) { 1123 if (platform_sp) { 1124 Stream &ostrm = result.GetOutputStream(); 1125 1126 lldb::pid_t pid = 1127 m_options.match_info.GetProcessInfo().GetProcessID(); 1128 if (pid != LLDB_INVALID_PROCESS_ID) { 1129 ProcessInstanceInfo proc_info; 1130 if (platform_sp->GetProcessInfo(pid, proc_info)) { 1131 ProcessInstanceInfo::DumpTableHeader(ostrm, m_options.show_args, 1132 m_options.verbose); 1133 proc_info.DumpAsTableRow(ostrm, platform_sp->GetUserIDResolver(), 1134 m_options.show_args, m_options.verbose); 1135 result.SetStatus(eReturnStatusSuccessFinishResult); 1136 } else { 1137 result.AppendErrorWithFormat( 1138 "no process found with pid = %" PRIu64 "\n", pid); 1139 result.SetStatus(eReturnStatusFailed); 1140 } 1141 } else { 1142 ProcessInstanceInfoList proc_infos; 1143 const uint32_t matches = 1144 platform_sp->FindProcesses(m_options.match_info, proc_infos); 1145 const char *match_desc = nullptr; 1146 const char *match_name = 1147 m_options.match_info.GetProcessInfo().GetName(); 1148 if (match_name && match_name[0]) { 1149 switch (m_options.match_info.GetNameMatchType()) { 1150 case NameMatch::Ignore: 1151 break; 1152 case NameMatch::Equals: 1153 match_desc = "matched"; 1154 break; 1155 case NameMatch::Contains: 1156 match_desc = "contained"; 1157 break; 1158 case NameMatch::StartsWith: 1159 match_desc = "started with"; 1160 break; 1161 case NameMatch::EndsWith: 1162 match_desc = "ended with"; 1163 break; 1164 case NameMatch::RegularExpression: 1165 match_desc = "matched the regular expression"; 1166 break; 1167 } 1168 } 1169 1170 if (matches == 0) { 1171 if (match_desc) 1172 result.AppendErrorWithFormat( 1173 "no processes were found that %s \"%s\" on the \"%s\" " 1174 "platform\n", 1175 match_desc, match_name, 1176 platform_sp->GetPluginName().GetCString()); 1177 else 1178 result.AppendErrorWithFormat( 1179 "no processes were found on the \"%s\" platform\n", 1180 platform_sp->GetPluginName().GetCString()); 1181 result.SetStatus(eReturnStatusFailed); 1182 } else { 1183 result.AppendMessageWithFormat( 1184 "%u matching process%s found on \"%s\"", matches, 1185 matches > 1 ? "es were" : " was", 1186 platform_sp->GetName().GetCString()); 1187 if (match_desc) 1188 result.AppendMessageWithFormat(" whose name %s \"%s\"", 1189 match_desc, match_name); 1190 result.AppendMessageWithFormat("\n"); 1191 ProcessInstanceInfo::DumpTableHeader(ostrm, m_options.show_args, 1192 m_options.verbose); 1193 for (uint32_t i = 0; i < matches; ++i) { 1194 proc_infos[i].DumpAsTableRow( 1195 ostrm, platform_sp->GetUserIDResolver(), 1196 m_options.show_args, m_options.verbose); 1197 } 1198 } 1199 } 1200 } 1201 } else { 1202 result.AppendError("invalid args: process list takes only options\n"); 1203 result.SetStatus(eReturnStatusFailed); 1204 } 1205 } else { 1206 result.AppendError("no platform is selected\n"); 1207 result.SetStatus(eReturnStatusFailed); 1208 } 1209 return result.Succeeded(); 1210 } 1211 1212 class CommandOptions : public Options { 1213 public: 1214 CommandOptions() 1215 : Options(), match_info(), show_args(false), verbose(false) {} 1216 1217 ~CommandOptions() override = default; 1218 1219 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 1220 ExecutionContext *execution_context) override { 1221 Status error; 1222 const int short_option = m_getopt_table[option_idx].val; 1223 bool success = false; 1224 1225 uint32_t id = LLDB_INVALID_PROCESS_ID; 1226 success = !option_arg.getAsInteger(0, id); 1227 switch (short_option) { 1228 case 'p': { 1229 match_info.GetProcessInfo().SetProcessID(id); 1230 if (!success) 1231 error.SetErrorStringWithFormat("invalid process ID string: '%s'", 1232 option_arg.str().c_str()); 1233 break; 1234 } 1235 case 'P': 1236 match_info.GetProcessInfo().SetParentProcessID(id); 1237 if (!success) 1238 error.SetErrorStringWithFormat( 1239 "invalid parent process ID string: '%s'", 1240 option_arg.str().c_str()); 1241 break; 1242 1243 case 'u': 1244 match_info.GetProcessInfo().SetUserID(success ? id : UINT32_MAX); 1245 if (!success) 1246 error.SetErrorStringWithFormat("invalid user ID string: '%s'", 1247 option_arg.str().c_str()); 1248 break; 1249 1250 case 'U': 1251 match_info.GetProcessInfo().SetEffectiveUserID(success ? id 1252 : UINT32_MAX); 1253 if (!success) 1254 error.SetErrorStringWithFormat( 1255 "invalid effective user ID string: '%s'", 1256 option_arg.str().c_str()); 1257 break; 1258 1259 case 'g': 1260 match_info.GetProcessInfo().SetGroupID(success ? id : UINT32_MAX); 1261 if (!success) 1262 error.SetErrorStringWithFormat("invalid group ID string: '%s'", 1263 option_arg.str().c_str()); 1264 break; 1265 1266 case 'G': 1267 match_info.GetProcessInfo().SetEffectiveGroupID(success ? id 1268 : UINT32_MAX); 1269 if (!success) 1270 error.SetErrorStringWithFormat( 1271 "invalid effective group ID string: '%s'", 1272 option_arg.str().c_str()); 1273 break; 1274 1275 case 'a': { 1276 TargetSP target_sp = 1277 execution_context ? execution_context->GetTargetSP() : TargetSP(); 1278 DebuggerSP debugger_sp = 1279 target_sp ? target_sp->GetDebugger().shared_from_this() 1280 : DebuggerSP(); 1281 PlatformSP platform_sp = 1282 debugger_sp ? debugger_sp->GetPlatformList().GetSelectedPlatform() 1283 : PlatformSP(); 1284 match_info.GetProcessInfo().GetArchitecture() = 1285 Platform::GetAugmentedArchSpec(platform_sp.get(), option_arg); 1286 } break; 1287 1288 case 'n': 1289 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1290 option_arg, FileSpec::Style::native); 1291 match_info.SetNameMatchType(NameMatch::Equals); 1292 break; 1293 1294 case 'e': 1295 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1296 option_arg, FileSpec::Style::native); 1297 match_info.SetNameMatchType(NameMatch::EndsWith); 1298 break; 1299 1300 case 's': 1301 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1302 option_arg, FileSpec::Style::native); 1303 match_info.SetNameMatchType(NameMatch::StartsWith); 1304 break; 1305 1306 case 'c': 1307 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1308 option_arg, FileSpec::Style::native); 1309 match_info.SetNameMatchType(NameMatch::Contains); 1310 break; 1311 1312 case 'r': 1313 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1314 option_arg, FileSpec::Style::native); 1315 match_info.SetNameMatchType(NameMatch::RegularExpression); 1316 break; 1317 1318 case 'A': 1319 show_args = true; 1320 break; 1321 1322 case 'v': 1323 verbose = true; 1324 break; 1325 1326 case 'x': 1327 match_info.SetMatchAllUsers(true); 1328 break; 1329 1330 default: 1331 llvm_unreachable("Unimplemented option"); 1332 } 1333 1334 return error; 1335 } 1336 1337 void OptionParsingStarting(ExecutionContext *execution_context) override { 1338 match_info.Clear(); 1339 show_args = false; 1340 verbose = false; 1341 } 1342 1343 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 1344 return llvm::makeArrayRef(g_platform_process_list_options); 1345 } 1346 1347 // Instance variables to hold the values for command options. 1348 1349 ProcessInstanceInfoMatch match_info; 1350 bool show_args; 1351 bool verbose; 1352 }; 1353 1354 CommandOptions m_options; 1355 }; 1356 1357 // "platform process info" 1358 class CommandObjectPlatformProcessInfo : public CommandObjectParsed { 1359 public: 1360 CommandObjectPlatformProcessInfo(CommandInterpreter &interpreter) 1361 : CommandObjectParsed( 1362 interpreter, "platform process info", 1363 "Get detailed information for one or more process by process ID.", 1364 "platform process info <pid> [<pid> <pid> ...]", 0) { 1365 CommandArgumentEntry arg; 1366 CommandArgumentData pid_args; 1367 1368 // Define the first (and only) variant of this arg. 1369 pid_args.arg_type = eArgTypePid; 1370 pid_args.arg_repetition = eArgRepeatStar; 1371 1372 // There is only one variant this argument could be; put it into the 1373 // argument entry. 1374 arg.push_back(pid_args); 1375 1376 // Push the data for the first argument into the m_arguments vector. 1377 m_arguments.push_back(arg); 1378 } 1379 1380 ~CommandObjectPlatformProcessInfo() override = default; 1381 1382 void 1383 HandleArgumentCompletion(CompletionRequest &request, 1384 OptionElementVector &opt_element_vector) override { 1385 CommandCompletions::InvokeCommonCompletionCallbacks( 1386 GetCommandInterpreter(), CommandCompletions::eProcessIDCompletion, 1387 request, nullptr); 1388 } 1389 1390 protected: 1391 bool DoExecute(Args &args, CommandReturnObject &result) override { 1392 Target *target = GetDebugger().GetSelectedTarget().get(); 1393 PlatformSP platform_sp; 1394 if (target) { 1395 platform_sp = target->GetPlatform(); 1396 } 1397 if (!platform_sp) { 1398 platform_sp = GetDebugger().GetPlatformList().GetSelectedPlatform(); 1399 } 1400 1401 if (platform_sp) { 1402 const size_t argc = args.GetArgumentCount(); 1403 if (argc > 0) { 1404 Status error; 1405 1406 if (platform_sp->IsConnected()) { 1407 Stream &ostrm = result.GetOutputStream(); 1408 for (auto &entry : args.entries()) { 1409 lldb::pid_t pid; 1410 if (entry.ref().getAsInteger(0, pid)) { 1411 result.AppendErrorWithFormat("invalid process ID argument '%s'", 1412 entry.ref().str().c_str()); 1413 result.SetStatus(eReturnStatusFailed); 1414 break; 1415 } else { 1416 ProcessInstanceInfo proc_info; 1417 if (platform_sp->GetProcessInfo(pid, proc_info)) { 1418 ostrm.Printf("Process information for process %" PRIu64 ":\n", 1419 pid); 1420 proc_info.Dump(ostrm, platform_sp->GetUserIDResolver()); 1421 } else { 1422 ostrm.Printf("error: no process information is available for " 1423 "process %" PRIu64 "\n", 1424 pid); 1425 } 1426 ostrm.EOL(); 1427 } 1428 } 1429 } else { 1430 // Not connected... 1431 result.AppendErrorWithFormat( 1432 "not connected to '%s'", 1433 platform_sp->GetPluginName().GetCString()); 1434 result.SetStatus(eReturnStatusFailed); 1435 } 1436 } else { 1437 // No args 1438 result.AppendError("one or more process id(s) must be specified"); 1439 result.SetStatus(eReturnStatusFailed); 1440 } 1441 } else { 1442 result.AppendError("no platform is currently selected"); 1443 result.SetStatus(eReturnStatusFailed); 1444 } 1445 return result.Succeeded(); 1446 } 1447 }; 1448 1449 #define LLDB_OPTIONS_platform_process_attach 1450 #include "CommandOptions.inc" 1451 1452 class CommandObjectPlatformProcessAttach : public CommandObjectParsed { 1453 public: 1454 class CommandOptions : public Options { 1455 public: 1456 CommandOptions() : Options() { 1457 // Keep default values of all options in one place: OptionParsingStarting 1458 // () 1459 OptionParsingStarting(nullptr); 1460 } 1461 1462 ~CommandOptions() override = default; 1463 1464 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 1465 ExecutionContext *execution_context) override { 1466 Status error; 1467 char short_option = (char)m_getopt_table[option_idx].val; 1468 switch (short_option) { 1469 case 'p': { 1470 lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; 1471 if (option_arg.getAsInteger(0, pid)) { 1472 error.SetErrorStringWithFormat("invalid process ID '%s'", 1473 option_arg.str().c_str()); 1474 } else { 1475 attach_info.SetProcessID(pid); 1476 } 1477 } break; 1478 1479 case 'P': 1480 attach_info.SetProcessPluginName(option_arg); 1481 break; 1482 1483 case 'n': 1484 attach_info.GetExecutableFile().SetFile(option_arg, 1485 FileSpec::Style::native); 1486 break; 1487 1488 case 'w': 1489 attach_info.SetWaitForLaunch(true); 1490 break; 1491 1492 default: 1493 llvm_unreachable("Unimplemented option"); 1494 } 1495 return error; 1496 } 1497 1498 void OptionParsingStarting(ExecutionContext *execution_context) override { 1499 attach_info.Clear(); 1500 } 1501 1502 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 1503 return llvm::makeArrayRef(g_platform_process_attach_options); 1504 } 1505 1506 // Options table: Required for subclasses of Options. 1507 1508 static OptionDefinition g_option_table[]; 1509 1510 // Instance variables to hold the values for command options. 1511 1512 ProcessAttachInfo attach_info; 1513 }; 1514 1515 CommandObjectPlatformProcessAttach(CommandInterpreter &interpreter) 1516 : CommandObjectParsed(interpreter, "platform process attach", 1517 "Attach to a process.", 1518 "platform process attach <cmd-options>"), 1519 m_options() {} 1520 1521 ~CommandObjectPlatformProcessAttach() override = default; 1522 1523 bool DoExecute(Args &command, CommandReturnObject &result) override { 1524 PlatformSP platform_sp( 1525 GetDebugger().GetPlatformList().GetSelectedPlatform()); 1526 if (platform_sp) { 1527 Status err; 1528 ProcessSP remote_process_sp = platform_sp->Attach( 1529 m_options.attach_info, GetDebugger(), nullptr, err); 1530 if (err.Fail()) { 1531 result.AppendError(err.AsCString()); 1532 result.SetStatus(eReturnStatusFailed); 1533 } else if (!remote_process_sp) { 1534 result.AppendError("could not attach: unknown reason"); 1535 result.SetStatus(eReturnStatusFailed); 1536 } else 1537 result.SetStatus(eReturnStatusSuccessFinishResult); 1538 } else { 1539 result.AppendError("no platform is currently selected"); 1540 result.SetStatus(eReturnStatusFailed); 1541 } 1542 return result.Succeeded(); 1543 } 1544 1545 Options *GetOptions() override { return &m_options; } 1546 1547 protected: 1548 CommandOptions m_options; 1549 }; 1550 1551 class CommandObjectPlatformProcess : public CommandObjectMultiword { 1552 public: 1553 // Constructors and Destructors 1554 CommandObjectPlatformProcess(CommandInterpreter &interpreter) 1555 : CommandObjectMultiword(interpreter, "platform process", 1556 "Commands to query, launch and attach to " 1557 "processes on the current platform.", 1558 "platform process [attach|launch|list] ...") { 1559 LoadSubCommand( 1560 "attach", 1561 CommandObjectSP(new CommandObjectPlatformProcessAttach(interpreter))); 1562 LoadSubCommand( 1563 "launch", 1564 CommandObjectSP(new CommandObjectPlatformProcessLaunch(interpreter))); 1565 LoadSubCommand("info", CommandObjectSP(new CommandObjectPlatformProcessInfo( 1566 interpreter))); 1567 LoadSubCommand("list", CommandObjectSP(new CommandObjectPlatformProcessList( 1568 interpreter))); 1569 } 1570 1571 ~CommandObjectPlatformProcess() override = default; 1572 1573 private: 1574 // For CommandObjectPlatform only 1575 CommandObjectPlatformProcess(const CommandObjectPlatformProcess &) = delete; 1576 const CommandObjectPlatformProcess & 1577 operator=(const CommandObjectPlatformProcess &) = delete; 1578 }; 1579 1580 // "platform shell" 1581 #define LLDB_OPTIONS_platform_shell 1582 #include "CommandOptions.inc" 1583 1584 class CommandObjectPlatformShell : public CommandObjectRaw { 1585 public: 1586 class CommandOptions : public Options { 1587 public: 1588 CommandOptions() : Options() {} 1589 1590 ~CommandOptions() override = default; 1591 1592 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 1593 return llvm::makeArrayRef(g_platform_shell_options); 1594 } 1595 1596 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 1597 ExecutionContext *execution_context) override { 1598 Status error; 1599 1600 const char short_option = (char)GetDefinitions()[option_idx].short_option; 1601 1602 switch (short_option) { 1603 case 'h': 1604 m_use_host_platform = true; 1605 break; 1606 case 't': 1607 uint32_t timeout_sec; 1608 if (option_arg.getAsInteger(10, timeout_sec)) 1609 error.SetErrorStringWithFormat( 1610 "could not convert \"%s\" to a numeric value.", 1611 option_arg.str().c_str()); 1612 else 1613 m_timeout = std::chrono::seconds(timeout_sec); 1614 break; 1615 case 's': { 1616 if (option_arg.empty()) { 1617 error.SetErrorStringWithFormat( 1618 "missing shell interpreter path for option -i|--interpreter."); 1619 return error; 1620 } 1621 1622 m_shell_interpreter = option_arg.str(); 1623 break; 1624 } 1625 default: 1626 llvm_unreachable("Unimplemented option"); 1627 } 1628 1629 return error; 1630 } 1631 1632 void OptionParsingStarting(ExecutionContext *execution_context) override { 1633 m_timeout.reset(); 1634 m_use_host_platform = false; 1635 m_shell_interpreter.clear(); 1636 } 1637 1638 Timeout<std::micro> m_timeout = std::chrono::seconds(10); 1639 bool m_use_host_platform; 1640 std::string m_shell_interpreter; 1641 }; 1642 1643 CommandObjectPlatformShell(CommandInterpreter &interpreter) 1644 : CommandObjectRaw(interpreter, "platform shell", 1645 "Run a shell command on the current platform.", 1646 "platform shell <shell-command>", 0), 1647 m_options() {} 1648 1649 ~CommandObjectPlatformShell() override = default; 1650 1651 Options *GetOptions() override { return &m_options; } 1652 1653 bool DoExecute(llvm::StringRef raw_command_line, 1654 CommandReturnObject &result) override { 1655 ExecutionContext exe_ctx = GetCommandInterpreter().GetExecutionContext(); 1656 m_options.NotifyOptionParsingStarting(&exe_ctx); 1657 1658 // Print out an usage syntax on an empty command line. 1659 if (raw_command_line.empty()) { 1660 result.GetOutputStream().Printf("%s\n", this->GetSyntax().str().c_str()); 1661 return true; 1662 } 1663 1664 const bool is_alias = !raw_command_line.contains("platform"); 1665 OptionsWithRaw args(raw_command_line); 1666 1667 if (args.HasArgs()) 1668 if (!ParseOptions(args.GetArgs(), result)) 1669 return false; 1670 1671 if (args.GetRawPart().empty()) { 1672 result.GetOutputStream().Printf("%s <shell-command>\n", 1673 is_alias ? "shell" : "platform shell"); 1674 return false; 1675 } 1676 1677 llvm::StringRef cmd = args.GetRawPart(); 1678 1679 PlatformSP platform_sp( 1680 m_options.m_use_host_platform 1681 ? Platform::GetHostPlatform() 1682 : GetDebugger().GetPlatformList().GetSelectedPlatform()); 1683 Status error; 1684 if (platform_sp) { 1685 FileSpec working_dir{}; 1686 std::string output; 1687 int status = -1; 1688 int signo = -1; 1689 error = (platform_sp->RunShellCommand(m_options.m_shell_interpreter, cmd, 1690 working_dir, &status, &signo, 1691 &output, m_options.m_timeout)); 1692 if (!output.empty()) 1693 result.GetOutputStream().PutCString(output); 1694 if (status > 0) { 1695 if (signo > 0) { 1696 const char *signo_cstr = Host::GetSignalAsCString(signo); 1697 if (signo_cstr) 1698 result.GetOutputStream().Printf( 1699 "error: command returned with status %i and signal %s\n", 1700 status, signo_cstr); 1701 else 1702 result.GetOutputStream().Printf( 1703 "error: command returned with status %i and signal %i\n", 1704 status, signo); 1705 } else 1706 result.GetOutputStream().Printf( 1707 "error: command returned with status %i\n", status); 1708 } 1709 } else { 1710 result.GetOutputStream().Printf( 1711 "error: cannot run remote shell commands without a platform\n"); 1712 error.SetErrorString( 1713 "error: cannot run remote shell commands without a platform"); 1714 } 1715 1716 if (error.Fail()) { 1717 result.AppendError(error.AsCString()); 1718 result.SetStatus(eReturnStatusFailed); 1719 } else { 1720 result.SetStatus(eReturnStatusSuccessFinishResult); 1721 } 1722 return true; 1723 } 1724 1725 CommandOptions m_options; 1726 }; 1727 1728 // "platform install" - install a target to a remote end 1729 class CommandObjectPlatformInstall : public CommandObjectParsed { 1730 public: 1731 CommandObjectPlatformInstall(CommandInterpreter &interpreter) 1732 : CommandObjectParsed( 1733 interpreter, "platform target-install", 1734 "Install a target (bundle or executable file) to the remote end.", 1735 "platform target-install <local-thing> <remote-sandbox>", 0) {} 1736 1737 ~CommandObjectPlatformInstall() override = default; 1738 1739 void 1740 HandleArgumentCompletion(CompletionRequest &request, 1741 OptionElementVector &opt_element_vector) override { 1742 if (request.GetCursorIndex()) 1743 return; 1744 CommandCompletions::InvokeCommonCompletionCallbacks( 1745 GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion, 1746 request, nullptr); 1747 } 1748 1749 bool DoExecute(Args &args, CommandReturnObject &result) override { 1750 if (args.GetArgumentCount() != 2) { 1751 result.AppendError("platform target-install takes two arguments"); 1752 result.SetStatus(eReturnStatusFailed); 1753 return false; 1754 } 1755 // TODO: move the bulk of this code over to the platform itself 1756 FileSpec src(args.GetArgumentAtIndex(0)); 1757 FileSystem::Instance().Resolve(src); 1758 FileSpec dst(args.GetArgumentAtIndex(1)); 1759 if (!FileSystem::Instance().Exists(src)) { 1760 result.AppendError("source location does not exist or is not accessible"); 1761 result.SetStatus(eReturnStatusFailed); 1762 return false; 1763 } 1764 PlatformSP platform_sp( 1765 GetDebugger().GetPlatformList().GetSelectedPlatform()); 1766 if (!platform_sp) { 1767 result.AppendError("no platform currently selected"); 1768 result.SetStatus(eReturnStatusFailed); 1769 return false; 1770 } 1771 1772 Status error = platform_sp->Install(src, dst); 1773 if (error.Success()) { 1774 result.SetStatus(eReturnStatusSuccessFinishNoResult); 1775 } else { 1776 result.AppendErrorWithFormat("install failed: %s", error.AsCString()); 1777 result.SetStatus(eReturnStatusFailed); 1778 } 1779 return result.Succeeded(); 1780 } 1781 }; 1782 1783 CommandObjectPlatform::CommandObjectPlatform(CommandInterpreter &interpreter) 1784 : CommandObjectMultiword( 1785 interpreter, "platform", "Commands to manage and create platforms.", 1786 "platform [connect|disconnect|info|list|status|select] ...") { 1787 LoadSubCommand("select", 1788 CommandObjectSP(new CommandObjectPlatformSelect(interpreter))); 1789 LoadSubCommand("list", 1790 CommandObjectSP(new CommandObjectPlatformList(interpreter))); 1791 LoadSubCommand("status", 1792 CommandObjectSP(new CommandObjectPlatformStatus(interpreter))); 1793 LoadSubCommand("connect", CommandObjectSP( 1794 new CommandObjectPlatformConnect(interpreter))); 1795 LoadSubCommand( 1796 "disconnect", 1797 CommandObjectSP(new CommandObjectPlatformDisconnect(interpreter))); 1798 LoadSubCommand("settings", CommandObjectSP(new CommandObjectPlatformSettings( 1799 interpreter))); 1800 LoadSubCommand("mkdir", 1801 CommandObjectSP(new CommandObjectPlatformMkDir(interpreter))); 1802 LoadSubCommand("file", 1803 CommandObjectSP(new CommandObjectPlatformFile(interpreter))); 1804 LoadSubCommand("get-file", CommandObjectSP(new CommandObjectPlatformGetFile( 1805 interpreter))); 1806 LoadSubCommand("get-size", CommandObjectSP(new CommandObjectPlatformGetSize( 1807 interpreter))); 1808 LoadSubCommand("put-file", CommandObjectSP(new CommandObjectPlatformPutFile( 1809 interpreter))); 1810 LoadSubCommand("process", CommandObjectSP( 1811 new CommandObjectPlatformProcess(interpreter))); 1812 LoadSubCommand("shell", 1813 CommandObjectSP(new CommandObjectPlatformShell(interpreter))); 1814 LoadSubCommand( 1815 "target-install", 1816 CommandObjectSP(new CommandObjectPlatformInstall(interpreter))); 1817 } 1818 1819 CommandObjectPlatform::~CommandObjectPlatform() = default; 1820