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