diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp')
| -rw-r--r-- | contrib/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp | 529 |
1 files changed, 365 insertions, 164 deletions
diff --git a/contrib/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp b/contrib/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp index fc07168b6c0a..00c3472444d2 100644 --- a/contrib/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp +++ b/contrib/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp @@ -30,6 +30,7 @@ #include "Commands/CommandObjectPlatform.h" #include "Commands/CommandObjectPlugin.h" #include "Commands/CommandObjectProcess.h" +#include "Commands/CommandObjectProtocolServer.h" #include "Commands/CommandObjectQuit.h" #include "Commands/CommandObjectRegexCommand.h" #include "Commands/CommandObjectRegister.h" @@ -46,7 +47,9 @@ #include "Commands/CommandObjectWatchpoint.h" #include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" +#include "lldb/Core/Telemetry.h" #include "lldb/Host/StreamFile.h" #include "lldb/Utility/ErrorMessages.h" #include "lldb/Utility/LLDBLog.h" @@ -57,6 +60,7 @@ #include "lldb/Utility/Timer.h" #include "lldb/Host/Config.h" +#include "lldb/lldb-forward.h" #if LLDB_ENABLE_LIBEDIT #include "lldb/Host/Editline.h" #endif @@ -87,6 +91,7 @@ #include "llvm/Support/Path.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/ScopedPrinter.h" +#include "llvm/Telemetry/Telemetry.h" #if defined(__APPLE__) #include <TargetConditionals.h> @@ -113,7 +118,6 @@ const char *CommandInterpreter::g_no_argument = "<no-argument>"; const char *CommandInterpreter::g_need_argument = "<need-argument>"; const char *CommandInterpreter::g_argument = "<argument>"; - #define LLDB_PROPERTIES_interpreter #include "InterpreterProperties.inc" @@ -285,8 +289,6 @@ bool CommandInterpreter::GetRequireCommandOverwrite() const { void CommandInterpreter::Initialize() { LLDB_SCOPED_TIMER(); - CommandReturnObject result(m_debugger.GetUseColor()); - LoadCommandDictionary(); // An alias arguments vector to reuse - reset it before use... @@ -441,20 +443,23 @@ void CommandInterpreter::Initialize() { cmd_obj_sp = GetCommandSPExact("expression"); if (cmd_obj_sp) { + // Ensure `e` runs `expression`. + AddAlias("e", cmd_obj_sp); AddAlias("call", cmd_obj_sp, "--")->SetHelpLong(""); CommandAlias *parray_alias = AddAlias("parray", cmd_obj_sp, "--element-count %1 --"); if (parray_alias) { - parray_alias->SetHelp - ("parray <COUNT> <EXPRESSION> -- lldb will evaluate EXPRESSION " - "to get a typed-pointer-to-an-array in memory, and will display " - "COUNT elements of that type from the array."); - parray_alias->SetHelpLong(""); + parray_alias->SetHelp( + "parray <COUNT> <EXPRESSION> -- lldb will evaluate EXPRESSION " + "to get a typed-pointer-to-an-array in memory, and will display " + "COUNT elements of that type from the array."); + parray_alias->SetHelpLong(""); } - CommandAlias *poarray_alias = AddAlias("poarray", cmd_obj_sp, - "--object-description --element-count %1 --"); + CommandAlias *poarray_alias = AddAlias( + "poarray", cmd_obj_sp, "--object-description --element-count %1 --"); if (poarray_alias) { - poarray_alias->SetHelp("poarray <COUNT> <EXPRESSION> -- lldb will " + poarray_alias->SetHelp( + "poarray <COUNT> <EXPRESSION> -- lldb will " "evaluate EXPRESSION to get the address of an array of COUNT " "objects in memory, and will call po on them."); poarray_alias->SetHelpLong(""); @@ -520,10 +525,6 @@ void CommandInterpreter::Initialize() { cmd_obj_sp = GetCommandSPExact("scripting run"); if (cmd_obj_sp) { - AddAlias("sc", cmd_obj_sp); - AddAlias("scr", cmd_obj_sp); - AddAlias("scri", cmd_obj_sp); - AddAlias("scrip", cmd_obj_sp); AddAlias("script", cmd_obj_sp); } @@ -538,9 +539,7 @@ void CommandInterpreter::Initialize() { } } -void CommandInterpreter::Clear() { - m_command_io_handler_sp.reset(); -} +void CommandInterpreter::Clear() { m_command_io_handler_sp.reset(); } const char *CommandInterpreter::ProcessEmbeddedScriptCommands(const char *arg) { // This function has not yet been implemented. @@ -576,6 +575,7 @@ void CommandInterpreter::LoadCommandDictionary() { REGISTER_COMMAND_OBJECT("platform", CommandObjectPlatform); REGISTER_COMMAND_OBJECT("plugin", CommandObjectPlugin); REGISTER_COMMAND_OBJECT("process", CommandObjectMultiwordProcess); + REGISTER_COMMAND_OBJECT("protocol-server", CommandObjectProtocolServer); REGISTER_COMMAND_OBJECT("quit", CommandObjectQuit); REGISTER_COMMAND_OBJECT("register", CommandObjectRegister); REGISTER_COMMAND_OBJECT("scripting", CommandObjectMultiwordScripting); @@ -801,7 +801,7 @@ void CommandInterpreter::LoadCommandDictionary() { new CommandObjectRegexCommand( *this, "gdb-remote", "Connect to a process via remote GDB server.\n" - "If no host is specifed, localhost is assumed.\n" + "If no host is specified, localhost is assumed.\n" "gdb-remote is an abbreviation for 'process connect --plugin " "gdb-remote connect://<hostname>:<port>'\n", "gdb-remote [<hostname>:]<portnum>", 0, false)); @@ -839,11 +839,12 @@ void CommandInterpreter::LoadCommandDictionary() { std::unique_ptr<CommandObjectRegexCommand> bt_regex_cmd_up( new CommandObjectRegexCommand( *this, "_regexp-bt", - "Show backtrace of the current thread's call stack. Any numeric " - "argument displays at most that many frames. The argument 'all' " - "displays all threads. Use 'settings set frame-format' to customize " + "Show backtrace of the current thread's call stack. Any numeric " + "argument displays at most that many frames. The argument 'all' " + "displays all threads. Use 'settings set frame-format' to customize " "the printing of individual frames and 'settings set thread-format' " - "to customize the thread header.", + "to customize the thread header. Frame recognizers may filter the " + "list. Use 'thread backtrace -u (--unfiltered)' to see them all.", "bt [<digit> | all]", 0, false)); if (bt_regex_cmd_up) { // accept but don't document "bt -c <number>" -- before bt was a regex @@ -852,10 +853,12 @@ void CommandInterpreter::LoadCommandDictionary() { // now "bt 3" is the preferred form, in line with gdb. if (bt_regex_cmd_up->AddRegexCommand("^([[:digit:]]+)[[:space:]]*$", "thread backtrace -c %1") && - bt_regex_cmd_up->AddRegexCommand("^-c ([[:digit:]]+)[[:space:]]*$", - "thread backtrace -c %1") && - bt_regex_cmd_up->AddRegexCommand("^all[[:space:]]*$", "thread backtrace all") && - bt_regex_cmd_up->AddRegexCommand("^[[:space:]]*$", "thread backtrace")) { + bt_regex_cmd_up->AddRegexCommand("^(-[^[:space:]].*)$", + "thread backtrace %1") && + bt_regex_cmd_up->AddRegexCommand("^all[[:space:]]*$", + "thread backtrace all") && + bt_regex_cmd_up->AddRegexCommand("^[[:space:]]*$", + "thread backtrace")) { CommandObjectSP command_sp(bt_regex_cmd_up.release()); m_command_dict[std::string(command_sp->GetCommandName())] = command_sp; } @@ -956,28 +959,32 @@ int CommandInterpreter::GetCommandNamesMatchingPartialString( return matches.GetSize(); } -CommandObjectMultiword *CommandInterpreter::VerifyUserMultiwordCmdPath( - Args &path, bool leaf_is_command, Status &result) { +CommandObjectMultiword * +CommandInterpreter::VerifyUserMultiwordCmdPath(Args &path, bool leaf_is_command, + Status &result) { result.Clear(); auto get_multi_or_report_error = [&result](CommandObjectSP cmd_sp, - const char *name) -> CommandObjectMultiword * { + const char *name) -> CommandObjectMultiword * { if (!cmd_sp) { - result.SetErrorStringWithFormat("Path component: '%s' not found", name); + result = Status::FromErrorStringWithFormat( + "Path component: '%s' not found", name); return nullptr; } if (!cmd_sp->IsUserCommand()) { - result.SetErrorStringWithFormat("Path component: '%s' is not a user " - "command", - name); + result = Status::FromErrorStringWithFormat( + "Path component: '%s' is not a user " + "command", + name); return nullptr; } CommandObjectMultiword *cmd_as_multi = cmd_sp->GetAsMultiwordCommand(); if (!cmd_as_multi) { - result.SetErrorStringWithFormat("Path component: '%s' is not a container " - "command", - name); + result = Status::FromErrorStringWithFormat( + "Path component: '%s' is not a container " + "command", + name); return nullptr; } return cmd_as_multi; @@ -985,7 +992,7 @@ CommandObjectMultiword *CommandInterpreter::VerifyUserMultiwordCmdPath( size_t num_args = path.GetArgumentCount(); if (num_args == 0) { - result.SetErrorString("empty command path"); + result = Status::FromErrorString("empty command path"); return nullptr; } @@ -1013,6 +1020,28 @@ CommandObjectMultiword *CommandInterpreter::VerifyUserMultiwordCmdPath( return cur_as_multi; } +CommandObjectSP CommandInterpreter::GetFrameLanguageCommand() const { + auto frame_sp = GetExecutionContext().GetFrameSP(); + if (!frame_sp) + return {}; + auto frame_language = + Language::GetPrimaryLanguage(frame_sp->GuessLanguage().AsLanguageType()); + + auto it = m_command_dict.find("language"); + if (it == m_command_dict.end()) + return {}; + // The root "language" command. + CommandObjectSP language_cmd_sp = it->second; + + auto *plugin = Language::FindPlugin(frame_language); + if (!plugin) + return {}; + // "cplusplus", "objc", etc. + auto lang_name = plugin->GetPluginName(); + + return language_cmd_sp->GetSubcommandSPExact(lang_name); +} + CommandObjectSP CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, bool include_aliases, bool exact, StringList *matches, @@ -1131,7 +1160,34 @@ CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, bool include_aliases, else return user_match_sp; } - } else if (matches && command_sp) { + } + + // When no single match is found, attempt to resolve the command as a language + // plugin subcommand. + if (!command_sp) { + // The `language` subcommand ("language objc", "language cplusplus", etc). + CommandObjectMultiword *lang_subcmd = nullptr; + if (auto lang_subcmd_sp = GetFrameLanguageCommand()) { + lang_subcmd = lang_subcmd_sp->GetAsMultiwordCommand(); + command_sp = lang_subcmd_sp->GetSubcommandSPExact(cmd_str); + } + + if (!command_sp && !exact && lang_subcmd) { + StringList lang_matches; + AddNamesMatchingPartialString(lang_subcmd->GetSubcommandDictionary(), + cmd_str, lang_matches, descriptions); + if (matches) + matches->AppendList(lang_matches); + if (lang_matches.GetSize() == 1) { + const auto &lang_dict = lang_subcmd->GetSubcommandDictionary(); + auto pos = lang_dict.find(lang_matches[0]); + if (pos != lang_dict.end()) + return pos->second; + } + } + } + + if (matches && command_sp) { matches->AppendString(cmd_str); if (descriptions) descriptions->AppendString(command_sp->GetHelp()); @@ -1172,18 +1228,19 @@ Status CommandInterpreter::AddUserCommand(llvm::StringRef name, lldbassert((this == &cmd_sp->GetCommandInterpreter()) && "tried to add a CommandObject from a different interpreter"); if (name.empty()) { - result.SetErrorString("can't use the empty string for a command name"); + result = Status::FromErrorString( + "can't use the empty string for a command name"); return result; } // do not allow replacement of internal commands if (CommandExists(name)) { - result.SetErrorString("can't replace builtin command"); + result = Status::FromErrorString("can't replace builtin command"); return result; } if (UserCommandExists(name)) { if (!can_replace) { - result.SetErrorStringWithFormatv( + result = Status::FromErrorStringWithFormatv( "user command \"{0}\" already exists and force replace was not set " "by --overwrite or 'settings set interpreter.require-overwrite " "false'", @@ -1192,13 +1249,14 @@ Status CommandInterpreter::AddUserCommand(llvm::StringRef name, } if (cmd_sp->IsMultiwordObject()) { if (!m_user_mw_dict[std::string(name)]->IsRemovable()) { - result.SetErrorString( + result = Status::FromErrorString( "can't replace explicitly non-removable multi-word command"); return result; } } else { if (!m_user_dict[std::string(name)]->IsRemovable()) { - result.SetErrorString("can't replace explicitly non-removable command"); + result = Status::FromErrorString( + "can't replace explicitly non-removable command"); return result; } } @@ -1262,15 +1320,15 @@ CommandInterpreter::GetCommandObject(llvm::StringRef cmd_str, // Try to find a match among commands and aliases. Allowing inexact matches, // but perferring exact matches. return GetCommandSP(cmd_str, /*include_aliases=*/true, /*exact=*/false, - matches, descriptions) - .get(); + matches, descriptions) + .get(); } CommandObject *CommandInterpreter::GetUserCommandObject( llvm::StringRef cmd, StringList *matches, StringList *descriptions) const { std::string cmd_str(cmd); auto find_exact = [&](const CommandObject::CommandMap &map) { - auto found_elem = map.find(std::string(cmd)); + auto found_elem = map.find(cmd); if (found_elem == map.end()) return (CommandObject *)nullptr; CommandObject *exact_cmd = found_elem->second.get(); @@ -1296,20 +1354,52 @@ CommandObject *CommandInterpreter::GetUserCommandObject( StringList tmp_list; StringList *matches_ptr = matches ? matches : &tmp_list; AddNamesMatchingPartialString(GetUserCommands(), cmd_str, *matches_ptr); - AddNamesMatchingPartialString(GetUserMultiwordCommands(), - cmd_str, *matches_ptr); + AddNamesMatchingPartialString(GetUserMultiwordCommands(), cmd_str, + *matches_ptr); + + return {}; +} + +CommandObject *CommandInterpreter::GetAliasCommandObject( + llvm::StringRef cmd, StringList *matches, StringList *descriptions) const { + auto find_exact = + [&](const CommandObject::CommandMap &map) -> CommandObject * { + auto found_elem = map.find(cmd); + if (found_elem == map.end()) + return (CommandObject *)nullptr; + CommandObject *exact_cmd = found_elem->second.get(); + if (!exact_cmd) + return nullptr; + + if (matches) + matches->AppendString(exact_cmd->GetCommandName()); + + if (descriptions) + descriptions->AppendString(exact_cmd->GetHelp()); + + return exact_cmd; + return nullptr; + }; + + CommandObject *exact_cmd = find_exact(GetAliases()); + if (exact_cmd) + return exact_cmd; + + // We didn't have an exact command, so now look for partial matches. + StringList tmp_list; + StringList *matches_ptr = matches ? matches : &tmp_list; + AddNamesMatchingPartialString(GetAliases(), cmd, *matches_ptr); return {}; } bool CommandInterpreter::CommandExists(llvm::StringRef cmd) const { - return m_command_dict.find(std::string(cmd)) != m_command_dict.end(); + return m_command_dict.find(cmd) != m_command_dict.end(); } bool CommandInterpreter::GetAliasFullName(llvm::StringRef cmd, std::string &full_name) const { - bool exact_match = - (m_alias_dict.find(std::string(cmd)) != m_alias_dict.end()); + bool exact_match = (m_alias_dict.find(cmd) != m_alias_dict.end()); if (exact_match) { full_name.assign(std::string(cmd)); return exact_match; @@ -1337,15 +1427,15 @@ bool CommandInterpreter::GetAliasFullName(llvm::StringRef cmd, } bool CommandInterpreter::AliasExists(llvm::StringRef cmd) const { - return m_alias_dict.find(std::string(cmd)) != m_alias_dict.end(); + return m_alias_dict.find(cmd) != m_alias_dict.end(); } bool CommandInterpreter::UserCommandExists(llvm::StringRef cmd) const { - return m_user_dict.find(std::string(cmd)) != m_user_dict.end(); + return m_user_dict.find(cmd) != m_user_dict.end(); } bool CommandInterpreter::UserMultiwordCommandExists(llvm::StringRef cmd) const { - return m_user_mw_dict.find(std::string(cmd)) != m_user_mw_dict.end(); + return m_user_mw_dict.find(cmd) != m_user_mw_dict.end(); } CommandAlias * @@ -1369,7 +1459,7 @@ CommandInterpreter::AddAlias(llvm::StringRef alias_name, } bool CommandInterpreter::RemoveAlias(llvm::StringRef alias_name) { - auto pos = m_alias_dict.find(std::string(alias_name)); + auto pos = m_alias_dict.find(alias_name); if (pos != m_alias_dict.end()) { m_alias_dict.erase(pos); return true; @@ -1378,7 +1468,7 @@ bool CommandInterpreter::RemoveAlias(llvm::StringRef alias_name) { } bool CommandInterpreter::RemoveCommand(llvm::StringRef cmd, bool force) { - auto pos = m_command_dict.find(std::string(cmd)); + auto pos = m_command_dict.find(cmd); if (pos != m_command_dict.end()) { if (force || pos->second->IsRemovable()) { // Only regular expression objects or python commands are removable under @@ -1391,8 +1481,7 @@ bool CommandInterpreter::RemoveCommand(llvm::StringRef cmd, bool force) { } bool CommandInterpreter::RemoveUser(llvm::StringRef user_name) { - CommandObject::CommandMap::iterator pos = - m_user_dict.find(std::string(user_name)); + CommandObject::CommandMap::iterator pos = m_user_dict.find(user_name); if (pos != m_user_dict.end()) { m_user_dict.erase(pos); return true; @@ -1401,8 +1490,7 @@ bool CommandInterpreter::RemoveUser(llvm::StringRef user_name) { } bool CommandInterpreter::RemoveUserMultiword(llvm::StringRef multi_name) { - CommandObject::CommandMap::iterator pos = - m_user_mw_dict.find(std::string(multi_name)); + CommandObject::CommandMap::iterator pos = m_user_mw_dict.find(multi_name); if (pos != m_user_mw_dict.end()) { m_user_mw_dict.erase(pos); return true; @@ -1765,8 +1853,7 @@ Status CommandInterpreter::PreprocessCommand(std::string &command) { return error; } -Status -CommandInterpreter::PreprocessToken(std::string &expr_str) { +Status CommandInterpreter::PreprocessToken(std::string &expr_str) { Status error; ExecutionContext exe_ctx(GetExecutionContext()); @@ -1786,9 +1873,8 @@ CommandInterpreter::PreprocessToken(std::string &expr_str) { options.SetTryAllThreads(true); options.SetTimeout(std::nullopt); - ExpressionResults expr_result = - target.EvaluateExpression(expr_str.c_str(), exe_ctx.GetFramePtr(), - expr_result_valobj_sp, options); + ExpressionResults expr_result = target.EvaluateExpression( + expr_str.c_str(), exe_ctx.GetFramePtr(), expr_result_valobj_sp, options); if (expr_result == eExpressionCompleted) { Scalar scalar; @@ -1805,16 +1891,18 @@ CommandInterpreter::PreprocessToken(std::string &expr_str) { if (value_string_size) { expr_str = value_strm.GetData(); } else { - error.SetErrorStringWithFormat("expression value didn't result " - "in a scalar value for the " - "expression '%s'", - expr_str.c_str()); + error = + Status::FromErrorStringWithFormat("expression value didn't result " + "in a scalar value for the " + "expression '%s'", + expr_str.c_str()); } } else { - error.SetErrorStringWithFormat("expression value didn't result " - "in a scalar value for the " - "expression '%s'", - expr_str.c_str()); + error = + Status::FromErrorStringWithFormat("expression value didn't result " + "in a scalar value for the " + "expression '%s'", + expr_str.c_str()); } return error; } @@ -1824,11 +1912,12 @@ CommandInterpreter::PreprocessToken(std::string &expr_str) { // But if for some reason we didn't get a value object at all, then we will // make up some helpful errors from the expression result. if (expr_result_valobj_sp) - error = expr_result_valobj_sp->GetError(); + error = expr_result_valobj_sp->GetError().Clone(); if (error.Success()) { - std::string result = lldb_private::toString(expr_result); - error.SetErrorString(result + "for the expression '" + expr_str + "'"); + std::string result = lldb_private::toString(expr_result) + + "for the expression '" + expr_str + "'"; + error = Status(result); } return error; } @@ -1848,16 +1937,64 @@ bool CommandInterpreter::HandleCommand(const char *command_line, LazyBool lazy_add_to_history, CommandReturnObject &result, bool force_repeat_command) { + // These are assigned later in the function but they must be declared before + // the ScopedDispatcher object because we need their destructions to occur + // after the dispatcher's dtor call, which may reference them. + // TODO: This function could be refactored? + std::string parsed_command_args; + CommandObject *cmd_obj = nullptr; + + telemetry::ScopedDispatcher<telemetry::CommandInfo> helper(&m_debugger); + const bool detailed_command_telemetry = + telemetry::TelemetryManager::GetInstance() + ->GetConfig() + ->detailed_command_telemetry; + const int command_id = telemetry::CommandInfo::GetNextID(); + std::string command_string(command_line); - std::string original_command_string(command_line); + std::string original_command_string(command_string); + std::string real_original_command_string(command_string); - Log *log = GetLog(LLDBLog::Commands); - llvm::PrettyStackTraceFormat stack_trace("HandleCommand(command = \"%s\")", - command_line); + helper.DispatchNow([&](lldb_private::telemetry::CommandInfo *info) { + info->command_id = command_id; + if (Target *target = GetExecutionContext().GetTargetPtr()) { + // If we have a target attached to this command, then get the UUID. + info->target_uuid = target->GetExecutableModule() != nullptr + ? target->GetExecutableModule()->GetUUID() + : UUID(); + } + if (detailed_command_telemetry) + info->original_command = original_command_string; + // The rest (eg., command_name, args, etc) hasn't been parsed yet; + // Those will be collected by the on-exit-callback. + }); + + helper.DispatchOnExit([&cmd_obj, &parsed_command_args, &result, + detailed_command_telemetry, command_id]( + lldb_private::telemetry::CommandInfo *info) { + // TODO: this is logging the time the command-handler finishes. + // But we may want a finer-grain durations too? + // (ie., the execute_time recorded below?) + info->command_id = command_id; + llvm::StringRef command_name = + cmd_obj ? cmd_obj->GetCommandName() : "<not found>"; + info->command_name = command_name.str(); + info->ret_status = result.GetStatus(); + if (std::string error_str = result.GetErrorString(); !error_str.empty()) + info->error_data = std::move(error_str); + + if (detailed_command_telemetry) + info->args = parsed_command_args; + }); + Log *log = GetLog(LLDBLog::Commands); LLDB_LOGF(log, "Processing command: %s", command_line); LLDB_SCOPED_TIMERF("Processing command: %s.", command_line); + // Set the command in the CommandReturnObject here so that it's there even if + // the command is interrupted. + result.SetCommand(command_line); + if (INTERRUPT_REQUESTED(GetDebugger(), "Interrupted initiating command")) { result.AppendError("... Interrupted"); return false; @@ -1954,7 +2091,7 @@ bool CommandInterpreter::HandleCommand(const char *command_line, // From 1 above, we can determine whether the Execute function wants raw // input or not. - CommandObject *cmd_obj = ResolveCommandImpl(command_string, result); + cmd_obj = ResolveCommandImpl(command_string, result); // We have to preprocess the whole command string for Raw commands, since we // don't know the structure of the command. For parsed commands, we only @@ -1974,7 +2111,8 @@ bool CommandInterpreter::HandleCommand(const char *command_line, // has the command expanded to the full name. For example, if the input was // "br s -n main", command_string is now "breakpoint set -n main". if (log) { - llvm::StringRef command_name = cmd_obj ? cmd_obj->GetCommandName() : "<not found>"; + llvm::StringRef command_name = + cmd_obj ? cmd_obj->GetCommandName() : "<not found>"; LLDB_LOGF(log, "HandleCommand, cmd_obj : '%s'", command_name.str().c_str()); LLDB_LOGF(log, "HandleCommand, (revised) command_string: '%s'", command_string.c_str()); @@ -2015,30 +2153,36 @@ bool CommandInterpreter::HandleCommand(const char *command_line, if (add_to_history) m_command_history.AppendString(original_command_string); - std::string remainder; const std::size_t actual_cmd_name_len = cmd_obj->GetCommandName().size(); if (actual_cmd_name_len < command_string.length()) - remainder = command_string.substr(actual_cmd_name_len); + parsed_command_args = command_string.substr(actual_cmd_name_len); // Remove any initial spaces - size_t pos = remainder.find_first_not_of(k_white_space); + size_t pos = parsed_command_args.find_first_not_of(k_white_space); if (pos != 0 && pos != std::string::npos) - remainder.erase(0, pos); + parsed_command_args.erase(0, pos); LLDB_LOGF( log, "HandleCommand, command line after removing command name(s): '%s'", - remainder.c_str()); + parsed_command_args.c_str()); // To test whether or not transcript should be saved, `transcript_item` is - // used instead of `GetSaveTrasncript()`. This is because the latter will + // used instead of `GetSaveTranscript()`. This is because the latter will // fail when the command is "settings set interpreter.save-transcript true". if (transcript_item) { transcript_item->AddStringItem("commandName", cmd_obj->GetCommandName()); - transcript_item->AddStringItem("commandArguments", remainder); + transcript_item->AddStringItem("commandArguments", parsed_command_args); } ElapsedTime elapsed(execute_time); - cmd_obj->Execute(remainder.c_str(), result); + cmd_obj->SetOriginalCommandString(real_original_command_string); + // Set the indent to the position of the command in the command line. + pos = real_original_command_string.rfind(parsed_command_args); + std::optional<uint16_t> indent; + if (pos != std::string::npos) + indent = pos; + result.SetDiagnosticIndent(indent); + cmd_obj->Execute(parsed_command_args.c_str(), result); } LLDB_LOGF(log, "HandleCommand, command %s", @@ -2048,11 +2192,11 @@ bool CommandInterpreter::HandleCommand(const char *command_line, // used instead of `GetSaveTrasncript()`. This is because the latter will // fail when the command is "settings set interpreter.save-transcript true". if (transcript_item) { - m_transcript_stream << result.GetOutputData(); - m_transcript_stream << result.GetErrorData(); + m_transcript_stream << result.GetOutputString(); + m_transcript_stream << result.GetErrorString(); - transcript_item->AddStringItem("output", result.GetOutputData()); - transcript_item->AddStringItem("error", result.GetErrorData()); + transcript_item->AddStringItem("output", result.GetOutputString()); + transcript_item->AddStringItem("error", result.GetErrorString()); transcript_item->AddFloatItem("durationInSeconds", execute_time.get().count()); } @@ -2143,12 +2287,17 @@ CommandInterpreter::GetAutoSuggestionForCommand(llvm::StringRef line) { void CommandInterpreter::UpdatePrompt(llvm::StringRef new_prompt) { EventSP prompt_change_event_sp( new Event(eBroadcastBitResetPrompt, new EventDataBytes(new_prompt))); - ; + BroadcastEvent(prompt_change_event_sp); if (m_command_io_handler_sp) m_command_io_handler_sp->SetPrompt(new_prompt); } +void CommandInterpreter::UpdateUseColor(bool use_color) { + if (m_command_io_handler_sp) + m_command_io_handler_sp->SetUseColor(use_color); +} + bool CommandInterpreter::Confirm(llvm::StringRef message, bool default_answer) { // Check AutoConfirm first: if (m_debugger.GetAutoConfirm()) @@ -2165,18 +2314,22 @@ const CommandAlias * CommandInterpreter::GetAlias(llvm::StringRef alias_name) const { OptionArgVectorSP ret_val; - auto pos = m_alias_dict.find(std::string(alias_name)); + auto pos = m_alias_dict.find(alias_name); if (pos != m_alias_dict.end()) return (CommandAlias *)pos->second.get(); return nullptr; } -bool CommandInterpreter::HasCommands() const { return (!m_command_dict.empty()); } +bool CommandInterpreter::HasCommands() const { + return (!m_command_dict.empty()); +} bool CommandInterpreter::HasAliases() const { return (!m_alias_dict.empty()); } -bool CommandInterpreter::HasUserCommands() const { return (!m_user_dict.empty()); } +bool CommandInterpreter::HasUserCommands() const { + return (!m_user_dict.empty()); +} bool CommandInterpreter::HasUserMultiwordCommands() const { return (!m_user_mw_dict.empty()); @@ -2513,7 +2666,8 @@ bool CommandInterpreter::DidProcessStopAbnormally() const { const StopReason reason = stop_info->GetStopReason(); if (reason == eStopReasonException || reason == eStopReasonInstrumentation || - reason == eStopReasonProcessorTrace) + reason == eStopReasonProcessorTrace || reason == eStopReasonInterrupt || + reason == eStopReasonHistoryBoundary) return true; if (reason == eStopReasonSignal) { @@ -2534,20 +2688,18 @@ bool CommandInterpreter::DidProcessStopAbnormally() const { return false; } -void -CommandInterpreter::HandleCommands(const StringList &commands, - const ExecutionContext &override_context, - const CommandInterpreterRunOptions &options, - CommandReturnObject &result) { +void CommandInterpreter::HandleCommands( + const StringList &commands, const ExecutionContext &override_context, + const CommandInterpreterRunOptions &options, CommandReturnObject &result) { OverrideExecutionContext(override_context); HandleCommands(commands, options, result); RestoreExecutionContext(); } -void CommandInterpreter::HandleCommands(const StringList &commands, - const CommandInterpreterRunOptions &options, - CommandReturnObject &result) { +void CommandInterpreter::HandleCommands( + const StringList &commands, const CommandInterpreterRunOptions &options, + CommandReturnObject &result) { size_t num_lines = commands.GetSize(); // If we are going to continue past a "continue" then we need to run the @@ -2586,24 +2738,23 @@ void CommandInterpreter::HandleCommands(const StringList &commands, if (options.GetPrintResults()) { if (tmp_result.Succeeded()) - result.AppendMessage(tmp_result.GetOutputData()); + result.AppendMessage(tmp_result.GetOutputString()); } if (!success || !tmp_result.Succeeded()) { - llvm::StringRef error_msg = tmp_result.GetErrorData(); + std::string error_msg = tmp_result.GetErrorString(); if (error_msg.empty()) error_msg = "<unknown error>.\n"; if (options.GetStopOnError()) { - result.AppendErrorWithFormat( - "Aborting reading of commands after command #%" PRIu64 - ": '%s' failed with %s", - (uint64_t)idx, cmd, error_msg.str().c_str()); + result.AppendErrorWithFormatv("Aborting reading of commands after " + "command #{0}: '{1}' failed with {2}", + (uint64_t)idx, cmd, error_msg); m_debugger.SetAsyncExecution(old_async_execution); return; - } else if (options.GetPrintResults()) { - result.AppendMessageWithFormat( - "Command #%" PRIu64 " '%s' failed with %s", (uint64_t)idx + 1, cmd, - error_msg.str().c_str()); + } + if (options.GetPrintResults()) { + result.AppendMessageWithFormatv("Command #{0} '{1}' failed with {2}", + (uint64_t)idx + 1, cmd, error_msg); } } @@ -2686,8 +2837,9 @@ void CommandInterpreter::HandleCommandsFromFile( RestoreExecutionContext(); } -void CommandInterpreter::HandleCommandsFromFile(FileSpec &cmd_file, - const CommandInterpreterRunOptions &options, CommandReturnObject &result) { +void CommandInterpreter::HandleCommandsFromFile( + FileSpec &cmd_file, const CommandInterpreterRunOptions &options, + CommandReturnObject &result) { if (!FileSystem::Instance().Exists(cmd_file)) { result.AppendErrorWithFormat( "Error reading commands from file %s - file not found.\n", @@ -2791,13 +2943,13 @@ void CommandInterpreter::HandleCommandsFromFile(FileSpec &cmd_file, } if (flags & eHandleCommandFlagPrintResult) { - debugger.GetOutputFile().Printf("Executing commands in '%s'.\n", - cmd_file_path.c_str()); + debugger.GetOutputFileSP()->Printf("Executing commands in '%s'.\n", + cmd_file_path.c_str()); } // Used for inheriting the right settings when "command source" might // have nested "command source" commands - lldb::StreamFileSP empty_stream_sp; + lldb::LockableStreamFileSP empty_stream_sp; m_command_source_flags.push_back(flags); IOHandlerSP io_handler_sp(new IOHandlerEditline( debugger, IOHandler::Type::CommandInterpreter, input_file_sp, @@ -3054,25 +3206,26 @@ void CommandInterpreter::PrintCommandOutput(IOHandler &io_handler, llvm::StringRef str, bool is_stdout) { - lldb::StreamFileSP stream = is_stdout ? io_handler.GetOutputStreamFileSP() - : io_handler.GetErrorStreamFileSP(); + lldb::LockableStreamFileSP stream = is_stdout + ? io_handler.GetOutputStreamFileSP() + : io_handler.GetErrorStreamFileSP(); // Split the output into lines and poll for interrupt requests bool had_output = !str.empty(); while (!str.empty()) { llvm::StringRef line; std::tie(line, str) = str.split('\n'); { - std::lock_guard<std::recursive_mutex> guard(io_handler.GetOutputMutex()); - stream->Write(line.data(), line.size()); - stream->Write("\n", 1); + LockedStreamFile stream_file = stream->Lock(); + stream_file.Write(line.data(), line.size()); + stream_file.Write("\n", 1); } } - std::lock_guard<std::recursive_mutex> guard(io_handler.GetOutputMutex()); + LockedStreamFile stream_file = stream->Lock(); if (had_output && INTERRUPT_REQUESTED(GetDebugger(), "Interrupted dumping command output")) - stream->Printf("\n... Interrupted.\n"); - stream->Flush(); + stream_file.Printf("\n... Interrupted.\n"); + stream_file.Flush(); } bool CommandInterpreter::EchoCommandNonInteractive( @@ -3092,9 +3245,9 @@ bool CommandInterpreter::EchoCommandNonInteractive( void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler, std::string &line) { - // If we were interrupted, bail out... - if (WasInterrupted()) - return; + // If we were interrupted, bail out... + if (WasInterrupted()) + return; const bool is_interactive = io_handler.GetIsInteractive(); const bool allow_repeats = @@ -3114,9 +3267,9 @@ void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler, // from a file) we need to echo the command out so we don't just see the // command output and no command... if (EchoCommandNonInteractive(line, io_handler.GetFlags())) { - std::lock_guard<std::recursive_mutex> guard(io_handler.GetOutputMutex()); - io_handler.GetOutputStreamFileSP()->Printf( - "%s%s\n", io_handler.GetPrompt(), line.c_str()); + LockedStreamFile locked_stream = + io_handler.GetOutputStreamFileSP()->Lock(); + locked_stream.Printf("%s%s\n", io_handler.GetPrompt(), line.c_str()); } } @@ -3140,18 +3293,40 @@ void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler, if ((result.Succeeded() && io_handler.GetFlags().Test(eHandleCommandFlagPrintResult)) || io_handler.GetFlags().Test(eHandleCommandFlagPrintErrors)) { - // Display any STDOUT/STDERR _prior_ to emitting the command result text - GetProcessOutput(); + auto DefaultPrintCallback = [&](const CommandReturnObject &result) { + // Display any inline diagnostics first. + const bool inline_diagnostics = !result.GetImmediateErrorStream() && + GetDebugger().GetShowInlineDiagnostics(); + if (inline_diagnostics) { + unsigned prompt_len = m_debugger.GetPrompt().size(); + if (auto indent = result.GetDiagnosticIndent()) { + std::string diags = + result.GetInlineDiagnosticString(prompt_len + *indent); + PrintCommandOutput(io_handler, diags, true); + } + } - if (!result.GetImmediateOutputStream()) { - llvm::StringRef output = result.GetOutputData(); - PrintCommandOutput(io_handler, output, true); - } + // Display any STDOUT/STDERR _prior_ to emitting the command result text. + GetProcessOutput(); + + if (!result.GetImmediateOutputStream()) { + llvm::StringRef output = result.GetOutputString(); + PrintCommandOutput(io_handler, output, true); + } - // Now emit the command error text from the command we just executed - if (!result.GetImmediateErrorStream()) { - llvm::StringRef error = result.GetErrorData(); - PrintCommandOutput(io_handler, error, false); + // Now emit the command error text from the command we just executed. + if (!result.GetImmediateErrorStream()) { + std::string error = result.GetErrorString(!inline_diagnostics); + PrintCommandOutput(io_handler, error, false); + } + }; + + if (m_print_callback) { + const auto callback_result = m_print_callback(result); + if (callback_result == eCommandReturnObjectPrintCallbackSkipped) + DefaultPrintCallback(result); + } else { + DefaultPrintCallback(result); } } @@ -3222,9 +3397,9 @@ bool CommandInterpreter::SaveTranscript( CommandReturnObject &result, std::optional<std::string> output_file) { if (output_file == std::nullopt || output_file->empty()) { std::string now = llvm::to_string(std::chrono::system_clock::now()); - std::replace(now.begin(), now.end(), ' ', '_'); + llvm::replace(now, ' ', '_'); // Can't have file name with colons on Windows - std::replace(now.begin(), now.end(), ':', '-'); + llvm::replace(now, ':', '-'); const std::string file_name = "lldb_session_" + now + ".log"; FileSpec save_location = GetSaveSessionDirectory(); @@ -3268,6 +3443,10 @@ bool CommandInterpreter::SaveTranscript( result.SetStatus(eReturnStatusSuccessFinishNoResult); result.AppendMessageWithFormat("Session's transcripts saved to %s\n", output_file->c_str()); + if (!GetSaveTranscript()) + result.AppendError( + "Note: the setting interpreter.save-transcript is set to false, so the " + "transcript might not have been recorded."); if (GetOpenTranscriptInEditor() && Host::IsInteractiveGraphicSession()) { const FileSpec file_spec; @@ -3421,6 +3600,19 @@ CommandInterpreter::ResolveCommandImpl(std::string &command_line, std::string next_word; StringList matches; bool done = false; + + auto build_alias_cmd = [&](std::string &full_name) { + revised_command_line.Clear(); + matches.Clear(); + std::string alias_result; + cmd_obj = + BuildAliasResult(full_name, scratch_command, alias_result, result); + revised_command_line.Printf("%s", alias_result.c_str()); + if (cmd_obj) { + wants_raw_input = cmd_obj->WantsRawCommandString(); + } + }; + while (!done) { char quote_char = '\0'; std::string suffix; @@ -3432,14 +3624,7 @@ CommandInterpreter::ResolveCommandImpl(std::string &command_line, bool is_real_command = (!is_alias) || (cmd_obj != nullptr && !cmd_obj->IsAlias()); if (!is_real_command) { - matches.Clear(); - std::string alias_result; - cmd_obj = - BuildAliasResult(full_name, scratch_command, alias_result, result); - revised_command_line.Printf("%s", alias_result.c_str()); - if (cmd_obj) { - wants_raw_input = cmd_obj->WantsRawCommandString(); - } + build_alias_cmd(full_name); } else { if (cmd_obj) { llvm::StringRef cmd_name = cmd_obj->GetCommandName(); @@ -3486,21 +3671,32 @@ CommandInterpreter::ResolveCommandImpl(std::string &command_line, if (cmd_obj == nullptr) { const size_t num_matches = matches.GetSize(); if (matches.GetSize() > 1) { - StreamString error_msg; - error_msg.Printf("Ambiguous command '%s'. Possible matches:\n", - next_word.c_str()); + StringList alias_matches; + GetAliasCommandObject(next_word, &alias_matches); + + if (alias_matches.GetSize() == 1) { + std::string full_name; + GetAliasFullName(alias_matches.GetStringAtIndex(0), full_name); + build_alias_cmd(full_name); + done = static_cast<bool>(cmd_obj); + } else { + StreamString error_msg; + error_msg.Printf("Ambiguous command '%s'. Possible matches:\n", + next_word.c_str()); - for (uint32_t i = 0; i < num_matches; ++i) { - error_msg.Printf("\t%s\n", matches.GetStringAtIndex(i)); + for (uint32_t i = 0; i < num_matches; ++i) { + error_msg.Printf("\t%s\n", matches.GetStringAtIndex(i)); + } + result.AppendRawError(error_msg.GetString()); } - result.AppendRawError(error_msg.GetString()); } else { // We didn't have only one match, otherwise we wouldn't get here. lldbassert(num_matches == 0); result.AppendErrorWithFormat("'%s' is not a valid command.\n", next_word.c_str()); } - return nullptr; + if (!done) + return nullptr; } if (cmd_obj->IsMultiwordObject()) { @@ -3581,3 +3777,8 @@ llvm::json::Value CommandInterpreter::GetStatistics() { const StructuredData::Array &CommandInterpreter::GetTranscript() const { return m_transcript; } + +void CommandInterpreter::SetPrintCallback( + CommandReturnObjectCallback callback) { + m_print_callback = callback; +} |
