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