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