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