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