Path: blob/main/contrib/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp
39587 views
//===-- CommandInterpreter.cpp --------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include <chrono>9#include <cstdlib>10#include <limits>11#include <memory>12#include <optional>13#include <string>14#include <vector>1516#include "Commands/CommandObjectApropos.h"17#include "Commands/CommandObjectBreakpoint.h"18#include "Commands/CommandObjectCommands.h"19#include "Commands/CommandObjectDWIMPrint.h"20#include "Commands/CommandObjectDiagnostics.h"21#include "Commands/CommandObjectDisassemble.h"22#include "Commands/CommandObjectExpression.h"23#include "Commands/CommandObjectFrame.h"24#include "Commands/CommandObjectGUI.h"25#include "Commands/CommandObjectHelp.h"26#include "Commands/CommandObjectLanguage.h"27#include "Commands/CommandObjectLog.h"28#include "Commands/CommandObjectMemory.h"29#include "Commands/CommandObjectPlatform.h"30#include "Commands/CommandObjectPlugin.h"31#include "Commands/CommandObjectProcess.h"32#include "Commands/CommandObjectQuit.h"33#include "Commands/CommandObjectRegexCommand.h"34#include "Commands/CommandObjectRegister.h"35#include "Commands/CommandObjectScripting.h"36#include "Commands/CommandObjectSession.h"37#include "Commands/CommandObjectSettings.h"38#include "Commands/CommandObjectSource.h"39#include "Commands/CommandObjectStats.h"40#include "Commands/CommandObjectTarget.h"41#include "Commands/CommandObjectThread.h"42#include "Commands/CommandObjectTrace.h"43#include "Commands/CommandObjectType.h"44#include "Commands/CommandObjectVersion.h"45#include "Commands/CommandObjectWatchpoint.h"4647#include "lldb/Core/Debugger.h"48#include "lldb/Core/PluginManager.h"49#include "lldb/Host/StreamFile.h"50#include "lldb/Utility/ErrorMessages.h"51#include "lldb/Utility/LLDBLog.h"52#include "lldb/Utility/Log.h"53#include "lldb/Utility/State.h"54#include "lldb/Utility/Stream.h"55#include "lldb/Utility/StructuredData.h"56#include "lldb/Utility/Timer.h"5758#include "lldb/Host/Config.h"59#if LLDB_ENABLE_LIBEDIT60#include "lldb/Host/Editline.h"61#endif62#include "lldb/Host/File.h"63#include "lldb/Host/FileCache.h"64#include "lldb/Host/Host.h"65#include "lldb/Host/HostInfo.h"6667#include "lldb/Interpreter/CommandCompletions.h"68#include "lldb/Interpreter/CommandInterpreter.h"69#include "lldb/Interpreter/CommandReturnObject.h"70#include "lldb/Interpreter/OptionValueProperties.h"71#include "lldb/Interpreter/Options.h"72#include "lldb/Interpreter/Property.h"73#include "lldb/Utility/Args.h"7475#include "lldb/Target/Language.h"76#include "lldb/Target/Process.h"77#include "lldb/Target/StopInfo.h"78#include "lldb/Target/TargetList.h"79#include "lldb/Target/Thread.h"80#include "lldb/Target/UnixSignals.h"8182#include "llvm/ADT/STLExtras.h"83#include "llvm/ADT/ScopeExit.h"84#include "llvm/ADT/SmallString.h"85#include "llvm/Support/FormatAdapters.h"86#include "llvm/Support/Path.h"87#include "llvm/Support/PrettyStackTrace.h"88#include "llvm/Support/ScopedPrinter.h"8990#if defined(__APPLE__)91#include <TargetConditionals.h>92#endif9394using namespace lldb;95using namespace lldb_private;9697static const char *k_white_space = " \t\v";9899static constexpr const char *InitFileWarning =100"There is a .lldbinit file in the current directory which is not being "101"read.\n"102"To silence this warning without sourcing in the local .lldbinit,\n"103"add the following to the lldbinit file in your home directory:\n"104" settings set target.load-cwd-lldbinit false\n"105"To allow lldb to source .lldbinit files in the current working "106"directory,\n"107"set the value of this variable to true. Only do so if you understand "108"and\n"109"accept the security risk.";110111const char *CommandInterpreter::g_no_argument = "<no-argument>";112const char *CommandInterpreter::g_need_argument = "<need-argument>";113const char *CommandInterpreter::g_argument = "<argument>";114115116#define LLDB_PROPERTIES_interpreter117#include "InterpreterProperties.inc"118119enum {120#define LLDB_PROPERTIES_interpreter121#include "InterpreterPropertiesEnum.inc"122};123124llvm::StringRef CommandInterpreter::GetStaticBroadcasterClass() {125static constexpr llvm::StringLiteral class_name("lldb.commandInterpreter");126return class_name;127}128129CommandInterpreter::CommandInterpreter(Debugger &debugger,130bool synchronous_execution)131: Broadcaster(debugger.GetBroadcasterManager(),132CommandInterpreter::GetStaticBroadcasterClass().str()),133Properties(134OptionValuePropertiesSP(new OptionValueProperties("interpreter"))),135IOHandlerDelegate(IOHandlerDelegate::Completion::LLDBCommand),136m_debugger(debugger), m_synchronous_execution(true),137m_skip_lldbinit_files(false), m_skip_app_init_files(false),138m_comment_char('#'), m_batch_command_mode(false),139m_truncation_warning(eNoOmission), m_max_depth_warning(eNoOmission),140m_command_source_depth(0) {141SetEventName(eBroadcastBitThreadShouldExit, "thread-should-exit");142SetEventName(eBroadcastBitResetPrompt, "reset-prompt");143SetEventName(eBroadcastBitQuitCommandReceived, "quit");144SetSynchronous(synchronous_execution);145CheckInWithManager();146m_collection_sp->Initialize(g_interpreter_properties);147}148149bool CommandInterpreter::GetExpandRegexAliases() const {150const uint32_t idx = ePropertyExpandRegexAliases;151return GetPropertyAtIndexAs<bool>(152idx, g_interpreter_properties[idx].default_uint_value != 0);153}154155bool CommandInterpreter::GetPromptOnQuit() const {156const uint32_t idx = ePropertyPromptOnQuit;157return GetPropertyAtIndexAs<bool>(158idx, g_interpreter_properties[idx].default_uint_value != 0);159}160161void CommandInterpreter::SetPromptOnQuit(bool enable) {162const uint32_t idx = ePropertyPromptOnQuit;163SetPropertyAtIndex(idx, enable);164}165166bool CommandInterpreter::GetSaveTranscript() const {167const uint32_t idx = ePropertySaveTranscript;168return GetPropertyAtIndexAs<bool>(169idx, g_interpreter_properties[idx].default_uint_value != 0);170}171172void CommandInterpreter::SetSaveTranscript(bool enable) {173const uint32_t idx = ePropertySaveTranscript;174SetPropertyAtIndex(idx, enable);175}176177bool CommandInterpreter::GetSaveSessionOnQuit() const {178const uint32_t idx = ePropertySaveSessionOnQuit;179return GetPropertyAtIndexAs<bool>(180idx, g_interpreter_properties[idx].default_uint_value != 0);181}182183void CommandInterpreter::SetSaveSessionOnQuit(bool enable) {184const uint32_t idx = ePropertySaveSessionOnQuit;185SetPropertyAtIndex(idx, enable);186}187188bool CommandInterpreter::GetOpenTranscriptInEditor() const {189const uint32_t idx = ePropertyOpenTranscriptInEditor;190return GetPropertyAtIndexAs<bool>(191idx, g_interpreter_properties[idx].default_uint_value != 0);192}193194void CommandInterpreter::SetOpenTranscriptInEditor(bool enable) {195const uint32_t idx = ePropertyOpenTranscriptInEditor;196SetPropertyAtIndex(idx, enable);197}198199FileSpec CommandInterpreter::GetSaveSessionDirectory() const {200const uint32_t idx = ePropertySaveSessionDirectory;201return GetPropertyAtIndexAs<FileSpec>(idx, {});202}203204void CommandInterpreter::SetSaveSessionDirectory(llvm::StringRef path) {205const uint32_t idx = ePropertySaveSessionDirectory;206SetPropertyAtIndex(idx, path);207}208209bool CommandInterpreter::GetEchoCommands() const {210const uint32_t idx = ePropertyEchoCommands;211return GetPropertyAtIndexAs<bool>(212idx, g_interpreter_properties[idx].default_uint_value != 0);213}214215void CommandInterpreter::SetEchoCommands(bool enable) {216const uint32_t idx = ePropertyEchoCommands;217SetPropertyAtIndex(idx, enable);218}219220bool CommandInterpreter::GetEchoCommentCommands() const {221const uint32_t idx = ePropertyEchoCommentCommands;222return GetPropertyAtIndexAs<bool>(223idx, g_interpreter_properties[idx].default_uint_value != 0);224}225226void CommandInterpreter::SetEchoCommentCommands(bool enable) {227const uint32_t idx = ePropertyEchoCommentCommands;228SetPropertyAtIndex(idx, enable);229}230231void CommandInterpreter::AllowExitCodeOnQuit(bool allow) {232m_allow_exit_code = allow;233if (!allow)234m_quit_exit_code.reset();235}236237bool CommandInterpreter::SetQuitExitCode(int exit_code) {238if (!m_allow_exit_code)239return false;240m_quit_exit_code = exit_code;241return true;242}243244int CommandInterpreter::GetQuitExitCode(bool &exited) const {245exited = m_quit_exit_code.has_value();246if (exited)247return *m_quit_exit_code;248return 0;249}250251void CommandInterpreter::ResolveCommand(const char *command_line,252CommandReturnObject &result) {253std::string command = command_line;254if (ResolveCommandImpl(command, result) != nullptr) {255result.AppendMessageWithFormat("%s", command.c_str());256result.SetStatus(eReturnStatusSuccessFinishResult);257}258}259260bool CommandInterpreter::GetStopCmdSourceOnError() const {261const uint32_t idx = ePropertyStopCmdSourceOnError;262return GetPropertyAtIndexAs<bool>(263idx, g_interpreter_properties[idx].default_uint_value != 0);264}265266bool CommandInterpreter::GetSpaceReplPrompts() const {267const uint32_t idx = ePropertySpaceReplPrompts;268return GetPropertyAtIndexAs<bool>(269idx, g_interpreter_properties[idx].default_uint_value != 0);270}271272bool CommandInterpreter::GetRepeatPreviousCommand() const {273const uint32_t idx = ePropertyRepeatPreviousCommand;274return GetPropertyAtIndexAs<bool>(275idx, g_interpreter_properties[idx].default_uint_value != 0);276}277278bool CommandInterpreter::GetRequireCommandOverwrite() const {279const uint32_t idx = ePropertyRequireCommandOverwrite;280return GetPropertyAtIndexAs<bool>(281idx, g_interpreter_properties[idx].default_uint_value != 0);282}283284void CommandInterpreter::Initialize() {285LLDB_SCOPED_TIMER();286287CommandReturnObject result(m_debugger.GetUseColor());288289LoadCommandDictionary();290291// An alias arguments vector to reuse - reset it before use...292OptionArgVectorSP alias_arguments_vector_sp(new OptionArgVector);293294// Set up some initial aliases.295CommandObjectSP cmd_obj_sp = GetCommandSPExact("quit");296if (cmd_obj_sp) {297AddAlias("q", cmd_obj_sp);298AddAlias("exit", cmd_obj_sp);299}300301cmd_obj_sp = GetCommandSPExact("_regexp-attach");302if (cmd_obj_sp)303AddAlias("attach", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());304305cmd_obj_sp = GetCommandSPExact("process detach");306if (cmd_obj_sp) {307AddAlias("detach", cmd_obj_sp);308}309310cmd_obj_sp = GetCommandSPExact("process continue");311if (cmd_obj_sp) {312AddAlias("c", cmd_obj_sp);313AddAlias("continue", cmd_obj_sp);314}315316cmd_obj_sp = GetCommandSPExact("_regexp-break");317if (cmd_obj_sp)318AddAlias("b", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());319320cmd_obj_sp = GetCommandSPExact("_regexp-tbreak");321if (cmd_obj_sp)322AddAlias("tbreak", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());323324cmd_obj_sp = GetCommandSPExact("thread step-inst");325if (cmd_obj_sp) {326AddAlias("stepi", cmd_obj_sp);327AddAlias("si", cmd_obj_sp);328}329330cmd_obj_sp = GetCommandSPExact("thread step-inst-over");331if (cmd_obj_sp) {332AddAlias("nexti", cmd_obj_sp);333AddAlias("ni", cmd_obj_sp);334}335336cmd_obj_sp = GetCommandSPExact("thread step-in");337if (cmd_obj_sp) {338AddAlias("s", cmd_obj_sp);339AddAlias("step", cmd_obj_sp);340CommandAlias *sif_alias = AddAlias(341"sif", cmd_obj_sp, "--end-linenumber block --step-in-target %1");342if (sif_alias) {343sif_alias->SetHelp("Step through the current block, stopping if you step "344"directly into a function whose name matches the "345"TargetFunctionName.");346sif_alias->SetSyntax("sif <TargetFunctionName>");347}348}349350cmd_obj_sp = GetCommandSPExact("thread step-over");351if (cmd_obj_sp) {352AddAlias("n", cmd_obj_sp);353AddAlias("next", cmd_obj_sp);354}355356cmd_obj_sp = GetCommandSPExact("thread step-out");357if (cmd_obj_sp) {358AddAlias("finish", cmd_obj_sp);359}360361cmd_obj_sp = GetCommandSPExact("frame select");362if (cmd_obj_sp) {363AddAlias("f", cmd_obj_sp);364}365366cmd_obj_sp = GetCommandSPExact("thread select");367if (cmd_obj_sp) {368AddAlias("t", cmd_obj_sp);369}370371cmd_obj_sp = GetCommandSPExact("_regexp-jump");372if (cmd_obj_sp) {373AddAlias("j", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());374AddAlias("jump", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());375}376377cmd_obj_sp = GetCommandSPExact("_regexp-list");378if (cmd_obj_sp) {379AddAlias("l", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());380AddAlias("list", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());381}382383cmd_obj_sp = GetCommandSPExact("_regexp-env");384if (cmd_obj_sp)385AddAlias("env", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());386387cmd_obj_sp = GetCommandSPExact("memory read");388if (cmd_obj_sp)389AddAlias("x", cmd_obj_sp);390391cmd_obj_sp = GetCommandSPExact("_regexp-up");392if (cmd_obj_sp)393AddAlias("up", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());394395cmd_obj_sp = GetCommandSPExact("_regexp-down");396if (cmd_obj_sp)397AddAlias("down", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());398399cmd_obj_sp = GetCommandSPExact("_regexp-display");400if (cmd_obj_sp)401AddAlias("display", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());402403cmd_obj_sp = GetCommandSPExact("disassemble");404if (cmd_obj_sp)405AddAlias("dis", cmd_obj_sp);406407cmd_obj_sp = GetCommandSPExact("disassemble");408if (cmd_obj_sp)409AddAlias("di", cmd_obj_sp);410411cmd_obj_sp = GetCommandSPExact("_regexp-undisplay");412if (cmd_obj_sp)413AddAlias("undisplay", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());414415cmd_obj_sp = GetCommandSPExact("_regexp-bt");416if (cmd_obj_sp)417AddAlias("bt", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());418419cmd_obj_sp = GetCommandSPExact("target create");420if (cmd_obj_sp)421AddAlias("file", cmd_obj_sp);422423cmd_obj_sp = GetCommandSPExact("target modules");424if (cmd_obj_sp)425AddAlias("image", cmd_obj_sp);426427alias_arguments_vector_sp = std::make_shared<OptionArgVector>();428429cmd_obj_sp = GetCommandSPExact("dwim-print");430if (cmd_obj_sp) {431AddAlias("p", cmd_obj_sp, "--")->SetHelpLong("");432AddAlias("print", cmd_obj_sp, "--")->SetHelpLong("");433if (auto *po = AddAlias("po", cmd_obj_sp, "-O --")) {434po->SetHelp("Evaluate an expression on the current thread. Displays any "435"returned value with formatting "436"controlled by the type's author.");437po->SetHelpLong("");438}439}440441cmd_obj_sp = GetCommandSPExact("expression");442if (cmd_obj_sp) {443AddAlias("call", cmd_obj_sp, "--")->SetHelpLong("");444CommandAlias *parray_alias =445AddAlias("parray", cmd_obj_sp, "--element-count %1 --");446if (parray_alias) {447parray_alias->SetHelp448("parray <COUNT> <EXPRESSION> -- lldb will evaluate EXPRESSION "449"to get a typed-pointer-to-an-array in memory, and will display "450"COUNT elements of that type from the array.");451parray_alias->SetHelpLong("");452}453CommandAlias *poarray_alias = AddAlias("poarray", cmd_obj_sp,454"--object-description --element-count %1 --");455if (poarray_alias) {456poarray_alias->SetHelp("poarray <COUNT> <EXPRESSION> -- lldb will "457"evaluate EXPRESSION to get the address of an array of COUNT "458"objects in memory, and will call po on them.");459poarray_alias->SetHelpLong("");460}461}462463cmd_obj_sp = GetCommandSPExact("platform shell");464if (cmd_obj_sp) {465CommandAlias *shell_alias = AddAlias("shell", cmd_obj_sp, " --host --");466if (shell_alias) {467shell_alias->SetHelp("Run a shell command on the host.");468shell_alias->SetHelpLong("");469shell_alias->SetSyntax("shell <shell-command>");470}471}472473cmd_obj_sp = GetCommandSPExact("process kill");474if (cmd_obj_sp) {475AddAlias("kill", cmd_obj_sp);476}477478cmd_obj_sp = GetCommandSPExact("process launch");479if (cmd_obj_sp) {480alias_arguments_vector_sp = std::make_shared<OptionArgVector>();481#if defined(__APPLE__)482#if TARGET_OS_IPHONE483AddAlias("r", cmd_obj_sp, "--");484AddAlias("run", cmd_obj_sp, "--");485#else486AddAlias("r", cmd_obj_sp, "--shell-expand-args true --");487AddAlias("run", cmd_obj_sp, "--shell-expand-args true --");488#endif489#else490StreamString defaultshell;491defaultshell.Printf("--shell=%s --",492HostInfo::GetDefaultShell().GetPath().c_str());493AddAlias("r", cmd_obj_sp, defaultshell.GetString());494AddAlias("run", cmd_obj_sp, defaultshell.GetString());495#endif496}497498cmd_obj_sp = GetCommandSPExact("target symbols add");499if (cmd_obj_sp) {500AddAlias("add-dsym", cmd_obj_sp);501}502503cmd_obj_sp = GetCommandSPExact("breakpoint set");504if (cmd_obj_sp) {505AddAlias("rbreak", cmd_obj_sp, "--func-regex %1");506}507508cmd_obj_sp = GetCommandSPExact("frame variable");509if (cmd_obj_sp) {510AddAlias("v", cmd_obj_sp);511AddAlias("var", cmd_obj_sp);512AddAlias("vo", cmd_obj_sp, "--object-description");513}514515cmd_obj_sp = GetCommandSPExact("register");516if (cmd_obj_sp) {517AddAlias("re", cmd_obj_sp);518}519520cmd_obj_sp = GetCommandSPExact("scripting run");521if (cmd_obj_sp) {522AddAlias("sc", cmd_obj_sp);523AddAlias("scr", cmd_obj_sp);524AddAlias("scri", cmd_obj_sp);525AddAlias("scrip", cmd_obj_sp);526AddAlias("script", cmd_obj_sp);527}528529cmd_obj_sp = GetCommandSPExact("session history");530if (cmd_obj_sp) {531AddAlias("history", cmd_obj_sp);532}533534cmd_obj_sp = GetCommandSPExact("help");535if (cmd_obj_sp) {536AddAlias("h", cmd_obj_sp);537}538}539540void CommandInterpreter::Clear() {541m_command_io_handler_sp.reset();542}543544const char *CommandInterpreter::ProcessEmbeddedScriptCommands(const char *arg) {545// This function has not yet been implemented.546547// Look for any embedded script command548// If found,549// get interpreter object from the command dictionary,550// call execute_one_command on it,551// get the results as a string,552// substitute that string for current stuff.553554return arg;555}556557#define REGISTER_COMMAND_OBJECT(NAME, CLASS) \558m_command_dict[NAME] = std::make_shared<CLASS>(*this);559560void CommandInterpreter::LoadCommandDictionary() {561LLDB_SCOPED_TIMER();562563REGISTER_COMMAND_OBJECT("apropos", CommandObjectApropos);564REGISTER_COMMAND_OBJECT("breakpoint", CommandObjectMultiwordBreakpoint);565REGISTER_COMMAND_OBJECT("command", CommandObjectMultiwordCommands);566REGISTER_COMMAND_OBJECT("diagnostics", CommandObjectDiagnostics);567REGISTER_COMMAND_OBJECT("disassemble", CommandObjectDisassemble);568REGISTER_COMMAND_OBJECT("dwim-print", CommandObjectDWIMPrint);569REGISTER_COMMAND_OBJECT("expression", CommandObjectExpression);570REGISTER_COMMAND_OBJECT("frame", CommandObjectMultiwordFrame);571REGISTER_COMMAND_OBJECT("gui", CommandObjectGUI);572REGISTER_COMMAND_OBJECT("help", CommandObjectHelp);573REGISTER_COMMAND_OBJECT("log", CommandObjectLog);574REGISTER_COMMAND_OBJECT("memory", CommandObjectMemory);575REGISTER_COMMAND_OBJECT("platform", CommandObjectPlatform);576REGISTER_COMMAND_OBJECT("plugin", CommandObjectPlugin);577REGISTER_COMMAND_OBJECT("process", CommandObjectMultiwordProcess);578REGISTER_COMMAND_OBJECT("quit", CommandObjectQuit);579REGISTER_COMMAND_OBJECT("register", CommandObjectRegister);580REGISTER_COMMAND_OBJECT("scripting", CommandObjectMultiwordScripting);581REGISTER_COMMAND_OBJECT("settings", CommandObjectMultiwordSettings);582REGISTER_COMMAND_OBJECT("session", CommandObjectSession);583REGISTER_COMMAND_OBJECT("source", CommandObjectMultiwordSource);584REGISTER_COMMAND_OBJECT("statistics", CommandObjectStats);585REGISTER_COMMAND_OBJECT("target", CommandObjectMultiwordTarget);586REGISTER_COMMAND_OBJECT("thread", CommandObjectMultiwordThread);587REGISTER_COMMAND_OBJECT("trace", CommandObjectTrace);588REGISTER_COMMAND_OBJECT("type", CommandObjectType);589REGISTER_COMMAND_OBJECT("version", CommandObjectVersion);590REGISTER_COMMAND_OBJECT("watchpoint", CommandObjectMultiwordWatchpoint);591REGISTER_COMMAND_OBJECT("language", CommandObjectLanguage);592593// clang-format off594const char *break_regexes[][2] = {595{"^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*$",596"breakpoint set --file '%1' --line %2 --column %3"},597{"^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*$",598"breakpoint set --file '%1' --line %2"},599{"^/([^/]+)/$", "breakpoint set --source-pattern-regexp '%1'"},600{"^([[:digit:]]+)[[:space:]]*$", "breakpoint set --line %1"},601{"^\\*?(0x[[:xdigit:]]+)[[:space:]]*$", "breakpoint set --address %1"},602{"^[\"']?([-+]?\\[.*\\])[\"']?[[:space:]]*$",603"breakpoint set --name '%1'"},604{"^(-.*)$", "breakpoint set %1"},605{"^(.*[^[:space:]])`(.*[^[:space:]])[[:space:]]*$",606"breakpoint set --name '%2' --shlib '%1'"},607{"^\\&(.*[^[:space:]])[[:space:]]*$",608"breakpoint set --name '%1' --skip-prologue=0"},609{"^[\"']?(.*[^[:space:]\"'])[\"']?[[:space:]]*$",610"breakpoint set --name '%1'"}};611// clang-format on612613size_t num_regexes = std::size(break_regexes);614615std::unique_ptr<CommandObjectRegexCommand> break_regex_cmd_up(616new CommandObjectRegexCommand(617*this, "_regexp-break",618"Set a breakpoint using one of several shorthand formats.",619"\n"620"_regexp-break <filename>:<linenum>:<colnum>\n"621" main.c:12:21 // Break at line 12 and column "622"21 of main.c\n\n"623"_regexp-break <filename>:<linenum>\n"624" main.c:12 // Break at line 12 of "625"main.c\n\n"626"_regexp-break <linenum>\n"627" 12 // Break at line 12 of current "628"file\n\n"629"_regexp-break 0x<address>\n"630" 0x1234000 // Break at address "631"0x1234000\n\n"632"_regexp-break <name>\n"633" main // Break in 'main' after the "634"prologue\n\n"635"_regexp-break &<name>\n"636" &main // Break at first instruction "637"in 'main'\n\n"638"_regexp-break <module>`<name>\n"639" libc.so`malloc // Break in 'malloc' from "640"'libc.so'\n\n"641"_regexp-break /<source-regex>/\n"642" /break here/ // Break on source lines in "643"current file\n"644" // containing text 'break "645"here'.\n",646lldb::eSymbolCompletion | lldb::eSourceFileCompletion, false));647648if (break_regex_cmd_up) {649bool success = true;650for (size_t i = 0; i < num_regexes; i++) {651success = break_regex_cmd_up->AddRegexCommand(break_regexes[i][0],652break_regexes[i][1]);653if (!success)654break;655}656success =657break_regex_cmd_up->AddRegexCommand("^$", "breakpoint list --full");658659if (success) {660CommandObjectSP break_regex_cmd_sp(break_regex_cmd_up.release());661m_command_dict[std::string(break_regex_cmd_sp->GetCommandName())] =662break_regex_cmd_sp;663}664}665666std::unique_ptr<CommandObjectRegexCommand> tbreak_regex_cmd_up(667new CommandObjectRegexCommand(668*this, "_regexp-tbreak",669"Set a one-shot breakpoint using one of several shorthand formats.",670"\n"671"_regexp-break <filename>:<linenum>:<colnum>\n"672" main.c:12:21 // Break at line 12 and column "673"21 of main.c\n\n"674"_regexp-break <filename>:<linenum>\n"675" main.c:12 // Break at line 12 of "676"main.c\n\n"677"_regexp-break <linenum>\n"678" 12 // Break at line 12 of current "679"file\n\n"680"_regexp-break 0x<address>\n"681" 0x1234000 // Break at address "682"0x1234000\n\n"683"_regexp-break <name>\n"684" main // Break in 'main' after the "685"prologue\n\n"686"_regexp-break &<name>\n"687" &main // Break at first instruction "688"in 'main'\n\n"689"_regexp-break <module>`<name>\n"690" libc.so`malloc // Break in 'malloc' from "691"'libc.so'\n\n"692"_regexp-break /<source-regex>/\n"693" /break here/ // Break on source lines in "694"current file\n"695" // containing text 'break "696"here'.\n",697lldb::eSymbolCompletion | lldb::eSourceFileCompletion, false));698699if (tbreak_regex_cmd_up) {700bool success = true;701for (size_t i = 0; i < num_regexes; i++) {702std::string command = break_regexes[i][1];703command += " -o 1";704success =705tbreak_regex_cmd_up->AddRegexCommand(break_regexes[i][0], command);706if (!success)707break;708}709success =710tbreak_regex_cmd_up->AddRegexCommand("^$", "breakpoint list --full");711712if (success) {713CommandObjectSP tbreak_regex_cmd_sp(tbreak_regex_cmd_up.release());714m_command_dict[std::string(tbreak_regex_cmd_sp->GetCommandName())] =715tbreak_regex_cmd_sp;716}717}718719std::unique_ptr<CommandObjectRegexCommand> attach_regex_cmd_up(720new CommandObjectRegexCommand(721*this, "_regexp-attach", "Attach to process by ID or name.",722"_regexp-attach <pid> | <process-name>", 0, false));723if (attach_regex_cmd_up) {724if (attach_regex_cmd_up->AddRegexCommand("^([0-9]+)[[:space:]]*$",725"process attach --pid %1") &&726attach_regex_cmd_up->AddRegexCommand(727"^(-.*|.* -.*)$", "process attach %1") && // Any options that are728// specified get passed to729// 'process attach'730attach_regex_cmd_up->AddRegexCommand("^(.+)$",731"process attach --name '%1'") &&732attach_regex_cmd_up->AddRegexCommand("^$", "process attach")) {733CommandObjectSP attach_regex_cmd_sp(attach_regex_cmd_up.release());734m_command_dict[std::string(attach_regex_cmd_sp->GetCommandName())] =735attach_regex_cmd_sp;736}737}738739std::unique_ptr<CommandObjectRegexCommand> down_regex_cmd_up(740new CommandObjectRegexCommand(*this, "_regexp-down",741"Select a newer stack frame. Defaults to "742"moving one frame, a numeric argument can "743"specify an arbitrary number.",744"_regexp-down [<count>]", 0, false));745if (down_regex_cmd_up) {746if (down_regex_cmd_up->AddRegexCommand("^$", "frame select -r -1") &&747down_regex_cmd_up->AddRegexCommand("^([0-9]+)$",748"frame select -r -%1")) {749CommandObjectSP down_regex_cmd_sp(down_regex_cmd_up.release());750m_command_dict[std::string(down_regex_cmd_sp->GetCommandName())] =751down_regex_cmd_sp;752}753}754755std::unique_ptr<CommandObjectRegexCommand> up_regex_cmd_up(756new CommandObjectRegexCommand(757*this, "_regexp-up",758"Select an older stack frame. Defaults to moving one "759"frame, a numeric argument can specify an arbitrary number.",760"_regexp-up [<count>]", 0, false));761if (up_regex_cmd_up) {762if (up_regex_cmd_up->AddRegexCommand("^$", "frame select -r 1") &&763up_regex_cmd_up->AddRegexCommand("^([0-9]+)$", "frame select -r %1")) {764CommandObjectSP up_regex_cmd_sp(up_regex_cmd_up.release());765m_command_dict[std::string(up_regex_cmd_sp->GetCommandName())] =766up_regex_cmd_sp;767}768}769770std::unique_ptr<CommandObjectRegexCommand> display_regex_cmd_up(771new CommandObjectRegexCommand(772*this, "_regexp-display",773"Evaluate an expression at every stop (see 'help target stop-hook'.)",774"_regexp-display expression", 0, false));775if (display_regex_cmd_up) {776if (display_regex_cmd_up->AddRegexCommand(777"^(.+)$", "target stop-hook add -o \"expr -- %1\"")) {778CommandObjectSP display_regex_cmd_sp(display_regex_cmd_up.release());779m_command_dict[std::string(display_regex_cmd_sp->GetCommandName())] =780display_regex_cmd_sp;781}782}783784std::unique_ptr<CommandObjectRegexCommand> undisplay_regex_cmd_up(785new CommandObjectRegexCommand(*this, "_regexp-undisplay",786"Stop displaying expression at every "787"stop (specified by stop-hook index.)",788"_regexp-undisplay stop-hook-number", 0,789false));790if (undisplay_regex_cmd_up) {791if (undisplay_regex_cmd_up->AddRegexCommand("^([0-9]+)$",792"target stop-hook delete %1")) {793CommandObjectSP undisplay_regex_cmd_sp(undisplay_regex_cmd_up.release());794m_command_dict[std::string(undisplay_regex_cmd_sp->GetCommandName())] =795undisplay_regex_cmd_sp;796}797}798799std::unique_ptr<CommandObjectRegexCommand> connect_gdb_remote_cmd_up(800new CommandObjectRegexCommand(801*this, "gdb-remote",802"Connect to a process via remote GDB server.\n"803"If no host is specifed, localhost is assumed.\n"804"gdb-remote is an abbreviation for 'process connect --plugin "805"gdb-remote connect://<hostname>:<port>'\n",806"gdb-remote [<hostname>:]<portnum>", 0, false));807if (connect_gdb_remote_cmd_up) {808if (connect_gdb_remote_cmd_up->AddRegexCommand(809"^([^:]+|\\[[0-9a-fA-F:]+.*\\]):([0-9]+)$",810"process connect --plugin gdb-remote connect://%1:%2") &&811connect_gdb_remote_cmd_up->AddRegexCommand(812"^([[:digit:]]+)$",813"process connect --plugin gdb-remote connect://localhost:%1")) {814CommandObjectSP command_sp(connect_gdb_remote_cmd_up.release());815m_command_dict[std::string(command_sp->GetCommandName())] = command_sp;816}817}818819std::unique_ptr<CommandObjectRegexCommand> connect_kdp_remote_cmd_up(820new CommandObjectRegexCommand(821*this, "kdp-remote",822"Connect to a process via remote KDP server.\n"823"If no UDP port is specified, port 41139 is assumed.\n"824"kdp-remote is an abbreviation for 'process connect --plugin "825"kdp-remote udp://<hostname>:<port>'\n",826"kdp-remote <hostname>[:<portnum>]", 0, false));827if (connect_kdp_remote_cmd_up) {828if (connect_kdp_remote_cmd_up->AddRegexCommand(829"^([^:]+:[[:digit:]]+)$",830"process connect --plugin kdp-remote udp://%1") &&831connect_kdp_remote_cmd_up->AddRegexCommand(832"^(.+)$", "process connect --plugin kdp-remote udp://%1:41139")) {833CommandObjectSP command_sp(connect_kdp_remote_cmd_up.release());834m_command_dict[std::string(command_sp->GetCommandName())] = command_sp;835}836}837838std::unique_ptr<CommandObjectRegexCommand> bt_regex_cmd_up(839new CommandObjectRegexCommand(840*this, "_regexp-bt",841"Show backtrace of the current thread's call stack. Any numeric "842"argument displays at most that many frames. The argument 'all' "843"displays all threads. Use 'settings set frame-format' to customize "844"the printing of individual frames and 'settings set thread-format' "845"to customize the thread header.",846"bt [<digit> | all]", 0, false));847if (bt_regex_cmd_up) {848// accept but don't document "bt -c <number>" -- before bt was a regex849// 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 so851// now "bt 3" is the preferred form, in line with gdb.852if (bt_regex_cmd_up->AddRegexCommand("^([[:digit:]]+)[[:space:]]*$",853"thread backtrace -c %1") &&854bt_regex_cmd_up->AddRegexCommand("^-c ([[:digit:]]+)[[:space:]]*$",855"thread backtrace -c %1") &&856bt_regex_cmd_up->AddRegexCommand("^all[[:space:]]*$", "thread backtrace all") &&857bt_regex_cmd_up->AddRegexCommand("^[[:space:]]*$", "thread backtrace")) {858CommandObjectSP command_sp(bt_regex_cmd_up.release());859m_command_dict[std::string(command_sp->GetCommandName())] = command_sp;860}861}862863std::unique_ptr<CommandObjectRegexCommand> list_regex_cmd_up(864new CommandObjectRegexCommand(865*this, "_regexp-list",866"List relevant source code using one of several shorthand formats.",867"\n"868"_regexp-list <file>:<line> // List around specific file/line\n"869"_regexp-list <line> // List current file around specified "870"line\n"871"_regexp-list <function-name> // List specified function\n"872"_regexp-list 0x<address> // List around specified address\n"873"_regexp-list -[<count>] // List previous <count> lines\n"874"_regexp-list // List subsequent lines",875lldb::eSourceFileCompletion, false));876if (list_regex_cmd_up) {877if (list_regex_cmd_up->AddRegexCommand("^([0-9]+)[[:space:]]*$",878"source list --line %1") &&879list_regex_cmd_up->AddRegexCommand(880"^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]"881"]*$",882"source list --file '%1' --line %2") &&883list_regex_cmd_up->AddRegexCommand(884"^\\*?(0x[[:xdigit:]]+)[[:space:]]*$",885"source list --address %1") &&886list_regex_cmd_up->AddRegexCommand("^-[[:space:]]*$",887"source list --reverse") &&888list_regex_cmd_up->AddRegexCommand(889"^-([[:digit:]]+)[[:space:]]*$",890"source list --reverse --count %1") &&891list_regex_cmd_up->AddRegexCommand("^(.+)$",892"source list --name \"%1\"") &&893list_regex_cmd_up->AddRegexCommand("^$", "source list")) {894CommandObjectSP list_regex_cmd_sp(list_regex_cmd_up.release());895m_command_dict[std::string(list_regex_cmd_sp->GetCommandName())] =896list_regex_cmd_sp;897}898}899900std::unique_ptr<CommandObjectRegexCommand> env_regex_cmd_up(901new CommandObjectRegexCommand(902*this, "_regexp-env",903"Shorthand for viewing and setting environment variables.",904"\n"905"_regexp-env // Show environment\n"906"_regexp-env <name>=<value> // Set an environment variable",9070, false));908if (env_regex_cmd_up) {909if (env_regex_cmd_up->AddRegexCommand("^$",910"settings show target.env-vars") &&911env_regex_cmd_up->AddRegexCommand("^([A-Za-z_][A-Za-z_0-9]*=.*)$",912"settings set target.env-vars %1")) {913CommandObjectSP env_regex_cmd_sp(env_regex_cmd_up.release());914m_command_dict[std::string(env_regex_cmd_sp->GetCommandName())] =915env_regex_cmd_sp;916}917}918919std::unique_ptr<CommandObjectRegexCommand> jump_regex_cmd_up(920new CommandObjectRegexCommand(921*this, "_regexp-jump", "Set the program counter to a new address.",922"\n"923"_regexp-jump <line>\n"924"_regexp-jump +<line-offset> | -<line-offset>\n"925"_regexp-jump <file>:<line>\n"926"_regexp-jump *<addr>\n",9270, false));928if (jump_regex_cmd_up) {929if (jump_regex_cmd_up->AddRegexCommand("^\\*(.*)$",930"thread jump --addr %1") &&931jump_regex_cmd_up->AddRegexCommand("^([0-9]+)$",932"thread jump --line %1") &&933jump_regex_cmd_up->AddRegexCommand("^([^:]+):([0-9]+)$",934"thread jump --file %1 --line %2") &&935jump_regex_cmd_up->AddRegexCommand("^([+\\-][0-9]+)$",936"thread jump --by %1")) {937CommandObjectSP jump_regex_cmd_sp(jump_regex_cmd_up.release());938m_command_dict[std::string(jump_regex_cmd_sp->GetCommandName())] =939jump_regex_cmd_sp;940}941}942}943944int CommandInterpreter::GetCommandNamesMatchingPartialString(945const char *cmd_str, bool include_aliases, StringList &matches,946StringList &descriptions) {947AddNamesMatchingPartialString(m_command_dict, cmd_str, matches,948&descriptions);949950if (include_aliases) {951AddNamesMatchingPartialString(m_alias_dict, cmd_str, matches,952&descriptions);953}954955return matches.GetSize();956}957958CommandObjectMultiword *CommandInterpreter::VerifyUserMultiwordCmdPath(959Args &path, bool leaf_is_command, Status &result) {960result.Clear();961962auto get_multi_or_report_error =963[&result](CommandObjectSP cmd_sp,964const char *name) -> CommandObjectMultiword * {965if (!cmd_sp) {966result.SetErrorStringWithFormat("Path component: '%s' not found", name);967return nullptr;968}969if (!cmd_sp->IsUserCommand()) {970result.SetErrorStringWithFormat("Path component: '%s' is not a user "971"command",972name);973return nullptr;974}975CommandObjectMultiword *cmd_as_multi = cmd_sp->GetAsMultiwordCommand();976if (!cmd_as_multi) {977result.SetErrorStringWithFormat("Path component: '%s' is not a container "978"command",979name);980return nullptr;981}982return cmd_as_multi;983};984985size_t num_args = path.GetArgumentCount();986if (num_args == 0) {987result.SetErrorString("empty command path");988return nullptr;989}990991if (num_args == 1 && leaf_is_command) {992// We just got a leaf command to be added to the root. That's not an error,993// just return null for the container.994return nullptr;995}996997// Start by getting the root command from the interpreter.998const char *cur_name = path.GetArgumentAtIndex(0);999CommandObjectSP cur_cmd_sp = GetCommandSPExact(cur_name);1000CommandObjectMultiword *cur_as_multi =1001get_multi_or_report_error(cur_cmd_sp, cur_name);1002if (cur_as_multi == nullptr)1003return nullptr;10041005size_t num_path_elements = num_args - (leaf_is_command ? 1 : 0);1006for (size_t cursor = 1; cursor < num_path_elements && cur_as_multi != nullptr;1007cursor++) {1008cur_name = path.GetArgumentAtIndex(cursor);1009cur_cmd_sp = cur_as_multi->GetSubcommandSPExact(cur_name);1010cur_as_multi = get_multi_or_report_error(cur_cmd_sp, cur_name);1011}1012return cur_as_multi;1013}10141015CommandObjectSP1016CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, bool include_aliases,1017bool exact, StringList *matches,1018StringList *descriptions) const {1019CommandObjectSP command_sp;10201021std::string cmd = std::string(cmd_str);10221023if (HasCommands()) {1024auto pos = m_command_dict.find(cmd);1025if (pos != m_command_dict.end())1026command_sp = pos->second;1027}10281029if (include_aliases && HasAliases()) {1030auto alias_pos = m_alias_dict.find(cmd);1031if (alias_pos != m_alias_dict.end())1032command_sp = alias_pos->second;1033}10341035if (HasUserCommands()) {1036auto pos = m_user_dict.find(cmd);1037if (pos != m_user_dict.end())1038command_sp = pos->second;1039}10401041if (HasUserMultiwordCommands()) {1042auto pos = m_user_mw_dict.find(cmd);1043if (pos != m_user_mw_dict.end())1044command_sp = pos->second;1045}10461047if (!exact && !command_sp) {1048// We will only get into here if we didn't find any exact matches.10491050CommandObjectSP user_match_sp, user_mw_match_sp, alias_match_sp,1051real_match_sp;10521053StringList local_matches;1054if (matches == nullptr)1055matches = &local_matches;10561057unsigned int num_cmd_matches = 0;1058unsigned int num_alias_matches = 0;1059unsigned int num_user_matches = 0;1060unsigned int num_user_mw_matches = 0;10611062// Look through the command dictionaries one by one, and if we get only one1063// match from any of them in toto, then return that, otherwise return an1064// empty CommandObjectSP and the list of matches.10651066if (HasCommands()) {1067num_cmd_matches = AddNamesMatchingPartialString(m_command_dict, cmd_str,1068*matches, descriptions);1069}10701071if (num_cmd_matches == 1) {1072cmd.assign(matches->GetStringAtIndex(0));1073auto pos = m_command_dict.find(cmd);1074if (pos != m_command_dict.end())1075real_match_sp = pos->second;1076}10771078if (include_aliases && HasAliases()) {1079num_alias_matches = AddNamesMatchingPartialString(m_alias_dict, cmd_str,1080*matches, descriptions);1081}10821083if (num_alias_matches == 1) {1084cmd.assign(matches->GetStringAtIndex(num_cmd_matches));1085auto alias_pos = m_alias_dict.find(cmd);1086if (alias_pos != m_alias_dict.end())1087alias_match_sp = alias_pos->second;1088}10891090if (HasUserCommands()) {1091num_user_matches = AddNamesMatchingPartialString(m_user_dict, cmd_str,1092*matches, descriptions);1093}10941095if (num_user_matches == 1) {1096cmd.assign(1097matches->GetStringAtIndex(num_cmd_matches + num_alias_matches));10981099auto pos = m_user_dict.find(cmd);1100if (pos != m_user_dict.end())1101user_match_sp = pos->second;1102}11031104if (HasUserMultiwordCommands()) {1105num_user_mw_matches = AddNamesMatchingPartialString(1106m_user_mw_dict, cmd_str, *matches, descriptions);1107}11081109if (num_user_mw_matches == 1) {1110cmd.assign(matches->GetStringAtIndex(num_cmd_matches + num_alias_matches +1111num_user_matches));11121113auto pos = m_user_mw_dict.find(cmd);1114if (pos != m_user_mw_dict.end())1115user_mw_match_sp = pos->second;1116}11171118// If we got exactly one match, return that, otherwise return the match1119// list.11201121if (num_user_matches + num_user_mw_matches + num_cmd_matches +1122num_alias_matches ==11231) {1124if (num_cmd_matches)1125return real_match_sp;1126else if (num_alias_matches)1127return alias_match_sp;1128else if (num_user_mw_matches)1129return user_mw_match_sp;1130else1131return user_match_sp;1132}1133} else if (matches && command_sp) {1134matches->AppendString(cmd_str);1135if (descriptions)1136descriptions->AppendString(command_sp->GetHelp());1137}11381139return command_sp;1140}11411142bool CommandInterpreter::AddCommand(llvm::StringRef name,1143const lldb::CommandObjectSP &cmd_sp,1144bool can_replace) {1145if (cmd_sp.get())1146lldbassert((this == &cmd_sp->GetCommandInterpreter()) &&1147"tried to add a CommandObject from a different interpreter");11481149if (name.empty())1150return false;11511152cmd_sp->SetIsUserCommand(false);11531154std::string name_sstr(name);1155auto name_iter = m_command_dict.find(name_sstr);1156if (name_iter != m_command_dict.end()) {1157if (!can_replace || !name_iter->second->IsRemovable())1158return false;1159name_iter->second = cmd_sp;1160} else {1161m_command_dict[name_sstr] = cmd_sp;1162}1163return true;1164}11651166Status CommandInterpreter::AddUserCommand(llvm::StringRef name,1167const lldb::CommandObjectSP &cmd_sp,1168bool can_replace) {1169Status result;1170if (cmd_sp.get())1171lldbassert((this == &cmd_sp->GetCommandInterpreter()) &&1172"tried to add a CommandObject from a different interpreter");1173if (name.empty()) {1174result.SetErrorString("can't use the empty string for a command name");1175return result;1176}1177// do not allow replacement of internal commands1178if (CommandExists(name)) {1179result.SetErrorString("can't replace builtin command");1180return result;1181}11821183if (UserCommandExists(name)) {1184if (!can_replace) {1185result.SetErrorStringWithFormatv(1186"user command \"{0}\" already exists and force replace was not set "1187"by --overwrite or 'settings set interpreter.require-overwrite "1188"false'",1189name);1190return result;1191}1192if (cmd_sp->IsMultiwordObject()) {1193if (!m_user_mw_dict[std::string(name)]->IsRemovable()) {1194result.SetErrorString(1195"can't replace explicitly non-removable multi-word command");1196return result;1197}1198} else {1199if (!m_user_dict[std::string(name)]->IsRemovable()) {1200result.SetErrorString("can't replace explicitly non-removable command");1201return result;1202}1203}1204}12051206cmd_sp->SetIsUserCommand(true);12071208if (cmd_sp->IsMultiwordObject())1209m_user_mw_dict[std::string(name)] = cmd_sp;1210else1211m_user_dict[std::string(name)] = cmd_sp;1212return result;1213}12141215CommandObjectSP1216CommandInterpreter::GetCommandSPExact(llvm::StringRef cmd_str,1217bool include_aliases) const {1218// Break up the command string into words, in case it's a multi-word command.1219Args cmd_words(cmd_str);12201221if (cmd_str.empty())1222return {};12231224if (cmd_words.GetArgumentCount() == 1)1225return GetCommandSP(cmd_str, include_aliases, true);12261227// We have a multi-word command (seemingly), so we need to do more work.1228// First, get the cmd_obj_sp for the first word in the command.1229CommandObjectSP cmd_obj_sp =1230GetCommandSP(cmd_words.GetArgumentAtIndex(0), include_aliases, true);1231if (!cmd_obj_sp)1232return {};12331234// Loop through the rest of the words in the command (everything passed in1235// was supposed to be part of a command name), and find the appropriate1236// sub-command SP for each command word....1237size_t end = cmd_words.GetArgumentCount();1238for (size_t i = 1; i < end; ++i) {1239if (!cmd_obj_sp->IsMultiwordObject()) {1240// We have more words in the command name, but we don't have a1241// multiword object. Fail and return.1242return {};1243}12441245cmd_obj_sp = cmd_obj_sp->GetSubcommandSP(cmd_words.GetArgumentAtIndex(i));1246if (!cmd_obj_sp) {1247// The sub-command name was invalid. Fail and return.1248return {};1249}1250}12511252// We successfully looped through all the command words and got valid1253// command objects for them.1254return cmd_obj_sp;1255}12561257CommandObject *1258CommandInterpreter::GetCommandObject(llvm::StringRef cmd_str,1259StringList *matches,1260StringList *descriptions) const {1261// Try to find a match among commands and aliases. Allowing inexact matches,1262// but perferring exact matches.1263return GetCommandSP(cmd_str, /*include_aliases=*/true, /*exact=*/false,1264matches, descriptions)1265.get();1266}12671268CommandObject *CommandInterpreter::GetUserCommandObject(1269llvm::StringRef cmd, StringList *matches, StringList *descriptions) const {1270std::string cmd_str(cmd);1271auto find_exact = [&](const CommandObject::CommandMap &map) {1272auto found_elem = map.find(std::string(cmd));1273if (found_elem == map.end())1274return (CommandObject *)nullptr;1275CommandObject *exact_cmd = found_elem->second.get();1276if (exact_cmd) {1277if (matches)1278matches->AppendString(exact_cmd->GetCommandName());1279if (descriptions)1280descriptions->AppendString(exact_cmd->GetHelp());1281return exact_cmd;1282}1283return (CommandObject *)nullptr;1284};12851286CommandObject *exact_cmd = find_exact(GetUserCommands());1287if (exact_cmd)1288return exact_cmd;12891290exact_cmd = find_exact(GetUserMultiwordCommands());1291if (exact_cmd)1292return exact_cmd;12931294// We didn't have an exact command, so now look for partial matches.1295StringList tmp_list;1296StringList *matches_ptr = matches ? matches : &tmp_list;1297AddNamesMatchingPartialString(GetUserCommands(), cmd_str, *matches_ptr);1298AddNamesMatchingPartialString(GetUserMultiwordCommands(),1299cmd_str, *matches_ptr);13001301return {};1302}13031304bool CommandInterpreter::CommandExists(llvm::StringRef cmd) const {1305return m_command_dict.find(std::string(cmd)) != m_command_dict.end();1306}13071308bool CommandInterpreter::GetAliasFullName(llvm::StringRef cmd,1309std::string &full_name) const {1310bool exact_match =1311(m_alias_dict.find(std::string(cmd)) != m_alias_dict.end());1312if (exact_match) {1313full_name.assign(std::string(cmd));1314return exact_match;1315} else {1316StringList matches;1317size_t num_alias_matches;1318num_alias_matches =1319AddNamesMatchingPartialString(m_alias_dict, cmd, matches);1320if (num_alias_matches == 1) {1321// Make sure this isn't shadowing a command in the regular command space:1322StringList regular_matches;1323const bool include_aliases = false;1324const bool exact = false;1325CommandObjectSP cmd_obj_sp(1326GetCommandSP(cmd, include_aliases, exact, ®ular_matches));1327if (cmd_obj_sp || regular_matches.GetSize() > 0)1328return false;1329else {1330full_name.assign(matches.GetStringAtIndex(0));1331return true;1332}1333} else1334return false;1335}1336}13371338bool CommandInterpreter::AliasExists(llvm::StringRef cmd) const {1339return m_alias_dict.find(std::string(cmd)) != m_alias_dict.end();1340}13411342bool CommandInterpreter::UserCommandExists(llvm::StringRef cmd) const {1343return m_user_dict.find(std::string(cmd)) != m_user_dict.end();1344}13451346bool CommandInterpreter::UserMultiwordCommandExists(llvm::StringRef cmd) const {1347return m_user_mw_dict.find(std::string(cmd)) != m_user_mw_dict.end();1348}13491350CommandAlias *1351CommandInterpreter::AddAlias(llvm::StringRef alias_name,1352lldb::CommandObjectSP &command_obj_sp,1353llvm::StringRef args_string) {1354if (command_obj_sp.get())1355lldbassert((this == &command_obj_sp->GetCommandInterpreter()) &&1356"tried to add a CommandObject from a different interpreter");13571358std::unique_ptr<CommandAlias> command_alias_up(1359new CommandAlias(*this, command_obj_sp, args_string, alias_name));13601361if (command_alias_up && command_alias_up->IsValid()) {1362m_alias_dict[std::string(alias_name)] =1363CommandObjectSP(command_alias_up.get());1364return command_alias_up.release();1365}13661367return nullptr;1368}13691370bool CommandInterpreter::RemoveAlias(llvm::StringRef alias_name) {1371auto pos = m_alias_dict.find(std::string(alias_name));1372if (pos != m_alias_dict.end()) {1373m_alias_dict.erase(pos);1374return true;1375}1376return false;1377}13781379bool CommandInterpreter::RemoveCommand(llvm::StringRef cmd, bool force) {1380auto pos = m_command_dict.find(std::string(cmd));1381if (pos != m_command_dict.end()) {1382if (force || pos->second->IsRemovable()) {1383// Only regular expression objects or python commands are removable under1384// normal circumstances.1385m_command_dict.erase(pos);1386return true;1387}1388}1389return false;1390}13911392bool CommandInterpreter::RemoveUser(llvm::StringRef user_name) {1393CommandObject::CommandMap::iterator pos =1394m_user_dict.find(std::string(user_name));1395if (pos != m_user_dict.end()) {1396m_user_dict.erase(pos);1397return true;1398}1399return false;1400}14011402bool CommandInterpreter::RemoveUserMultiword(llvm::StringRef multi_name) {1403CommandObject::CommandMap::iterator pos =1404m_user_mw_dict.find(std::string(multi_name));1405if (pos != m_user_mw_dict.end()) {1406m_user_mw_dict.erase(pos);1407return true;1408}1409return false;1410}14111412void CommandInterpreter::GetHelp(CommandReturnObject &result,1413uint32_t cmd_types) {1414llvm::StringRef help_prologue(GetDebugger().GetIOHandlerHelpPrologue());1415if (!help_prologue.empty()) {1416OutputFormattedHelpText(result.GetOutputStream(), llvm::StringRef(),1417help_prologue);1418}14191420CommandObject::CommandMap::const_iterator pos;1421size_t max_len = FindLongestCommandWord(m_command_dict);14221423if ((cmd_types & eCommandTypesBuiltin) == eCommandTypesBuiltin) {1424result.AppendMessage("Debugger commands:");1425result.AppendMessage("");14261427for (pos = m_command_dict.begin(); pos != m_command_dict.end(); ++pos) {1428if (!(cmd_types & eCommandTypesHidden) &&1429(pos->first.compare(0, 1, "_") == 0))1430continue;14311432OutputFormattedHelpText(result.GetOutputStream(), pos->first, "--",1433pos->second->GetHelp(), max_len);1434}1435result.AppendMessage("");1436}14371438if (!m_alias_dict.empty() &&1439((cmd_types & eCommandTypesAliases) == eCommandTypesAliases)) {1440result.AppendMessageWithFormat(1441"Current command abbreviations "1442"(type '%shelp command alias' for more info):\n",1443GetCommandPrefix());1444result.AppendMessage("");1445max_len = FindLongestCommandWord(m_alias_dict);14461447for (auto alias_pos = m_alias_dict.begin(); alias_pos != m_alias_dict.end();1448++alias_pos) {1449OutputFormattedHelpText(result.GetOutputStream(), alias_pos->first, "--",1450alias_pos->second->GetHelp(), max_len);1451}1452result.AppendMessage("");1453}14541455if (!m_user_dict.empty() &&1456((cmd_types & eCommandTypesUserDef) == eCommandTypesUserDef)) {1457result.AppendMessage("Current user-defined commands:");1458result.AppendMessage("");1459max_len = FindLongestCommandWord(m_user_dict);1460for (pos = m_user_dict.begin(); pos != m_user_dict.end(); ++pos) {1461OutputFormattedHelpText(result.GetOutputStream(), pos->first, "--",1462pos->second->GetHelp(), max_len);1463}1464result.AppendMessage("");1465}14661467if (!m_user_mw_dict.empty() &&1468((cmd_types & eCommandTypesUserMW) == eCommandTypesUserMW)) {1469result.AppendMessage("Current user-defined container commands:");1470result.AppendMessage("");1471max_len = FindLongestCommandWord(m_user_mw_dict);1472for (pos = m_user_mw_dict.begin(); pos != m_user_mw_dict.end(); ++pos) {1473OutputFormattedHelpText(result.GetOutputStream(), pos->first, "--",1474pos->second->GetHelp(), max_len);1475}1476result.AppendMessage("");1477}14781479result.AppendMessageWithFormat(1480"For more information on any command, type '%shelp <command-name>'.\n",1481GetCommandPrefix());1482}14831484CommandObject *CommandInterpreter::GetCommandObjectForCommand(1485llvm::StringRef &command_string) {1486// This function finds the final, lowest-level, alias-resolved command object1487// whose 'Execute' function will eventually be invoked by the given command1488// line.14891490CommandObject *cmd_obj = nullptr;1491size_t start = command_string.find_first_not_of(k_white_space);1492size_t end = 0;1493bool done = false;1494while (!done) {1495if (start != std::string::npos) {1496// Get the next word from command_string.1497end = command_string.find_first_of(k_white_space, start);1498if (end == std::string::npos)1499end = command_string.size();1500std::string cmd_word =1501std::string(command_string.substr(start, end - start));15021503if (cmd_obj == nullptr)1504// Since cmd_obj is NULL we are on our first time through this loop.1505// Check to see if cmd_word is a valid command or alias.1506cmd_obj = GetCommandObject(cmd_word);1507else if (cmd_obj->IsMultiwordObject()) {1508// Our current object is a multi-word object; see if the cmd_word is a1509// valid sub-command for our object.1510CommandObject *sub_cmd_obj =1511cmd_obj->GetSubcommandObject(cmd_word.c_str());1512if (sub_cmd_obj)1513cmd_obj = sub_cmd_obj;1514else // cmd_word was not a valid sub-command word, so we are done1515done = true;1516} else1517// We have a cmd_obj and it is not a multi-word object, so we are done.1518done = true;15191520// If we didn't find a valid command object, or our command object is not1521// a multi-word object, or we are at the end of the command_string, then1522// we are done. Otherwise, find the start of the next word.15231524if (!cmd_obj || !cmd_obj->IsMultiwordObject() ||1525end >= command_string.size())1526done = true;1527else1528start = command_string.find_first_not_of(k_white_space, end);1529} else1530// Unable to find any more words.1531done = true;1532}15331534command_string = command_string.substr(end);1535return cmd_obj;1536}15371538static const char *k_valid_command_chars =1539"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";1540static void StripLeadingSpaces(std::string &s) {1541if (!s.empty()) {1542size_t pos = s.find_first_not_of(k_white_space);1543if (pos == std::string::npos)1544s.clear();1545else if (pos == 0)1546return;1547s.erase(0, pos);1548}1549}15501551static size_t FindArgumentTerminator(const std::string &s) {1552const size_t s_len = s.size();1553size_t offset = 0;1554while (offset < s_len) {1555size_t pos = s.find("--", offset);1556if (pos == std::string::npos)1557break;1558if (pos > 0) {1559if (llvm::isSpace(s[pos - 1])) {1560// Check if the string ends "\s--" (where \s is a space character) or1561// if we have "\s--\s".1562if ((pos + 2 >= s_len) || llvm::isSpace(s[pos + 2])) {1563return pos;1564}1565}1566}1567offset = pos + 2;1568}1569return std::string::npos;1570}15711572static bool ExtractCommand(std::string &command_string, std::string &command,1573std::string &suffix, char "e_char) {1574command.clear();1575suffix.clear();1576StripLeadingSpaces(command_string);15771578bool result = false;1579quote_char = '\0';15801581if (!command_string.empty()) {1582const char first_char = command_string[0];1583if (first_char == '\'' || first_char == '"') {1584quote_char = first_char;1585const size_t end_quote_pos = command_string.find(quote_char, 1);1586if (end_quote_pos == std::string::npos) {1587command.swap(command_string);1588command_string.erase();1589} else {1590command.assign(command_string, 1, end_quote_pos - 1);1591if (end_quote_pos + 1 < command_string.size())1592command_string.erase(0, command_string.find_first_not_of(1593k_white_space, end_quote_pos + 1));1594else1595command_string.erase();1596}1597} else {1598const size_t first_space_pos =1599command_string.find_first_of(k_white_space);1600if (first_space_pos == std::string::npos) {1601command.swap(command_string);1602command_string.erase();1603} else {1604command.assign(command_string, 0, first_space_pos);1605command_string.erase(0, command_string.find_first_not_of(1606k_white_space, first_space_pos));1607}1608}1609result = true;1610}16111612if (!command.empty()) {1613// actual commands can't start with '-' or '_'1614if (command[0] != '-' && command[0] != '_') {1615size_t pos = command.find_first_not_of(k_valid_command_chars);1616if (pos > 0 && pos != std::string::npos) {1617suffix.assign(command.begin() + pos, command.end());1618command.erase(pos);1619}1620}1621}16221623return result;1624}16251626CommandObject *CommandInterpreter::BuildAliasResult(1627llvm::StringRef alias_name, std::string &raw_input_string,1628std::string &alias_result, CommandReturnObject &result) {1629CommandObject *alias_cmd_obj = nullptr;1630Args cmd_args(raw_input_string);1631alias_cmd_obj = GetCommandObject(alias_name);1632StreamString result_str;16331634if (!alias_cmd_obj || !alias_cmd_obj->IsAlias()) {1635alias_result.clear();1636return alias_cmd_obj;1637}1638std::pair<CommandObjectSP, OptionArgVectorSP> desugared =1639((CommandAlias *)alias_cmd_obj)->Desugar();1640OptionArgVectorSP option_arg_vector_sp = desugared.second;1641alias_cmd_obj = desugared.first.get();1642std::string alias_name_str = std::string(alias_name);1643if ((cmd_args.GetArgumentCount() == 0) ||1644(alias_name_str != cmd_args.GetArgumentAtIndex(0)))1645cmd_args.Unshift(alias_name_str);16461647result_str.Printf("%s", alias_cmd_obj->GetCommandName().str().c_str());16481649if (!option_arg_vector_sp.get()) {1650alias_result = std::string(result_str.GetString());1651return alias_cmd_obj;1652}1653OptionArgVector *option_arg_vector = option_arg_vector_sp.get();16541655int value_type;1656std::string option;1657std::string value;1658for (const auto &entry : *option_arg_vector) {1659std::tie(option, value_type, value) = entry;1660if (option == g_argument) {1661result_str.Printf(" %s", value.c_str());1662continue;1663}16641665result_str.Printf(" %s", option.c_str());1666if (value_type == OptionParser::eNoArgument)1667continue;16681669if (value_type != OptionParser::eOptionalArgument)1670result_str.Printf(" ");1671int index = GetOptionArgumentPosition(value.c_str());1672if (index == 0)1673result_str.Printf("%s", value.c_str());1674else if (static_cast<size_t>(index) >= cmd_args.GetArgumentCount()) {16751676result.AppendErrorWithFormat("Not enough arguments provided; you "1677"need at least %d arguments to use "1678"this alias.\n",1679index);1680return nullptr;1681} else {1682const Args::ArgEntry &entry = cmd_args[index];1683size_t strpos = raw_input_string.find(entry.c_str());1684const char quote_char = entry.GetQuoteChar();1685if (strpos != std::string::npos) {1686const size_t start_fudge = quote_char == '\0' ? 0 : 1;1687const size_t len_fudge = quote_char == '\0' ? 0 : 2;16881689// Make sure we aren't going outside the bounds of the cmd string:1690if (strpos < start_fudge) {1691result.AppendError("Unmatched quote at command beginning.");1692return nullptr;1693}1694llvm::StringRef arg_text = entry.ref();1695if (strpos - start_fudge + arg_text.size() + len_fudge >1696raw_input_string.size()) {1697result.AppendError("Unmatched quote at command end.");1698return nullptr;1699}1700raw_input_string = raw_input_string.erase(1701strpos - start_fudge,1702strlen(cmd_args.GetArgumentAtIndex(index)) + len_fudge);1703}1704if (quote_char == '\0')1705result_str.Printf("%s", cmd_args.GetArgumentAtIndex(index));1706else1707result_str.Printf("%c%s%c", quote_char, entry.c_str(), quote_char);1708}1709}17101711alias_result = std::string(result_str.GetString());1712return alias_cmd_obj;1713}17141715Status CommandInterpreter::PreprocessCommand(std::string &command) {1716// The command preprocessor needs to do things to the command line before any1717// parsing of arguments or anything else is done. The only current stuff that1718// gets preprocessed is anything enclosed in backtick ('`') characters is1719// evaluated as an expression and the result of the expression must be a1720// scalar that can be substituted into the command. An example would be:1721// (lldb) memory read `$rsp + 20`1722Status error; // Status for any expressions that might not evaluate1723size_t start_backtick;1724size_t pos = 0;1725while ((start_backtick = command.find('`', pos)) != std::string::npos) {1726// Stop if an error was encountered during the previous iteration.1727if (error.Fail())1728break;17291730if (start_backtick > 0 && command[start_backtick - 1] == '\\') {1731// The backtick was preceded by a '\' character, remove the slash and1732// don't treat the backtick as the start of an expression.1733command.erase(start_backtick - 1, 1);1734// No need to add one to start_backtick since we just deleted a char.1735pos = start_backtick;1736continue;1737}17381739const size_t expr_content_start = start_backtick + 1;1740const size_t end_backtick = command.find('`', expr_content_start);17411742if (end_backtick == std::string::npos) {1743// Stop if there's no end backtick.1744break;1745}17461747if (end_backtick == expr_content_start) {1748// Skip over empty expression. (two backticks in a row)1749command.erase(start_backtick, 2);1750continue;1751}17521753std::string expr_str(command, expr_content_start,1754end_backtick - expr_content_start);1755error = PreprocessToken(expr_str);1756// We always stop at the first error:1757if (error.Fail())1758break;17591760command.erase(start_backtick, end_backtick - start_backtick + 1);1761command.insert(start_backtick, std::string(expr_str));1762pos = start_backtick + expr_str.size();1763}1764return error;1765}17661767Status1768CommandInterpreter::PreprocessToken(std::string &expr_str) {1769Status error;1770ExecutionContext exe_ctx(GetExecutionContext());17711772// Get a dummy target to allow for calculator mode while processing1773// backticks. This also helps break the infinite loop caused when target is1774// null.1775Target *exe_target = exe_ctx.GetTargetPtr();1776Target &target = exe_target ? *exe_target : m_debugger.GetDummyTarget();17771778ValueObjectSP expr_result_valobj_sp;17791780EvaluateExpressionOptions options;1781options.SetCoerceToId(false);1782options.SetUnwindOnError(true);1783options.SetIgnoreBreakpoints(true);1784options.SetKeepInMemory(false);1785options.SetTryAllThreads(true);1786options.SetTimeout(std::nullopt);17871788ExpressionResults expr_result =1789target.EvaluateExpression(expr_str.c_str(), exe_ctx.GetFramePtr(),1790expr_result_valobj_sp, options);17911792if (expr_result == eExpressionCompleted) {1793Scalar scalar;1794if (expr_result_valobj_sp)1795expr_result_valobj_sp =1796expr_result_valobj_sp->GetQualifiedRepresentationIfAvailable(1797expr_result_valobj_sp->GetDynamicValueType(), true);1798if (expr_result_valobj_sp->ResolveValue(scalar)) {17991800StreamString value_strm;1801const bool show_type = false;1802scalar.GetValue(value_strm, show_type);1803size_t value_string_size = value_strm.GetSize();1804if (value_string_size) {1805expr_str = value_strm.GetData();1806} else {1807error.SetErrorStringWithFormat("expression value didn't result "1808"in a scalar value for the "1809"expression '%s'",1810expr_str.c_str());1811}1812} else {1813error.SetErrorStringWithFormat("expression value didn't result "1814"in a scalar value for the "1815"expression '%s'",1816expr_str.c_str());1817}1818return error;1819}18201821// If we have an error from the expression evaluation it will be in the1822// ValueObject error, which won't be success and we will just report it.1823// But if for some reason we didn't get a value object at all, then we will1824// make up some helpful errors from the expression result.1825if (expr_result_valobj_sp)1826error = expr_result_valobj_sp->GetError();18271828if (error.Success()) {1829std::string result = lldb_private::toString(expr_result);1830error.SetErrorString(result + "for the expression '" + expr_str + "'");1831}1832return error;1833}18341835bool CommandInterpreter::HandleCommand(const char *command_line,1836LazyBool lazy_add_to_history,1837const ExecutionContext &override_context,1838CommandReturnObject &result) {18391840OverrideExecutionContext(override_context);1841bool status = HandleCommand(command_line, lazy_add_to_history, result);1842RestoreExecutionContext();1843return status;1844}18451846bool CommandInterpreter::HandleCommand(const char *command_line,1847LazyBool lazy_add_to_history,1848CommandReturnObject &result,1849bool force_repeat_command) {1850std::string command_string(command_line);1851std::string original_command_string(command_line);18521853Log *log = GetLog(LLDBLog::Commands);1854llvm::PrettyStackTraceFormat stack_trace("HandleCommand(command = \"%s\")",1855command_line);18561857LLDB_LOGF(log, "Processing command: %s", command_line);1858LLDB_SCOPED_TIMERF("Processing command: %s.", command_line);18591860if (INTERRUPT_REQUESTED(GetDebugger(), "Interrupted initiating command")) {1861result.AppendError("... Interrupted");1862return false;1863}18641865bool add_to_history;1866if (lazy_add_to_history == eLazyBoolCalculate)1867add_to_history = (m_command_source_depth == 0);1868else1869add_to_history = (lazy_add_to_history == eLazyBoolYes);18701871// The same `transcript_item` will be used below to add output and error of1872// the command.1873StructuredData::DictionarySP transcript_item;1874if (GetSaveTranscript()) {1875m_transcript_stream << "(lldb) " << command_line << '\n';18761877transcript_item = std::make_shared<StructuredData::Dictionary>();1878transcript_item->AddStringItem("command", command_line);1879transcript_item->AddIntegerItem(1880"timestampInEpochSeconds",1881std::chrono::duration_cast<std::chrono::seconds>(1882std::chrono::system_clock::now().time_since_epoch())1883.count());1884m_transcript.AddItem(transcript_item);1885}18861887bool empty_command = false;1888bool comment_command = false;1889if (command_string.empty())1890empty_command = true;1891else {1892const char *k_space_characters = "\t\n\v\f\r ";18931894size_t non_space = command_string.find_first_not_of(k_space_characters);1895// Check for empty line or comment line (lines whose first non-space1896// character is the comment character for this interpreter)1897if (non_space == std::string::npos)1898empty_command = true;1899else if (command_string[non_space] == m_comment_char)1900comment_command = true;1901else if (command_string[non_space] == CommandHistory::g_repeat_char) {1902llvm::StringRef search_str(command_string);1903search_str = search_str.drop_front(non_space);1904if (auto hist_str = m_command_history.FindString(search_str)) {1905add_to_history = false;1906command_string = std::string(*hist_str);1907original_command_string = std::string(*hist_str);1908} else {1909result.AppendErrorWithFormat("Could not find entry: %s in history",1910command_string.c_str());1911return false;1912}1913}1914}19151916if (empty_command) {1917if (!GetRepeatPreviousCommand()) {1918result.SetStatus(eReturnStatusSuccessFinishNoResult);1919return true;1920}19211922if (m_command_history.IsEmpty()) {1923result.AppendError("empty command");1924return false;1925}19261927command_line = m_repeat_command.c_str();1928command_string = command_line;1929original_command_string = command_line;1930if (m_repeat_command.empty()) {1931result.AppendError("No auto repeat.");1932return false;1933}19341935add_to_history = false;1936} else if (comment_command) {1937result.SetStatus(eReturnStatusSuccessFinishNoResult);1938return true;1939}19401941// Phase 1.19421943// Before we do ANY kind of argument processing, we need to figure out what1944// the real/final command object is for the specified command. This gets1945// complicated by the fact that the user could have specified an alias, and,1946// in translating the alias, there may also be command options and/or even1947// data (including raw text strings) that need to be found and inserted into1948// the command line as part of the translation. So this first step is plain1949// look-up and replacement, resulting in:1950// 1. the command object whose Execute method will actually be called1951// 2. a revised command string, with all substitutions and replacements1952// taken care of1953// From 1 above, we can determine whether the Execute function wants raw1954// input or not.19551956CommandObject *cmd_obj = ResolveCommandImpl(command_string, result);19571958// We have to preprocess the whole command string for Raw commands, since we1959// don't know the structure of the command. For parsed commands, we only1960// treat backticks as quote characters specially.1961// FIXME: We probably want to have raw commands do their own preprocessing.1962// For instance, I don't think people expect substitution in expr expressions.1963if (cmd_obj && cmd_obj->WantsRawCommandString()) {1964Status error(PreprocessCommand(command_string));19651966if (error.Fail()) {1967result.AppendError(error.AsCString());1968return false;1969}1970}19711972// Although the user may have abbreviated the command, the command_string now1973// has the command expanded to the full name. For example, if the input was1974// "br s -n main", command_string is now "breakpoint set -n main".1975if (log) {1976llvm::StringRef command_name = cmd_obj ? cmd_obj->GetCommandName() : "<not found>";1977LLDB_LOGF(log, "HandleCommand, cmd_obj : '%s'", command_name.str().c_str());1978LLDB_LOGF(log, "HandleCommand, (revised) command_string: '%s'",1979command_string.c_str());1980const bool wants_raw_input =1981(cmd_obj != nullptr) ? cmd_obj->WantsRawCommandString() : false;1982LLDB_LOGF(log, "HandleCommand, wants_raw_input:'%s'",1983wants_raw_input ? "True" : "False");1984}19851986// Phase 2.1987// Take care of things like setting up the history command & calling the1988// appropriate Execute method on the CommandObject, with the appropriate1989// arguments.1990StatsDuration execute_time;1991if (cmd_obj != nullptr) {1992bool generate_repeat_command = add_to_history;1993// If we got here when empty_command was true, then this command is a1994// stored "repeat command" which we should give a chance to produce it's1995// repeat command, even though we don't add repeat commands to the history.1996generate_repeat_command |= empty_command;1997// For `command regex`, the regex command (ex `bt`) is added to history, but1998// the resolved command (ex `thread backtrace`) is _not_ added to history.1999// However, the resolved command must be given the opportunity to provide a2000// repeat command. `force_repeat_command` supports this case.2001generate_repeat_command |= force_repeat_command;2002if (generate_repeat_command) {2003Args command_args(command_string);2004std::optional<std::string> repeat_command =2005cmd_obj->GetRepeatCommand(command_args, 0);2006if (repeat_command) {2007LLDB_LOGF(log, "Repeat command: %s", repeat_command->data());2008m_repeat_command.assign(*repeat_command);2009} else {2010m_repeat_command.assign(original_command_string);2011}2012}20132014if (add_to_history)2015m_command_history.AppendString(original_command_string);20162017std::string remainder;2018const std::size_t actual_cmd_name_len = cmd_obj->GetCommandName().size();2019if (actual_cmd_name_len < command_string.length())2020remainder = command_string.substr(actual_cmd_name_len);20212022// Remove any initial spaces2023size_t pos = remainder.find_first_not_of(k_white_space);2024if (pos != 0 && pos != std::string::npos)2025remainder.erase(0, pos);20262027LLDB_LOGF(2028log, "HandleCommand, command line after removing command name(s): '%s'",2029remainder.c_str());20302031// To test whether or not transcript should be saved, `transcript_item` is2032// used instead of `GetSaveTrasncript()`. This is because the latter will2033// fail when the command is "settings set interpreter.save-transcript true".2034if (transcript_item) {2035transcript_item->AddStringItem("commandName", cmd_obj->GetCommandName());2036transcript_item->AddStringItem("commandArguments", remainder);2037}20382039ElapsedTime elapsed(execute_time);2040cmd_obj->Execute(remainder.c_str(), result);2041}20422043LLDB_LOGF(log, "HandleCommand, command %s",2044(result.Succeeded() ? "succeeded" : "did not succeed"));20452046// To test whether or not transcript should be saved, `transcript_item` is2047// used instead of `GetSaveTrasncript()`. This is because the latter will2048// fail when the command is "settings set interpreter.save-transcript true".2049if (transcript_item) {2050m_transcript_stream << result.GetOutputData();2051m_transcript_stream << result.GetErrorData();20522053transcript_item->AddStringItem("output", result.GetOutputData());2054transcript_item->AddStringItem("error", result.GetErrorData());2055transcript_item->AddFloatItem("durationInSeconds",2056execute_time.get().count());2057}20582059return result.Succeeded();2060}20612062void CommandInterpreter::HandleCompletionMatches(CompletionRequest &request) {2063bool look_for_subcommand = false;20642065// For any of the command completions a unique match will be a complete word.20662067if (request.GetParsedLine().GetArgumentCount() == 0) {2068// We got nothing on the command line, so return the list of commands2069bool include_aliases = true;2070StringList new_matches, descriptions;2071GetCommandNamesMatchingPartialString("", include_aliases, new_matches,2072descriptions);2073request.AddCompletions(new_matches, descriptions);2074} else if (request.GetCursorIndex() == 0) {2075// The cursor is in the first argument, so just do a lookup in the2076// dictionary.2077StringList new_matches, new_descriptions;2078CommandObject *cmd_obj =2079GetCommandObject(request.GetParsedLine().GetArgumentAtIndex(0),2080&new_matches, &new_descriptions);20812082if (new_matches.GetSize() && cmd_obj && cmd_obj->IsMultiwordObject() &&2083new_matches.GetStringAtIndex(0) != nullptr &&2084strcmp(request.GetParsedLine().GetArgumentAtIndex(0),2085new_matches.GetStringAtIndex(0)) == 0) {2086if (request.GetParsedLine().GetArgumentCount() != 1) {2087look_for_subcommand = true;2088new_matches.DeleteStringAtIndex(0);2089new_descriptions.DeleteStringAtIndex(0);2090request.AppendEmptyArgument();2091}2092}2093request.AddCompletions(new_matches, new_descriptions);2094}20952096if (request.GetCursorIndex() > 0 || look_for_subcommand) {2097// We are completing further on into a commands arguments, so find the2098// command and tell it to complete the command. First see if there is a2099// matching initial command:2100CommandObject *command_object =2101GetCommandObject(request.GetParsedLine().GetArgumentAtIndex(0));2102if (command_object) {2103request.ShiftArguments();2104command_object->HandleCompletion(request);2105}2106}2107}21082109void CommandInterpreter::HandleCompletion(CompletionRequest &request) {21102111// Don't complete comments, and if the line we are completing is just the2112// history repeat character, substitute the appropriate history line.2113llvm::StringRef first_arg = request.GetParsedLine().GetArgumentAtIndex(0);21142115if (!first_arg.empty()) {2116if (first_arg.front() == m_comment_char)2117return;2118if (first_arg.front() == CommandHistory::g_repeat_char) {2119if (auto hist_str = m_command_history.FindString(first_arg))2120request.AddCompletion(*hist_str, "Previous command history event",2121CompletionMode::RewriteLine);2122return;2123}2124}21252126HandleCompletionMatches(request);2127}21282129std::optional<std::string>2130CommandInterpreter::GetAutoSuggestionForCommand(llvm::StringRef line) {2131if (line.empty())2132return std::nullopt;2133const size_t s = m_command_history.GetSize();2134for (int i = s - 1; i >= 0; --i) {2135llvm::StringRef entry = m_command_history.GetStringAtIndex(i);2136if (entry.consume_front(line))2137return entry.str();2138}2139return std::nullopt;2140}21412142void CommandInterpreter::UpdatePrompt(llvm::StringRef new_prompt) {2143EventSP prompt_change_event_sp(2144new Event(eBroadcastBitResetPrompt, new EventDataBytes(new_prompt)));2145;2146BroadcastEvent(prompt_change_event_sp);2147if (m_command_io_handler_sp)2148m_command_io_handler_sp->SetPrompt(new_prompt);2149}21502151bool CommandInterpreter::Confirm(llvm::StringRef message, bool default_answer) {2152// Check AutoConfirm first:2153if (m_debugger.GetAutoConfirm())2154return default_answer;21552156IOHandlerConfirm *confirm =2157new IOHandlerConfirm(m_debugger, message, default_answer);2158IOHandlerSP io_handler_sp(confirm);2159m_debugger.RunIOHandlerSync(io_handler_sp);2160return confirm->GetResponse();2161}21622163const CommandAlias *2164CommandInterpreter::GetAlias(llvm::StringRef alias_name) const {2165OptionArgVectorSP ret_val;21662167auto pos = m_alias_dict.find(std::string(alias_name));2168if (pos != m_alias_dict.end())2169return (CommandAlias *)pos->second.get();21702171return nullptr;2172}21732174bool CommandInterpreter::HasCommands() const { return (!m_command_dict.empty()); }21752176bool CommandInterpreter::HasAliases() const { return (!m_alias_dict.empty()); }21772178bool CommandInterpreter::HasUserCommands() const { return (!m_user_dict.empty()); }21792180bool CommandInterpreter::HasUserMultiwordCommands() const {2181return (!m_user_mw_dict.empty());2182}21832184bool CommandInterpreter::HasAliasOptions() const { return HasAliases(); }21852186void CommandInterpreter::BuildAliasCommandArgs(CommandObject *alias_cmd_obj,2187const char *alias_name,2188Args &cmd_args,2189std::string &raw_input_string,2190CommandReturnObject &result) {2191OptionArgVectorSP option_arg_vector_sp =2192GetAlias(alias_name)->GetOptionArguments();21932194bool wants_raw_input = alias_cmd_obj->WantsRawCommandString();21952196// Make sure that the alias name is the 0th element in cmd_args2197std::string alias_name_str = alias_name;2198if (alias_name_str != cmd_args.GetArgumentAtIndex(0))2199cmd_args.Unshift(alias_name_str);22002201Args new_args(alias_cmd_obj->GetCommandName());2202if (new_args.GetArgumentCount() == 2)2203new_args.Shift();22042205if (option_arg_vector_sp.get()) {2206if (wants_raw_input) {2207// We have a command that both has command options and takes raw input.2208// Make *sure* it has a " -- " in the right place in the2209// raw_input_string.2210size_t pos = raw_input_string.find(" -- ");2211if (pos == std::string::npos) {2212// None found; assume it goes at the beginning of the raw input string2213raw_input_string.insert(0, " -- ");2214}2215}22162217OptionArgVector *option_arg_vector = option_arg_vector_sp.get();2218const size_t old_size = cmd_args.GetArgumentCount();2219std::vector<bool> used(old_size + 1, false);22202221used[0] = true;22222223int value_type;2224std::string option;2225std::string value;2226for (const auto &option_entry : *option_arg_vector) {2227std::tie(option, value_type, value) = option_entry;2228if (option == g_argument) {2229if (!wants_raw_input || (value != "--")) {2230// Since we inserted this above, make sure we don't insert it twice2231new_args.AppendArgument(value);2232}2233continue;2234}22352236if (value_type != OptionParser::eOptionalArgument)2237new_args.AppendArgument(option);22382239if (value == g_no_argument)2240continue;22412242int index = GetOptionArgumentPosition(value.c_str());2243if (index == 0) {2244// value was NOT a positional argument; must be a real value2245if (value_type != OptionParser::eOptionalArgument)2246new_args.AppendArgument(value);2247else {2248new_args.AppendArgument(option + value);2249}22502251} else if (static_cast<size_t>(index) >= cmd_args.GetArgumentCount()) {2252result.AppendErrorWithFormat("Not enough arguments provided; you "2253"need at least %d arguments to use "2254"this alias.\n",2255index);2256return;2257} else {2258// Find and remove cmd_args.GetArgumentAtIndex(i) from raw_input_string2259size_t strpos =2260raw_input_string.find(cmd_args.GetArgumentAtIndex(index));2261if (strpos != std::string::npos) {2262raw_input_string = raw_input_string.erase(2263strpos, strlen(cmd_args.GetArgumentAtIndex(index)));2264}22652266if (value_type != OptionParser::eOptionalArgument)2267new_args.AppendArgument(cmd_args.GetArgumentAtIndex(index));2268else {2269new_args.AppendArgument(option + cmd_args.GetArgumentAtIndex(index));2270}2271used[index] = true;2272}2273}22742275for (auto entry : llvm::enumerate(cmd_args.entries())) {2276if (!used[entry.index()] && !wants_raw_input)2277new_args.AppendArgument(entry.value().ref());2278}22792280cmd_args.Clear();2281cmd_args.SetArguments(new_args.GetArgumentCount(),2282new_args.GetConstArgumentVector());2283} else {2284result.SetStatus(eReturnStatusSuccessFinishNoResult);2285// This alias was not created with any options; nothing further needs to be2286// done, unless it is a command that wants raw input, in which case we need2287// to clear the rest of the data from cmd_args, since its in the raw input2288// string.2289if (wants_raw_input) {2290cmd_args.Clear();2291cmd_args.SetArguments(new_args.GetArgumentCount(),2292new_args.GetConstArgumentVector());2293}2294return;2295}22962297result.SetStatus(eReturnStatusSuccessFinishNoResult);2298}22992300int CommandInterpreter::GetOptionArgumentPosition(const char *in_string) {2301int position = 0; // Any string that isn't an argument position, i.e. '%'2302// followed by an integer, gets a position2303// of zero.23042305const char *cptr = in_string;23062307// Does it start with '%'2308if (cptr[0] == '%') {2309++cptr;23102311// Is the rest of it entirely digits?2312if (isdigit(cptr[0])) {2313const char *start = cptr;2314while (isdigit(cptr[0]))2315++cptr;23162317// We've gotten to the end of the digits; are we at the end of the2318// string?2319if (cptr[0] == '\0')2320position = atoi(start);2321}2322}23232324return position;2325}23262327static void GetHomeInitFile(llvm::SmallVectorImpl<char> &init_file,2328llvm::StringRef suffix = {}) {2329std::string init_file_name = ".lldbinit";2330if (!suffix.empty()) {2331init_file_name.append("-");2332init_file_name.append(suffix.str());2333}23342335FileSystem::Instance().GetHomeDirectory(init_file);2336llvm::sys::path::append(init_file, init_file_name);23372338FileSystem::Instance().Resolve(init_file);2339}23402341static void GetHomeREPLInitFile(llvm::SmallVectorImpl<char> &init_file,2342LanguageType language) {2343if (language == eLanguageTypeUnknown) {2344LanguageSet repl_languages = Language::GetLanguagesSupportingREPLs();2345if (auto main_repl_language = repl_languages.GetSingularLanguage())2346language = *main_repl_language;2347else2348return;2349}23502351std::string init_file_name =2352(llvm::Twine(".lldbinit-") +2353llvm::Twine(Language::GetNameForLanguageType(language)) +2354llvm::Twine("-repl"))2355.str();2356FileSystem::Instance().GetHomeDirectory(init_file);2357llvm::sys::path::append(init_file, init_file_name);2358FileSystem::Instance().Resolve(init_file);2359}23602361static void GetCwdInitFile(llvm::SmallVectorImpl<char> &init_file) {2362llvm::StringRef s = ".lldbinit";2363init_file.assign(s.begin(), s.end());2364FileSystem::Instance().Resolve(init_file);2365}23662367void CommandInterpreter::SourceInitFile(FileSpec file,2368CommandReturnObject &result) {2369assert(!m_skip_lldbinit_files);23702371if (!FileSystem::Instance().Exists(file)) {2372result.SetStatus(eReturnStatusSuccessFinishNoResult);2373return;2374}23752376// Use HandleCommand to 'source' the given file; this will do the actual2377// broadcasting of the commands back to any appropriate listener (see2378// CommandObjectSource::Execute for more details).2379const bool saved_batch = SetBatchCommandMode(true);2380CommandInterpreterRunOptions options;2381options.SetSilent(true);2382options.SetPrintErrors(true);2383options.SetStopOnError(false);2384options.SetStopOnContinue(true);2385HandleCommandsFromFile(file, options, result);2386SetBatchCommandMode(saved_batch);2387}23882389void CommandInterpreter::SourceInitFileCwd(CommandReturnObject &result) {2390if (m_skip_lldbinit_files) {2391result.SetStatus(eReturnStatusSuccessFinishNoResult);2392return;2393}23942395llvm::SmallString<128> init_file;2396GetCwdInitFile(init_file);2397if (!FileSystem::Instance().Exists(init_file)) {2398result.SetStatus(eReturnStatusSuccessFinishNoResult);2399return;2400}24012402LoadCWDlldbinitFile should_load =2403Target::GetGlobalProperties().GetLoadCWDlldbinitFile();24042405switch (should_load) {2406case eLoadCWDlldbinitFalse:2407result.SetStatus(eReturnStatusSuccessFinishNoResult);2408break;2409case eLoadCWDlldbinitTrue:2410SourceInitFile(FileSpec(init_file.str()), result);2411break;2412case eLoadCWDlldbinitWarn: {2413llvm::SmallString<128> home_init_file;2414GetHomeInitFile(home_init_file);2415if (llvm::sys::path::parent_path(init_file) ==2416llvm::sys::path::parent_path(home_init_file)) {2417result.SetStatus(eReturnStatusSuccessFinishNoResult);2418} else {2419result.AppendError(InitFileWarning);2420}2421}2422}2423}24242425/// We will first see if there is an application specific ".lldbinit" file2426/// whose name is "~/.lldbinit" followed by a "-" and the name of the program.2427/// If this file doesn't exist, we fall back to the REPL init file or the2428/// default home init file in "~/.lldbinit".2429void CommandInterpreter::SourceInitFileHome(CommandReturnObject &result,2430bool is_repl) {2431if (m_skip_lldbinit_files) {2432result.SetStatus(eReturnStatusSuccessFinishNoResult);2433return;2434}24352436llvm::SmallString<128> init_file;24372438if (is_repl)2439GetHomeREPLInitFile(init_file, GetDebugger().GetREPLLanguage());24402441if (init_file.empty())2442GetHomeInitFile(init_file);24432444if (!m_skip_app_init_files) {2445llvm::StringRef program_name =2446HostInfo::GetProgramFileSpec().GetFilename().GetStringRef();2447llvm::SmallString<128> program_init_file;2448GetHomeInitFile(program_init_file, program_name);2449if (FileSystem::Instance().Exists(program_init_file))2450init_file = program_init_file;2451}24522453SourceInitFile(FileSpec(init_file.str()), result);2454}24552456void CommandInterpreter::SourceInitFileGlobal(CommandReturnObject &result) {2457#ifdef LLDB_GLOBAL_INIT_DIRECTORY2458if (!m_skip_lldbinit_files) {2459FileSpec init_file(LLDB_GLOBAL_INIT_DIRECTORY);2460if (init_file)2461init_file.MakeAbsolute(HostInfo::GetShlibDir());24622463init_file.AppendPathComponent("lldbinit");2464SourceInitFile(init_file, result);2465return;2466}2467#endif2468result.SetStatus(eReturnStatusSuccessFinishNoResult);2469}24702471const char *CommandInterpreter::GetCommandPrefix() {2472const char *prefix = GetDebugger().GetIOHandlerCommandPrefix();2473return prefix == nullptr ? "" : prefix;2474}24752476PlatformSP CommandInterpreter::GetPlatform(bool prefer_target_platform) {2477PlatformSP platform_sp;2478if (prefer_target_platform) {2479ExecutionContext exe_ctx(GetExecutionContext());2480Target *target = exe_ctx.GetTargetPtr();2481if (target)2482platform_sp = target->GetPlatform();2483}24842485if (!platform_sp)2486platform_sp = m_debugger.GetPlatformList().GetSelectedPlatform();2487return platform_sp;2488}24892490bool CommandInterpreter::DidProcessStopAbnormally() const {2491auto exe_ctx = GetExecutionContext();2492TargetSP target_sp = exe_ctx.GetTargetSP();2493if (!target_sp)2494return false;24952496ProcessSP process_sp(target_sp->GetProcessSP());2497if (!process_sp)2498return false;24992500if (eStateStopped != process_sp->GetState())2501return false;25022503for (const auto &thread_sp : process_sp->GetThreadList().Threads()) {2504StopInfoSP stop_info = thread_sp->GetStopInfo();2505if (!stop_info) {2506// If there's no stop_info, keep iterating through the other threads;2507// it's enough that any thread has got a stop_info that indicates2508// an abnormal stop, to consider the process to be stopped abnormally.2509continue;2510}25112512const StopReason reason = stop_info->GetStopReason();2513if (reason == eStopReasonException ||2514reason == eStopReasonInstrumentation ||2515reason == eStopReasonProcessorTrace)2516return true;25172518if (reason == eStopReasonSignal) {2519const auto stop_signal = static_cast<int32_t>(stop_info->GetValue());2520UnixSignalsSP signals_sp = process_sp->GetUnixSignals();2521if (!signals_sp || !signals_sp->SignalIsValid(stop_signal))2522// The signal is unknown, treat it as abnormal.2523return true;25242525const auto sigint_num = signals_sp->GetSignalNumberFromName("SIGINT");2526const auto sigstop_num = signals_sp->GetSignalNumberFromName("SIGSTOP");2527if ((stop_signal != sigint_num) && (stop_signal != sigstop_num))2528// The signal very likely implies a crash.2529return true;2530}2531}25322533return false;2534}25352536void2537CommandInterpreter::HandleCommands(const StringList &commands,2538const ExecutionContext &override_context,2539const CommandInterpreterRunOptions &options,2540CommandReturnObject &result) {25412542OverrideExecutionContext(override_context);2543HandleCommands(commands, options, result);2544RestoreExecutionContext();2545}25462547void CommandInterpreter::HandleCommands(const StringList &commands,2548const CommandInterpreterRunOptions &options,2549CommandReturnObject &result) {2550size_t num_lines = commands.GetSize();25512552// If we are going to continue past a "continue" then we need to run the2553// commands synchronously. Make sure you reset this value anywhere you return2554// from the function.25552556bool old_async_execution = m_debugger.GetAsyncExecution();25572558if (!options.GetStopOnContinue()) {2559m_debugger.SetAsyncExecution(false);2560}25612562for (size_t idx = 0; idx < num_lines; idx++) {2563const char *cmd = commands.GetStringAtIndex(idx);2564if (cmd[0] == '\0')2565continue;25662567if (options.GetEchoCommands()) {2568// TODO: Add Stream support.2569result.AppendMessageWithFormat("%s %s\n",2570m_debugger.GetPrompt().str().c_str(), cmd);2571}25722573CommandReturnObject tmp_result(m_debugger.GetUseColor());2574tmp_result.SetInteractive(result.GetInteractive());2575tmp_result.SetSuppressImmediateOutput(true);25762577// We might call into a regex or alias command, in which case the2578// add_to_history will get lost. This m_command_source_depth dingus is the2579// way we turn off adding to the history in that case, so set it up here.2580if (!options.GetAddToHistory())2581m_command_source_depth++;2582bool success = HandleCommand(cmd, options.m_add_to_history, tmp_result);2583if (!options.GetAddToHistory())2584m_command_source_depth--;25852586if (options.GetPrintResults()) {2587if (tmp_result.Succeeded())2588result.AppendMessage(tmp_result.GetOutputData());2589}25902591if (!success || !tmp_result.Succeeded()) {2592llvm::StringRef error_msg = tmp_result.GetErrorData();2593if (error_msg.empty())2594error_msg = "<unknown error>.\n";2595if (options.GetStopOnError()) {2596result.AppendErrorWithFormat(2597"Aborting reading of commands after command #%" PRIu642598": '%s' failed with %s",2599(uint64_t)idx, cmd, error_msg.str().c_str());2600m_debugger.SetAsyncExecution(old_async_execution);2601return;2602} else if (options.GetPrintResults()) {2603result.AppendMessageWithFormat(2604"Command #%" PRIu64 " '%s' failed with %s", (uint64_t)idx + 1, cmd,2605error_msg.str().c_str());2606}2607}26082609if (result.GetImmediateOutputStream())2610result.GetImmediateOutputStream()->Flush();26112612if (result.GetImmediateErrorStream())2613result.GetImmediateErrorStream()->Flush();26142615// N.B. Can't depend on DidChangeProcessState, because the state coming2616// into the command execution could be running (for instance in Breakpoint2617// Commands. So we check the return value to see if it is has running in2618// it.2619if ((tmp_result.GetStatus() == eReturnStatusSuccessContinuingNoResult) ||2620(tmp_result.GetStatus() == eReturnStatusSuccessContinuingResult)) {2621if (options.GetStopOnContinue()) {2622// If we caused the target to proceed, and we're going to stop in that2623// case, set the status in our real result before returning. This is2624// an error if the continue was not the last command in the set of2625// commands to be run.2626if (idx != num_lines - 1)2627result.AppendErrorWithFormat(2628"Aborting reading of commands after command #%" PRIu642629": '%s' continued the target.\n",2630(uint64_t)idx + 1, cmd);2631else2632result.AppendMessageWithFormat("Command #%" PRIu642633" '%s' continued the target.\n",2634(uint64_t)idx + 1, cmd);26352636result.SetStatus(tmp_result.GetStatus());2637m_debugger.SetAsyncExecution(old_async_execution);26382639return;2640}2641}26422643// Also check for "stop on crash here:2644if (tmp_result.GetDidChangeProcessState() && options.GetStopOnCrash() &&2645DidProcessStopAbnormally()) {2646if (idx != num_lines - 1)2647result.AppendErrorWithFormat(2648"Aborting reading of commands after command #%" PRIu642649": '%s' stopped with a signal or exception.\n",2650(uint64_t)idx + 1, cmd);2651else2652result.AppendMessageWithFormat(2653"Command #%" PRIu64 " '%s' stopped with a signal or exception.\n",2654(uint64_t)idx + 1, cmd);26552656result.SetStatus(tmp_result.GetStatus());2657m_debugger.SetAsyncExecution(old_async_execution);26582659return;2660}2661}26622663result.SetStatus(eReturnStatusSuccessFinishResult);2664m_debugger.SetAsyncExecution(old_async_execution);2665}26662667// Make flags that we can pass into the IOHandler so our delegates can do the2668// right thing2669enum {2670eHandleCommandFlagStopOnContinue = (1u << 0),2671eHandleCommandFlagStopOnError = (1u << 1),2672eHandleCommandFlagEchoCommand = (1u << 2),2673eHandleCommandFlagEchoCommentCommand = (1u << 3),2674eHandleCommandFlagPrintResult = (1u << 4),2675eHandleCommandFlagPrintErrors = (1u << 5),2676eHandleCommandFlagStopOnCrash = (1u << 6),2677eHandleCommandFlagAllowRepeats = (1u << 7)2678};26792680void CommandInterpreter::HandleCommandsFromFile(2681FileSpec &cmd_file, const ExecutionContext &context,2682const CommandInterpreterRunOptions &options, CommandReturnObject &result) {2683OverrideExecutionContext(context);2684HandleCommandsFromFile(cmd_file, options, result);2685RestoreExecutionContext();2686}26872688void CommandInterpreter::HandleCommandsFromFile(FileSpec &cmd_file,2689const CommandInterpreterRunOptions &options, CommandReturnObject &result) {2690if (!FileSystem::Instance().Exists(cmd_file)) {2691result.AppendErrorWithFormat(2692"Error reading commands from file %s - file not found.\n",2693cmd_file.GetFilename().AsCString("<Unknown>"));2694return;2695}26962697std::string cmd_file_path = cmd_file.GetPath();2698auto input_file_up =2699FileSystem::Instance().Open(cmd_file, File::eOpenOptionReadOnly);2700if (!input_file_up) {2701std::string error = llvm::toString(input_file_up.takeError());2702result.AppendErrorWithFormatv(2703"error: an error occurred read file '{0}': {1}\n", cmd_file_path,2704llvm::fmt_consume(input_file_up.takeError()));2705return;2706}2707FileSP input_file_sp = FileSP(std::move(input_file_up.get()));27082709Debugger &debugger = GetDebugger();27102711uint32_t flags = 0;27122713if (options.m_stop_on_continue == eLazyBoolCalculate) {2714if (m_command_source_flags.empty()) {2715// Stop on continue by default2716flags |= eHandleCommandFlagStopOnContinue;2717} else if (m_command_source_flags.back() &2718eHandleCommandFlagStopOnContinue) {2719flags |= eHandleCommandFlagStopOnContinue;2720}2721} else if (options.m_stop_on_continue == eLazyBoolYes) {2722flags |= eHandleCommandFlagStopOnContinue;2723}27242725if (options.m_stop_on_error == eLazyBoolCalculate) {2726if (m_command_source_flags.empty()) {2727if (GetStopCmdSourceOnError())2728flags |= eHandleCommandFlagStopOnError;2729} else if (m_command_source_flags.back() & eHandleCommandFlagStopOnError) {2730flags |= eHandleCommandFlagStopOnError;2731}2732} else if (options.m_stop_on_error == eLazyBoolYes) {2733flags |= eHandleCommandFlagStopOnError;2734}27352736// stop-on-crash can only be set, if it is present in all levels of2737// pushed flag sets.2738if (options.GetStopOnCrash()) {2739if (m_command_source_flags.empty()) {2740flags |= eHandleCommandFlagStopOnCrash;2741} else if (m_command_source_flags.back() & eHandleCommandFlagStopOnCrash) {2742flags |= eHandleCommandFlagStopOnCrash;2743}2744}27452746if (options.m_echo_commands == eLazyBoolCalculate) {2747if (m_command_source_flags.empty()) {2748// Echo command by default2749flags |= eHandleCommandFlagEchoCommand;2750} else if (m_command_source_flags.back() & eHandleCommandFlagEchoCommand) {2751flags |= eHandleCommandFlagEchoCommand;2752}2753} else if (options.m_echo_commands == eLazyBoolYes) {2754flags |= eHandleCommandFlagEchoCommand;2755}27562757// We will only ever ask for this flag, if we echo commands in general.2758if (options.m_echo_comment_commands == eLazyBoolCalculate) {2759if (m_command_source_flags.empty()) {2760// Echo comments by default2761flags |= eHandleCommandFlagEchoCommentCommand;2762} else if (m_command_source_flags.back() &2763eHandleCommandFlagEchoCommentCommand) {2764flags |= eHandleCommandFlagEchoCommentCommand;2765}2766} else if (options.m_echo_comment_commands == eLazyBoolYes) {2767flags |= eHandleCommandFlagEchoCommentCommand;2768}27692770if (options.m_print_results == eLazyBoolCalculate) {2771if (m_command_source_flags.empty()) {2772// Print output by default2773flags |= eHandleCommandFlagPrintResult;2774} else if (m_command_source_flags.back() & eHandleCommandFlagPrintResult) {2775flags |= eHandleCommandFlagPrintResult;2776}2777} else if (options.m_print_results == eLazyBoolYes) {2778flags |= eHandleCommandFlagPrintResult;2779}27802781if (options.m_print_errors == eLazyBoolCalculate) {2782if (m_command_source_flags.empty()) {2783// Print output by default2784flags |= eHandleCommandFlagPrintErrors;2785} else if (m_command_source_flags.back() & eHandleCommandFlagPrintErrors) {2786flags |= eHandleCommandFlagPrintErrors;2787}2788} else if (options.m_print_errors == eLazyBoolYes) {2789flags |= eHandleCommandFlagPrintErrors;2790}27912792if (flags & eHandleCommandFlagPrintResult) {2793debugger.GetOutputFile().Printf("Executing commands in '%s'.\n",2794cmd_file_path.c_str());2795}27962797// Used for inheriting the right settings when "command source" might2798// have nested "command source" commands2799lldb::StreamFileSP empty_stream_sp;2800m_command_source_flags.push_back(flags);2801IOHandlerSP io_handler_sp(new IOHandlerEditline(2802debugger, IOHandler::Type::CommandInterpreter, input_file_sp,2803empty_stream_sp, // Pass in an empty stream so we inherit the top2804// input reader output stream2805empty_stream_sp, // Pass in an empty stream so we inherit the top2806// input reader error stream2807flags,2808nullptr, // Pass in NULL for "editline_name" so no history is saved,2809// or written2810debugger.GetPrompt(), llvm::StringRef(),2811false, // Not multi-line2812debugger.GetUseColor(), 0, *this));2813const bool old_async_execution = debugger.GetAsyncExecution();28142815// Set synchronous execution if we are not stopping on continue2816if ((flags & eHandleCommandFlagStopOnContinue) == 0)2817debugger.SetAsyncExecution(false);28182819m_command_source_depth++;2820m_command_source_dirs.push_back(cmd_file.CopyByRemovingLastPathComponent());28212822debugger.RunIOHandlerSync(io_handler_sp);2823if (!m_command_source_flags.empty())2824m_command_source_flags.pop_back();28252826m_command_source_dirs.pop_back();2827m_command_source_depth--;28282829result.SetStatus(eReturnStatusSuccessFinishNoResult);2830debugger.SetAsyncExecution(old_async_execution);2831}28322833bool CommandInterpreter::GetSynchronous() { return m_synchronous_execution; }28342835void CommandInterpreter::SetSynchronous(bool value) {2836m_synchronous_execution = value;2837}28382839void CommandInterpreter::OutputFormattedHelpText(Stream &strm,2840llvm::StringRef prefix,2841llvm::StringRef help_text) {2842const uint32_t max_columns = m_debugger.GetTerminalWidth();28432844size_t line_width_max = max_columns - prefix.size();2845if (line_width_max < 16)2846line_width_max = help_text.size() + prefix.size();28472848strm.IndentMore(prefix.size());2849bool prefixed_yet = false;2850// Even if we have no help text we still want to emit the command name.2851if (help_text.empty())2852help_text = "No help text";2853while (!help_text.empty()) {2854// Prefix the first line, indent subsequent lines to line up2855if (!prefixed_yet) {2856strm << prefix;2857prefixed_yet = true;2858} else2859strm.Indent();28602861// Never print more than the maximum on one line.2862llvm::StringRef this_line = help_text.substr(0, line_width_max);28632864// Always break on an explicit newline.2865std::size_t first_newline = this_line.find_first_of("\n");28662867// Don't break on space/tab unless the text is too long to fit on one line.2868std::size_t last_space = llvm::StringRef::npos;2869if (this_line.size() != help_text.size())2870last_space = this_line.find_last_of(" \t");28712872// Break at whichever condition triggered first.2873this_line = this_line.substr(0, std::min(first_newline, last_space));2874strm.PutCString(this_line);2875strm.EOL();28762877// Remove whitespace / newlines after breaking.2878help_text = help_text.drop_front(this_line.size()).ltrim();2879}2880strm.IndentLess(prefix.size());2881}28822883void CommandInterpreter::OutputFormattedHelpText(Stream &strm,2884llvm::StringRef word_text,2885llvm::StringRef separator,2886llvm::StringRef help_text,2887size_t max_word_len) {2888StreamString prefix_stream;2889prefix_stream.Printf(" %-*s %*s ", (int)max_word_len, word_text.data(),2890(int)separator.size(), separator.data());2891OutputFormattedHelpText(strm, prefix_stream.GetString(), help_text);2892}28932894void CommandInterpreter::OutputHelpText(Stream &strm, llvm::StringRef word_text,2895llvm::StringRef separator,2896llvm::StringRef help_text,2897uint32_t max_word_len) {2898int indent_size = max_word_len + separator.size() + 2;28992900strm.IndentMore(indent_size);29012902StreamString text_strm;2903text_strm.Printf("%-*s ", (int)max_word_len, word_text.data());2904text_strm << separator << " " << help_text;29052906const uint32_t max_columns = m_debugger.GetTerminalWidth();29072908llvm::StringRef text = text_strm.GetString();29092910uint32_t chars_left = max_columns;29112912auto nextWordLength = [](llvm::StringRef S) {2913size_t pos = S.find(' ');2914return pos == llvm::StringRef::npos ? S.size() : pos;2915};29162917while (!text.empty()) {2918if (text.front() == '\n' ||2919(text.front() == ' ' && nextWordLength(text.ltrim(' ')) > chars_left)) {2920strm.EOL();2921strm.Indent();2922chars_left = max_columns - indent_size;2923if (text.front() == '\n')2924text = text.drop_front();2925else2926text = text.ltrim(' ');2927} else {2928strm.PutChar(text.front());2929--chars_left;2930text = text.drop_front();2931}2932}29332934strm.EOL();2935strm.IndentLess(indent_size);2936}29372938void CommandInterpreter::FindCommandsForApropos(2939llvm::StringRef search_word, StringList &commands_found,2940StringList &commands_help, const CommandObject::CommandMap &command_map) {2941for (const auto &pair : command_map) {2942llvm::StringRef command_name = pair.first;2943CommandObject *cmd_obj = pair.second.get();29442945const bool search_short_help = true;2946const bool search_long_help = false;2947const bool search_syntax = false;2948const bool search_options = false;2949if (command_name.contains_insensitive(search_word) ||2950cmd_obj->HelpTextContainsWord(search_word, search_short_help,2951search_long_help, search_syntax,2952search_options)) {2953commands_found.AppendString(command_name);2954commands_help.AppendString(cmd_obj->GetHelp());2955}29562957if (auto *multiword_cmd = cmd_obj->GetAsMultiwordCommand()) {2958StringList subcommands_found;2959FindCommandsForApropos(search_word, subcommands_found, commands_help,2960multiword_cmd->GetSubcommandDictionary());2961for (const auto &subcommand_name : subcommands_found) {2962std::string qualified_name =2963(command_name + " " + subcommand_name).str();2964commands_found.AppendString(qualified_name);2965}2966}2967}2968}29692970void CommandInterpreter::FindCommandsForApropos(llvm::StringRef search_word,2971StringList &commands_found,2972StringList &commands_help,2973bool search_builtin_commands,2974bool search_user_commands,2975bool search_alias_commands,2976bool search_user_mw_commands) {2977CommandObject::CommandMap::const_iterator pos;29782979if (search_builtin_commands)2980FindCommandsForApropos(search_word, commands_found, commands_help,2981m_command_dict);29822983if (search_user_commands)2984FindCommandsForApropos(search_word, commands_found, commands_help,2985m_user_dict);29862987if (search_user_mw_commands)2988FindCommandsForApropos(search_word, commands_found, commands_help,2989m_user_mw_dict);29902991if (search_alias_commands)2992FindCommandsForApropos(search_word, commands_found, commands_help,2993m_alias_dict);2994}29952996ExecutionContext CommandInterpreter::GetExecutionContext() const {2997return !m_overriden_exe_contexts.empty()2998? m_overriden_exe_contexts.top()2999: m_debugger.GetSelectedExecutionContext();3000}30013002void CommandInterpreter::OverrideExecutionContext(3003const ExecutionContext &override_context) {3004m_overriden_exe_contexts.push(override_context);3005}30063007void CommandInterpreter::RestoreExecutionContext() {3008if (!m_overriden_exe_contexts.empty())3009m_overriden_exe_contexts.pop();3010}30113012void CommandInterpreter::GetProcessOutput() {3013if (ProcessSP process_sp = GetExecutionContext().GetProcessSP())3014m_debugger.FlushProcessOutput(*process_sp, /*flush_stdout*/ true,3015/*flush_stderr*/ true);3016}30173018void CommandInterpreter::StartHandlingCommand() {3019auto idle_state = CommandHandlingState::eIdle;3020if (m_command_state.compare_exchange_strong(3021idle_state, CommandHandlingState::eInProgress))3022lldbassert(m_iohandler_nesting_level == 0);3023else3024lldbassert(m_iohandler_nesting_level > 0);3025++m_iohandler_nesting_level;3026}30273028void CommandInterpreter::FinishHandlingCommand() {3029lldbassert(m_iohandler_nesting_level > 0);3030if (--m_iohandler_nesting_level == 0) {3031auto prev_state = m_command_state.exchange(CommandHandlingState::eIdle);3032lldbassert(prev_state != CommandHandlingState::eIdle);3033}3034}30353036bool CommandInterpreter::InterruptCommand() {3037auto in_progress = CommandHandlingState::eInProgress;3038return m_command_state.compare_exchange_strong(3039in_progress, CommandHandlingState::eInterrupted);3040}30413042bool CommandInterpreter::WasInterrupted() const {3043if (!m_debugger.IsIOHandlerThreadCurrentThread())3044return false;30453046bool was_interrupted =3047(m_command_state == CommandHandlingState::eInterrupted);3048lldbassert(!was_interrupted || m_iohandler_nesting_level > 0);3049return was_interrupted;3050}30513052void CommandInterpreter::PrintCommandOutput(IOHandler &io_handler,3053llvm::StringRef str,3054bool is_stdout) {30553056lldb::StreamFileSP stream = is_stdout ? io_handler.GetOutputStreamFileSP()3057: io_handler.GetErrorStreamFileSP();3058// Split the output into lines and poll for interrupt requests3059bool had_output = !str.empty();3060while (!str.empty()) {3061llvm::StringRef line;3062std::tie(line, str) = str.split('\n');3063{3064std::lock_guard<std::recursive_mutex> guard(io_handler.GetOutputMutex());3065stream->Write(line.data(), line.size());3066stream->Write("\n", 1);3067}3068}30693070std::lock_guard<std::recursive_mutex> guard(io_handler.GetOutputMutex());3071if (had_output &&3072INTERRUPT_REQUESTED(GetDebugger(), "Interrupted dumping command output"))3073stream->Printf("\n... Interrupted.\n");3074stream->Flush();3075}30763077bool CommandInterpreter::EchoCommandNonInteractive(3078llvm::StringRef line, const Flags &io_handler_flags) const {3079if (!io_handler_flags.Test(eHandleCommandFlagEchoCommand))3080return false;30813082llvm::StringRef command = line.trim();3083if (command.empty())3084return true;30853086if (command.front() == m_comment_char)3087return io_handler_flags.Test(eHandleCommandFlagEchoCommentCommand);30883089return true;3090}30913092void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler,3093std::string &line) {3094// If we were interrupted, bail out...3095if (WasInterrupted())3096return;30973098const bool is_interactive = io_handler.GetIsInteractive();3099const bool allow_repeats =3100io_handler.GetFlags().Test(eHandleCommandFlagAllowRepeats);31013102if (!is_interactive && !allow_repeats) {3103// When we are not interactive, don't execute blank lines. This will happen3104// sourcing a commands file. We don't want blank lines to repeat the3105// previous command and cause any errors to occur (like redefining an3106// alias, get an error and stop parsing the commands file).3107// But obey the AllowRepeats flag if the user has set it.3108if (line.empty())3109return;3110}3111if (!is_interactive) {3112// When using a non-interactive file handle (like when sourcing commands3113// from a file) we need to echo the command out so we don't just see the3114// command output and no command...3115if (EchoCommandNonInteractive(line, io_handler.GetFlags())) {3116std::lock_guard<std::recursive_mutex> guard(io_handler.GetOutputMutex());3117io_handler.GetOutputStreamFileSP()->Printf(3118"%s%s\n", io_handler.GetPrompt(), line.c_str());3119}3120}31213122StartHandlingCommand();31233124ExecutionContext exe_ctx = m_debugger.GetSelectedExecutionContext();3125bool pushed_exe_ctx = false;3126if (exe_ctx.HasTargetScope()) {3127OverrideExecutionContext(exe_ctx);3128pushed_exe_ctx = true;3129}3130auto finalize = llvm::make_scope_exit([this, pushed_exe_ctx]() {3131if (pushed_exe_ctx)3132RestoreExecutionContext();3133});31343135lldb_private::CommandReturnObject result(m_debugger.GetUseColor());3136HandleCommand(line.c_str(), eLazyBoolCalculate, result);31373138// Now emit the command output text from the command we just executed3139if ((result.Succeeded() &&3140io_handler.GetFlags().Test(eHandleCommandFlagPrintResult)) ||3141io_handler.GetFlags().Test(eHandleCommandFlagPrintErrors)) {3142// Display any STDOUT/STDERR _prior_ to emitting the command result text3143GetProcessOutput();31443145if (!result.GetImmediateOutputStream()) {3146llvm::StringRef output = result.GetOutputData();3147PrintCommandOutput(io_handler, output, true);3148}31493150// Now emit the command error text from the command we just executed3151if (!result.GetImmediateErrorStream()) {3152llvm::StringRef error = result.GetErrorData();3153PrintCommandOutput(io_handler, error, false);3154}3155}31563157FinishHandlingCommand();31583159switch (result.GetStatus()) {3160case eReturnStatusInvalid:3161case eReturnStatusSuccessFinishNoResult:3162case eReturnStatusSuccessFinishResult:3163case eReturnStatusStarted:3164break;31653166case eReturnStatusSuccessContinuingNoResult:3167case eReturnStatusSuccessContinuingResult:3168if (io_handler.GetFlags().Test(eHandleCommandFlagStopOnContinue))3169io_handler.SetIsDone(true);3170break;31713172case eReturnStatusFailed:3173m_result.IncrementNumberOfErrors();3174if (io_handler.GetFlags().Test(eHandleCommandFlagStopOnError)) {3175m_result.SetResult(lldb::eCommandInterpreterResultCommandError);3176io_handler.SetIsDone(true);3177}3178break;31793180case eReturnStatusQuit:3181m_result.SetResult(lldb::eCommandInterpreterResultQuitRequested);3182io_handler.SetIsDone(true);3183break;3184}31853186// Finally, if we're going to stop on crash, check that here:3187if (m_result.IsResult(lldb::eCommandInterpreterResultSuccess) &&3188result.GetDidChangeProcessState() &&3189io_handler.GetFlags().Test(eHandleCommandFlagStopOnCrash) &&3190DidProcessStopAbnormally()) {3191io_handler.SetIsDone(true);3192m_result.SetResult(lldb::eCommandInterpreterResultInferiorCrash);3193}3194}31953196bool CommandInterpreter::IOHandlerInterrupt(IOHandler &io_handler) {3197ExecutionContext exe_ctx(GetExecutionContext());3198Process *process = exe_ctx.GetProcessPtr();31993200if (InterruptCommand())3201return true;32023203if (process) {3204StateType state = process->GetState();3205if (StateIsRunningState(state)) {3206process->Halt();3207return true; // Don't do any updating when we are running3208}3209}32103211ScriptInterpreter *script_interpreter =3212m_debugger.GetScriptInterpreter(false);3213if (script_interpreter) {3214if (script_interpreter->Interrupt())3215return true;3216}3217return false;3218}32193220bool CommandInterpreter::SaveTranscript(3221CommandReturnObject &result, std::optional<std::string> output_file) {3222if (output_file == std::nullopt || output_file->empty()) {3223std::string now = llvm::to_string(std::chrono::system_clock::now());3224std::replace(now.begin(), now.end(), ' ', '_');3225// Can't have file name with colons on Windows3226std::replace(now.begin(), now.end(), ':', '-');3227const std::string file_name = "lldb_session_" + now + ".log";32283229FileSpec save_location = GetSaveSessionDirectory();32303231if (!save_location)3232save_location = HostInfo::GetGlobalTempDir();32333234FileSystem::Instance().Resolve(save_location);3235save_location.AppendPathComponent(file_name);3236output_file = save_location.GetPath();3237}32383239auto error_out = [&](llvm::StringRef error_message, std::string description) {3240LLDB_LOG(GetLog(LLDBLog::Commands), "{0} ({1}:{2})", error_message,3241output_file, description);3242result.AppendErrorWithFormatv(3243"Failed to save session's transcripts to {0}!", *output_file);3244return false;3245};32463247File::OpenOptions flags = File::eOpenOptionWriteOnly |3248File::eOpenOptionCanCreate |3249File::eOpenOptionTruncate;32503251auto opened_file = FileSystem::Instance().Open(FileSpec(*output_file), flags);32523253if (!opened_file)3254return error_out("Unable to create file",3255llvm::toString(opened_file.takeError()));32563257FileUP file = std::move(opened_file.get());32583259size_t byte_size = m_transcript_stream.GetSize();32603261Status error = file->Write(m_transcript_stream.GetData(), byte_size);32623263if (error.Fail() || byte_size != m_transcript_stream.GetSize())3264return error_out("Unable to write to destination file",3265"Bytes written do not match transcript size.");32663267result.SetStatus(eReturnStatusSuccessFinishNoResult);3268result.AppendMessageWithFormat("Session's transcripts saved to %s\n",3269output_file->c_str());32703271if (GetOpenTranscriptInEditor() && Host::IsInteractiveGraphicSession()) {3272const FileSpec file_spec;3273error = file->GetFileSpec(const_cast<FileSpec &>(file_spec));3274if (error.Success()) {3275if (llvm::Error e = Host::OpenFileInExternalEditor(3276m_debugger.GetExternalEditor(), file_spec, 1))3277result.AppendError(llvm::toString(std::move(e)));3278}3279}32803281return true;3282}32833284bool CommandInterpreter::IsInteractive() {3285return (GetIOHandler() ? GetIOHandler()->GetIsInteractive() : false);3286}32873288FileSpec CommandInterpreter::GetCurrentSourceDir() {3289if (m_command_source_dirs.empty())3290return {};3291return m_command_source_dirs.back();3292}32933294void CommandInterpreter::GetLLDBCommandsFromIOHandler(3295const char *prompt, IOHandlerDelegate &delegate, void *baton) {3296Debugger &debugger = GetDebugger();3297IOHandlerSP io_handler_sp(3298new IOHandlerEditline(debugger, IOHandler::Type::CommandList,3299"lldb", // Name of input reader for history3300llvm::StringRef(prompt), // Prompt3301llvm::StringRef(), // Continuation prompt3302true, // Get multiple lines3303debugger.GetUseColor(),33040, // Don't show line numbers3305delegate)); // IOHandlerDelegate33063307if (io_handler_sp) {3308io_handler_sp->SetUserData(baton);3309debugger.RunIOHandlerAsync(io_handler_sp);3310}3311}33123313void CommandInterpreter::GetPythonCommandsFromIOHandler(3314const char *prompt, IOHandlerDelegate &delegate, void *baton) {3315Debugger &debugger = GetDebugger();3316IOHandlerSP io_handler_sp(3317new IOHandlerEditline(debugger, IOHandler::Type::PythonCode,3318"lldb-python", // Name of input reader for history3319llvm::StringRef(prompt), // Prompt3320llvm::StringRef(), // Continuation prompt3321true, // Get multiple lines3322debugger.GetUseColor(),33230, // Don't show line numbers3324delegate)); // IOHandlerDelegate33253326if (io_handler_sp) {3327io_handler_sp->SetUserData(baton);3328debugger.RunIOHandlerAsync(io_handler_sp);3329}3330}33313332bool CommandInterpreter::IsActive() {3333return m_debugger.IsTopIOHandler(m_command_io_handler_sp);3334}33353336lldb::IOHandlerSP3337CommandInterpreter::GetIOHandler(bool force_create,3338CommandInterpreterRunOptions *options) {3339// Always re-create the IOHandlerEditline in case the input changed. The old3340// instance might have had a non-interactive input and now it does or vice3341// versa.3342if (force_create || !m_command_io_handler_sp) {3343// Always re-create the IOHandlerEditline in case the input changed. The3344// old instance might have had a non-interactive input and now it does or3345// vice versa.3346uint32_t flags = 0;33473348if (options) {3349if (options->m_stop_on_continue == eLazyBoolYes)3350flags |= eHandleCommandFlagStopOnContinue;3351if (options->m_stop_on_error == eLazyBoolYes)3352flags |= eHandleCommandFlagStopOnError;3353if (options->m_stop_on_crash == eLazyBoolYes)3354flags |= eHandleCommandFlagStopOnCrash;3355if (options->m_echo_commands != eLazyBoolNo)3356flags |= eHandleCommandFlagEchoCommand;3357if (options->m_echo_comment_commands != eLazyBoolNo)3358flags |= eHandleCommandFlagEchoCommentCommand;3359if (options->m_print_results != eLazyBoolNo)3360flags |= eHandleCommandFlagPrintResult;3361if (options->m_print_errors != eLazyBoolNo)3362flags |= eHandleCommandFlagPrintErrors;3363if (options->m_allow_repeats == eLazyBoolYes)3364flags |= eHandleCommandFlagAllowRepeats;3365} else {3366flags = eHandleCommandFlagEchoCommand | eHandleCommandFlagPrintResult |3367eHandleCommandFlagPrintErrors;3368}33693370m_command_io_handler_sp = std::make_shared<IOHandlerEditline>(3371m_debugger, IOHandler::Type::CommandInterpreter,3372m_debugger.GetInputFileSP(), m_debugger.GetOutputStreamSP(),3373m_debugger.GetErrorStreamSP(), flags, "lldb", m_debugger.GetPrompt(),3374llvm::StringRef(), // Continuation prompt3375false, // Don't enable multiple line input, just single line commands3376m_debugger.GetUseColor(),33770, // Don't show line numbers3378*this); // IOHandlerDelegate3379}3380return m_command_io_handler_sp;3381}33823383CommandInterpreterRunResult CommandInterpreter::RunCommandInterpreter(3384CommandInterpreterRunOptions &options) {3385// Always re-create the command interpreter when we run it in case any file3386// handles have changed.3387bool force_create = true;3388m_debugger.RunIOHandlerAsync(GetIOHandler(force_create, &options));3389m_result = CommandInterpreterRunResult();33903391if (options.GetAutoHandleEvents())3392m_debugger.StartEventHandlerThread();33933394if (options.GetSpawnThread()) {3395m_debugger.StartIOHandlerThread();3396} else {3397// If the current thread is not managed by a host thread, we won't detect3398// that this IS the CommandInterpreter IOHandler thread, so make it so:3399HostThread new_io_handler_thread(Host::GetCurrentThread());3400HostThread old_io_handler_thread =3401m_debugger.SetIOHandlerThread(new_io_handler_thread);3402m_debugger.RunIOHandlers();3403m_debugger.SetIOHandlerThread(old_io_handler_thread);34043405if (options.GetAutoHandleEvents())3406m_debugger.StopEventHandlerThread();3407}34083409return m_result;3410}34113412CommandObject *3413CommandInterpreter::ResolveCommandImpl(std::string &command_line,3414CommandReturnObject &result) {3415std::string scratch_command(command_line); // working copy so we don't modify3416// command_line unless we succeed3417CommandObject *cmd_obj = nullptr;3418StreamString revised_command_line;3419bool wants_raw_input = false;3420std::string next_word;3421StringList matches;3422bool done = false;3423while (!done) {3424char quote_char = '\0';3425std::string suffix;3426ExtractCommand(scratch_command, next_word, suffix, quote_char);3427if (cmd_obj == nullptr) {3428std::string full_name;3429bool is_alias = GetAliasFullName(next_word, full_name);3430cmd_obj = GetCommandObject(next_word, &matches);3431bool is_real_command =3432(!is_alias) || (cmd_obj != nullptr && !cmd_obj->IsAlias());3433if (!is_real_command) {3434matches.Clear();3435std::string alias_result;3436cmd_obj =3437BuildAliasResult(full_name, scratch_command, alias_result, result);3438revised_command_line.Printf("%s", alias_result.c_str());3439if (cmd_obj) {3440wants_raw_input = cmd_obj->WantsRawCommandString();3441}3442} else {3443if (cmd_obj) {3444llvm::StringRef cmd_name = cmd_obj->GetCommandName();3445revised_command_line.Printf("%s", cmd_name.str().c_str());3446wants_raw_input = cmd_obj->WantsRawCommandString();3447} else {3448revised_command_line.Printf("%s", next_word.c_str());3449}3450}3451} else {3452if (cmd_obj->IsMultiwordObject()) {3453CommandObject *sub_cmd_obj =3454cmd_obj->GetSubcommandObject(next_word.c_str());3455if (sub_cmd_obj) {3456// The subcommand's name includes the parent command's name, so3457// restart rather than append to the revised_command_line.3458llvm::StringRef sub_cmd_name = sub_cmd_obj->GetCommandName();3459revised_command_line.Clear();3460revised_command_line.Printf("%s", sub_cmd_name.str().c_str());3461cmd_obj = sub_cmd_obj;3462wants_raw_input = cmd_obj->WantsRawCommandString();3463} else {3464if (quote_char)3465revised_command_line.Printf(" %c%s%s%c", quote_char,3466next_word.c_str(), suffix.c_str(),3467quote_char);3468else3469revised_command_line.Printf(" %s%s", next_word.c_str(),3470suffix.c_str());3471done = true;3472}3473} else {3474if (quote_char)3475revised_command_line.Printf(" %c%s%s%c", quote_char,3476next_word.c_str(), suffix.c_str(),3477quote_char);3478else3479revised_command_line.Printf(" %s%s", next_word.c_str(),3480suffix.c_str());3481done = true;3482}3483}34843485if (cmd_obj == nullptr) {3486const size_t num_matches = matches.GetSize();3487if (matches.GetSize() > 1) {3488StreamString error_msg;3489error_msg.Printf("Ambiguous command '%s'. Possible matches:\n",3490next_word.c_str());34913492for (uint32_t i = 0; i < num_matches; ++i) {3493error_msg.Printf("\t%s\n", matches.GetStringAtIndex(i));3494}3495result.AppendRawError(error_msg.GetString());3496} else {3497// We didn't have only one match, otherwise we wouldn't get here.3498lldbassert(num_matches == 0);3499result.AppendErrorWithFormat("'%s' is not a valid command.\n",3500next_word.c_str());3501}3502return nullptr;3503}35043505if (cmd_obj->IsMultiwordObject()) {3506if (!suffix.empty()) {3507result.AppendErrorWithFormat(3508"command '%s' did not recognize '%s%s%s' as valid (subcommand "3509"might be invalid).\n",3510cmd_obj->GetCommandName().str().c_str(),3511next_word.empty() ? "" : next_word.c_str(),3512next_word.empty() ? " -- " : " ", suffix.c_str());3513return nullptr;3514}3515} else {3516// If we found a normal command, we are done3517done = true;3518if (!suffix.empty()) {3519switch (suffix[0]) {3520case '/':3521// GDB format suffixes3522{3523Options *command_options = cmd_obj->GetOptions();3524if (command_options &&3525command_options->SupportsLongOption("gdb-format")) {3526std::string gdb_format_option("--gdb-format=");3527gdb_format_option += (suffix.c_str() + 1);35283529std::string cmd = std::string(revised_command_line.GetString());3530size_t arg_terminator_idx = FindArgumentTerminator(cmd);3531if (arg_terminator_idx != std::string::npos) {3532// Insert the gdb format option before the "--" that terminates3533// options3534gdb_format_option.append(1, ' ');3535cmd.insert(arg_terminator_idx, gdb_format_option);3536revised_command_line.Clear();3537revised_command_line.PutCString(cmd);3538} else3539revised_command_line.Printf(" %s", gdb_format_option.c_str());35403541if (wants_raw_input &&3542FindArgumentTerminator(cmd) == std::string::npos)3543revised_command_line.PutCString(" --");3544} else {3545result.AppendErrorWithFormat(3546"the '%s' command doesn't support the --gdb-format option\n",3547cmd_obj->GetCommandName().str().c_str());3548return nullptr;3549}3550}3551break;35523553default:3554result.AppendErrorWithFormat(3555"unknown command shorthand suffix: '%s'\n", suffix.c_str());3556return nullptr;3557}3558}3559}3560if (scratch_command.empty())3561done = true;3562}35633564if (!scratch_command.empty())3565revised_command_line.Printf(" %s", scratch_command.c_str());35663567if (cmd_obj != nullptr)3568command_line = std::string(revised_command_line.GetString());35693570return cmd_obj;3571}35723573llvm::json::Value CommandInterpreter::GetStatistics() {3574llvm::json::Object stats;3575for (const auto &command_usage : m_command_usages)3576stats.try_emplace(command_usage.getKey(), command_usage.getValue());3577return stats;3578}35793580const StructuredData::Array &CommandInterpreter::GetTranscript() const {3581return m_transcript;3582}358335843585