1//===-- CommandInterpreter.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 <chrono>
10#include <cstdlib>
11#include <limits>
12#include <memory>
13#include <optional>
14#include <string>
15#include <vector>
16
17#include "Commands/CommandObjectApropos.h"
18#include "Commands/CommandObjectBreakpoint.h"
19#include "Commands/CommandObjectCommands.h"
20#include "Commands/CommandObjectDWIMPrint.h"
21#include "Commands/CommandObjectDiagnostics.h"
22#include "Commands/CommandObjectDisassemble.h"
23#include "Commands/CommandObjectExpression.h"
24#include "Commands/CommandObjectFrame.h"
25#include "Commands/CommandObjectGUI.h"
26#include "Commands/CommandObjectHelp.h"
27#include "Commands/CommandObjectLanguage.h"
28#include "Commands/CommandObjectLog.h"
29#include "Commands/CommandObjectMemory.h"
30#include "Commands/CommandObjectPlatform.h"
31#include "Commands/CommandObjectPlugin.h"
32#include "Commands/CommandObjectProcess.h"
33#include "Commands/CommandObjectQuit.h"
34#include "Commands/CommandObjectRegexCommand.h"
35#include "Commands/CommandObjectRegister.h"
36#include "Commands/CommandObjectScripting.h"
37#include "Commands/CommandObjectSession.h"
38#include "Commands/CommandObjectSettings.h"
39#include "Commands/CommandObjectSource.h"
40#include "Commands/CommandObjectStats.h"
41#include "Commands/CommandObjectTarget.h"
42#include "Commands/CommandObjectThread.h"
43#include "Commands/CommandObjectTrace.h"
44#include "Commands/CommandObjectType.h"
45#include "Commands/CommandObjectVersion.h"
46#include "Commands/CommandObjectWatchpoint.h"
47
48#include "lldb/Core/Debugger.h"
49#include "lldb/Core/Module.h"
50#include "lldb/Core/PluginManager.h"
51#include "lldb/Core/Telemetry.h"
52#include "lldb/Host/StreamFile.h"
53#include "lldb/Utility/ErrorMessages.h"
54#include "lldb/Utility/LLDBLog.h"
55#include "lldb/Utility/Log.h"
56#include "lldb/Utility/State.h"
57#include "lldb/Utility/Stream.h"
58#include "lldb/Utility/StructuredData.h"
59#include "lldb/Utility/Timer.h"
60
61#include "lldb/Host/Config.h"
62#include "lldb/lldb-forward.h"
63#if LLDB_ENABLE_LIBEDIT
64#include "lldb/Host/Editline.h"
65#endif
66#include "lldb/Host/File.h"
67#include "lldb/Host/FileCache.h"
68#include "lldb/Host/Host.h"
69#include "lldb/Host/HostInfo.h"
70
71#include "lldb/Interpreter/CommandCompletions.h"
72#include "lldb/Interpreter/CommandInterpreter.h"
73#include "lldb/Interpreter/CommandReturnObject.h"
74#include "lldb/Interpreter/OptionValueProperties.h"
75#include "lldb/Interpreter/Options.h"
76#include "lldb/Interpreter/Property.h"
77#include "lldb/Utility/Args.h"
78
79#include "lldb/Target/Language.h"
80#include "lldb/Target/Process.h"
81#include "lldb/Target/StopInfo.h"
82#include "lldb/Target/TargetList.h"
83#include "lldb/Target/Thread.h"
84#include "lldb/Target/UnixSignals.h"
85
86#include "llvm/ADT/STLExtras.h"
87#include "llvm/ADT/ScopeExit.h"
88#include "llvm/ADT/SmallString.h"
89#include "llvm/Support/FormatAdapters.h"
90#include "llvm/Support/Path.h"
91#include "llvm/Support/PrettyStackTrace.h"
92#include "llvm/Support/ScopedPrinter.h"
93#include "llvm/Telemetry/Telemetry.h"
94
95#if defined(__APPLE__)
96#include <TargetConditionals.h>
97#endif
98
99using namespace lldb;
100using namespace lldb_private;
101
102static const char *k_white_space = " \t\v";
103
104static constexpr const char *InitFileWarning =
105 "There is a .lldbinit file in the current directory which is not being "
106 "read.\n"
107 "To silence this warning without sourcing in the local .lldbinit,\n"
108 "add the following to the lldbinit file in your home directory:\n"
109 " settings set target.load-cwd-lldbinit false\n"
110 "To allow lldb to source .lldbinit files in the current working "
111 "directory,\n"
112 "set the value of this variable to true. Only do so if you understand "
113 "and\n"
114 "accept the security risk.";
115
116const char *CommandInterpreter::g_no_argument = "<no-argument>";
117const char *CommandInterpreter::g_need_argument = "<need-argument>";
118const char *CommandInterpreter::g_argument = "<argument>";
119
120#define LLDB_PROPERTIES_interpreter
121#include "InterpreterProperties.inc"
122
123enum {
124#define LLDB_PROPERTIES_interpreter
125#include "InterpreterPropertiesEnum.inc"
126};
127
128llvm::StringRef CommandInterpreter::GetStaticBroadcasterClass() {
129 static constexpr llvm::StringLiteral class_name("lldb.commandInterpreter");
130 return class_name;
131}
132
133CommandInterpreter::CommandInterpreter(Debugger &debugger,
134 bool synchronous_execution)
135 : Broadcaster(debugger.GetBroadcasterManager(),
136 CommandInterpreter::GetStaticBroadcasterClass().str()),
137 Properties(
138 OptionValuePropertiesSP(new OptionValueProperties("interpreter"))),
139 IOHandlerDelegate(IOHandlerDelegate::Completion::LLDBCommand),
140 m_debugger(debugger), m_synchronous_execution(true),
141 m_skip_lldbinit_files(false), m_skip_app_init_files(false),
142 m_comment_char('#'), m_batch_command_mode(false),
143 m_truncation_warning(eNoOmission), m_max_depth_warning(eNoOmission),
144 m_command_source_depth(0) {
145 SetEventName(event_mask: eBroadcastBitThreadShouldExit, name: "thread-should-exit");
146 SetEventName(event_mask: eBroadcastBitResetPrompt, name: "reset-prompt");
147 SetEventName(event_mask: eBroadcastBitQuitCommandReceived, name: "quit");
148 SetSynchronous(synchronous_execution);
149 CheckInWithManager();
150 m_collection_sp->Initialize(setting_definitions: g_interpreter_properties);
151}
152
153bool CommandInterpreter::GetExpandRegexAliases() const {
154 const uint32_t idx = ePropertyExpandRegexAliases;
155 return GetPropertyAtIndexAs<bool>(
156 idx, g_interpreter_properties[idx].default_uint_value != 0);
157}
158
159bool CommandInterpreter::GetPromptOnQuit() const {
160 const uint32_t idx = ePropertyPromptOnQuit;
161 return GetPropertyAtIndexAs<bool>(
162 idx, g_interpreter_properties[idx].default_uint_value != 0);
163}
164
165void CommandInterpreter::SetPromptOnQuit(bool enable) {
166 const uint32_t idx = ePropertyPromptOnQuit;
167 SetPropertyAtIndex(idx, t: enable);
168}
169
170bool CommandInterpreter::GetSaveTranscript() const {
171 const uint32_t idx = ePropertySaveTranscript;
172 return GetPropertyAtIndexAs<bool>(
173 idx, g_interpreter_properties[idx].default_uint_value != 0);
174}
175
176void CommandInterpreter::SetSaveTranscript(bool enable) {
177 const uint32_t idx = ePropertySaveTranscript;
178 SetPropertyAtIndex(idx, t: enable);
179}
180
181bool CommandInterpreter::GetSaveSessionOnQuit() const {
182 const uint32_t idx = ePropertySaveSessionOnQuit;
183 return GetPropertyAtIndexAs<bool>(
184 idx, g_interpreter_properties[idx].default_uint_value != 0);
185}
186
187void CommandInterpreter::SetSaveSessionOnQuit(bool enable) {
188 const uint32_t idx = ePropertySaveSessionOnQuit;
189 SetPropertyAtIndex(idx, t: enable);
190}
191
192bool CommandInterpreter::GetOpenTranscriptInEditor() const {
193 const uint32_t idx = ePropertyOpenTranscriptInEditor;
194 return GetPropertyAtIndexAs<bool>(
195 idx, g_interpreter_properties[idx].default_uint_value != 0);
196}
197
198void CommandInterpreter::SetOpenTranscriptInEditor(bool enable) {
199 const uint32_t idx = ePropertyOpenTranscriptInEditor;
200 SetPropertyAtIndex(idx, t: enable);
201}
202
203FileSpec CommandInterpreter::GetSaveSessionDirectory() const {
204 const uint32_t idx = ePropertySaveSessionDirectory;
205 return GetPropertyAtIndexAs<FileSpec>(idx, default_value: {});
206}
207
208void CommandInterpreter::SetSaveSessionDirectory(llvm::StringRef path) {
209 const uint32_t idx = ePropertySaveSessionDirectory;
210 SetPropertyAtIndex(idx, t: path);
211}
212
213bool CommandInterpreter::GetEchoCommands() const {
214 const uint32_t idx = ePropertyEchoCommands;
215 return GetPropertyAtIndexAs<bool>(
216 idx, g_interpreter_properties[idx].default_uint_value != 0);
217}
218
219void CommandInterpreter::SetEchoCommands(bool enable) {
220 const uint32_t idx = ePropertyEchoCommands;
221 SetPropertyAtIndex(idx, t: enable);
222}
223
224bool CommandInterpreter::GetEchoCommentCommands() const {
225 const uint32_t idx = ePropertyEchoCommentCommands;
226 return GetPropertyAtIndexAs<bool>(
227 idx, g_interpreter_properties[idx].default_uint_value != 0);
228}
229
230void CommandInterpreter::SetEchoCommentCommands(bool enable) {
231 const uint32_t idx = ePropertyEchoCommentCommands;
232 SetPropertyAtIndex(idx, t: enable);
233}
234
235void CommandInterpreter::AllowExitCodeOnQuit(bool allow) {
236 m_allow_exit_code = allow;
237 if (!allow)
238 m_quit_exit_code.reset();
239}
240
241bool CommandInterpreter::SetQuitExitCode(int exit_code) {
242 if (!m_allow_exit_code)
243 return false;
244 m_quit_exit_code = exit_code;
245 return true;
246}
247
248int CommandInterpreter::GetQuitExitCode(bool &exited) const {
249 exited = m_quit_exit_code.has_value();
250 if (exited)
251 return *m_quit_exit_code;
252 return 0;
253}
254
255void CommandInterpreter::ResolveCommand(const char *command_line,
256 CommandReturnObject &result) {
257 std::string command = command_line;
258 if (ResolveCommandImpl(command_line&: command, result) != nullptr) {
259 result.AppendMessageWithFormat(format: "%s", command.c_str());
260 result.SetStatus(eReturnStatusSuccessFinishResult);
261 }
262}
263
264bool CommandInterpreter::GetStopCmdSourceOnError() const {
265 const uint32_t idx = ePropertyStopCmdSourceOnError;
266 return GetPropertyAtIndexAs<bool>(
267 idx, g_interpreter_properties[idx].default_uint_value != 0);
268}
269
270bool CommandInterpreter::GetSpaceReplPrompts() const {
271 const uint32_t idx = ePropertySpaceReplPrompts;
272 return GetPropertyAtIndexAs<bool>(
273 idx, g_interpreter_properties[idx].default_uint_value != 0);
274}
275
276bool CommandInterpreter::GetRepeatPreviousCommand() const {
277 const uint32_t idx = ePropertyRepeatPreviousCommand;
278 return GetPropertyAtIndexAs<bool>(
279 idx, g_interpreter_properties[idx].default_uint_value != 0);
280}
281
282bool CommandInterpreter::GetRequireCommandOverwrite() const {
283 const uint32_t idx = ePropertyRequireCommandOverwrite;
284 return GetPropertyAtIndexAs<bool>(
285 idx, g_interpreter_properties[idx].default_uint_value != 0);
286}
287
288void CommandInterpreter::Initialize() {
289 LLDB_SCOPED_TIMER();
290
291 LoadCommandDictionary();
292
293 // An alias arguments vector to reuse - reset it before use...
294 OptionArgVectorSP alias_arguments_vector_sp(new OptionArgVector);
295
296 // Set up some initial aliases.
297 CommandObjectSP cmd_obj_sp = GetCommandSPExact(cmd: "quit");
298 if (cmd_obj_sp) {
299 AddAlias(alias_name: "q", command_obj_sp&: cmd_obj_sp);
300 AddAlias(alias_name: "exit", command_obj_sp&: cmd_obj_sp);
301 }
302
303 cmd_obj_sp = GetCommandSPExact(cmd: "_regexp-attach");
304 if (cmd_obj_sp)
305 AddAlias(alias_name: "attach", command_obj_sp&: cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
306
307 cmd_obj_sp = GetCommandSPExact(cmd: "process detach");
308 if (cmd_obj_sp) {
309 AddAlias(alias_name: "detach", command_obj_sp&: cmd_obj_sp);
310 }
311
312 cmd_obj_sp = GetCommandSPExact(cmd: "process continue");
313 if (cmd_obj_sp) {
314 AddAlias(alias_name: "c", command_obj_sp&: cmd_obj_sp);
315 AddAlias(alias_name: "continue", command_obj_sp&: cmd_obj_sp);
316 }
317
318 cmd_obj_sp = GetCommandSPExact(cmd: "_regexp-break");
319 if (cmd_obj_sp)
320 AddAlias(alias_name: "b", command_obj_sp&: cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
321
322 cmd_obj_sp = GetCommandSPExact(cmd: "_regexp-tbreak");
323 if (cmd_obj_sp)
324 AddAlias(alias_name: "tbreak", command_obj_sp&: cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
325
326 cmd_obj_sp = GetCommandSPExact(cmd: "thread step-inst");
327 if (cmd_obj_sp) {
328 AddAlias(alias_name: "stepi", command_obj_sp&: cmd_obj_sp);
329 AddAlias(alias_name: "si", command_obj_sp&: cmd_obj_sp);
330 }
331
332 cmd_obj_sp = GetCommandSPExact(cmd: "thread step-inst-over");
333 if (cmd_obj_sp) {
334 AddAlias(alias_name: "nexti", command_obj_sp&: cmd_obj_sp);
335 AddAlias(alias_name: "ni", command_obj_sp&: cmd_obj_sp);
336 }
337
338 cmd_obj_sp = GetCommandSPExact(cmd: "thread step-in");
339 if (cmd_obj_sp) {
340 AddAlias(alias_name: "s", command_obj_sp&: cmd_obj_sp);
341 AddAlias(alias_name: "step", command_obj_sp&: cmd_obj_sp);
342 CommandAlias *sif_alias = AddAlias(
343 alias_name: "sif", command_obj_sp&: cmd_obj_sp, args_string: "--end-linenumber block --step-in-target %1");
344 if (sif_alias) {
345 sif_alias->SetHelp("Step through the current block, stopping if you step "
346 "directly into a function whose name matches the "
347 "TargetFunctionName.");
348 sif_alias->SetSyntax("sif <TargetFunctionName>");
349 }
350 }
351
352 cmd_obj_sp = GetCommandSPExact(cmd: "thread step-over");
353 if (cmd_obj_sp) {
354 AddAlias(alias_name: "n", command_obj_sp&: cmd_obj_sp);
355 AddAlias(alias_name: "next", command_obj_sp&: cmd_obj_sp);
356 }
357
358 cmd_obj_sp = GetCommandSPExact(cmd: "thread step-out");
359 if (cmd_obj_sp) {
360 AddAlias(alias_name: "finish", command_obj_sp&: cmd_obj_sp);
361 }
362
363 cmd_obj_sp = GetCommandSPExact(cmd: "frame select");
364 if (cmd_obj_sp) {
365 AddAlias(alias_name: "f", command_obj_sp&: cmd_obj_sp);
366 }
367
368 cmd_obj_sp = GetCommandSPExact(cmd: "thread select");
369 if (cmd_obj_sp) {
370 AddAlias(alias_name: "t", command_obj_sp&: cmd_obj_sp);
371 }
372
373 cmd_obj_sp = GetCommandSPExact(cmd: "_regexp-jump");
374 if (cmd_obj_sp) {
375 AddAlias(alias_name: "j", command_obj_sp&: cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
376 AddAlias(alias_name: "jump", command_obj_sp&: cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
377 }
378
379 cmd_obj_sp = GetCommandSPExact(cmd: "_regexp-list");
380 if (cmd_obj_sp) {
381 AddAlias(alias_name: "l", command_obj_sp&: cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
382 AddAlias(alias_name: "list", command_obj_sp&: cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
383 }
384
385 cmd_obj_sp = GetCommandSPExact(cmd: "_regexp-env");
386 if (cmd_obj_sp)
387 AddAlias(alias_name: "env", command_obj_sp&: cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
388
389 cmd_obj_sp = GetCommandSPExact(cmd: "memory read");
390 if (cmd_obj_sp)
391 AddAlias(alias_name: "x", command_obj_sp&: cmd_obj_sp);
392
393 cmd_obj_sp = GetCommandSPExact(cmd: "_regexp-up");
394 if (cmd_obj_sp)
395 AddAlias(alias_name: "up", command_obj_sp&: cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
396
397 cmd_obj_sp = GetCommandSPExact(cmd: "_regexp-down");
398 if (cmd_obj_sp)
399 AddAlias(alias_name: "down", command_obj_sp&: cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
400
401 cmd_obj_sp = GetCommandSPExact(cmd: "_regexp-display");
402 if (cmd_obj_sp)
403 AddAlias(alias_name: "display", command_obj_sp&: cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
404
405 cmd_obj_sp = GetCommandSPExact(cmd: "disassemble");
406 if (cmd_obj_sp)
407 AddAlias(alias_name: "dis", command_obj_sp&: cmd_obj_sp);
408
409 cmd_obj_sp = GetCommandSPExact(cmd: "disassemble");
410 if (cmd_obj_sp)
411 AddAlias(alias_name: "di", command_obj_sp&: cmd_obj_sp);
412
413 cmd_obj_sp = GetCommandSPExact(cmd: "_regexp-undisplay");
414 if (cmd_obj_sp)
415 AddAlias(alias_name: "undisplay", command_obj_sp&: cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
416
417 cmd_obj_sp = GetCommandSPExact(cmd: "_regexp-bt");
418 if (cmd_obj_sp)
419 AddAlias(alias_name: "bt", command_obj_sp&: cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
420
421 cmd_obj_sp = GetCommandSPExact(cmd: "target create");
422 if (cmd_obj_sp)
423 AddAlias(alias_name: "file", command_obj_sp&: cmd_obj_sp);
424
425 cmd_obj_sp = GetCommandSPExact(cmd: "target modules");
426 if (cmd_obj_sp)
427 AddAlias(alias_name: "image", command_obj_sp&: cmd_obj_sp);
428
429 alias_arguments_vector_sp = std::make_shared<OptionArgVector>();
430
431 cmd_obj_sp = GetCommandSPExact(cmd: "dwim-print");
432 if (cmd_obj_sp) {
433 AddAlias(alias_name: "p", command_obj_sp&: cmd_obj_sp, args_string: "--")->SetHelpLong("");
434 AddAlias(alias_name: "print", command_obj_sp&: cmd_obj_sp, args_string: "--")->SetHelpLong("");
435 if (auto *po = AddAlias(alias_name: "po", command_obj_sp&: cmd_obj_sp, args_string: "-O --")) {
436 po->SetHelp("Evaluate an expression on the current thread. Displays any "
437 "returned value with formatting "
438 "controlled by the type's author.");
439 po->SetHelpLong("");
440 }
441 }
442
443 cmd_obj_sp = GetCommandSPExact(cmd: "expression");
444 if (cmd_obj_sp) {
445 // Ensure `e` runs `expression`.
446 AddAlias(alias_name: "e", command_obj_sp&: cmd_obj_sp);
447 AddAlias(alias_name: "call", command_obj_sp&: cmd_obj_sp, args_string: "--")->SetHelpLong("");
448 CommandAlias *parray_alias =
449 AddAlias(alias_name: "parray", command_obj_sp&: cmd_obj_sp, args_string: "--element-count %1 --");
450 if (parray_alias) {
451 parray_alias->SetHelp(
452 "parray <COUNT> <EXPRESSION> -- lldb will evaluate EXPRESSION "
453 "to get a typed-pointer-to-an-array in memory, and will display "
454 "COUNT elements of that type from the array.");
455 parray_alias->SetHelpLong("");
456 }
457 CommandAlias *poarray_alias = AddAlias(
458 alias_name: "poarray", command_obj_sp&: cmd_obj_sp, args_string: "--object-description --element-count %1 --");
459 if (poarray_alias) {
460 poarray_alias->SetHelp(
461 "poarray <COUNT> <EXPRESSION> -- lldb will "
462 "evaluate EXPRESSION to get the address of an array of COUNT "
463 "objects in memory, and will call po on them.");
464 poarray_alias->SetHelpLong("");
465 }
466 }
467
468 cmd_obj_sp = GetCommandSPExact(cmd: "platform shell");
469 if (cmd_obj_sp) {
470 CommandAlias *shell_alias = AddAlias(alias_name: "shell", command_obj_sp&: cmd_obj_sp, args_string: " --host --");
471 if (shell_alias) {
472 shell_alias->SetHelp("Run a shell command on the host.");
473 shell_alias->SetHelpLong("");
474 shell_alias->SetSyntax("shell <shell-command>");
475 }
476 }
477
478 cmd_obj_sp = GetCommandSPExact(cmd: "process kill");
479 if (cmd_obj_sp) {
480 AddAlias(alias_name: "kill", command_obj_sp&: cmd_obj_sp);
481 }
482
483 cmd_obj_sp = GetCommandSPExact(cmd: "process launch");
484 if (cmd_obj_sp) {
485 alias_arguments_vector_sp = std::make_shared<OptionArgVector>();
486#if defined(__APPLE__)
487#if TARGET_OS_IPHONE
488 AddAlias("r", cmd_obj_sp, "--");
489 AddAlias("run", cmd_obj_sp, "--");
490#else
491 AddAlias("r", cmd_obj_sp, "--shell-expand-args true --");
492 AddAlias("run", cmd_obj_sp, "--shell-expand-args true --");
493#endif
494#else
495 StreamString defaultshell;
496 defaultshell.Printf(format: "--shell=%s --",
497 HostInfo::GetDefaultShell().GetPath().c_str());
498 AddAlias(alias_name: "r", command_obj_sp&: cmd_obj_sp, args_string: defaultshell.GetString());
499 AddAlias(alias_name: "run", command_obj_sp&: cmd_obj_sp, args_string: defaultshell.GetString());
500#endif
501 }
502
503 cmd_obj_sp = GetCommandSPExact(cmd: "target symbols add");
504 if (cmd_obj_sp) {
505 AddAlias(alias_name: "add-dsym", command_obj_sp&: cmd_obj_sp);
506 }
507
508 cmd_obj_sp = GetCommandSPExact(cmd: "breakpoint set");
509 if (cmd_obj_sp) {
510 AddAlias(alias_name: "rbreak", command_obj_sp&: cmd_obj_sp, args_string: "--func-regex %1");
511 }
512
513 cmd_obj_sp = GetCommandSPExact(cmd: "frame variable");
514 if (cmd_obj_sp) {
515 AddAlias(alias_name: "v", command_obj_sp&: cmd_obj_sp);
516 AddAlias(alias_name: "var", command_obj_sp&: cmd_obj_sp);
517 AddAlias(alias_name: "vo", command_obj_sp&: cmd_obj_sp, args_string: "--object-description");
518 }
519
520 cmd_obj_sp = GetCommandSPExact(cmd: "register");
521 if (cmd_obj_sp) {
522 AddAlias(alias_name: "re", command_obj_sp&: cmd_obj_sp);
523 }
524
525 cmd_obj_sp = GetCommandSPExact(cmd: "scripting run");
526 if (cmd_obj_sp) {
527 AddAlias(alias_name: "script", command_obj_sp&: cmd_obj_sp);
528 }
529
530 cmd_obj_sp = GetCommandSPExact(cmd: "session history");
531 if (cmd_obj_sp) {
532 AddAlias(alias_name: "history", command_obj_sp&: cmd_obj_sp);
533 }
534
535 cmd_obj_sp = GetCommandSPExact(cmd: "help");
536 if (cmd_obj_sp) {
537 AddAlias(alias_name: "h", command_obj_sp&: cmd_obj_sp);
538 }
539}
540
541void CommandInterpreter::Clear() { m_command_io_handler_sp.reset(); }
542
543const char *CommandInterpreter::ProcessEmbeddedScriptCommands(const char *arg) {
544 // This function has not yet been implemented.
545
546 // Look for any embedded script command
547 // If found,
548 // get interpreter object from the command dictionary,
549 // call execute_one_command on it,
550 // get the results as a string,
551 // substitute that string for current stuff.
552
553 return arg;
554}
555
556#define REGISTER_COMMAND_OBJECT(NAME, CLASS) \
557 m_command_dict[NAME] = std::make_shared<CLASS>(*this);
558
559void CommandInterpreter::LoadCommandDictionary() {
560 LLDB_SCOPED_TIMER();
561
562 REGISTER_COMMAND_OBJECT("apropos", CommandObjectApropos);
563 REGISTER_COMMAND_OBJECT("breakpoint", CommandObjectMultiwordBreakpoint);
564 REGISTER_COMMAND_OBJECT("command", CommandObjectMultiwordCommands);
565 REGISTER_COMMAND_OBJECT("diagnostics", CommandObjectDiagnostics);
566 REGISTER_COMMAND_OBJECT("disassemble", CommandObjectDisassemble);
567 REGISTER_COMMAND_OBJECT("dwim-print", CommandObjectDWIMPrint);
568 REGISTER_COMMAND_OBJECT("expression", CommandObjectExpression);
569 REGISTER_COMMAND_OBJECT("frame", CommandObjectMultiwordFrame);
570 REGISTER_COMMAND_OBJECT("gui", CommandObjectGUI);
571 REGISTER_COMMAND_OBJECT("help", CommandObjectHelp);
572 REGISTER_COMMAND_OBJECT("log", CommandObjectLog);
573 REGISTER_COMMAND_OBJECT("memory", CommandObjectMemory);
574 REGISTER_COMMAND_OBJECT("platform", CommandObjectPlatform);
575 REGISTER_COMMAND_OBJECT("plugin", CommandObjectPlugin);
576 REGISTER_COMMAND_OBJECT("process", CommandObjectMultiwordProcess);
577 REGISTER_COMMAND_OBJECT("quit", CommandObjectQuit);
578 REGISTER_COMMAND_OBJECT("register", CommandObjectRegister);
579 REGISTER_COMMAND_OBJECT("scripting", CommandObjectMultiwordScripting);
580 REGISTER_COMMAND_OBJECT("settings", CommandObjectMultiwordSettings);
581 REGISTER_COMMAND_OBJECT("session", CommandObjectSession);
582 REGISTER_COMMAND_OBJECT("source", CommandObjectMultiwordSource);
583 REGISTER_COMMAND_OBJECT("statistics", CommandObjectStats);
584 REGISTER_COMMAND_OBJECT("target", CommandObjectMultiwordTarget);
585 REGISTER_COMMAND_OBJECT("thread", CommandObjectMultiwordThread);
586 REGISTER_COMMAND_OBJECT("trace", CommandObjectTrace);
587 REGISTER_COMMAND_OBJECT("type", CommandObjectType);
588 REGISTER_COMMAND_OBJECT("version", CommandObjectVersion);
589 REGISTER_COMMAND_OBJECT("watchpoint", CommandObjectMultiwordWatchpoint);
590 REGISTER_COMMAND_OBJECT("language", CommandObjectLanguage);
591
592 // clang-format off
593 const char *break_regexes[][2] = {
594 {"^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*$",
595 "breakpoint set --file '%1' --line %2 --column %3"},
596 {"^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*$",
597 "breakpoint set --file '%1' --line %2"},
598 {"^/([^/]+)/$", "breakpoint set --source-pattern-regexp '%1'"},
599 {"^([[:digit:]]+)[[:space:]]*$", "breakpoint set --line %1"},
600 {"^\\*?(0x[[:xdigit:]]+)[[:space:]]*$", "breakpoint set --address %1"},
601 {"^[\"']?([-+]?\\[.*\\])[\"']?[[:space:]]*$",
602 "breakpoint set --name '%1'"},
603 {"^(-.*)$", "breakpoint set %1"},
604 {"^(.*[^[:space:]])`(.*[^[:space:]])[[:space:]]*$",
605 "breakpoint set --name '%2' --shlib '%1'"},
606 {"^\\&(.*[^[:space:]])[[:space:]]*$",
607 "breakpoint set --name '%1' --skip-prologue=0"},
608 {"^[\"']?(.*[^[:space:]\"'])[\"']?[[:space:]]*$",
609 "breakpoint set --name '%1'"}};
610 // clang-format on
611
612 size_t num_regexes = std::size(break_regexes);
613
614 std::unique_ptr<CommandObjectRegexCommand> break_regex_cmd_up(
615 new CommandObjectRegexCommand(
616 *this, "_regexp-break",
617 "Set a breakpoint using one of several shorthand formats.",
618 "\n"
619 "_regexp-break <filename>:<linenum>:<colnum>\n"
620 " main.c:12:21 // Break at line 12 and column "
621 "21 of main.c\n\n"
622 "_regexp-break <filename>:<linenum>\n"
623 " main.c:12 // Break at line 12 of "
624 "main.c\n\n"
625 "_regexp-break <linenum>\n"
626 " 12 // Break at line 12 of current "
627 "file\n\n"
628 "_regexp-break 0x<address>\n"
629 " 0x1234000 // Break at address "
630 "0x1234000\n\n"
631 "_regexp-break <name>\n"
632 " main // Break in 'main' after the "
633 "prologue\n\n"
634 "_regexp-break &<name>\n"
635 " &main // Break at first instruction "
636 "in 'main'\n\n"
637 "_regexp-break <module>`<name>\n"
638 " libc.so`malloc // Break in 'malloc' from "
639 "'libc.so'\n\n"
640 "_regexp-break /<source-regex>/\n"
641 " /break here/ // Break on source lines in "
642 "current file\n"
643 " // containing text 'break "
644 "here'.\n",
645 lldb::eSymbolCompletion | lldb::eSourceFileCompletion, false));
646
647 if (break_regex_cmd_up) {
648 bool success = true;
649 for (size_t i = 0; i < num_regexes; i++) {
650 success = break_regex_cmd_up->AddRegexCommand(re_cstr: break_regexes[i][0],
651 command_cstr: break_regexes[i][1]);
652 if (!success)
653 break;
654 }
655 success =
656 break_regex_cmd_up->AddRegexCommand(re_cstr: "^$", command_cstr: "breakpoint list --full");
657
658 if (success) {
659 CommandObjectSP break_regex_cmd_sp(break_regex_cmd_up.release());
660 m_command_dict[std::string(break_regex_cmd_sp->GetCommandName())] =
661 break_regex_cmd_sp;
662 }
663 }
664
665 std::unique_ptr<CommandObjectRegexCommand> tbreak_regex_cmd_up(
666 new CommandObjectRegexCommand(
667 *this, "_regexp-tbreak",
668 "Set a one-shot breakpoint using one of several shorthand formats.",
669 "\n"
670 "_regexp-break <filename>:<linenum>:<colnum>\n"
671 " main.c:12:21 // Break at line 12 and column "
672 "21 of main.c\n\n"
673 "_regexp-break <filename>:<linenum>\n"
674 " main.c:12 // Break at line 12 of "
675 "main.c\n\n"
676 "_regexp-break <linenum>\n"
677 " 12 // Break at line 12 of current "
678 "file\n\n"
679 "_regexp-break 0x<address>\n"
680 " 0x1234000 // Break at address "
681 "0x1234000\n\n"
682 "_regexp-break <name>\n"
683 " main // Break in 'main' after the "
684 "prologue\n\n"
685 "_regexp-break &<name>\n"
686 " &main // Break at first instruction "
687 "in 'main'\n\n"
688 "_regexp-break <module>`<name>\n"
689 " libc.so`malloc // Break in 'malloc' from "
690 "'libc.so'\n\n"
691 "_regexp-break /<source-regex>/\n"
692 " /break here/ // Break on source lines in "
693 "current file\n"
694 " // containing text 'break "
695 "here'.\n",
696 lldb::eSymbolCompletion | lldb::eSourceFileCompletion, false));
697
698 if (tbreak_regex_cmd_up) {
699 bool success = true;
700 for (size_t i = 0; i < num_regexes; i++) {
701 std::string command = break_regexes[i][1];
702 command += " -o 1";
703 success =
704 tbreak_regex_cmd_up->AddRegexCommand(re_cstr: break_regexes[i][0], command_cstr: command);
705 if (!success)
706 break;
707 }
708 success =
709 tbreak_regex_cmd_up->AddRegexCommand(re_cstr: "^$", command_cstr: "breakpoint list --full");
710
711 if (success) {
712 CommandObjectSP tbreak_regex_cmd_sp(tbreak_regex_cmd_up.release());
713 m_command_dict[std::string(tbreak_regex_cmd_sp->GetCommandName())] =
714 tbreak_regex_cmd_sp;
715 }
716 }
717
718 std::unique_ptr<CommandObjectRegexCommand> attach_regex_cmd_up(
719 new CommandObjectRegexCommand(
720 *this, "_regexp-attach", "Attach to process by ID or name.",
721 "_regexp-attach <pid> | <process-name>", 0, false));
722 if (attach_regex_cmd_up) {
723 if (attach_regex_cmd_up->AddRegexCommand(re_cstr: "^([0-9]+)[[:space:]]*$",
724 command_cstr: "process attach --pid %1") &&
725 attach_regex_cmd_up->AddRegexCommand(
726 re_cstr: "^(-.*|.* -.*)$", command_cstr: "process attach %1") && // Any options that are
727 // specified get passed to
728 // 'process attach'
729 attach_regex_cmd_up->AddRegexCommand(re_cstr: "^(.+)$",
730 command_cstr: "process attach --name '%1'") &&
731 attach_regex_cmd_up->AddRegexCommand(re_cstr: "^$", command_cstr: "process attach")) {
732 CommandObjectSP attach_regex_cmd_sp(attach_regex_cmd_up.release());
733 m_command_dict[std::string(attach_regex_cmd_sp->GetCommandName())] =
734 attach_regex_cmd_sp;
735 }
736 }
737
738 std::unique_ptr<CommandObjectRegexCommand> down_regex_cmd_up(
739 new CommandObjectRegexCommand(*this, "_regexp-down",
740 "Select a newer stack frame. Defaults to "
741 "moving one frame, a numeric argument can "
742 "specify an arbitrary number.",
743 "_regexp-down [<count>]", 0, false));
744 if (down_regex_cmd_up) {
745 if (down_regex_cmd_up->AddRegexCommand(re_cstr: "^$", command_cstr: "frame select -r -1") &&
746 down_regex_cmd_up->AddRegexCommand(re_cstr: "^([0-9]+)$",
747 command_cstr: "frame select -r -%1")) {
748 CommandObjectSP down_regex_cmd_sp(down_regex_cmd_up.release());
749 m_command_dict[std::string(down_regex_cmd_sp->GetCommandName())] =
750 down_regex_cmd_sp;
751 }
752 }
753
754 std::unique_ptr<CommandObjectRegexCommand> up_regex_cmd_up(
755 new CommandObjectRegexCommand(
756 *this, "_regexp-up",
757 "Select an older stack frame. Defaults to moving one "
758 "frame, a numeric argument can specify an arbitrary number.",
759 "_regexp-up [<count>]", 0, false));
760 if (up_regex_cmd_up) {
761 if (up_regex_cmd_up->AddRegexCommand(re_cstr: "^$", command_cstr: "frame select -r 1") &&
762 up_regex_cmd_up->AddRegexCommand(re_cstr: "^([0-9]+)$", command_cstr: "frame select -r %1")) {
763 CommandObjectSP up_regex_cmd_sp(up_regex_cmd_up.release());
764 m_command_dict[std::string(up_regex_cmd_sp->GetCommandName())] =
765 up_regex_cmd_sp;
766 }
767 }
768
769 std::unique_ptr<CommandObjectRegexCommand> display_regex_cmd_up(
770 new CommandObjectRegexCommand(
771 *this, "_regexp-display",
772 "Evaluate an expression at every stop (see 'help target stop-hook'.)",
773 "_regexp-display expression", 0, false));
774 if (display_regex_cmd_up) {
775 if (display_regex_cmd_up->AddRegexCommand(
776 re_cstr: "^(.+)$", command_cstr: "target stop-hook add -o \"expr -- %1\"")) {
777 CommandObjectSP display_regex_cmd_sp(display_regex_cmd_up.release());
778 m_command_dict[std::string(display_regex_cmd_sp->GetCommandName())] =
779 display_regex_cmd_sp;
780 }
781 }
782
783 std::unique_ptr<CommandObjectRegexCommand> undisplay_regex_cmd_up(
784 new CommandObjectRegexCommand(*this, "_regexp-undisplay",
785 "Stop displaying expression at every "
786 "stop (specified by stop-hook index.)",
787 "_regexp-undisplay stop-hook-number", 0,
788 false));
789 if (undisplay_regex_cmd_up) {
790 if (undisplay_regex_cmd_up->AddRegexCommand(re_cstr: "^([0-9]+)$",
791 command_cstr: "target stop-hook delete %1")) {
792 CommandObjectSP undisplay_regex_cmd_sp(undisplay_regex_cmd_up.release());
793 m_command_dict[std::string(undisplay_regex_cmd_sp->GetCommandName())] =
794 undisplay_regex_cmd_sp;
795 }
796 }
797
798 std::unique_ptr<CommandObjectRegexCommand> connect_gdb_remote_cmd_up(
799 new CommandObjectRegexCommand(
800 *this, "gdb-remote",
801 "Connect to a process via remote GDB server.\n"
802 "If no host is specified, localhost is assumed.\n"
803 "gdb-remote is an abbreviation for 'process connect --plugin "
804 "gdb-remote connect://<hostname>:<port>'\n",
805 "gdb-remote [<hostname>:]<portnum>", 0, false));
806 if (connect_gdb_remote_cmd_up) {
807 if (connect_gdb_remote_cmd_up->AddRegexCommand(
808 re_cstr: "^([^:]+|\\[[0-9a-fA-F:]+.*\\]):([0-9]+)$",
809 command_cstr: "process connect --plugin gdb-remote connect://%1:%2") &&
810 connect_gdb_remote_cmd_up->AddRegexCommand(
811 re_cstr: "^([[:digit:]]+)$",
812 command_cstr: "process connect --plugin gdb-remote connect://localhost:%1")) {
813 CommandObjectSP command_sp(connect_gdb_remote_cmd_up.release());
814 m_command_dict[std::string(command_sp->GetCommandName())] = command_sp;
815 }
816 }
817
818 std::unique_ptr<CommandObjectRegexCommand> connect_kdp_remote_cmd_up(
819 new CommandObjectRegexCommand(
820 *this, "kdp-remote",
821 "Connect to a process via remote KDP server.\n"
822 "If no UDP port is specified, port 41139 is assumed.\n"
823 "kdp-remote is an abbreviation for 'process connect --plugin "
824 "kdp-remote udp://<hostname>:<port>'\n",
825 "kdp-remote <hostname>[:<portnum>]", 0, false));
826 if (connect_kdp_remote_cmd_up) {
827 if (connect_kdp_remote_cmd_up->AddRegexCommand(
828 re_cstr: "^([^:]+:[[:digit:]]+)$",
829 command_cstr: "process connect --plugin kdp-remote udp://%1") &&
830 connect_kdp_remote_cmd_up->AddRegexCommand(
831 re_cstr: "^(.+)$", command_cstr: "process connect --plugin kdp-remote udp://%1:41139")) {
832 CommandObjectSP command_sp(connect_kdp_remote_cmd_up.release());
833 m_command_dict[std::string(command_sp->GetCommandName())] = command_sp;
834 }
835 }
836
837 std::unique_ptr<CommandObjectRegexCommand> bt_regex_cmd_up(
838 new CommandObjectRegexCommand(
839 *this, "_regexp-bt",
840 "Show backtrace of the current thread's call stack. Any numeric "
841 "argument displays at most that many frames. The argument 'all' "
842 "displays all threads. Use 'settings set frame-format' to customize "
843 "the printing of individual frames and 'settings set thread-format' "
844 "to customize the thread header. Frame recognizers may filter the "
845 "list. Use 'thread backtrace -u (--unfiltered)' to see them all.",
846 "bt [<digit> | all]", 0, false));
847 if (bt_regex_cmd_up) {
848 // accept but don't document "bt -c <number>" -- before bt was a regex
849 // command if you wanted to backtrace three frames you would do "bt -c 3"
850 // but the intention is to have this emulate the gdb "bt" command and so
851 // now "bt 3" is the preferred form, in line with gdb.
852 if (bt_regex_cmd_up->AddRegexCommand(re_cstr: "^([[:digit:]]+)[[:space:]]*$",
853 command_cstr: "thread backtrace -c %1") &&
854 bt_regex_cmd_up->AddRegexCommand(re_cstr: "^(-[^[:space:]].*)$",
855 command_cstr: "thread backtrace %1") &&
856 bt_regex_cmd_up->AddRegexCommand(re_cstr: "^all[[:space:]]*$",
857 command_cstr: "thread backtrace all") &&
858 bt_regex_cmd_up->AddRegexCommand(re_cstr: "^[[:space:]]*$",
859 command_cstr: "thread backtrace")) {
860 CommandObjectSP command_sp(bt_regex_cmd_up.release());
861 m_command_dict[std::string(command_sp->GetCommandName())] = command_sp;
862 }
863 }
864
865 std::unique_ptr<CommandObjectRegexCommand> list_regex_cmd_up(
866 new CommandObjectRegexCommand(
867 *this, "_regexp-list",
868 "List relevant source code using one of several shorthand formats.",
869 "\n"
870 "_regexp-list <file>:<line> // List around specific file/line\n"
871 "_regexp-list <line> // List current file around specified "
872 "line\n"
873 "_regexp-list <function-name> // List specified function\n"
874 "_regexp-list 0x<address> // List around specified address\n"
875 "_regexp-list -[<count>] // List previous <count> lines\n"
876 "_regexp-list // List subsequent lines",
877 lldb::eSourceFileCompletion, false));
878 if (list_regex_cmd_up) {
879 if (list_regex_cmd_up->AddRegexCommand(re_cstr: "^([0-9]+)[[:space:]]*$",
880 command_cstr: "source list --line %1") &&
881 list_regex_cmd_up->AddRegexCommand(
882 re_cstr: "^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]"
883 "]*$",
884 command_cstr: "source list --file '%1' --line %2") &&
885 list_regex_cmd_up->AddRegexCommand(
886 re_cstr: "^\\*?(0x[[:xdigit:]]+)[[:space:]]*$",
887 command_cstr: "source list --address %1") &&
888 list_regex_cmd_up->AddRegexCommand(re_cstr: "^-[[:space:]]*$",
889 command_cstr: "source list --reverse") &&
890 list_regex_cmd_up->AddRegexCommand(
891 re_cstr: "^-([[:digit:]]+)[[:space:]]*$",
892 command_cstr: "source list --reverse --count %1") &&
893 list_regex_cmd_up->AddRegexCommand(re_cstr: "^(.+)$",
894 command_cstr: "source list --name \"%1\"") &&
895 list_regex_cmd_up->AddRegexCommand(re_cstr: "^$", command_cstr: "source list")) {
896 CommandObjectSP list_regex_cmd_sp(list_regex_cmd_up.release());
897 m_command_dict[std::string(list_regex_cmd_sp->GetCommandName())] =
898 list_regex_cmd_sp;
899 }
900 }
901
902 std::unique_ptr<CommandObjectRegexCommand> env_regex_cmd_up(
903 new CommandObjectRegexCommand(
904 *this, "_regexp-env",
905 "Shorthand for viewing and setting environment variables.",
906 "\n"
907 "_regexp-env // Show environment\n"
908 "_regexp-env <name>=<value> // Set an environment variable",
909 0, false));
910 if (env_regex_cmd_up) {
911 if (env_regex_cmd_up->AddRegexCommand(re_cstr: "^$",
912 command_cstr: "settings show target.env-vars") &&
913 env_regex_cmd_up->AddRegexCommand(re_cstr: "^([A-Za-z_][A-Za-z_0-9]*=.*)$",
914 command_cstr: "settings set target.env-vars %1")) {
915 CommandObjectSP env_regex_cmd_sp(env_regex_cmd_up.release());
916 m_command_dict[std::string(env_regex_cmd_sp->GetCommandName())] =
917 env_regex_cmd_sp;
918 }
919 }
920
921 std::unique_ptr<CommandObjectRegexCommand> jump_regex_cmd_up(
922 new CommandObjectRegexCommand(
923 *this, "_regexp-jump", "Set the program counter to a new address.",
924 "\n"
925 "_regexp-jump <line>\n"
926 "_regexp-jump +<line-offset> | -<line-offset>\n"
927 "_regexp-jump <file>:<line>\n"
928 "_regexp-jump *<addr>\n",
929 0, false));
930 if (jump_regex_cmd_up) {
931 if (jump_regex_cmd_up->AddRegexCommand(re_cstr: "^\\*(.*)$",
932 command_cstr: "thread jump --addr %1") &&
933 jump_regex_cmd_up->AddRegexCommand(re_cstr: "^([0-9]+)$",
934 command_cstr: "thread jump --line %1") &&
935 jump_regex_cmd_up->AddRegexCommand(re_cstr: "^([^:]+):([0-9]+)$",
936 command_cstr: "thread jump --file %1 --line %2") &&
937 jump_regex_cmd_up->AddRegexCommand(re_cstr: "^([+\\-][0-9]+)$",
938 command_cstr: "thread jump --by %1")) {
939 CommandObjectSP jump_regex_cmd_sp(jump_regex_cmd_up.release());
940 m_command_dict[std::string(jump_regex_cmd_sp->GetCommandName())] =
941 jump_regex_cmd_sp;
942 }
943 }
944}
945
946int CommandInterpreter::GetCommandNamesMatchingPartialString(
947 const char *cmd_str, bool include_aliases, StringList &matches,
948 StringList &descriptions) {
949 AddNamesMatchingPartialString(in_map: m_command_dict, cmd_str, matches,
950 descriptions: &descriptions);
951
952 if (include_aliases) {
953 AddNamesMatchingPartialString(in_map: m_alias_dict, cmd_str, matches,
954 descriptions: &descriptions);
955 }
956
957 return matches.GetSize();
958}
959
960CommandObjectMultiword *
961CommandInterpreter::VerifyUserMultiwordCmdPath(Args &path, bool leaf_is_command,
962 Status &result) {
963 result.Clear();
964
965 auto get_multi_or_report_error =
966 [&result](CommandObjectSP cmd_sp,
967 const char *name) -> CommandObjectMultiword * {
968 if (!cmd_sp) {
969 result = Status::FromErrorStringWithFormat(
970 format: "Path component: '%s' not found", name);
971 return nullptr;
972 }
973 if (!cmd_sp->IsUserCommand()) {
974 result = Status::FromErrorStringWithFormat(
975 format: "Path component: '%s' is not a user "
976 "command",
977 name);
978 return nullptr;
979 }
980 CommandObjectMultiword *cmd_as_multi = cmd_sp->GetAsMultiwordCommand();
981 if (!cmd_as_multi) {
982 result = Status::FromErrorStringWithFormat(
983 format: "Path component: '%s' is not a container "
984 "command",
985 name);
986 return nullptr;
987 }
988 return cmd_as_multi;
989 };
990
991 size_t num_args = path.GetArgumentCount();
992 if (num_args == 0) {
993 result = Status::FromErrorString(str: "empty command path");
994 return nullptr;
995 }
996
997 if (num_args == 1 && leaf_is_command) {
998 // We just got a leaf command to be added to the root. That's not an error,
999 // just return null for the container.
1000 return nullptr;
1001 }
1002
1003 // Start by getting the root command from the interpreter.
1004 const char *cur_name = path.GetArgumentAtIndex(idx: 0);
1005 CommandObjectSP cur_cmd_sp = GetCommandSPExact(cmd: cur_name);
1006 CommandObjectMultiword *cur_as_multi =
1007 get_multi_or_report_error(cur_cmd_sp, cur_name);
1008 if (cur_as_multi == nullptr)
1009 return nullptr;
1010
1011 size_t num_path_elements = num_args - (leaf_is_command ? 1 : 0);
1012 for (size_t cursor = 1; cursor < num_path_elements && cur_as_multi != nullptr;
1013 cursor++) {
1014 cur_name = path.GetArgumentAtIndex(idx: cursor);
1015 cur_cmd_sp = cur_as_multi->GetSubcommandSPExact(sub_cmd: cur_name);
1016 cur_as_multi = get_multi_or_report_error(cur_cmd_sp, cur_name);
1017 }
1018 return cur_as_multi;
1019}
1020
1021CommandObjectSP CommandInterpreter::GetFrameLanguageCommand() const {
1022 auto frame_sp = GetExecutionContext().GetFrameSP();
1023 if (!frame_sp)
1024 return {};
1025 auto frame_language =
1026 Language::GetPrimaryLanguage(language: frame_sp->GuessLanguage().AsLanguageType());
1027
1028 auto it = m_command_dict.find(x: "language");
1029 if (it == m_command_dict.end())
1030 return {};
1031 // The root "language" command.
1032 CommandObjectSP language_cmd_sp = it->second;
1033
1034 auto *plugin = Language::FindPlugin(language: frame_language);
1035 if (!plugin)
1036 return {};
1037 // "cplusplus", "objc", etc.
1038 auto lang_name = plugin->GetPluginName();
1039
1040 return language_cmd_sp->GetSubcommandSPExact(sub_cmd: lang_name);
1041}
1042
1043CommandObjectSP
1044CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, bool include_aliases,
1045 bool exact, StringList *matches,
1046 StringList *descriptions) const {
1047 CommandObjectSP command_sp;
1048
1049 std::string cmd = std::string(cmd_str);
1050
1051 if (HasCommands()) {
1052 auto pos = m_command_dict.find(x: cmd);
1053 if (pos != m_command_dict.end())
1054 command_sp = pos->second;
1055 }
1056
1057 if (include_aliases && HasAliases()) {
1058 auto alias_pos = m_alias_dict.find(x: cmd);
1059 if (alias_pos != m_alias_dict.end())
1060 command_sp = alias_pos->second;
1061 }
1062
1063 if (HasUserCommands()) {
1064 auto pos = m_user_dict.find(x: cmd);
1065 if (pos != m_user_dict.end())
1066 command_sp = pos->second;
1067 }
1068
1069 if (HasUserMultiwordCommands()) {
1070 auto pos = m_user_mw_dict.find(x: cmd);
1071 if (pos != m_user_mw_dict.end())
1072 command_sp = pos->second;
1073 }
1074
1075 if (!exact && !command_sp) {
1076 // We will only get into here if we didn't find any exact matches.
1077
1078 CommandObjectSP user_match_sp, user_mw_match_sp, alias_match_sp,
1079 real_match_sp;
1080
1081 StringList local_matches;
1082 if (matches == nullptr)
1083 matches = &local_matches;
1084
1085 unsigned int num_cmd_matches = 0;
1086 unsigned int num_alias_matches = 0;
1087 unsigned int num_user_matches = 0;
1088 unsigned int num_user_mw_matches = 0;
1089
1090 // Look through the command dictionaries one by one, and if we get only one
1091 // match from any of them in toto, then return that, otherwise return an
1092 // empty CommandObjectSP and the list of matches.
1093
1094 if (HasCommands()) {
1095 num_cmd_matches = AddNamesMatchingPartialString(in_map: m_command_dict, cmd_str,
1096 matches&: *matches, descriptions);
1097 }
1098
1099 if (num_cmd_matches == 1) {
1100 cmd.assign(s: matches->GetStringAtIndex(idx: 0));
1101 auto pos = m_command_dict.find(x: cmd);
1102 if (pos != m_command_dict.end())
1103 real_match_sp = pos->second;
1104 }
1105
1106 if (include_aliases && HasAliases()) {
1107 num_alias_matches = AddNamesMatchingPartialString(in_map: m_alias_dict, cmd_str,
1108 matches&: *matches, descriptions);
1109 }
1110
1111 if (num_alias_matches == 1) {
1112 cmd.assign(s: matches->GetStringAtIndex(idx: num_cmd_matches));
1113 auto alias_pos = m_alias_dict.find(x: cmd);
1114 if (alias_pos != m_alias_dict.end())
1115 alias_match_sp = alias_pos->second;
1116 }
1117
1118 if (HasUserCommands()) {
1119 num_user_matches = AddNamesMatchingPartialString(in_map: m_user_dict, cmd_str,
1120 matches&: *matches, descriptions);
1121 }
1122
1123 if (num_user_matches == 1) {
1124 cmd.assign(
1125 s: matches->GetStringAtIndex(idx: num_cmd_matches + num_alias_matches));
1126
1127 auto pos = m_user_dict.find(x: cmd);
1128 if (pos != m_user_dict.end())
1129 user_match_sp = pos->second;
1130 }
1131
1132 if (HasUserMultiwordCommands()) {
1133 num_user_mw_matches = AddNamesMatchingPartialString(
1134 in_map: m_user_mw_dict, cmd_str, matches&: *matches, descriptions);
1135 }
1136
1137 if (num_user_mw_matches == 1) {
1138 cmd.assign(s: matches->GetStringAtIndex(idx: num_cmd_matches + num_alias_matches +
1139 num_user_matches));
1140
1141 auto pos = m_user_mw_dict.find(x: cmd);
1142 if (pos != m_user_mw_dict.end())
1143 user_mw_match_sp = pos->second;
1144 }
1145
1146 // If we got exactly one match, return that, otherwise return the match
1147 // list.
1148
1149 if (num_user_matches + num_user_mw_matches + num_cmd_matches +
1150 num_alias_matches ==
1151 1) {
1152 if (num_cmd_matches)
1153 return real_match_sp;
1154 else if (num_alias_matches)
1155 return alias_match_sp;
1156 else if (num_user_mw_matches)
1157 return user_mw_match_sp;
1158 else
1159 return user_match_sp;
1160 }
1161 }
1162
1163 // When no single match is found, attempt to resolve the command as a language
1164 // plugin subcommand.
1165 if (!command_sp) {
1166 // The `language` subcommand ("language objc", "language cplusplus", etc).
1167 CommandObjectMultiword *lang_subcmd = nullptr;
1168 if (auto lang_subcmd_sp = GetFrameLanguageCommand()) {
1169 lang_subcmd = lang_subcmd_sp->GetAsMultiwordCommand();
1170 command_sp = lang_subcmd_sp->GetSubcommandSPExact(sub_cmd: cmd_str);
1171 }
1172
1173 if (!command_sp && !exact && lang_subcmd) {
1174 StringList lang_matches;
1175 AddNamesMatchingPartialString(in_map: lang_subcmd->GetSubcommandDictionary(),
1176 cmd_str, matches&: lang_matches, descriptions);
1177 if (matches)
1178 matches->AppendList(strings: lang_matches);
1179 if (lang_matches.GetSize() == 1) {
1180 const auto &lang_dict = lang_subcmd->GetSubcommandDictionary();
1181 auto pos = lang_dict.find(x: lang_matches[0]);
1182 if (pos != lang_dict.end())
1183 return pos->second;
1184 }
1185 }
1186 }
1187
1188 if (matches && command_sp) {
1189 matches->AppendString(str: cmd_str);
1190 if (descriptions)
1191 descriptions->AppendString(str: command_sp->GetHelp());
1192 }
1193
1194 return command_sp;
1195}
1196
1197bool CommandInterpreter::AddCommand(llvm::StringRef name,
1198 const lldb::CommandObjectSP &cmd_sp,
1199 bool can_replace) {
1200 if (cmd_sp.get())
1201 lldbassert((this == &cmd_sp->GetCommandInterpreter()) &&
1202 "tried to add a CommandObject from a different interpreter");
1203
1204 if (name.empty())
1205 return false;
1206
1207 cmd_sp->SetIsUserCommand(false);
1208
1209 std::string name_sstr(name);
1210 auto name_iter = m_command_dict.find(x: name_sstr);
1211 if (name_iter != m_command_dict.end()) {
1212 if (!can_replace || !name_iter->second->IsRemovable())
1213 return false;
1214 name_iter->second = cmd_sp;
1215 } else {
1216 m_command_dict[name_sstr] = cmd_sp;
1217 }
1218 return true;
1219}
1220
1221Status CommandInterpreter::AddUserCommand(llvm::StringRef name,
1222 const lldb::CommandObjectSP &cmd_sp,
1223 bool can_replace) {
1224 Status result;
1225 if (cmd_sp.get())
1226 lldbassert((this == &cmd_sp->GetCommandInterpreter()) &&
1227 "tried to add a CommandObject from a different interpreter");
1228 if (name.empty()) {
1229 result = Status::FromErrorString(
1230 str: "can't use the empty string for a command name");
1231 return result;
1232 }
1233 // do not allow replacement of internal commands
1234 if (CommandExists(cmd: name)) {
1235 result = Status::FromErrorString(str: "can't replace builtin command");
1236 return result;
1237 }
1238
1239 if (UserCommandExists(cmd: name)) {
1240 if (!can_replace) {
1241 result = Status::FromErrorStringWithFormatv(
1242 format: "user command \"{0}\" already exists and force replace was not set "
1243 "by --overwrite or 'settings set interpreter.require-overwrite "
1244 "false'",
1245 args&: name);
1246 return result;
1247 }
1248 if (cmd_sp->IsMultiwordObject()) {
1249 if (!m_user_mw_dict[std::string(name)]->IsRemovable()) {
1250 result = Status::FromErrorString(
1251 str: "can't replace explicitly non-removable multi-word command");
1252 return result;
1253 }
1254 } else {
1255 if (!m_user_dict[std::string(name)]->IsRemovable()) {
1256 result = Status::FromErrorString(
1257 str: "can't replace explicitly non-removable command");
1258 return result;
1259 }
1260 }
1261 }
1262
1263 cmd_sp->SetIsUserCommand(true);
1264
1265 if (cmd_sp->IsMultiwordObject())
1266 m_user_mw_dict[std::string(name)] = cmd_sp;
1267 else
1268 m_user_dict[std::string(name)] = cmd_sp;
1269 return result;
1270}
1271
1272CommandObjectSP
1273CommandInterpreter::GetCommandSPExact(llvm::StringRef cmd_str,
1274 bool include_aliases) const {
1275 // Break up the command string into words, in case it's a multi-word command.
1276 Args cmd_words(cmd_str);
1277
1278 if (cmd_str.empty())
1279 return {};
1280
1281 if (cmd_words.GetArgumentCount() == 1)
1282 return GetCommandSP(cmd_str, include_aliases, exact: true);
1283
1284 // We have a multi-word command (seemingly), so we need to do more work.
1285 // First, get the cmd_obj_sp for the first word in the command.
1286 CommandObjectSP cmd_obj_sp =
1287 GetCommandSP(cmd_str: cmd_words.GetArgumentAtIndex(idx: 0), include_aliases, exact: true);
1288 if (!cmd_obj_sp)
1289 return {};
1290
1291 // Loop through the rest of the words in the command (everything passed in
1292 // was supposed to be part of a command name), and find the appropriate
1293 // sub-command SP for each command word....
1294 size_t end = cmd_words.GetArgumentCount();
1295 for (size_t i = 1; i < end; ++i) {
1296 if (!cmd_obj_sp->IsMultiwordObject()) {
1297 // We have more words in the command name, but we don't have a
1298 // multiword object. Fail and return.
1299 return {};
1300 }
1301
1302 cmd_obj_sp = cmd_obj_sp->GetSubcommandSP(sub_cmd: cmd_words.GetArgumentAtIndex(idx: i));
1303 if (!cmd_obj_sp) {
1304 // The sub-command name was invalid. Fail and return.
1305 return {};
1306 }
1307 }
1308
1309 // We successfully looped through all the command words and got valid
1310 // command objects for them.
1311 return cmd_obj_sp;
1312}
1313
1314CommandObject *
1315CommandInterpreter::GetCommandObject(llvm::StringRef cmd_str,
1316 StringList *matches,
1317 StringList *descriptions) const {
1318 // Try to find a match among commands and aliases. Allowing inexact matches,
1319 // but perferring exact matches.
1320 return GetCommandSP(cmd_str, /*include_aliases=*/true, /*exact=*/false,
1321 matches, descriptions)
1322 .get();
1323}
1324
1325CommandObject *CommandInterpreter::GetUserCommandObject(
1326 llvm::StringRef cmd, StringList *matches, StringList *descriptions) const {
1327 std::string cmd_str(cmd);
1328 auto find_exact = [&](const CommandObject::CommandMap &map) {
1329 auto found_elem = map.find(x: cmd);
1330 if (found_elem == map.end())
1331 return (CommandObject *)nullptr;
1332 CommandObject *exact_cmd = found_elem->second.get();
1333 if (exact_cmd) {
1334 if (matches)
1335 matches->AppendString(str: exact_cmd->GetCommandName());
1336 if (descriptions)
1337 descriptions->AppendString(str: exact_cmd->GetHelp());
1338 return exact_cmd;
1339 }
1340 return (CommandObject *)nullptr;
1341 };
1342
1343 CommandObject *exact_cmd = find_exact(GetUserCommands());
1344 if (exact_cmd)
1345 return exact_cmd;
1346
1347 exact_cmd = find_exact(GetUserMultiwordCommands());
1348 if (exact_cmd)
1349 return exact_cmd;
1350
1351 // We didn't have an exact command, so now look for partial matches.
1352 StringList tmp_list;
1353 StringList *matches_ptr = matches ? matches : &tmp_list;
1354 AddNamesMatchingPartialString(in_map: GetUserCommands(), cmd_str, matches&: *matches_ptr);
1355 AddNamesMatchingPartialString(in_map: GetUserMultiwordCommands(), cmd_str,
1356 matches&: *matches_ptr);
1357
1358 return {};
1359}
1360
1361CommandObject *CommandInterpreter::GetAliasCommandObject(
1362 llvm::StringRef cmd, StringList *matches, StringList *descriptions) const {
1363 auto find_exact =
1364 [&](const CommandObject::CommandMap &map) -> CommandObject * {
1365 auto found_elem = map.find(x: cmd);
1366 if (found_elem == map.end())
1367 return (CommandObject *)nullptr;
1368 CommandObject *exact_cmd = found_elem->second.get();
1369 if (!exact_cmd)
1370 return nullptr;
1371
1372 if (matches)
1373 matches->AppendString(str: exact_cmd->GetCommandName());
1374
1375 if (descriptions)
1376 descriptions->AppendString(str: exact_cmd->GetHelp());
1377
1378 return exact_cmd;
1379 return nullptr;
1380 };
1381
1382 CommandObject *exact_cmd = find_exact(GetAliases());
1383 if (exact_cmd)
1384 return exact_cmd;
1385
1386 // We didn't have an exact command, so now look for partial matches.
1387 StringList tmp_list;
1388 StringList *matches_ptr = matches ? matches : &tmp_list;
1389 AddNamesMatchingPartialString(in_map: GetAliases(), cmd_str: cmd, matches&: *matches_ptr);
1390
1391 return {};
1392}
1393
1394bool CommandInterpreter::CommandExists(llvm::StringRef cmd) const {
1395 return m_command_dict.find(x: cmd) != m_command_dict.end();
1396}
1397
1398bool CommandInterpreter::GetAliasFullName(llvm::StringRef cmd,
1399 std::string &full_name) const {
1400 bool exact_match = (m_alias_dict.find(x: cmd) != m_alias_dict.end());
1401 if (exact_match) {
1402 full_name.assign(str: std::string(cmd));
1403 return exact_match;
1404 } else {
1405 StringList matches;
1406 size_t num_alias_matches;
1407 num_alias_matches =
1408 AddNamesMatchingPartialString(in_map: m_alias_dict, cmd_str: cmd, matches);
1409 if (num_alias_matches == 1) {
1410 // Make sure this isn't shadowing a command in the regular command space:
1411 StringList regular_matches;
1412 const bool include_aliases = false;
1413 const bool exact = false;
1414 CommandObjectSP cmd_obj_sp(
1415 GetCommandSP(cmd_str: cmd, include_aliases, exact, matches: &regular_matches));
1416 if (cmd_obj_sp || regular_matches.GetSize() > 0)
1417 return false;
1418 else {
1419 full_name.assign(s: matches.GetStringAtIndex(idx: 0));
1420 return true;
1421 }
1422 } else
1423 return false;
1424 }
1425}
1426
1427bool CommandInterpreter::AliasExists(llvm::StringRef cmd) const {
1428 return m_alias_dict.find(x: cmd) != m_alias_dict.end();
1429}
1430
1431bool CommandInterpreter::UserCommandExists(llvm::StringRef cmd) const {
1432 return m_user_dict.find(x: cmd) != m_user_dict.end();
1433}
1434
1435bool CommandInterpreter::UserMultiwordCommandExists(llvm::StringRef cmd) const {
1436 return m_user_mw_dict.find(x: cmd) != m_user_mw_dict.end();
1437}
1438
1439CommandAlias *
1440CommandInterpreter::AddAlias(llvm::StringRef alias_name,
1441 lldb::CommandObjectSP &command_obj_sp,
1442 llvm::StringRef args_string) {
1443 if (command_obj_sp.get())
1444 lldbassert((this == &command_obj_sp->GetCommandInterpreter()) &&
1445 "tried to add a CommandObject from a different interpreter");
1446
1447 std::unique_ptr<CommandAlias> command_alias_up(
1448 new CommandAlias(*this, command_obj_sp, args_string, alias_name));
1449
1450 if (command_alias_up && command_alias_up->IsValid()) {
1451 m_alias_dict[std::string(alias_name)] =
1452 CommandObjectSP(command_alias_up.get());
1453 return command_alias_up.release();
1454 }
1455
1456 return nullptr;
1457}
1458
1459bool CommandInterpreter::RemoveAlias(llvm::StringRef alias_name) {
1460 auto pos = m_alias_dict.find(x: alias_name);
1461 if (pos != m_alias_dict.end()) {
1462 m_alias_dict.erase(position: pos);
1463 return true;
1464 }
1465 return false;
1466}
1467
1468bool CommandInterpreter::RemoveCommand(llvm::StringRef cmd, bool force) {
1469 auto pos = m_command_dict.find(x: cmd);
1470 if (pos != m_command_dict.end()) {
1471 if (force || pos->second->IsRemovable()) {
1472 // Only regular expression objects or python commands are removable under
1473 // normal circumstances.
1474 m_command_dict.erase(position: pos);
1475 return true;
1476 }
1477 }
1478 return false;
1479}
1480
1481bool CommandInterpreter::RemoveUser(llvm::StringRef user_name) {
1482 CommandObject::CommandMap::iterator pos = m_user_dict.find(x: user_name);
1483 if (pos != m_user_dict.end()) {
1484 m_user_dict.erase(position: pos);
1485 return true;
1486 }
1487 return false;
1488}
1489
1490bool CommandInterpreter::RemoveUserMultiword(llvm::StringRef multi_name) {
1491 CommandObject::CommandMap::iterator pos = m_user_mw_dict.find(x: multi_name);
1492 if (pos != m_user_mw_dict.end()) {
1493 m_user_mw_dict.erase(position: pos);
1494 return true;
1495 }
1496 return false;
1497}
1498
1499void CommandInterpreter::GetHelp(CommandReturnObject &result,
1500 uint32_t cmd_types) {
1501 llvm::StringRef help_prologue(GetDebugger().GetIOHandlerHelpPrologue());
1502 if (!help_prologue.empty()) {
1503 OutputFormattedHelpText(strm&: result.GetOutputStream(), prefix: llvm::StringRef(),
1504 help_text: help_prologue);
1505 }
1506
1507 CommandObject::CommandMap::const_iterator pos;
1508 size_t max_len = FindLongestCommandWord(dict&: m_command_dict);
1509
1510 if ((cmd_types & eCommandTypesBuiltin) == eCommandTypesBuiltin) {
1511 result.AppendMessage(in_string: "Debugger commands:");
1512 result.AppendMessage(in_string: "");
1513
1514 for (pos = m_command_dict.begin(); pos != m_command_dict.end(); ++pos) {
1515 if (!(cmd_types & eCommandTypesHidden) &&
1516 (pos->first.compare(pos: 0, n1: 1, s: "_") == 0))
1517 continue;
1518
1519 OutputFormattedHelpText(stream&: result.GetOutputStream(), command_word: pos->first, separator: "--",
1520 help_text: pos->second->GetHelp(), max_word_len: max_len);
1521 }
1522 result.AppendMessage(in_string: "");
1523 }
1524
1525 if (!m_alias_dict.empty() &&
1526 ((cmd_types & eCommandTypesAliases) == eCommandTypesAliases)) {
1527 result.AppendMessageWithFormat(
1528 format: "Current command abbreviations "
1529 "(type '%shelp command alias' for more info):\n",
1530 GetCommandPrefix());
1531 result.AppendMessage(in_string: "");
1532 max_len = FindLongestCommandWord(dict&: m_alias_dict);
1533
1534 for (auto alias_pos = m_alias_dict.begin(); alias_pos != m_alias_dict.end();
1535 ++alias_pos) {
1536 OutputFormattedHelpText(stream&: result.GetOutputStream(), command_word: alias_pos->first, separator: "--",
1537 help_text: alias_pos->second->GetHelp(), max_word_len: max_len);
1538 }
1539 result.AppendMessage(in_string: "");
1540 }
1541
1542 if (!m_user_dict.empty() &&
1543 ((cmd_types & eCommandTypesUserDef) == eCommandTypesUserDef)) {
1544 result.AppendMessage(in_string: "Current user-defined commands:");
1545 result.AppendMessage(in_string: "");
1546 max_len = FindLongestCommandWord(dict&: m_user_dict);
1547 for (pos = m_user_dict.begin(); pos != m_user_dict.end(); ++pos) {
1548 OutputFormattedHelpText(stream&: result.GetOutputStream(), command_word: pos->first, separator: "--",
1549 help_text: pos->second->GetHelp(), max_word_len: max_len);
1550 }
1551 result.AppendMessage(in_string: "");
1552 }
1553
1554 if (!m_user_mw_dict.empty() &&
1555 ((cmd_types & eCommandTypesUserMW) == eCommandTypesUserMW)) {
1556 result.AppendMessage(in_string: "Current user-defined container commands:");
1557 result.AppendMessage(in_string: "");
1558 max_len = FindLongestCommandWord(dict&: m_user_mw_dict);
1559 for (pos = m_user_mw_dict.begin(); pos != m_user_mw_dict.end(); ++pos) {
1560 OutputFormattedHelpText(stream&: result.GetOutputStream(), command_word: pos->first, separator: "--",
1561 help_text: pos->second->GetHelp(), max_word_len: max_len);
1562 }
1563 result.AppendMessage(in_string: "");
1564 }
1565
1566 result.AppendMessageWithFormat(
1567 format: "For more information on any command, type '%shelp <command-name>'.\n",
1568 GetCommandPrefix());
1569}
1570
1571CommandObject *CommandInterpreter::GetCommandObjectForCommand(
1572 llvm::StringRef &command_string) {
1573 // This function finds the final, lowest-level, alias-resolved command object
1574 // whose 'Execute' function will eventually be invoked by the given command
1575 // line.
1576
1577 CommandObject *cmd_obj = nullptr;
1578 size_t start = command_string.find_first_not_of(Chars: k_white_space);
1579 size_t end = 0;
1580 bool done = false;
1581 while (!done) {
1582 if (start != std::string::npos) {
1583 // Get the next word from command_string.
1584 end = command_string.find_first_of(Chars: k_white_space, From: start);
1585 if (end == std::string::npos)
1586 end = command_string.size();
1587 std::string cmd_word =
1588 std::string(command_string.substr(Start: start, N: end - start));
1589
1590 if (cmd_obj == nullptr)
1591 // Since cmd_obj is NULL we are on our first time through this loop.
1592 // Check to see if cmd_word is a valid command or alias.
1593 cmd_obj = GetCommandObject(cmd_str: cmd_word);
1594 else if (cmd_obj->IsMultiwordObject()) {
1595 // Our current object is a multi-word object; see if the cmd_word is a
1596 // valid sub-command for our object.
1597 CommandObject *sub_cmd_obj =
1598 cmd_obj->GetSubcommandObject(sub_cmd: cmd_word.c_str());
1599 if (sub_cmd_obj)
1600 cmd_obj = sub_cmd_obj;
1601 else // cmd_word was not a valid sub-command word, so we are done
1602 done = true;
1603 } else
1604 // We have a cmd_obj and it is not a multi-word object, so we are done.
1605 done = true;
1606
1607 // If we didn't find a valid command object, or our command object is not
1608 // a multi-word object, or we are at the end of the command_string, then
1609 // we are done. Otherwise, find the start of the next word.
1610
1611 if (!cmd_obj || !cmd_obj->IsMultiwordObject() ||
1612 end >= command_string.size())
1613 done = true;
1614 else
1615 start = command_string.find_first_not_of(Chars: k_white_space, From: end);
1616 } else
1617 // Unable to find any more words.
1618 done = true;
1619 }
1620
1621 command_string = command_string.substr(Start: end);
1622 return cmd_obj;
1623}
1624
1625static const char *k_valid_command_chars =
1626 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
1627static void StripLeadingSpaces(std::string &s) {
1628 if (!s.empty()) {
1629 size_t pos = s.find_first_not_of(s: k_white_space);
1630 if (pos == std::string::npos)
1631 s.clear();
1632 else if (pos == 0)
1633 return;
1634 s.erase(pos: 0, n: pos);
1635 }
1636}
1637
1638static size_t FindArgumentTerminator(const std::string &s) {
1639 const size_t s_len = s.size();
1640 size_t offset = 0;
1641 while (offset < s_len) {
1642 size_t pos = s.find(s: "--", pos: offset);
1643 if (pos == std::string::npos)
1644 break;
1645 if (pos > 0) {
1646 if (llvm::isSpace(C: s[pos - 1])) {
1647 // Check if the string ends "\s--" (where \s is a space character) or
1648 // if we have "\s--\s".
1649 if ((pos + 2 >= s_len) || llvm::isSpace(C: s[pos + 2])) {
1650 return pos;
1651 }
1652 }
1653 }
1654 offset = pos + 2;
1655 }
1656 return std::string::npos;
1657}
1658
1659static bool ExtractCommand(std::string &command_string, std::string &command,
1660 std::string &suffix, char &quote_char) {
1661 command.clear();
1662 suffix.clear();
1663 StripLeadingSpaces(s&: command_string);
1664
1665 bool result = false;
1666 quote_char = '\0';
1667
1668 if (!command_string.empty()) {
1669 const char first_char = command_string[0];
1670 if (first_char == '\'' || first_char == '"') {
1671 quote_char = first_char;
1672 const size_t end_quote_pos = command_string.find(c: quote_char, pos: 1);
1673 if (end_quote_pos == std::string::npos) {
1674 command.swap(s&: command_string);
1675 command_string.erase();
1676 } else {
1677 command.assign(str: command_string, pos: 1, n: end_quote_pos - 1);
1678 if (end_quote_pos + 1 < command_string.size())
1679 command_string.erase(pos: 0, n: command_string.find_first_not_of(
1680 s: k_white_space, pos: end_quote_pos + 1));
1681 else
1682 command_string.erase();
1683 }
1684 } else {
1685 const size_t first_space_pos =
1686 command_string.find_first_of(s: k_white_space);
1687 if (first_space_pos == std::string::npos) {
1688 command.swap(s&: command_string);
1689 command_string.erase();
1690 } else {
1691 command.assign(str: command_string, pos: 0, n: first_space_pos);
1692 command_string.erase(pos: 0, n: command_string.find_first_not_of(
1693 s: k_white_space, pos: first_space_pos));
1694 }
1695 }
1696 result = true;
1697 }
1698
1699 if (!command.empty()) {
1700 // actual commands can't start with '-' or '_'
1701 if (command[0] != '-' && command[0] != '_') {
1702 size_t pos = command.find_first_not_of(s: k_valid_command_chars);
1703 if (pos > 0 && pos != std::string::npos) {
1704 suffix.assign(first: command.begin() + pos, last: command.end());
1705 command.erase(pos: pos);
1706 }
1707 }
1708 }
1709
1710 return result;
1711}
1712
1713CommandObject *CommandInterpreter::BuildAliasResult(
1714 llvm::StringRef alias_name, std::string &raw_input_string,
1715 std::string &alias_result, CommandReturnObject &result) {
1716 CommandObject *alias_cmd_obj = nullptr;
1717 Args cmd_args(raw_input_string);
1718 alias_cmd_obj = GetCommandObject(cmd_str: alias_name);
1719 StreamString result_str;
1720
1721 if (!alias_cmd_obj || !alias_cmd_obj->IsAlias()) {
1722 alias_result.clear();
1723 return alias_cmd_obj;
1724 }
1725 std::pair<CommandObjectSP, OptionArgVectorSP> desugared =
1726 ((CommandAlias *)alias_cmd_obj)->Desugar();
1727 OptionArgVectorSP option_arg_vector_sp = desugared.second;
1728 alias_cmd_obj = desugared.first.get();
1729 std::string alias_name_str = std::string(alias_name);
1730 if ((cmd_args.GetArgumentCount() == 0) ||
1731 (alias_name_str != cmd_args.GetArgumentAtIndex(idx: 0)))
1732 cmd_args.Unshift(arg_str: alias_name_str);
1733
1734 result_str.Printf(format: "%s", alias_cmd_obj->GetCommandName().str().c_str());
1735
1736 if (!option_arg_vector_sp.get()) {
1737 alias_result = std::string(result_str.GetString());
1738 return alias_cmd_obj;
1739 }
1740 OptionArgVector *option_arg_vector = option_arg_vector_sp.get();
1741
1742 int value_type;
1743 std::string option;
1744 std::string value;
1745 for (const auto &entry : *option_arg_vector) {
1746 std::tie(args&: option, args&: value_type, args&: value) = entry;
1747 if (option == g_argument) {
1748 result_str.Printf(format: " %s", value.c_str());
1749 continue;
1750 }
1751
1752 result_str.Printf(format: " %s", option.c_str());
1753 if (value_type == OptionParser::eNoArgument)
1754 continue;
1755
1756 if (value_type != OptionParser::eOptionalArgument)
1757 result_str.Printf(format: " ");
1758 int index = GetOptionArgumentPosition(in_string: value.c_str());
1759 if (index == 0)
1760 result_str.Printf(format: "%s", value.c_str());
1761 else if (static_cast<size_t>(index) >= cmd_args.GetArgumentCount()) {
1762
1763 result.AppendErrorWithFormat(format: "Not enough arguments provided; you "
1764 "need at least %d arguments to use "
1765 "this alias.\n",
1766 index);
1767 return nullptr;
1768 } else {
1769 const Args::ArgEntry &entry = cmd_args[index];
1770 size_t strpos = raw_input_string.find(s: entry.c_str());
1771 const char quote_char = entry.GetQuoteChar();
1772 if (strpos != std::string::npos) {
1773 const size_t start_fudge = quote_char == '\0' ? 0 : 1;
1774 const size_t len_fudge = quote_char == '\0' ? 0 : 2;
1775
1776 // Make sure we aren't going outside the bounds of the cmd string:
1777 if (strpos < start_fudge) {
1778 result.AppendError(in_string: "Unmatched quote at command beginning.");
1779 return nullptr;
1780 }
1781 llvm::StringRef arg_text = entry.ref();
1782 if (strpos - start_fudge + arg_text.size() + len_fudge >
1783 raw_input_string.size()) {
1784 result.AppendError(in_string: "Unmatched quote at command end.");
1785 return nullptr;
1786 }
1787 raw_input_string = raw_input_string.erase(
1788 pos: strpos - start_fudge,
1789 n: strlen(s: cmd_args.GetArgumentAtIndex(idx: index)) + len_fudge);
1790 }
1791 if (quote_char == '\0')
1792 result_str.Printf(format: "%s", cmd_args.GetArgumentAtIndex(idx: index));
1793 else
1794 result_str.Printf(format: "%c%s%c", quote_char, entry.c_str(), quote_char);
1795 }
1796 }
1797
1798 alias_result = std::string(result_str.GetString());
1799 return alias_cmd_obj;
1800}
1801
1802Status CommandInterpreter::PreprocessCommand(std::string &command) {
1803 // The command preprocessor needs to do things to the command line before any
1804 // parsing of arguments or anything else is done. The only current stuff that
1805 // gets preprocessed is anything enclosed in backtick ('`') characters is
1806 // evaluated as an expression and the result of the expression must be a
1807 // scalar that can be substituted into the command. An example would be:
1808 // (lldb) memory read `$rsp + 20`
1809 Status error; // Status for any expressions that might not evaluate
1810 size_t start_backtick;
1811 size_t pos = 0;
1812 while ((start_backtick = command.find(c: '`', pos: pos)) != std::string::npos) {
1813 // Stop if an error was encountered during the previous iteration.
1814 if (error.Fail())
1815 break;
1816
1817 if (start_backtick > 0 && command[start_backtick - 1] == '\\') {
1818 // The backtick was preceded by a '\' character, remove the slash and
1819 // don't treat the backtick as the start of an expression.
1820 command.erase(pos: start_backtick - 1, n: 1);
1821 // No need to add one to start_backtick since we just deleted a char.
1822 pos = start_backtick;
1823 continue;
1824 }
1825
1826 const size_t expr_content_start = start_backtick + 1;
1827 const size_t end_backtick = command.find(c: '`', pos: expr_content_start);
1828
1829 if (end_backtick == std::string::npos) {
1830 // Stop if there's no end backtick.
1831 break;
1832 }
1833
1834 if (end_backtick == expr_content_start) {
1835 // Skip over empty expression. (two backticks in a row)
1836 command.erase(pos: start_backtick, n: 2);
1837 continue;
1838 }
1839
1840 std::string expr_str(command, expr_content_start,
1841 end_backtick - expr_content_start);
1842 error = PreprocessToken(token&: expr_str);
1843 // We always stop at the first error:
1844 if (error.Fail())
1845 break;
1846
1847 command.erase(pos: start_backtick, n: end_backtick - start_backtick + 1);
1848 command.insert(pos1: start_backtick, str: std::string(expr_str));
1849 pos = start_backtick + expr_str.size();
1850 }
1851 return error;
1852}
1853
1854Status CommandInterpreter::PreprocessToken(std::string &expr_str) {
1855 Status error;
1856 ExecutionContext exe_ctx(GetExecutionContext());
1857
1858 // Get a dummy target to allow for calculator mode while processing
1859 // backticks. This also helps break the infinite loop caused when target is
1860 // null.
1861 Target *exe_target = exe_ctx.GetTargetPtr();
1862 Target &target = exe_target ? *exe_target : m_debugger.GetDummyTarget();
1863
1864 ValueObjectSP expr_result_valobj_sp;
1865
1866 EvaluateExpressionOptions options;
1867 options.SetCoerceToId(false);
1868 options.SetUnwindOnError(true);
1869 options.SetIgnoreBreakpoints(true);
1870 options.SetKeepInMemory(false);
1871 options.SetTryAllThreads(true);
1872 options.SetTimeout(std::nullopt);
1873
1874 ExpressionResults expr_result = target.EvaluateExpression(
1875 expression: expr_str.c_str(), exe_scope: exe_ctx.GetFramePtr(), result_valobj_sp&: expr_result_valobj_sp, options);
1876
1877 if (expr_result == eExpressionCompleted) {
1878 Scalar scalar;
1879 if (expr_result_valobj_sp)
1880 expr_result_valobj_sp =
1881 expr_result_valobj_sp->GetQualifiedRepresentationIfAvailable(
1882 dynValue: expr_result_valobj_sp->GetDynamicValueType(), synthValue: true);
1883 if (expr_result_valobj_sp->ResolveValue(scalar)) {
1884
1885 StreamString value_strm;
1886 const bool show_type = false;
1887 scalar.GetValue(s&: value_strm, show_type);
1888 size_t value_string_size = value_strm.GetSize();
1889 if (value_string_size) {
1890 expr_str = value_strm.GetData();
1891 } else {
1892 error =
1893 Status::FromErrorStringWithFormat(format: "expression value didn't result "
1894 "in a scalar value for the "
1895 "expression '%s'",
1896 expr_str.c_str());
1897 }
1898 } else {
1899 error =
1900 Status::FromErrorStringWithFormat(format: "expression value didn't result "
1901 "in a scalar value for the "
1902 "expression '%s'",
1903 expr_str.c_str());
1904 }
1905 return error;
1906 }
1907
1908 // If we have an error from the expression evaluation it will be in the
1909 // ValueObject error, which won't be success and we will just report it.
1910 // But if for some reason we didn't get a value object at all, then we will
1911 // make up some helpful errors from the expression result.
1912 if (expr_result_valobj_sp)
1913 error = expr_result_valobj_sp->GetError().Clone();
1914
1915 if (error.Success()) {
1916 std::string result = lldb_private::toString(e: expr_result) +
1917 "for the expression '" + expr_str + "'";
1918 error = Status(result);
1919 }
1920 return error;
1921}
1922
1923bool CommandInterpreter::HandleCommand(const char *command_line,
1924 LazyBool lazy_add_to_history,
1925 const ExecutionContext &override_context,
1926 CommandReturnObject &result) {
1927
1928 OverrideExecutionContext(override_context);
1929 bool status = HandleCommand(command_line, add_to_history: lazy_add_to_history, result);
1930 RestoreExecutionContext();
1931 return status;
1932}
1933
1934bool CommandInterpreter::HandleCommand(const char *command_line,
1935 LazyBool lazy_add_to_history,
1936 CommandReturnObject &result,
1937 bool force_repeat_command) {
1938 // These are assigned later in the function but they must be declared before
1939 // the ScopedDispatcher object because we need their destructions to occur
1940 // after the dispatcher's dtor call, which may reference them.
1941 // TODO: This function could be refactored?
1942 std::string parsed_command_args;
1943 CommandObject *cmd_obj = nullptr;
1944
1945 telemetry::ScopedDispatcher<telemetry::CommandInfo> helper(&m_debugger);
1946 const bool detailed_command_telemetry =
1947 telemetry::TelemetryManager::GetInstance()
1948 ->GetConfig()
1949 ->detailed_command_telemetry;
1950 const int command_id = telemetry::CommandInfo::GetNextID();
1951
1952 std::string command_string(command_line);
1953 std::string original_command_string(command_string);
1954 std::string real_original_command_string(command_string);
1955
1956 helper.DispatchNow(populate_fields_cb: [&](lldb_private::telemetry::CommandInfo *info) {
1957 info->command_id = command_id;
1958 if (Target *target = GetExecutionContext().GetTargetPtr()) {
1959 // If we have a target attached to this command, then get the UUID.
1960 info->target_uuid = target->GetExecutableModule() != nullptr
1961 ? target->GetExecutableModule()->GetUUID()
1962 : UUID();
1963 }
1964 if (detailed_command_telemetry)
1965 info->original_command = original_command_string;
1966 // The rest (eg., command_name, args, etc) hasn't been parsed yet;
1967 // Those will be collected by the on-exit-callback.
1968 });
1969
1970 helper.DispatchOnExit(final_callback: [&cmd_obj, &parsed_command_args, &result,
1971 detailed_command_telemetry, command_id](
1972 lldb_private::telemetry::CommandInfo *info) {
1973 // TODO: this is logging the time the command-handler finishes.
1974 // But we may want a finer-grain durations too?
1975 // (ie., the execute_time recorded below?)
1976 info->command_id = command_id;
1977 llvm::StringRef command_name =
1978 cmd_obj ? cmd_obj->GetCommandName() : "<not found>";
1979 info->command_name = command_name.str();
1980 info->ret_status = result.GetStatus();
1981 if (std::string error_str = result.GetErrorString(); !error_str.empty())
1982 info->error_data = std::move(error_str);
1983
1984 if (detailed_command_telemetry)
1985 info->args = parsed_command_args;
1986 });
1987
1988 Log *log = GetLog(mask: LLDBLog::Commands);
1989 LLDB_LOGF(log, "Processing command: %s", command_line);
1990 LLDB_SCOPED_TIMERF("Processing command: %s.", command_line);
1991
1992 // Set the command in the CommandReturnObject here so that it's there even if
1993 // the command is interrupted.
1994 result.SetCommand(command_line);
1995
1996 if (INTERRUPT_REQUESTED(GetDebugger(), "Interrupted initiating command")) {
1997 result.AppendError(in_string: "... Interrupted");
1998 return false;
1999 }
2000
2001 bool add_to_history;
2002 if (lazy_add_to_history == eLazyBoolCalculate)
2003 add_to_history = (m_command_source_depth == 0);
2004 else
2005 add_to_history = (lazy_add_to_history == eLazyBoolYes);
2006
2007 // The same `transcript_item` will be used below to add output and error of
2008 // the command.
2009 StructuredData::DictionarySP transcript_item;
2010 if (GetSaveTranscript()) {
2011 m_transcript_stream << "(lldb) " << command_line << '\n';
2012
2013 transcript_item = std::make_shared<StructuredData::Dictionary>();
2014 transcript_item->AddStringItem(key: "command", value: command_line);
2015 transcript_item->AddIntegerItem(
2016 key: "timestampInEpochSeconds",
2017 value: std::chrono::duration_cast<std::chrono::seconds>(
2018 d: std::chrono::system_clock::now().time_since_epoch())
2019 .count());
2020 m_transcript.AddItem(item: transcript_item);
2021 }
2022
2023 bool empty_command = false;
2024 bool comment_command = false;
2025 if (command_string.empty())
2026 empty_command = true;
2027 else {
2028 const char *k_space_characters = "\t\n\v\f\r ";
2029
2030 size_t non_space = command_string.find_first_not_of(s: k_space_characters);
2031 // Check for empty line or comment line (lines whose first non-space
2032 // character is the comment character for this interpreter)
2033 if (non_space == std::string::npos)
2034 empty_command = true;
2035 else if (command_string[non_space] == m_comment_char)
2036 comment_command = true;
2037 else if (command_string[non_space] == CommandHistory::g_repeat_char) {
2038 llvm::StringRef search_str(command_string);
2039 search_str = search_str.drop_front(N: non_space);
2040 if (auto hist_str = m_command_history.FindString(input_str: search_str)) {
2041 add_to_history = false;
2042 command_string = std::string(*hist_str);
2043 original_command_string = std::string(*hist_str);
2044 } else {
2045 result.AppendErrorWithFormat(format: "Could not find entry: %s in history",
2046 command_string.c_str());
2047 return false;
2048 }
2049 }
2050 }
2051
2052 if (empty_command) {
2053 if (!GetRepeatPreviousCommand()) {
2054 result.SetStatus(eReturnStatusSuccessFinishNoResult);
2055 return true;
2056 }
2057
2058 if (m_command_history.IsEmpty()) {
2059 result.AppendError(in_string: "empty command");
2060 return false;
2061 }
2062
2063 command_line = m_repeat_command.c_str();
2064 command_string = command_line;
2065 original_command_string = command_line;
2066 if (m_repeat_command.empty()) {
2067 result.AppendError(in_string: "No auto repeat.");
2068 return false;
2069 }
2070
2071 add_to_history = false;
2072 } else if (comment_command) {
2073 result.SetStatus(eReturnStatusSuccessFinishNoResult);
2074 return true;
2075 }
2076
2077 // Phase 1.
2078
2079 // Before we do ANY kind of argument processing, we need to figure out what
2080 // the real/final command object is for the specified command. This gets
2081 // complicated by the fact that the user could have specified an alias, and,
2082 // in translating the alias, there may also be command options and/or even
2083 // data (including raw text strings) that need to be found and inserted into
2084 // the command line as part of the translation. So this first step is plain
2085 // look-up and replacement, resulting in:
2086 // 1. the command object whose Execute method will actually be called
2087 // 2. a revised command string, with all substitutions and replacements
2088 // taken care of
2089 // From 1 above, we can determine whether the Execute function wants raw
2090 // input or not.
2091
2092 cmd_obj = ResolveCommandImpl(command_line&: command_string, result);
2093
2094 // We have to preprocess the whole command string for Raw commands, since we
2095 // don't know the structure of the command. For parsed commands, we only
2096 // treat backticks as quote characters specially.
2097 // FIXME: We probably want to have raw commands do their own preprocessing.
2098 // For instance, I don't think people expect substitution in expr expressions.
2099 if (cmd_obj && cmd_obj->WantsRawCommandString()) {
2100 Status error(PreprocessCommand(command&: command_string));
2101
2102 if (error.Fail()) {
2103 result.AppendError(in_string: error.AsCString());
2104 return false;
2105 }
2106 }
2107
2108 // Although the user may have abbreviated the command, the command_string now
2109 // has the command expanded to the full name. For example, if the input was
2110 // "br s -n main", command_string is now "breakpoint set -n main".
2111 if (log) {
2112 llvm::StringRef command_name =
2113 cmd_obj ? cmd_obj->GetCommandName() : "<not found>";
2114 LLDB_LOGF(log, "HandleCommand, cmd_obj : '%s'", command_name.str().c_str());
2115 LLDB_LOGF(log, "HandleCommand, (revised) command_string: '%s'",
2116 command_string.c_str());
2117 const bool wants_raw_input =
2118 (cmd_obj != nullptr) ? cmd_obj->WantsRawCommandString() : false;
2119 LLDB_LOGF(log, "HandleCommand, wants_raw_input:'%s'",
2120 wants_raw_input ? "True" : "False");
2121 }
2122
2123 // Phase 2.
2124 // Take care of things like setting up the history command & calling the
2125 // appropriate Execute method on the CommandObject, with the appropriate
2126 // arguments.
2127 StatsDuration execute_time;
2128 if (cmd_obj != nullptr) {
2129 bool generate_repeat_command = add_to_history;
2130 // If we got here when empty_command was true, then this command is a
2131 // stored "repeat command" which we should give a chance to produce it's
2132 // repeat command, even though we don't add repeat commands to the history.
2133 generate_repeat_command |= empty_command;
2134 // For `command regex`, the regex command (ex `bt`) is added to history, but
2135 // the resolved command (ex `thread backtrace`) is _not_ added to history.
2136 // However, the resolved command must be given the opportunity to provide a
2137 // repeat command. `force_repeat_command` supports this case.
2138 generate_repeat_command |= force_repeat_command;
2139 if (generate_repeat_command) {
2140 Args command_args(command_string);
2141 std::optional<std::string> repeat_command =
2142 cmd_obj->GetRepeatCommand(current_command_args&: command_args, index: 0);
2143 if (repeat_command) {
2144 LLDB_LOGF(log, "Repeat command: %s", repeat_command->data());
2145 m_repeat_command.assign(str: *repeat_command);
2146 } else {
2147 m_repeat_command.assign(str: original_command_string);
2148 }
2149 }
2150
2151 if (add_to_history)
2152 m_command_history.AppendString(str: original_command_string);
2153
2154 const std::size_t actual_cmd_name_len = cmd_obj->GetCommandName().size();
2155 if (actual_cmd_name_len < command_string.length())
2156 parsed_command_args = command_string.substr(pos: actual_cmd_name_len);
2157
2158 // Remove any initial spaces
2159 size_t pos = parsed_command_args.find_first_not_of(s: k_white_space);
2160 if (pos != 0 && pos != std::string::npos)
2161 parsed_command_args.erase(pos: 0, n: pos);
2162
2163 LLDB_LOGF(
2164 log, "HandleCommand, command line after removing command name(s): '%s'",
2165 parsed_command_args.c_str());
2166
2167 // To test whether or not transcript should be saved, `transcript_item` is
2168 // used instead of `GetSaveTranscript()`. This is because the latter will
2169 // fail when the command is "settings set interpreter.save-transcript true".
2170 if (transcript_item) {
2171 transcript_item->AddStringItem(key: "commandName", value: cmd_obj->GetCommandName());
2172 transcript_item->AddStringItem(key: "commandArguments", value: parsed_command_args);
2173 }
2174
2175 ElapsedTime elapsed(execute_time);
2176 cmd_obj->SetOriginalCommandString(real_original_command_string);
2177 // Set the indent to the position of the command in the command line.
2178 pos = real_original_command_string.rfind(str: parsed_command_args);
2179 std::optional<uint16_t> indent;
2180 if (pos != std::string::npos)
2181 indent = pos;
2182 result.SetDiagnosticIndent(indent);
2183 cmd_obj->Execute(args_string: parsed_command_args.c_str(), result);
2184 }
2185
2186 LLDB_LOGF(log, "HandleCommand, command %s",
2187 (result.Succeeded() ? "succeeded" : "did not succeed"));
2188
2189 // To test whether or not transcript should be saved, `transcript_item` is
2190 // used instead of `GetSaveTrasncript()`. This is because the latter will
2191 // fail when the command is "settings set interpreter.save-transcript true".
2192 if (transcript_item) {
2193 m_transcript_stream << result.GetOutputString();
2194 m_transcript_stream << result.GetErrorString();
2195
2196 transcript_item->AddStringItem(key: "output", value: result.GetOutputString());
2197 transcript_item->AddStringItem(key: "error", value: result.GetErrorString());
2198 transcript_item->AddFloatItem(key: "durationInSeconds",
2199 value: execute_time.get().count());
2200 }
2201
2202 return result.Succeeded();
2203}
2204
2205void CommandInterpreter::HandleCompletionMatches(CompletionRequest &request) {
2206 bool look_for_subcommand = false;
2207
2208 // For any of the command completions a unique match will be a complete word.
2209
2210 if (request.GetParsedLine().GetArgumentCount() == 0) {
2211 // We got nothing on the command line, so return the list of commands
2212 bool include_aliases = true;
2213 StringList new_matches, descriptions;
2214 GetCommandNamesMatchingPartialString(cmd_str: "", include_aliases, matches&: new_matches,
2215 descriptions);
2216 request.AddCompletions(completions: new_matches, descriptions);
2217 } else if (request.GetCursorIndex() == 0) {
2218 // The cursor is in the first argument, so just do a lookup in the
2219 // dictionary.
2220 StringList new_matches, new_descriptions;
2221 CommandObject *cmd_obj =
2222 GetCommandObject(cmd_str: request.GetParsedLine().GetArgumentAtIndex(idx: 0),
2223 matches: &new_matches, descriptions: &new_descriptions);
2224
2225 if (new_matches.GetSize() && cmd_obj && cmd_obj->IsMultiwordObject() &&
2226 new_matches.GetStringAtIndex(idx: 0) != nullptr &&
2227 strcmp(s1: request.GetParsedLine().GetArgumentAtIndex(idx: 0),
2228 s2: new_matches.GetStringAtIndex(idx: 0)) == 0) {
2229 if (request.GetParsedLine().GetArgumentCount() != 1) {
2230 look_for_subcommand = true;
2231 new_matches.DeleteStringAtIndex(id: 0);
2232 new_descriptions.DeleteStringAtIndex(id: 0);
2233 request.AppendEmptyArgument();
2234 }
2235 }
2236 request.AddCompletions(completions: new_matches, descriptions: new_descriptions);
2237 }
2238
2239 if (request.GetCursorIndex() > 0 || look_for_subcommand) {
2240 // We are completing further on into a commands arguments, so find the
2241 // command and tell it to complete the command. First see if there is a
2242 // matching initial command:
2243 CommandObject *command_object =
2244 GetCommandObject(cmd_str: request.GetParsedLine().GetArgumentAtIndex(idx: 0));
2245 if (command_object) {
2246 request.ShiftArguments();
2247 command_object->HandleCompletion(request);
2248 }
2249 }
2250}
2251
2252void CommandInterpreter::HandleCompletion(CompletionRequest &request) {
2253
2254 // Don't complete comments, and if the line we are completing is just the
2255 // history repeat character, substitute the appropriate history line.
2256 llvm::StringRef first_arg = request.GetParsedLine().GetArgumentAtIndex(idx: 0);
2257
2258 if (!first_arg.empty()) {
2259 if (first_arg.front() == m_comment_char)
2260 return;
2261 if (first_arg.front() == CommandHistory::g_repeat_char) {
2262 if (auto hist_str = m_command_history.FindString(input_str: first_arg))
2263 request.AddCompletion(completion: *hist_str, description: "Previous command history event",
2264 mode: CompletionMode::RewriteLine);
2265 return;
2266 }
2267 }
2268
2269 HandleCompletionMatches(request);
2270}
2271
2272std::optional<std::string>
2273CommandInterpreter::GetAutoSuggestionForCommand(llvm::StringRef line) {
2274 if (line.empty())
2275 return std::nullopt;
2276 const size_t s = m_command_history.GetSize();
2277 for (int i = s - 1; i >= 0; --i) {
2278 llvm::StringRef entry = m_command_history.GetStringAtIndex(idx: i);
2279 if (entry.consume_front(Prefix: line))
2280 return entry.str();
2281 }
2282 return std::nullopt;
2283}
2284
2285void CommandInterpreter::UpdatePrompt(llvm::StringRef new_prompt) {
2286 EventSP prompt_change_event_sp(
2287 new Event(eBroadcastBitResetPrompt, new EventDataBytes(new_prompt)));
2288
2289 BroadcastEvent(event_sp&: prompt_change_event_sp);
2290 if (m_command_io_handler_sp)
2291 m_command_io_handler_sp->SetPrompt(new_prompt);
2292}
2293
2294void CommandInterpreter::UpdateUseColor(bool use_color) {
2295 if (m_command_io_handler_sp)
2296 m_command_io_handler_sp->SetUseColor(use_color);
2297}
2298
2299bool CommandInterpreter::Confirm(llvm::StringRef message, bool default_answer) {
2300 // Check AutoConfirm first:
2301 if (m_debugger.GetAutoConfirm())
2302 return default_answer;
2303
2304 IOHandlerConfirm *confirm =
2305 new IOHandlerConfirm(m_debugger, message, default_answer);
2306 IOHandlerSP io_handler_sp(confirm);
2307 m_debugger.RunIOHandlerSync(reader_sp: io_handler_sp);
2308 return confirm->GetResponse();
2309}
2310
2311const CommandAlias *
2312CommandInterpreter::GetAlias(llvm::StringRef alias_name) const {
2313 OptionArgVectorSP ret_val;
2314
2315 auto pos = m_alias_dict.find(x: alias_name);
2316 if (pos != m_alias_dict.end())
2317 return (CommandAlias *)pos->second.get();
2318
2319 return nullptr;
2320}
2321
2322bool CommandInterpreter::HasCommands() const {
2323 return (!m_command_dict.empty());
2324}
2325
2326bool CommandInterpreter::HasAliases() const { return (!m_alias_dict.empty()); }
2327
2328bool CommandInterpreter::HasUserCommands() const {
2329 return (!m_user_dict.empty());
2330}
2331
2332bool CommandInterpreter::HasUserMultiwordCommands() const {
2333 return (!m_user_mw_dict.empty());
2334}
2335
2336bool CommandInterpreter::HasAliasOptions() const { return HasAliases(); }
2337
2338void CommandInterpreter::BuildAliasCommandArgs(CommandObject *alias_cmd_obj,
2339 const char *alias_name,
2340 Args &cmd_args,
2341 std::string &raw_input_string,
2342 CommandReturnObject &result) {
2343 OptionArgVectorSP option_arg_vector_sp =
2344 GetAlias(alias_name)->GetOptionArguments();
2345
2346 bool wants_raw_input = alias_cmd_obj->WantsRawCommandString();
2347
2348 // Make sure that the alias name is the 0th element in cmd_args
2349 std::string alias_name_str = alias_name;
2350 if (alias_name_str != cmd_args.GetArgumentAtIndex(idx: 0))
2351 cmd_args.Unshift(arg_str: alias_name_str);
2352
2353 Args new_args(alias_cmd_obj->GetCommandName());
2354 if (new_args.GetArgumentCount() == 2)
2355 new_args.Shift();
2356
2357 if (option_arg_vector_sp.get()) {
2358 if (wants_raw_input) {
2359 // We have a command that both has command options and takes raw input.
2360 // Make *sure* it has a " -- " in the right place in the
2361 // raw_input_string.
2362 size_t pos = raw_input_string.find(s: " -- ");
2363 if (pos == std::string::npos) {
2364 // None found; assume it goes at the beginning of the raw input string
2365 raw_input_string.insert(pos: 0, s: " -- ");
2366 }
2367 }
2368
2369 OptionArgVector *option_arg_vector = option_arg_vector_sp.get();
2370 const size_t old_size = cmd_args.GetArgumentCount();
2371 std::vector<bool> used(old_size + 1, false);
2372
2373 used[0] = true;
2374
2375 int value_type;
2376 std::string option;
2377 std::string value;
2378 for (const auto &option_entry : *option_arg_vector) {
2379 std::tie(args&: option, args&: value_type, args&: value) = option_entry;
2380 if (option == g_argument) {
2381 if (!wants_raw_input || (value != "--")) {
2382 // Since we inserted this above, make sure we don't insert it twice
2383 new_args.AppendArgument(arg_str: value);
2384 }
2385 continue;
2386 }
2387
2388 if (value_type != OptionParser::eOptionalArgument)
2389 new_args.AppendArgument(arg_str: option);
2390
2391 if (value == g_no_argument)
2392 continue;
2393
2394 int index = GetOptionArgumentPosition(in_string: value.c_str());
2395 if (index == 0) {
2396 // value was NOT a positional argument; must be a real value
2397 if (value_type != OptionParser::eOptionalArgument)
2398 new_args.AppendArgument(arg_str: value);
2399 else {
2400 new_args.AppendArgument(arg_str: option + value);
2401 }
2402
2403 } else if (static_cast<size_t>(index) >= cmd_args.GetArgumentCount()) {
2404 result.AppendErrorWithFormat(format: "Not enough arguments provided; you "
2405 "need at least %d arguments to use "
2406 "this alias.\n",
2407 index);
2408 return;
2409 } else {
2410 // Find and remove cmd_args.GetArgumentAtIndex(i) from raw_input_string
2411 size_t strpos =
2412 raw_input_string.find(s: cmd_args.GetArgumentAtIndex(idx: index));
2413 if (strpos != std::string::npos) {
2414 raw_input_string = raw_input_string.erase(
2415 pos: strpos, n: strlen(s: cmd_args.GetArgumentAtIndex(idx: index)));
2416 }
2417
2418 if (value_type != OptionParser::eOptionalArgument)
2419 new_args.AppendArgument(arg_str: cmd_args.GetArgumentAtIndex(idx: index));
2420 else {
2421 new_args.AppendArgument(arg_str: option + cmd_args.GetArgumentAtIndex(idx: index));
2422 }
2423 used[index] = true;
2424 }
2425 }
2426
2427 for (auto entry : llvm::enumerate(First: cmd_args.entries())) {
2428 if (!used[entry.index()] && !wants_raw_input)
2429 new_args.AppendArgument(arg_str: entry.value().ref());
2430 }
2431
2432 cmd_args.Clear();
2433 cmd_args.SetArguments(argc: new_args.GetArgumentCount(),
2434 argv: new_args.GetConstArgumentVector());
2435 } else {
2436 result.SetStatus(eReturnStatusSuccessFinishNoResult);
2437 // This alias was not created with any options; nothing further needs to be
2438 // done, unless it is a command that wants raw input, in which case we need
2439 // to clear the rest of the data from cmd_args, since its in the raw input
2440 // string.
2441 if (wants_raw_input) {
2442 cmd_args.Clear();
2443 cmd_args.SetArguments(argc: new_args.GetArgumentCount(),
2444 argv: new_args.GetConstArgumentVector());
2445 }
2446 return;
2447 }
2448
2449 result.SetStatus(eReturnStatusSuccessFinishNoResult);
2450}
2451
2452int CommandInterpreter::GetOptionArgumentPosition(const char *in_string) {
2453 int position = 0; // Any string that isn't an argument position, i.e. '%'
2454 // followed by an integer, gets a position
2455 // of zero.
2456
2457 const char *cptr = in_string;
2458
2459 // Does it start with '%'
2460 if (cptr[0] == '%') {
2461 ++cptr;
2462
2463 // Is the rest of it entirely digits?
2464 if (isdigit(cptr[0])) {
2465 const char *start = cptr;
2466 while (isdigit(cptr[0]))
2467 ++cptr;
2468
2469 // We've gotten to the end of the digits; are we at the end of the
2470 // string?
2471 if (cptr[0] == '\0')
2472 position = atoi(nptr: start);
2473 }
2474 }
2475
2476 return position;
2477}
2478
2479static void GetHomeInitFile(llvm::SmallVectorImpl<char> &init_file,
2480 llvm::StringRef suffix = {}) {
2481 std::string init_file_name = ".lldbinit";
2482 if (!suffix.empty()) {
2483 init_file_name.append(s: "-");
2484 init_file_name.append(str: suffix.str());
2485 }
2486
2487 FileSystem::Instance().GetHomeDirectory(path&: init_file);
2488 llvm::sys::path::append(path&: init_file, a: init_file_name);
2489
2490 FileSystem::Instance().Resolve(path&: init_file);
2491}
2492
2493static void GetHomeREPLInitFile(llvm::SmallVectorImpl<char> &init_file,
2494 LanguageType language) {
2495 if (language == eLanguageTypeUnknown) {
2496 LanguageSet repl_languages = Language::GetLanguagesSupportingREPLs();
2497 if (auto main_repl_language = repl_languages.GetSingularLanguage())
2498 language = *main_repl_language;
2499 else
2500 return;
2501 }
2502
2503 std::string init_file_name =
2504 (llvm::Twine(".lldbinit-") +
2505 llvm::Twine(Language::GetNameForLanguageType(language)) +
2506 llvm::Twine("-repl"))
2507 .str();
2508 FileSystem::Instance().GetHomeDirectory(path&: init_file);
2509 llvm::sys::path::append(path&: init_file, a: init_file_name);
2510 FileSystem::Instance().Resolve(path&: init_file);
2511}
2512
2513static void GetCwdInitFile(llvm::SmallVectorImpl<char> &init_file) {
2514 llvm::StringRef s = ".lldbinit";
2515 init_file.assign(in_start: s.begin(), in_end: s.end());
2516 FileSystem::Instance().Resolve(path&: init_file);
2517}
2518
2519void CommandInterpreter::SourceInitFile(FileSpec file,
2520 CommandReturnObject &result) {
2521 assert(!m_skip_lldbinit_files);
2522
2523 if (!FileSystem::Instance().Exists(file_spec: file)) {
2524 result.SetStatus(eReturnStatusSuccessFinishNoResult);
2525 return;
2526 }
2527
2528 // Use HandleCommand to 'source' the given file; this will do the actual
2529 // broadcasting of the commands back to any appropriate listener (see
2530 // CommandObjectSource::Execute for more details).
2531 const bool saved_batch = SetBatchCommandMode(true);
2532 CommandInterpreterRunOptions options;
2533 options.SetSilent(true);
2534 options.SetPrintErrors(true);
2535 options.SetStopOnError(false);
2536 options.SetStopOnContinue(true);
2537 HandleCommandsFromFile(file, options, result);
2538 SetBatchCommandMode(saved_batch);
2539}
2540
2541void CommandInterpreter::SourceInitFileCwd(CommandReturnObject &result) {
2542 if (m_skip_lldbinit_files) {
2543 result.SetStatus(eReturnStatusSuccessFinishNoResult);
2544 return;
2545 }
2546
2547 llvm::SmallString<128> init_file;
2548 GetCwdInitFile(init_file);
2549 if (!FileSystem::Instance().Exists(path: init_file)) {
2550 result.SetStatus(eReturnStatusSuccessFinishNoResult);
2551 return;
2552 }
2553
2554 LoadCWDlldbinitFile should_load =
2555 Target::GetGlobalProperties().GetLoadCWDlldbinitFile();
2556
2557 switch (should_load) {
2558 case eLoadCWDlldbinitFalse:
2559 result.SetStatus(eReturnStatusSuccessFinishNoResult);
2560 break;
2561 case eLoadCWDlldbinitTrue:
2562 SourceInitFile(file: FileSpec(init_file.str()), result);
2563 break;
2564 case eLoadCWDlldbinitWarn: {
2565 llvm::SmallString<128> home_init_file;
2566 GetHomeInitFile(init_file&: home_init_file);
2567 if (llvm::sys::path::parent_path(path: init_file) ==
2568 llvm::sys::path::parent_path(path: home_init_file)) {
2569 result.SetStatus(eReturnStatusSuccessFinishNoResult);
2570 } else {
2571 result.AppendError(in_string: InitFileWarning);
2572 }
2573 }
2574 }
2575}
2576
2577/// We will first see if there is an application specific ".lldbinit" file
2578/// whose name is "~/.lldbinit" followed by a "-" and the name of the program.
2579/// If this file doesn't exist, we fall back to the REPL init file or the
2580/// default home init file in "~/.lldbinit".
2581void CommandInterpreter::SourceInitFileHome(CommandReturnObject &result,
2582 bool is_repl) {
2583 if (m_skip_lldbinit_files) {
2584 result.SetStatus(eReturnStatusSuccessFinishNoResult);
2585 return;
2586 }
2587
2588 llvm::SmallString<128> init_file;
2589
2590 if (is_repl)
2591 GetHomeREPLInitFile(init_file, language: GetDebugger().GetREPLLanguage());
2592
2593 if (init_file.empty())
2594 GetHomeInitFile(init_file);
2595
2596 if (!m_skip_app_init_files) {
2597 llvm::StringRef program_name =
2598 HostInfo::GetProgramFileSpec().GetFilename().GetStringRef();
2599 llvm::SmallString<128> program_init_file;
2600 GetHomeInitFile(init_file&: program_init_file, suffix: program_name);
2601 if (FileSystem::Instance().Exists(path: program_init_file))
2602 init_file = program_init_file;
2603 }
2604
2605 SourceInitFile(file: FileSpec(init_file.str()), result);
2606}
2607
2608void CommandInterpreter::SourceInitFileGlobal(CommandReturnObject &result) {
2609#ifdef LLDB_GLOBAL_INIT_DIRECTORY
2610 if (!m_skip_lldbinit_files) {
2611 FileSpec init_file(LLDB_GLOBAL_INIT_DIRECTORY);
2612 if (init_file)
2613 init_file.MakeAbsolute(HostInfo::GetShlibDir());
2614
2615 init_file.AppendPathComponent("lldbinit");
2616 SourceInitFile(init_file, result);
2617 return;
2618 }
2619#endif
2620 result.SetStatus(eReturnStatusSuccessFinishNoResult);
2621}
2622
2623const char *CommandInterpreter::GetCommandPrefix() {
2624 const char *prefix = GetDebugger().GetIOHandlerCommandPrefix();
2625 return prefix == nullptr ? "" : prefix;
2626}
2627
2628PlatformSP CommandInterpreter::GetPlatform(bool prefer_target_platform) {
2629 PlatformSP platform_sp;
2630 if (prefer_target_platform) {
2631 ExecutionContext exe_ctx(GetExecutionContext());
2632 Target *target = exe_ctx.GetTargetPtr();
2633 if (target)
2634 platform_sp = target->GetPlatform();
2635 }
2636
2637 if (!platform_sp)
2638 platform_sp = m_debugger.GetPlatformList().GetSelectedPlatform();
2639 return platform_sp;
2640}
2641
2642bool CommandInterpreter::DidProcessStopAbnormally() const {
2643 auto exe_ctx = GetExecutionContext();
2644 TargetSP target_sp = exe_ctx.GetTargetSP();
2645 if (!target_sp)
2646 return false;
2647
2648 ProcessSP process_sp(target_sp->GetProcessSP());
2649 if (!process_sp)
2650 return false;
2651
2652 if (eStateStopped != process_sp->GetState())
2653 return false;
2654
2655 for (const auto &thread_sp : process_sp->GetThreadList().Threads()) {
2656 StopInfoSP stop_info = thread_sp->GetStopInfo();
2657 if (!stop_info) {
2658 // If there's no stop_info, keep iterating through the other threads;
2659 // it's enough that any thread has got a stop_info that indicates
2660 // an abnormal stop, to consider the process to be stopped abnormally.
2661 continue;
2662 }
2663
2664 const StopReason reason = stop_info->GetStopReason();
2665 if (reason == eStopReasonException ||
2666 reason == eStopReasonInstrumentation ||
2667 reason == eStopReasonProcessorTrace || reason == eStopReasonInterrupt ||
2668 reason == eStopReasonHistoryBoundary)
2669 return true;
2670
2671 if (reason == eStopReasonSignal) {
2672 const auto stop_signal = static_cast<int32_t>(stop_info->GetValue());
2673 UnixSignalsSP signals_sp = process_sp->GetUnixSignals();
2674 if (!signals_sp || !signals_sp->SignalIsValid(signo: stop_signal))
2675 // The signal is unknown, treat it as abnormal.
2676 return true;
2677
2678 const auto sigint_num = signals_sp->GetSignalNumberFromName(name: "SIGINT");
2679 const auto sigstop_num = signals_sp->GetSignalNumberFromName(name: "SIGSTOP");
2680 if ((stop_signal != sigint_num) && (stop_signal != sigstop_num))
2681 // The signal very likely implies a crash.
2682 return true;
2683 }
2684 }
2685
2686 return false;
2687}
2688
2689void CommandInterpreter::HandleCommands(
2690 const StringList &commands, const ExecutionContext &override_context,
2691 const CommandInterpreterRunOptions &options, CommandReturnObject &result) {
2692
2693 OverrideExecutionContext(override_context);
2694 HandleCommands(commands, options, result);
2695 RestoreExecutionContext();
2696}
2697
2698void CommandInterpreter::HandleCommands(
2699 const StringList &commands, const CommandInterpreterRunOptions &options,
2700 CommandReturnObject &result) {
2701 size_t num_lines = commands.GetSize();
2702
2703 // If we are going to continue past a "continue" then we need to run the
2704 // commands synchronously. Make sure you reset this value anywhere you return
2705 // from the function.
2706
2707 bool old_async_execution = m_debugger.GetAsyncExecution();
2708
2709 if (!options.GetStopOnContinue()) {
2710 m_debugger.SetAsyncExecution(false);
2711 }
2712
2713 for (size_t idx = 0; idx < num_lines; idx++) {
2714 const char *cmd = commands.GetStringAtIndex(idx);
2715 if (cmd[0] == '\0')
2716 continue;
2717
2718 if (options.GetEchoCommands()) {
2719 // TODO: Add Stream support.
2720 result.AppendMessageWithFormat(format: "%s %s\n",
2721 m_debugger.GetPrompt().str().c_str(), cmd);
2722 }
2723
2724 CommandReturnObject tmp_result(m_debugger.GetUseColor());
2725 tmp_result.SetInteractive(result.GetInteractive());
2726 tmp_result.SetSuppressImmediateOutput(true);
2727
2728 // We might call into a regex or alias command, in which case the
2729 // add_to_history will get lost. This m_command_source_depth dingus is the
2730 // way we turn off adding to the history in that case, so set it up here.
2731 if (!options.GetAddToHistory())
2732 m_command_source_depth++;
2733 bool success = HandleCommand(command_line: cmd, lazy_add_to_history: options.m_add_to_history, result&: tmp_result);
2734 if (!options.GetAddToHistory())
2735 m_command_source_depth--;
2736
2737 if (options.GetPrintResults()) {
2738 if (tmp_result.Succeeded())
2739 result.AppendMessage(in_string: tmp_result.GetOutputString());
2740 }
2741
2742 if (!success || !tmp_result.Succeeded()) {
2743 std::string error_msg = tmp_result.GetErrorString();
2744 if (error_msg.empty())
2745 error_msg = "<unknown error>.\n";
2746 if (options.GetStopOnError()) {
2747 result.AppendErrorWithFormatv(format: "Aborting reading of commands after "
2748 "command #{0}: '{1}' failed with {2}",
2749 args: (uint64_t)idx, args&: cmd, args&: error_msg);
2750 m_debugger.SetAsyncExecution(old_async_execution);
2751 return;
2752 }
2753 if (options.GetPrintResults()) {
2754 result.AppendMessageWithFormatv(format: "Command #{0} '{1}' failed with {2}",
2755 args: (uint64_t)idx + 1, args&: cmd, args&: error_msg);
2756 }
2757 }
2758
2759 if (result.GetImmediateOutputStream())
2760 result.GetImmediateOutputStream()->Flush();
2761
2762 if (result.GetImmediateErrorStream())
2763 result.GetImmediateErrorStream()->Flush();
2764
2765 // N.B. Can't depend on DidChangeProcessState, because the state coming
2766 // into the command execution could be running (for instance in Breakpoint
2767 // Commands. So we check the return value to see if it is has running in
2768 // it.
2769 if ((tmp_result.GetStatus() == eReturnStatusSuccessContinuingNoResult) ||
2770 (tmp_result.GetStatus() == eReturnStatusSuccessContinuingResult)) {
2771 if (options.GetStopOnContinue()) {
2772 // If we caused the target to proceed, and we're going to stop in that
2773 // case, set the status in our real result before returning. This is
2774 // an error if the continue was not the last command in the set of
2775 // commands to be run.
2776 if (idx != num_lines - 1)
2777 result.AppendErrorWithFormat(
2778 format: "Aborting reading of commands after command #%" PRIu64
2779 ": '%s' continued the target.\n",
2780 (uint64_t)idx + 1, cmd);
2781 else
2782 result.AppendMessageWithFormat(format: "Command #%" PRIu64
2783 " '%s' continued the target.\n",
2784 (uint64_t)idx + 1, cmd);
2785
2786 result.SetStatus(tmp_result.GetStatus());
2787 m_debugger.SetAsyncExecution(old_async_execution);
2788
2789 return;
2790 }
2791 }
2792
2793 // Also check for "stop on crash here:
2794 if (tmp_result.GetDidChangeProcessState() && options.GetStopOnCrash() &&
2795 DidProcessStopAbnormally()) {
2796 if (idx != num_lines - 1)
2797 result.AppendErrorWithFormat(
2798 format: "Aborting reading of commands after command #%" PRIu64
2799 ": '%s' stopped with a signal or exception.\n",
2800 (uint64_t)idx + 1, cmd);
2801 else
2802 result.AppendMessageWithFormat(
2803 format: "Command #%" PRIu64 " '%s' stopped with a signal or exception.\n",
2804 (uint64_t)idx + 1, cmd);
2805
2806 result.SetStatus(tmp_result.GetStatus());
2807 m_debugger.SetAsyncExecution(old_async_execution);
2808
2809 return;
2810 }
2811 }
2812
2813 result.SetStatus(eReturnStatusSuccessFinishResult);
2814 m_debugger.SetAsyncExecution(old_async_execution);
2815}
2816
2817// Make flags that we can pass into the IOHandler so our delegates can do the
2818// right thing
2819enum {
2820 eHandleCommandFlagStopOnContinue = (1u << 0),
2821 eHandleCommandFlagStopOnError = (1u << 1),
2822 eHandleCommandFlagEchoCommand = (1u << 2),
2823 eHandleCommandFlagEchoCommentCommand = (1u << 3),
2824 eHandleCommandFlagPrintResult = (1u << 4),
2825 eHandleCommandFlagPrintErrors = (1u << 5),
2826 eHandleCommandFlagStopOnCrash = (1u << 6),
2827 eHandleCommandFlagAllowRepeats = (1u << 7)
2828};
2829
2830void CommandInterpreter::HandleCommandsFromFile(
2831 FileSpec &cmd_file, const ExecutionContext &context,
2832 const CommandInterpreterRunOptions &options, CommandReturnObject &result) {
2833 OverrideExecutionContext(override_context: context);
2834 HandleCommandsFromFile(file&: cmd_file, options, result);
2835 RestoreExecutionContext();
2836}
2837
2838void CommandInterpreter::HandleCommandsFromFile(
2839 FileSpec &cmd_file, const CommandInterpreterRunOptions &options,
2840 CommandReturnObject &result) {
2841 if (!FileSystem::Instance().Exists(file_spec: cmd_file)) {
2842 result.AppendErrorWithFormat(
2843 format: "Error reading commands from file %s - file not found.\n",
2844 cmd_file.GetFilename().AsCString(value_if_empty: "<Unknown>"));
2845 return;
2846 }
2847
2848 std::string cmd_file_path = cmd_file.GetPath();
2849 auto input_file_up =
2850 FileSystem::Instance().Open(file_spec: cmd_file, options: File::eOpenOptionReadOnly);
2851 if (!input_file_up) {
2852 std::string error = llvm::toString(E: input_file_up.takeError());
2853 result.AppendErrorWithFormatv(
2854 format: "error: an error occurred read file '{0}': {1}\n", args&: cmd_file_path,
2855 args: llvm::fmt_consume(Item: input_file_up.takeError()));
2856 return;
2857 }
2858 FileSP input_file_sp = FileSP(std::move(input_file_up.get()));
2859
2860 Debugger &debugger = GetDebugger();
2861
2862 uint32_t flags = 0;
2863
2864 if (options.m_stop_on_continue == eLazyBoolCalculate) {
2865 if (m_command_source_flags.empty()) {
2866 // Stop on continue by default
2867 flags |= eHandleCommandFlagStopOnContinue;
2868 } else if (m_command_source_flags.back() &
2869 eHandleCommandFlagStopOnContinue) {
2870 flags |= eHandleCommandFlagStopOnContinue;
2871 }
2872 } else if (options.m_stop_on_continue == eLazyBoolYes) {
2873 flags |= eHandleCommandFlagStopOnContinue;
2874 }
2875
2876 if (options.m_stop_on_error == eLazyBoolCalculate) {
2877 if (m_command_source_flags.empty()) {
2878 if (GetStopCmdSourceOnError())
2879 flags |= eHandleCommandFlagStopOnError;
2880 } else if (m_command_source_flags.back() & eHandleCommandFlagStopOnError) {
2881 flags |= eHandleCommandFlagStopOnError;
2882 }
2883 } else if (options.m_stop_on_error == eLazyBoolYes) {
2884 flags |= eHandleCommandFlagStopOnError;
2885 }
2886
2887 // stop-on-crash can only be set, if it is present in all levels of
2888 // pushed flag sets.
2889 if (options.GetStopOnCrash()) {
2890 if (m_command_source_flags.empty()) {
2891 flags |= eHandleCommandFlagStopOnCrash;
2892 } else if (m_command_source_flags.back() & eHandleCommandFlagStopOnCrash) {
2893 flags |= eHandleCommandFlagStopOnCrash;
2894 }
2895 }
2896
2897 if (options.m_echo_commands == eLazyBoolCalculate) {
2898 if (m_command_source_flags.empty()) {
2899 // Echo command by default
2900 flags |= eHandleCommandFlagEchoCommand;
2901 } else if (m_command_source_flags.back() & eHandleCommandFlagEchoCommand) {
2902 flags |= eHandleCommandFlagEchoCommand;
2903 }
2904 } else if (options.m_echo_commands == eLazyBoolYes) {
2905 flags |= eHandleCommandFlagEchoCommand;
2906 }
2907
2908 // We will only ever ask for this flag, if we echo commands in general.
2909 if (options.m_echo_comment_commands == eLazyBoolCalculate) {
2910 if (m_command_source_flags.empty()) {
2911 // Echo comments by default
2912 flags |= eHandleCommandFlagEchoCommentCommand;
2913 } else if (m_command_source_flags.back() &
2914 eHandleCommandFlagEchoCommentCommand) {
2915 flags |= eHandleCommandFlagEchoCommentCommand;
2916 }
2917 } else if (options.m_echo_comment_commands == eLazyBoolYes) {
2918 flags |= eHandleCommandFlagEchoCommentCommand;
2919 }
2920
2921 if (options.m_print_results == eLazyBoolCalculate) {
2922 if (m_command_source_flags.empty()) {
2923 // Print output by default
2924 flags |= eHandleCommandFlagPrintResult;
2925 } else if (m_command_source_flags.back() & eHandleCommandFlagPrintResult) {
2926 flags |= eHandleCommandFlagPrintResult;
2927 }
2928 } else if (options.m_print_results == eLazyBoolYes) {
2929 flags |= eHandleCommandFlagPrintResult;
2930 }
2931
2932 if (options.m_print_errors == eLazyBoolCalculate) {
2933 if (m_command_source_flags.empty()) {
2934 // Print output by default
2935 flags |= eHandleCommandFlagPrintErrors;
2936 } else if (m_command_source_flags.back() & eHandleCommandFlagPrintErrors) {
2937 flags |= eHandleCommandFlagPrintErrors;
2938 }
2939 } else if (options.m_print_errors == eLazyBoolYes) {
2940 flags |= eHandleCommandFlagPrintErrors;
2941 }
2942
2943 if (flags & eHandleCommandFlagPrintResult) {
2944 debugger.GetOutputFileSP()->Printf(format: "Executing commands in '%s'.\n",
2945 cmd_file_path.c_str());
2946 }
2947
2948 // Used for inheriting the right settings when "command source" might
2949 // have nested "command source" commands
2950 lldb::LockableStreamFileSP empty_stream_sp;
2951 m_command_source_flags.push_back(x: flags);
2952 IOHandlerSP io_handler_sp(new IOHandlerEditline(
2953 debugger, IOHandler::Type::CommandInterpreter, input_file_sp,
2954 empty_stream_sp, // Pass in an empty stream so we inherit the top
2955 // input reader output stream
2956 empty_stream_sp, // Pass in an empty stream so we inherit the top
2957 // input reader error stream
2958 flags,
2959 nullptr, // Pass in NULL for "editline_name" so no history is saved,
2960 // or written
2961 debugger.GetPrompt(), llvm::StringRef(),
2962 false, // Not multi-line
2963 debugger.GetUseColor(), 0, *this));
2964 const bool old_async_execution = debugger.GetAsyncExecution();
2965
2966 // Set synchronous execution if we are not stopping on continue
2967 if ((flags & eHandleCommandFlagStopOnContinue) == 0)
2968 debugger.SetAsyncExecution(false);
2969
2970 m_command_source_depth++;
2971 m_command_source_dirs.push_back(x: cmd_file.CopyByRemovingLastPathComponent());
2972
2973 debugger.RunIOHandlerSync(reader_sp: io_handler_sp);
2974 if (!m_command_source_flags.empty())
2975 m_command_source_flags.pop_back();
2976
2977 m_command_source_dirs.pop_back();
2978 m_command_source_depth--;
2979
2980 result.SetStatus(eReturnStatusSuccessFinishNoResult);
2981 debugger.SetAsyncExecution(old_async_execution);
2982}
2983
2984bool CommandInterpreter::GetSynchronous() { return m_synchronous_execution; }
2985
2986void CommandInterpreter::SetSynchronous(bool value) {
2987 m_synchronous_execution = value;
2988}
2989
2990void CommandInterpreter::OutputFormattedHelpText(Stream &strm,
2991 llvm::StringRef prefix,
2992 llvm::StringRef help_text) {
2993 const uint32_t max_columns = m_debugger.GetTerminalWidth();
2994
2995 size_t line_width_max = max_columns - prefix.size();
2996 if (line_width_max < 16)
2997 line_width_max = help_text.size() + prefix.size();
2998
2999 strm.IndentMore(amount: prefix.size());
3000 bool prefixed_yet = false;
3001 // Even if we have no help text we still want to emit the command name.
3002 if (help_text.empty())
3003 help_text = "No help text";
3004 while (!help_text.empty()) {
3005 // Prefix the first line, indent subsequent lines to line up
3006 if (!prefixed_yet) {
3007 strm << prefix;
3008 prefixed_yet = true;
3009 } else
3010 strm.Indent();
3011
3012 // Never print more than the maximum on one line.
3013 llvm::StringRef this_line = help_text.substr(Start: 0, N: line_width_max);
3014
3015 // Always break on an explicit newline.
3016 std::size_t first_newline = this_line.find_first_of(Chars: "\n");
3017
3018 // Don't break on space/tab unless the text is too long to fit on one line.
3019 std::size_t last_space = llvm::StringRef::npos;
3020 if (this_line.size() != help_text.size())
3021 last_space = this_line.find_last_of(Chars: " \t");
3022
3023 // Break at whichever condition triggered first.
3024 this_line = this_line.substr(Start: 0, N: std::min(a: first_newline, b: last_space));
3025 strm.PutCString(cstr: this_line);
3026 strm.EOL();
3027
3028 // Remove whitespace / newlines after breaking.
3029 help_text = help_text.drop_front(N: this_line.size()).ltrim();
3030 }
3031 strm.IndentLess(amount: prefix.size());
3032}
3033
3034void CommandInterpreter::OutputFormattedHelpText(Stream &strm,
3035 llvm::StringRef word_text,
3036 llvm::StringRef separator,
3037 llvm::StringRef help_text,
3038 size_t max_word_len) {
3039 StreamString prefix_stream;
3040 prefix_stream.Printf(format: " %-*s %*s ", (int)max_word_len, word_text.data(),
3041 (int)separator.size(), separator.data());
3042 OutputFormattedHelpText(strm, prefix: prefix_stream.GetString(), help_text);
3043}
3044
3045void CommandInterpreter::OutputHelpText(Stream &strm, llvm::StringRef word_text,
3046 llvm::StringRef separator,
3047 llvm::StringRef help_text,
3048 uint32_t max_word_len) {
3049 int indent_size = max_word_len + separator.size() + 2;
3050
3051 strm.IndentMore(amount: indent_size);
3052
3053 StreamString text_strm;
3054 text_strm.Printf(format: "%-*s ", (int)max_word_len, word_text.data());
3055 text_strm << separator << " " << help_text;
3056
3057 const uint32_t max_columns = m_debugger.GetTerminalWidth();
3058
3059 llvm::StringRef text = text_strm.GetString();
3060
3061 uint32_t chars_left = max_columns;
3062
3063 auto nextWordLength = [](llvm::StringRef S) {
3064 size_t pos = S.find(C: ' ');
3065 return pos == llvm::StringRef::npos ? S.size() : pos;
3066 };
3067
3068 while (!text.empty()) {
3069 if (text.front() == '\n' ||
3070 (text.front() == ' ' && nextWordLength(text.ltrim(Char: ' ')) > chars_left)) {
3071 strm.EOL();
3072 strm.Indent();
3073 chars_left = max_columns - indent_size;
3074 if (text.front() == '\n')
3075 text = text.drop_front();
3076 else
3077 text = text.ltrim(Char: ' ');
3078 } else {
3079 strm.PutChar(ch: text.front());
3080 --chars_left;
3081 text = text.drop_front();
3082 }
3083 }
3084
3085 strm.EOL();
3086 strm.IndentLess(amount: indent_size);
3087}
3088
3089void CommandInterpreter::FindCommandsForApropos(
3090 llvm::StringRef search_word, StringList &commands_found,
3091 StringList &commands_help, const CommandObject::CommandMap &command_map) {
3092 for (const auto &pair : command_map) {
3093 llvm::StringRef command_name = pair.first;
3094 CommandObject *cmd_obj = pair.second.get();
3095
3096 const bool search_short_help = true;
3097 const bool search_long_help = false;
3098 const bool search_syntax = false;
3099 const bool search_options = false;
3100 if (command_name.contains_insensitive(Other: search_word) ||
3101 cmd_obj->HelpTextContainsWord(search_word, search_short_help,
3102 search_long_help, search_syntax,
3103 search_options)) {
3104 commands_found.AppendString(str: command_name);
3105 commands_help.AppendString(str: cmd_obj->GetHelp());
3106 }
3107
3108 if (auto *multiword_cmd = cmd_obj->GetAsMultiwordCommand()) {
3109 StringList subcommands_found;
3110 FindCommandsForApropos(search_word, commands_found&: subcommands_found, commands_help,
3111 command_map: multiword_cmd->GetSubcommandDictionary());
3112 for (const auto &subcommand_name : subcommands_found) {
3113 std::string qualified_name =
3114 (command_name + " " + subcommand_name).str();
3115 commands_found.AppendString(s: qualified_name);
3116 }
3117 }
3118 }
3119}
3120
3121void CommandInterpreter::FindCommandsForApropos(llvm::StringRef search_word,
3122 StringList &commands_found,
3123 StringList &commands_help,
3124 bool search_builtin_commands,
3125 bool search_user_commands,
3126 bool search_alias_commands,
3127 bool search_user_mw_commands) {
3128 CommandObject::CommandMap::const_iterator pos;
3129
3130 if (search_builtin_commands)
3131 FindCommandsForApropos(search_word, commands_found, commands_help,
3132 command_map: m_command_dict);
3133
3134 if (search_user_commands)
3135 FindCommandsForApropos(search_word, commands_found, commands_help,
3136 command_map: m_user_dict);
3137
3138 if (search_user_mw_commands)
3139 FindCommandsForApropos(search_word, commands_found, commands_help,
3140 command_map: m_user_mw_dict);
3141
3142 if (search_alias_commands)
3143 FindCommandsForApropos(search_word, commands_found, commands_help,
3144 command_map: m_alias_dict);
3145}
3146
3147ExecutionContext CommandInterpreter::GetExecutionContext() const {
3148 return !m_overriden_exe_contexts.empty()
3149 ? m_overriden_exe_contexts.top()
3150 : m_debugger.GetSelectedExecutionContext();
3151}
3152
3153void CommandInterpreter::OverrideExecutionContext(
3154 const ExecutionContext &override_context) {
3155 m_overriden_exe_contexts.push(x: override_context);
3156}
3157
3158void CommandInterpreter::RestoreExecutionContext() {
3159 if (!m_overriden_exe_contexts.empty())
3160 m_overriden_exe_contexts.pop();
3161}
3162
3163void CommandInterpreter::GetProcessOutput() {
3164 if (ProcessSP process_sp = GetExecutionContext().GetProcessSP())
3165 m_debugger.FlushProcessOutput(process&: *process_sp, /*flush_stdout*/ true,
3166 /*flush_stderr*/ true);
3167}
3168
3169void CommandInterpreter::StartHandlingCommand() {
3170 auto idle_state = CommandHandlingState::eIdle;
3171 if (m_command_state.compare_exchange_strong(
3172 e&: idle_state, i: CommandHandlingState::eInProgress))
3173 lldbassert(m_iohandler_nesting_level == 0);
3174 else
3175 lldbassert(m_iohandler_nesting_level > 0);
3176 ++m_iohandler_nesting_level;
3177}
3178
3179void CommandInterpreter::FinishHandlingCommand() {
3180 lldbassert(m_iohandler_nesting_level > 0);
3181 if (--m_iohandler_nesting_level == 0) {
3182 auto prev_state = m_command_state.exchange(i: CommandHandlingState::eIdle);
3183 lldbassert(prev_state != CommandHandlingState::eIdle);
3184 }
3185}
3186
3187bool CommandInterpreter::InterruptCommand() {
3188 auto in_progress = CommandHandlingState::eInProgress;
3189 return m_command_state.compare_exchange_strong(
3190 e&: in_progress, i: CommandHandlingState::eInterrupted);
3191}
3192
3193bool CommandInterpreter::WasInterrupted() const {
3194 if (!m_debugger.IsIOHandlerThreadCurrentThread())
3195 return false;
3196
3197 bool was_interrupted =
3198 (m_command_state == CommandHandlingState::eInterrupted);
3199 lldbassert(!was_interrupted || m_iohandler_nesting_level > 0);
3200 return was_interrupted;
3201}
3202
3203void CommandInterpreter::PrintCommandOutput(IOHandler &io_handler,
3204 llvm::StringRef str,
3205 bool is_stdout) {
3206
3207 lldb::LockableStreamFileSP stream = is_stdout
3208 ? io_handler.GetOutputStreamFileSP()
3209 : io_handler.GetErrorStreamFileSP();
3210 // Split the output into lines and poll for interrupt requests
3211 bool had_output = !str.empty();
3212 while (!str.empty()) {
3213 llvm::StringRef line;
3214 std::tie(args&: line, args&: str) = str.split(Separator: '\n');
3215 {
3216 LockedStreamFile stream_file = stream->Lock();
3217 stream_file.Write(src: line.data(), src_len: line.size());
3218 stream_file.Write(src: "\n", src_len: 1);
3219 }
3220 }
3221
3222 LockedStreamFile stream_file = stream->Lock();
3223 if (had_output &&
3224 INTERRUPT_REQUESTED(GetDebugger(), "Interrupted dumping command output"))
3225 stream_file.Printf(format: "\n... Interrupted.\n");
3226 stream_file.Flush();
3227}
3228
3229bool CommandInterpreter::EchoCommandNonInteractive(
3230 llvm::StringRef line, const Flags &io_handler_flags) const {
3231 if (!io_handler_flags.Test(bit: eHandleCommandFlagEchoCommand))
3232 return false;
3233
3234 llvm::StringRef command = line.trim();
3235 if (command.empty())
3236 return true;
3237
3238 if (command.front() == m_comment_char)
3239 return io_handler_flags.Test(bit: eHandleCommandFlagEchoCommentCommand);
3240
3241 return true;
3242}
3243
3244void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler,
3245 std::string &line) {
3246 // If we were interrupted, bail out...
3247 if (WasInterrupted())
3248 return;
3249
3250 const bool is_interactive = io_handler.GetIsInteractive();
3251 const bool allow_repeats =
3252 io_handler.GetFlags().Test(bit: eHandleCommandFlagAllowRepeats);
3253
3254 if (!is_interactive && !allow_repeats) {
3255 // When we are not interactive, don't execute blank lines. This will happen
3256 // sourcing a commands file. We don't want blank lines to repeat the
3257 // previous command and cause any errors to occur (like redefining an
3258 // alias, get an error and stop parsing the commands file).
3259 // But obey the AllowRepeats flag if the user has set it.
3260 if (line.empty())
3261 return;
3262 }
3263 if (!is_interactive) {
3264 // When using a non-interactive file handle (like when sourcing commands
3265 // from a file) we need to echo the command out so we don't just see the
3266 // command output and no command...
3267 if (EchoCommandNonInteractive(line, io_handler_flags: io_handler.GetFlags())) {
3268 LockedStreamFile locked_stream =
3269 io_handler.GetOutputStreamFileSP()->Lock();
3270 locked_stream.Printf(format: "%s%s\n", io_handler.GetPrompt(), line.c_str());
3271 }
3272 }
3273
3274 StartHandlingCommand();
3275
3276 ExecutionContext exe_ctx = m_debugger.GetSelectedExecutionContext();
3277 bool pushed_exe_ctx = false;
3278 if (exe_ctx.HasTargetScope()) {
3279 OverrideExecutionContext(override_context: exe_ctx);
3280 pushed_exe_ctx = true;
3281 }
3282 auto finalize = llvm::make_scope_exit(F: [this, pushed_exe_ctx]() {
3283 if (pushed_exe_ctx)
3284 RestoreExecutionContext();
3285 });
3286
3287 lldb_private::CommandReturnObject result(m_debugger.GetUseColor());
3288 HandleCommand(command_line: line.c_str(), lazy_add_to_history: eLazyBoolCalculate, result);
3289
3290 // Now emit the command output text from the command we just executed
3291 if ((result.Succeeded() &&
3292 io_handler.GetFlags().Test(bit: eHandleCommandFlagPrintResult)) ||
3293 io_handler.GetFlags().Test(bit: eHandleCommandFlagPrintErrors)) {
3294 auto DefaultPrintCallback = [&](const CommandReturnObject &result) {
3295 // Display any inline diagnostics first.
3296 const bool inline_diagnostics = !result.GetImmediateErrorStream() &&
3297 GetDebugger().GetShowInlineDiagnostics();
3298 if (inline_diagnostics) {
3299 unsigned prompt_len = m_debugger.GetPrompt().size();
3300 if (auto indent = result.GetDiagnosticIndent()) {
3301 std::string diags =
3302 result.GetInlineDiagnosticString(indent: prompt_len + *indent);
3303 PrintCommandOutput(io_handler, str: diags, is_stdout: true);
3304 }
3305 }
3306
3307 // Display any STDOUT/STDERR _prior_ to emitting the command result text.
3308 GetProcessOutput();
3309
3310 if (!result.GetImmediateOutputStream()) {
3311 llvm::StringRef output = result.GetOutputString();
3312 PrintCommandOutput(io_handler, str: output, is_stdout: true);
3313 }
3314
3315 // Now emit the command error text from the command we just executed.
3316 if (!result.GetImmediateErrorStream()) {
3317 std::string error = result.GetErrorString(with_diagnostics: !inline_diagnostics);
3318 PrintCommandOutput(io_handler, str: error, is_stdout: false);
3319 }
3320 };
3321
3322 if (m_print_callback) {
3323 const auto callback_result = m_print_callback(result);
3324 if (callback_result == eCommandReturnObjectPrintCallbackSkipped)
3325 DefaultPrintCallback(result);
3326 } else {
3327 DefaultPrintCallback(result);
3328 }
3329 }
3330
3331 FinishHandlingCommand();
3332
3333 switch (result.GetStatus()) {
3334 case eReturnStatusInvalid:
3335 case eReturnStatusSuccessFinishNoResult:
3336 case eReturnStatusSuccessFinishResult:
3337 case eReturnStatusStarted:
3338 break;
3339
3340 case eReturnStatusSuccessContinuingNoResult:
3341 case eReturnStatusSuccessContinuingResult:
3342 if (io_handler.GetFlags().Test(bit: eHandleCommandFlagStopOnContinue))
3343 io_handler.SetIsDone(true);
3344 break;
3345
3346 case eReturnStatusFailed:
3347 m_result.IncrementNumberOfErrors();
3348 if (io_handler.GetFlags().Test(bit: eHandleCommandFlagStopOnError)) {
3349 m_result.SetResult(lldb::eCommandInterpreterResultCommandError);
3350 io_handler.SetIsDone(true);
3351 }
3352 break;
3353
3354 case eReturnStatusQuit:
3355 m_result.SetResult(lldb::eCommandInterpreterResultQuitRequested);
3356 io_handler.SetIsDone(true);
3357 break;
3358 }
3359
3360 // Finally, if we're going to stop on crash, check that here:
3361 if (m_result.IsResult(result: lldb::eCommandInterpreterResultSuccess) &&
3362 result.GetDidChangeProcessState() &&
3363 io_handler.GetFlags().Test(bit: eHandleCommandFlagStopOnCrash) &&
3364 DidProcessStopAbnormally()) {
3365 io_handler.SetIsDone(true);
3366 m_result.SetResult(lldb::eCommandInterpreterResultInferiorCrash);
3367 }
3368}
3369
3370bool CommandInterpreter::IOHandlerInterrupt(IOHandler &io_handler) {
3371 ExecutionContext exe_ctx(GetExecutionContext());
3372 Process *process = exe_ctx.GetProcessPtr();
3373
3374 if (InterruptCommand())
3375 return true;
3376
3377 if (process) {
3378 StateType state = process->GetState();
3379 if (StateIsRunningState(state)) {
3380 process->Halt();
3381 return true; // Don't do any updating when we are running
3382 }
3383 }
3384
3385 ScriptInterpreter *script_interpreter =
3386 m_debugger.GetScriptInterpreter(can_create: false);
3387 if (script_interpreter) {
3388 if (script_interpreter->Interrupt())
3389 return true;
3390 }
3391 return false;
3392}
3393
3394bool CommandInterpreter::SaveTranscript(
3395 CommandReturnObject &result, std::optional<std::string> output_file) {
3396 if (output_file == std::nullopt || output_file->empty()) {
3397 std::string now = llvm::to_string(Value: std::chrono::system_clock::now());
3398 llvm::replace(Range&: now, OldValue: ' ', NewValue: '_');
3399 // Can't have file name with colons on Windows
3400 llvm::replace(Range&: now, OldValue: ':', NewValue: '-');
3401 const std::string file_name = "lldb_session_" + now + ".log";
3402
3403 FileSpec save_location = GetSaveSessionDirectory();
3404
3405 if (!save_location)
3406 save_location = HostInfo::GetGlobalTempDir();
3407
3408 FileSystem::Instance().Resolve(file_spec&: save_location);
3409 save_location.AppendPathComponent(component: file_name);
3410 output_file = save_location.GetPath();
3411 }
3412
3413 auto error_out = [&](llvm::StringRef error_message, std::string description) {
3414 LLDB_LOG(GetLog(LLDBLog::Commands), "{0} ({1}:{2})", error_message,
3415 output_file, description);
3416 result.AppendErrorWithFormatv(
3417 format: "Failed to save session's transcripts to {0}!", args&: *output_file);
3418 return false;
3419 };
3420
3421 File::OpenOptions flags = File::eOpenOptionWriteOnly |
3422 File::eOpenOptionCanCreate |
3423 File::eOpenOptionTruncate;
3424
3425 auto opened_file = FileSystem::Instance().Open(file_spec: FileSpec(*output_file), options: flags);
3426
3427 if (!opened_file)
3428 return error_out("Unable to create file",
3429 llvm::toString(E: opened_file.takeError()));
3430
3431 FileUP file = std::move(opened_file.get());
3432
3433 size_t byte_size = m_transcript_stream.GetSize();
3434
3435 Status error = file->Write(buf: m_transcript_stream.GetData(), num_bytes&: byte_size);
3436
3437 if (error.Fail() || byte_size != m_transcript_stream.GetSize())
3438 return error_out("Unable to write to destination file",
3439 "Bytes written do not match transcript size.");
3440
3441 result.SetStatus(eReturnStatusSuccessFinishNoResult);
3442 result.AppendMessageWithFormat(format: "Session's transcripts saved to %s\n",
3443 output_file->c_str());
3444 if (!GetSaveTranscript())
3445 result.AppendError(
3446 in_string: "Note: the setting interpreter.save-transcript is set to false, so the "
3447 "transcript might not have been recorded.");
3448
3449 if (GetOpenTranscriptInEditor() && Host::IsInteractiveGraphicSession()) {
3450 const FileSpec file_spec;
3451 error = file->GetFileSpec(file_spec&: const_cast<FileSpec &>(file_spec));
3452 if (error.Success()) {
3453 if (llvm::Error e = Host::OpenFileInExternalEditor(
3454 editor: m_debugger.GetExternalEditor(), file_spec, line_no: 1))
3455 result.AppendError(in_string: llvm::toString(E: std::move(e)));
3456 }
3457 }
3458
3459 return true;
3460}
3461
3462bool CommandInterpreter::IsInteractive() {
3463 return (GetIOHandler() ? GetIOHandler()->GetIsInteractive() : false);
3464}
3465
3466FileSpec CommandInterpreter::GetCurrentSourceDir() {
3467 if (m_command_source_dirs.empty())
3468 return {};
3469 return m_command_source_dirs.back();
3470}
3471
3472void CommandInterpreter::GetLLDBCommandsFromIOHandler(
3473 const char *prompt, IOHandlerDelegate &delegate, void *baton) {
3474 Debugger &debugger = GetDebugger();
3475 IOHandlerSP io_handler_sp(
3476 new IOHandlerEditline(debugger, IOHandler::Type::CommandList,
3477 "lldb", // Name of input reader for history
3478 llvm::StringRef(prompt), // Prompt
3479 llvm::StringRef(), // Continuation prompt
3480 true, // Get multiple lines
3481 debugger.GetUseColor(),
3482 0, // Don't show line numbers
3483 delegate)); // IOHandlerDelegate
3484
3485 if (io_handler_sp) {
3486 io_handler_sp->SetUserData(baton);
3487 debugger.RunIOHandlerAsync(reader_sp: io_handler_sp);
3488 }
3489}
3490
3491void CommandInterpreter::GetPythonCommandsFromIOHandler(
3492 const char *prompt, IOHandlerDelegate &delegate, void *baton) {
3493 Debugger &debugger = GetDebugger();
3494 IOHandlerSP io_handler_sp(
3495 new IOHandlerEditline(debugger, IOHandler::Type::PythonCode,
3496 "lldb-python", // Name of input reader for history
3497 llvm::StringRef(prompt), // Prompt
3498 llvm::StringRef(), // Continuation prompt
3499 true, // Get multiple lines
3500 debugger.GetUseColor(),
3501 0, // Don't show line numbers
3502 delegate)); // IOHandlerDelegate
3503
3504 if (io_handler_sp) {
3505 io_handler_sp->SetUserData(baton);
3506 debugger.RunIOHandlerAsync(reader_sp: io_handler_sp);
3507 }
3508}
3509
3510bool CommandInterpreter::IsActive() {
3511 return m_debugger.IsTopIOHandler(reader_sp: m_command_io_handler_sp);
3512}
3513
3514lldb::IOHandlerSP
3515CommandInterpreter::GetIOHandler(bool force_create,
3516 CommandInterpreterRunOptions *options) {
3517 // Always re-create the IOHandlerEditline in case the input changed. The old
3518 // instance might have had a non-interactive input and now it does or vice
3519 // versa.
3520 if (force_create || !m_command_io_handler_sp) {
3521 // Always re-create the IOHandlerEditline in case the input changed. The
3522 // old instance might have had a non-interactive input and now it does or
3523 // vice versa.
3524 uint32_t flags = 0;
3525
3526 if (options) {
3527 if (options->m_stop_on_continue == eLazyBoolYes)
3528 flags |= eHandleCommandFlagStopOnContinue;
3529 if (options->m_stop_on_error == eLazyBoolYes)
3530 flags |= eHandleCommandFlagStopOnError;
3531 if (options->m_stop_on_crash == eLazyBoolYes)
3532 flags |= eHandleCommandFlagStopOnCrash;
3533 if (options->m_echo_commands != eLazyBoolNo)
3534 flags |= eHandleCommandFlagEchoCommand;
3535 if (options->m_echo_comment_commands != eLazyBoolNo)
3536 flags |= eHandleCommandFlagEchoCommentCommand;
3537 if (options->m_print_results != eLazyBoolNo)
3538 flags |= eHandleCommandFlagPrintResult;
3539 if (options->m_print_errors != eLazyBoolNo)
3540 flags |= eHandleCommandFlagPrintErrors;
3541 if (options->m_allow_repeats == eLazyBoolYes)
3542 flags |= eHandleCommandFlagAllowRepeats;
3543 } else {
3544 flags = eHandleCommandFlagEchoCommand | eHandleCommandFlagPrintResult |
3545 eHandleCommandFlagPrintErrors;
3546 }
3547
3548 m_command_io_handler_sp = std::make_shared<IOHandlerEditline>(
3549 args&: m_debugger, args: IOHandler::Type::CommandInterpreter,
3550 args: m_debugger.GetInputFileSP(), args: m_debugger.GetOutputStreamSP(),
3551 args: m_debugger.GetErrorStreamSP(), args&: flags, args: "lldb", args: m_debugger.GetPrompt(),
3552 args: llvm::StringRef(), // Continuation prompt
3553 args: false, // Don't enable multiple line input, just single line commands
3554 args: m_debugger.GetUseColor(),
3555 args: 0, // Don't show line numbers
3556 args&: *this); // IOHandlerDelegate
3557 }
3558 return m_command_io_handler_sp;
3559}
3560
3561CommandInterpreterRunResult CommandInterpreter::RunCommandInterpreter(
3562 CommandInterpreterRunOptions &options) {
3563 // Always re-create the command interpreter when we run it in case any file
3564 // handles have changed.
3565 bool force_create = true;
3566 m_debugger.RunIOHandlerAsync(reader_sp: GetIOHandler(force_create, options: &options));
3567 m_result = CommandInterpreterRunResult();
3568
3569 if (options.GetAutoHandleEvents())
3570 m_debugger.StartEventHandlerThread();
3571
3572 if (options.GetSpawnThread()) {
3573 m_debugger.StartIOHandlerThread();
3574 } else {
3575 // If the current thread is not managed by a host thread, we won't detect
3576 // that this IS the CommandInterpreter IOHandler thread, so make it so:
3577 HostThread new_io_handler_thread(Host::GetCurrentThread());
3578 HostThread old_io_handler_thread =
3579 m_debugger.SetIOHandlerThread(new_io_handler_thread);
3580 m_debugger.RunIOHandlers();
3581 m_debugger.SetIOHandlerThread(old_io_handler_thread);
3582
3583 if (options.GetAutoHandleEvents())
3584 m_debugger.StopEventHandlerThread();
3585 }
3586
3587 return m_result;
3588}
3589
3590CommandObject *
3591CommandInterpreter::ResolveCommandImpl(std::string &command_line,
3592 CommandReturnObject &result) {
3593 std::string scratch_command(command_line); // working copy so we don't modify
3594 // command_line unless we succeed
3595 CommandObject *cmd_obj = nullptr;
3596 StreamString revised_command_line;
3597 bool wants_raw_input = false;
3598 std::string next_word;
3599 StringList matches;
3600 bool done = false;
3601
3602 auto build_alias_cmd = [&](std::string &full_name) {
3603 revised_command_line.Clear();
3604 matches.Clear();
3605 std::string alias_result;
3606 cmd_obj =
3607 BuildAliasResult(alias_name: full_name, raw_input_string&: scratch_command, alias_result, result);
3608 revised_command_line.Printf(format: "%s", alias_result.c_str());
3609 if (cmd_obj) {
3610 wants_raw_input = cmd_obj->WantsRawCommandString();
3611 }
3612 };
3613
3614 while (!done) {
3615 char quote_char = '\0';
3616 std::string suffix;
3617 ExtractCommand(command_string&: scratch_command, command&: next_word, suffix, quote_char);
3618 if (cmd_obj == nullptr) {
3619 std::string full_name;
3620 bool is_alias = GetAliasFullName(cmd: next_word, full_name);
3621 cmd_obj = GetCommandObject(cmd_str: next_word, matches: &matches);
3622 bool is_real_command =
3623 (!is_alias) || (cmd_obj != nullptr && !cmd_obj->IsAlias());
3624 if (!is_real_command) {
3625 build_alias_cmd(full_name);
3626 } else {
3627 if (cmd_obj) {
3628 llvm::StringRef cmd_name = cmd_obj->GetCommandName();
3629 revised_command_line.Printf(format: "%s", cmd_name.str().c_str());
3630 wants_raw_input = cmd_obj->WantsRawCommandString();
3631 } else {
3632 revised_command_line.Printf(format: "%s", next_word.c_str());
3633 }
3634 }
3635 } else {
3636 if (cmd_obj->IsMultiwordObject()) {
3637 CommandObject *sub_cmd_obj =
3638 cmd_obj->GetSubcommandObject(sub_cmd: next_word.c_str());
3639 if (sub_cmd_obj) {
3640 // The subcommand's name includes the parent command's name, so
3641 // restart rather than append to the revised_command_line.
3642 llvm::StringRef sub_cmd_name = sub_cmd_obj->GetCommandName();
3643 revised_command_line.Clear();
3644 revised_command_line.Printf(format: "%s", sub_cmd_name.str().c_str());
3645 cmd_obj = sub_cmd_obj;
3646 wants_raw_input = cmd_obj->WantsRawCommandString();
3647 } else {
3648 if (quote_char)
3649 revised_command_line.Printf(format: " %c%s%s%c", quote_char,
3650 next_word.c_str(), suffix.c_str(),
3651 quote_char);
3652 else
3653 revised_command_line.Printf(format: " %s%s", next_word.c_str(),
3654 suffix.c_str());
3655 done = true;
3656 }
3657 } else {
3658 if (quote_char)
3659 revised_command_line.Printf(format: " %c%s%s%c", quote_char,
3660 next_word.c_str(), suffix.c_str(),
3661 quote_char);
3662 else
3663 revised_command_line.Printf(format: " %s%s", next_word.c_str(),
3664 suffix.c_str());
3665 done = true;
3666 }
3667 }
3668
3669 if (cmd_obj == nullptr) {
3670 const size_t num_matches = matches.GetSize();
3671 if (matches.GetSize() > 1) {
3672 StringList alias_matches;
3673 GetAliasCommandObject(cmd: next_word, matches: &alias_matches);
3674
3675 if (alias_matches.GetSize() == 1) {
3676 std::string full_name;
3677 GetAliasFullName(cmd: alias_matches.GetStringAtIndex(idx: 0), full_name);
3678 build_alias_cmd(full_name);
3679 done = static_cast<bool>(cmd_obj);
3680 } else {
3681 StreamString error_msg;
3682 error_msg.Printf(format: "Ambiguous command '%s'. Possible matches:\n",
3683 next_word.c_str());
3684
3685 for (uint32_t i = 0; i < num_matches; ++i) {
3686 error_msg.Printf(format: "\t%s\n", matches.GetStringAtIndex(idx: i));
3687 }
3688 result.AppendRawError(in_string: error_msg.GetString());
3689 }
3690 } else {
3691 // We didn't have only one match, otherwise we wouldn't get here.
3692 lldbassert(num_matches == 0);
3693 result.AppendErrorWithFormat(format: "'%s' is not a valid command.\n",
3694 next_word.c_str());
3695 }
3696 if (!done)
3697 return nullptr;
3698 }
3699
3700 if (cmd_obj->IsMultiwordObject()) {
3701 if (!suffix.empty()) {
3702 result.AppendErrorWithFormat(
3703 format: "command '%s' did not recognize '%s%s%s' as valid (subcommand "
3704 "might be invalid).\n",
3705 cmd_obj->GetCommandName().str().c_str(),
3706 next_word.empty() ? "" : next_word.c_str(),
3707 next_word.empty() ? " -- " : " ", suffix.c_str());
3708 return nullptr;
3709 }
3710 } else {
3711 // If we found a normal command, we are done
3712 done = true;
3713 if (!suffix.empty()) {
3714 switch (suffix[0]) {
3715 case '/':
3716 // GDB format suffixes
3717 {
3718 Options *command_options = cmd_obj->GetOptions();
3719 if (command_options &&
3720 command_options->SupportsLongOption(long_option: "gdb-format")) {
3721 std::string gdb_format_option("--gdb-format=");
3722 gdb_format_option += (suffix.c_str() + 1);
3723
3724 std::string cmd = std::string(revised_command_line.GetString());
3725 size_t arg_terminator_idx = FindArgumentTerminator(s: cmd);
3726 if (arg_terminator_idx != std::string::npos) {
3727 // Insert the gdb format option before the "--" that terminates
3728 // options
3729 gdb_format_option.append(n: 1, c: ' ');
3730 cmd.insert(pos1: arg_terminator_idx, str: gdb_format_option);
3731 revised_command_line.Clear();
3732 revised_command_line.PutCString(cstr: cmd);
3733 } else
3734 revised_command_line.Printf(format: " %s", gdb_format_option.c_str());
3735
3736 if (wants_raw_input &&
3737 FindArgumentTerminator(s: cmd) == std::string::npos)
3738 revised_command_line.PutCString(cstr: " --");
3739 } else {
3740 result.AppendErrorWithFormat(
3741 format: "the '%s' command doesn't support the --gdb-format option\n",
3742 cmd_obj->GetCommandName().str().c_str());
3743 return nullptr;
3744 }
3745 }
3746 break;
3747
3748 default:
3749 result.AppendErrorWithFormat(
3750 format: "unknown command shorthand suffix: '%s'\n", suffix.c_str());
3751 return nullptr;
3752 }
3753 }
3754 }
3755 if (scratch_command.empty())
3756 done = true;
3757 }
3758
3759 if (!scratch_command.empty())
3760 revised_command_line.Printf(format: " %s", scratch_command.c_str());
3761
3762 if (cmd_obj != nullptr)
3763 command_line = std::string(revised_command_line.GetString());
3764
3765 return cmd_obj;
3766}
3767
3768llvm::json::Value CommandInterpreter::GetStatistics() {
3769 llvm::json::Object stats;
3770 for (const auto &command_usage : m_command_usages)
3771 stats.try_emplace(K: command_usage.getKey(), Args: command_usage.getValue());
3772 return stats;
3773}
3774
3775const StructuredData::Array &CommandInterpreter::GetTranscript() const {
3776 return m_transcript;
3777}
3778
3779void CommandInterpreter::SetPrintCallback(
3780 CommandReturnObjectCallback callback) {
3781 m_print_callback = callback;
3782}
3783

source code of lldb/source/Interpreter/CommandInterpreter.cpp