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/Host/StringConvert.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', 0, 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 protected: 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 bool DoExecute(Args &args, CommandReturnObject &result) override { 491 PlatformSP platform_sp( 492 GetDebugger().GetPlatformList().GetSelectedPlatform()); 493 if (platform_sp) { 494 Status error; 495 std::string cmd_line; 496 args.GetCommandString(cmd_line); 497 mode_t perms; 498 const OptionPermissions *options_permissions = 499 (const OptionPermissions *)m_options.GetGroupWithOption('r'); 500 if (options_permissions) 501 perms = options_permissions->m_permissions; 502 else 503 perms = lldb::eFilePermissionsUserRW | lldb::eFilePermissionsGroupRW | 504 lldb::eFilePermissionsWorldRead; 505 lldb::user_id_t fd = platform_sp->OpenFile( 506 FileSpec(cmd_line), 507 File::eOpenOptionRead | File::eOpenOptionWrite | 508 File::eOpenOptionAppend | File::eOpenOptionCanCreate, 509 perms, error); 510 if (error.Success()) { 511 result.AppendMessageWithFormat("File Descriptor = %" PRIu64 "\n", fd); 512 result.SetStatus(eReturnStatusSuccessFinishResult); 513 } else { 514 result.AppendError(error.AsCString()); 515 result.SetStatus(eReturnStatusFailed); 516 } 517 } else { 518 result.AppendError("no platform currently selected\n"); 519 result.SetStatus(eReturnStatusFailed); 520 } 521 return result.Succeeded(); 522 } 523 524 Options *GetOptions() override { 525 if (!m_options.DidFinalize()) { 526 m_options.Append(new OptionPermissions()); 527 m_options.Finalize(); 528 } 529 return &m_options; 530 } 531 532 OptionGroupOptions m_options; 533 }; 534 535 // "platform fclose" 536 class CommandObjectPlatformFClose : public CommandObjectParsed { 537 public: 538 CommandObjectPlatformFClose(CommandInterpreter &interpreter) 539 : CommandObjectParsed(interpreter, "platform file close", 540 "Close a file on the remote end.", nullptr, 0) {} 541 542 ~CommandObjectPlatformFClose() override = default; 543 544 bool DoExecute(Args &args, CommandReturnObject &result) override { 545 PlatformSP platform_sp( 546 GetDebugger().GetPlatformList().GetSelectedPlatform()); 547 if (platform_sp) { 548 std::string cmd_line; 549 args.GetCommandString(cmd_line); 550 const lldb::user_id_t fd = 551 StringConvert::ToUInt64(cmd_line.c_str(), UINT64_MAX); 552 Status error; 553 bool success = platform_sp->CloseFile(fd, error); 554 if (success) { 555 result.AppendMessageWithFormat("file %" PRIu64 " closed.\n", fd); 556 result.SetStatus(eReturnStatusSuccessFinishResult); 557 } else { 558 result.AppendError(error.AsCString()); 559 result.SetStatus(eReturnStatusFailed); 560 } 561 } else { 562 result.AppendError("no platform currently selected\n"); 563 result.SetStatus(eReturnStatusFailed); 564 } 565 return result.Succeeded(); 566 } 567 }; 568 569 // "platform fread" 570 571 #define LLDB_OPTIONS_platform_fread 572 #include "CommandOptions.inc" 573 574 class CommandObjectPlatformFRead : public CommandObjectParsed { 575 public: 576 CommandObjectPlatformFRead(CommandInterpreter &interpreter) 577 : CommandObjectParsed(interpreter, "platform file read", 578 "Read data from a file on the remote end.", nullptr, 579 0), 580 m_options() {} 581 582 ~CommandObjectPlatformFRead() override = default; 583 584 bool DoExecute(Args &args, CommandReturnObject &result) override { 585 PlatformSP platform_sp( 586 GetDebugger().GetPlatformList().GetSelectedPlatform()); 587 if (platform_sp) { 588 std::string cmd_line; 589 args.GetCommandString(cmd_line); 590 const lldb::user_id_t fd = 591 StringConvert::ToUInt64(cmd_line.c_str(), UINT64_MAX); 592 std::string buffer(m_options.m_count, 0); 593 Status error; 594 uint32_t retcode = platform_sp->ReadFile( 595 fd, m_options.m_offset, &buffer[0], m_options.m_count, error); 596 result.AppendMessageWithFormat("Return = %d\n", retcode); 597 result.AppendMessageWithFormat("Data = \"%s\"\n", buffer.c_str()); 598 result.SetStatus(eReturnStatusSuccessFinishResult); 599 } else { 600 result.AppendError("no platform currently selected\n"); 601 result.SetStatus(eReturnStatusFailed); 602 } 603 return result.Succeeded(); 604 } 605 606 Options *GetOptions() override { return &m_options; } 607 608 protected: 609 class CommandOptions : public Options { 610 public: 611 CommandOptions() : Options() {} 612 613 ~CommandOptions() override = default; 614 615 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 616 ExecutionContext *execution_context) override { 617 Status error; 618 char short_option = (char)m_getopt_table[option_idx].val; 619 620 switch (short_option) { 621 case 'o': 622 if (option_arg.getAsInteger(0, m_offset)) 623 error.SetErrorStringWithFormat("invalid offset: '%s'", 624 option_arg.str().c_str()); 625 break; 626 case 'c': 627 if (option_arg.getAsInteger(0, m_count)) 628 error.SetErrorStringWithFormat("invalid offset: '%s'", 629 option_arg.str().c_str()); 630 break; 631 default: 632 llvm_unreachable("Unimplemented option"); 633 } 634 635 return error; 636 } 637 638 void OptionParsingStarting(ExecutionContext *execution_context) override { 639 m_offset = 0; 640 m_count = 1; 641 } 642 643 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 644 return llvm::makeArrayRef(g_platform_fread_options); 645 } 646 647 // Instance variables to hold the values for command options. 648 649 uint32_t m_offset; 650 uint32_t m_count; 651 }; 652 653 CommandOptions m_options; 654 }; 655 656 // "platform fwrite" 657 658 #define LLDB_OPTIONS_platform_fwrite 659 #include "CommandOptions.inc" 660 661 class CommandObjectPlatformFWrite : public CommandObjectParsed { 662 public: 663 CommandObjectPlatformFWrite(CommandInterpreter &interpreter) 664 : CommandObjectParsed(interpreter, "platform file write", 665 "Write data to a file on the remote end.", nullptr, 666 0), 667 m_options() {} 668 669 ~CommandObjectPlatformFWrite() override = default; 670 671 bool DoExecute(Args &args, CommandReturnObject &result) override { 672 PlatformSP platform_sp( 673 GetDebugger().GetPlatformList().GetSelectedPlatform()); 674 if (platform_sp) { 675 std::string cmd_line; 676 args.GetCommandString(cmd_line); 677 Status error; 678 const lldb::user_id_t fd = 679 StringConvert::ToUInt64(cmd_line.c_str(), UINT64_MAX); 680 uint32_t retcode = 681 platform_sp->WriteFile(fd, m_options.m_offset, &m_options.m_data[0], 682 m_options.m_data.size(), error); 683 result.AppendMessageWithFormat("Return = %d\n", retcode); 684 result.SetStatus(eReturnStatusSuccessFinishResult); 685 } else { 686 result.AppendError("no platform currently selected\n"); 687 result.SetStatus(eReturnStatusFailed); 688 } 689 return result.Succeeded(); 690 } 691 692 Options *GetOptions() override { return &m_options; } 693 694 protected: 695 class CommandOptions : public Options { 696 public: 697 CommandOptions() : Options() {} 698 699 ~CommandOptions() override = default; 700 701 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 702 ExecutionContext *execution_context) override { 703 Status error; 704 char short_option = (char)m_getopt_table[option_idx].val; 705 706 switch (short_option) { 707 case 'o': 708 if (option_arg.getAsInteger(0, m_offset)) 709 error.SetErrorStringWithFormat("invalid offset: '%s'", 710 option_arg.str().c_str()); 711 break; 712 case 'd': 713 m_data.assign(std::string(option_arg)); 714 break; 715 default: 716 llvm_unreachable("Unimplemented option"); 717 } 718 719 return error; 720 } 721 722 void OptionParsingStarting(ExecutionContext *execution_context) override { 723 m_offset = 0; 724 m_data.clear(); 725 } 726 727 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 728 return llvm::makeArrayRef(g_platform_fwrite_options); 729 } 730 731 // Instance variables to hold the values for command options. 732 733 uint32_t m_offset; 734 std::string m_data; 735 }; 736 737 CommandOptions m_options; 738 }; 739 740 class CommandObjectPlatformFile : public CommandObjectMultiword { 741 public: 742 // Constructors and Destructors 743 CommandObjectPlatformFile(CommandInterpreter &interpreter) 744 : CommandObjectMultiword( 745 interpreter, "platform file", 746 "Commands to access files on the current platform.", 747 "platform file [open|close|read|write] ...") { 748 LoadSubCommand( 749 "open", CommandObjectSP(new CommandObjectPlatformFOpen(interpreter))); 750 LoadSubCommand( 751 "close", CommandObjectSP(new CommandObjectPlatformFClose(interpreter))); 752 LoadSubCommand( 753 "read", CommandObjectSP(new CommandObjectPlatformFRead(interpreter))); 754 LoadSubCommand( 755 "write", CommandObjectSP(new CommandObjectPlatformFWrite(interpreter))); 756 } 757 758 ~CommandObjectPlatformFile() override = default; 759 760 private: 761 // For CommandObjectPlatform only 762 CommandObjectPlatformFile(const CommandObjectPlatformFile &) = delete; 763 const CommandObjectPlatformFile & 764 operator=(const CommandObjectPlatformFile &) = delete; 765 }; 766 767 // "platform get-file remote-file-path host-file-path" 768 class CommandObjectPlatformGetFile : public CommandObjectParsed { 769 public: 770 CommandObjectPlatformGetFile(CommandInterpreter &interpreter) 771 : CommandObjectParsed( 772 interpreter, "platform get-file", 773 "Transfer a file from the remote end to the local host.", 774 "platform get-file <remote-file-spec> <local-file-spec>", 0) { 775 SetHelpLong( 776 R"(Examples: 777 778 (lldb) platform get-file /the/remote/file/path /the/local/file/path 779 780 Transfer a file from the remote end with file path /the/remote/file/path to the local host.)"); 781 782 CommandArgumentEntry arg1, arg2; 783 CommandArgumentData file_arg_remote, file_arg_host; 784 785 // Define the first (and only) variant of this arg. 786 file_arg_remote.arg_type = eArgTypeFilename; 787 file_arg_remote.arg_repetition = eArgRepeatPlain; 788 // There is only one variant this argument could be; put it into the 789 // argument entry. 790 arg1.push_back(file_arg_remote); 791 792 // Define the second (and only) variant of this arg. 793 file_arg_host.arg_type = eArgTypeFilename; 794 file_arg_host.arg_repetition = eArgRepeatPlain; 795 // There is only one variant this argument could be; put it into the 796 // argument entry. 797 arg2.push_back(file_arg_host); 798 799 // Push the data for the first and the second arguments into the 800 // m_arguments vector. 801 m_arguments.push_back(arg1); 802 m_arguments.push_back(arg2); 803 } 804 805 ~CommandObjectPlatformGetFile() override = default; 806 807 bool DoExecute(Args &args, CommandReturnObject &result) override { 808 // If the number of arguments is incorrect, issue an error message. 809 if (args.GetArgumentCount() != 2) { 810 result.GetErrorStream().Printf("error: required arguments missing; " 811 "specify both the source and destination " 812 "file paths\n"); 813 result.SetStatus(eReturnStatusFailed); 814 return false; 815 } 816 817 PlatformSP platform_sp( 818 GetDebugger().GetPlatformList().GetSelectedPlatform()); 819 if (platform_sp) { 820 const char *remote_file_path = args.GetArgumentAtIndex(0); 821 const char *local_file_path = args.GetArgumentAtIndex(1); 822 Status error = platform_sp->GetFile(FileSpec(remote_file_path), 823 FileSpec(local_file_path)); 824 if (error.Success()) { 825 result.AppendMessageWithFormat( 826 "successfully get-file from %s (remote) to %s (host)\n", 827 remote_file_path, local_file_path); 828 result.SetStatus(eReturnStatusSuccessFinishResult); 829 } else { 830 result.AppendMessageWithFormat("get-file failed: %s\n", 831 error.AsCString()); 832 result.SetStatus(eReturnStatusFailed); 833 } 834 } else { 835 result.AppendError("no platform currently selected\n"); 836 result.SetStatus(eReturnStatusFailed); 837 } 838 return result.Succeeded(); 839 } 840 }; 841 842 // "platform get-size remote-file-path" 843 class CommandObjectPlatformGetSize : public CommandObjectParsed { 844 public: 845 CommandObjectPlatformGetSize(CommandInterpreter &interpreter) 846 : CommandObjectParsed(interpreter, "platform get-size", 847 "Get the file size from the remote end.", 848 "platform get-size <remote-file-spec>", 0) { 849 SetHelpLong( 850 R"(Examples: 851 852 (lldb) platform get-size /the/remote/file/path 853 854 Get the file size from the remote end with path /the/remote/file/path.)"); 855 856 CommandArgumentEntry arg1; 857 CommandArgumentData file_arg_remote; 858 859 // Define the first (and only) variant of this arg. 860 file_arg_remote.arg_type = eArgTypeFilename; 861 file_arg_remote.arg_repetition = eArgRepeatPlain; 862 // There is only one variant this argument could be; put it into the 863 // argument entry. 864 arg1.push_back(file_arg_remote); 865 866 // Push the data for the first argument into the m_arguments vector. 867 m_arguments.push_back(arg1); 868 } 869 870 ~CommandObjectPlatformGetSize() override = default; 871 872 bool DoExecute(Args &args, CommandReturnObject &result) override { 873 // If the number of arguments is incorrect, issue an error message. 874 if (args.GetArgumentCount() != 1) { 875 result.GetErrorStream().Printf("error: required argument missing; " 876 "specify the source file path as the only " 877 "argument\n"); 878 result.SetStatus(eReturnStatusFailed); 879 return false; 880 } 881 882 PlatformSP platform_sp( 883 GetDebugger().GetPlatformList().GetSelectedPlatform()); 884 if (platform_sp) { 885 std::string remote_file_path(args.GetArgumentAtIndex(0)); 886 user_id_t size = platform_sp->GetFileSize(FileSpec(remote_file_path)); 887 if (size != UINT64_MAX) { 888 result.AppendMessageWithFormat("File size of %s (remote): %" PRIu64 889 "\n", 890 remote_file_path.c_str(), size); 891 result.SetStatus(eReturnStatusSuccessFinishResult); 892 } else { 893 result.AppendMessageWithFormat( 894 "Error getting file size of %s (remote)\n", 895 remote_file_path.c_str()); 896 result.SetStatus(eReturnStatusFailed); 897 } 898 } else { 899 result.AppendError("no platform currently selected\n"); 900 result.SetStatus(eReturnStatusFailed); 901 } 902 return result.Succeeded(); 903 } 904 }; 905 906 // "platform put-file" 907 class CommandObjectPlatformPutFile : public CommandObjectParsed { 908 public: 909 CommandObjectPlatformPutFile(CommandInterpreter &interpreter) 910 : CommandObjectParsed( 911 interpreter, "platform put-file", 912 "Transfer a file from this system to the remote end.", nullptr, 0) { 913 } 914 915 ~CommandObjectPlatformPutFile() override = default; 916 917 bool DoExecute(Args &args, CommandReturnObject &result) override { 918 const char *src = args.GetArgumentAtIndex(0); 919 const char *dst = args.GetArgumentAtIndex(1); 920 921 FileSpec src_fs(src); 922 FileSystem::Instance().Resolve(src_fs); 923 FileSpec dst_fs(dst ? dst : src_fs.GetFilename().GetCString()); 924 925 PlatformSP platform_sp( 926 GetDebugger().GetPlatformList().GetSelectedPlatform()); 927 if (platform_sp) { 928 Status error(platform_sp->PutFile(src_fs, dst_fs)); 929 if (error.Success()) { 930 result.SetStatus(eReturnStatusSuccessFinishNoResult); 931 } else { 932 result.AppendError(error.AsCString()); 933 result.SetStatus(eReturnStatusFailed); 934 } 935 } else { 936 result.AppendError("no platform currently selected\n"); 937 result.SetStatus(eReturnStatusFailed); 938 } 939 return result.Succeeded(); 940 } 941 }; 942 943 // "platform process launch" 944 class CommandObjectPlatformProcessLaunch : public CommandObjectParsed { 945 public: 946 CommandObjectPlatformProcessLaunch(CommandInterpreter &interpreter) 947 : CommandObjectParsed(interpreter, "platform process launch", 948 "Launch a new process on a remote platform.", 949 "platform process launch program", 950 eCommandRequiresTarget | eCommandTryTargetAPILock), 951 m_options() {} 952 953 ~CommandObjectPlatformProcessLaunch() override = default; 954 955 Options *GetOptions() override { return &m_options; } 956 957 protected: 958 bool DoExecute(Args &args, CommandReturnObject &result) override { 959 Target *target = GetDebugger().GetSelectedTarget().get(); 960 PlatformSP platform_sp; 961 if (target) { 962 platform_sp = target->GetPlatform(); 963 } 964 if (!platform_sp) { 965 platform_sp = GetDebugger().GetPlatformList().GetSelectedPlatform(); 966 } 967 968 if (platform_sp) { 969 Status error; 970 const size_t argc = args.GetArgumentCount(); 971 Target *target = m_exe_ctx.GetTargetPtr(); 972 Module *exe_module = target->GetExecutableModulePointer(); 973 if (exe_module) { 974 m_options.launch_info.GetExecutableFile() = exe_module->GetFileSpec(); 975 llvm::SmallString<128> exe_path; 976 m_options.launch_info.GetExecutableFile().GetPath(exe_path); 977 if (!exe_path.empty()) 978 m_options.launch_info.GetArguments().AppendArgument(exe_path); 979 m_options.launch_info.GetArchitecture() = exe_module->GetArchitecture(); 980 } 981 982 if (argc > 0) { 983 if (m_options.launch_info.GetExecutableFile()) { 984 // We already have an executable file, so we will use this and all 985 // arguments to this function are extra arguments 986 m_options.launch_info.GetArguments().AppendArguments(args); 987 } else { 988 // We don't have any file yet, so the first argument is our 989 // executable, and the rest are program arguments 990 const bool first_arg_is_executable = true; 991 m_options.launch_info.SetArguments(args, first_arg_is_executable); 992 } 993 } 994 995 if (m_options.launch_info.GetExecutableFile()) { 996 Debugger &debugger = GetDebugger(); 997 998 if (argc == 0) 999 target->GetRunArguments(m_options.launch_info.GetArguments()); 1000 1001 ProcessSP process_sp(platform_sp->DebugProcess( 1002 m_options.launch_info, debugger, target, error)); 1003 if (process_sp && process_sp->IsAlive()) { 1004 result.SetStatus(eReturnStatusSuccessFinishNoResult); 1005 return true; 1006 } 1007 1008 if (error.Success()) 1009 result.AppendError("process launch failed"); 1010 else 1011 result.AppendError(error.AsCString()); 1012 result.SetStatus(eReturnStatusFailed); 1013 } else { 1014 result.AppendError("'platform process launch' uses the current target " 1015 "file and arguments, or the executable and its " 1016 "arguments can be specified in this command"); 1017 result.SetStatus(eReturnStatusFailed); 1018 return false; 1019 } 1020 } else { 1021 result.AppendError("no platform is selected\n"); 1022 } 1023 return result.Succeeded(); 1024 } 1025 1026 protected: 1027 ProcessLaunchCommandOptions m_options; 1028 }; 1029 1030 // "platform process list" 1031 1032 static PosixPlatformCommandOptionValidator posix_validator; 1033 #define LLDB_OPTIONS_platform_process_list 1034 #include "CommandOptions.inc" 1035 1036 class CommandObjectPlatformProcessList : public CommandObjectParsed { 1037 public: 1038 CommandObjectPlatformProcessList(CommandInterpreter &interpreter) 1039 : CommandObjectParsed(interpreter, "platform process list", 1040 "List processes on a remote platform by name, pid, " 1041 "or many other matching attributes.", 1042 "platform process list", 0), 1043 m_options() {} 1044 1045 ~CommandObjectPlatformProcessList() override = default; 1046 1047 Options *GetOptions() override { return &m_options; } 1048 1049 protected: 1050 bool DoExecute(Args &args, CommandReturnObject &result) override { 1051 Target *target = GetDebugger().GetSelectedTarget().get(); 1052 PlatformSP platform_sp; 1053 if (target) { 1054 platform_sp = target->GetPlatform(); 1055 } 1056 if (!platform_sp) { 1057 platform_sp = GetDebugger().GetPlatformList().GetSelectedPlatform(); 1058 } 1059 1060 if (platform_sp) { 1061 Status error; 1062 if (args.GetArgumentCount() == 0) { 1063 if (platform_sp) { 1064 Stream &ostrm = result.GetOutputStream(); 1065 1066 lldb::pid_t pid = 1067 m_options.match_info.GetProcessInfo().GetProcessID(); 1068 if (pid != LLDB_INVALID_PROCESS_ID) { 1069 ProcessInstanceInfo proc_info; 1070 if (platform_sp->GetProcessInfo(pid, proc_info)) { 1071 ProcessInstanceInfo::DumpTableHeader(ostrm, m_options.show_args, 1072 m_options.verbose); 1073 proc_info.DumpAsTableRow(ostrm, platform_sp->GetUserIDResolver(), 1074 m_options.show_args, m_options.verbose); 1075 result.SetStatus(eReturnStatusSuccessFinishResult); 1076 } else { 1077 result.AppendErrorWithFormat( 1078 "no process found with pid = %" PRIu64 "\n", pid); 1079 result.SetStatus(eReturnStatusFailed); 1080 } 1081 } else { 1082 ProcessInstanceInfoList proc_infos; 1083 const uint32_t matches = 1084 platform_sp->FindProcesses(m_options.match_info, proc_infos); 1085 const char *match_desc = nullptr; 1086 const char *match_name = 1087 m_options.match_info.GetProcessInfo().GetName(); 1088 if (match_name && match_name[0]) { 1089 switch (m_options.match_info.GetNameMatchType()) { 1090 case NameMatch::Ignore: 1091 break; 1092 case NameMatch::Equals: 1093 match_desc = "matched"; 1094 break; 1095 case NameMatch::Contains: 1096 match_desc = "contained"; 1097 break; 1098 case NameMatch::StartsWith: 1099 match_desc = "started with"; 1100 break; 1101 case NameMatch::EndsWith: 1102 match_desc = "ended with"; 1103 break; 1104 case NameMatch::RegularExpression: 1105 match_desc = "matched the regular expression"; 1106 break; 1107 } 1108 } 1109 1110 if (matches == 0) { 1111 if (match_desc) 1112 result.AppendErrorWithFormat( 1113 "no processes were found that %s \"%s\" on the \"%s\" " 1114 "platform\n", 1115 match_desc, match_name, 1116 platform_sp->GetPluginName().GetCString()); 1117 else 1118 result.AppendErrorWithFormat( 1119 "no processes were found on the \"%s\" platform\n", 1120 platform_sp->GetPluginName().GetCString()); 1121 result.SetStatus(eReturnStatusFailed); 1122 } else { 1123 result.AppendMessageWithFormat( 1124 "%u matching process%s found on \"%s\"", matches, 1125 matches > 1 ? "es were" : " was", 1126 platform_sp->GetName().GetCString()); 1127 if (match_desc) 1128 result.AppendMessageWithFormat(" whose name %s \"%s\"", 1129 match_desc, match_name); 1130 result.AppendMessageWithFormat("\n"); 1131 ProcessInstanceInfo::DumpTableHeader(ostrm, m_options.show_args, 1132 m_options.verbose); 1133 for (uint32_t i = 0; i < matches; ++i) { 1134 proc_infos[i].DumpAsTableRow( 1135 ostrm, platform_sp->GetUserIDResolver(), 1136 m_options.show_args, m_options.verbose); 1137 } 1138 } 1139 } 1140 } 1141 } else { 1142 result.AppendError("invalid args: process list takes only options\n"); 1143 result.SetStatus(eReturnStatusFailed); 1144 } 1145 } else { 1146 result.AppendError("no platform is selected\n"); 1147 result.SetStatus(eReturnStatusFailed); 1148 } 1149 return result.Succeeded(); 1150 } 1151 1152 class CommandOptions : public Options { 1153 public: 1154 CommandOptions() 1155 : Options(), match_info(), show_args(false), verbose(false) {} 1156 1157 ~CommandOptions() override = default; 1158 1159 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 1160 ExecutionContext *execution_context) override { 1161 Status error; 1162 const int short_option = m_getopt_table[option_idx].val; 1163 bool success = false; 1164 1165 uint32_t id = LLDB_INVALID_PROCESS_ID; 1166 success = !option_arg.getAsInteger(0, id); 1167 switch (short_option) { 1168 case 'p': { 1169 match_info.GetProcessInfo().SetProcessID(id); 1170 if (!success) 1171 error.SetErrorStringWithFormat("invalid process ID string: '%s'", 1172 option_arg.str().c_str()); 1173 break; 1174 } 1175 case 'P': 1176 match_info.GetProcessInfo().SetParentProcessID(id); 1177 if (!success) 1178 error.SetErrorStringWithFormat( 1179 "invalid parent process ID string: '%s'", 1180 option_arg.str().c_str()); 1181 break; 1182 1183 case 'u': 1184 match_info.GetProcessInfo().SetUserID(success ? id : UINT32_MAX); 1185 if (!success) 1186 error.SetErrorStringWithFormat("invalid user ID string: '%s'", 1187 option_arg.str().c_str()); 1188 break; 1189 1190 case 'U': 1191 match_info.GetProcessInfo().SetEffectiveUserID(success ? id 1192 : UINT32_MAX); 1193 if (!success) 1194 error.SetErrorStringWithFormat( 1195 "invalid effective user ID string: '%s'", 1196 option_arg.str().c_str()); 1197 break; 1198 1199 case 'g': 1200 match_info.GetProcessInfo().SetGroupID(success ? id : UINT32_MAX); 1201 if (!success) 1202 error.SetErrorStringWithFormat("invalid group ID string: '%s'", 1203 option_arg.str().c_str()); 1204 break; 1205 1206 case 'G': 1207 match_info.GetProcessInfo().SetEffectiveGroupID(success ? id 1208 : UINT32_MAX); 1209 if (!success) 1210 error.SetErrorStringWithFormat( 1211 "invalid effective group ID string: '%s'", 1212 option_arg.str().c_str()); 1213 break; 1214 1215 case 'a': { 1216 TargetSP target_sp = 1217 execution_context ? execution_context->GetTargetSP() : TargetSP(); 1218 DebuggerSP debugger_sp = 1219 target_sp ? target_sp->GetDebugger().shared_from_this() 1220 : DebuggerSP(); 1221 PlatformSP platform_sp = 1222 debugger_sp ? debugger_sp->GetPlatformList().GetSelectedPlatform() 1223 : PlatformSP(); 1224 match_info.GetProcessInfo().GetArchitecture() = 1225 Platform::GetAugmentedArchSpec(platform_sp.get(), option_arg); 1226 } break; 1227 1228 case 'n': 1229 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1230 option_arg, FileSpec::Style::native); 1231 match_info.SetNameMatchType(NameMatch::Equals); 1232 break; 1233 1234 case 'e': 1235 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1236 option_arg, FileSpec::Style::native); 1237 match_info.SetNameMatchType(NameMatch::EndsWith); 1238 break; 1239 1240 case 's': 1241 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1242 option_arg, FileSpec::Style::native); 1243 match_info.SetNameMatchType(NameMatch::StartsWith); 1244 break; 1245 1246 case 'c': 1247 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1248 option_arg, FileSpec::Style::native); 1249 match_info.SetNameMatchType(NameMatch::Contains); 1250 break; 1251 1252 case 'r': 1253 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1254 option_arg, FileSpec::Style::native); 1255 match_info.SetNameMatchType(NameMatch::RegularExpression); 1256 break; 1257 1258 case 'A': 1259 show_args = true; 1260 break; 1261 1262 case 'v': 1263 verbose = true; 1264 break; 1265 1266 case 'x': 1267 match_info.SetMatchAllUsers(true); 1268 break; 1269 1270 default: 1271 llvm_unreachable("Unimplemented option"); 1272 } 1273 1274 return error; 1275 } 1276 1277 void OptionParsingStarting(ExecutionContext *execution_context) override { 1278 match_info.Clear(); 1279 show_args = false; 1280 verbose = false; 1281 } 1282 1283 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 1284 return llvm::makeArrayRef(g_platform_process_list_options); 1285 } 1286 1287 // Instance variables to hold the values for command options. 1288 1289 ProcessInstanceInfoMatch match_info; 1290 bool show_args; 1291 bool verbose; 1292 }; 1293 1294 CommandOptions m_options; 1295 }; 1296 1297 // "platform process info" 1298 class CommandObjectPlatformProcessInfo : public CommandObjectParsed { 1299 public: 1300 CommandObjectPlatformProcessInfo(CommandInterpreter &interpreter) 1301 : CommandObjectParsed( 1302 interpreter, "platform process info", 1303 "Get detailed information for one or more process by process ID.", 1304 "platform process info <pid> [<pid> <pid> ...]", 0) { 1305 CommandArgumentEntry arg; 1306 CommandArgumentData pid_args; 1307 1308 // Define the first (and only) variant of this arg. 1309 pid_args.arg_type = eArgTypePid; 1310 pid_args.arg_repetition = eArgRepeatStar; 1311 1312 // There is only one variant this argument could be; put it into the 1313 // argument entry. 1314 arg.push_back(pid_args); 1315 1316 // Push the data for the first argument into the m_arguments vector. 1317 m_arguments.push_back(arg); 1318 } 1319 1320 ~CommandObjectPlatformProcessInfo() override = default; 1321 1322 protected: 1323 bool DoExecute(Args &args, CommandReturnObject &result) override { 1324 Target *target = GetDebugger().GetSelectedTarget().get(); 1325 PlatformSP platform_sp; 1326 if (target) { 1327 platform_sp = target->GetPlatform(); 1328 } 1329 if (!platform_sp) { 1330 platform_sp = GetDebugger().GetPlatformList().GetSelectedPlatform(); 1331 } 1332 1333 if (platform_sp) { 1334 const size_t argc = args.GetArgumentCount(); 1335 if (argc > 0) { 1336 Status error; 1337 1338 if (platform_sp->IsConnected()) { 1339 Stream &ostrm = result.GetOutputStream(); 1340 for (auto &entry : args.entries()) { 1341 lldb::pid_t pid; 1342 if (entry.ref().getAsInteger(0, pid)) { 1343 result.AppendErrorWithFormat("invalid process ID argument '%s'", 1344 entry.ref().str().c_str()); 1345 result.SetStatus(eReturnStatusFailed); 1346 break; 1347 } else { 1348 ProcessInstanceInfo proc_info; 1349 if (platform_sp->GetProcessInfo(pid, proc_info)) { 1350 ostrm.Printf("Process information for process %" PRIu64 ":\n", 1351 pid); 1352 proc_info.Dump(ostrm, platform_sp->GetUserIDResolver()); 1353 } else { 1354 ostrm.Printf("error: no process information is available for " 1355 "process %" PRIu64 "\n", 1356 pid); 1357 } 1358 ostrm.EOL(); 1359 } 1360 } 1361 } else { 1362 // Not connected... 1363 result.AppendErrorWithFormat( 1364 "not connected to '%s'", 1365 platform_sp->GetPluginName().GetCString()); 1366 result.SetStatus(eReturnStatusFailed); 1367 } 1368 } else { 1369 // No args 1370 result.AppendError("one or more process id(s) must be specified"); 1371 result.SetStatus(eReturnStatusFailed); 1372 } 1373 } else { 1374 result.AppendError("no platform is currently selected"); 1375 result.SetStatus(eReturnStatusFailed); 1376 } 1377 return result.Succeeded(); 1378 } 1379 }; 1380 1381 #define LLDB_OPTIONS_platform_process_attach 1382 #include "CommandOptions.inc" 1383 1384 class CommandObjectPlatformProcessAttach : public CommandObjectParsed { 1385 public: 1386 class CommandOptions : public Options { 1387 public: 1388 CommandOptions() : Options() { 1389 // Keep default values of all options in one place: OptionParsingStarting 1390 // () 1391 OptionParsingStarting(nullptr); 1392 } 1393 1394 ~CommandOptions() override = default; 1395 1396 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 1397 ExecutionContext *execution_context) override { 1398 Status error; 1399 char short_option = (char)m_getopt_table[option_idx].val; 1400 switch (short_option) { 1401 case 'p': { 1402 lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; 1403 if (option_arg.getAsInteger(0, pid)) { 1404 error.SetErrorStringWithFormat("invalid process ID '%s'", 1405 option_arg.str().c_str()); 1406 } else { 1407 attach_info.SetProcessID(pid); 1408 } 1409 } break; 1410 1411 case 'P': 1412 attach_info.SetProcessPluginName(option_arg); 1413 break; 1414 1415 case 'n': 1416 attach_info.GetExecutableFile().SetFile(option_arg, 1417 FileSpec::Style::native); 1418 break; 1419 1420 case 'w': 1421 attach_info.SetWaitForLaunch(true); 1422 break; 1423 1424 default: 1425 llvm_unreachable("Unimplemented option"); 1426 } 1427 return error; 1428 } 1429 1430 void OptionParsingStarting(ExecutionContext *execution_context) override { 1431 attach_info.Clear(); 1432 } 1433 1434 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 1435 return llvm::makeArrayRef(g_platform_process_attach_options); 1436 } 1437 1438 void HandleOptionArgumentCompletion( 1439 CompletionRequest &request, OptionElementVector &opt_element_vector, 1440 int opt_element_index, CommandInterpreter &interpreter) override { 1441 int opt_arg_pos = opt_element_vector[opt_element_index].opt_arg_pos; 1442 int opt_defs_index = opt_element_vector[opt_element_index].opt_defs_index; 1443 1444 // We are only completing the name option for now... 1445 1446 // Are we in the name? 1447 if (GetDefinitions()[opt_defs_index].short_option != 'n') 1448 return; 1449 1450 // Look to see if there is a -P argument provided, and if so use that 1451 // plugin, otherwise use the default plugin. 1452 1453 const char *partial_name = nullptr; 1454 partial_name = request.GetParsedLine().GetArgumentAtIndex(opt_arg_pos); 1455 1456 PlatformSP platform_sp(interpreter.GetPlatform(true)); 1457 if (!platform_sp) 1458 return; 1459 1460 ProcessInstanceInfoList process_infos; 1461 ProcessInstanceInfoMatch match_info; 1462 if (partial_name) { 1463 match_info.GetProcessInfo().GetExecutableFile().SetFile( 1464 partial_name, FileSpec::Style::native); 1465 match_info.SetNameMatchType(NameMatch::StartsWith); 1466 } 1467 platform_sp->FindProcesses(match_info, process_infos); 1468 const uint32_t num_matches = process_infos.size(); 1469 if (num_matches == 0) 1470 return; 1471 1472 for (uint32_t i = 0; i < num_matches; ++i) { 1473 request.AddCompletion(process_infos[i].GetNameAsStringRef()); 1474 } 1475 return; 1476 } 1477 1478 // Options table: Required for subclasses of Options. 1479 1480 static OptionDefinition g_option_table[]; 1481 1482 // Instance variables to hold the values for command options. 1483 1484 ProcessAttachInfo attach_info; 1485 }; 1486 1487 CommandObjectPlatformProcessAttach(CommandInterpreter &interpreter) 1488 : CommandObjectParsed(interpreter, "platform process attach", 1489 "Attach to a process.", 1490 "platform process attach <cmd-options>"), 1491 m_options() {} 1492 1493 ~CommandObjectPlatformProcessAttach() override = default; 1494 1495 bool DoExecute(Args &command, CommandReturnObject &result) override { 1496 PlatformSP platform_sp( 1497 GetDebugger().GetPlatformList().GetSelectedPlatform()); 1498 if (platform_sp) { 1499 Status err; 1500 ProcessSP remote_process_sp = platform_sp->Attach( 1501 m_options.attach_info, GetDebugger(), nullptr, err); 1502 if (err.Fail()) { 1503 result.AppendError(err.AsCString()); 1504 result.SetStatus(eReturnStatusFailed); 1505 } else if (!remote_process_sp) { 1506 result.AppendError("could not attach: unknown reason"); 1507 result.SetStatus(eReturnStatusFailed); 1508 } else 1509 result.SetStatus(eReturnStatusSuccessFinishResult); 1510 } else { 1511 result.AppendError("no platform is currently selected"); 1512 result.SetStatus(eReturnStatusFailed); 1513 } 1514 return result.Succeeded(); 1515 } 1516 1517 Options *GetOptions() override { return &m_options; } 1518 1519 protected: 1520 CommandOptions m_options; 1521 }; 1522 1523 class CommandObjectPlatformProcess : public CommandObjectMultiword { 1524 public: 1525 // Constructors and Destructors 1526 CommandObjectPlatformProcess(CommandInterpreter &interpreter) 1527 : CommandObjectMultiword(interpreter, "platform process", 1528 "Commands to query, launch and attach to " 1529 "processes on the current platform.", 1530 "platform process [attach|launch|list] ...") { 1531 LoadSubCommand( 1532 "attach", 1533 CommandObjectSP(new CommandObjectPlatformProcessAttach(interpreter))); 1534 LoadSubCommand( 1535 "launch", 1536 CommandObjectSP(new CommandObjectPlatformProcessLaunch(interpreter))); 1537 LoadSubCommand("info", CommandObjectSP(new CommandObjectPlatformProcessInfo( 1538 interpreter))); 1539 LoadSubCommand("list", CommandObjectSP(new CommandObjectPlatformProcessList( 1540 interpreter))); 1541 } 1542 1543 ~CommandObjectPlatformProcess() override = default; 1544 1545 private: 1546 // For CommandObjectPlatform only 1547 CommandObjectPlatformProcess(const CommandObjectPlatformProcess &) = delete; 1548 const CommandObjectPlatformProcess & 1549 operator=(const CommandObjectPlatformProcess &) = delete; 1550 }; 1551 1552 // "platform shell" 1553 #define LLDB_OPTIONS_platform_shell 1554 #include "CommandOptions.inc" 1555 1556 class CommandObjectPlatformShell : public CommandObjectRaw { 1557 public: 1558 class CommandOptions : public Options { 1559 public: 1560 CommandOptions() : Options() {} 1561 1562 ~CommandOptions() override = default; 1563 1564 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 1565 return llvm::makeArrayRef(g_platform_shell_options); 1566 } 1567 1568 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 1569 ExecutionContext *execution_context) override { 1570 Status error; 1571 1572 const char short_option = (char)GetDefinitions()[option_idx].short_option; 1573 1574 switch (short_option) { 1575 case 'h': 1576 m_use_host_platform = true; 1577 break; 1578 case 't': 1579 uint32_t timeout_sec; 1580 if (option_arg.getAsInteger(10, timeout_sec)) 1581 error.SetErrorStringWithFormat( 1582 "could not convert \"%s\" to a numeric value.", 1583 option_arg.str().c_str()); 1584 else 1585 m_timeout = std::chrono::seconds(timeout_sec); 1586 break; 1587 default: 1588 llvm_unreachable("Unimplemented option"); 1589 } 1590 1591 return error; 1592 } 1593 1594 void OptionParsingStarting(ExecutionContext *execution_context) override { 1595 m_timeout.reset(); 1596 m_use_host_platform = false; 1597 } 1598 1599 Timeout<std::micro> m_timeout = std::chrono::seconds(10); 1600 bool m_use_host_platform; 1601 }; 1602 1603 CommandObjectPlatformShell(CommandInterpreter &interpreter) 1604 : CommandObjectRaw(interpreter, "platform shell", 1605 "Run a shell command on the current platform.", 1606 "platform shell <shell-command>", 0), 1607 m_options() {} 1608 1609 ~CommandObjectPlatformShell() override = default; 1610 1611 Options *GetOptions() override { return &m_options; } 1612 1613 bool DoExecute(llvm::StringRef raw_command_line, 1614 CommandReturnObject &result) override { 1615 ExecutionContext exe_ctx = GetCommandInterpreter().GetExecutionContext(); 1616 m_options.NotifyOptionParsingStarting(&exe_ctx); 1617 1618 // Print out an usage syntax on an empty command line. 1619 if (raw_command_line.empty()) { 1620 result.GetOutputStream().Printf("%s\n", this->GetSyntax().str().c_str()); 1621 return true; 1622 } 1623 1624 const bool is_alias = !raw_command_line.contains("platform"); 1625 OptionsWithRaw args(raw_command_line); 1626 const char *expr = args.GetRawPart().c_str(); 1627 1628 if (args.HasArgs()) 1629 if (!ParseOptions(args.GetArgs(), result)) 1630 return false; 1631 1632 if (args.GetRawPart().empty()) { 1633 result.GetOutputStream().Printf("%s <shell-command>\n", 1634 is_alias ? "shell" : "platform shell"); 1635 return false; 1636 } 1637 1638 PlatformSP platform_sp( 1639 m_options.m_use_host_platform 1640 ? Platform::GetHostPlatform() 1641 : GetDebugger().GetPlatformList().GetSelectedPlatform()); 1642 Status error; 1643 if (platform_sp) { 1644 FileSpec working_dir{}; 1645 std::string output; 1646 int status = -1; 1647 int signo = -1; 1648 error = (platform_sp->RunShellCommand(expr, working_dir, &status, &signo, 1649 &output, m_options.m_timeout)); 1650 if (!output.empty()) 1651 result.GetOutputStream().PutCString(output); 1652 if (status > 0) { 1653 if (signo > 0) { 1654 const char *signo_cstr = Host::GetSignalAsCString(signo); 1655 if (signo_cstr) 1656 result.GetOutputStream().Printf( 1657 "error: command returned with status %i and signal %s\n", 1658 status, signo_cstr); 1659 else 1660 result.GetOutputStream().Printf( 1661 "error: command returned with status %i and signal %i\n", 1662 status, signo); 1663 } else 1664 result.GetOutputStream().Printf( 1665 "error: command returned with status %i\n", status); 1666 } 1667 } else { 1668 result.GetOutputStream().Printf( 1669 "error: cannot run remote shell commands without a platform\n"); 1670 error.SetErrorString( 1671 "error: cannot run remote shell commands without a platform"); 1672 } 1673 1674 if (error.Fail()) { 1675 result.AppendError(error.AsCString()); 1676 result.SetStatus(eReturnStatusFailed); 1677 } else { 1678 result.SetStatus(eReturnStatusSuccessFinishResult); 1679 } 1680 return true; 1681 } 1682 1683 CommandOptions m_options; 1684 }; 1685 1686 // "platform install" - install a target to a remote end 1687 class CommandObjectPlatformInstall : public CommandObjectParsed { 1688 public: 1689 CommandObjectPlatformInstall(CommandInterpreter &interpreter) 1690 : CommandObjectParsed( 1691 interpreter, "platform target-install", 1692 "Install a target (bundle or executable file) to the remote end.", 1693 "platform target-install <local-thing> <remote-sandbox>", 0) {} 1694 1695 ~CommandObjectPlatformInstall() override = default; 1696 1697 bool DoExecute(Args &args, CommandReturnObject &result) override { 1698 if (args.GetArgumentCount() != 2) { 1699 result.AppendError("platform target-install takes two arguments"); 1700 result.SetStatus(eReturnStatusFailed); 1701 return false; 1702 } 1703 // TODO: move the bulk of this code over to the platform itself 1704 FileSpec src(args.GetArgumentAtIndex(0)); 1705 FileSystem::Instance().Resolve(src); 1706 FileSpec dst(args.GetArgumentAtIndex(1)); 1707 if (!FileSystem::Instance().Exists(src)) { 1708 result.AppendError("source location does not exist or is not accessible"); 1709 result.SetStatus(eReturnStatusFailed); 1710 return false; 1711 } 1712 PlatformSP platform_sp( 1713 GetDebugger().GetPlatformList().GetSelectedPlatform()); 1714 if (!platform_sp) { 1715 result.AppendError("no platform currently selected"); 1716 result.SetStatus(eReturnStatusFailed); 1717 return false; 1718 } 1719 1720 Status error = platform_sp->Install(src, dst); 1721 if (error.Success()) { 1722 result.SetStatus(eReturnStatusSuccessFinishNoResult); 1723 } else { 1724 result.AppendErrorWithFormat("install failed: %s", error.AsCString()); 1725 result.SetStatus(eReturnStatusFailed); 1726 } 1727 return result.Succeeded(); 1728 } 1729 }; 1730 1731 CommandObjectPlatform::CommandObjectPlatform(CommandInterpreter &interpreter) 1732 : CommandObjectMultiword( 1733 interpreter, "platform", "Commands to manage and create platforms.", 1734 "platform [connect|disconnect|info|list|status|select] ...") { 1735 LoadSubCommand("select", 1736 CommandObjectSP(new CommandObjectPlatformSelect(interpreter))); 1737 LoadSubCommand("list", 1738 CommandObjectSP(new CommandObjectPlatformList(interpreter))); 1739 LoadSubCommand("status", 1740 CommandObjectSP(new CommandObjectPlatformStatus(interpreter))); 1741 LoadSubCommand("connect", CommandObjectSP( 1742 new CommandObjectPlatformConnect(interpreter))); 1743 LoadSubCommand( 1744 "disconnect", 1745 CommandObjectSP(new CommandObjectPlatformDisconnect(interpreter))); 1746 LoadSubCommand("settings", CommandObjectSP(new CommandObjectPlatformSettings( 1747 interpreter))); 1748 LoadSubCommand("mkdir", 1749 CommandObjectSP(new CommandObjectPlatformMkDir(interpreter))); 1750 LoadSubCommand("file", 1751 CommandObjectSP(new CommandObjectPlatformFile(interpreter))); 1752 LoadSubCommand("get-file", CommandObjectSP(new CommandObjectPlatformGetFile( 1753 interpreter))); 1754 LoadSubCommand("get-size", CommandObjectSP(new CommandObjectPlatformGetSize( 1755 interpreter))); 1756 LoadSubCommand("put-file", CommandObjectSP(new CommandObjectPlatformPutFile( 1757 interpreter))); 1758 LoadSubCommand("process", CommandObjectSP( 1759 new CommandObjectPlatformProcess(interpreter))); 1760 LoadSubCommand("shell", 1761 CommandObjectSP(new CommandObjectPlatformShell(interpreter))); 1762 LoadSubCommand( 1763 "target-install", 1764 CommandObjectSP(new CommandObjectPlatformInstall(interpreter))); 1765 } 1766 1767 CommandObjectPlatform::~CommandObjectPlatform() = default; 1768