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