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