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