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