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