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