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