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