aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/source/Commands
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lldb/source/Commands')
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandCompletions.cpp853
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectApropos.cpp95
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectApropos.h31
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpoint.cpp2554
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpoint.h48
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpointCommand.cpp724
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpointCommand.h27
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectCommands.cpp2134
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectCommands.h28
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectDisassemble.cpp546
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectDisassemble.h96
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectExpression.cpp663
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectExpression.h101
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectFrame.cpp1173
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectFrame.h27
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectGUI.cpp47
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectGUI.h30
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectHelp.cpp222
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectHelp.h87
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectLanguage.cpp26
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectLanguage.h26
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectLog.cpp674
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectLog.h33
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectMemory.cpp1890
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectMemory.h25
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectMemoryTag.cpp320
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectMemoryTag.h25
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectMultiword.cpp443
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectPlatform.cpp1940
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectPlatform.h32
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectPlugin.cpp79
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectPlugin.h25
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.cpp1999
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.h27
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectQuit.cpp108
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectQuit.h32
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectRegexCommand.cpp110
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectRegexCommand.h66
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectRegister.cpp407
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectRegister.h34
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectReproducer.cpp588
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectReproducer.h27
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectScript.cpp136
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectScript.h42
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectSession.cpp206
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectSession.h23
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectSettings.cpp1164
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectSettings.h27
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectSource.cpp1256
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectSource.h28
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectStats.cpp133
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectStats.h23
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectTarget.cpp5180
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectTarget.h27
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectThread.cpp2527
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectThread.h25
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectThreadUtil.cpp210
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectThreadUtil.h103
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectTrace.cpp433
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectTrace.h51
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectType.cpp3065
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectType.h25
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectVersion.cpp29
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectVersion.h30
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpoint.cpp1209
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpoint.h31
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpointCommand.cpp659
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpointCommand.h27
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandOptionsProcessLaunch.cpp147
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandOptionsProcessLaunch.h49
-rw-r--r--contrib/llvm-project/lldb/source/Commands/Options.td1377
-rw-r--r--contrib/llvm-project/lldb/source/Commands/OptionsBase.td178
72 files changed, 36842 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandCompletions.cpp b/contrib/llvm-project/lldb/source/Commands/CommandCompletions.cpp
new file mode 100644
index 000000000000..ae1ee1fdd30b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandCompletions.cpp
@@ -0,0 +1,853 @@
+//===-- CommandCompletions.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringSet.h"
+
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Core/FileSpecList.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/DataFormatters/DataVisualization.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Interpreter/CommandCompletions.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/TildeExpressionResolver.h"
+
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+
+using namespace lldb_private;
+
+// This is the command completion callback that is used to complete the
+// argument of the option it is bound to (in the OptionDefinition table
+// below).
+typedef void (*CompletionCallback)(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ // A search filter to limit the search...
+ lldb_private::SearchFilter *searcher);
+
+struct CommonCompletionElement {
+ uint32_t type;
+ CompletionCallback callback;
+};
+
+bool CommandCompletions::InvokeCommonCompletionCallbacks(
+ CommandInterpreter &interpreter, uint32_t completion_mask,
+ CompletionRequest &request, SearchFilter *searcher) {
+ bool handled = false;
+
+ const CommonCompletionElement common_completions[] = {
+ {eSourceFileCompletion, CommandCompletions::SourceFiles},
+ {eDiskFileCompletion, CommandCompletions::DiskFiles},
+ {eDiskDirectoryCompletion, CommandCompletions::DiskDirectories},
+ {eSymbolCompletion, CommandCompletions::Symbols},
+ {eModuleCompletion, CommandCompletions::Modules},
+ {eModuleUUIDCompletion, CommandCompletions::ModuleUUIDs},
+ {eSettingsNameCompletion, CommandCompletions::SettingsNames},
+ {ePlatformPluginCompletion, CommandCompletions::PlatformPluginNames},
+ {eArchitectureCompletion, CommandCompletions::ArchitectureNames},
+ {eVariablePathCompletion, CommandCompletions::VariablePath},
+ {eRegisterCompletion, CommandCompletions::Registers},
+ {eBreakpointCompletion, CommandCompletions::Breakpoints},
+ {eProcessPluginCompletion, CommandCompletions::ProcessPluginNames},
+ {eDisassemblyFlavorCompletion, CommandCompletions::DisassemblyFlavors},
+ {eTypeLanguageCompletion, CommandCompletions::TypeLanguages},
+ {eFrameIndexCompletion, CommandCompletions::FrameIndexes},
+ {eStopHookIDCompletion, CommandCompletions::StopHookIDs},
+ {eThreadIndexCompletion, CommandCompletions::ThreadIndexes},
+ {eWatchPointIDCompletion, CommandCompletions::WatchPointIDs},
+ {eBreakpointNameCompletion, CommandCompletions::BreakpointNames},
+ {eProcessIDCompletion, CommandCompletions::ProcessIDs},
+ {eProcessNameCompletion, CommandCompletions::ProcessNames},
+ {eRemoteDiskFileCompletion, CommandCompletions::RemoteDiskFiles},
+ {eRemoteDiskDirectoryCompletion,
+ CommandCompletions::RemoteDiskDirectories},
+ {eTypeCategoryNameCompletion, CommandCompletions::TypeCategoryNames},
+ {eNoCompletion, nullptr} // This one has to be last in the list.
+ };
+
+ for (int i = 0;; i++) {
+ if (common_completions[i].type == eNoCompletion)
+ break;
+ else if ((common_completions[i].type & completion_mask) ==
+ common_completions[i].type &&
+ common_completions[i].callback != nullptr) {
+ handled = true;
+ common_completions[i].callback(interpreter, request, searcher);
+ }
+ }
+ return handled;
+}
+
+namespace {
+// The Completer class is a convenient base class for building searchers that
+// go along with the SearchFilter passed to the standard Completer functions.
+class Completer : public Searcher {
+public:
+ Completer(CommandInterpreter &interpreter, CompletionRequest &request)
+ : m_interpreter(interpreter), m_request(request) {}
+
+ ~Completer() override = default;
+
+ CallbackReturn SearchCallback(SearchFilter &filter, SymbolContext &context,
+ Address *addr) override = 0;
+
+ lldb::SearchDepth GetDepth() override = 0;
+
+ virtual void DoCompletion(SearchFilter *filter) = 0;
+
+protected:
+ CommandInterpreter &m_interpreter;
+ CompletionRequest &m_request;
+
+private:
+ Completer(const Completer &) = delete;
+ const Completer &operator=(const Completer &) = delete;
+};
+} // namespace
+
+// SourceFileCompleter implements the source file completer
+namespace {
+class SourceFileCompleter : public Completer {
+public:
+ SourceFileCompleter(CommandInterpreter &interpreter,
+ CompletionRequest &request)
+ : Completer(interpreter, request) {
+ FileSpec partial_spec(m_request.GetCursorArgumentPrefix());
+ m_file_name = partial_spec.GetFilename().GetCString();
+ m_dir_name = partial_spec.GetDirectory().GetCString();
+ }
+
+ lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthCompUnit; }
+
+ Searcher::CallbackReturn SearchCallback(SearchFilter &filter,
+ SymbolContext &context,
+ Address *addr) override {
+ if (context.comp_unit != nullptr) {
+ const char *cur_file_name =
+ context.comp_unit->GetPrimaryFile().GetFilename().GetCString();
+ const char *cur_dir_name =
+ context.comp_unit->GetPrimaryFile().GetDirectory().GetCString();
+
+ bool match = false;
+ if (m_file_name && cur_file_name &&
+ strstr(cur_file_name, m_file_name) == cur_file_name)
+ match = true;
+
+ if (match && m_dir_name && cur_dir_name &&
+ strstr(cur_dir_name, m_dir_name) != cur_dir_name)
+ match = false;
+
+ if (match) {
+ m_matching_files.AppendIfUnique(context.comp_unit->GetPrimaryFile());
+ }
+ }
+ return Searcher::eCallbackReturnContinue;
+ }
+
+ void DoCompletion(SearchFilter *filter) override {
+ filter->Search(*this);
+ // Now convert the filelist to completions:
+ for (size_t i = 0; i < m_matching_files.GetSize(); i++) {
+ m_request.AddCompletion(
+ m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString());
+ }
+ }
+
+private:
+ FileSpecList m_matching_files;
+ const char *m_file_name;
+ const char *m_dir_name;
+
+ SourceFileCompleter(const SourceFileCompleter &) = delete;
+ const SourceFileCompleter &operator=(const SourceFileCompleter &) = delete;
+};
+} // namespace
+
+static bool regex_chars(const char comp) {
+ return llvm::StringRef("[](){}+.*|^$\\?").contains(comp);
+}
+
+namespace {
+class SymbolCompleter : public Completer {
+
+public:
+ SymbolCompleter(CommandInterpreter &interpreter, CompletionRequest &request)
+ : Completer(interpreter, request) {
+ std::string regex_str;
+ if (!m_request.GetCursorArgumentPrefix().empty()) {
+ regex_str.append("^");
+ regex_str.append(std::string(m_request.GetCursorArgumentPrefix()));
+ } else {
+ // Match anything since the completion string is empty
+ regex_str.append(".");
+ }
+ std::string::iterator pos =
+ find_if(regex_str.begin() + 1, regex_str.end(), regex_chars);
+ while (pos < regex_str.end()) {
+ pos = regex_str.insert(pos, '\\');
+ pos = find_if(pos + 2, regex_str.end(), regex_chars);
+ }
+ m_regex = RegularExpression(regex_str);
+ }
+
+ lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; }
+
+ Searcher::CallbackReturn SearchCallback(SearchFilter &filter,
+ SymbolContext &context,
+ Address *addr) override {
+ if (context.module_sp) {
+ SymbolContextList sc_list;
+ ModuleFunctionSearchOptions function_options;
+ function_options.include_symbols = true;
+ function_options.include_inlines = true;
+ context.module_sp->FindFunctions(m_regex, function_options, sc_list);
+
+ SymbolContext sc;
+ // Now add the functions & symbols to the list - only add if unique:
+ for (uint32_t i = 0; i < sc_list.GetSize(); i++) {
+ if (sc_list.GetContextAtIndex(i, sc)) {
+ ConstString func_name = sc.GetFunctionName(Mangled::ePreferDemangled);
+ // Ensure that the function name matches the regex. This is more than
+ // a sanity check. It is possible that the demangled function name
+ // does not start with the prefix, for example when it's in an
+ // anonymous namespace.
+ if (!func_name.IsEmpty() && m_regex.Execute(func_name.GetStringRef()))
+ m_match_set.insert(func_name);
+ }
+ }
+ }
+ return Searcher::eCallbackReturnContinue;
+ }
+
+ void DoCompletion(SearchFilter *filter) override {
+ filter->Search(*this);
+ collection::iterator pos = m_match_set.begin(), end = m_match_set.end();
+ for (pos = m_match_set.begin(); pos != end; pos++)
+ m_request.AddCompletion((*pos).GetCString());
+ }
+
+private:
+ RegularExpression m_regex;
+ typedef std::set<ConstString> collection;
+ collection m_match_set;
+
+ SymbolCompleter(const SymbolCompleter &) = delete;
+ const SymbolCompleter &operator=(const SymbolCompleter &) = delete;
+};
+} // namespace
+
+namespace {
+class ModuleCompleter : public Completer {
+public:
+ ModuleCompleter(CommandInterpreter &interpreter, CompletionRequest &request)
+ : Completer(interpreter, request) {
+ FileSpec partial_spec(m_request.GetCursorArgumentPrefix());
+ m_file_name = partial_spec.GetFilename().GetCString();
+ m_dir_name = partial_spec.GetDirectory().GetCString();
+ }
+
+ lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; }
+
+ Searcher::CallbackReturn SearchCallback(SearchFilter &filter,
+ SymbolContext &context,
+ Address *addr) override {
+ if (context.module_sp) {
+ const char *cur_file_name =
+ context.module_sp->GetFileSpec().GetFilename().GetCString();
+ const char *cur_dir_name =
+ context.module_sp->GetFileSpec().GetDirectory().GetCString();
+
+ bool match = false;
+ if (m_file_name && cur_file_name &&
+ strstr(cur_file_name, m_file_name) == cur_file_name)
+ match = true;
+
+ if (match && m_dir_name && cur_dir_name &&
+ strstr(cur_dir_name, m_dir_name) != cur_dir_name)
+ match = false;
+
+ if (match) {
+ m_request.AddCompletion(cur_file_name);
+ }
+ }
+ return Searcher::eCallbackReturnContinue;
+ }
+
+ void DoCompletion(SearchFilter *filter) override { filter->Search(*this); }
+
+private:
+ const char *m_file_name;
+ const char *m_dir_name;
+
+ ModuleCompleter(const ModuleCompleter &) = delete;
+ const ModuleCompleter &operator=(const ModuleCompleter &) = delete;
+};
+} // namespace
+
+void CommandCompletions::SourceFiles(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ SourceFileCompleter completer(interpreter, request);
+
+ if (searcher == nullptr) {
+ lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
+ SearchFilterForUnconstrainedSearches null_searcher(target_sp);
+ completer.DoCompletion(&null_searcher);
+ } else {
+ completer.DoCompletion(searcher);
+ }
+}
+
+static void DiskFilesOrDirectories(const llvm::Twine &partial_name,
+ bool only_directories,
+ CompletionRequest &request,
+ TildeExpressionResolver &Resolver) {
+ llvm::SmallString<256> CompletionBuffer;
+ llvm::SmallString<256> Storage;
+ partial_name.toVector(CompletionBuffer);
+
+ if (CompletionBuffer.size() >= PATH_MAX)
+ return;
+
+ namespace path = llvm::sys::path;
+
+ llvm::StringRef SearchDir;
+ llvm::StringRef PartialItem;
+
+ if (CompletionBuffer.startswith("~")) {
+ llvm::StringRef Buffer = CompletionBuffer;
+ size_t FirstSep =
+ Buffer.find_if([](char c) { return path::is_separator(c); });
+
+ llvm::StringRef Username = Buffer.take_front(FirstSep);
+ llvm::StringRef Remainder;
+ if (FirstSep != llvm::StringRef::npos)
+ Remainder = Buffer.drop_front(FirstSep + 1);
+
+ llvm::SmallString<256> Resolved;
+ if (!Resolver.ResolveExact(Username, Resolved)) {
+ // We couldn't resolve it as a full username. If there were no slashes
+ // then this might be a partial username. We try to resolve it as such
+ // but after that, we're done regardless of any matches.
+ if (FirstSep == llvm::StringRef::npos) {
+ llvm::StringSet<> MatchSet;
+ Resolver.ResolvePartial(Username, MatchSet);
+ for (const auto &S : MatchSet) {
+ Resolved = S.getKey();
+ path::append(Resolved, path::get_separator());
+ request.AddCompletion(Resolved, "", CompletionMode::Partial);
+ }
+ }
+ return;
+ }
+
+ // If there was no trailing slash, then we're done as soon as we resolve
+ // the expression to the correct directory. Otherwise we need to continue
+ // looking for matches within that directory.
+ if (FirstSep == llvm::StringRef::npos) {
+ // Make sure it ends with a separator.
+ path::append(CompletionBuffer, path::get_separator());
+ request.AddCompletion(CompletionBuffer, "", CompletionMode::Partial);
+ return;
+ }
+
+ // We want to keep the form the user typed, so we special case this to
+ // search in the fully resolved directory, but CompletionBuffer keeps the
+ // unmodified form that the user typed.
+ Storage = Resolved;
+ llvm::StringRef RemainderDir = path::parent_path(Remainder);
+ if (!RemainderDir.empty()) {
+ // Append the remaining path to the resolved directory.
+ Storage.append(path::get_separator());
+ Storage.append(RemainderDir);
+ }
+ SearchDir = Storage;
+ } else {
+ SearchDir = path::parent_path(CompletionBuffer);
+ }
+
+ size_t FullPrefixLen = CompletionBuffer.size();
+
+ PartialItem = path::filename(CompletionBuffer);
+
+ // path::filename() will return "." when the passed path ends with a
+ // directory separator. We have to filter those out, but only when the
+ // "." doesn't come from the completion request itself.
+ if (PartialItem == "." && path::is_separator(CompletionBuffer.back()))
+ PartialItem = llvm::StringRef();
+
+ if (SearchDir.empty()) {
+ llvm::sys::fs::current_path(Storage);
+ SearchDir = Storage;
+ }
+ assert(!PartialItem.contains(path::get_separator()));
+
+ // SearchDir now contains the directory to search in, and Prefix contains the
+ // text we want to match against items in that directory.
+
+ FileSystem &fs = FileSystem::Instance();
+ std::error_code EC;
+ llvm::vfs::directory_iterator Iter = fs.DirBegin(SearchDir, EC);
+ llvm::vfs::directory_iterator End;
+ for (; Iter != End && !EC; Iter.increment(EC)) {
+ auto &Entry = *Iter;
+ llvm::ErrorOr<llvm::vfs::Status> Status = fs.GetStatus(Entry.path());
+
+ if (!Status)
+ continue;
+
+ auto Name = path::filename(Entry.path());
+
+ // Omit ".", ".."
+ if (Name == "." || Name == ".." || !Name.startswith(PartialItem))
+ continue;
+
+ bool is_dir = Status->isDirectory();
+
+ // If it's a symlink, then we treat it as a directory as long as the target
+ // is a directory.
+ if (Status->isSymlink()) {
+ FileSpec symlink_filespec(Entry.path());
+ FileSpec resolved_filespec;
+ auto error = fs.ResolveSymbolicLink(symlink_filespec, resolved_filespec);
+ if (error.Success())
+ is_dir = fs.IsDirectory(symlink_filespec);
+ }
+
+ if (only_directories && !is_dir)
+ continue;
+
+ // Shrink it back down so that it just has the original prefix the user
+ // typed and remove the part of the name which is common to the located
+ // item and what the user typed.
+ CompletionBuffer.resize(FullPrefixLen);
+ Name = Name.drop_front(PartialItem.size());
+ CompletionBuffer.append(Name);
+
+ if (is_dir) {
+ path::append(CompletionBuffer, path::get_separator());
+ }
+
+ CompletionMode mode =
+ is_dir ? CompletionMode::Partial : CompletionMode::Normal;
+ request.AddCompletion(CompletionBuffer, "", mode);
+ }
+}
+
+static void DiskFilesOrDirectories(const llvm::Twine &partial_name,
+ bool only_directories, StringList &matches,
+ TildeExpressionResolver &Resolver) {
+ CompletionResult result;
+ std::string partial_name_str = partial_name.str();
+ CompletionRequest request(partial_name_str, partial_name_str.size(), result);
+ DiskFilesOrDirectories(partial_name, only_directories, request, Resolver);
+ result.GetMatches(matches);
+}
+
+static void DiskFilesOrDirectories(CompletionRequest &request,
+ bool only_directories) {
+ StandardTildeExpressionResolver resolver;
+ DiskFilesOrDirectories(request.GetCursorArgumentPrefix(), only_directories,
+ request, resolver);
+}
+
+void CommandCompletions::DiskFiles(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ DiskFilesOrDirectories(request, /*only_dirs*/ false);
+}
+
+void CommandCompletions::DiskFiles(const llvm::Twine &partial_file_name,
+ StringList &matches,
+ TildeExpressionResolver &Resolver) {
+ DiskFilesOrDirectories(partial_file_name, false, matches, Resolver);
+}
+
+void CommandCompletions::DiskDirectories(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ DiskFilesOrDirectories(request, /*only_dirs*/ true);
+}
+
+void CommandCompletions::DiskDirectories(const llvm::Twine &partial_file_name,
+ StringList &matches,
+ TildeExpressionResolver &Resolver) {
+ DiskFilesOrDirectories(partial_file_name, true, matches, Resolver);
+}
+
+void CommandCompletions::RemoteDiskFiles(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ lldb::PlatformSP platform_sp =
+ interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform();
+ if (platform_sp)
+ platform_sp->AutoCompleteDiskFileOrDirectory(request, false);
+}
+
+void CommandCompletions::RemoteDiskDirectories(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ lldb::PlatformSP platform_sp =
+ interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform();
+ if (platform_sp)
+ platform_sp->AutoCompleteDiskFileOrDirectory(request, true);
+}
+
+void CommandCompletions::Modules(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ ModuleCompleter completer(interpreter, request);
+
+ if (searcher == nullptr) {
+ lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
+ SearchFilterForUnconstrainedSearches null_searcher(target_sp);
+ completer.DoCompletion(&null_searcher);
+ } else {
+ completer.DoCompletion(searcher);
+ }
+}
+
+void CommandCompletions::ModuleUUIDs(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ const ExecutionContext &exe_ctx = interpreter.GetExecutionContext();
+ if (!exe_ctx.HasTargetScope())
+ return;
+
+ exe_ctx.GetTargetPtr()->GetImages().ForEach(
+ [&request](const lldb::ModuleSP &module) {
+ StreamString strm;
+ module->GetDescription(strm.AsRawOstream(),
+ lldb::eDescriptionLevelInitial);
+ request.TryCompleteCurrentArg(module->GetUUID().GetAsString(),
+ strm.GetString());
+ return true;
+ });
+}
+
+void CommandCompletions::Symbols(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ SymbolCompleter completer(interpreter, request);
+
+ if (searcher == nullptr) {
+ lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
+ SearchFilterForUnconstrainedSearches null_searcher(target_sp);
+ completer.DoCompletion(&null_searcher);
+ } else {
+ completer.DoCompletion(searcher);
+ }
+}
+
+void CommandCompletions::SettingsNames(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ // Cache the full setting name list
+ static StringList g_property_names;
+ if (g_property_names.GetSize() == 0) {
+ // Generate the full setting name list on demand
+ lldb::OptionValuePropertiesSP properties_sp(
+ interpreter.GetDebugger().GetValueProperties());
+ if (properties_sp) {
+ StreamString strm;
+ properties_sp->DumpValue(nullptr, strm, OptionValue::eDumpOptionName);
+ const std::string &str = std::string(strm.GetString());
+ g_property_names.SplitIntoLines(str.c_str(), str.size());
+ }
+ }
+
+ for (const std::string &s : g_property_names)
+ request.TryCompleteCurrentArg(s);
+}
+
+void CommandCompletions::PlatformPluginNames(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ PluginManager::AutoCompletePlatformName(request.GetCursorArgumentPrefix(),
+ request);
+}
+
+void CommandCompletions::ArchitectureNames(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ ArchSpec::AutoComplete(request);
+}
+
+void CommandCompletions::VariablePath(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ Variable::AutoComplete(interpreter.GetExecutionContext(), request);
+}
+
+void CommandCompletions::Registers(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ std::string reg_prefix;
+ if (request.GetCursorArgumentPrefix().startswith("$"))
+ reg_prefix = "$";
+
+ RegisterContext *reg_ctx =
+ interpreter.GetExecutionContext().GetRegisterContext();
+ const size_t reg_num = reg_ctx->GetRegisterCount();
+ for (size_t reg_idx = 0; reg_idx < reg_num; ++reg_idx) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_idx);
+ request.TryCompleteCurrentArg(reg_prefix + reg_info->name,
+ reg_info->alt_name);
+ }
+}
+
+void CommandCompletions::Breakpoints(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ lldb::TargetSP target = interpreter.GetDebugger().GetSelectedTarget();
+ if (!target)
+ return;
+
+ const BreakpointList &breakpoints = target->GetBreakpointList();
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetBreakpointList().GetListMutex(lock);
+
+ size_t num_breakpoints = breakpoints.GetSize();
+ if (num_breakpoints == 0)
+ return;
+
+ for (size_t i = 0; i < num_breakpoints; ++i) {
+ lldb::BreakpointSP bp = breakpoints.GetBreakpointAtIndex(i);
+
+ StreamString s;
+ bp->GetDescription(&s, lldb::eDescriptionLevelBrief);
+ llvm::StringRef bp_info = s.GetString();
+
+ const size_t colon_pos = bp_info.find_first_of(':');
+ if (colon_pos != llvm::StringRef::npos)
+ bp_info = bp_info.drop_front(colon_pos + 2);
+
+ request.TryCompleteCurrentArg(std::to_string(bp->GetID()), bp_info);
+ }
+}
+
+void CommandCompletions::BreakpointNames(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ lldb::TargetSP target = interpreter.GetDebugger().GetSelectedTarget();
+ if (!target)
+ return;
+
+ std::vector<std::string> name_list;
+ target->GetBreakpointNames(name_list);
+
+ for (const std::string &name : name_list)
+ request.TryCompleteCurrentArg(name);
+}
+
+void CommandCompletions::ProcessPluginNames(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ PluginManager::AutoCompleteProcessName(request.GetCursorArgumentPrefix(),
+ request);
+}
+void CommandCompletions::DisassemblyFlavors(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ // Currently the only valid options for disassemble -F are default, and for
+ // Intel architectures, att and intel.
+ static const char *flavors[] = {"default", "att", "intel"};
+ for (const char *flavor : flavors) {
+ request.TryCompleteCurrentArg(flavor);
+ }
+}
+
+void CommandCompletions::ProcessIDs(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ lldb::PlatformSP platform_sp(interpreter.GetPlatform(true));
+ if (!platform_sp)
+ return;
+ ProcessInstanceInfoList process_infos;
+ ProcessInstanceInfoMatch match_info;
+ platform_sp->FindProcesses(match_info, process_infos);
+ for (const ProcessInstanceInfo &info : process_infos)
+ request.TryCompleteCurrentArg(std::to_string(info.GetProcessID()),
+ info.GetNameAsStringRef());
+}
+
+void CommandCompletions::ProcessNames(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ lldb::PlatformSP platform_sp(interpreter.GetPlatform(true));
+ if (!platform_sp)
+ return;
+ ProcessInstanceInfoList process_infos;
+ ProcessInstanceInfoMatch match_info;
+ platform_sp->FindProcesses(match_info, process_infos);
+ for (const ProcessInstanceInfo &info : process_infos)
+ request.TryCompleteCurrentArg(info.GetNameAsStringRef());
+}
+
+void CommandCompletions::TypeLanguages(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ for (int bit :
+ Language::GetLanguagesSupportingTypeSystems().bitvector.set_bits()) {
+ request.TryCompleteCurrentArg(
+ Language::GetNameForLanguageType(static_cast<lldb::LanguageType>(bit)));
+ }
+}
+
+void CommandCompletions::FrameIndexes(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ const ExecutionContext &exe_ctx = interpreter.GetExecutionContext();
+ if (!exe_ctx.HasProcessScope())
+ return;
+
+ lldb::ThreadSP thread_sp = exe_ctx.GetThreadSP();
+ const uint32_t frame_num = thread_sp->GetStackFrameCount();
+ for (uint32_t i = 0; i < frame_num; ++i) {
+ lldb::StackFrameSP frame_sp = thread_sp->GetStackFrameAtIndex(i);
+ StreamString strm;
+ frame_sp->Dump(&strm, false, true);
+ request.TryCompleteCurrentArg(std::to_string(i), strm.GetString());
+ }
+}
+
+void CommandCompletions::StopHookIDs(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ const lldb::TargetSP target_sp =
+ interpreter.GetExecutionContext().GetTargetSP();
+ if (!target_sp)
+ return;
+
+ const size_t num = target_sp->GetNumStopHooks();
+ for (size_t idx = 0; idx < num; ++idx) {
+ StreamString strm;
+ // The value 11 is an offset to make the completion description looks
+ // neater.
+ strm.SetIndentLevel(11);
+ const Target::StopHookSP stophook_sp = target_sp->GetStopHookAtIndex(idx);
+ stophook_sp->GetDescription(&strm, lldb::eDescriptionLevelInitial);
+ request.TryCompleteCurrentArg(std::to_string(stophook_sp->GetID()),
+ strm.GetString());
+ }
+}
+
+void CommandCompletions::ThreadIndexes(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ const ExecutionContext &exe_ctx = interpreter.GetExecutionContext();
+ if (!exe_ctx.HasProcessScope())
+ return;
+
+ ThreadList &threads = exe_ctx.GetProcessPtr()->GetThreadList();
+ lldb::ThreadSP thread_sp;
+ for (uint32_t idx = 0; (thread_sp = threads.GetThreadAtIndex(idx)); ++idx) {
+ StreamString strm;
+ thread_sp->GetStatus(strm, 0, 1, 1, true);
+ request.TryCompleteCurrentArg(std::to_string(thread_sp->GetIndexID()),
+ strm.GetString());
+ }
+}
+
+void CommandCompletions::WatchPointIDs(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ const ExecutionContext &exe_ctx = interpreter.GetExecutionContext();
+ if (!exe_ctx.HasTargetScope())
+ return;
+
+ const WatchpointList &wp_list = exe_ctx.GetTargetPtr()->GetWatchpointList();
+ for (lldb::WatchpointSP wp_sp : wp_list.Watchpoints()) {
+ StreamString strm;
+ wp_sp->Dump(&strm);
+ request.TryCompleteCurrentArg(std::to_string(wp_sp->GetID()),
+ strm.GetString());
+ }
+}
+
+void CommandCompletions::TypeCategoryNames(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ DataVisualization::Categories::ForEach(
+ [&request](const lldb::TypeCategoryImplSP &category_sp) {
+ request.TryCompleteCurrentArg(category_sp->GetName(),
+ category_sp->GetDescription());
+ return true;
+ });
+}
+
+void CommandCompletions::CompleteModifiableCmdPathArgs(
+ CommandInterpreter &interpreter, CompletionRequest &request,
+ OptionElementVector &opt_element_vector) {
+ // The only arguments constitute a command path, however, there might be
+ // options interspersed among the arguments, and we need to skip those. Do that
+ // by copying the args vector, and just dropping all the option bits:
+ Args args = request.GetParsedLine();
+ std::vector<size_t> to_delete;
+ for (auto &elem : opt_element_vector) {
+ to_delete.push_back(elem.opt_pos);
+ if (elem.opt_arg_pos != 0)
+ to_delete.push_back(elem.opt_arg_pos);
+ }
+ sort(to_delete.begin(), to_delete.end(), std::greater<size_t>());
+ for (size_t idx : to_delete)
+ args.DeleteArgumentAtIndex(idx);
+
+ // At this point, we should only have args, so now lookup the command up to
+ // the cursor element.
+
+ // There's nothing here but options. It doesn't seem very useful here to
+ // dump all the commands, so just return.
+ size_t num_args = args.GetArgumentCount();
+ if (num_args == 0)
+ return;
+
+ // There's just one argument, so we should complete its name:
+ StringList matches;
+ if (num_args == 1) {
+ interpreter.GetUserCommandObject(args.GetArgumentAtIndex(0), &matches,
+ nullptr);
+ request.AddCompletions(matches);
+ return;
+ }
+
+ // There was more than one path element, lets find the containing command:
+ Status error;
+ CommandObjectMultiword *mwc =
+ interpreter.VerifyUserMultiwordCmdPath(args, true, error);
+
+ // Something was wrong somewhere along the path, but I don't think there's
+ // a good way to go back and fill in the missing elements:
+ if (error.Fail())
+ return;
+
+ // This should never happen. We already handled the case of one argument
+ // above, and we can only get Success & nullptr back if there's a one-word
+ // leaf.
+ assert(mwc != nullptr);
+
+ mwc->GetSubcommandObject(args.GetArgumentAtIndex(num_args - 1), &matches);
+ if (matches.GetSize() == 0)
+ return;
+
+ request.AddCompletions(matches);
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectApropos.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectApropos.cpp
new file mode 100644
index 000000000000..c6680f8b140d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectApropos.cpp
@@ -0,0 +1,95 @@
+//===-- CommandObjectApropos.cpp ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectApropos.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/Property.h"
+#include "lldb/Utility/Args.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// CommandObjectApropos
+
+CommandObjectApropos::CommandObjectApropos(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "apropos",
+ "List debugger commands related to a word or subject.", nullptr) {
+ CommandArgumentEntry arg;
+ CommandArgumentData search_word_arg;
+
+ // Define the first (and only) variant of this arg.
+ search_word_arg.arg_type = eArgTypeSearchWord;
+ search_word_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the argument
+ // entry.
+ arg.push_back(search_word_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+}
+
+CommandObjectApropos::~CommandObjectApropos() = default;
+
+bool CommandObjectApropos::DoExecute(Args &args, CommandReturnObject &result) {
+ const size_t argc = args.GetArgumentCount();
+
+ if (argc == 1) {
+ auto search_word = args[0].ref();
+ if (!search_word.empty()) {
+ // The bulk of the work must be done inside the Command Interpreter,
+ // since the command dictionary is private.
+ StringList commands_found;
+ StringList commands_help;
+
+ m_interpreter.FindCommandsForApropos(
+ search_word, commands_found, commands_help, true, true, true, true);
+
+ if (commands_found.GetSize() == 0) {
+ result.AppendMessageWithFormat("No commands found pertaining to '%s'. "
+ "Try 'help' to see a complete list of "
+ "debugger commands.\n",
+ args[0].c_str());
+ } else {
+ if (commands_found.GetSize() > 0) {
+ result.AppendMessageWithFormat(
+ "The following commands may relate to '%s':\n", args[0].c_str());
+ const size_t max_len = commands_found.GetMaxStringLength();
+
+ for (size_t i = 0; i < commands_found.GetSize(); ++i)
+ m_interpreter.OutputFormattedHelpText(
+ result.GetOutputStream(), commands_found.GetStringAtIndex(i),
+ "--", commands_help.GetStringAtIndex(i), max_len);
+ }
+ }
+
+ std::vector<const Property *> properties;
+ const size_t num_properties =
+ GetDebugger().Apropos(search_word, properties);
+ if (num_properties) {
+ const bool dump_qualified_name = true;
+ result.AppendMessageWithFormatv(
+ "\nThe following settings variables may relate to '{0}': \n\n",
+ args[0].ref());
+ for (size_t i = 0; i < num_properties; ++i)
+ properties[i]->DumpDescription(
+ m_interpreter, result.GetOutputStream(), 0, dump_qualified_name);
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendError("'' is not a valid search word.\n");
+ }
+ } else {
+ result.AppendError("'apropos' must be called with exactly one argument.\n");
+ }
+
+ return result.Succeeded();
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectApropos.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectApropos.h
new file mode 100644
index 000000000000..042753f24032
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectApropos.h
@@ -0,0 +1,31 @@
+//===-- CommandObjectApropos.h -----------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTAPROPOS_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTAPROPOS_H
+
+#include "lldb/Interpreter/CommandObject.h"
+
+namespace lldb_private {
+
+// CommandObjectApropos
+
+class CommandObjectApropos : public CommandObjectParsed {
+public:
+ CommandObjectApropos(CommandInterpreter &interpreter);
+
+ ~CommandObjectApropos() override;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTAPROPOS_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpoint.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpoint.cpp
new file mode 100644
index 000000000000..755cf6c61f26
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpoint.cpp
@@ -0,0 +1,2554 @@
+//===-- CommandObjectBreakpoint.cpp ---------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectBreakpoint.h"
+#include "CommandObjectBreakpointCommand.h"
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointIDList.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionGroupPythonClassWithDict.h"
+#include "lldb/Interpreter/OptionValueBoolean.h"
+#include "lldb/Interpreter/OptionValueFileColonLine.h"
+#include "lldb/Interpreter/OptionValueString.h"
+#include "lldb/Interpreter/OptionValueUInt64.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/StreamString.h"
+
+#include <memory>
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+
+static void AddBreakpointDescription(Stream *s, Breakpoint *bp,
+ lldb::DescriptionLevel level) {
+ s->IndentMore();
+ bp->GetDescription(s, level, true);
+ s->IndentLess();
+ s->EOL();
+}
+
+// Modifiable Breakpoint Options
+#pragma mark Modify::CommandOptions
+#define LLDB_OPTIONS_breakpoint_modify
+#include "CommandOptions.inc"
+
+class lldb_private::BreakpointOptionGroup : public OptionGroup {
+public:
+ BreakpointOptionGroup() : m_bp_opts(false) {}
+
+ ~BreakpointOptionGroup() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_breakpoint_modify_options);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option =
+ g_breakpoint_modify_options[option_idx].short_option;
+
+ switch (short_option) {
+ case 'c':
+ // Normally an empty breakpoint condition marks is as unset. But we need
+ // to say it was passed in.
+ m_bp_opts.SetCondition(option_arg.str().c_str());
+ m_bp_opts.m_set_flags.Set(BreakpointOptions::eCondition);
+ break;
+ case 'C':
+ m_commands.push_back(std::string(option_arg));
+ break;
+ case 'd':
+ m_bp_opts.SetEnabled(false);
+ break;
+ case 'e':
+ m_bp_opts.SetEnabled(true);
+ break;
+ case 'G': {
+ bool value, success;
+ value = OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (success) {
+ m_bp_opts.SetAutoContinue(value);
+ } else
+ error.SetErrorStringWithFormat(
+ "invalid boolean value '%s' passed for -G option",
+ option_arg.str().c_str());
+ } break;
+ case 'i': {
+ uint32_t ignore_count;
+ if (option_arg.getAsInteger(0, ignore_count))
+ error.SetErrorStringWithFormat("invalid ignore count '%s'",
+ option_arg.str().c_str());
+ else
+ m_bp_opts.SetIgnoreCount(ignore_count);
+ } break;
+ case 'o': {
+ bool value, success;
+ value = OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (success) {
+ m_bp_opts.SetOneShot(value);
+ } else
+ error.SetErrorStringWithFormat(
+ "invalid boolean value '%s' passed for -o option",
+ option_arg.str().c_str());
+ } break;
+ case 't': {
+ lldb::tid_t thread_id = LLDB_INVALID_THREAD_ID;
+ if (option_arg[0] != '\0') {
+ if (option_arg == "current") {
+ if (!execution_context) {
+ error.SetErrorStringWithFormat("No context to determine current "
+ "thread");
+ } else {
+ ThreadSP ctx_thread_sp = execution_context->GetThreadSP();
+ if (!ctx_thread_sp || !ctx_thread_sp->IsValid()) {
+ error.SetErrorStringWithFormat("No currently selected thread");
+ } else {
+ thread_id = ctx_thread_sp->GetID();
+ }
+ }
+ } else if (option_arg.getAsInteger(0, thread_id))
+ error.SetErrorStringWithFormat("invalid thread id string '%s'",
+ option_arg.str().c_str());
+ }
+ m_bp_opts.SetThreadID(thread_id);
+ } break;
+ case 'T':
+ m_bp_opts.GetThreadSpec()->SetName(option_arg.str().c_str());
+ break;
+ case 'q':
+ m_bp_opts.GetThreadSpec()->SetQueueName(option_arg.str().c_str());
+ break;
+ case 'x': {
+ uint32_t thread_index = UINT32_MAX;
+ if (option_arg[0] != '\n') {
+ if (option_arg.getAsInteger(0, thread_index))
+ error.SetErrorStringWithFormat("invalid thread index string '%s'",
+ option_arg.str().c_str());
+ }
+ m_bp_opts.GetThreadSpec()->SetIndex(thread_index);
+ } break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_bp_opts.Clear();
+ m_commands.clear();
+ }
+
+ Status OptionParsingFinished(ExecutionContext *execution_context) override {
+ if (!m_commands.empty()) {
+ auto cmd_data = std::make_unique<BreakpointOptions::CommandData>();
+
+ for (std::string &str : m_commands)
+ cmd_data->user_source.AppendString(str);
+
+ cmd_data->stop_on_error = true;
+ m_bp_opts.SetCommandDataCallback(cmd_data);
+ }
+ return Status();
+ }
+
+ const BreakpointOptions &GetBreakpointOptions() { return m_bp_opts; }
+
+ std::vector<std::string> m_commands;
+ BreakpointOptions m_bp_opts;
+};
+
+#define LLDB_OPTIONS_breakpoint_dummy
+#include "CommandOptions.inc"
+
+class BreakpointDummyOptionGroup : public OptionGroup {
+public:
+ BreakpointDummyOptionGroup() = default;
+
+ ~BreakpointDummyOptionGroup() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_breakpoint_dummy_options);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option =
+ g_breakpoint_dummy_options[option_idx].short_option;
+
+ switch (short_option) {
+ case 'D':
+ m_use_dummy = true;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_use_dummy = false;
+ }
+
+ bool m_use_dummy;
+};
+
+#define LLDB_OPTIONS_breakpoint_set
+#include "CommandOptions.inc"
+
+// CommandObjectBreakpointSet
+
+class CommandObjectBreakpointSet : public CommandObjectParsed {
+public:
+ enum BreakpointSetType {
+ eSetTypeInvalid,
+ eSetTypeFileAndLine,
+ eSetTypeAddress,
+ eSetTypeFunctionName,
+ eSetTypeFunctionRegexp,
+ eSetTypeSourceRegexp,
+ eSetTypeException,
+ eSetTypeScripted,
+ };
+
+ CommandObjectBreakpointSet(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "breakpoint set",
+ "Sets a breakpoint or set of breakpoints in the executable.",
+ "breakpoint set <cmd-options>"),
+ m_python_class_options("scripted breakpoint", true, 'P') {
+ // We're picking up all the normal options, commands and disable.
+ m_all_options.Append(&m_python_class_options,
+ LLDB_OPT_SET_1 | LLDB_OPT_SET_2, LLDB_OPT_SET_11);
+ m_all_options.Append(&m_bp_opts,
+ LLDB_OPT_SET_1 | LLDB_OPT_SET_3 | LLDB_OPT_SET_4,
+ LLDB_OPT_SET_ALL);
+ m_all_options.Append(&m_dummy_options, LLDB_OPT_SET_1, LLDB_OPT_SET_ALL);
+ m_all_options.Append(&m_options);
+ m_all_options.Finalize();
+ }
+
+ ~CommandObjectBreakpointSet() override = default;
+
+ Options *GetOptions() override { return &m_all_options; }
+
+ class CommandOptions : public OptionGroup {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option =
+ g_breakpoint_set_options[option_idx].short_option;
+
+ switch (short_option) {
+ case 'a': {
+ m_load_addr = OptionArgParser::ToAddress(execution_context, option_arg,
+ LLDB_INVALID_ADDRESS, &error);
+ } break;
+
+ case 'A':
+ m_all_files = true;
+ break;
+
+ case 'b':
+ m_func_names.push_back(std::string(option_arg));
+ m_func_name_type_mask |= eFunctionNameTypeBase;
+ break;
+
+ case 'u':
+ if (option_arg.getAsInteger(0, m_column))
+ error.SetErrorStringWithFormat("invalid column number: %s",
+ option_arg.str().c_str());
+ break;
+
+ case 'E': {
+ LanguageType language = Language::GetLanguageTypeFromString(option_arg);
+
+ switch (language) {
+ case eLanguageTypeC89:
+ case eLanguageTypeC:
+ case eLanguageTypeC99:
+ case eLanguageTypeC11:
+ m_exception_language = eLanguageTypeC;
+ break;
+ case eLanguageTypeC_plus_plus:
+ case eLanguageTypeC_plus_plus_03:
+ case eLanguageTypeC_plus_plus_11:
+ case eLanguageTypeC_plus_plus_14:
+ m_exception_language = eLanguageTypeC_plus_plus;
+ break;
+ case eLanguageTypeObjC:
+ m_exception_language = eLanguageTypeObjC;
+ break;
+ case eLanguageTypeObjC_plus_plus:
+ error.SetErrorStringWithFormat(
+ "Set exception breakpoints separately for c++ and objective-c");
+ break;
+ case eLanguageTypeUnknown:
+ error.SetErrorStringWithFormat(
+ "Unknown language type: '%s' for exception breakpoint",
+ option_arg.str().c_str());
+ break;
+ default:
+ error.SetErrorStringWithFormat(
+ "Unsupported language type: '%s' for exception breakpoint",
+ option_arg.str().c_str());
+ }
+ } break;
+
+ case 'f':
+ m_filenames.AppendIfUnique(FileSpec(option_arg));
+ break;
+
+ case 'F':
+ m_func_names.push_back(std::string(option_arg));
+ m_func_name_type_mask |= eFunctionNameTypeFull;
+ break;
+
+ case 'h': {
+ bool success;
+ m_catch_bp = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "Invalid boolean value for on-catch option: '%s'",
+ option_arg.str().c_str());
+ } break;
+
+ case 'H':
+ m_hardware = true;
+ break;
+
+ case 'K': {
+ bool success;
+ bool value;
+ value = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (value)
+ m_skip_prologue = eLazyBoolYes;
+ else
+ m_skip_prologue = eLazyBoolNo;
+
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "Invalid boolean value for skip prologue option: '%s'",
+ option_arg.str().c_str());
+ } break;
+
+ case 'l':
+ if (option_arg.getAsInteger(0, m_line_num))
+ error.SetErrorStringWithFormat("invalid line number: %s.",
+ option_arg.str().c_str());
+ break;
+
+ case 'L':
+ m_language = Language::GetLanguageTypeFromString(option_arg);
+ if (m_language == eLanguageTypeUnknown)
+ error.SetErrorStringWithFormat(
+ "Unknown language type: '%s' for breakpoint",
+ option_arg.str().c_str());
+ break;
+
+ case 'm': {
+ bool success;
+ bool value;
+ value = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (value)
+ m_move_to_nearest_code = eLazyBoolYes;
+ else
+ m_move_to_nearest_code = eLazyBoolNo;
+
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "Invalid boolean value for move-to-nearest-code option: '%s'",
+ option_arg.str().c_str());
+ break;
+ }
+
+ case 'M':
+ m_func_names.push_back(std::string(option_arg));
+ m_func_name_type_mask |= eFunctionNameTypeMethod;
+ break;
+
+ case 'n':
+ m_func_names.push_back(std::string(option_arg));
+ m_func_name_type_mask |= eFunctionNameTypeAuto;
+ break;
+
+ case 'N': {
+ if (BreakpointID::StringIsBreakpointName(option_arg, error))
+ m_breakpoint_names.push_back(std::string(option_arg));
+ else
+ error.SetErrorStringWithFormat("Invalid breakpoint name: %s",
+ option_arg.str().c_str());
+ break;
+ }
+
+ case 'R': {
+ lldb::addr_t tmp_offset_addr;
+ tmp_offset_addr = OptionArgParser::ToAddress(execution_context,
+ option_arg, 0, &error);
+ if (error.Success())
+ m_offset_addr = tmp_offset_addr;
+ } break;
+
+ case 'O':
+ m_exception_extra_args.AppendArgument("-O");
+ m_exception_extra_args.AppendArgument(option_arg);
+ break;
+
+ case 'p':
+ m_source_text_regexp.assign(std::string(option_arg));
+ break;
+
+ case 'r':
+ m_func_regexp.assign(std::string(option_arg));
+ break;
+
+ case 's':
+ m_modules.AppendIfUnique(FileSpec(option_arg));
+ break;
+
+ case 'S':
+ m_func_names.push_back(std::string(option_arg));
+ m_func_name_type_mask |= eFunctionNameTypeSelector;
+ break;
+
+ case 'w': {
+ bool success;
+ m_throw_bp = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "Invalid boolean value for on-throw option: '%s'",
+ option_arg.str().c_str());
+ } break;
+
+ case 'X':
+ m_source_regex_func_names.insert(std::string(option_arg));
+ break;
+
+ case 'y':
+ {
+ OptionValueFileColonLine value;
+ Status fcl_err = value.SetValueFromString(option_arg);
+ if (!fcl_err.Success()) {
+ error.SetErrorStringWithFormat(
+ "Invalid value for file:line specifier: %s",
+ fcl_err.AsCString());
+ } else {
+ m_filenames.AppendIfUnique(value.GetFileSpec());
+ m_line_num = value.GetLineNumber();
+ m_column = value.GetColumnNumber();
+ }
+ } break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_filenames.Clear();
+ m_line_num = 0;
+ m_column = 0;
+ m_func_names.clear();
+ m_func_name_type_mask = eFunctionNameTypeNone;
+ m_func_regexp.clear();
+ m_source_text_regexp.clear();
+ m_modules.Clear();
+ m_load_addr = LLDB_INVALID_ADDRESS;
+ m_offset_addr = 0;
+ m_catch_bp = false;
+ m_throw_bp = true;
+ m_hardware = false;
+ m_exception_language = eLanguageTypeUnknown;
+ m_language = lldb::eLanguageTypeUnknown;
+ m_skip_prologue = eLazyBoolCalculate;
+ m_breakpoint_names.clear();
+ m_all_files = false;
+ m_exception_extra_args.Clear();
+ m_move_to_nearest_code = eLazyBoolCalculate;
+ m_source_regex_func_names.clear();
+ m_current_key.clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_breakpoint_set_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ std::string m_condition;
+ FileSpecList m_filenames;
+ uint32_t m_line_num = 0;
+ uint32_t m_column = 0;
+ std::vector<std::string> m_func_names;
+ std::vector<std::string> m_breakpoint_names;
+ lldb::FunctionNameType m_func_name_type_mask = eFunctionNameTypeNone;
+ std::string m_func_regexp;
+ std::string m_source_text_regexp;
+ FileSpecList m_modules;
+ lldb::addr_t m_load_addr = 0;
+ lldb::addr_t m_offset_addr;
+ bool m_catch_bp = false;
+ bool m_throw_bp = true;
+ bool m_hardware = false; // Request to use hardware breakpoints
+ lldb::LanguageType m_exception_language = eLanguageTypeUnknown;
+ lldb::LanguageType m_language = lldb::eLanguageTypeUnknown;
+ LazyBool m_skip_prologue = eLazyBoolCalculate;
+ bool m_all_files = false;
+ Args m_exception_extra_args;
+ LazyBool m_move_to_nearest_code = eLazyBoolCalculate;
+ std::unordered_set<std::string> m_source_regex_func_names;
+ std::string m_current_key;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target &target = GetSelectedOrDummyTarget(m_dummy_options.m_use_dummy);
+
+ // The following are the various types of breakpoints that could be set:
+ // 1). -f -l -p [-s -g] (setting breakpoint by source location)
+ // 2). -a [-s -g] (setting breakpoint by address)
+ // 3). -n [-s -g] (setting breakpoint by function name)
+ // 4). -r [-s -g] (setting breakpoint by function name regular
+ // expression)
+ // 5). -p -f (setting a breakpoint by comparing a reg-exp
+ // to source text)
+ // 6). -E [-w -h] (setting a breakpoint for exceptions for a
+ // given language.)
+
+ BreakpointSetType break_type = eSetTypeInvalid;
+
+ if (!m_python_class_options.GetName().empty())
+ break_type = eSetTypeScripted;
+ else if (m_options.m_line_num != 0)
+ break_type = eSetTypeFileAndLine;
+ else if (m_options.m_load_addr != LLDB_INVALID_ADDRESS)
+ break_type = eSetTypeAddress;
+ else if (!m_options.m_func_names.empty())
+ break_type = eSetTypeFunctionName;
+ else if (!m_options.m_func_regexp.empty())
+ break_type = eSetTypeFunctionRegexp;
+ else if (!m_options.m_source_text_regexp.empty())
+ break_type = eSetTypeSourceRegexp;
+ else if (m_options.m_exception_language != eLanguageTypeUnknown)
+ break_type = eSetTypeException;
+
+ BreakpointSP bp_sp = nullptr;
+ FileSpec module_spec;
+ const bool internal = false;
+
+ // If the user didn't specify skip-prologue, having an offset should turn
+ // that off.
+ if (m_options.m_offset_addr != 0 &&
+ m_options.m_skip_prologue == eLazyBoolCalculate)
+ m_options.m_skip_prologue = eLazyBoolNo;
+
+ switch (break_type) {
+ case eSetTypeFileAndLine: // Breakpoint by source position
+ {
+ FileSpec file;
+ const size_t num_files = m_options.m_filenames.GetSize();
+ if (num_files == 0) {
+ if (!GetDefaultFile(target, file, result)) {
+ result.AppendError("No file supplied and no default file available.");
+ return false;
+ }
+ } else if (num_files > 1) {
+ result.AppendError("Only one file at a time is allowed for file and "
+ "line breakpoints.");
+ return false;
+ } else
+ file = m_options.m_filenames.GetFileSpecAtIndex(0);
+
+ // Only check for inline functions if
+ LazyBool check_inlines = eLazyBoolCalculate;
+
+ bp_sp = target.CreateBreakpoint(
+ &(m_options.m_modules), file, m_options.m_line_num,
+ m_options.m_column, m_options.m_offset_addr, check_inlines,
+ m_options.m_skip_prologue, internal, m_options.m_hardware,
+ m_options.m_move_to_nearest_code);
+ } break;
+
+ case eSetTypeAddress: // Breakpoint by address
+ {
+ // If a shared library has been specified, make an lldb_private::Address
+ // with the library, and use that. That way the address breakpoint
+ // will track the load location of the library.
+ size_t num_modules_specified = m_options.m_modules.GetSize();
+ if (num_modules_specified == 1) {
+ const FileSpec *file_spec =
+ m_options.m_modules.GetFileSpecPointerAtIndex(0);
+ bp_sp = target.CreateAddressInModuleBreakpoint(
+ m_options.m_load_addr, internal, file_spec, m_options.m_hardware);
+ } else if (num_modules_specified == 0) {
+ bp_sp = target.CreateBreakpoint(m_options.m_load_addr, internal,
+ m_options.m_hardware);
+ } else {
+ result.AppendError("Only one shared library can be specified for "
+ "address breakpoints.");
+ return false;
+ }
+ break;
+ }
+ case eSetTypeFunctionName: // Breakpoint by function name
+ {
+ FunctionNameType name_type_mask = m_options.m_func_name_type_mask;
+
+ if (name_type_mask == 0)
+ name_type_mask = eFunctionNameTypeAuto;
+
+ bp_sp = target.CreateBreakpoint(
+ &(m_options.m_modules), &(m_options.m_filenames),
+ m_options.m_func_names, name_type_mask, m_options.m_language,
+ m_options.m_offset_addr, m_options.m_skip_prologue, internal,
+ m_options.m_hardware);
+ } break;
+
+ case eSetTypeFunctionRegexp: // Breakpoint by regular expression function
+ // name
+ {
+ RegularExpression regexp(m_options.m_func_regexp);
+ if (llvm::Error err = regexp.GetError()) {
+ result.AppendErrorWithFormat(
+ "Function name regular expression could not be compiled: %s",
+ llvm::toString(std::move(err)).c_str());
+ // Check if the incorrect regex looks like a globbing expression and
+ // warn the user about it.
+ if (!m_options.m_func_regexp.empty()) {
+ if (m_options.m_func_regexp[0] == '*' ||
+ m_options.m_func_regexp[0] == '?')
+ result.AppendWarning(
+ "Function name regex does not accept glob patterns.");
+ }
+ return false;
+ }
+
+ bp_sp = target.CreateFuncRegexBreakpoint(
+ &(m_options.m_modules), &(m_options.m_filenames), std::move(regexp),
+ m_options.m_language, m_options.m_skip_prologue, internal,
+ m_options.m_hardware);
+ } break;
+ case eSetTypeSourceRegexp: // Breakpoint by regexp on source text.
+ {
+ const size_t num_files = m_options.m_filenames.GetSize();
+
+ if (num_files == 0 && !m_options.m_all_files) {
+ FileSpec file;
+ if (!GetDefaultFile(target, file, result)) {
+ result.AppendError(
+ "No files provided and could not find default file.");
+ return false;
+ } else {
+ m_options.m_filenames.Append(file);
+ }
+ }
+
+ RegularExpression regexp(m_options.m_source_text_regexp);
+ if (llvm::Error err = regexp.GetError()) {
+ result.AppendErrorWithFormat(
+ "Source text regular expression could not be compiled: \"%s\"",
+ llvm::toString(std::move(err)).c_str());
+ return false;
+ }
+ bp_sp = target.CreateSourceRegexBreakpoint(
+ &(m_options.m_modules), &(m_options.m_filenames),
+ m_options.m_source_regex_func_names, std::move(regexp), internal,
+ m_options.m_hardware, m_options.m_move_to_nearest_code);
+ } break;
+ case eSetTypeException: {
+ Status precond_error;
+ bp_sp = target.CreateExceptionBreakpoint(
+ m_options.m_exception_language, m_options.m_catch_bp,
+ m_options.m_throw_bp, internal, &m_options.m_exception_extra_args,
+ &precond_error);
+ if (precond_error.Fail()) {
+ result.AppendErrorWithFormat(
+ "Error setting extra exception arguments: %s",
+ precond_error.AsCString());
+ target.RemoveBreakpointByID(bp_sp->GetID());
+ return false;
+ }
+ } break;
+ case eSetTypeScripted: {
+
+ Status error;
+ bp_sp = target.CreateScriptedBreakpoint(
+ m_python_class_options.GetName().c_str(), &(m_options.m_modules),
+ &(m_options.m_filenames), false, m_options.m_hardware,
+ m_python_class_options.GetStructuredData(), &error);
+ if (error.Fail()) {
+ result.AppendErrorWithFormat(
+ "Error setting extra exception arguments: %s", error.AsCString());
+ target.RemoveBreakpointByID(bp_sp->GetID());
+ return false;
+ }
+ } break;
+ default:
+ break;
+ }
+
+ // Now set the various options that were passed in:
+ if (bp_sp) {
+ bp_sp->GetOptions().CopyOverSetOptions(m_bp_opts.GetBreakpointOptions());
+
+ if (!m_options.m_breakpoint_names.empty()) {
+ Status name_error;
+ for (auto name : m_options.m_breakpoint_names) {
+ target.AddNameToBreakpoint(bp_sp, name.c_str(), name_error);
+ if (name_error.Fail()) {
+ result.AppendErrorWithFormat("Invalid breakpoint name: %s",
+ name.c_str());
+ target.RemoveBreakpointByID(bp_sp->GetID());
+ return false;
+ }
+ }
+ }
+ }
+
+ if (bp_sp) {
+ Stream &output_stream = result.GetOutputStream();
+ const bool show_locations = false;
+ bp_sp->GetDescription(&output_stream, lldb::eDescriptionLevelInitial,
+ show_locations);
+ if (&target == &GetDummyTarget())
+ output_stream.Printf("Breakpoint set in dummy target, will get copied "
+ "into future targets.\n");
+ else {
+ // Don't print out this warning for exception breakpoints. They can
+ // get set before the target is set, but we won't know how to actually
+ // set the breakpoint till we run.
+ if (bp_sp->GetNumLocations() == 0 && break_type != eSetTypeException) {
+ output_stream.Printf("WARNING: Unable to resolve breakpoint to any "
+ "actual locations.\n");
+ }
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else if (!bp_sp) {
+ result.AppendError("Breakpoint creation failed: No breakpoint created.");
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ bool GetDefaultFile(Target &target, FileSpec &file,
+ CommandReturnObject &result) {
+ uint32_t default_line;
+ // First use the Source Manager's default file. Then use the current stack
+ // frame's file.
+ if (!target.GetSourceManager().GetDefaultFileAndLine(file, default_line)) {
+ StackFrame *cur_frame = m_exe_ctx.GetFramePtr();
+ if (cur_frame == nullptr) {
+ result.AppendError(
+ "No selected frame to use to find the default file.");
+ return false;
+ } else if (!cur_frame->HasDebugInformation()) {
+ result.AppendError("Cannot use the selected frame to find the default "
+ "file, it has no debug info.");
+ return false;
+ } else {
+ const SymbolContext &sc =
+ cur_frame->GetSymbolContext(eSymbolContextLineEntry);
+ if (sc.line_entry.file) {
+ file = sc.line_entry.file;
+ } else {
+ result.AppendError("Can't find the file for the selected frame to "
+ "use as the default file.");
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ BreakpointOptionGroup m_bp_opts;
+ BreakpointDummyOptionGroup m_dummy_options;
+ OptionGroupPythonClassWithDict m_python_class_options;
+ CommandOptions m_options;
+ OptionGroupOptions m_all_options;
+};
+
+// CommandObjectBreakpointModify
+#pragma mark Modify
+
+class CommandObjectBreakpointModify : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointModify(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "breakpoint modify",
+ "Modify the options on a breakpoint or set of "
+ "breakpoints in the executable. "
+ "If no breakpoint is specified, acts on the last "
+ "created breakpoint. "
+ "With the exception of -e, -d and -i, passing an "
+ "empty argument clears the modification.",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandObject::AddIDsArgumentData(arg, eArgTypeBreakpointID,
+ eArgTypeBreakpointIDRange);
+ // Add the entry for the first argument for this command to the object's
+ // arguments vector.
+ m_arguments.push_back(arg);
+
+ m_options.Append(&m_bp_opts,
+ LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3,
+ LLDB_OPT_SET_ALL);
+ m_options.Append(&m_dummy_opts, LLDB_OPT_SET_1, LLDB_OPT_SET_ALL);
+ m_options.Finalize();
+ }
+
+ ~CommandObjectBreakpointModify() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eBreakpointCompletion,
+ request, nullptr);
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target &target = GetSelectedOrDummyTarget(m_dummy_opts.m_use_dummy);
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target.GetBreakpointList().GetListMutex(lock);
+
+ BreakpointIDList valid_bp_ids;
+
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
+ command, &target, result, &valid_bp_ids,
+ BreakpointName::Permissions::PermissionKinds::disablePerm);
+
+ if (result.Succeeded()) {
+ const size_t count = valid_bp_ids.GetSize();
+ for (size_t i = 0; i < count; ++i) {
+ BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
+
+ if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
+ Breakpoint *bp =
+ target.GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
+ if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) {
+ BreakpointLocation *location =
+ bp->FindLocationByID(cur_bp_id.GetLocationID()).get();
+ if (location)
+ location->GetLocationOptions().CopyOverSetOptions(
+ m_bp_opts.GetBreakpointOptions());
+ } else {
+ bp->GetOptions().CopyOverSetOptions(
+ m_bp_opts.GetBreakpointOptions());
+ }
+ }
+ }
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ BreakpointOptionGroup m_bp_opts;
+ BreakpointDummyOptionGroup m_dummy_opts;
+ OptionGroupOptions m_options;
+};
+
+// CommandObjectBreakpointEnable
+#pragma mark Enable
+
+class CommandObjectBreakpointEnable : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointEnable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "enable",
+ "Enable the specified disabled breakpoint(s). If "
+ "no breakpoints are specified, enable all of them.",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandObject::AddIDsArgumentData(arg, eArgTypeBreakpointID,
+ eArgTypeBreakpointIDRange);
+ // Add the entry for the first argument for this command to the object's
+ // arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectBreakpointEnable() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eBreakpointCompletion,
+ request, nullptr);
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target &target = GetSelectedOrDummyTarget();
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target.GetBreakpointList().GetListMutex(lock);
+
+ const BreakpointList &breakpoints = target.GetBreakpointList();
+
+ size_t num_breakpoints = breakpoints.GetSize();
+
+ if (num_breakpoints == 0) {
+ result.AppendError("No breakpoints exist to be enabled.");
+ return false;
+ }
+
+ if (command.empty()) {
+ // No breakpoint selected; enable all currently set breakpoints.
+ target.EnableAllowedBreakpoints();
+ result.AppendMessageWithFormat("All breakpoints enabled. (%" PRIu64
+ " breakpoints)\n",
+ (uint64_t)num_breakpoints);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ // Particular breakpoint selected; enable that breakpoint.
+ BreakpointIDList valid_bp_ids;
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
+ command, &target, result, &valid_bp_ids,
+ BreakpointName::Permissions::PermissionKinds::disablePerm);
+
+ if (result.Succeeded()) {
+ int enable_count = 0;
+ int loc_count = 0;
+ const size_t count = valid_bp_ids.GetSize();
+ for (size_t i = 0; i < count; ++i) {
+ BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
+
+ if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
+ Breakpoint *breakpoint =
+ target.GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
+ if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) {
+ BreakpointLocation *location =
+ breakpoint->FindLocationByID(cur_bp_id.GetLocationID()).get();
+ if (location) {
+ location->SetEnabled(true);
+ ++loc_count;
+ }
+ } else {
+ breakpoint->SetEnabled(true);
+ ++enable_count;
+ }
+ }
+ }
+ result.AppendMessageWithFormat("%d breakpoints enabled.\n",
+ enable_count + loc_count);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectBreakpointDisable
+#pragma mark Disable
+
+class CommandObjectBreakpointDisable : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointDisable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "breakpoint disable",
+ "Disable the specified breakpoint(s) without deleting "
+ "them. If none are specified, disable all "
+ "breakpoints.",
+ nullptr) {
+ SetHelpLong(
+ "Disable the specified breakpoint(s) without deleting them. \
+If none are specified, disable all breakpoints."
+ R"(
+
+)"
+ "Note: disabling a breakpoint will cause none of its locations to be hit \
+regardless of whether individual locations are enabled or disabled. After the sequence:"
+ R"(
+
+ (lldb) break disable 1
+ (lldb) break enable 1.1
+
+execution will NOT stop at location 1.1. To achieve that, type:
+
+ (lldb) break disable 1.*
+ (lldb) break enable 1.1
+
+)"
+ "The first command disables all locations for breakpoint 1, \
+the second re-enables the first location.");
+
+ CommandArgumentEntry arg;
+ CommandObject::AddIDsArgumentData(arg, eArgTypeBreakpointID,
+ eArgTypeBreakpointIDRange);
+ // Add the entry for the first argument for this command to the object's
+ // arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectBreakpointDisable() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eBreakpointCompletion,
+ request, nullptr);
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target &target = GetSelectedOrDummyTarget();
+ std::unique_lock<std::recursive_mutex> lock;
+ target.GetBreakpointList().GetListMutex(lock);
+
+ const BreakpointList &breakpoints = target.GetBreakpointList();
+ size_t num_breakpoints = breakpoints.GetSize();
+
+ if (num_breakpoints == 0) {
+ result.AppendError("No breakpoints exist to be disabled.");
+ return false;
+ }
+
+ if (command.empty()) {
+ // No breakpoint selected; disable all currently set breakpoints.
+ target.DisableAllowedBreakpoints();
+ result.AppendMessageWithFormat("All breakpoints disabled. (%" PRIu64
+ " breakpoints)\n",
+ (uint64_t)num_breakpoints);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ // Particular breakpoint selected; disable that breakpoint.
+ BreakpointIDList valid_bp_ids;
+
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
+ command, &target, result, &valid_bp_ids,
+ BreakpointName::Permissions::PermissionKinds::disablePerm);
+
+ if (result.Succeeded()) {
+ int disable_count = 0;
+ int loc_count = 0;
+ const size_t count = valid_bp_ids.GetSize();
+ for (size_t i = 0; i < count; ++i) {
+ BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
+
+ if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
+ Breakpoint *breakpoint =
+ target.GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
+ if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) {
+ BreakpointLocation *location =
+ breakpoint->FindLocationByID(cur_bp_id.GetLocationID()).get();
+ if (location) {
+ location->SetEnabled(false);
+ ++loc_count;
+ }
+ } else {
+ breakpoint->SetEnabled(false);
+ ++disable_count;
+ }
+ }
+ }
+ result.AppendMessageWithFormat("%d breakpoints disabled.\n",
+ disable_count + loc_count);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectBreakpointList
+
+#pragma mark List::CommandOptions
+#define LLDB_OPTIONS_breakpoint_list
+#include "CommandOptions.inc"
+
+#pragma mark List
+
+class CommandObjectBreakpointList : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "breakpoint list",
+ "List some or all breakpoints at configurable levels of detail.",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandArgumentData bp_id_arg;
+
+ // Define the first (and only) variant of this arg.
+ bp_id_arg.arg_type = eArgTypeBreakpointID;
+ bp_id_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(bp_id_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectBreakpointList() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'b':
+ m_level = lldb::eDescriptionLevelBrief;
+ break;
+ case 'D':
+ m_use_dummy = true;
+ break;
+ case 'f':
+ m_level = lldb::eDescriptionLevelFull;
+ break;
+ case 'v':
+ m_level = lldb::eDescriptionLevelVerbose;
+ break;
+ case 'i':
+ m_internal = true;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_level = lldb::eDescriptionLevelFull;
+ m_internal = false;
+ m_use_dummy = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_breakpoint_list_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ lldb::DescriptionLevel m_level = lldb::eDescriptionLevelBrief;
+
+ bool m_internal;
+ bool m_use_dummy = false;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target &target = GetSelectedOrDummyTarget(m_options.m_use_dummy);
+
+ const BreakpointList &breakpoints =
+ target.GetBreakpointList(m_options.m_internal);
+ std::unique_lock<std::recursive_mutex> lock;
+ target.GetBreakpointList(m_options.m_internal).GetListMutex(lock);
+
+ size_t num_breakpoints = breakpoints.GetSize();
+
+ if (num_breakpoints == 0) {
+ result.AppendMessage("No breakpoints currently set.");
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+
+ Stream &output_stream = result.GetOutputStream();
+
+ if (command.empty()) {
+ // No breakpoint selected; show info about all currently set breakpoints.
+ result.AppendMessage("Current breakpoints:");
+ for (size_t i = 0; i < num_breakpoints; ++i) {
+ Breakpoint *breakpoint = breakpoints.GetBreakpointAtIndex(i).get();
+ if (breakpoint->AllowList())
+ AddBreakpointDescription(&output_stream, breakpoint,
+ m_options.m_level);
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ // Particular breakpoints selected; show info about that breakpoint.
+ BreakpointIDList valid_bp_ids;
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
+ command, &target, result, &valid_bp_ids,
+ BreakpointName::Permissions::PermissionKinds::listPerm);
+
+ if (result.Succeeded()) {
+ for (size_t i = 0; i < valid_bp_ids.GetSize(); ++i) {
+ BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
+ Breakpoint *breakpoint =
+ target.GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
+ AddBreakpointDescription(&output_stream, breakpoint,
+ m_options.m_level);
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendError("Invalid breakpoint ID.");
+ }
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectBreakpointClear
+#pragma mark Clear::CommandOptions
+
+#define LLDB_OPTIONS_breakpoint_clear
+#include "CommandOptions.inc"
+
+#pragma mark Clear
+
+class CommandObjectBreakpointClear : public CommandObjectParsed {
+public:
+ enum BreakpointClearType { eClearTypeInvalid, eClearTypeFileAndLine };
+
+ CommandObjectBreakpointClear(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "breakpoint clear",
+ "Delete or disable breakpoints matching the "
+ "specified source file and line.",
+ "breakpoint clear <cmd-options>") {}
+
+ ~CommandObjectBreakpointClear() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'f':
+ m_filename.assign(std::string(option_arg));
+ break;
+
+ case 'l':
+ option_arg.getAsInteger(0, m_line_num);
+ break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_filename.clear();
+ m_line_num = 0;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_breakpoint_clear_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ std::string m_filename;
+ uint32_t m_line_num = 0;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target &target = GetSelectedOrDummyTarget();
+
+ // The following are the various types of breakpoints that could be
+ // cleared:
+ // 1). -f -l (clearing breakpoint by source location)
+
+ BreakpointClearType break_type = eClearTypeInvalid;
+
+ if (m_options.m_line_num != 0)
+ break_type = eClearTypeFileAndLine;
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target.GetBreakpointList().GetListMutex(lock);
+
+ BreakpointList &breakpoints = target.GetBreakpointList();
+ size_t num_breakpoints = breakpoints.GetSize();
+
+ // Early return if there's no breakpoint at all.
+ if (num_breakpoints == 0) {
+ result.AppendError("Breakpoint clear: No breakpoint cleared.");
+ return result.Succeeded();
+ }
+
+ // Find matching breakpoints and delete them.
+
+ // First create a copy of all the IDs.
+ std::vector<break_id_t> BreakIDs;
+ for (size_t i = 0; i < num_breakpoints; ++i)
+ BreakIDs.push_back(breakpoints.GetBreakpointAtIndex(i)->GetID());
+
+ int num_cleared = 0;
+ StreamString ss;
+ switch (break_type) {
+ case eClearTypeFileAndLine: // Breakpoint by source position
+ {
+ const ConstString filename(m_options.m_filename.c_str());
+ BreakpointLocationCollection loc_coll;
+
+ for (size_t i = 0; i < num_breakpoints; ++i) {
+ Breakpoint *bp = breakpoints.FindBreakpointByID(BreakIDs[i]).get();
+
+ if (bp->GetMatchingFileLine(filename, m_options.m_line_num, loc_coll)) {
+ // If the collection size is 0, it's a full match and we can just
+ // remove the breakpoint.
+ if (loc_coll.GetSize() == 0) {
+ bp->GetDescription(&ss, lldb::eDescriptionLevelBrief);
+ ss.EOL();
+ target.RemoveBreakpointByID(bp->GetID());
+ ++num_cleared;
+ }
+ }
+ }
+ } break;
+
+ default:
+ break;
+ }
+
+ if (num_cleared > 0) {
+ Stream &output_stream = result.GetOutputStream();
+ output_stream.Printf("%d breakpoints cleared:\n", num_cleared);
+ output_stream << ss.GetString();
+ output_stream.EOL();
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendError("Breakpoint clear: No breakpoint cleared.");
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectBreakpointDelete
+#define LLDB_OPTIONS_breakpoint_delete
+#include "CommandOptions.inc"
+
+#pragma mark Delete
+
+class CommandObjectBreakpointDelete : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "breakpoint delete",
+ "Delete the specified breakpoint(s). If no "
+ "breakpoints are specified, delete them all.",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandObject::AddIDsArgumentData(arg, eArgTypeBreakpointID,
+ eArgTypeBreakpointIDRange);
+ // Add the entry for the first argument for this command to the object's
+ // arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectBreakpointDelete() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eBreakpointCompletion,
+ request, nullptr);
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'f':
+ m_force = true;
+ break;
+
+ case 'D':
+ m_use_dummy = true;
+ break;
+
+ case 'd':
+ m_delete_disabled = true;
+ break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_use_dummy = false;
+ m_force = false;
+ m_delete_disabled = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_breakpoint_delete_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ bool m_use_dummy = false;
+ bool m_force = false;
+ bool m_delete_disabled = false;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target &target = GetSelectedOrDummyTarget(m_options.m_use_dummy);
+ result.Clear();
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target.GetBreakpointList().GetListMutex(lock);
+
+ BreakpointList &breakpoints = target.GetBreakpointList();
+
+ size_t num_breakpoints = breakpoints.GetSize();
+
+ if (num_breakpoints == 0) {
+ result.AppendError("No breakpoints exist to be deleted.");
+ return false;
+ }
+
+ // Handle the delete all breakpoints case:
+ if (command.empty() && !m_options.m_delete_disabled) {
+ if (!m_options.m_force &&
+ !m_interpreter.Confirm(
+ "About to delete all breakpoints, do you want to do that?",
+ true)) {
+ result.AppendMessage("Operation cancelled...");
+ } else {
+ target.RemoveAllowedBreakpoints();
+ result.AppendMessageWithFormat(
+ "All breakpoints removed. (%" PRIu64 " breakpoint%s)\n",
+ (uint64_t)num_breakpoints, num_breakpoints > 1 ? "s" : "");
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+ }
+
+ // Either we have some kind of breakpoint specification(s),
+ // or we are handling "break disable --deleted". Gather the list
+ // of breakpoints to delete here, the we'll delete them below.
+ BreakpointIDList valid_bp_ids;
+
+ if (m_options.m_delete_disabled) {
+ BreakpointIDList excluded_bp_ids;
+
+ if (!command.empty()) {
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
+ command, &target, result, &excluded_bp_ids,
+ BreakpointName::Permissions::PermissionKinds::deletePerm);
+ if (!result.Succeeded())
+ return false;
+ }
+
+ for (auto breakpoint_sp : breakpoints.Breakpoints()) {
+ if (!breakpoint_sp->IsEnabled() && breakpoint_sp->AllowDelete()) {
+ BreakpointID bp_id(breakpoint_sp->GetID());
+ size_t pos = 0;
+ if (!excluded_bp_ids.FindBreakpointID(bp_id, &pos))
+ valid_bp_ids.AddBreakpointID(breakpoint_sp->GetID());
+ }
+ }
+ if (valid_bp_ids.GetSize() == 0) {
+ result.AppendError("No disabled breakpoints.");
+ return false;
+ }
+ } else {
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
+ command, &target, result, &valid_bp_ids,
+ BreakpointName::Permissions::PermissionKinds::deletePerm);
+ if (!result.Succeeded())
+ return false;
+ }
+
+ int delete_count = 0;
+ int disable_count = 0;
+ const size_t count = valid_bp_ids.GetSize();
+ for (size_t i = 0; i < count; ++i) {
+ BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
+
+ if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
+ if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) {
+ Breakpoint *breakpoint =
+ target.GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
+ BreakpointLocation *location =
+ breakpoint->FindLocationByID(cur_bp_id.GetLocationID()).get();
+ // It makes no sense to try to delete individual locations, so we
+ // disable them instead.
+ if (location) {
+ location->SetEnabled(false);
+ ++disable_count;
+ }
+ } else {
+ target.RemoveBreakpointByID(cur_bp_id.GetBreakpointID());
+ ++delete_count;
+ }
+ }
+ }
+ result.AppendMessageWithFormat(
+ "%d breakpoints deleted; %d breakpoint locations disabled.\n",
+ delete_count, disable_count);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectBreakpointName
+#define LLDB_OPTIONS_breakpoint_name
+#include "CommandOptions.inc"
+
+class BreakpointNameOptionGroup : public OptionGroup {
+public:
+ BreakpointNameOptionGroup()
+ : m_breakpoint(LLDB_INVALID_BREAK_ID), m_use_dummy(false) {}
+
+ ~BreakpointNameOptionGroup() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_breakpoint_name_options);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = g_breakpoint_name_options[option_idx].short_option;
+
+ switch (short_option) {
+ case 'N':
+ if (BreakpointID::StringIsBreakpointName(option_arg, error) &&
+ error.Success())
+ m_name.SetValueFromString(option_arg);
+ break;
+ case 'B':
+ if (m_breakpoint.SetValueFromString(option_arg).Fail())
+ error.SetErrorStringWithFormat(
+ "unrecognized value \"%s\" for breakpoint",
+ option_arg.str().c_str());
+ break;
+ case 'D':
+ if (m_use_dummy.SetValueFromString(option_arg).Fail())
+ error.SetErrorStringWithFormat(
+ "unrecognized value \"%s\" for use-dummy",
+ option_arg.str().c_str());
+ break;
+ case 'H':
+ m_help_string.SetValueFromString(option_arg);
+ break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_name.Clear();
+ m_breakpoint.Clear();
+ m_use_dummy.Clear();
+ m_use_dummy.SetDefaultValue(false);
+ m_help_string.Clear();
+ }
+
+ OptionValueString m_name;
+ OptionValueUInt64 m_breakpoint;
+ OptionValueBoolean m_use_dummy;
+ OptionValueString m_help_string;
+};
+
+#define LLDB_OPTIONS_breakpoint_access
+#include "CommandOptions.inc"
+
+class BreakpointAccessOptionGroup : public OptionGroup {
+public:
+ BreakpointAccessOptionGroup() = default;
+
+ ~BreakpointAccessOptionGroup() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_breakpoint_access_options);
+ }
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option =
+ g_breakpoint_access_options[option_idx].short_option;
+
+ switch (short_option) {
+ case 'L': {
+ bool value, success;
+ value = OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (success) {
+ m_permissions.SetAllowList(value);
+ } else
+ error.SetErrorStringWithFormat(
+ "invalid boolean value '%s' passed for -L option",
+ option_arg.str().c_str());
+ } break;
+ case 'A': {
+ bool value, success;
+ value = OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (success) {
+ m_permissions.SetAllowDisable(value);
+ } else
+ error.SetErrorStringWithFormat(
+ "invalid boolean value '%s' passed for -L option",
+ option_arg.str().c_str());
+ } break;
+ case 'D': {
+ bool value, success;
+ value = OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (success) {
+ m_permissions.SetAllowDelete(value);
+ } else
+ error.SetErrorStringWithFormat(
+ "invalid boolean value '%s' passed for -L option",
+ option_arg.str().c_str());
+ } break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {}
+
+ const BreakpointName::Permissions &GetPermissions() const {
+ return m_permissions;
+ }
+ BreakpointName::Permissions m_permissions;
+};
+
+class CommandObjectBreakpointNameConfigure : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointNameConfigure(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "configure",
+ "Configure the options for the breakpoint"
+ " name provided. "
+ "If you provide a breakpoint id, the options will be copied from "
+ "the breakpoint, otherwise only the options specified will be set "
+ "on the name.",
+ "breakpoint name configure <command-options> "
+ "<breakpoint-name-list>") {
+ // Create the first variant for the first (and only) argument for this
+ // command.
+ CommandArgumentEntry arg1;
+ CommandArgumentData id_arg;
+ id_arg.arg_type = eArgTypeBreakpointName;
+ id_arg.arg_repetition = eArgRepeatOptional;
+ arg1.push_back(id_arg);
+ m_arguments.push_back(arg1);
+
+ m_option_group.Append(&m_bp_opts, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_access_options, LLDB_OPT_SET_ALL,
+ LLDB_OPT_SET_ALL);
+ m_option_group.Append(&m_bp_id, LLDB_OPT_SET_2 | LLDB_OPT_SET_4,
+ LLDB_OPT_SET_ALL);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectBreakpointNameConfigure() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+
+ const size_t argc = command.GetArgumentCount();
+ if (argc == 0) {
+ result.AppendError("No names provided.");
+ return false;
+ }
+
+ Target &target = GetSelectedOrDummyTarget(false);
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target.GetBreakpointList().GetListMutex(lock);
+
+ // Make a pass through first to see that all the names are legal.
+ for (auto &entry : command.entries()) {
+ Status error;
+ if (!BreakpointID::StringIsBreakpointName(entry.ref(), error)) {
+ result.AppendErrorWithFormat("Invalid breakpoint name: %s - %s",
+ entry.c_str(), error.AsCString());
+ return false;
+ }
+ }
+ // Now configure them, we already pre-checked the names so we don't need to
+ // check the error:
+ BreakpointSP bp_sp;
+ if (m_bp_id.m_breakpoint.OptionWasSet()) {
+ lldb::break_id_t bp_id = m_bp_id.m_breakpoint.GetUInt64Value();
+ bp_sp = target.GetBreakpointByID(bp_id);
+ if (!bp_sp) {
+ result.AppendErrorWithFormatv("Could not find specified breakpoint {0}",
+ bp_id);
+ return false;
+ }
+ }
+
+ Status error;
+ for (auto &entry : command.entries()) {
+ ConstString name(entry.c_str());
+ BreakpointName *bp_name = target.FindBreakpointName(name, true, error);
+ if (!bp_name)
+ continue;
+ if (m_bp_id.m_help_string.OptionWasSet())
+ bp_name->SetHelp(m_bp_id.m_help_string.GetStringValue().str().c_str());
+
+ if (bp_sp)
+ target.ConfigureBreakpointName(*bp_name, bp_sp->GetOptions(),
+ m_access_options.GetPermissions());
+ else
+ target.ConfigureBreakpointName(*bp_name,
+ m_bp_opts.GetBreakpointOptions(),
+ m_access_options.GetPermissions());
+ }
+ return true;
+ }
+
+private:
+ BreakpointNameOptionGroup m_bp_id; // Only using the id part of this.
+ BreakpointOptionGroup m_bp_opts;
+ BreakpointAccessOptionGroup m_access_options;
+ OptionGroupOptions m_option_group;
+};
+
+class CommandObjectBreakpointNameAdd : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointNameAdd(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "add", "Add a name to the breakpoints provided.",
+ "breakpoint name add <command-options> <breakpoint-id-list>") {
+ // Create the first variant for the first (and only) argument for this
+ // command.
+ CommandArgumentEntry arg1;
+ CommandArgumentData id_arg;
+ id_arg.arg_type = eArgTypeBreakpointID;
+ id_arg.arg_repetition = eArgRepeatOptional;
+ arg1.push_back(id_arg);
+ m_arguments.push_back(arg1);
+
+ m_option_group.Append(&m_name_options, LLDB_OPT_SET_1, LLDB_OPT_SET_ALL);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectBreakpointNameAdd() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eBreakpointCompletion,
+ request, nullptr);
+ }
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (!m_name_options.m_name.OptionWasSet()) {
+ result.AppendError("No name option provided.");
+ return false;
+ }
+
+ Target &target =
+ GetSelectedOrDummyTarget(m_name_options.m_use_dummy.GetCurrentValue());
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target.GetBreakpointList().GetListMutex(lock);
+
+ const BreakpointList &breakpoints = target.GetBreakpointList();
+
+ size_t num_breakpoints = breakpoints.GetSize();
+ if (num_breakpoints == 0) {
+ result.AppendError("No breakpoints, cannot add names.");
+ return false;
+ }
+
+ // Particular breakpoint selected; disable that breakpoint.
+ BreakpointIDList valid_bp_ids;
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs(
+ command, &target, result, &valid_bp_ids,
+ BreakpointName::Permissions::PermissionKinds::listPerm);
+
+ if (result.Succeeded()) {
+ if (valid_bp_ids.GetSize() == 0) {
+ result.AppendError("No breakpoints specified, cannot add names.");
+ return false;
+ }
+ size_t num_valid_ids = valid_bp_ids.GetSize();
+ const char *bp_name = m_name_options.m_name.GetCurrentValue();
+ Status error; // This error reports illegal names, but we've already
+ // checked that, so we don't need to check it again here.
+ for (size_t index = 0; index < num_valid_ids; index++) {
+ lldb::break_id_t bp_id =
+ valid_bp_ids.GetBreakpointIDAtIndex(index).GetBreakpointID();
+ BreakpointSP bp_sp = breakpoints.FindBreakpointByID(bp_id);
+ target.AddNameToBreakpoint(bp_sp, bp_name, error);
+ }
+ }
+
+ return true;
+ }
+
+private:
+ BreakpointNameOptionGroup m_name_options;
+ OptionGroupOptions m_option_group;
+};
+
+class CommandObjectBreakpointNameDelete : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointNameDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "delete",
+ "Delete a name from the breakpoints provided.",
+ "breakpoint name delete <command-options> <breakpoint-id-list>") {
+ // Create the first variant for the first (and only) argument for this
+ // command.
+ CommandArgumentEntry arg1;
+ CommandArgumentData id_arg;
+ id_arg.arg_type = eArgTypeBreakpointID;
+ id_arg.arg_repetition = eArgRepeatOptional;
+ arg1.push_back(id_arg);
+ m_arguments.push_back(arg1);
+
+ m_option_group.Append(&m_name_options, LLDB_OPT_SET_1, LLDB_OPT_SET_ALL);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectBreakpointNameDelete() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eBreakpointCompletion,
+ request, nullptr);
+ }
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (!m_name_options.m_name.OptionWasSet()) {
+ result.AppendError("No name option provided.");
+ return false;
+ }
+
+ Target &target =
+ GetSelectedOrDummyTarget(m_name_options.m_use_dummy.GetCurrentValue());
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target.GetBreakpointList().GetListMutex(lock);
+
+ const BreakpointList &breakpoints = target.GetBreakpointList();
+
+ size_t num_breakpoints = breakpoints.GetSize();
+ if (num_breakpoints == 0) {
+ result.AppendError("No breakpoints, cannot delete names.");
+ return false;
+ }
+
+ // Particular breakpoint selected; disable that breakpoint.
+ BreakpointIDList valid_bp_ids;
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs(
+ command, &target, result, &valid_bp_ids,
+ BreakpointName::Permissions::PermissionKinds::deletePerm);
+
+ if (result.Succeeded()) {
+ if (valid_bp_ids.GetSize() == 0) {
+ result.AppendError("No breakpoints specified, cannot delete names.");
+ return false;
+ }
+ ConstString bp_name(m_name_options.m_name.GetCurrentValue());
+ size_t num_valid_ids = valid_bp_ids.GetSize();
+ for (size_t index = 0; index < num_valid_ids; index++) {
+ lldb::break_id_t bp_id =
+ valid_bp_ids.GetBreakpointIDAtIndex(index).GetBreakpointID();
+ BreakpointSP bp_sp = breakpoints.FindBreakpointByID(bp_id);
+ target.RemoveNameFromBreakpoint(bp_sp, bp_name);
+ }
+ }
+
+ return true;
+ }
+
+private:
+ BreakpointNameOptionGroup m_name_options;
+ OptionGroupOptions m_option_group;
+};
+
+class CommandObjectBreakpointNameList : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointNameList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "list",
+ "List either the names for a breakpoint or info "
+ "about a given name. With no arguments, lists all "
+ "names",
+ "breakpoint name list <command-options>") {
+ m_option_group.Append(&m_name_options, LLDB_OPT_SET_3, LLDB_OPT_SET_ALL);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectBreakpointNameList() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target &target =
+ GetSelectedOrDummyTarget(m_name_options.m_use_dummy.GetCurrentValue());
+
+ std::vector<std::string> name_list;
+ if (command.empty()) {
+ target.GetBreakpointNames(name_list);
+ } else {
+ for (const Args::ArgEntry &arg : command) {
+ name_list.push_back(arg.c_str());
+ }
+ }
+
+ if (name_list.empty()) {
+ result.AppendMessage("No breakpoint names found.");
+ } else {
+ for (const std::string &name_str : name_list) {
+ const char *name = name_str.c_str();
+ // First print out the options for the name:
+ Status error;
+ BreakpointName *bp_name =
+ target.FindBreakpointName(ConstString(name), false, error);
+ if (bp_name) {
+ StreamString s;
+ result.AppendMessageWithFormat("Name: %s\n", name);
+ if (bp_name->GetDescription(&s, eDescriptionLevelFull)) {
+ result.AppendMessage(s.GetString());
+ }
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target.GetBreakpointList().GetListMutex(lock);
+
+ BreakpointList &breakpoints = target.GetBreakpointList();
+ bool any_set = false;
+ for (BreakpointSP bp_sp : breakpoints.Breakpoints()) {
+ if (bp_sp->MatchesName(name)) {
+ StreamString s;
+ any_set = true;
+ bp_sp->GetDescription(&s, eDescriptionLevelBrief);
+ s.EOL();
+ result.AppendMessage(s.GetString());
+ }
+ }
+ if (!any_set)
+ result.AppendMessage("No breakpoints using this name.");
+ } else {
+ result.AppendMessageWithFormat("Name: %s not found.\n", name);
+ }
+ }
+ }
+ return true;
+ }
+
+private:
+ BreakpointNameOptionGroup m_name_options;
+ OptionGroupOptions m_option_group;
+};
+
+// CommandObjectBreakpointName
+class CommandObjectBreakpointName : public CommandObjectMultiword {
+public:
+ CommandObjectBreakpointName(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "name", "Commands to manage breakpoint names") {
+
+
+ SetHelpLong(
+ R"(
+Breakpoint names provide a general tagging mechanism for breakpoints. Each
+breakpoint name can be added to any number of breakpoints, and each breakpoint
+can have any number of breakpoint names attached to it. For instance:
+
+ (lldb) break name add -N MyName 1-10
+
+adds the name MyName to breakpoints 1-10, and:
+
+ (lldb) break set -n myFunc -N Name1 -N Name2
+
+adds two names to the breakpoint set at myFunc.
+
+They have a number of interrelated uses:
+
+1) They provide a stable way to refer to a breakpoint (e.g. in another
+breakpoint's action). Using the breakpoint ID for this purpose is fragile, since
+it depends on the order of breakpoint creation. Giving a name to the breakpoint
+you want to act on, and then referring to it by name, is more robust:
+
+ (lldb) break set -n myFunc -N BKPT1
+ (lldb) break set -n myOtherFunc -C "break disable BKPT1"
+
+2) This is actually just a specific use of a more general feature of breakpoint
+names. The <breakpt-id-list> argument type used to specify one or more
+breakpoints in most of the commands that deal with breakpoints also accepts
+breakpoint names. That allows you to refer to one breakpoint in a stable
+manner, but also makes them a convenient grouping mechanism, allowing you to
+easily act on a group of breakpoints by using their name, for instance disabling
+them all in one action:
+
+ (lldb) break set -n myFunc -N Group1
+ (lldb) break set -n myOtherFunc -N Group1
+ (lldb) break disable Group1
+
+3) But breakpoint names are also entities in their own right, and can be
+configured with all the modifiable attributes of a breakpoint. Then when you
+add a breakpoint name to a breakpoint, the breakpoint will be configured to
+match the state of the breakpoint name. The link between the name and the
+breakpoints sharing it remains live, so if you change the configuration on the
+name, it will also change the configurations on the breakpoints:
+
+ (lldb) break name configure -i 10 IgnoreSome
+ (lldb) break set -n myFunc -N IgnoreSome
+ (lldb) break list IgnoreSome
+ 2: name = 'myFunc', locations = 0 (pending) Options: ignore: 10 enabled
+ Names:
+ IgnoreSome
+ (lldb) break name configure -i 5 IgnoreSome
+ (lldb) break list IgnoreSome
+ 2: name = 'myFunc', locations = 0 (pending) Options: ignore: 5 enabled
+ Names:
+ IgnoreSome
+
+Options that are not configured on a breakpoint name don't affect the value of
+those options on the breakpoints they are added to. So for instance, if Name1
+has the -i option configured and Name2 the -c option, adding both names to a
+breakpoint will set the -i option from Name1 and the -c option from Name2, and
+the other options will be unaltered.
+
+If you add multiple names to a breakpoint which have configured values for
+the same option, the last name added's value wins.
+
+The "liveness" of these settings is one way, from name to breakpoint.
+If you use "break modify" to change an option that is also configured on a name
+which that breakpoint has, the "break modify" command will override the setting
+for that breakpoint, but won't change the value configured in the name or on the
+other breakpoints sharing that name.
+
+4) Breakpoint names are also a convenient way to copy option sets from one
+breakpoint to another. Using the -B option to "breakpoint name configure" makes
+a name configured with all the options of the original breakpoint. Then
+adding that name to another breakpoint copies over all the values from the
+original breakpoint to the new one.
+
+5) You can also use breakpoint names to hide breakpoints from the breakpoint
+operations that act on all breakpoints: "break delete", "break disable" and
+"break list". You do that by specifying a "false" value for the
+--allow-{list,delete,disable} options to "breakpoint name configure" and then
+adding that name to a breakpoint.
+
+This won't keep the breakpoint from being deleted or disabled if you refer to it
+specifically by ID. The point of the feature is to make sure users don't
+inadvertently delete or disable useful breakpoints (e.g. ones an IDE is using
+for its own purposes) as part of a "delete all" or "disable all" operation. The
+list hiding is because it's confusing for people to see breakpoints they
+didn't set.
+
+)");
+ CommandObjectSP add_command_object(
+ new CommandObjectBreakpointNameAdd(interpreter));
+ CommandObjectSP delete_command_object(
+ new CommandObjectBreakpointNameDelete(interpreter));
+ CommandObjectSP list_command_object(
+ new CommandObjectBreakpointNameList(interpreter));
+ CommandObjectSP configure_command_object(
+ new CommandObjectBreakpointNameConfigure(interpreter));
+
+ LoadSubCommand("add", add_command_object);
+ LoadSubCommand("delete", delete_command_object);
+ LoadSubCommand("list", list_command_object);
+ LoadSubCommand("configure", configure_command_object);
+ }
+
+ ~CommandObjectBreakpointName() override = default;
+};
+
+// CommandObjectBreakpointRead
+#pragma mark Read::CommandOptions
+#define LLDB_OPTIONS_breakpoint_read
+#include "CommandOptions.inc"
+
+#pragma mark Read
+
+class CommandObjectBreakpointRead : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointRead(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "breakpoint read",
+ "Read and set the breakpoints previously saved to "
+ "a file with \"breakpoint write\". ",
+ nullptr) {}
+
+ ~CommandObjectBreakpointRead() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'f':
+ m_filename.assign(std::string(option_arg));
+ break;
+ case 'N': {
+ Status name_error;
+ if (!BreakpointID::StringIsBreakpointName(llvm::StringRef(option_arg),
+ name_error)) {
+ error.SetErrorStringWithFormat("Invalid breakpoint name: %s",
+ name_error.AsCString());
+ }
+ m_names.push_back(std::string(option_arg));
+ break;
+ }
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_filename.clear();
+ m_names.clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_breakpoint_read_options);
+ }
+
+ void HandleOptionArgumentCompletion(
+ CompletionRequest &request, OptionElementVector &opt_element_vector,
+ int opt_element_index, CommandInterpreter &interpreter) override {
+ int opt_arg_pos = opt_element_vector[opt_element_index].opt_arg_pos;
+ int opt_defs_index = opt_element_vector[opt_element_index].opt_defs_index;
+
+ switch (GetDefinitions()[opt_defs_index].short_option) {
+ case 'f':
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ interpreter, CommandCompletions::eDiskFileCompletion, request,
+ nullptr);
+ break;
+
+ case 'N':
+ llvm::Optional<FileSpec> file_spec;
+ const llvm::StringRef dash_f("-f");
+ for (int arg_idx = 0; arg_idx < opt_arg_pos; arg_idx++) {
+ if (dash_f == request.GetParsedLine().GetArgumentAtIndex(arg_idx)) {
+ file_spec.emplace(
+ request.GetParsedLine().GetArgumentAtIndex(arg_idx + 1));
+ break;
+ }
+ }
+ if (!file_spec)
+ return;
+
+ FileSystem::Instance().Resolve(*file_spec);
+ Status error;
+ StructuredData::ObjectSP input_data_sp =
+ StructuredData::ParseJSONFromFile(*file_spec, error);
+ if (!error.Success())
+ return;
+
+ StructuredData::Array *bkpt_array = input_data_sp->GetAsArray();
+ if (!bkpt_array)
+ return;
+
+ const size_t num_bkpts = bkpt_array->GetSize();
+ for (size_t i = 0; i < num_bkpts; i++) {
+ StructuredData::ObjectSP bkpt_object_sp =
+ bkpt_array->GetItemAtIndex(i);
+ if (!bkpt_object_sp)
+ return;
+
+ StructuredData::Dictionary *bkpt_dict =
+ bkpt_object_sp->GetAsDictionary();
+ if (!bkpt_dict)
+ return;
+
+ StructuredData::ObjectSP bkpt_data_sp =
+ bkpt_dict->GetValueForKey(Breakpoint::GetSerializationKey());
+ if (!bkpt_data_sp)
+ return;
+
+ bkpt_dict = bkpt_data_sp->GetAsDictionary();
+ if (!bkpt_dict)
+ return;
+
+ StructuredData::Array *names_array;
+
+ if (!bkpt_dict->GetValueForKeyAsArray("Names", names_array))
+ return;
+
+ size_t num_names = names_array->GetSize();
+
+ for (size_t i = 0; i < num_names; i++) {
+ llvm::StringRef name;
+ if (names_array->GetItemAtIndexAsString(i, name))
+ request.TryCompleteCurrentArg(name);
+ }
+ }
+ }
+ }
+
+ std::string m_filename;
+ std::vector<std::string> m_names;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target &target = GetSelectedOrDummyTarget();
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target.GetBreakpointList().GetListMutex(lock);
+
+ FileSpec input_spec(m_options.m_filename);
+ FileSystem::Instance().Resolve(input_spec);
+ BreakpointIDList new_bps;
+ Status error = target.CreateBreakpointsFromFile(input_spec,
+ m_options.m_names, new_bps);
+
+ if (!error.Success()) {
+ result.AppendError(error.AsCString());
+ return false;
+ }
+
+ Stream &output_stream = result.GetOutputStream();
+
+ size_t num_breakpoints = new_bps.GetSize();
+ if (num_breakpoints == 0) {
+ result.AppendMessage("No breakpoints added.");
+ } else {
+ // No breakpoint selected; show info about all currently set breakpoints.
+ result.AppendMessage("New breakpoints:");
+ for (size_t i = 0; i < num_breakpoints; ++i) {
+ BreakpointID bp_id = new_bps.GetBreakpointIDAtIndex(i);
+ Breakpoint *bp = target.GetBreakpointList()
+ .FindBreakpointByID(bp_id.GetBreakpointID())
+ .get();
+ if (bp)
+ bp->GetDescription(&output_stream, lldb::eDescriptionLevelInitial,
+ false);
+ }
+ }
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectBreakpointWrite
+#pragma mark Write::CommandOptions
+#define LLDB_OPTIONS_breakpoint_write
+#include "CommandOptions.inc"
+
+#pragma mark Write
+class CommandObjectBreakpointWrite : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointWrite(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "breakpoint write",
+ "Write the breakpoints listed to a file that can "
+ "be read in with \"breakpoint read\". "
+ "If given no arguments, writes all breakpoints.",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandObject::AddIDsArgumentData(arg, eArgTypeBreakpointID,
+ eArgTypeBreakpointIDRange);
+ // Add the entry for the first argument for this command to the object's
+ // arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectBreakpointWrite() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eBreakpointCompletion,
+ request, nullptr);
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'f':
+ m_filename.assign(std::string(option_arg));
+ break;
+ case 'a':
+ m_append = true;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_filename.clear();
+ m_append = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_breakpoint_write_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ std::string m_filename;
+ bool m_append = false;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target &target = GetSelectedOrDummyTarget();
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target.GetBreakpointList().GetListMutex(lock);
+
+ BreakpointIDList valid_bp_ids;
+ if (!command.empty()) {
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs(
+ command, &target, result, &valid_bp_ids,
+ BreakpointName::Permissions::PermissionKinds::listPerm);
+
+ if (!result.Succeeded()) {
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+ FileSpec file_spec(m_options.m_filename);
+ FileSystem::Instance().Resolve(file_spec);
+ Status error = target.SerializeBreakpointsToFile(file_spec, valid_bp_ids,
+ m_options.m_append);
+ if (!error.Success()) {
+ result.AppendErrorWithFormat("error serializing breakpoints: %s.",
+ error.AsCString());
+ }
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectMultiwordBreakpoint
+#pragma mark MultiwordBreakpoint
+
+CommandObjectMultiwordBreakpoint::CommandObjectMultiwordBreakpoint(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "breakpoint",
+ "Commands for operating on breakpoints (see 'help b' for shorthand.)",
+ "breakpoint <subcommand> [<command-options>]") {
+ CommandObjectSP list_command_object(
+ new CommandObjectBreakpointList(interpreter));
+ CommandObjectSP enable_command_object(
+ new CommandObjectBreakpointEnable(interpreter));
+ CommandObjectSP disable_command_object(
+ new CommandObjectBreakpointDisable(interpreter));
+ CommandObjectSP clear_command_object(
+ new CommandObjectBreakpointClear(interpreter));
+ CommandObjectSP delete_command_object(
+ new CommandObjectBreakpointDelete(interpreter));
+ CommandObjectSP set_command_object(
+ new CommandObjectBreakpointSet(interpreter));
+ CommandObjectSP command_command_object(
+ new CommandObjectBreakpointCommand(interpreter));
+ CommandObjectSP modify_command_object(
+ new CommandObjectBreakpointModify(interpreter));
+ CommandObjectSP name_command_object(
+ new CommandObjectBreakpointName(interpreter));
+ CommandObjectSP write_command_object(
+ new CommandObjectBreakpointWrite(interpreter));
+ CommandObjectSP read_command_object(
+ new CommandObjectBreakpointRead(interpreter));
+
+ list_command_object->SetCommandName("breakpoint list");
+ enable_command_object->SetCommandName("breakpoint enable");
+ disable_command_object->SetCommandName("breakpoint disable");
+ clear_command_object->SetCommandName("breakpoint clear");
+ delete_command_object->SetCommandName("breakpoint delete");
+ set_command_object->SetCommandName("breakpoint set");
+ command_command_object->SetCommandName("breakpoint command");
+ modify_command_object->SetCommandName("breakpoint modify");
+ name_command_object->SetCommandName("breakpoint name");
+ write_command_object->SetCommandName("breakpoint write");
+ read_command_object->SetCommandName("breakpoint read");
+
+ LoadSubCommand("list", list_command_object);
+ LoadSubCommand("enable", enable_command_object);
+ LoadSubCommand("disable", disable_command_object);
+ LoadSubCommand("clear", clear_command_object);
+ LoadSubCommand("delete", delete_command_object);
+ LoadSubCommand("set", set_command_object);
+ LoadSubCommand("command", command_command_object);
+ LoadSubCommand("modify", modify_command_object);
+ LoadSubCommand("name", name_command_object);
+ LoadSubCommand("write", write_command_object);
+ LoadSubCommand("read", read_command_object);
+}
+
+CommandObjectMultiwordBreakpoint::~CommandObjectMultiwordBreakpoint() = default;
+
+void CommandObjectMultiwordBreakpoint::VerifyIDs(
+ Args &args, Target *target, bool allow_locations,
+ CommandReturnObject &result, BreakpointIDList *valid_ids,
+ BreakpointName::Permissions ::PermissionKinds purpose) {
+ // args can be strings representing 1). integers (for breakpoint ids)
+ // 2). the full breakpoint & location
+ // canonical representation
+ // 3). the word "to" or a hyphen,
+ // representing a range (in which case there
+ // had *better* be an entry both before &
+ // after of one of the first two types.
+ // 4). A breakpoint name
+ // If args is empty, we will use the last created breakpoint (if there is
+ // one.)
+
+ Args temp_args;
+
+ if (args.empty()) {
+ if (target->GetLastCreatedBreakpoint()) {
+ valid_ids->AddBreakpointID(BreakpointID(
+ target->GetLastCreatedBreakpoint()->GetID(), LLDB_INVALID_BREAK_ID));
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendError(
+ "No breakpoint specified and no last created breakpoint.");
+ }
+ return;
+ }
+
+ // Create a new Args variable to use; copy any non-breakpoint-id-ranges stuff
+ // directly from the old ARGS to the new TEMP_ARGS. Do not copy breakpoint
+ // id range strings over; instead generate a list of strings for all the
+ // breakpoint ids in the range, and shove all of those breakpoint id strings
+ // into TEMP_ARGS.
+
+ BreakpointIDList::FindAndReplaceIDRanges(args, target, allow_locations,
+ purpose, result, temp_args);
+
+ // NOW, convert the list of breakpoint id strings in TEMP_ARGS into an actual
+ // BreakpointIDList:
+
+ valid_ids->InsertStringArray(temp_args.GetArgumentArrayRef(), result);
+
+ // At this point, all of the breakpoint ids that the user passed in have
+ // been converted to breakpoint IDs and put into valid_ids.
+
+ if (result.Succeeded()) {
+ // Now that we've converted everything from args into a list of breakpoint
+ // ids, go through our tentative list of breakpoint id's and verify that
+ // they correspond to valid/currently set breakpoints.
+
+ const size_t count = valid_ids->GetSize();
+ for (size_t i = 0; i < count; ++i) {
+ BreakpointID cur_bp_id = valid_ids->GetBreakpointIDAtIndex(i);
+ Breakpoint *breakpoint =
+ target->GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
+ if (breakpoint != nullptr) {
+ const size_t num_locations = breakpoint->GetNumLocations();
+ if (static_cast<size_t>(cur_bp_id.GetLocationID()) > num_locations) {
+ StreamString id_str;
+ BreakpointID::GetCanonicalReference(
+ &id_str, cur_bp_id.GetBreakpointID(), cur_bp_id.GetLocationID());
+ i = valid_ids->GetSize() + 1;
+ result.AppendErrorWithFormat(
+ "'%s' is not a currently valid breakpoint/location id.\n",
+ id_str.GetData());
+ }
+ } else {
+ i = valid_ids->GetSize() + 1;
+ result.AppendErrorWithFormat(
+ "'%d' is not a currently valid breakpoint ID.\n",
+ cur_bp_id.GetBreakpointID());
+ }
+ }
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpoint.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpoint.h
new file mode 100644
index 000000000000..6625652b260b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpoint.h
@@ -0,0 +1,48 @@
+//===-- CommandObjectBreakpoint.h -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTBREAKPOINT_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTBREAKPOINT_H
+
+#include "lldb/Breakpoint/BreakpointName.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+// CommandObjectMultiwordBreakpoint
+
+class CommandObjectMultiwordBreakpoint : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordBreakpoint(CommandInterpreter &interpreter);
+
+ ~CommandObjectMultiwordBreakpoint() override;
+
+ static void VerifyBreakpointOrLocationIDs(
+ Args &args, Target *target, CommandReturnObject &result,
+ BreakpointIDList *valid_ids,
+ BreakpointName::Permissions ::PermissionKinds purpose) {
+ VerifyIDs(args, target, true, result, valid_ids, purpose);
+ }
+
+ static void
+ VerifyBreakpointIDs(Args &args, Target *target, CommandReturnObject &result,
+ BreakpointIDList *valid_ids,
+ BreakpointName::Permissions::PermissionKinds purpose) {
+ VerifyIDs(args, target, false, result, valid_ids, purpose);
+ }
+
+private:
+ static void VerifyIDs(Args &args, Target *target, bool allow_locations,
+ CommandReturnObject &result,
+ BreakpointIDList *valid_ids,
+ BreakpointName::Permissions::PermissionKinds purpose);
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTBREAKPOINT_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpointCommand.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpointCommand.cpp
new file mode 100644
index 000000000000..bf0cae51d733
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpointCommand.cpp
@@ -0,0 +1,724 @@
+//===-- CommandObjectBreakpointCommand.cpp --------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectBreakpointCommand.h"
+#include "CommandObjectBreakpoint.h"
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointIDList.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/IOHandler.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionGroupPythonClassWithDict.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// FIXME: "script-type" needs to have its contents determined dynamically, so
+// somebody can add a new scripting language to lldb and have it pickable here
+// without having to change this enumeration by hand and rebuild lldb proper.
+static constexpr OptionEnumValueElement g_script_option_enumeration[] = {
+ {
+ eScriptLanguageNone,
+ "command",
+ "Commands are in the lldb command interpreter language",
+ },
+ {
+ eScriptLanguagePython,
+ "python",
+ "Commands are in the Python language.",
+ },
+ {
+ eScriptLanguageLua,
+ "lua",
+ "Commands are in the Lua language.",
+ },
+ {
+ eScriptLanguageDefault,
+ "default-script",
+ "Commands are in the default scripting language.",
+ },
+};
+
+static constexpr OptionEnumValues ScriptOptionEnum() {
+ return OptionEnumValues(g_script_option_enumeration);
+}
+
+#define LLDB_OPTIONS_breakpoint_command_add
+#include "CommandOptions.inc"
+
+class CommandObjectBreakpointCommandAdd : public CommandObjectParsed,
+ public IOHandlerDelegateMultiline {
+public:
+ CommandObjectBreakpointCommandAdd(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "add",
+ "Add LLDB commands to a breakpoint, to be executed "
+ "whenever the breakpoint is hit. "
+ "The commands added to the breakpoint replace any "
+ "commands previously added to it."
+ " If no breakpoint is specified, adds the "
+ "commands to the last created breakpoint.",
+ nullptr),
+ IOHandlerDelegateMultiline("DONE",
+ IOHandlerDelegate::Completion::LLDBCommand),
+ m_func_options("breakpoint command", false, 'F') {
+ SetHelpLong(
+ R"(
+General information about entering breakpoint commands
+------------------------------------------------------
+
+)"
+ "This command will prompt for commands to be executed when the specified \
+breakpoint is hit. Each command is typed on its own line following the '> ' \
+prompt until 'DONE' is entered."
+ R"(
+
+)"
+ "Syntactic errors may not be detected when initially entered, and many \
+malformed commands can silently fail when executed. If your breakpoint commands \
+do not appear to be executing, double-check the command syntax."
+ R"(
+
+)"
+ "Note: You may enter any debugger command exactly as you would at the debugger \
+prompt. There is no limit to the number of commands supplied, but do NOT enter \
+more than one command per line."
+ R"(
+
+Special information about PYTHON breakpoint commands
+----------------------------------------------------
+
+)"
+ "You may enter either one or more lines of Python, including function \
+definitions or calls to functions that will have been imported by the time \
+the code executes. Single line breakpoint commands will be interpreted 'as is' \
+when the breakpoint is hit. Multiple lines of Python will be wrapped in a \
+generated function, and a call to the function will be attached to the breakpoint."
+ R"(
+
+This auto-generated function is passed in three arguments:
+
+ frame: an lldb.SBFrame object for the frame which hit breakpoint.
+
+ bp_loc: an lldb.SBBreakpointLocation object that represents the breakpoint location that was hit.
+
+ dict: the python session dictionary hit.
+
+)"
+ "When specifying a python function with the --python-function option, you need \
+to supply the function name prepended by the module name:"
+ R"(
+
+ --python-function myutils.breakpoint_callback
+
+The function itself must have either of the following prototypes:
+
+def breakpoint_callback(frame, bp_loc, internal_dict):
+ # Your code goes here
+
+or:
+
+def breakpoint_callback(frame, bp_loc, extra_args, internal_dict):
+ # Your code goes here
+
+)"
+ "The arguments are the same as the arguments passed to generated functions as \
+described above. In the second form, any -k and -v pairs provided to the command will \
+be packaged into a SBDictionary in an SBStructuredData and passed as the extra_args parameter. \
+\n\n\
+Note that the global variable 'lldb.frame' will NOT be updated when \
+this function is called, so be sure to use the 'frame' argument. The 'frame' argument \
+can get you to the thread via frame.GetThread(), the thread can get you to the \
+process via thread.GetProcess(), and the process can get you back to the target \
+via process.GetTarget()."
+ R"(
+
+)"
+ "Important Note: As Python code gets collected into functions, access to global \
+variables requires explicit scoping using the 'global' keyword. Be sure to use correct \
+Python syntax, including indentation, when entering Python breakpoint commands."
+ R"(
+
+Example Python one-line breakpoint command:
+
+(lldb) breakpoint command add -s python 1
+Enter your Python command(s). Type 'DONE' to end.
+def function (frame, bp_loc, internal_dict):
+ """frame: the lldb.SBFrame for the location at which you stopped
+ bp_loc: an lldb.SBBreakpointLocation for the breakpoint location information
+ internal_dict: an LLDB support object not to be used"""
+ print("Hit this breakpoint!")
+ DONE
+
+As a convenience, this also works for a short Python one-liner:
+
+(lldb) breakpoint command add -s python 1 -o 'import time; print(time.asctime())'
+(lldb) run
+Launching '.../a.out' (x86_64)
+(lldb) Fri Sep 10 12:17:45 2010
+Process 21778 Stopped
+* thread #1: tid = 0x2e03, 0x0000000100000de8 a.out`c + 7 at main.c:39, stop reason = breakpoint 1.1, queue = com.apple.main-thread
+ 36
+ 37 int c(int val)
+ 38 {
+ 39 -> return val + 3;
+ 40 }
+ 41
+ 42 int main (int argc, char const *argv[])
+
+Example multiple line Python breakpoint command:
+
+(lldb) breakpoint command add -s p 1
+Enter your Python command(s). Type 'DONE' to end.
+def function (frame, bp_loc, internal_dict):
+ """frame: the lldb.SBFrame for the location at which you stopped
+ bp_loc: an lldb.SBBreakpointLocation for the breakpoint location information
+ internal_dict: an LLDB support object not to be used"""
+ global bp_count
+ bp_count = bp_count + 1
+ print("Hit this breakpoint " + repr(bp_count) + " times!")
+ DONE
+
+)"
+ "In this case, since there is a reference to a global variable, \
+'bp_count', you will also need to make sure 'bp_count' exists and is \
+initialized:"
+ R"(
+
+(lldb) script
+>>> bp_count = 0
+>>> quit()
+
+)"
+ "Your Python code, however organized, can optionally return a value. \
+If the returned value is False, that tells LLDB not to stop at the breakpoint \
+to which the code is associated. Returning anything other than False, or even \
+returning None, or even omitting a return statement entirely, will cause \
+LLDB to stop."
+ R"(
+
+)"
+ "Final Note: A warning that no breakpoint command was generated when there \
+are no syntax errors may indicate that a function was declared but never called.");
+
+ m_all_options.Append(&m_options);
+ m_all_options.Append(&m_func_options, LLDB_OPT_SET_2 | LLDB_OPT_SET_3,
+ LLDB_OPT_SET_2);
+ m_all_options.Finalize();
+
+ CommandArgumentEntry arg;
+ CommandArgumentData bp_id_arg;
+
+ // Define the first (and only) variant of this arg.
+ bp_id_arg.arg_type = eArgTypeBreakpointID;
+ bp_id_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(bp_id_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectBreakpointCommandAdd() override = default;
+
+ Options *GetOptions() override { return &m_all_options; }
+
+ void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
+ StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
+ if (output_sp && interactive) {
+ output_sp->PutCString(g_reader_instructions);
+ output_sp->Flush();
+ }
+ }
+
+ void IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &line) override {
+ io_handler.SetIsDone(true);
+
+ std::vector<std::reference_wrapper<BreakpointOptions>> *bp_options_vec =
+ (std::vector<std::reference_wrapper<BreakpointOptions>> *)
+ io_handler.GetUserData();
+ for (BreakpointOptions &bp_options : *bp_options_vec) {
+ auto cmd_data = std::make_unique<BreakpointOptions::CommandData>();
+ cmd_data->user_source.SplitIntoLines(line.c_str(), line.size());
+ bp_options.SetCommandDataCallback(cmd_data);
+ }
+ }
+
+ void CollectDataForBreakpointCommandCallback(
+ std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
+ CommandReturnObject &result) {
+ m_interpreter.GetLLDBCommandsFromIOHandler(
+ "> ", // Prompt
+ *this, // IOHandlerDelegate
+ &bp_options_vec); // Baton for the "io_handler" that will be passed back
+ // into our IOHandlerDelegate functions
+ }
+
+ /// Set a one-liner as the callback for the breakpoint.
+ void SetBreakpointCommandCallback(
+ std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
+ const char *oneliner) {
+ for (BreakpointOptions &bp_options : bp_options_vec) {
+ auto cmd_data = std::make_unique<BreakpointOptions::CommandData>();
+
+ cmd_data->user_source.AppendString(oneliner);
+ cmd_data->stop_on_error = m_options.m_stop_on_error;
+
+ bp_options.SetCommandDataCallback(cmd_data);
+ }
+ }
+
+ class CommandOptions : public OptionGroup {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option =
+ g_breakpoint_command_add_options[option_idx].short_option;
+
+ switch (short_option) {
+ case 'o':
+ m_use_one_liner = true;
+ m_one_liner = std::string(option_arg);
+ break;
+
+ case 's':
+ m_script_language = (lldb::ScriptLanguage)OptionArgParser::ToOptionEnum(
+ option_arg,
+ g_breakpoint_command_add_options[option_idx].enum_values,
+ eScriptLanguageNone, error);
+ switch (m_script_language) {
+ case eScriptLanguagePython:
+ case eScriptLanguageLua:
+ m_use_script_language = true;
+ break;
+ case eScriptLanguageNone:
+ case eScriptLanguageUnknown:
+ m_use_script_language = false;
+ break;
+ }
+ break;
+
+ case 'e': {
+ bool success = false;
+ m_stop_on_error =
+ OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "invalid value for stop-on-error: \"%s\"",
+ option_arg.str().c_str());
+ } break;
+
+ case 'D':
+ m_use_dummy = true;
+ break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_use_commands = true;
+ m_use_script_language = false;
+ m_script_language = eScriptLanguageNone;
+
+ m_use_one_liner = false;
+ m_stop_on_error = true;
+ m_one_liner.clear();
+ m_use_dummy = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_breakpoint_command_add_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ bool m_use_commands = false;
+ bool m_use_script_language = false;
+ lldb::ScriptLanguage m_script_language = eScriptLanguageNone;
+
+ // Instance variables to hold the values for one_liner options.
+ bool m_use_one_liner = false;
+ std::string m_one_liner;
+ bool m_stop_on_error;
+ bool m_use_dummy;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target &target = GetSelectedOrDummyTarget(m_options.m_use_dummy);
+
+ const BreakpointList &breakpoints = target.GetBreakpointList();
+ size_t num_breakpoints = breakpoints.GetSize();
+
+ if (num_breakpoints == 0) {
+ result.AppendError("No breakpoints exist to have commands added");
+ return false;
+ }
+
+ if (!m_func_options.GetName().empty()) {
+ m_options.m_use_one_liner = false;
+ if (!m_options.m_use_script_language) {
+ m_options.m_script_language = GetDebugger().GetScriptLanguage();
+ m_options.m_use_script_language = true;
+ }
+ }
+
+ BreakpointIDList valid_bp_ids;
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
+ command, &target, result, &valid_bp_ids,
+ BreakpointName::Permissions::PermissionKinds::listPerm);
+
+ m_bp_options_vec.clear();
+
+ if (result.Succeeded()) {
+ const size_t count = valid_bp_ids.GetSize();
+
+ for (size_t i = 0; i < count; ++i) {
+ BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
+ if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
+ Breakpoint *bp =
+ target.GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
+ if (cur_bp_id.GetLocationID() == LLDB_INVALID_BREAK_ID) {
+ // This breakpoint does not have an associated location.
+ m_bp_options_vec.push_back(bp->GetOptions());
+ } else {
+ BreakpointLocationSP bp_loc_sp(
+ bp->FindLocationByID(cur_bp_id.GetLocationID()));
+ // This breakpoint does have an associated location. Get its
+ // breakpoint options.
+ if (bp_loc_sp)
+ m_bp_options_vec.push_back(bp_loc_sp->GetLocationOptions());
+ }
+ }
+ }
+
+ // If we are using script language, get the script interpreter in order
+ // to set or collect command callback. Otherwise, call the methods
+ // associated with this object.
+ if (m_options.m_use_script_language) {
+ Status error;
+ ScriptInterpreter *script_interp = GetDebugger().GetScriptInterpreter(
+ /*can_create=*/true, m_options.m_script_language);
+ // Special handling for one-liner specified inline.
+ if (m_options.m_use_one_liner) {
+ error = script_interp->SetBreakpointCommandCallback(
+ m_bp_options_vec, m_options.m_one_liner.c_str());
+ } else if (!m_func_options.GetName().empty()) {
+ error = script_interp->SetBreakpointCommandCallbackFunction(
+ m_bp_options_vec, m_func_options.GetName().c_str(),
+ m_func_options.GetStructuredData());
+ } else {
+ script_interp->CollectDataForBreakpointCommandCallback(
+ m_bp_options_vec, result);
+ }
+ if (!error.Success())
+ result.SetError(error);
+ } else {
+ // Special handling for one-liner specified inline.
+ if (m_options.m_use_one_liner)
+ SetBreakpointCommandCallback(m_bp_options_vec,
+ m_options.m_one_liner.c_str());
+ else
+ CollectDataForBreakpointCommandCallback(m_bp_options_vec, result);
+ }
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+ OptionGroupPythonClassWithDict m_func_options;
+ OptionGroupOptions m_all_options;
+
+ std::vector<std::reference_wrapper<BreakpointOptions>>
+ m_bp_options_vec; // This stores the
+ // breakpoint options that
+ // we are currently
+ // collecting commands for. In the CollectData... calls we need to hand this
+ // off to the IOHandler, which may run asynchronously. So we have to have
+ // some way to keep it alive, and not leak it. Making it an ivar of the
+ // command object, which never goes away achieves this. Note that if we were
+ // able to run the same command concurrently in one interpreter we'd have to
+ // make this "per invocation". But there are many more reasons why it is not
+ // in general safe to do that in lldb at present, so it isn't worthwhile to
+ // come up with a more complex mechanism to address this particular weakness
+ // right now.
+ static const char *g_reader_instructions;
+};
+
+const char *CommandObjectBreakpointCommandAdd::g_reader_instructions =
+ "Enter your debugger command(s). Type 'DONE' to end.\n";
+
+// CommandObjectBreakpointCommandDelete
+
+#define LLDB_OPTIONS_breakpoint_command_delete
+#include "CommandOptions.inc"
+
+class CommandObjectBreakpointCommandDelete : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointCommandDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "delete",
+ "Delete the set of commands from a breakpoint.",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandArgumentData bp_id_arg;
+
+ // Define the first (and only) variant of this arg.
+ bp_id_arg.arg_type = eArgTypeBreakpointID;
+ bp_id_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(bp_id_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectBreakpointCommandDelete() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'D':
+ m_use_dummy = true;
+ break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_use_dummy = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_breakpoint_command_delete_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ bool m_use_dummy = false;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target &target = GetSelectedOrDummyTarget(m_options.m_use_dummy);
+
+ const BreakpointList &breakpoints = target.GetBreakpointList();
+ size_t num_breakpoints = breakpoints.GetSize();
+
+ if (num_breakpoints == 0) {
+ result.AppendError("No breakpoints exist to have commands deleted");
+ return false;
+ }
+
+ if (command.empty()) {
+ result.AppendError(
+ "No breakpoint specified from which to delete the commands");
+ return false;
+ }
+
+ BreakpointIDList valid_bp_ids;
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
+ command, &target, result, &valid_bp_ids,
+ BreakpointName::Permissions::PermissionKinds::listPerm);
+
+ if (result.Succeeded()) {
+ const size_t count = valid_bp_ids.GetSize();
+ for (size_t i = 0; i < count; ++i) {
+ BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
+ if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
+ Breakpoint *bp =
+ target.GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
+ if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) {
+ BreakpointLocationSP bp_loc_sp(
+ bp->FindLocationByID(cur_bp_id.GetLocationID()));
+ if (bp_loc_sp)
+ bp_loc_sp->ClearCallback();
+ else {
+ result.AppendErrorWithFormat("Invalid breakpoint ID: %u.%u.\n",
+ cur_bp_id.GetBreakpointID(),
+ cur_bp_id.GetLocationID());
+ return false;
+ }
+ } else {
+ bp->ClearCallback();
+ }
+ }
+ }
+ }
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectBreakpointCommandList
+
+class CommandObjectBreakpointCommandList : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointCommandList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "list",
+ "List the script or set of commands to be "
+ "executed when the breakpoint is hit.",
+ nullptr, eCommandRequiresTarget) {
+ CommandArgumentEntry arg;
+ CommandArgumentData bp_id_arg;
+
+ // Define the first (and only) variant of this arg.
+ bp_id_arg.arg_type = eArgTypeBreakpointID;
+ bp_id_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(bp_id_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectBreakpointCommandList() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+
+ const BreakpointList &breakpoints = target->GetBreakpointList();
+ size_t num_breakpoints = breakpoints.GetSize();
+
+ if (num_breakpoints == 0) {
+ result.AppendError("No breakpoints exist for which to list commands");
+ return false;
+ }
+
+ if (command.empty()) {
+ result.AppendError(
+ "No breakpoint specified for which to list the commands");
+ return false;
+ }
+
+ BreakpointIDList valid_bp_ids;
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
+ command, target, result, &valid_bp_ids,
+ BreakpointName::Permissions::PermissionKinds::listPerm);
+
+ if (result.Succeeded()) {
+ const size_t count = valid_bp_ids.GetSize();
+ for (size_t i = 0; i < count; ++i) {
+ BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
+ if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
+ Breakpoint *bp =
+ target->GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
+
+ if (bp) {
+ BreakpointLocationSP bp_loc_sp;
+ if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) {
+ bp_loc_sp = bp->FindLocationByID(cur_bp_id.GetLocationID());
+ if (!bp_loc_sp) {
+ result.AppendErrorWithFormat("Invalid breakpoint ID: %u.%u.\n",
+ cur_bp_id.GetBreakpointID(),
+ cur_bp_id.GetLocationID());
+ return false;
+ }
+ }
+
+ StreamString id_str;
+ BreakpointID::GetCanonicalReference(&id_str,
+ cur_bp_id.GetBreakpointID(),
+ cur_bp_id.GetLocationID());
+ const Baton *baton = nullptr;
+ if (bp_loc_sp)
+ baton =
+ bp_loc_sp
+ ->GetOptionsSpecifyingKind(BreakpointOptions::eCallback)
+ .GetBaton();
+ else
+ baton = bp->GetOptions().GetBaton();
+
+ if (baton) {
+ result.GetOutputStream().Printf("Breakpoint %s:\n",
+ id_str.GetData());
+ baton->GetDescription(result.GetOutputStream().AsRawOstream(),
+ eDescriptionLevelFull,
+ result.GetOutputStream().GetIndentLevel() +
+ 2);
+ } else {
+ result.AppendMessageWithFormat(
+ "Breakpoint %s does not have an associated command.\n",
+ id_str.GetData());
+ }
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("Invalid breakpoint ID: %u.\n",
+ cur_bp_id.GetBreakpointID());
+ }
+ }
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectBreakpointCommand
+
+CommandObjectBreakpointCommand::CommandObjectBreakpointCommand(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "command",
+ "Commands for adding, removing and listing "
+ "LLDB commands executed when a breakpoint is "
+ "hit.",
+ "command <sub-command> [<sub-command-options>] <breakpoint-id>") {
+ CommandObjectSP add_command_object(
+ new CommandObjectBreakpointCommandAdd(interpreter));
+ CommandObjectSP delete_command_object(
+ new CommandObjectBreakpointCommandDelete(interpreter));
+ CommandObjectSP list_command_object(
+ new CommandObjectBreakpointCommandList(interpreter));
+
+ add_command_object->SetCommandName("breakpoint command add");
+ delete_command_object->SetCommandName("breakpoint command delete");
+ list_command_object->SetCommandName("breakpoint command list");
+
+ LoadSubCommand("add", add_command_object);
+ LoadSubCommand("delete", delete_command_object);
+ LoadSubCommand("list", list_command_object);
+}
+
+CommandObjectBreakpointCommand::~CommandObjectBreakpointCommand() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpointCommand.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpointCommand.h
new file mode 100644
index 000000000000..cb516d76ea37
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpointCommand.h
@@ -0,0 +1,27 @@
+//===-- CommandObjectBreakpointCommand.h ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTBREAKPOINTCOMMAND_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTBREAKPOINTCOMMAND_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+// CommandObjectMultiwordBreakpoint
+
+class CommandObjectBreakpointCommand : public CommandObjectMultiword {
+public:
+ CommandObjectBreakpointCommand(CommandInterpreter &interpreter);
+
+ ~CommandObjectBreakpointCommand() override;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTBREAKPOINTCOMMAND_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectCommands.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectCommands.cpp
new file mode 100644
index 000000000000..39c7207bbdbe
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectCommands.cpp
@@ -0,0 +1,2134 @@
+//===-- CommandObjectCommands.cpp -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectCommands.h"
+#include "CommandObjectHelp.h"
+#include "CommandObjectRegexCommand.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/IOHandler.h"
+#include "lldb/Interpreter/CommandHistory.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionValueBoolean.h"
+#include "lldb/Interpreter/OptionValueString.h"
+#include "lldb/Interpreter/OptionValueUInt64.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/StringList.h"
+#include "llvm/ADT/StringRef.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// CommandObjectCommandsSource
+
+#define LLDB_OPTIONS_source
+#include "CommandOptions.inc"
+
+class CommandObjectCommandsSource : public CommandObjectParsed {
+public:
+ CommandObjectCommandsSource(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "command source",
+ "Read and execute LLDB commands from the file <filename>.",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandArgumentData file_arg;
+
+ // Define the first (and only) variant of this arg.
+ file_arg.arg_type = eArgTypeFilename;
+ file_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(file_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectCommandsSource() override = default;
+
+ llvm::Optional<std::string> GetRepeatCommand(Args &current_command_args,
+ uint32_t index) override {
+ return std::string("");
+ }
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+ request, nullptr);
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions()
+ : m_stop_on_error(true), m_silent_run(false), m_stop_on_continue(true),
+ m_cmd_relative_to_command_file(false) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'e':
+ error = m_stop_on_error.SetValueFromString(option_arg);
+ break;
+
+ case 'c':
+ error = m_stop_on_continue.SetValueFromString(option_arg);
+ break;
+
+ case 'C':
+ m_cmd_relative_to_command_file = true;
+ break;
+
+ case 's':
+ error = m_silent_run.SetValueFromString(option_arg);
+ break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_stop_on_error.Clear();
+ m_silent_run.Clear();
+ m_stop_on_continue.Clear();
+ m_cmd_relative_to_command_file.Clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_source_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ OptionValueBoolean m_stop_on_error;
+ OptionValueBoolean m_silent_run;
+ OptionValueBoolean m_stop_on_continue;
+ OptionValueBoolean m_cmd_relative_to_command_file;
+ };
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (command.GetArgumentCount() != 1) {
+ result.AppendErrorWithFormat(
+ "'%s' takes exactly one executable filename argument.\n",
+ GetCommandName().str().c_str());
+ return false;
+ }
+
+ FileSpec source_dir = {};
+ if (m_options.m_cmd_relative_to_command_file) {
+ source_dir = GetDebugger().GetCommandInterpreter().GetCurrentSourceDir();
+ if (!source_dir) {
+ result.AppendError("command source -C can only be specified "
+ "from a command file");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ FileSpec cmd_file(command[0].ref());
+ if (source_dir) {
+ // Prepend the source_dir to the cmd_file path:
+ if (!cmd_file.IsRelative()) {
+ result.AppendError("command source -C can only be used "
+ "with a relative path.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ cmd_file.MakeAbsolute(source_dir);
+ }
+
+ FileSystem::Instance().Resolve(cmd_file);
+
+ CommandInterpreterRunOptions options;
+ // If any options were set, then use them
+ if (m_options.m_stop_on_error.OptionWasSet() ||
+ m_options.m_silent_run.OptionWasSet() ||
+ m_options.m_stop_on_continue.OptionWasSet()) {
+ if (m_options.m_stop_on_continue.OptionWasSet())
+ options.SetStopOnContinue(
+ m_options.m_stop_on_continue.GetCurrentValue());
+
+ if (m_options.m_stop_on_error.OptionWasSet())
+ options.SetStopOnError(m_options.m_stop_on_error.GetCurrentValue());
+
+ // Individual silent setting is override for global command echo settings.
+ if (m_options.m_silent_run.GetCurrentValue()) {
+ options.SetSilent(true);
+ } else {
+ options.SetPrintResults(true);
+ options.SetPrintErrors(true);
+ options.SetEchoCommands(m_interpreter.GetEchoCommands());
+ options.SetEchoCommentCommands(m_interpreter.GetEchoCommentCommands());
+ }
+ }
+
+ m_interpreter.HandleCommandsFromFile(cmd_file, options, result);
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+#pragma mark CommandObjectCommandsAlias
+// CommandObjectCommandsAlias
+
+#define LLDB_OPTIONS_alias
+#include "CommandOptions.inc"
+
+static const char *g_python_command_instructions =
+ "Enter your Python command(s). Type 'DONE' to end.\n"
+ "You must define a Python function with this signature:\n"
+ "def my_command_impl(debugger, args, result, internal_dict):\n";
+
+class CommandObjectCommandsAlias : public CommandObjectRaw {
+protected:
+ class CommandOptions : public OptionGroup {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_alias_options);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
+ ExecutionContext *execution_context) override {
+ Status error;
+
+ const int short_option = GetDefinitions()[option_idx].short_option;
+ std::string option_str(option_value);
+
+ switch (short_option) {
+ case 'h':
+ m_help.SetCurrentValue(option_str);
+ m_help.SetOptionWasSet();
+ break;
+
+ case 'H':
+ m_long_help.SetCurrentValue(option_str);
+ m_long_help.SetOptionWasSet();
+ break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_help.Clear();
+ m_long_help.Clear();
+ }
+
+ OptionValueString m_help;
+ OptionValueString m_long_help;
+ };
+
+ OptionGroupOptions m_option_group;
+ CommandOptions m_command_options;
+
+public:
+ Options *GetOptions() override { return &m_option_group; }
+
+ CommandObjectCommandsAlias(CommandInterpreter &interpreter)
+ : CommandObjectRaw(
+ interpreter, "command alias",
+ "Define a custom command in terms of an existing command.") {
+ m_option_group.Append(&m_command_options);
+ m_option_group.Finalize();
+
+ SetHelpLong(
+ "'alias' allows the user to create a short-cut or abbreviation for long \
+commands, multi-word commands, and commands that take particular options. \
+Below are some simple examples of how one might use the 'alias' command:"
+ R"(
+
+(lldb) command alias sc script
+
+ Creates the abbreviation 'sc' for the 'script' command.
+
+(lldb) command alias bp breakpoint
+
+)"
+ " Creates the abbreviation 'bp' for the 'breakpoint' command. Since \
+breakpoint commands are two-word commands, the user would still need to \
+enter the second word after 'bp', e.g. 'bp enable' or 'bp delete'."
+ R"(
+
+(lldb) command alias bpl breakpoint list
+
+ Creates the abbreviation 'bpl' for the two-word command 'breakpoint list'.
+
+)"
+ "An alias can include some options for the command, with the values either \
+filled in at the time the alias is created, or specified as positional \
+arguments, to be filled in when the alias is invoked. The following example \
+shows how to create aliases with options:"
+ R"(
+
+(lldb) command alias bfl breakpoint set -f %1 -l %2
+
+)"
+ " Creates the abbreviation 'bfl' (for break-file-line), with the -f and -l \
+options already part of the alias. So if the user wants to set a breakpoint \
+by file and line without explicitly having to use the -f and -l options, the \
+user can now use 'bfl' instead. The '%1' and '%2' are positional placeholders \
+for the actual arguments that will be passed when the alias command is used. \
+The number in the placeholder refers to the position/order the actual value \
+occupies when the alias is used. All the occurrences of '%1' in the alias \
+will be replaced with the first argument, all the occurrences of '%2' in the \
+alias will be replaced with the second argument, and so on. This also allows \
+actual arguments to be used multiple times within an alias (see 'process \
+launch' example below)."
+ R"(
+
+)"
+ "Note: the positional arguments must substitute as whole words in the resultant \
+command, so you can't at present do something like this to append the file extension \
+\".cpp\":"
+ R"(
+
+(lldb) command alias bcppfl breakpoint set -f %1.cpp -l %2
+
+)"
+ "For more complex aliasing, use the \"command regex\" command instead. In the \
+'bfl' case above, the actual file value will be filled in with the first argument \
+following 'bfl' and the actual line number value will be filled in with the second \
+argument. The user would use this alias as follows:"
+ R"(
+
+(lldb) command alias bfl breakpoint set -f %1 -l %2
+(lldb) bfl my-file.c 137
+
+This would be the same as if the user had entered 'breakpoint set -f my-file.c -l 137'.
+
+Another example:
+
+(lldb) command alias pltty process launch -s -o %1 -e %1
+(lldb) pltty /dev/tty0
+
+ Interpreted as 'process launch -s -o /dev/tty0 -e /dev/tty0'
+
+)"
+ "If the user always wanted to pass the same value to a particular option, the \
+alias could be defined with that value directly in the alias as a constant, \
+rather than using a positional placeholder:"
+ R"(
+
+(lldb) command alias bl3 breakpoint set -f %1 -l 3
+
+ Always sets a breakpoint on line 3 of whatever file is indicated.)");
+
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentEntry arg3;
+ CommandArgumentData alias_arg;
+ CommandArgumentData cmd_arg;
+ CommandArgumentData options_arg;
+
+ // Define the first (and only) variant of this arg.
+ alias_arg.arg_type = eArgTypeAliasName;
+ alias_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(alias_arg);
+
+ // Define the first (and only) variant of this arg.
+ cmd_arg.arg_type = eArgTypeCommandName;
+ cmd_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg2.push_back(cmd_arg);
+
+ // Define the first (and only) variant of this arg.
+ options_arg.arg_type = eArgTypeAliasOptions;
+ options_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg3.push_back(options_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+ m_arguments.push_back(arg3);
+ }
+
+ ~CommandObjectCommandsAlias() override = default;
+
+protected:
+ bool DoExecute(llvm::StringRef raw_command_line,
+ CommandReturnObject &result) override {
+ if (raw_command_line.empty()) {
+ result.AppendError("'command alias' requires at least two arguments");
+ return false;
+ }
+
+ ExecutionContext exe_ctx = GetCommandInterpreter().GetExecutionContext();
+ m_option_group.NotifyOptionParsingStarting(&exe_ctx);
+
+ OptionsWithRaw args_with_suffix(raw_command_line);
+
+ if (args_with_suffix.HasArgs())
+ if (!ParseOptionsAndNotify(args_with_suffix.GetArgs(), result,
+ m_option_group, exe_ctx))
+ return false;
+
+ llvm::StringRef raw_command_string = args_with_suffix.GetRawPart();
+ Args args(raw_command_string);
+
+ if (args.GetArgumentCount() < 2) {
+ result.AppendError("'command alias' requires at least two arguments");
+ return false;
+ }
+
+ // Get the alias command.
+
+ auto alias_command = args[0].ref();
+ if (alias_command.startswith("-")) {
+ result.AppendError("aliases starting with a dash are not supported");
+ if (alias_command == "--help" || alias_command == "--long-help") {
+ result.AppendWarning("if trying to pass options to 'command alias' add "
+ "a -- at the end of the options");
+ }
+ return false;
+ }
+
+ // Strip the new alias name off 'raw_command_string' (leave it on args,
+ // which gets passed to 'Execute', which does the stripping itself.
+ size_t pos = raw_command_string.find(alias_command);
+ if (pos == 0) {
+ raw_command_string = raw_command_string.substr(alias_command.size());
+ pos = raw_command_string.find_first_not_of(' ');
+ if ((pos != std::string::npos) && (pos > 0))
+ raw_command_string = raw_command_string.substr(pos);
+ } else {
+ result.AppendError("Error parsing command string. No alias created.");
+ return false;
+ }
+
+ // Verify that the command is alias-able.
+ if (m_interpreter.CommandExists(alias_command)) {
+ result.AppendErrorWithFormat(
+ "'%s' is a permanent debugger command and cannot be redefined.\n",
+ args[0].c_str());
+ return false;
+ }
+
+ if (m_interpreter.UserMultiwordCommandExists(alias_command)) {
+ result.AppendErrorWithFormat(
+ "'%s' is a user container command and cannot be overwritten.\n"
+ "Delete it first with 'command container delete'\n",
+ args[0].c_str());
+ return false;
+ }
+
+ // Get CommandObject that is being aliased. The command name is read from
+ // the front of raw_command_string. raw_command_string is returned with the
+ // name of the command object stripped off the front.
+ llvm::StringRef original_raw_command_string = raw_command_string;
+ CommandObject *cmd_obj =
+ m_interpreter.GetCommandObjectForCommand(raw_command_string);
+
+ if (!cmd_obj) {
+ result.AppendErrorWithFormat("invalid command given to 'command alias'. "
+ "'%s' does not begin with a valid command."
+ " No alias created.",
+ original_raw_command_string.str().c_str());
+ return false;
+ } else if (!cmd_obj->WantsRawCommandString()) {
+ // Note that args was initialized with the original command, and has not
+ // been updated to this point. Therefore can we pass it to the version of
+ // Execute that does not need/expect raw input in the alias.
+ return HandleAliasingNormalCommand(args, result);
+ } else {
+ return HandleAliasingRawCommand(alias_command, raw_command_string,
+ *cmd_obj, result);
+ }
+ return result.Succeeded();
+ }
+
+ bool HandleAliasingRawCommand(llvm::StringRef alias_command,
+ llvm::StringRef raw_command_string,
+ CommandObject &cmd_obj,
+ CommandReturnObject &result) {
+ // Verify & handle any options/arguments passed to the alias command
+
+ OptionArgVectorSP option_arg_vector_sp =
+ OptionArgVectorSP(new OptionArgVector);
+
+ const bool include_aliases = true;
+ if (CommandObjectSP cmd_obj_sp = m_interpreter.GetCommandSPExact(
+ cmd_obj.GetCommandName(), include_aliases)) {
+ if (m_interpreter.AliasExists(alias_command) ||
+ m_interpreter.UserCommandExists(alias_command)) {
+ result.AppendWarningWithFormat(
+ "Overwriting existing definition for '%s'.\n",
+ alias_command.str().c_str());
+ }
+ if (CommandAlias *alias = m_interpreter.AddAlias(
+ alias_command, cmd_obj_sp, raw_command_string)) {
+ if (m_command_options.m_help.OptionWasSet())
+ alias->SetHelp(m_command_options.m_help.GetCurrentValue());
+ if (m_command_options.m_long_help.OptionWasSet())
+ alias->SetHelpLong(m_command_options.m_long_help.GetCurrentValue());
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendError("Unable to create requested alias.\n");
+ }
+
+ } else {
+ result.AppendError("Unable to create requested alias.\n");
+ }
+
+ return result.Succeeded();
+ }
+
+ bool HandleAliasingNormalCommand(Args &args, CommandReturnObject &result) {
+ size_t argc = args.GetArgumentCount();
+
+ if (argc < 2) {
+ result.AppendError("'command alias' requires at least two arguments");
+ return false;
+ }
+
+ // Save these in std::strings since we're going to shift them off.
+ const std::string alias_command(std::string(args[0].ref()));
+ const std::string actual_command(std::string(args[1].ref()));
+
+ args.Shift(); // Shift the alias command word off the argument vector.
+ args.Shift(); // Shift the old command word off the argument vector.
+
+ // Verify that the command is alias'able, and get the appropriate command
+ // object.
+
+ if (m_interpreter.CommandExists(alias_command)) {
+ result.AppendErrorWithFormat(
+ "'%s' is a permanent debugger command and cannot be redefined.\n",
+ alias_command.c_str());
+ return false;
+ }
+
+ if (m_interpreter.UserMultiwordCommandExists(alias_command)) {
+ result.AppendErrorWithFormat(
+ "'%s' is user container command and cannot be overwritten.\n"
+ "Delete it first with 'command container delete'",
+ alias_command.c_str());
+ return false;
+ }
+
+ CommandObjectSP command_obj_sp(
+ m_interpreter.GetCommandSPExact(actual_command, true));
+ CommandObjectSP subcommand_obj_sp;
+ bool use_subcommand = false;
+ if (!command_obj_sp) {
+ result.AppendErrorWithFormat("'%s' is not an existing command.\n",
+ actual_command.c_str());
+ return false;
+ }
+ CommandObject *cmd_obj = command_obj_sp.get();
+ CommandObject *sub_cmd_obj = nullptr;
+ OptionArgVectorSP option_arg_vector_sp =
+ OptionArgVectorSP(new OptionArgVector);
+
+ while (cmd_obj->IsMultiwordObject() && !args.empty()) {
+ auto sub_command = args[0].ref();
+ assert(!sub_command.empty());
+ subcommand_obj_sp = cmd_obj->GetSubcommandSP(sub_command);
+ if (!subcommand_obj_sp) {
+ result.AppendErrorWithFormat(
+ "'%s' is not a valid sub-command of '%s'. "
+ "Unable to create alias.\n",
+ args[0].c_str(), actual_command.c_str());
+ return false;
+ }
+
+ sub_cmd_obj = subcommand_obj_sp.get();
+ use_subcommand = true;
+ args.Shift(); // Shift the sub_command word off the argument vector.
+ cmd_obj = sub_cmd_obj;
+ }
+
+ // Verify & handle any options/arguments passed to the alias command
+
+ std::string args_string;
+
+ if (!args.empty()) {
+ CommandObjectSP tmp_sp =
+ m_interpreter.GetCommandSPExact(cmd_obj->GetCommandName());
+ if (use_subcommand)
+ tmp_sp = m_interpreter.GetCommandSPExact(sub_cmd_obj->GetCommandName());
+
+ args.GetCommandString(args_string);
+ }
+
+ if (m_interpreter.AliasExists(alias_command) ||
+ m_interpreter.UserCommandExists(alias_command)) {
+ result.AppendWarningWithFormat(
+ "Overwriting existing definition for '%s'.\n", alias_command.c_str());
+ }
+
+ if (CommandAlias *alias = m_interpreter.AddAlias(
+ alias_command, use_subcommand ? subcommand_obj_sp : command_obj_sp,
+ args_string)) {
+ if (m_command_options.m_help.OptionWasSet())
+ alias->SetHelp(m_command_options.m_help.GetCurrentValue());
+ if (m_command_options.m_long_help.OptionWasSet())
+ alias->SetHelpLong(m_command_options.m_long_help.GetCurrentValue());
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendError("Unable to create requested alias.\n");
+ return false;
+ }
+
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectCommandsUnalias
+// CommandObjectCommandsUnalias
+
+class CommandObjectCommandsUnalias : public CommandObjectParsed {
+public:
+ CommandObjectCommandsUnalias(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "command unalias",
+ "Delete one or more custom commands defined by 'command alias'.",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandArgumentData alias_arg;
+
+ // Define the first (and only) variant of this arg.
+ alias_arg.arg_type = eArgTypeAliasName;
+ alias_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(alias_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectCommandsUnalias() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (!m_interpreter.HasCommands() || request.GetCursorIndex() != 0)
+ return;
+
+ for (const auto &ent : m_interpreter.GetAliases()) {
+ request.TryCompleteCurrentArg(ent.first, ent.second->GetHelp());
+ }
+ }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ CommandObject::CommandMap::iterator pos;
+ CommandObject *cmd_obj;
+
+ if (args.empty()) {
+ result.AppendError("must call 'unalias' with a valid alias");
+ return false;
+ }
+
+ auto command_name = args[0].ref();
+ cmd_obj = m_interpreter.GetCommandObject(command_name);
+ if (!cmd_obj) {
+ result.AppendErrorWithFormat(
+ "'%s' is not a known command.\nTry 'help' to see a "
+ "current list of commands.\n",
+ args[0].c_str());
+ return false;
+ }
+
+ if (m_interpreter.CommandExists(command_name)) {
+ if (cmd_obj->IsRemovable()) {
+ result.AppendErrorWithFormat(
+ "'%s' is not an alias, it is a debugger command which can be "
+ "removed using the 'command delete' command.\n",
+ args[0].c_str());
+ } else {
+ result.AppendErrorWithFormat(
+ "'%s' is a permanent debugger command and cannot be removed.\n",
+ args[0].c_str());
+ }
+ return false;
+ }
+
+ if (!m_interpreter.RemoveAlias(command_name)) {
+ if (m_interpreter.AliasExists(command_name))
+ result.AppendErrorWithFormat(
+ "Error occurred while attempting to unalias '%s'.\n",
+ args[0].c_str());
+ else
+ result.AppendErrorWithFormat("'%s' is not an existing alias.\n",
+ args[0].c_str());
+ return false;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectCommandsDelete
+// CommandObjectCommandsDelete
+
+class CommandObjectCommandsDelete : public CommandObjectParsed {
+public:
+ CommandObjectCommandsDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "command delete",
+ "Delete one or more custom commands defined by 'command regex'.",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandArgumentData alias_arg;
+
+ // Define the first (and only) variant of this arg.
+ alias_arg.arg_type = eArgTypeCommandName;
+ alias_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(alias_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectCommandsDelete() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (!m_interpreter.HasCommands() || request.GetCursorIndex() != 0)
+ return;
+
+ for (const auto &ent : m_interpreter.GetCommands()) {
+ if (ent.second->IsRemovable())
+ request.TryCompleteCurrentArg(ent.first, ent.second->GetHelp());
+ }
+ }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ CommandObject::CommandMap::iterator pos;
+
+ if (args.empty()) {
+ result.AppendErrorWithFormat("must call '%s' with one or more valid user "
+ "defined regular expression command names",
+ GetCommandName().str().c_str());
+ return false;
+ }
+
+ auto command_name = args[0].ref();
+ if (!m_interpreter.CommandExists(command_name)) {
+ StreamString error_msg_stream;
+ const bool generate_upropos = true;
+ const bool generate_type_lookup = false;
+ CommandObjectHelp::GenerateAdditionalHelpAvenuesMessage(
+ &error_msg_stream, command_name, llvm::StringRef(), llvm::StringRef(),
+ generate_upropos, generate_type_lookup);
+ result.AppendError(error_msg_stream.GetString());
+ return false;
+ }
+
+ if (!m_interpreter.RemoveCommand(command_name)) {
+ result.AppendErrorWithFormat(
+ "'%s' is a permanent debugger command and cannot be removed.\n",
+ args[0].c_str());
+ return false;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+};
+
+// CommandObjectCommandsAddRegex
+
+#define LLDB_OPTIONS_regex
+#include "CommandOptions.inc"
+
+#pragma mark CommandObjectCommandsAddRegex
+
+class CommandObjectCommandsAddRegex : public CommandObjectParsed,
+ public IOHandlerDelegateMultiline {
+public:
+ CommandObjectCommandsAddRegex(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "command regex",
+ "Define a custom command in terms of "
+ "existing commands by matching "
+ "regular expressions.",
+ "command regex <cmd-name> [s/<regex>/<subst>/ ...]"),
+ IOHandlerDelegateMultiline("",
+ IOHandlerDelegate::Completion::LLDBCommand) {
+ SetHelpLong(
+ R"(
+)"
+ "This command allows the user to create powerful regular expression commands \
+with substitutions. The regular expressions and substitutions are specified \
+using the regular expression substitution format of:"
+ R"(
+
+ s/<regex>/<subst>/
+
+)"
+ "<regex> is a regular expression that can use parenthesis to capture regular \
+expression input and substitute the captured matches in the output using %1 \
+for the first match, %2 for the second, and so on."
+ R"(
+
+)"
+ "The regular expressions can all be specified on the command line if more than \
+one argument is provided. If just the command name is provided on the command \
+line, then the regular expressions and substitutions can be entered on separate \
+lines, followed by an empty line to terminate the command definition."
+ R"(
+
+EXAMPLES
+
+)"
+ "The following example will define a regular expression command named 'f' that \
+will call 'finish' if there are no arguments, or 'frame select <frame-idx>' if \
+a number follows 'f':"
+ R"(
+
+ (lldb) command regex f s/^$/finish/ 's/([0-9]+)/frame select %1/')");
+ CommandArgumentData thread_arg{eArgTypeSEDStylePair, eArgRepeatOptional};
+ m_arguments.push_back({thread_arg});
+ }
+
+ ~CommandObjectCommandsAddRegex() override = default;
+
+protected:
+ void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
+ StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
+ if (output_sp && interactive) {
+ output_sp->PutCString("Enter one or more sed substitution commands in "
+ "the form: 's/<regex>/<subst>/'.\nTerminate the "
+ "substitution list with an empty line.\n");
+ output_sp->Flush();
+ }
+ }
+
+ void IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &data) override {
+ io_handler.SetIsDone(true);
+ if (m_regex_cmd_up) {
+ StringList lines;
+ if (lines.SplitIntoLines(data)) {
+ bool check_only = false;
+ for (const std::string &line : lines) {
+ Status error = AppendRegexSubstitution(line, check_only);
+ if (error.Fail()) {
+ if (!GetDebugger().GetCommandInterpreter().GetBatchCommandMode()) {
+ StreamSP out_stream = GetDebugger().GetAsyncOutputStream();
+ out_stream->Printf("error: %s\n", error.AsCString());
+ }
+ }
+ }
+ }
+ if (m_regex_cmd_up->HasRegexEntries()) {
+ CommandObjectSP cmd_sp(m_regex_cmd_up.release());
+ m_interpreter.AddCommand(cmd_sp->GetCommandName(), cmd_sp, true);
+ }
+ }
+ }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ if (argc == 0) {
+ result.AppendError("usage: 'command regex <command-name> "
+ "[s/<regex1>/<subst1>/ s/<regex2>/<subst2>/ ...]'\n");
+ return false;
+ }
+
+ Status error;
+ auto name = command[0].ref();
+ m_regex_cmd_up = std::make_unique<CommandObjectRegexCommand>(
+ m_interpreter, name, m_options.GetHelp(), m_options.GetSyntax(), 10, 0,
+ true);
+
+ if (argc == 1) {
+ Debugger &debugger = GetDebugger();
+ bool color_prompt = debugger.GetUseColor();
+ const bool multiple_lines = true; // Get multiple lines
+ IOHandlerSP io_handler_sp(new IOHandlerEditline(
+ debugger, IOHandler::Type::Other,
+ "lldb-regex", // Name of input reader for history
+ llvm::StringRef("> "), // Prompt
+ llvm::StringRef(), // Continuation prompt
+ multiple_lines, color_prompt,
+ 0, // Don't show line numbers
+ *this, nullptr));
+
+ if (io_handler_sp) {
+ debugger.RunIOHandlerAsync(io_handler_sp);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+ } else {
+ for (auto &entry : command.entries().drop_front()) {
+ bool check_only = false;
+ error = AppendRegexSubstitution(entry.ref(), check_only);
+ if (error.Fail())
+ break;
+ }
+
+ if (error.Success()) {
+ AddRegexCommandToInterpreter();
+ }
+ }
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ }
+
+ return result.Succeeded();
+ }
+
+ Status AppendRegexSubstitution(const llvm::StringRef &regex_sed,
+ bool check_only) {
+ Status error;
+
+ if (!m_regex_cmd_up) {
+ error.SetErrorStringWithFormat(
+ "invalid regular expression command object for: '%.*s'",
+ (int)regex_sed.size(), regex_sed.data());
+ return error;
+ }
+
+ size_t regex_sed_size = regex_sed.size();
+
+ if (regex_sed_size <= 1) {
+ error.SetErrorStringWithFormat(
+ "regular expression substitution string is too short: '%.*s'",
+ (int)regex_sed.size(), regex_sed.data());
+ return error;
+ }
+
+ if (regex_sed[0] != 's') {
+ error.SetErrorStringWithFormat("regular expression substitution string "
+ "doesn't start with 's': '%.*s'",
+ (int)regex_sed.size(), regex_sed.data());
+ return error;
+ }
+ const size_t first_separator_char_pos = 1;
+ // use the char that follows 's' as the regex separator character so we can
+ // have "s/<regex>/<subst>/" or "s|<regex>|<subst>|"
+ const char separator_char = regex_sed[first_separator_char_pos];
+ const size_t second_separator_char_pos =
+ regex_sed.find(separator_char, first_separator_char_pos + 1);
+
+ if (second_separator_char_pos == std::string::npos) {
+ error.SetErrorStringWithFormat(
+ "missing second '%c' separator char after '%.*s' in '%.*s'",
+ separator_char,
+ (int)(regex_sed.size() - first_separator_char_pos - 1),
+ regex_sed.data() + (first_separator_char_pos + 1),
+ (int)regex_sed.size(), regex_sed.data());
+ return error;
+ }
+
+ const size_t third_separator_char_pos =
+ regex_sed.find(separator_char, second_separator_char_pos + 1);
+
+ if (third_separator_char_pos == std::string::npos) {
+ error.SetErrorStringWithFormat(
+ "missing third '%c' separator char after '%.*s' in '%.*s'",
+ separator_char,
+ (int)(regex_sed.size() - second_separator_char_pos - 1),
+ regex_sed.data() + (second_separator_char_pos + 1),
+ (int)regex_sed.size(), regex_sed.data());
+ return error;
+ }
+
+ if (third_separator_char_pos != regex_sed_size - 1) {
+ // Make sure that everything that follows the last regex separator char
+ if (regex_sed.find_first_not_of("\t\n\v\f\r ",
+ third_separator_char_pos + 1) !=
+ std::string::npos) {
+ error.SetErrorStringWithFormat(
+ "extra data found after the '%.*s' regular expression substitution "
+ "string: '%.*s'",
+ (int)third_separator_char_pos + 1, regex_sed.data(),
+ (int)(regex_sed.size() - third_separator_char_pos - 1),
+ regex_sed.data() + (third_separator_char_pos + 1));
+ return error;
+ }
+ } else if (first_separator_char_pos + 1 == second_separator_char_pos) {
+ error.SetErrorStringWithFormat(
+ "<regex> can't be empty in 's%c<regex>%c<subst>%c' string: '%.*s'",
+ separator_char, separator_char, separator_char, (int)regex_sed.size(),
+ regex_sed.data());
+ return error;
+ } else if (second_separator_char_pos + 1 == third_separator_char_pos) {
+ error.SetErrorStringWithFormat(
+ "<subst> can't be empty in 's%c<regex>%c<subst>%c' string: '%.*s'",
+ separator_char, separator_char, separator_char, (int)regex_sed.size(),
+ regex_sed.data());
+ return error;
+ }
+
+ if (!check_only) {
+ std::string regex(std::string(regex_sed.substr(
+ first_separator_char_pos + 1,
+ second_separator_char_pos - first_separator_char_pos - 1)));
+ std::string subst(std::string(regex_sed.substr(
+ second_separator_char_pos + 1,
+ third_separator_char_pos - second_separator_char_pos - 1)));
+ m_regex_cmd_up->AddRegexCommand(regex, subst);
+ }
+ return error;
+ }
+
+ void AddRegexCommandToInterpreter() {
+ if (m_regex_cmd_up) {
+ if (m_regex_cmd_up->HasRegexEntries()) {
+ CommandObjectSP cmd_sp(m_regex_cmd_up.release());
+ m_interpreter.AddCommand(cmd_sp->GetCommandName(), cmd_sp, true);
+ }
+ }
+ }
+
+private:
+ std::unique_ptr<CommandObjectRegexCommand> m_regex_cmd_up;
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'h':
+ m_help.assign(std::string(option_arg));
+ break;
+ case 's':
+ m_syntax.assign(std::string(option_arg));
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_help.clear();
+ m_syntax.clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_regex_options);
+ }
+
+ llvm::StringRef GetHelp() { return m_help; }
+
+ llvm::StringRef GetSyntax() { return m_syntax; }
+
+ protected:
+ // Instance variables to hold the values for command options.
+
+ std::string m_help;
+ std::string m_syntax;
+ };
+
+ Options *GetOptions() override { return &m_options; }
+
+ CommandOptions m_options;
+};
+
+class CommandObjectPythonFunction : public CommandObjectRaw {
+public:
+ CommandObjectPythonFunction(CommandInterpreter &interpreter, std::string name,
+ std::string funct, std::string help,
+ ScriptedCommandSynchronicity synch)
+ : CommandObjectRaw(interpreter, name), m_function_name(funct),
+ m_synchro(synch) {
+ if (!help.empty())
+ SetHelp(help);
+ else {
+ StreamString stream;
+ stream.Printf("For more information run 'help %s'", name.c_str());
+ SetHelp(stream.GetString());
+ }
+ }
+
+ ~CommandObjectPythonFunction() override = default;
+
+ bool IsRemovable() const override { return true; }
+
+ const std::string &GetFunctionName() { return m_function_name; }
+
+ ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; }
+
+ llvm::StringRef GetHelpLong() override {
+ if (m_fetched_help_long)
+ return CommandObjectRaw::GetHelpLong();
+
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+ if (!scripter)
+ return CommandObjectRaw::GetHelpLong();
+
+ std::string docstring;
+ m_fetched_help_long =
+ scripter->GetDocumentationForItem(m_function_name.c_str(), docstring);
+ if (!docstring.empty())
+ SetHelpLong(docstring);
+ return CommandObjectRaw::GetHelpLong();
+ }
+
+protected:
+ bool DoExecute(llvm::StringRef raw_command_line,
+ CommandReturnObject &result) override {
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+
+ Status error;
+
+ result.SetStatus(eReturnStatusInvalid);
+
+ if (!scripter || !scripter->RunScriptBasedCommand(
+ m_function_name.c_str(), raw_command_line, m_synchro,
+ result, error, m_exe_ctx)) {
+ result.AppendError(error.AsCString());
+ } else {
+ // Don't change the status if the command already set it...
+ if (result.GetStatus() == eReturnStatusInvalid) {
+ if (result.GetOutputData().empty())
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ else
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ std::string m_function_name;
+ ScriptedCommandSynchronicity m_synchro;
+ bool m_fetched_help_long = false;
+};
+
+class CommandObjectScriptingObject : public CommandObjectRaw {
+public:
+ CommandObjectScriptingObject(CommandInterpreter &interpreter,
+ std::string name,
+ StructuredData::GenericSP cmd_obj_sp,
+ ScriptedCommandSynchronicity synch)
+ : CommandObjectRaw(interpreter, name), m_cmd_obj_sp(cmd_obj_sp),
+ m_synchro(synch), m_fetched_help_short(false),
+ m_fetched_help_long(false) {
+ StreamString stream;
+ stream.Printf("For more information run 'help %s'", name.c_str());
+ SetHelp(stream.GetString());
+ if (ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter())
+ GetFlags().Set(scripter->GetFlagsForCommandObject(cmd_obj_sp));
+ }
+
+ ~CommandObjectScriptingObject() override = default;
+
+ bool IsRemovable() const override { return true; }
+
+ ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; }
+
+ llvm::StringRef GetHelp() override {
+ if (m_fetched_help_short)
+ return CommandObjectRaw::GetHelp();
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+ if (!scripter)
+ return CommandObjectRaw::GetHelp();
+ std::string docstring;
+ m_fetched_help_short =
+ scripter->GetShortHelpForCommandObject(m_cmd_obj_sp, docstring);
+ if (!docstring.empty())
+ SetHelp(docstring);
+
+ return CommandObjectRaw::GetHelp();
+ }
+
+ llvm::StringRef GetHelpLong() override {
+ if (m_fetched_help_long)
+ return CommandObjectRaw::GetHelpLong();
+
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+ if (!scripter)
+ return CommandObjectRaw::GetHelpLong();
+
+ std::string docstring;
+ m_fetched_help_long =
+ scripter->GetLongHelpForCommandObject(m_cmd_obj_sp, docstring);
+ if (!docstring.empty())
+ SetHelpLong(docstring);
+ return CommandObjectRaw::GetHelpLong();
+ }
+
+protected:
+ bool DoExecute(llvm::StringRef raw_command_line,
+ CommandReturnObject &result) override {
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+
+ Status error;
+
+ result.SetStatus(eReturnStatusInvalid);
+
+ if (!scripter ||
+ !scripter->RunScriptBasedCommand(m_cmd_obj_sp, raw_command_line,
+ m_synchro, result, error, m_exe_ctx)) {
+ result.AppendError(error.AsCString());
+ } else {
+ // Don't change the status if the command already set it...
+ if (result.GetStatus() == eReturnStatusInvalid) {
+ if (result.GetOutputData().empty())
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ else
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ StructuredData::GenericSP m_cmd_obj_sp;
+ ScriptedCommandSynchronicity m_synchro;
+ bool m_fetched_help_short : 1;
+ bool m_fetched_help_long : 1;
+};
+
+// CommandObjectCommandsScriptImport
+#define LLDB_OPTIONS_script_import
+#include "CommandOptions.inc"
+
+class CommandObjectCommandsScriptImport : public CommandObjectParsed {
+public:
+ CommandObjectCommandsScriptImport(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "command script import",
+ "Import a scripting module in LLDB.", nullptr) {
+ CommandArgumentEntry arg1;
+ CommandArgumentData cmd_arg;
+
+ // Define the first (and only) variant of this arg.
+ cmd_arg.arg_type = eArgTypeFilename;
+ cmd_arg.arg_repetition = eArgRepeatPlus;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(cmd_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ }
+
+ ~CommandObjectCommandsScriptImport() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+ request, nullptr);
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'r':
+ // NO-OP
+ break;
+ case 'c':
+ relative_to_command_file = true;
+ break;
+ case 's':
+ silent = true;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ relative_to_command_file = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_script_import_options);
+ }
+ bool relative_to_command_file = false;
+ bool silent = false;
+ };
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (command.empty()) {
+ result.AppendError("command script import needs one or more arguments");
+ return false;
+ }
+
+ FileSpec source_dir = {};
+ if (m_options.relative_to_command_file) {
+ source_dir = GetDebugger().GetCommandInterpreter().GetCurrentSourceDir();
+ if (!source_dir) {
+ result.AppendError("command script import -c can only be specified "
+ "from a command file");
+ return false;
+ }
+ }
+
+ for (auto &entry : command.entries()) {
+ Status error;
+
+ LoadScriptOptions options;
+ options.SetInitSession(true);
+ options.SetSilent(m_options.silent);
+
+ // FIXME: this is necessary because CommandObject::CheckRequirements()
+ // assumes that commands won't ever be recursively invoked, but it's
+ // actually possible to craft a Python script that does other "command
+ // script imports" in __lldb_init_module the real fix is to have
+ // recursive commands possible with a CommandInvocation object separate
+ // from the CommandObject itself, so that recursive command invocations
+ // won't stomp on each other (wrt to execution contents, options, and
+ // more)
+ m_exe_ctx.Clear();
+ if (GetDebugger().GetScriptInterpreter()->LoadScriptingModule(
+ entry.c_str(), options, error, /*module_sp=*/nullptr,
+ source_dir)) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendErrorWithFormat("module importing failed: %s",
+ error.AsCString());
+ }
+ }
+
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectCommandsScriptAdd
+static constexpr OptionEnumValueElement g_script_synchro_type[] = {
+ {
+ eScriptedCommandSynchronicitySynchronous,
+ "synchronous",
+ "Run synchronous",
+ },
+ {
+ eScriptedCommandSynchronicityAsynchronous,
+ "asynchronous",
+ "Run asynchronous",
+ },
+ {
+ eScriptedCommandSynchronicityCurrentValue,
+ "current",
+ "Do not alter current setting",
+ },
+};
+
+static constexpr OptionEnumValues ScriptSynchroType() {
+ return OptionEnumValues(g_script_synchro_type);
+}
+
+#define LLDB_OPTIONS_script_add
+#include "CommandOptions.inc"
+
+class CommandObjectCommandsScriptAdd : public CommandObjectParsed,
+ public IOHandlerDelegateMultiline {
+public:
+ CommandObjectCommandsScriptAdd(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "command script add",
+ "Add a scripted function as an LLDB command.",
+ "Add a scripted function as an lldb command. "
+ "If you provide a single argument, the command "
+ "will be added at the root level of the command "
+ "hierarchy. If there are more arguments they "
+ "must be a path to a user-added container "
+ "command, and the last element will be the new "
+ "command name."),
+ IOHandlerDelegateMultiline("DONE") {
+ CommandArgumentEntry arg1;
+ CommandArgumentData cmd_arg;
+
+ // This is one or more command names, which form the path to the command
+ // you want to add.
+ cmd_arg.arg_type = eArgTypeCommand;
+ cmd_arg.arg_repetition = eArgRepeatPlus;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(cmd_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ }
+
+ ~CommandObjectCommandsScriptAdd() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::CompleteModifiableCmdPathArgs(m_interpreter, request,
+ opt_element_vector);
+ }
+
+protected:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'f':
+ if (!option_arg.empty())
+ m_funct_name = std::string(option_arg);
+ break;
+ case 'c':
+ if (!option_arg.empty())
+ m_class_name = std::string(option_arg);
+ break;
+ case 'h':
+ if (!option_arg.empty())
+ m_short_help = std::string(option_arg);
+ break;
+ case 'o':
+ m_overwrite_lazy = eLazyBoolYes;
+ break;
+ case 's':
+ m_synchronicity =
+ (ScriptedCommandSynchronicity)OptionArgParser::ToOptionEnum(
+ option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
+ if (!error.Success())
+ error.SetErrorStringWithFormat(
+ "unrecognized value for synchronicity '%s'",
+ option_arg.str().c_str());
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_class_name.clear();
+ m_funct_name.clear();
+ m_short_help.clear();
+ m_overwrite_lazy = eLazyBoolCalculate;
+ m_synchronicity = eScriptedCommandSynchronicitySynchronous;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_script_add_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ std::string m_class_name;
+ std::string m_funct_name;
+ std::string m_short_help;
+ LazyBool m_overwrite_lazy = eLazyBoolCalculate;
+ ScriptedCommandSynchronicity m_synchronicity =
+ eScriptedCommandSynchronicitySynchronous;
+ };
+
+ void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
+ StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
+ if (output_sp && interactive) {
+ output_sp->PutCString(g_python_command_instructions);
+ output_sp->Flush();
+ }
+ }
+
+ void IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &data) override {
+ StreamFileSP error_sp = io_handler.GetErrorStreamFileSP();
+
+ ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
+ if (interpreter) {
+ StringList lines;
+ lines.SplitIntoLines(data);
+ if (lines.GetSize() > 0) {
+ std::string funct_name_str;
+ if (interpreter->GenerateScriptAliasFunction(lines, funct_name_str)) {
+ if (funct_name_str.empty()) {
+ error_sp->Printf("error: unable to obtain a function name, didn't "
+ "add python command.\n");
+ error_sp->Flush();
+ } else {
+ // everything should be fine now, let's add this alias
+
+ CommandObjectSP command_obj_sp(new CommandObjectPythonFunction(
+ m_interpreter, m_cmd_name, funct_name_str, m_short_help,
+ m_synchronicity));
+ if (!m_container) {
+ Status error = m_interpreter.AddUserCommand(
+ m_cmd_name, command_obj_sp, m_overwrite);
+ if (error.Fail()) {
+ error_sp->Printf("error: unable to add selected command: '%s'",
+ error.AsCString());
+ error_sp->Flush();
+ }
+ } else {
+ llvm::Error llvm_error = m_container->LoadUserSubcommand(
+ m_cmd_name, command_obj_sp, m_overwrite);
+ if (llvm_error) {
+ error_sp->Printf("error: unable to add selected command: '%s'",
+ llvm::toString(std::move(llvm_error)).c_str());
+ error_sp->Flush();
+ }
+ }
+ }
+ } else {
+ error_sp->Printf(
+ "error: unable to create function, didn't add python command\n");
+ error_sp->Flush();
+ }
+ } else {
+ error_sp->Printf("error: empty function, didn't add python command\n");
+ error_sp->Flush();
+ }
+ } else {
+ error_sp->Printf(
+ "error: script interpreter missing, didn't add python command\n");
+ error_sp->Flush();
+ }
+
+ io_handler.SetIsDone(true);
+ }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (GetDebugger().GetScriptLanguage() != lldb::eScriptLanguagePython) {
+ result.AppendError("only scripting language supported for scripted "
+ "commands is currently Python");
+ return false;
+ }
+
+ if (command.GetArgumentCount() == 0) {
+ result.AppendError("'command script add' requires at least one argument");
+ return false;
+ }
+ // Store the options in case we get multi-line input, also figure out the
+ // default if not user supplied:
+ switch (m_options.m_overwrite_lazy) {
+ case eLazyBoolCalculate:
+ m_overwrite = !GetDebugger().GetCommandInterpreter().GetRequireCommandOverwrite();
+ break;
+ case eLazyBoolYes:
+ m_overwrite = true;
+ break;
+ case eLazyBoolNo:
+ m_overwrite = false;
+ }
+
+ Status path_error;
+ m_container = GetCommandInterpreter().VerifyUserMultiwordCmdPath(
+ command, true, path_error);
+
+ if (path_error.Fail()) {
+ result.AppendErrorWithFormat("error in command path: %s",
+ path_error.AsCString());
+ return false;
+ }
+
+ if (!m_container) {
+ // This is getting inserted into the root of the interpreter.
+ m_cmd_name = std::string(command[0].ref());
+ } else {
+ size_t num_args = command.GetArgumentCount();
+ m_cmd_name = std::string(command[num_args - 1].ref());
+ }
+
+ m_short_help.assign(m_options.m_short_help);
+ m_synchronicity = m_options.m_synchronicity;
+
+ // Handle the case where we prompt for the script code first:
+ if (m_options.m_class_name.empty() && m_options.m_funct_name.empty()) {
+ m_interpreter.GetPythonCommandsFromIOHandler(" ", // Prompt
+ *this); // IOHandlerDelegate
+ return result.Succeeded();
+ }
+
+ CommandObjectSP new_cmd_sp;
+ if (m_options.m_class_name.empty()) {
+ new_cmd_sp.reset(new CommandObjectPythonFunction(
+ m_interpreter, m_cmd_name, m_options.m_funct_name,
+ m_options.m_short_help, m_synchronicity));
+ } else {
+ ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
+ if (!interpreter) {
+ result.AppendError("cannot find ScriptInterpreter");
+ return false;
+ }
+
+ auto cmd_obj_sp = interpreter->CreateScriptCommandObject(
+ m_options.m_class_name.c_str());
+ if (!cmd_obj_sp) {
+ result.AppendError("cannot create helper object");
+ return false;
+ }
+
+ new_cmd_sp.reset(new CommandObjectScriptingObject(
+ m_interpreter, m_cmd_name, cmd_obj_sp, m_synchronicity));
+ }
+
+ // Assume we're going to succeed...
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ if (!m_container) {
+ Status add_error =
+ m_interpreter.AddUserCommand(m_cmd_name, new_cmd_sp, m_overwrite);
+ if (add_error.Fail())
+ result.AppendErrorWithFormat("cannot add command: %s",
+ add_error.AsCString());
+ } else {
+ llvm::Error llvm_error =
+ m_container->LoadUserSubcommand(m_cmd_name, new_cmd_sp, m_overwrite);
+ if (llvm_error)
+ result.AppendErrorWithFormat("cannot add command: %s",
+ llvm::toString(std::move(llvm_error)).c_str());
+ }
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+ std::string m_cmd_name;
+ CommandObjectMultiword *m_container = nullptr;
+ std::string m_short_help;
+ bool m_overwrite = false;
+ ScriptedCommandSynchronicity m_synchronicity =
+ eScriptedCommandSynchronicitySynchronous;
+};
+
+// CommandObjectCommandsScriptList
+
+class CommandObjectCommandsScriptList : public CommandObjectParsed {
+public:
+ CommandObjectCommandsScriptList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "command script list",
+ "List defined top-level scripted commands.",
+ nullptr) {}
+
+ ~CommandObjectCommandsScriptList() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ m_interpreter.GetHelp(result, CommandInterpreter::eCommandTypesUserDef);
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ return true;
+ }
+};
+
+// CommandObjectCommandsScriptClear
+
+class CommandObjectCommandsScriptClear : public CommandObjectParsed {
+public:
+ CommandObjectCommandsScriptClear(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "command script clear",
+ "Delete all scripted commands.", nullptr) {}
+
+ ~CommandObjectCommandsScriptClear() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ m_interpreter.RemoveAllUser();
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ return true;
+ }
+};
+
+// CommandObjectCommandsScriptDelete
+
+class CommandObjectCommandsScriptDelete : public CommandObjectParsed {
+public:
+ CommandObjectCommandsScriptDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "command script delete",
+ "Delete a scripted command by specifying the path to the command.",
+ nullptr) {
+ CommandArgumentEntry arg1;
+ CommandArgumentData cmd_arg;
+
+ // This is a list of command names forming the path to the command
+ // to be deleted.
+ cmd_arg.arg_type = eArgTypeCommand;
+ cmd_arg.arg_repetition = eArgRepeatPlus;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(cmd_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ }
+
+ ~CommandObjectCommandsScriptDelete() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::CompleteModifiableCmdPathArgs(m_interpreter, request,
+ opt_element_vector);
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+
+ llvm::StringRef root_cmd = command[0].ref();
+ size_t num_args = command.GetArgumentCount();
+
+ if (root_cmd.empty()) {
+ result.AppendErrorWithFormat("empty root command name");
+ return false;
+ }
+ if (!m_interpreter.HasUserCommands() &&
+ !m_interpreter.HasUserMultiwordCommands()) {
+ result.AppendErrorWithFormat("can only delete user defined commands, "
+ "but no user defined commands found");
+ return false;
+ }
+
+ CommandObjectSP cmd_sp = m_interpreter.GetCommandSPExact(root_cmd);
+ if (!cmd_sp) {
+ result.AppendErrorWithFormat("command '%s' not found.",
+ command[0].c_str());
+ return false;
+ }
+ if (!cmd_sp->IsUserCommand()) {
+ result.AppendErrorWithFormat("command '%s' is not a user command.",
+ command[0].c_str());
+ return false;
+ }
+ if (cmd_sp->GetAsMultiwordCommand() && num_args == 1) {
+ result.AppendErrorWithFormat("command '%s' is a multi-word command.\n "
+ "Delete with \"command container delete\"",
+ command[0].c_str());
+ return false;
+ }
+
+ if (command.GetArgumentCount() == 1) {
+ m_interpreter.RemoveUser(root_cmd);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ // We're deleting a command from a multiword command. Verify the command
+ // path:
+ Status error;
+ CommandObjectMultiword *container =
+ GetCommandInterpreter().VerifyUserMultiwordCmdPath(command, true,
+ error);
+ if (error.Fail()) {
+ result.AppendErrorWithFormat("could not resolve command path: %s",
+ error.AsCString());
+ return false;
+ }
+ if (!container) {
+ // This means that command only had a leaf command, so the container is
+ // the root. That should have been handled above.
+ result.AppendErrorWithFormat("could not find a container for '%s'",
+ command[0].c_str());
+ return false;
+ }
+ const char *leaf_cmd = command[num_args - 1].c_str();
+ llvm::Error llvm_error = container->RemoveUserSubcommand(leaf_cmd,
+ /* multiword not okay */ false);
+ if (llvm_error) {
+ result.AppendErrorWithFormat("could not delete command '%s': %s",
+ leaf_cmd,
+ llvm::toString(std::move(llvm_error)).c_str());
+ return false;
+ }
+
+ Stream &out_stream = result.GetOutputStream();
+
+ out_stream << "Deleted command:";
+ for (size_t idx = 0; idx < num_args; idx++) {
+ out_stream << ' ';
+ out_stream << command[idx].c_str();
+ }
+ out_stream << '\n';
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+#pragma mark CommandObjectMultiwordCommandsScript
+
+// CommandObjectMultiwordCommandsScript
+
+class CommandObjectMultiwordCommandsScript : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordCommandsScript(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "command script",
+ "Commands for managing custom "
+ "commands implemented by "
+ "interpreter scripts.",
+ "command script <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("add", CommandObjectSP(
+ new CommandObjectCommandsScriptAdd(interpreter)));
+ LoadSubCommand(
+ "delete",
+ CommandObjectSP(new CommandObjectCommandsScriptDelete(interpreter)));
+ LoadSubCommand(
+ "clear",
+ CommandObjectSP(new CommandObjectCommandsScriptClear(interpreter)));
+ LoadSubCommand("list", CommandObjectSP(new CommandObjectCommandsScriptList(
+ interpreter)));
+ LoadSubCommand(
+ "import",
+ CommandObjectSP(new CommandObjectCommandsScriptImport(interpreter)));
+ }
+
+ ~CommandObjectMultiwordCommandsScript() override = default;
+};
+
+#pragma mark CommandObjectCommandContainer
+#define LLDB_OPTIONS_container_add
+#include "CommandOptions.inc"
+
+class CommandObjectCommandsContainerAdd : public CommandObjectParsed {
+public:
+ CommandObjectCommandsContainerAdd(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "command container add",
+ "Add a container command to lldb. Adding to built-"
+ "in container commands is not allowed.",
+ "command container add [[path1]...] container-name") {
+ CommandArgumentEntry arg1;
+ CommandArgumentData cmd_arg;
+
+ // This is one or more command names, which form the path to the command
+ // you want to add.
+ cmd_arg.arg_type = eArgTypeCommand;
+ cmd_arg.arg_repetition = eArgRepeatPlus;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(cmd_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ }
+
+ ~CommandObjectCommandsContainerAdd() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::CompleteModifiableCmdPathArgs(m_interpreter, request,
+ opt_element_vector);
+ }
+
+protected:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'h':
+ if (!option_arg.empty())
+ m_short_help = std::string(option_arg);
+ break;
+ case 'o':
+ m_overwrite = true;
+ break;
+ case 'H':
+ if (!option_arg.empty())
+ m_long_help = std::string(option_arg);
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_short_help.clear();
+ m_long_help.clear();
+ m_overwrite = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_container_add_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ std::string m_short_help;
+ std::string m_long_help;
+ bool m_overwrite = false;
+ };
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ size_t num_args = command.GetArgumentCount();
+
+ if (num_args == 0) {
+ result.AppendError("no command was specified");
+ return false;
+ }
+
+ if (num_args == 1) {
+ // We're adding this as a root command, so use the interpreter.
+ const char *cmd_name = command.GetArgumentAtIndex(0);
+ auto cmd_sp = CommandObjectSP(new CommandObjectMultiword(
+ GetCommandInterpreter(), cmd_name, m_options.m_short_help.c_str(),
+ m_options.m_long_help.c_str()));
+ cmd_sp->GetAsMultiwordCommand()->SetRemovable(true);
+ Status add_error = GetCommandInterpreter().AddUserCommand(
+ cmd_name, cmd_sp, m_options.m_overwrite);
+ if (add_error.Fail()) {
+ result.AppendErrorWithFormat("error adding command: %s",
+ add_error.AsCString());
+ return false;
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+
+ // We're adding this to a subcommand, first find the subcommand:
+ Status path_error;
+ CommandObjectMultiword *add_to_me =
+ GetCommandInterpreter().VerifyUserMultiwordCmdPath(command, true,
+ path_error);
+
+ if (!add_to_me) {
+ result.AppendErrorWithFormat("error adding command: %s",
+ path_error.AsCString());
+ return false;
+ }
+
+ const char *cmd_name = command.GetArgumentAtIndex(num_args - 1);
+ auto cmd_sp = CommandObjectSP(new CommandObjectMultiword(
+ GetCommandInterpreter(), cmd_name, m_options.m_short_help.c_str(),
+ m_options.m_long_help.c_str()));
+ llvm::Error llvm_error =
+ add_to_me->LoadUserSubcommand(cmd_name, cmd_sp, m_options.m_overwrite);
+ if (llvm_error) {
+ result.AppendErrorWithFormat("error adding subcommand: %s",
+ llvm::toString(std::move(llvm_error)).c_str());
+ return false;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+#define LLDB_OPTIONS_multiword_delete
+#include "CommandOptions.inc"
+class CommandObjectCommandsContainerDelete : public CommandObjectParsed {
+public:
+ CommandObjectCommandsContainerDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "command container delete",
+ "Delete a container command previously added to "
+ "lldb.",
+ "command container delete [[path1] ...] container-cmd") {
+ CommandArgumentEntry arg1;
+ CommandArgumentData cmd_arg;
+
+ // This is one or more command names, which form the path to the command
+ // you want to add.
+ cmd_arg.arg_type = eArgTypeCommand;
+ cmd_arg.arg_repetition = eArgRepeatPlus;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(cmd_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ }
+
+ ~CommandObjectCommandsContainerDelete() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::CompleteModifiableCmdPathArgs(m_interpreter, request,
+ opt_element_vector);
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ size_t num_args = command.GetArgumentCount();
+
+ if (num_args == 0) {
+ result.AppendError("No command was specified.");
+ return false;
+ }
+
+ if (num_args == 1) {
+ // We're removing a root command, so we need to delete it from the
+ // interpreter.
+ const char *cmd_name = command.GetArgumentAtIndex(0);
+ // Let's do a little more work here so we can do better error reporting.
+ CommandInterpreter &interp = GetCommandInterpreter();
+ CommandObjectSP cmd_sp = interp.GetCommandSPExact(cmd_name);
+ if (!cmd_sp) {
+ result.AppendErrorWithFormat("container command %s doesn't exist.",
+ cmd_name);
+ return false;
+ }
+ if (!cmd_sp->IsUserCommand()) {
+ result.AppendErrorWithFormat(
+ "container command %s is not a user command", cmd_name);
+ return false;
+ }
+ if (!cmd_sp->GetAsMultiwordCommand()) {
+ result.AppendErrorWithFormat("command %s is not a container command",
+ cmd_name);
+ return false;
+ }
+
+ bool did_remove = GetCommandInterpreter().RemoveUserMultiword(cmd_name);
+ if (!did_remove) {
+ result.AppendErrorWithFormat("error removing command %s.", cmd_name);
+ return false;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+
+ // We're removing a subcommand, first find the subcommand's owner:
+ Status path_error;
+ CommandObjectMultiword *container =
+ GetCommandInterpreter().VerifyUserMultiwordCmdPath(command, true,
+ path_error);
+
+ if (!container) {
+ result.AppendErrorWithFormat("error removing container command: %s",
+ path_error.AsCString());
+ return false;
+ }
+ const char *leaf = command.GetArgumentAtIndex(num_args - 1);
+ llvm::Error llvm_error =
+ container->RemoveUserSubcommand(leaf, /* multiword okay */ true);
+ if (llvm_error) {
+ result.AppendErrorWithFormat("error removing container command: %s",
+ llvm::toString(std::move(llvm_error)).c_str());
+ return false;
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+};
+
+class CommandObjectCommandContainer : public CommandObjectMultiword {
+public:
+ CommandObjectCommandContainer(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "command container",
+ "Commands for adding container commands to lldb. "
+ "Container commands are containers for other commands. You can "
+ "add nested container commands by specifying a command path, "
+ "but you can't add commands into the built-in command hierarchy.",
+ "command container <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("add", CommandObjectSP(new CommandObjectCommandsContainerAdd(
+ interpreter)));
+ LoadSubCommand(
+ "delete",
+ CommandObjectSP(new CommandObjectCommandsContainerDelete(interpreter)));
+ }
+
+ ~CommandObjectCommandContainer() override = default;
+};
+
+#pragma mark CommandObjectMultiwordCommands
+
+// CommandObjectMultiwordCommands
+
+CommandObjectMultiwordCommands::CommandObjectMultiwordCommands(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "command",
+ "Commands for managing custom LLDB commands.",
+ "command <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("source",
+ CommandObjectSP(new CommandObjectCommandsSource(interpreter)));
+ LoadSubCommand("alias",
+ CommandObjectSP(new CommandObjectCommandsAlias(interpreter)));
+ LoadSubCommand("unalias", CommandObjectSP(
+ new CommandObjectCommandsUnalias(interpreter)));
+ LoadSubCommand("delete",
+ CommandObjectSP(new CommandObjectCommandsDelete(interpreter)));
+ LoadSubCommand("container", CommandObjectSP(new CommandObjectCommandContainer(
+ interpreter)));
+ LoadSubCommand(
+ "regex", CommandObjectSP(new CommandObjectCommandsAddRegex(interpreter)));
+ LoadSubCommand(
+ "script",
+ CommandObjectSP(new CommandObjectMultiwordCommandsScript(interpreter)));
+}
+
+CommandObjectMultiwordCommands::~CommandObjectMultiwordCommands() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectCommands.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectCommands.h
new file mode 100644
index 000000000000..f418e5ba779b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectCommands.h
@@ -0,0 +1,28 @@
+//===-- CommandObjectCommands.h -----------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTCOMMANDS_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTCOMMANDS_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+// CommandObjectMultiwordCommands
+
+class CommandObjectMultiwordCommands : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordCommands(CommandInterpreter &interpreter);
+
+ ~CommandObjectMultiwordCommands() override;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTCOMMANDS_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectDisassemble.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectDisassemble.cpp
new file mode 100644
index 000000000000..6c33edc8a3a8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectDisassemble.cpp
@@ -0,0 +1,546 @@
+//===-- CommandObjectDisassemble.cpp --------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectDisassemble.h"
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+
+static constexpr unsigned default_disasm_byte_size = 32;
+static constexpr unsigned default_disasm_num_ins = 4;
+
+using namespace lldb;
+using namespace lldb_private;
+
+#define LLDB_OPTIONS_disassemble
+#include "CommandOptions.inc"
+
+CommandObjectDisassemble::CommandOptions::CommandOptions() {
+ OptionParsingStarting(nullptr);
+}
+
+CommandObjectDisassemble::CommandOptions::~CommandOptions() = default;
+
+Status CommandObjectDisassemble::CommandOptions::SetOptionValue(
+ uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'm':
+ show_mixed = true;
+ break;
+
+ case 'C':
+ if (option_arg.getAsInteger(0, num_lines_context))
+ error.SetErrorStringWithFormat("invalid num context lines string: \"%s\"",
+ option_arg.str().c_str());
+ break;
+
+ case 'c':
+ if (option_arg.getAsInteger(0, num_instructions))
+ error.SetErrorStringWithFormat(
+ "invalid num of instructions string: \"%s\"",
+ option_arg.str().c_str());
+ break;
+
+ case 'b':
+ show_bytes = true;
+ break;
+
+ case 'k':
+ show_control_flow_kind = true;
+ break;
+
+ case 's': {
+ start_addr = OptionArgParser::ToAddress(execution_context, option_arg,
+ LLDB_INVALID_ADDRESS, &error);
+ if (start_addr != LLDB_INVALID_ADDRESS)
+ some_location_specified = true;
+ } break;
+ case 'e': {
+ end_addr = OptionArgParser::ToAddress(execution_context, option_arg,
+ LLDB_INVALID_ADDRESS, &error);
+ if (end_addr != LLDB_INVALID_ADDRESS)
+ some_location_specified = true;
+ } break;
+
+ case 'n':
+ func_name.assign(std::string(option_arg));
+ some_location_specified = true;
+ break;
+
+ case 'p':
+ at_pc = true;
+ some_location_specified = true;
+ break;
+
+ case 'l':
+ frame_line = true;
+ // Disassemble the current source line kind of implies showing mixed source
+ // code context.
+ show_mixed = true;
+ some_location_specified = true;
+ break;
+
+ case 'P':
+ plugin_name.assign(std::string(option_arg));
+ break;
+
+ case 'F': {
+ TargetSP target_sp =
+ execution_context ? execution_context->GetTargetSP() : TargetSP();
+ if (target_sp && (target_sp->GetArchitecture().GetTriple().getArch() ==
+ llvm::Triple::x86 ||
+ target_sp->GetArchitecture().GetTriple().getArch() ==
+ llvm::Triple::x86_64)) {
+ flavor_string.assign(std::string(option_arg));
+ } else
+ error.SetErrorStringWithFormat("Disassembler flavors are currently only "
+ "supported for x86 and x86_64 targets.");
+ break;
+ }
+
+ case 'r':
+ raw = true;
+ break;
+
+ case 'f':
+ current_function = true;
+ some_location_specified = true;
+ break;
+
+ case 'A':
+ if (execution_context) {
+ const auto &target_sp = execution_context->GetTargetSP();
+ auto platform_ptr = target_sp ? target_sp->GetPlatform().get() : nullptr;
+ arch = Platform::GetAugmentedArchSpec(platform_ptr, option_arg);
+ }
+ break;
+
+ case 'a': {
+ symbol_containing_addr = OptionArgParser::ToAddress(
+ execution_context, option_arg, LLDB_INVALID_ADDRESS, &error);
+ if (symbol_containing_addr != LLDB_INVALID_ADDRESS) {
+ some_location_specified = true;
+ }
+ } break;
+
+ case '\x01':
+ force = true;
+ break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+}
+
+void CommandObjectDisassemble::CommandOptions::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ show_mixed = false;
+ show_bytes = false;
+ show_control_flow_kind = false;
+ num_lines_context = 0;
+ num_instructions = 0;
+ func_name.clear();
+ current_function = false;
+ at_pc = false;
+ frame_line = false;
+ start_addr = LLDB_INVALID_ADDRESS;
+ end_addr = LLDB_INVALID_ADDRESS;
+ symbol_containing_addr = LLDB_INVALID_ADDRESS;
+ raw = false;
+ plugin_name.clear();
+
+ Target *target =
+ execution_context ? execution_context->GetTargetPtr() : nullptr;
+
+ // This is a hack till we get the ability to specify features based on
+ // architecture. For now GetDisassemblyFlavor is really only valid for x86
+ // (and for the llvm assembler plugin, but I'm papering over that since that
+ // is the only disassembler plugin we have...
+ if (target) {
+ if (target->GetArchitecture().GetTriple().getArch() == llvm::Triple::x86 ||
+ target->GetArchitecture().GetTriple().getArch() ==
+ llvm::Triple::x86_64) {
+ flavor_string.assign(target->GetDisassemblyFlavor());
+ } else
+ flavor_string.assign("default");
+
+ } else
+ flavor_string.assign("default");
+
+ arch.Clear();
+ some_location_specified = false;
+ force = false;
+}
+
+Status CommandObjectDisassemble::CommandOptions::OptionParsingFinished(
+ ExecutionContext *execution_context) {
+ if (!some_location_specified)
+ current_function = true;
+ return Status();
+}
+
+llvm::ArrayRef<OptionDefinition>
+CommandObjectDisassemble::CommandOptions::GetDefinitions() {
+ return llvm::makeArrayRef(g_disassemble_options);
+}
+
+// CommandObjectDisassemble
+
+CommandObjectDisassemble::CommandObjectDisassemble(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "disassemble",
+ "Disassemble specified instructions in the current target. "
+ "Defaults to the current function for the current thread and "
+ "stack frame.",
+ "disassemble [<cmd-options>]", eCommandRequiresTarget),
+ m_options() {}
+
+CommandObjectDisassemble::~CommandObjectDisassemble() = default;
+
+llvm::Error CommandObjectDisassemble::CheckRangeSize(const AddressRange &range,
+ llvm::StringRef what) {
+ if (m_options.num_instructions > 0 || m_options.force ||
+ range.GetByteSize() < GetDebugger().GetStopDisassemblyMaxSize())
+ return llvm::Error::success();
+ StreamString msg;
+ msg << "Not disassembling " << what << " because it is very large ";
+ range.Dump(&msg, &GetSelectedTarget(), Address::DumpStyleLoadAddress,
+ Address::DumpStyleFileAddress);
+ msg << ". To disassemble specify an instruction count limit, start/stop "
+ "addresses or use the --force option.";
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ msg.GetString());
+}
+
+llvm::Expected<std::vector<AddressRange>>
+CommandObjectDisassemble::GetContainingAddressRanges() {
+ std::vector<AddressRange> ranges;
+ const auto &get_range = [&](Address addr) {
+ ModuleSP module_sp(addr.GetModule());
+ SymbolContext sc;
+ bool resolve_tail_call_address = true;
+ addr.GetModule()->ResolveSymbolContextForAddress(
+ addr, eSymbolContextEverything, sc, resolve_tail_call_address);
+ if (sc.function || sc.symbol) {
+ AddressRange range;
+ sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0,
+ false, range);
+ ranges.push_back(range);
+ }
+ };
+
+ Target &target = GetSelectedTarget();
+ if (!target.GetSectionLoadList().IsEmpty()) {
+ Address symbol_containing_address;
+ if (target.GetSectionLoadList().ResolveLoadAddress(
+ m_options.symbol_containing_addr, symbol_containing_address)) {
+ get_range(symbol_containing_address);
+ }
+ } else {
+ for (lldb::ModuleSP module_sp : target.GetImages().Modules()) {
+ Address file_address;
+ if (module_sp->ResolveFileAddress(m_options.symbol_containing_addr,
+ file_address)) {
+ get_range(file_address);
+ }
+ }
+ }
+
+ if (ranges.empty()) {
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Could not find function bounds for address 0x%" PRIx64,
+ m_options.symbol_containing_addr);
+ }
+
+ if (llvm::Error err = CheckRangeSize(ranges[0], "the function"))
+ return std::move(err);
+ return ranges;
+}
+
+llvm::Expected<std::vector<AddressRange>>
+CommandObjectDisassemble::GetCurrentFunctionRanges() {
+ Process *process = m_exe_ctx.GetProcessPtr();
+ StackFrame *frame = m_exe_ctx.GetFramePtr();
+ if (!frame) {
+ if (process) {
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Cannot disassemble around the current "
+ "function without the process being stopped.\n");
+ } else {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "Cannot disassemble around the current "
+ "function without a selected frame: "
+ "no currently running process.\n");
+ }
+ }
+ SymbolContext sc(
+ frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol));
+ AddressRange range;
+ if (sc.function)
+ range = sc.function->GetAddressRange();
+ else if (sc.symbol && sc.symbol->ValueIsAddress()) {
+ range = {sc.symbol->GetAddress(), sc.symbol->GetByteSize()};
+ } else
+ range = {frame->GetFrameCodeAddress(), default_disasm_byte_size};
+
+ if (llvm::Error err = CheckRangeSize(range, "the current function"))
+ return std::move(err);
+ return std::vector<AddressRange>{range};
+}
+
+llvm::Expected<std::vector<AddressRange>>
+CommandObjectDisassemble::GetCurrentLineRanges() {
+ Process *process = m_exe_ctx.GetProcessPtr();
+ StackFrame *frame = m_exe_ctx.GetFramePtr();
+ if (!frame) {
+ if (process) {
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Cannot disassemble around the current "
+ "function without the process being stopped.\n");
+ } else {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "Cannot disassemble around the current "
+ "line without a selected frame: "
+ "no currently running process.\n");
+ }
+ }
+
+ LineEntry pc_line_entry(
+ frame->GetSymbolContext(eSymbolContextLineEntry).line_entry);
+ if (pc_line_entry.IsValid())
+ return std::vector<AddressRange>{pc_line_entry.range};
+
+ // No line entry, so just disassemble around the current pc
+ m_options.show_mixed = false;
+ return GetPCRanges();
+}
+
+llvm::Expected<std::vector<AddressRange>>
+CommandObjectDisassemble::GetNameRanges(CommandReturnObject &result) {
+ ConstString name(m_options.func_name.c_str());
+
+ ModuleFunctionSearchOptions function_options;
+ function_options.include_symbols = true;
+ function_options.include_inlines = true;
+
+ // Find functions matching the given name.
+ SymbolContextList sc_list;
+ GetSelectedTarget().GetImages().FindFunctions(name, eFunctionNameTypeAuto,
+ function_options, sc_list);
+
+ std::vector<AddressRange> ranges;
+ llvm::Error range_errs = llvm::Error::success();
+ AddressRange range;
+ const uint32_t scope =
+ eSymbolContextBlock | eSymbolContextFunction | eSymbolContextSymbol;
+ const bool use_inline_block_range = true;
+ for (SymbolContext sc : sc_list.SymbolContexts()) {
+ for (uint32_t range_idx = 0;
+ sc.GetAddressRange(scope, range_idx, use_inline_block_range, range);
+ ++range_idx) {
+ if (llvm::Error err = CheckRangeSize(range, "a range"))
+ range_errs = joinErrors(std::move(range_errs), std::move(err));
+ else
+ ranges.push_back(range);
+ }
+ }
+ if (ranges.empty()) {
+ if (range_errs)
+ return std::move(range_errs);
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "Unable to find symbol with name '%s'.\n",
+ name.GetCString());
+ }
+ if (range_errs)
+ result.AppendWarning(toString(std::move(range_errs)));
+ return ranges;
+}
+
+llvm::Expected<std::vector<AddressRange>>
+CommandObjectDisassemble::GetPCRanges() {
+ Process *process = m_exe_ctx.GetProcessPtr();
+ StackFrame *frame = m_exe_ctx.GetFramePtr();
+ if (!frame) {
+ if (process) {
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Cannot disassemble around the current "
+ "function without the process being stopped.\n");
+ } else {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "Cannot disassemble around the current "
+ "PC without a selected frame: "
+ "no currently running process.\n");
+ }
+ }
+
+ if (m_options.num_instructions == 0) {
+ // Disassembling at the PC always disassembles some number of
+ // instructions (not the whole function).
+ m_options.num_instructions = default_disasm_num_ins;
+ }
+ return std::vector<AddressRange>{{frame->GetFrameCodeAddress(), 0}};
+}
+
+llvm::Expected<std::vector<AddressRange>>
+CommandObjectDisassemble::GetStartEndAddressRanges() {
+ addr_t size = 0;
+ if (m_options.end_addr != LLDB_INVALID_ADDRESS) {
+ if (m_options.end_addr <= m_options.start_addr) {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "End address before start address.");
+ }
+ size = m_options.end_addr - m_options.start_addr;
+ }
+ return std::vector<AddressRange>{{Address(m_options.start_addr), size}};
+}
+
+llvm::Expected<std::vector<AddressRange>>
+CommandObjectDisassemble::GetRangesForSelectedMode(
+ CommandReturnObject &result) {
+ if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS)
+ return CommandObjectDisassemble::GetContainingAddressRanges();
+ if (m_options.current_function)
+ return CommandObjectDisassemble::GetCurrentFunctionRanges();
+ if (m_options.frame_line)
+ return CommandObjectDisassemble::GetCurrentLineRanges();
+ if (!m_options.func_name.empty())
+ return CommandObjectDisassemble::GetNameRanges(result);
+ if (m_options.start_addr != LLDB_INVALID_ADDRESS)
+ return CommandObjectDisassemble::GetStartEndAddressRanges();
+ return CommandObjectDisassemble::GetPCRanges();
+}
+
+bool CommandObjectDisassemble::DoExecute(Args &command,
+ CommandReturnObject &result) {
+ Target *target = &GetSelectedTarget();
+
+ if (!m_options.arch.IsValid())
+ m_options.arch = target->GetArchitecture();
+
+ if (!m_options.arch.IsValid()) {
+ result.AppendError(
+ "use the --arch option or set the target architecture to disassemble");
+ return false;
+ }
+
+ const char *plugin_name = m_options.GetPluginName();
+ const char *flavor_string = m_options.GetFlavorString();
+
+ DisassemblerSP disassembler =
+ Disassembler::FindPlugin(m_options.arch, flavor_string, plugin_name);
+
+ if (!disassembler) {
+ if (plugin_name) {
+ result.AppendErrorWithFormat(
+ "Unable to find Disassembler plug-in named '%s' that supports the "
+ "'%s' architecture.\n",
+ plugin_name, m_options.arch.GetArchitectureName());
+ } else
+ result.AppendErrorWithFormat(
+ "Unable to find Disassembler plug-in for the '%s' architecture.\n",
+ m_options.arch.GetArchitectureName());
+ return false;
+ } else if (flavor_string != nullptr && !disassembler->FlavorValidForArchSpec(
+ m_options.arch, flavor_string))
+ result.AppendWarningWithFormat(
+ "invalid disassembler flavor \"%s\", using default.\n", flavor_string);
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ if (!command.empty()) {
+ result.AppendErrorWithFormat(
+ "\"disassemble\" arguments are specified as options.\n");
+ const int terminal_width =
+ GetCommandInterpreter().GetDebugger().GetTerminalWidth();
+ GetOptions()->GenerateOptionUsage(result.GetErrorStream(), *this,
+ terminal_width);
+ return false;
+ }
+
+ if (m_options.show_mixed && m_options.num_lines_context == 0)
+ m_options.num_lines_context = 2;
+
+ // Always show the PC in the disassembly
+ uint32_t options = Disassembler::eOptionMarkPCAddress;
+
+ // Mark the source line for the current PC only if we are doing mixed source
+ // and assembly
+ if (m_options.show_mixed)
+ options |= Disassembler::eOptionMarkPCSourceLine;
+
+ if (m_options.show_bytes)
+ options |= Disassembler::eOptionShowBytes;
+
+ if (m_options.show_control_flow_kind)
+ options |= Disassembler::eOptionShowControlFlowKind;
+
+ if (m_options.raw)
+ options |= Disassembler::eOptionRawOuput;
+
+ llvm::Expected<std::vector<AddressRange>> ranges =
+ GetRangesForSelectedMode(result);
+ if (!ranges) {
+ result.AppendError(toString(ranges.takeError()));
+ return result.Succeeded();
+ }
+
+ bool print_sc_header = ranges->size() > 1;
+ for (AddressRange cur_range : *ranges) {
+ Disassembler::Limit limit;
+ if (m_options.num_instructions == 0) {
+ limit = {Disassembler::Limit::Bytes, cur_range.GetByteSize()};
+ if (limit.value == 0)
+ limit.value = default_disasm_byte_size;
+ } else {
+ limit = {Disassembler::Limit::Instructions, m_options.num_instructions};
+ }
+ if (Disassembler::Disassemble(
+ GetDebugger(), m_options.arch, plugin_name, flavor_string,
+ m_exe_ctx, cur_range.GetBaseAddress(), limit, m_options.show_mixed,
+ m_options.show_mixed ? m_options.num_lines_context : 0, options,
+ result.GetOutputStream())) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS) {
+ result.AppendErrorWithFormat(
+ "Failed to disassemble memory in function at 0x%8.8" PRIx64 ".\n",
+ m_options.symbol_containing_addr);
+ } else {
+ result.AppendErrorWithFormat(
+ "Failed to disassemble memory at 0x%8.8" PRIx64 ".\n",
+ cur_range.GetBaseAddress().GetLoadAddress(target));
+ }
+ }
+ if (print_sc_header)
+ result.GetOutputStream() << "\n";
+ }
+
+ return result.Succeeded();
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectDisassemble.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectDisassemble.h
new file mode 100644
index 000000000000..b5146863628d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectDisassemble.h
@@ -0,0 +1,96 @@
+//===-- CommandObjectDisassemble.h ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTDISASSEMBLE_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTDISASSEMBLE_H
+
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Utility/ArchSpec.h"
+
+namespace lldb_private {
+
+// CommandObjectDisassemble
+
+class CommandObjectDisassemble : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions();
+
+ ~CommandOptions() override;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override;
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override;
+
+ const char *GetPluginName() {
+ return (plugin_name.empty() ? nullptr : plugin_name.c_str());
+ }
+
+ const char *GetFlavorString() {
+ if (flavor_string.empty() || flavor_string == "default")
+ return nullptr;
+ return flavor_string.c_str();
+ }
+
+ Status OptionParsingFinished(ExecutionContext *execution_context) override;
+
+ bool show_mixed; // Show mixed source/assembly
+ bool show_bytes;
+ bool show_control_flow_kind;
+ uint32_t num_lines_context = 0;
+ uint32_t num_instructions = 0;
+ bool raw;
+ std::string func_name;
+ bool current_function = false;
+ lldb::addr_t start_addr = 0;
+ lldb::addr_t end_addr = 0;
+ bool at_pc = false;
+ bool frame_line = false;
+ std::string plugin_name;
+ std::string flavor_string;
+ ArchSpec arch;
+ bool some_location_specified = false; // If no location was specified, we'll
+ // select "at_pc". This should be set
+ // in SetOptionValue if anything the selects a location is set.
+ lldb::addr_t symbol_containing_addr = 0;
+ bool force = false;
+ };
+
+ CommandObjectDisassemble(CommandInterpreter &interpreter);
+
+ ~CommandObjectDisassemble() override;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override;
+
+ llvm::Expected<std::vector<AddressRange>>
+ GetRangesForSelectedMode(CommandReturnObject &result);
+
+ llvm::Expected<std::vector<AddressRange>> GetContainingAddressRanges();
+ llvm::Expected<std::vector<AddressRange>> GetCurrentFunctionRanges();
+ llvm::Expected<std::vector<AddressRange>> GetCurrentLineRanges();
+ llvm::Expected<std::vector<AddressRange>>
+ GetNameRanges(CommandReturnObject &result);
+ llvm::Expected<std::vector<AddressRange>> GetPCRanges();
+ llvm::Expected<std::vector<AddressRange>> GetStartEndAddressRanges();
+
+ llvm::Error CheckRangeSize(const AddressRange &range, llvm::StringRef what);
+
+ CommandOptions m_options;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTDISASSEMBLE_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectExpression.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectExpression.cpp
new file mode 100644
index 000000000000..59c2cdb0938c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectExpression.cpp
@@ -0,0 +1,663 @@
+//===-- CommandObjectExpression.cpp ---------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/StringRef.h"
+
+#include "CommandObjectExpression.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Expression/REPL.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+CommandObjectExpression::CommandOptions::CommandOptions() = default;
+
+CommandObjectExpression::CommandOptions::~CommandOptions() = default;
+
+static constexpr OptionEnumValueElement g_description_verbosity_type[] = {
+ {
+ eLanguageRuntimeDescriptionDisplayVerbosityCompact,
+ "compact",
+ "Only show the description string",
+ },
+ {
+ eLanguageRuntimeDescriptionDisplayVerbosityFull,
+ "full",
+ "Show the full output, including persistent variable's name and type",
+ },
+};
+
+static constexpr OptionEnumValues DescriptionVerbosityTypes() {
+ return OptionEnumValues(g_description_verbosity_type);
+}
+
+#define LLDB_OPTIONS_expression
+#include "CommandOptions.inc"
+
+Status CommandObjectExpression::CommandOptions::SetOptionValue(
+ uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+
+ const int short_option = GetDefinitions()[option_idx].short_option;
+
+ switch (short_option) {
+ case 'l':
+ language = Language::GetLanguageTypeFromString(option_arg);
+ if (language == eLanguageTypeUnknown)
+ error.SetErrorStringWithFormat(
+ "unknown language type: '%s' for expression",
+ option_arg.str().c_str());
+ break;
+
+ case 'a': {
+ bool success;
+ bool result;
+ result = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "invalid all-threads value setting: \"%s\"",
+ option_arg.str().c_str());
+ else
+ try_all_threads = result;
+ } break;
+
+ case 'i': {
+ bool success;
+ bool tmp_value = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (success)
+ ignore_breakpoints = tmp_value;
+ else
+ error.SetErrorStringWithFormat(
+ "could not convert \"%s\" to a boolean value.",
+ option_arg.str().c_str());
+ break;
+ }
+
+ case 'j': {
+ bool success;
+ bool tmp_value = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (success)
+ allow_jit = tmp_value;
+ else
+ error.SetErrorStringWithFormat(
+ "could not convert \"%s\" to a boolean value.",
+ option_arg.str().c_str());
+ break;
+ }
+
+ case 't':
+ if (option_arg.getAsInteger(0, timeout)) {
+ timeout = 0;
+ error.SetErrorStringWithFormat("invalid timeout setting \"%s\"",
+ option_arg.str().c_str());
+ }
+ break;
+
+ case 'u': {
+ bool success;
+ bool tmp_value = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (success)
+ unwind_on_error = tmp_value;
+ else
+ error.SetErrorStringWithFormat(
+ "could not convert \"%s\" to a boolean value.",
+ option_arg.str().c_str());
+ break;
+ }
+
+ case 'v':
+ if (option_arg.empty()) {
+ m_verbosity = eLanguageRuntimeDescriptionDisplayVerbosityFull;
+ break;
+ }
+ m_verbosity = (LanguageRuntimeDescriptionDisplayVerbosity)
+ OptionArgParser::ToOptionEnum(
+ option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
+ if (!error.Success())
+ error.SetErrorStringWithFormat(
+ "unrecognized value for description-verbosity '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'g':
+ debug = true;
+ unwind_on_error = false;
+ ignore_breakpoints = false;
+ break;
+
+ case 'p':
+ top_level = true;
+ break;
+
+ case 'X': {
+ bool success;
+ bool tmp_value = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (success)
+ auto_apply_fixits = tmp_value ? eLazyBoolYes : eLazyBoolNo;
+ else
+ error.SetErrorStringWithFormat(
+ "could not convert \"%s\" to a boolean value.",
+ option_arg.str().c_str());
+ break;
+ }
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+}
+
+void CommandObjectExpression::CommandOptions::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ auto process_sp =
+ execution_context ? execution_context->GetProcessSP() : ProcessSP();
+ if (process_sp) {
+ ignore_breakpoints = process_sp->GetIgnoreBreakpointsInExpressions();
+ unwind_on_error = process_sp->GetUnwindOnErrorInExpressions();
+ } else {
+ ignore_breakpoints = true;
+ unwind_on_error = true;
+ }
+
+ show_summary = true;
+ try_all_threads = true;
+ timeout = 0;
+ debug = false;
+ language = eLanguageTypeUnknown;
+ m_verbosity = eLanguageRuntimeDescriptionDisplayVerbosityCompact;
+ auto_apply_fixits = eLazyBoolCalculate;
+ top_level = false;
+ allow_jit = true;
+}
+
+llvm::ArrayRef<OptionDefinition>
+CommandObjectExpression::CommandOptions::GetDefinitions() {
+ return llvm::makeArrayRef(g_expression_options);
+}
+
+CommandObjectExpression::CommandObjectExpression(
+ CommandInterpreter &interpreter)
+ : CommandObjectRaw(interpreter, "expression",
+ "Evaluate an expression on the current "
+ "thread. Displays any returned value "
+ "with LLDB's default formatting.",
+ "",
+ eCommandProcessMustBePaused | eCommandTryTargetAPILock),
+ IOHandlerDelegate(IOHandlerDelegate::Completion::Expression),
+ m_format_options(eFormatDefault),
+ m_repl_option(LLDB_OPT_SET_1, false, "repl", 'r', "Drop into REPL", false,
+ true),
+ m_command_options(), m_expr_line_count(0) {
+ SetHelpLong(
+ R"(
+Single and multi-line expressions:
+
+)"
+ " The expression provided on the command line must be a complete expression \
+with no newlines. To evaluate a multi-line expression, \
+hit a return after an empty expression, and lldb will enter the multi-line expression editor. \
+Hit return on an empty line to end the multi-line expression."
+
+ R"(
+
+Timeouts:
+
+)"
+ " If the expression can be evaluated statically (without running code) then it will be. \
+Otherwise, by default the expression will run on the current thread with a short timeout: \
+currently .25 seconds. If it doesn't return in that time, the evaluation will be interrupted \
+and resumed with all threads running. You can use the -a option to disable retrying on all \
+threads. You can use the -t option to set a shorter timeout."
+ R"(
+
+User defined variables:
+
+)"
+ " You can define your own variables for convenience or to be used in subsequent expressions. \
+You define them the same way you would define variables in C. If the first character of \
+your user defined variable is a $, then the variable's value will be available in future \
+expressions, otherwise it will just be available in the current expression."
+ R"(
+
+Continuing evaluation after a breakpoint:
+
+)"
+ " If the \"-i false\" option is used, and execution is interrupted by a breakpoint hit, once \
+you are done with your investigation, you can either remove the expression execution frames \
+from the stack with \"thread return -x\" or if you are still interested in the expression result \
+you can issue the \"continue\" command and the expression evaluation will complete and the \
+expression result will be available using the \"thread.completed-expression\" key in the thread \
+format."
+
+ R"(
+
+Examples:
+
+ expr my_struct->a = my_array[3]
+ expr -f bin -- (index * 8) + 5
+ expr unsigned int $foo = 5
+ expr char c[] = \"foo\"; c[0])");
+
+ CommandArgumentEntry arg;
+ CommandArgumentData expression_arg;
+
+ // Define the first (and only) variant of this arg.
+ expression_arg.arg_type = eArgTypeExpression;
+ expression_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the argument
+ // entry.
+ arg.push_back(expression_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+
+ // Add the "--format" and "--gdb-format"
+ m_option_group.Append(&m_format_options,
+ OptionGroupFormat::OPTION_GROUP_FORMAT |
+ OptionGroupFormat::OPTION_GROUP_GDB_FMT,
+ LLDB_OPT_SET_1);
+ m_option_group.Append(&m_command_options);
+ m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL,
+ LLDB_OPT_SET_1 | LLDB_OPT_SET_2);
+ m_option_group.Append(&m_repl_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_3);
+ m_option_group.Finalize();
+}
+
+CommandObjectExpression::~CommandObjectExpression() = default;
+
+Options *CommandObjectExpression::GetOptions() { return &m_option_group; }
+
+void CommandObjectExpression::HandleCompletion(CompletionRequest &request) {
+ EvaluateExpressionOptions options;
+ options.SetCoerceToId(m_varobj_options.use_objc);
+ options.SetLanguage(m_command_options.language);
+ options.SetExecutionPolicy(lldb_private::eExecutionPolicyNever);
+ options.SetAutoApplyFixIts(false);
+ options.SetGenerateDebugInfo(false);
+
+ ExecutionContext exe_ctx(m_interpreter.GetExecutionContext());
+
+ // Get out before we start doing things that expect a valid frame pointer.
+ if (exe_ctx.GetFramePtr() == nullptr)
+ return;
+
+ Target *exe_target = exe_ctx.GetTargetPtr();
+ Target &target = exe_target ? *exe_target : GetDummyTarget();
+
+ unsigned cursor_pos = request.GetRawCursorPos();
+ // Get the full user input including the suffix. The suffix is necessary
+ // as OptionsWithRaw will use it to detect if the cursor is cursor is in the
+ // argument part of in the raw input part of the arguments. If we cut of
+ // of the suffix then "expr -arg[cursor] --" would interpret the "-arg" as
+ // the raw input (as the "--" is hidden in the suffix).
+ llvm::StringRef code = request.GetRawLineWithUnusedSuffix();
+
+ const std::size_t original_code_size = code.size();
+
+ // Remove the first token which is 'expr' or some alias/abbreviation of that.
+ code = llvm::getToken(code).second.ltrim();
+ OptionsWithRaw args(code);
+ code = args.GetRawPart();
+
+ // The position where the expression starts in the command line.
+ assert(original_code_size >= code.size());
+ std::size_t raw_start = original_code_size - code.size();
+
+ // Check if the cursor is actually in the expression string, and if not, we
+ // exit.
+ // FIXME: We should complete the options here.
+ if (cursor_pos < raw_start)
+ return;
+
+ // Make the cursor_pos again relative to the start of the code string.
+ assert(cursor_pos >= raw_start);
+ cursor_pos -= raw_start;
+
+ auto language = exe_ctx.GetFrameRef().GetLanguage();
+
+ Status error;
+ lldb::UserExpressionSP expr(target.GetUserExpressionForLanguage(
+ code, llvm::StringRef(), language, UserExpression::eResultTypeAny,
+ options, nullptr, error));
+ if (error.Fail())
+ return;
+
+ expr->Complete(exe_ctx, request, cursor_pos);
+}
+
+static lldb_private::Status
+CanBeUsedForElementCountPrinting(ValueObject &valobj) {
+ CompilerType type(valobj.GetCompilerType());
+ CompilerType pointee;
+ if (!type.IsPointerType(&pointee))
+ return Status("as it does not refer to a pointer");
+ if (pointee.IsVoidType())
+ return Status("as it refers to a pointer to void");
+ return Status();
+}
+
+EvaluateExpressionOptions
+CommandObjectExpression::GetEvalOptions(const Target &target) {
+ EvaluateExpressionOptions options;
+ options.SetCoerceToId(m_varobj_options.use_objc);
+ options.SetUnwindOnError(m_command_options.unwind_on_error);
+ options.SetIgnoreBreakpoints(m_command_options.ignore_breakpoints);
+ options.SetKeepInMemory(true);
+ options.SetUseDynamic(m_varobj_options.use_dynamic);
+ options.SetTryAllThreads(m_command_options.try_all_threads);
+ options.SetDebug(m_command_options.debug);
+ options.SetLanguage(m_command_options.language);
+ options.SetExecutionPolicy(
+ m_command_options.allow_jit
+ ? EvaluateExpressionOptions::default_execution_policy
+ : lldb_private::eExecutionPolicyNever);
+
+ bool auto_apply_fixits;
+ if (m_command_options.auto_apply_fixits == eLazyBoolCalculate)
+ auto_apply_fixits = target.GetEnableAutoApplyFixIts();
+ else
+ auto_apply_fixits = m_command_options.auto_apply_fixits == eLazyBoolYes;
+
+ options.SetAutoApplyFixIts(auto_apply_fixits);
+ options.SetRetriesWithFixIts(target.GetNumberOfRetriesWithFixits());
+
+ if (m_command_options.top_level)
+ options.SetExecutionPolicy(eExecutionPolicyTopLevel);
+
+ // If there is any chance we are going to stop and want to see what went
+ // wrong with our expression, we should generate debug info
+ if (!m_command_options.ignore_breakpoints ||
+ !m_command_options.unwind_on_error)
+ options.SetGenerateDebugInfo(true);
+
+ if (m_command_options.timeout > 0)
+ options.SetTimeout(std::chrono::microseconds(m_command_options.timeout));
+ else
+ options.SetTimeout(llvm::None);
+ return options;
+}
+
+bool CommandObjectExpression::EvaluateExpression(llvm::StringRef expr,
+ Stream &output_stream,
+ Stream &error_stream,
+ CommandReturnObject &result) {
+ // Don't use m_exe_ctx as this might be called asynchronously after the
+ // command object DoExecute has finished when doing multi-line expression
+ // that use an input reader...
+ ExecutionContext exe_ctx(m_interpreter.GetExecutionContext());
+ Target *exe_target = exe_ctx.GetTargetPtr();
+ Target &target = exe_target ? *exe_target : GetDummyTarget();
+
+ lldb::ValueObjectSP result_valobj_sp;
+ StackFrame *frame = exe_ctx.GetFramePtr();
+
+ if (m_command_options.top_level && !m_command_options.allow_jit) {
+ result.AppendErrorWithFormat(
+ "Can't disable JIT compilation for top-level expressions.\n");
+ return false;
+ }
+
+ const EvaluateExpressionOptions options = GetEvalOptions(target);
+ ExpressionResults success = target.EvaluateExpression(
+ expr, frame, result_valobj_sp, options, &m_fixed_expression);
+
+ // We only tell you about the FixIt if we applied it. The compiler errors
+ // will suggest the FixIt if it parsed.
+ if (!m_fixed_expression.empty() && target.GetEnableNotifyAboutFixIts()) {
+ error_stream.Printf(" Fix-it applied, fixed expression was: \n %s\n",
+ m_fixed_expression.c_str());
+ }
+
+ if (result_valobj_sp) {
+ Format format = m_format_options.GetFormat();
+
+ if (result_valobj_sp->GetError().Success()) {
+ if (format != eFormatVoid) {
+ if (format != eFormatDefault)
+ result_valobj_sp->SetFormat(format);
+
+ if (m_varobj_options.elem_count > 0) {
+ Status error(CanBeUsedForElementCountPrinting(*result_valobj_sp));
+ if (error.Fail()) {
+ result.AppendErrorWithFormat(
+ "expression cannot be used with --element-count %s\n",
+ error.AsCString(""));
+ return false;
+ }
+ }
+
+ DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(
+ m_command_options.m_verbosity, format));
+ options.SetVariableFormatDisplayLanguage(
+ result_valobj_sp->GetPreferredDisplayLanguage());
+
+ result_valobj_sp->Dump(output_stream, options);
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+ } else {
+ if (result_valobj_sp->GetError().GetError() ==
+ UserExpression::kNoResult) {
+ if (format != eFormatVoid && GetDebugger().GetNotifyVoid()) {
+ error_stream.PutCString("(void)\n");
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ const char *error_cstr = result_valobj_sp->GetError().AsCString();
+ if (error_cstr && error_cstr[0]) {
+ const size_t error_cstr_len = strlen(error_cstr);
+ const bool ends_with_newline = error_cstr[error_cstr_len - 1] == '\n';
+ if (strstr(error_cstr, "error:") != error_cstr)
+ error_stream.PutCString("error: ");
+ error_stream.Write(error_cstr, error_cstr_len);
+ if (!ends_with_newline)
+ error_stream.EOL();
+ } else {
+ error_stream.PutCString("error: unknown error\n");
+ }
+
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ }
+
+ return (success != eExpressionSetupError &&
+ success != eExpressionParseError);
+}
+
+void CommandObjectExpression::IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &line) {
+ io_handler.SetIsDone(true);
+ StreamFileSP output_sp = io_handler.GetOutputStreamFileSP();
+ StreamFileSP error_sp = io_handler.GetErrorStreamFileSP();
+
+ CommandReturnObject return_obj(
+ GetCommandInterpreter().GetDebugger().GetUseColor());
+ EvaluateExpression(line.c_str(), *output_sp, *error_sp, return_obj);
+ if (output_sp)
+ output_sp->Flush();
+ if (error_sp)
+ error_sp->Flush();
+}
+
+bool CommandObjectExpression::IOHandlerIsInputComplete(IOHandler &io_handler,
+ StringList &lines) {
+ // An empty lines is used to indicate the end of input
+ const size_t num_lines = lines.GetSize();
+ if (num_lines > 0 && lines[num_lines - 1].empty()) {
+ // Remove the last empty line from "lines" so it doesn't appear in our
+ // resulting input and return true to indicate we are done getting lines
+ lines.PopBack();
+ return true;
+ }
+ return false;
+}
+
+void CommandObjectExpression::GetMultilineExpression() {
+ m_expr_lines.clear();
+ m_expr_line_count = 0;
+
+ Debugger &debugger = GetCommandInterpreter().GetDebugger();
+ bool color_prompt = debugger.GetUseColor();
+ const bool multiple_lines = true; // Get multiple lines
+ IOHandlerSP io_handler_sp(
+ new IOHandlerEditline(debugger, IOHandler::Type::Expression,
+ "lldb-expr", // Name of input reader for history
+ llvm::StringRef(), // No prompt
+ llvm::StringRef(), // Continuation prompt
+ multiple_lines, color_prompt,
+ 1, // Show line numbers starting at 1
+ *this, nullptr));
+
+ StreamFileSP output_sp = io_handler_sp->GetOutputStreamFileSP();
+ if (output_sp) {
+ output_sp->PutCString(
+ "Enter expressions, then terminate with an empty line to evaluate:\n");
+ output_sp->Flush();
+ }
+ debugger.RunIOHandlerAsync(io_handler_sp);
+}
+
+static EvaluateExpressionOptions
+GetExprOptions(ExecutionContext &ctx,
+ CommandObjectExpression::CommandOptions command_options) {
+ command_options.OptionParsingStarting(&ctx);
+
+ // Default certain settings for REPL regardless of the global settings.
+ command_options.unwind_on_error = false;
+ command_options.ignore_breakpoints = false;
+ command_options.debug = false;
+
+ EvaluateExpressionOptions expr_options;
+ expr_options.SetUnwindOnError(command_options.unwind_on_error);
+ expr_options.SetIgnoreBreakpoints(command_options.ignore_breakpoints);
+ expr_options.SetTryAllThreads(command_options.try_all_threads);
+
+ if (command_options.timeout > 0)
+ expr_options.SetTimeout(std::chrono::microseconds(command_options.timeout));
+ else
+ expr_options.SetTimeout(llvm::None);
+
+ return expr_options;
+}
+
+bool CommandObjectExpression::DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) {
+ m_fixed_expression.clear();
+ auto exe_ctx = GetCommandInterpreter().GetExecutionContext();
+ m_option_group.NotifyOptionParsingStarting(&exe_ctx);
+
+ if (command.empty()) {
+ GetMultilineExpression();
+ return result.Succeeded();
+ }
+
+ OptionsWithRaw args(command);
+ llvm::StringRef expr = args.GetRawPart();
+
+ if (args.HasArgs()) {
+ if (!ParseOptionsAndNotify(args.GetArgs(), result, m_option_group, exe_ctx))
+ return false;
+
+ if (m_repl_option.GetOptionValue().GetCurrentValue()) {
+ Target &target = GetSelectedOrDummyTarget();
+ // Drop into REPL
+ m_expr_lines.clear();
+ m_expr_line_count = 0;
+
+ Debugger &debugger = target.GetDebugger();
+
+ // Check if the LLDB command interpreter is sitting on top of a REPL
+ // that launched it...
+ if (debugger.CheckTopIOHandlerTypes(IOHandler::Type::CommandInterpreter,
+ IOHandler::Type::REPL)) {
+ // the LLDB command interpreter is sitting on top of a REPL that
+ // launched it, so just say the command interpreter is done and
+ // fall back to the existing REPL
+ m_interpreter.GetIOHandler(false)->SetIsDone(true);
+ } else {
+ // We are launching the REPL on top of the current LLDB command
+ // interpreter, so just push one
+ bool initialize = false;
+ Status repl_error;
+ REPLSP repl_sp(target.GetREPL(repl_error, m_command_options.language,
+ nullptr, false));
+
+ if (!repl_sp) {
+ initialize = true;
+ repl_sp = target.GetREPL(repl_error, m_command_options.language,
+ nullptr, true);
+ if (!repl_error.Success()) {
+ result.SetError(repl_error);
+ return result.Succeeded();
+ }
+ }
+
+ if (repl_sp) {
+ if (initialize) {
+ repl_sp->SetEvaluateOptions(
+ GetExprOptions(exe_ctx, m_command_options));
+ repl_sp->SetFormatOptions(m_format_options);
+ repl_sp->SetValueObjectDisplayOptions(m_varobj_options);
+ }
+
+ IOHandlerSP io_handler_sp(repl_sp->GetIOHandler());
+ io_handler_sp->SetIsDone(false);
+ debugger.RunIOHandlerAsync(io_handler_sp);
+ } else {
+ repl_error.SetErrorStringWithFormat(
+ "Couldn't create a REPL for %s",
+ Language::GetNameForLanguageType(m_command_options.language));
+ result.SetError(repl_error);
+ return result.Succeeded();
+ }
+ }
+ }
+ // No expression following options
+ else if (expr.empty()) {
+ GetMultilineExpression();
+ return result.Succeeded();
+ }
+ }
+
+ Target &target = GetSelectedOrDummyTarget();
+ if (EvaluateExpression(expr, result.GetOutputStream(),
+ result.GetErrorStream(), result)) {
+
+ if (!m_fixed_expression.empty() && target.GetEnableNotifyAboutFixIts()) {
+ CommandHistory &history = m_interpreter.GetCommandHistory();
+ // FIXME: Can we figure out what the user actually typed (e.g. some alias
+ // for expr???)
+ // If we can it would be nice to show that.
+ std::string fixed_command("expression ");
+ if (args.HasArgs()) {
+ // Add in any options that might have been in the original command:
+ fixed_command.append(std::string(args.GetArgStringWithDelimiter()));
+ fixed_command.append(m_fixed_expression);
+ } else
+ fixed_command.append(m_fixed_expression);
+ history.AppendString(fixed_command);
+ }
+ return true;
+ }
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectExpression.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectExpression.h
new file mode 100644
index 000000000000..1e59cbc14528
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectExpression.h
@@ -0,0 +1,101 @@
+//===-- CommandObjectExpression.h -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTEXPRESSION_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTEXPRESSION_H
+
+#include "lldb/Core/IOHandler.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/OptionGroupBoolean.h"
+#include "lldb/Interpreter/OptionGroupFormat.h"
+#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
+#include "lldb/Target/Target.h"
+#include "lldb/lldb-private-enumerations.h"
+
+namespace lldb_private {
+
+class CommandObjectExpression : public CommandObjectRaw,
+ public IOHandlerDelegate {
+public:
+ class CommandOptions : public OptionGroup {
+ public:
+ CommandOptions();
+
+ ~CommandOptions() override;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
+ ExecutionContext *execution_context) override;
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override;
+
+ bool top_level;
+ bool unwind_on_error;
+ bool ignore_breakpoints;
+ bool allow_jit;
+ bool show_types;
+ bool show_summary;
+ bool debug;
+ uint32_t timeout;
+ bool try_all_threads;
+ lldb::LanguageType language;
+ LanguageRuntimeDescriptionDisplayVerbosity m_verbosity;
+ LazyBool auto_apply_fixits;
+ };
+
+ CommandObjectExpression(CommandInterpreter &interpreter);
+
+ ~CommandObjectExpression() override;
+
+ Options *GetOptions() override;
+
+ void HandleCompletion(CompletionRequest &request) override;
+
+protected:
+ // IOHandler::Delegate functions
+ void IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &line) override;
+
+ bool IOHandlerIsInputComplete(IOHandler &io_handler,
+ StringList &lines) override;
+
+ bool DoExecute(llvm::StringRef command, CommandReturnObject &result) override;
+
+ /// Return the appropriate expression options used for evaluating the
+ /// expression in the given target.
+ EvaluateExpressionOptions GetEvalOptions(const Target &target);
+
+ /// Evaluates the given expression.
+ /// \param output_stream The stream to which the evaluation result will be
+ /// printed.
+ /// \param error_stream Contains error messages that should be displayed to
+ /// the user in case the evaluation fails.
+ /// \param result A CommandReturnObject which status will be set to the
+ /// appropriate value depending on evaluation success and
+ /// whether the expression produced any result.
+ /// \return Returns true iff the expression was successfully evaluated,
+ /// executed and the result could be printed to the output stream.
+ bool EvaluateExpression(llvm::StringRef expr, Stream &output_stream,
+ Stream &error_stream, CommandReturnObject &result);
+
+ void GetMultilineExpression();
+
+ OptionGroupOptions m_option_group;
+ OptionGroupFormat m_format_options;
+ OptionGroupValueObjectDisplay m_varobj_options;
+ OptionGroupBoolean m_repl_option;
+ CommandOptions m_command_options;
+ uint32_t m_expr_line_count;
+ std::string m_expr_lines; // Multi-line expression support
+ std::string m_fixed_expression; // Holds the current expression's fixed text.
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTEXPRESSION_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectFrame.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectFrame.cpp
new file mode 100644
index 000000000000..9f2c79d84ad4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectFrame.cpp
@@ -0,0 +1,1173 @@
+//===-- CommandObjectFrame.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "CommandObjectFrame.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/DataVisualization.h"
+#include "lldb/DataFormatters/ValueObjectPrinter.h"
+#include "lldb/Host/Config.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionGroupFormat.h"
+#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
+#include "lldb/Interpreter/OptionGroupVariable.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/StackFrameRecognizer.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Args.h"
+
+#include <memory>
+#include <string>
+
+using namespace lldb;
+using namespace lldb_private;
+
+#pragma mark CommandObjectFrameDiagnose
+
+// CommandObjectFrameInfo
+
+// CommandObjectFrameDiagnose
+
+#define LLDB_OPTIONS_frame_diag
+#include "CommandOptions.inc"
+
+class CommandObjectFrameDiagnose : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() { OptionParsingStarting(nullptr); }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+ switch (short_option) {
+ case 'r':
+ reg = ConstString(option_arg);
+ break;
+
+ case 'a': {
+ address.emplace();
+ if (option_arg.getAsInteger(0, *address)) {
+ address.reset();
+ error.SetErrorStringWithFormat("invalid address argument '%s'",
+ option_arg.str().c_str());
+ }
+ } break;
+
+ case 'o': {
+ offset.emplace();
+ if (option_arg.getAsInteger(0, *offset)) {
+ offset.reset();
+ error.SetErrorStringWithFormat("invalid offset argument '%s'",
+ option_arg.str().c_str());
+ }
+ } break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ address.reset();
+ reg.reset();
+ offset.reset();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_frame_diag_options);
+ }
+
+ // Options.
+ llvm::Optional<lldb::addr_t> address;
+ llvm::Optional<ConstString> reg;
+ llvm::Optional<int64_t> offset;
+ };
+
+ CommandObjectFrameDiagnose(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "frame diagnose",
+ "Try to determine what path the current stop "
+ "location used to get to a register or address",
+ nullptr,
+ eCommandRequiresThread | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused) {
+ CommandArgumentEntry arg;
+ CommandArgumentData index_arg;
+
+ // Define the first (and only) variant of this arg.
+ index_arg.arg_type = eArgTypeFrameIndex;
+ index_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(index_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectFrameDiagnose() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Thread *thread = m_exe_ctx.GetThreadPtr();
+ StackFrameSP frame_sp = thread->GetSelectedFrame();
+
+ ValueObjectSP valobj_sp;
+
+ if (m_options.address) {
+ if (m_options.reg || m_options.offset) {
+ result.AppendError(
+ "`frame diagnose --address` is incompatible with other arguments.");
+ return false;
+ }
+ valobj_sp = frame_sp->GuessValueForAddress(m_options.address.getValue());
+ } else if (m_options.reg) {
+ valobj_sp = frame_sp->GuessValueForRegisterAndOffset(
+ m_options.reg.getValue(), m_options.offset.value_or(0));
+ } else {
+ StopInfoSP stop_info_sp = thread->GetStopInfo();
+ if (!stop_info_sp) {
+ result.AppendError("No arguments provided, and no stop info.");
+ return false;
+ }
+
+ valobj_sp = StopInfo::GetCrashingDereference(stop_info_sp);
+ }
+
+ if (!valobj_sp) {
+ result.AppendError("No diagnosis available.");
+ return false;
+ }
+
+ DumpValueObjectOptions::DeclPrintingHelper helper =
+ [&valobj_sp](ConstString type, ConstString var,
+ const DumpValueObjectOptions &opts,
+ Stream &stream) -> bool {
+ const ValueObject::GetExpressionPathFormat format = ValueObject::
+ GetExpressionPathFormat::eGetExpressionPathFormatHonorPointers;
+ valobj_sp->GetExpressionPath(stream, format);
+ stream.PutCString(" =");
+ return true;
+ };
+
+ DumpValueObjectOptions options;
+ options.SetDeclPrintingHelper(helper);
+ ValueObjectPrinter printer(valobj_sp.get(), &result.GetOutputStream(),
+ options);
+ printer.PrintValueObject();
+
+ return true;
+ }
+
+ CommandOptions m_options;
+};
+
+#pragma mark CommandObjectFrameInfo
+
+// CommandObjectFrameInfo
+
+class CommandObjectFrameInfo : public CommandObjectParsed {
+public:
+ CommandObjectFrameInfo(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "frame info",
+ "List information about the current "
+ "stack frame in the current thread.",
+ "frame info",
+ eCommandRequiresFrame | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused) {}
+
+ ~CommandObjectFrameInfo() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ m_exe_ctx.GetFrameRef().DumpUsingSettingsFormat(&result.GetOutputStream());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectFrameSelect
+
+// CommandObjectFrameSelect
+
+#define LLDB_OPTIONS_frame_select
+#include "CommandOptions.inc"
+
+class CommandObjectFrameSelect : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() { OptionParsingStarting(nullptr); }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+ switch (short_option) {
+ case 'r': {
+ int32_t offset = 0;
+ if (option_arg.getAsInteger(0, offset) || offset == INT32_MIN) {
+ error.SetErrorStringWithFormat("invalid frame offset argument '%s'",
+ option_arg.str().c_str());
+ } else
+ relative_frame_offset = offset;
+ break;
+ }
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ relative_frame_offset.reset();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_frame_select_options);
+ }
+
+ llvm::Optional<int32_t> relative_frame_offset;
+ };
+
+ CommandObjectFrameSelect(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "frame select",
+ "Select the current stack frame by "
+ "index from within the current thread "
+ "(see 'thread backtrace'.)",
+ nullptr,
+ eCommandRequiresThread | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused) {
+ CommandArgumentEntry arg;
+ CommandArgumentData index_arg;
+
+ // Define the first (and only) variant of this arg.
+ index_arg.arg_type = eArgTypeFrameIndex;
+ index_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(index_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectFrameSelect() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (request.GetCursorIndex() != 0)
+ return;
+
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eFrameIndexCompletion,
+ request, nullptr);
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ // No need to check "thread" for validity as eCommandRequiresThread ensures
+ // it is valid
+ Thread *thread = m_exe_ctx.GetThreadPtr();
+
+ uint32_t frame_idx = UINT32_MAX;
+ if (m_options.relative_frame_offset) {
+ // The one and only argument is a signed relative frame index
+ frame_idx = thread->GetSelectedFrameIndex();
+ if (frame_idx == UINT32_MAX)
+ frame_idx = 0;
+
+ if (*m_options.relative_frame_offset < 0) {
+ if (static_cast<int32_t>(frame_idx) >=
+ -*m_options.relative_frame_offset)
+ frame_idx += *m_options.relative_frame_offset;
+ else {
+ if (frame_idx == 0) {
+ // If you are already at the bottom of the stack, then just warn
+ // and don't reset the frame.
+ result.AppendError("Already at the bottom of the stack.");
+ return false;
+ } else
+ frame_idx = 0;
+ }
+ } else if (*m_options.relative_frame_offset > 0) {
+ // I don't want "up 20" where "20" takes you past the top of the stack
+ // to produce
+ // an error, but rather to just go to the top. So I have to count the
+ // stack here...
+ const uint32_t num_frames = thread->GetStackFrameCount();
+ if (static_cast<int32_t>(num_frames - frame_idx) >
+ *m_options.relative_frame_offset)
+ frame_idx += *m_options.relative_frame_offset;
+ else {
+ if (frame_idx == num_frames - 1) {
+ // If we are already at the top of the stack, just warn and don't
+ // reset the frame.
+ result.AppendError("Already at the top of the stack.");
+ return false;
+ } else
+ frame_idx = num_frames - 1;
+ }
+ }
+ } else {
+ if (command.GetArgumentCount() > 1) {
+ result.AppendErrorWithFormat(
+ "too many arguments; expected frame-index, saw '%s'.\n",
+ command[0].c_str());
+ m_options.GenerateOptionUsage(
+ result.GetErrorStream(), *this,
+ GetCommandInterpreter().GetDebugger().GetTerminalWidth());
+ return false;
+ }
+
+ if (command.GetArgumentCount() == 1) {
+ if (command[0].ref().getAsInteger(0, frame_idx)) {
+ result.AppendErrorWithFormat("invalid frame index argument '%s'.",
+ command[0].c_str());
+ return false;
+ }
+ } else if (command.GetArgumentCount() == 0) {
+ frame_idx = thread->GetSelectedFrameIndex();
+ if (frame_idx == UINT32_MAX) {
+ frame_idx = 0;
+ }
+ }
+ }
+
+ bool success = thread->SetSelectedFrameByIndexNoisily(
+ frame_idx, result.GetOutputStream());
+ if (success) {
+ m_exe_ctx.SetFrameSP(thread->GetSelectedFrame());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("Frame index (%u) out of range.\n",
+ frame_idx);
+ }
+
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+#pragma mark CommandObjectFrameVariable
+// List images with associated information
+class CommandObjectFrameVariable : public CommandObjectParsed {
+public:
+ CommandObjectFrameVariable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "frame variable",
+ "Show variables for the current stack frame. Defaults to all "
+ "arguments and local variables in scope. Names of argument, "
+ "local, file static and file global variables can be specified.",
+ nullptr,
+ eCommandRequiresFrame | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused |
+ eCommandRequiresProcess),
+ m_option_variable(
+ true), // Include the frame specific options by passing "true"
+ m_option_format(eFormatDefault) {
+ SetHelpLong(R"(
+Children of aggregate variables can be specified such as 'var->child.x'. In
+'frame variable', the operators -> and [] do not invoke operator overloads if
+they exist, but directly access the specified element. If you want to trigger
+operator overloads use the expression command to print the variable instead.
+
+It is worth noting that except for overloaded operators, when printing local
+variables 'expr local_var' and 'frame var local_var' produce the same results.
+However, 'frame variable' is more efficient, since it uses debug information and
+memory reads directly, rather than parsing and evaluating an expression, which
+may even involve JITing and running code in the target program.)");
+
+ CommandArgumentEntry arg;
+ CommandArgumentData var_name_arg;
+
+ // Define the first (and only) variant of this arg.
+ var_name_arg.arg_type = eArgTypeVarName;
+ var_name_arg.arg_repetition = eArgRepeatStar;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(var_name_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+
+ m_option_group.Append(&m_option_variable, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_option_format,
+ OptionGroupFormat::OPTION_GROUP_FORMAT |
+ OptionGroupFormat::OPTION_GROUP_GDB_FMT,
+ LLDB_OPT_SET_1);
+ m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectFrameVariable() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ // Arguments are the standard source file completer.
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eVariablePathCompletion,
+ request, nullptr);
+ }
+
+protected:
+ llvm::StringRef GetScopeString(VariableSP var_sp) {
+ if (!var_sp)
+ return llvm::StringRef();
+
+ switch (var_sp->GetScope()) {
+ case eValueTypeVariableGlobal:
+ return "GLOBAL: ";
+ case eValueTypeVariableStatic:
+ return "STATIC: ";
+ case eValueTypeVariableArgument:
+ return "ARG: ";
+ case eValueTypeVariableLocal:
+ return "LOCAL: ";
+ case eValueTypeVariableThreadLocal:
+ return "THREAD: ";
+ default:
+ break;
+ }
+
+ return llvm::StringRef();
+ }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ // No need to check "frame" for validity as eCommandRequiresFrame ensures
+ // it is valid
+ StackFrame *frame = m_exe_ctx.GetFramePtr();
+
+ Stream &s = result.GetOutputStream();
+
+ // Be careful about the stack frame, if any summary formatter runs code, it
+ // might clear the StackFrameList for the thread. So hold onto a shared
+ // pointer to the frame so it stays alive.
+
+ VariableList *variable_list =
+ frame->GetVariableList(m_option_variable.show_globals);
+
+ VariableSP var_sp;
+ ValueObjectSP valobj_sp;
+
+ TypeSummaryImplSP summary_format_sp;
+ if (!m_option_variable.summary.IsCurrentValueEmpty())
+ DataVisualization::NamedSummaryFormats::GetSummaryFormat(
+ ConstString(m_option_variable.summary.GetCurrentValue()),
+ summary_format_sp);
+ else if (!m_option_variable.summary_string.IsCurrentValueEmpty())
+ summary_format_sp = std::make_shared<StringSummaryFormat>(
+ TypeSummaryImpl::Flags(),
+ m_option_variable.summary_string.GetCurrentValue());
+
+ DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(
+ eLanguageRuntimeDescriptionDisplayVerbosityFull, eFormatDefault,
+ summary_format_sp));
+
+ const SymbolContext &sym_ctx =
+ frame->GetSymbolContext(eSymbolContextFunction);
+ if (sym_ctx.function && sym_ctx.function->IsTopLevelFunction())
+ m_option_variable.show_globals = true;
+
+ if (variable_list) {
+ const Format format = m_option_format.GetFormat();
+ options.SetFormat(format);
+
+ if (!command.empty()) {
+ VariableList regex_var_list;
+
+ // If we have any args to the variable command, we will make variable
+ // objects from them...
+ for (auto &entry : command) {
+ if (m_option_variable.use_regex) {
+ const size_t regex_start_index = regex_var_list.GetSize();
+ llvm::StringRef name_str = entry.ref();
+ RegularExpression regex(name_str);
+ if (regex.IsValid()) {
+ size_t num_matches = 0;
+ const size_t num_new_regex_vars =
+ variable_list->AppendVariablesIfUnique(regex, regex_var_list,
+ num_matches);
+ if (num_new_regex_vars > 0) {
+ for (size_t regex_idx = regex_start_index,
+ end_index = regex_var_list.GetSize();
+ regex_idx < end_index; ++regex_idx) {
+ var_sp = regex_var_list.GetVariableAtIndex(regex_idx);
+ if (var_sp) {
+ valobj_sp = frame->GetValueObjectForFrameVariable(
+ var_sp, m_varobj_options.use_dynamic);
+ if (valobj_sp) {
+ std::string scope_string;
+ if (m_option_variable.show_scope)
+ scope_string = GetScopeString(var_sp).str();
+
+ if (!scope_string.empty())
+ s.PutCString(scope_string);
+
+ if (m_option_variable.show_decl &&
+ var_sp->GetDeclaration().GetFile()) {
+ bool show_fullpaths = false;
+ bool show_module = true;
+ if (var_sp->DumpDeclaration(&s, show_fullpaths,
+ show_module))
+ s.PutCString(": ");
+ }
+ valobj_sp->Dump(result.GetOutputStream(), options);
+ }
+ }
+ }
+ } else if (num_matches == 0) {
+ result.AppendErrorWithFormat(
+ "no variables matched the regular expression '%s'.",
+ entry.c_str());
+ }
+ } else {
+ if (llvm::Error err = regex.GetError())
+ result.AppendError(llvm::toString(std::move(err)));
+ else
+ result.AppendErrorWithFormat(
+ "unknown regex error when compiling '%s'", entry.c_str());
+ }
+ } else // No regex, either exact variable names or variable
+ // expressions.
+ {
+ Status error;
+ uint32_t expr_path_options =
+ StackFrame::eExpressionPathOptionCheckPtrVsMember |
+ StackFrame::eExpressionPathOptionsAllowDirectIVarAccess |
+ StackFrame::eExpressionPathOptionsInspectAnonymousUnions;
+ lldb::VariableSP var_sp;
+ valobj_sp = frame->GetValueForVariableExpressionPath(
+ entry.ref(), m_varobj_options.use_dynamic, expr_path_options,
+ var_sp, error);
+ if (valobj_sp) {
+ std::string scope_string;
+ if (m_option_variable.show_scope)
+ scope_string = GetScopeString(var_sp).str();
+
+ if (!scope_string.empty())
+ s.PutCString(scope_string);
+ if (m_option_variable.show_decl && var_sp &&
+ var_sp->GetDeclaration().GetFile()) {
+ var_sp->GetDeclaration().DumpStopContext(&s, false);
+ s.PutCString(": ");
+ }
+
+ options.SetFormat(format);
+ options.SetVariableFormatDisplayLanguage(
+ valobj_sp->GetPreferredDisplayLanguage());
+
+ Stream &output_stream = result.GetOutputStream();
+ options.SetRootValueObjectName(
+ valobj_sp->GetParent() ? entry.c_str() : nullptr);
+ valobj_sp->Dump(output_stream, options);
+ } else {
+ if (auto error_cstr = error.AsCString(nullptr))
+ result.AppendError(error_cstr);
+ else
+ result.AppendErrorWithFormat(
+ "unable to find any variable expression path that matches "
+ "'%s'.",
+ entry.c_str());
+ }
+ }
+ }
+ } else // No command arg specified. Use variable_list, instead.
+ {
+ const size_t num_variables = variable_list->GetSize();
+ if (num_variables > 0) {
+ for (size_t i = 0; i < num_variables; i++) {
+ var_sp = variable_list->GetVariableAtIndex(i);
+ switch (var_sp->GetScope()) {
+ case eValueTypeVariableGlobal:
+ if (!m_option_variable.show_globals)
+ continue;
+ break;
+ case eValueTypeVariableStatic:
+ if (!m_option_variable.show_globals)
+ continue;
+ break;
+ case eValueTypeVariableArgument:
+ if (!m_option_variable.show_args)
+ continue;
+ break;
+ case eValueTypeVariableLocal:
+ if (!m_option_variable.show_locals)
+ continue;
+ break;
+ default:
+ continue;
+ break;
+ }
+ std::string scope_string;
+ if (m_option_variable.show_scope)
+ scope_string = GetScopeString(var_sp).str();
+
+ // Use the variable object code to make sure we are using the same
+ // APIs as the public API will be using...
+ valobj_sp = frame->GetValueObjectForFrameVariable(
+ var_sp, m_varobj_options.use_dynamic);
+ if (valobj_sp) {
+ // When dumping all variables, don't print any variables that are
+ // not in scope to avoid extra unneeded output
+ if (valobj_sp->IsInScope()) {
+ if (!valobj_sp->GetTargetSP()
+ ->GetDisplayRuntimeSupportValues() &&
+ valobj_sp->IsRuntimeSupportValue())
+ continue;
+
+ if (!scope_string.empty())
+ s.PutCString(scope_string);
+
+ if (m_option_variable.show_decl &&
+ var_sp->GetDeclaration().GetFile()) {
+ var_sp->GetDeclaration().DumpStopContext(&s, false);
+ s.PutCString(": ");
+ }
+
+ options.SetFormat(format);
+ options.SetVariableFormatDisplayLanguage(
+ valobj_sp->GetPreferredDisplayLanguage());
+ options.SetRootValueObjectName(
+ var_sp ? var_sp->GetName().AsCString() : nullptr);
+ valobj_sp->Dump(result.GetOutputStream(), options);
+ }
+ }
+ }
+ }
+ }
+ if (result.GetStatus() != eReturnStatusFailed)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+
+ if (m_option_variable.show_recognized_args) {
+ auto recognized_frame = frame->GetRecognizedFrame();
+ if (recognized_frame) {
+ ValueObjectListSP recognized_arg_list =
+ recognized_frame->GetRecognizedArguments();
+ if (recognized_arg_list) {
+ for (auto &rec_value_sp : recognized_arg_list->GetObjects()) {
+ options.SetFormat(m_option_format.GetFormat());
+ options.SetVariableFormatDisplayLanguage(
+ rec_value_sp->GetPreferredDisplayLanguage());
+ options.SetRootValueObjectName(rec_value_sp->GetName().AsCString());
+ rec_value_sp->Dump(result.GetOutputStream(), options);
+ }
+ }
+ }
+ }
+
+ m_interpreter.PrintWarningsIfNecessary(result.GetOutputStream(),
+ m_cmd_name);
+
+ // Increment statistics.
+ bool res = result.Succeeded();
+ TargetStats &target_stats = GetSelectedOrDummyTarget().GetStatistics();
+ if (res)
+ target_stats.GetFrameVariableStats().NotifySuccess();
+ else
+ target_stats.GetFrameVariableStats().NotifyFailure();
+ return res;
+ }
+
+ OptionGroupOptions m_option_group;
+ OptionGroupVariable m_option_variable;
+ OptionGroupFormat m_option_format;
+ OptionGroupValueObjectDisplay m_varobj_options;
+};
+
+#pragma mark CommandObjectFrameRecognizer
+
+#define LLDB_OPTIONS_frame_recognizer_add
+#include "CommandOptions.inc"
+
+class CommandObjectFrameRecognizerAdd : public CommandObjectParsed {
+private:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'f': {
+ bool value, success;
+ value = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (success) {
+ m_first_instruction_only = value;
+ } else {
+ error.SetErrorStringWithFormat(
+ "invalid boolean value '%s' passed for -f option",
+ option_arg.str().c_str());
+ }
+ } break;
+ case 'l':
+ m_class_name = std::string(option_arg);
+ break;
+ case 's':
+ m_module = std::string(option_arg);
+ break;
+ case 'n':
+ m_symbols.push_back(std::string(option_arg));
+ break;
+ case 'x':
+ m_regex = true;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_module = "";
+ m_symbols.clear();
+ m_class_name = "";
+ m_regex = false;
+ m_first_instruction_only = true;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_frame_recognizer_add_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ std::string m_class_name;
+ std::string m_module;
+ std::vector<std::string> m_symbols;
+ bool m_regex;
+ bool m_first_instruction_only;
+ };
+
+ CommandOptions m_options;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override;
+
+public:
+ CommandObjectFrameRecognizerAdd(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "frame recognizer add",
+ "Add a new frame recognizer.", nullptr) {
+ SetHelpLong(R"(
+Frame recognizers allow for retrieving information about special frames based on
+ABI, arguments or other special properties of that frame, even without source
+code or debug info. Currently, one use case is to extract function arguments
+that would otherwise be unaccesible, or augment existing arguments.
+
+Adding a custom frame recognizer is possible by implementing a Python class
+and using the 'frame recognizer add' command. The Python class should have a
+'get_recognized_arguments' method and it will receive an argument of type
+lldb.SBFrame representing the current frame that we are trying to recognize.
+The method should return a (possibly empty) list of lldb.SBValue objects that
+represent the recognized arguments.
+
+An example of a recognizer that retrieves the file descriptor values from libc
+functions 'read', 'write' and 'close' follows:
+
+ class LibcFdRecognizer(object):
+ def get_recognized_arguments(self, frame):
+ if frame.name in ["read", "write", "close"]:
+ fd = frame.EvaluateExpression("$arg1").unsigned
+ value = lldb.target.CreateValueFromExpression("fd", "(int)%d" % fd)
+ return [value]
+ return []
+
+The file containing this implementation can be imported via 'command script
+import' and then we can register this recognizer with 'frame recognizer add'.
+It's important to restrict the recognizer to the libc library (which is
+libsystem_kernel.dylib on macOS) to avoid matching functions with the same name
+in other modules:
+
+(lldb) command script import .../fd_recognizer.py
+(lldb) frame recognizer add -l fd_recognizer.LibcFdRecognizer -n read -s libsystem_kernel.dylib
+
+When the program is stopped at the beginning of the 'read' function in libc, we
+can view the recognizer arguments in 'frame variable':
+
+(lldb) b read
+(lldb) r
+Process 1234 stopped
+* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3
+ frame #0: 0x00007fff06013ca0 libsystem_kernel.dylib`read
+(lldb) frame variable
+(int) fd = 3
+
+ )");
+ }
+ ~CommandObjectFrameRecognizerAdd() override = default;
+};
+
+bool CommandObjectFrameRecognizerAdd::DoExecute(Args &command,
+ CommandReturnObject &result) {
+#if LLDB_ENABLE_PYTHON
+ if (m_options.m_class_name.empty()) {
+ result.AppendErrorWithFormat(
+ "%s needs a Python class name (-l argument).\n", m_cmd_name.c_str());
+ return false;
+ }
+
+ if (m_options.m_module.empty()) {
+ result.AppendErrorWithFormat("%s needs a module name (-s argument).\n",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ if (m_options.m_symbols.empty()) {
+ result.AppendErrorWithFormat(
+ "%s needs at least one symbol name (-n argument).\n",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ if (m_options.m_regex && m_options.m_symbols.size() > 1) {
+ result.AppendErrorWithFormat(
+ "%s needs only one symbol regular expression (-n argument).\n",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
+
+ if (interpreter &&
+ !interpreter->CheckObjectExists(m_options.m_class_name.c_str())) {
+ result.AppendWarning("The provided class does not exist - please define it "
+ "before attempting to use this frame recognizer");
+ }
+
+ StackFrameRecognizerSP recognizer_sp =
+ StackFrameRecognizerSP(new ScriptedStackFrameRecognizer(
+ interpreter, m_options.m_class_name.c_str()));
+ if (m_options.m_regex) {
+ auto module =
+ RegularExpressionSP(new RegularExpression(m_options.m_module));
+ auto func =
+ RegularExpressionSP(new RegularExpression(m_options.m_symbols.front()));
+ GetSelectedOrDummyTarget().GetFrameRecognizerManager().AddRecognizer(
+ recognizer_sp, module, func, m_options.m_first_instruction_only);
+ } else {
+ auto module = ConstString(m_options.m_module);
+ std::vector<ConstString> symbols(m_options.m_symbols.begin(),
+ m_options.m_symbols.end());
+ GetSelectedOrDummyTarget().GetFrameRecognizerManager().AddRecognizer(
+ recognizer_sp, module, symbols, m_options.m_first_instruction_only);
+ }
+#endif
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+}
+
+class CommandObjectFrameRecognizerClear : public CommandObjectParsed {
+public:
+ CommandObjectFrameRecognizerClear(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "frame recognizer clear",
+ "Delete all frame recognizers.", nullptr) {}
+
+ ~CommandObjectFrameRecognizerClear() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ GetSelectedOrDummyTarget()
+ .GetFrameRecognizerManager()
+ .RemoveAllRecognizers();
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+class CommandObjectFrameRecognizerDelete : public CommandObjectParsed {
+public:
+ CommandObjectFrameRecognizerDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "frame recognizer delete",
+ "Delete an existing frame recognizer by id.",
+ nullptr) {
+ CommandArgumentData thread_arg{eArgTypeRecognizerID, eArgRepeatPlain};
+ m_arguments.push_back({thread_arg});
+ }
+
+ ~CommandObjectFrameRecognizerDelete() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (request.GetCursorIndex() != 0)
+ return;
+
+ GetSelectedOrDummyTarget().GetFrameRecognizerManager().ForEach(
+ [&request](uint32_t rid, std::string rname, std::string module,
+ llvm::ArrayRef<lldb_private::ConstString> symbols,
+ bool regexp) {
+ StreamString strm;
+ if (rname.empty())
+ rname = "(internal)";
+
+ strm << rname;
+ if (!module.empty())
+ strm << ", module " << module;
+ if (!symbols.empty())
+ for (auto &symbol : symbols)
+ strm << ", symbol " << symbol;
+ if (regexp)
+ strm << " (regexp)";
+
+ request.TryCompleteCurrentArg(std::to_string(rid), strm.GetString());
+ });
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (command.GetArgumentCount() == 0) {
+ if (!m_interpreter.Confirm(
+ "About to delete all frame recognizers, do you want to do that?",
+ true)) {
+ result.AppendMessage("Operation cancelled...");
+ return false;
+ }
+
+ GetSelectedOrDummyTarget()
+ .GetFrameRecognizerManager()
+ .RemoveAllRecognizers();
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+
+ if (command.GetArgumentCount() != 1) {
+ result.AppendErrorWithFormat("'%s' takes zero or one arguments.\n",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ uint32_t recognizer_id;
+ if (!llvm::to_integer(command.GetArgumentAtIndex(0), recognizer_id)) {
+ result.AppendErrorWithFormat("'%s' is not a valid recognizer id.\n",
+ command.GetArgumentAtIndex(0));
+ return false;
+ }
+
+ if (!GetSelectedOrDummyTarget()
+ .GetFrameRecognizerManager()
+ .RemoveRecognizerWithID(recognizer_id)) {
+ result.AppendErrorWithFormat("'%s' is not a valid recognizer id.\n",
+ command.GetArgumentAtIndex(0));
+ return false;
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+class CommandObjectFrameRecognizerList : public CommandObjectParsed {
+public:
+ CommandObjectFrameRecognizerList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "frame recognizer list",
+ "Show a list of active frame recognizers.",
+ nullptr) {}
+
+ ~CommandObjectFrameRecognizerList() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ bool any_printed = false;
+ GetSelectedOrDummyTarget().GetFrameRecognizerManager().ForEach(
+ [&result, &any_printed](
+ uint32_t recognizer_id, std::string name, std::string module,
+ llvm::ArrayRef<ConstString> symbols, bool regexp) {
+ Stream &stream = result.GetOutputStream();
+
+ if (name.empty())
+ name = "(internal)";
+
+ stream << std::to_string(recognizer_id) << ": " << name;
+ if (!module.empty())
+ stream << ", module " << module;
+ if (!symbols.empty())
+ for (auto &symbol : symbols)
+ stream << ", symbol " << symbol;
+ if (regexp)
+ stream << " (regexp)";
+
+ stream.EOL();
+ stream.Flush();
+
+ any_printed = true;
+ });
+
+ if (any_printed)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else {
+ result.GetOutputStream().PutCString("no matching results found.\n");
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+ return result.Succeeded();
+ }
+};
+
+class CommandObjectFrameRecognizerInfo : public CommandObjectParsed {
+public:
+ CommandObjectFrameRecognizerInfo(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "frame recognizer info",
+ "Show which frame recognizer is applied a stack frame (if any).",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandArgumentData index_arg;
+
+ // Define the first (and only) variant of this arg.
+ index_arg.arg_type = eArgTypeFrameIndex;
+ index_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(index_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectFrameRecognizerInfo() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const char *frame_index_str = command.GetArgumentAtIndex(0);
+ uint32_t frame_index;
+ if (!llvm::to_integer(frame_index_str, frame_index)) {
+ result.AppendErrorWithFormat("'%s' is not a valid frame index.",
+ frame_index_str);
+ return false;
+ }
+
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (process == nullptr) {
+ result.AppendError("no process");
+ return false;
+ }
+ Thread *thread = m_exe_ctx.GetThreadPtr();
+ if (thread == nullptr) {
+ result.AppendError("no thread");
+ return false;
+ }
+ if (command.GetArgumentCount() != 1) {
+ result.AppendErrorWithFormat(
+ "'%s' takes exactly one frame index argument.\n", m_cmd_name.c_str());
+ return false;
+ }
+
+ StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_index);
+ if (!frame_sp) {
+ result.AppendErrorWithFormat("no frame with index %u", frame_index);
+ return false;
+ }
+
+ auto recognizer = GetSelectedOrDummyTarget()
+ .GetFrameRecognizerManager()
+ .GetRecognizerForFrame(frame_sp);
+
+ Stream &output_stream = result.GetOutputStream();
+ output_stream.Printf("frame %d ", frame_index);
+ if (recognizer) {
+ output_stream << "is recognized by ";
+ output_stream << recognizer->GetName();
+ } else {
+ output_stream << "not recognized by any recognizer";
+ }
+ output_stream.EOL();
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+class CommandObjectFrameRecognizer : public CommandObjectMultiword {
+public:
+ CommandObjectFrameRecognizer(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "frame recognizer",
+ "Commands for editing and viewing frame recognizers.",
+ "frame recognizer [<sub-command-options>] ") {
+ LoadSubCommand("add", CommandObjectSP(new CommandObjectFrameRecognizerAdd(
+ interpreter)));
+ LoadSubCommand(
+ "clear",
+ CommandObjectSP(new CommandObjectFrameRecognizerClear(interpreter)));
+ LoadSubCommand(
+ "delete",
+ CommandObjectSP(new CommandObjectFrameRecognizerDelete(interpreter)));
+ LoadSubCommand("list", CommandObjectSP(new CommandObjectFrameRecognizerList(
+ interpreter)));
+ LoadSubCommand("info", CommandObjectSP(new CommandObjectFrameRecognizerInfo(
+ interpreter)));
+ }
+
+ ~CommandObjectFrameRecognizer() override = default;
+};
+
+#pragma mark CommandObjectMultiwordFrame
+
+// CommandObjectMultiwordFrame
+
+CommandObjectMultiwordFrame::CommandObjectMultiwordFrame(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "frame",
+ "Commands for selecting and "
+ "examing the current "
+ "thread's stack frames.",
+ "frame <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("diagnose",
+ CommandObjectSP(new CommandObjectFrameDiagnose(interpreter)));
+ LoadSubCommand("info",
+ CommandObjectSP(new CommandObjectFrameInfo(interpreter)));
+ LoadSubCommand("select",
+ CommandObjectSP(new CommandObjectFrameSelect(interpreter)));
+ LoadSubCommand("variable",
+ CommandObjectSP(new CommandObjectFrameVariable(interpreter)));
+#if LLDB_ENABLE_PYTHON
+ LoadSubCommand("recognizer", CommandObjectSP(new CommandObjectFrameRecognizer(
+ interpreter)));
+#endif
+}
+
+CommandObjectMultiwordFrame::~CommandObjectMultiwordFrame() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectFrame.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectFrame.h
new file mode 100644
index 000000000000..71cb94aa8f5a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectFrame.h
@@ -0,0 +1,27 @@
+//===-- CommandObjectFrame.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTFRAME_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTFRAME_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+// CommandObjectMultiwordFrame
+
+class CommandObjectMultiwordFrame : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordFrame(CommandInterpreter &interpreter);
+
+ ~CommandObjectMultiwordFrame() override;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTFRAME_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectGUI.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectGUI.cpp
new file mode 100644
index 000000000000..a63d1718610c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectGUI.cpp
@@ -0,0 +1,47 @@
+//===-- CommandObjectGUI.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectGUI.h"
+
+#include "lldb/Core/IOHandlerCursesGUI.h"
+#include "lldb/Host/Config.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// CommandObjectGUI
+
+CommandObjectGUI::CommandObjectGUI(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "gui",
+ "Switch into the curses based GUI mode.", "gui") {}
+
+CommandObjectGUI::~CommandObjectGUI() = default;
+
+bool CommandObjectGUI::DoExecute(Args &args, CommandReturnObject &result) {
+#if LLDB_ENABLE_CURSES
+ Debugger &debugger = GetDebugger();
+
+ File &input = debugger.GetInputFile();
+ File &output = debugger.GetOutputFile();
+ if (input.GetStream() && output.GetStream() && input.GetIsRealTerminal() &&
+ input.GetIsInteractive()) {
+ IOHandlerSP io_handler_sp(new IOHandlerCursesGUI(debugger));
+ if (io_handler_sp)
+ debugger.RunIOHandlerAsync(io_handler_sp);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError("the gui command requires an interactive terminal.");
+ }
+ return true;
+#else
+ result.AppendError("lldb was not built with gui support");
+ return false;
+#endif
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectGUI.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectGUI.h
new file mode 100644
index 000000000000..49bad49a957d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectGUI.h
@@ -0,0 +1,30 @@
+//===-- CommandObjectGUI.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTGUI_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTGUI_H
+
+#include "lldb/Interpreter/CommandObject.h"
+
+namespace lldb_private {
+
+// CommandObjectGUI
+
+class CommandObjectGUI : public CommandObjectParsed {
+public:
+ CommandObjectGUI(CommandInterpreter &interpreter);
+
+ ~CommandObjectGUI() override;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTGUI_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectHelp.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectHelp.cpp
new file mode 100644
index 000000000000..a2f682049ae0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectHelp.cpp
@@ -0,0 +1,222 @@
+//===-- CommandObjectHelp.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectHelp.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// CommandObjectHelp
+
+void CommandObjectHelp::GenerateAdditionalHelpAvenuesMessage(
+ Stream *s, llvm::StringRef command, llvm::StringRef prefix,
+ llvm::StringRef subcommand, bool include_upropos,
+ bool include_type_lookup) {
+ if (!s || command.empty())
+ return;
+
+ std::string command_str = command.str();
+ std::string prefix_str = prefix.str();
+ std::string subcommand_str = subcommand.str();
+ const std::string &lookup_str =
+ !subcommand_str.empty() ? subcommand_str : command_str;
+ s->Printf("'%s' is not a known command.\n", command_str.c_str());
+ s->Printf("Try '%shelp' to see a current list of commands.\n",
+ prefix.str().c_str());
+ if (include_upropos) {
+ s->Printf("Try '%sapropos %s' for a list of related commands.\n",
+ prefix_str.c_str(), lookup_str.c_str());
+ }
+ if (include_type_lookup) {
+ s->Printf("Try '%stype lookup %s' for information on types, methods, "
+ "functions, modules, etc.",
+ prefix_str.c_str(), lookup_str.c_str());
+ }
+}
+
+CommandObjectHelp::CommandObjectHelp(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "help",
+ "Show a list of all debugger "
+ "commands, or give details "
+ "about a specific command.",
+ "help [<cmd-name>]") {
+ CommandArgumentEntry arg;
+ CommandArgumentData command_arg;
+
+ // A list of command names forming a path to the command we want help on.
+ // No names is allowed - in which case we dump the top-level help.
+ command_arg.arg_type = eArgTypeCommand;
+ command_arg.arg_repetition = eArgRepeatStar;
+
+ // There is only one variant this argument could be; put it into the argument
+ // entry.
+ arg.push_back(command_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+}
+
+CommandObjectHelp::~CommandObjectHelp() = default;
+
+#define LLDB_OPTIONS_help
+#include "CommandOptions.inc"
+
+llvm::ArrayRef<OptionDefinition>
+CommandObjectHelp::CommandOptions::GetDefinitions() {
+ return llvm::makeArrayRef(g_help_options);
+}
+
+bool CommandObjectHelp::DoExecute(Args &command, CommandReturnObject &result) {
+ CommandObject::CommandMap::iterator pos;
+ CommandObject *cmd_obj;
+ const size_t argc = command.GetArgumentCount();
+
+ // 'help' doesn't take any arguments, other than command names. If argc is
+ // 0, we show the user all commands (aliases and user commands if asked for).
+ // Otherwise every argument must be the name of a command or a sub-command.
+ if (argc == 0) {
+ uint32_t cmd_types = CommandInterpreter::eCommandTypesBuiltin;
+ if (m_options.m_show_aliases)
+ cmd_types |= CommandInterpreter::eCommandTypesAliases;
+ if (m_options.m_show_user_defined) {
+ cmd_types |= CommandInterpreter::eCommandTypesUserDef;
+ cmd_types |= CommandInterpreter::eCommandTypesUserMW;
+ }
+ if (m_options.m_show_hidden)
+ cmd_types |= CommandInterpreter::eCommandTypesHidden;
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ m_interpreter.GetHelp(result, cmd_types); // General help
+ } else {
+ // Get command object for the first command argument. Only search built-in
+ // command dictionary.
+ StringList matches;
+ auto command_name = command[0].ref();
+ cmd_obj = m_interpreter.GetCommandObject(command_name, &matches);
+
+ if (cmd_obj != nullptr) {
+ StringList matches;
+ bool all_okay = true;
+ CommandObject *sub_cmd_obj = cmd_obj;
+ // Loop down through sub_command dictionaries until we find the command
+ // object that corresponds to the help command entered.
+ std::string sub_command;
+ for (auto &entry : command.entries().drop_front()) {
+ sub_command = std::string(entry.ref());
+ matches.Clear();
+ if (sub_cmd_obj->IsAlias())
+ sub_cmd_obj =
+ ((CommandAlias *)sub_cmd_obj)->GetUnderlyingCommand().get();
+ if (!sub_cmd_obj->IsMultiwordObject()) {
+ all_okay = false;
+ break;
+ } else {
+ CommandObject *found_cmd;
+ found_cmd =
+ sub_cmd_obj->GetSubcommandObject(sub_command.c_str(), &matches);
+ if (found_cmd == nullptr || matches.GetSize() > 1) {
+ all_okay = false;
+ break;
+ } else
+ sub_cmd_obj = found_cmd;
+ }
+ }
+
+ if (!all_okay || (sub_cmd_obj == nullptr)) {
+ std::string cmd_string;
+ command.GetCommandString(cmd_string);
+ if (matches.GetSize() >= 2) {
+ StreamString s;
+ s.Printf("ambiguous command %s", cmd_string.c_str());
+ size_t num_matches = matches.GetSize();
+ for (size_t match_idx = 0; match_idx < num_matches; match_idx++) {
+ s.Printf("\n\t%s", matches.GetStringAtIndex(match_idx));
+ }
+ s.Printf("\n");
+ result.AppendError(s.GetString());
+ return false;
+ } else if (!sub_cmd_obj) {
+ StreamString error_msg_stream;
+ GenerateAdditionalHelpAvenuesMessage(
+ &error_msg_stream, cmd_string.c_str(),
+ m_interpreter.GetCommandPrefix(), sub_command.c_str());
+ result.AppendError(error_msg_stream.GetString());
+ return false;
+ } else {
+ GenerateAdditionalHelpAvenuesMessage(
+ &result.GetOutputStream(), cmd_string.c_str(),
+ m_interpreter.GetCommandPrefix(), sub_command.c_str());
+ result.GetOutputStream().Printf(
+ "\nThe closest match is '%s'. Help on it follows.\n\n",
+ sub_cmd_obj->GetCommandName().str().c_str());
+ }
+ }
+
+ sub_cmd_obj->GenerateHelpText(result);
+ std::string alias_full_name;
+ // Don't use AliasExists here, that only checks exact name matches. If
+ // the user typed a shorter unique alias name, we should still tell them
+ // it was an alias.
+ if (m_interpreter.GetAliasFullName(command_name, alias_full_name)) {
+ StreamString sstr;
+ m_interpreter.GetAlias(alias_full_name)->GetAliasExpansion(sstr);
+ result.GetOutputStream().Printf("\n'%s' is an abbreviation for %s\n",
+ command[0].c_str(), sstr.GetData());
+ }
+ } else if (matches.GetSize() > 0) {
+ Stream &output_strm = result.GetOutputStream();
+ output_strm.Printf("Help requested with ambiguous command name, possible "
+ "completions:\n");
+ const size_t match_count = matches.GetSize();
+ for (size_t i = 0; i < match_count; i++) {
+ output_strm.Printf("\t%s\n", matches.GetStringAtIndex(i));
+ }
+ } else {
+ // Maybe the user is asking for help about a command argument rather than
+ // a command.
+ const CommandArgumentType arg_type =
+ CommandObject::LookupArgumentName(command_name);
+ if (arg_type != eArgTypeLastArg) {
+ Stream &output_strm = result.GetOutputStream();
+ CommandObject::GetArgumentHelp(output_strm, arg_type, m_interpreter);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ StreamString error_msg_stream;
+ GenerateAdditionalHelpAvenuesMessage(&error_msg_stream, command_name,
+ m_interpreter.GetCommandPrefix(),
+ "");
+ result.AppendError(error_msg_stream.GetString());
+ }
+ }
+ }
+
+ return result.Succeeded();
+}
+
+void CommandObjectHelp::HandleCompletion(CompletionRequest &request) {
+ // Return the completions of the commands in the help system:
+ if (request.GetCursorIndex() == 0) {
+ m_interpreter.HandleCompletionMatches(request);
+ return;
+ }
+ CommandObject *cmd_obj =
+ m_interpreter.GetCommandObject(request.GetParsedLine()[0].ref());
+
+ // The command that they are getting help on might be ambiguous, in which
+ // case we should complete that, otherwise complete with the command the
+ // user is getting help on...
+
+ if (cmd_obj) {
+ request.ShiftArguments();
+ cmd_obj->HandleCompletion(request);
+ return;
+ }
+ m_interpreter.HandleCompletionMatches(request);
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectHelp.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectHelp.h
new file mode 100644
index 000000000000..a0ed157b9caf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectHelp.h
@@ -0,0 +1,87 @@
+//===-- CommandObjectHelp.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTHELP_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTHELP_H
+
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/Options.h"
+
+namespace lldb_private {
+
+// CommandObjectHelp
+
+class CommandObjectHelp : public CommandObjectParsed {
+public:
+ CommandObjectHelp(CommandInterpreter &interpreter);
+
+ ~CommandObjectHelp() override;
+
+ void HandleCompletion(CompletionRequest &request) override;
+
+ static void GenerateAdditionalHelpAvenuesMessage(
+ Stream *s, llvm::StringRef command, llvm::StringRef prefix,
+ llvm::StringRef subcommand, bool include_upropos = true,
+ bool include_type_lookup = true);
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'a':
+ m_show_aliases = false;
+ break;
+ case 'u':
+ m_show_user_defined = false;
+ break;
+ case 'h':
+ m_show_hidden = true;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_show_aliases = true;
+ m_show_user_defined = true;
+ m_show_hidden = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override;
+
+ // Instance variables to hold the values for command options.
+
+ bool m_show_aliases;
+ bool m_show_user_defined;
+ bool m_show_hidden;
+ };
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override;
+
+private:
+ CommandOptions m_options;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTHELP_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectLanguage.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectLanguage.cpp
new file mode 100644
index 000000000000..925db599e4ab
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectLanguage.cpp
@@ -0,0 +1,26 @@
+//===-- CommandObjectLanguage.cpp -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectLanguage.h"
+
+
+
+#include "lldb/Target/LanguageRuntime.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+CommandObjectLanguage::CommandObjectLanguage(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "language", "Commands specific to a source language.",
+ "language <language-name> <subcommand> [<subcommand-options>]") {
+ // Let the LanguageRuntime populates this command with subcommands
+ LanguageRuntime::InitializeCommands(this);
+}
+
+CommandObjectLanguage::~CommandObjectLanguage() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectLanguage.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectLanguage.h
new file mode 100644
index 000000000000..7a280902a07e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectLanguage.h
@@ -0,0 +1,26 @@
+//===-- CommandObjectLanguage.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTLANGUAGE_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTLANGUAGE_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+class CommandObjectLanguage : public CommandObjectMultiword {
+public:
+ CommandObjectLanguage(CommandInterpreter &interpreter);
+
+ ~CommandObjectLanguage() override;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result);
+};
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTLANGUAGE_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectLog.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectLog.cpp
new file mode 100644
index 000000000000..684cb35da1cc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectLog.cpp
@@ -0,0 +1,674 @@
+//===-- CommandObjectLog.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectLog.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionValueEnumeration.h"
+#include "lldb/Interpreter/OptionValueUInt64.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/Timer.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static constexpr OptionEnumValueElement g_log_handler_type[] = {
+ {
+ eLogHandlerDefault,
+ "default",
+ "Use the default (stream) log handler",
+ },
+ {
+ eLogHandlerStream,
+ "stream",
+ "Write log messages to the debugger output stream or to a file if one "
+ "is specified. A buffer size (in bytes) can be specified with -b. If "
+ "no buffer size is specified the output is unbuffered.",
+ },
+ {
+ eLogHandlerCircular,
+ "circular",
+ "Write log messages to a fixed size circular buffer. A buffer size "
+ "(number of messages) must be specified with -b.",
+ },
+ {
+ eLogHandlerSystem,
+ "os",
+ "Write log messages to the operating system log.",
+ },
+};
+
+static constexpr OptionEnumValues LogHandlerType() {
+ return OptionEnumValues(g_log_handler_type);
+}
+
+#define LLDB_OPTIONS_log_enable
+#include "CommandOptions.inc"
+
+#define LLDB_OPTIONS_log_dump
+#include "CommandOptions.inc"
+
+/// Common completion logic for log enable/disable.
+static void CompleteEnableDisable(CompletionRequest &request) {
+ size_t arg_index = request.GetCursorIndex();
+ if (arg_index == 0) { // We got: log enable/disable x[tab]
+ for (llvm::StringRef channel : Log::ListChannels())
+ request.TryCompleteCurrentArg(channel);
+ } else if (arg_index >= 1) { // We got: log enable/disable channel x[tab]
+ llvm::StringRef channel = request.GetParsedLine().GetArgumentAtIndex(0);
+ Log::ForEachChannelCategory(
+ channel, [&request](llvm::StringRef name, llvm::StringRef desc) {
+ request.TryCompleteCurrentArg(name, desc);
+ });
+ }
+}
+
+class CommandObjectLogEnable : public CommandObjectParsed {
+public:
+ // Constructors and Destructors
+ CommandObjectLogEnable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "log enable",
+ "Enable logging for a single log channel.",
+ nullptr) {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentData channel_arg;
+ CommandArgumentData category_arg;
+
+ // Define the first (and only) variant of this arg.
+ channel_arg.arg_type = eArgTypeLogChannel;
+ channel_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(channel_arg);
+
+ category_arg.arg_type = eArgTypeLogCategory;
+ category_arg.arg_repetition = eArgRepeatPlus;
+
+ arg2.push_back(category_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+ }
+
+ ~CommandObjectLogEnable() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'f':
+ log_file.SetFile(option_arg, FileSpec::Style::native);
+ FileSystem::Instance().Resolve(log_file);
+ break;
+ case 'h':
+ handler = (LogHandlerKind)OptionArgParser::ToOptionEnum(
+ option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
+ if (!error.Success())
+ error.SetErrorStringWithFormat(
+ "unrecognized value for log handler '%s'",
+ option_arg.str().c_str());
+ break;
+ case 'b':
+ error =
+ buffer_size.SetValueFromString(option_arg, eVarSetOperationAssign);
+ break;
+ case 'v':
+ log_options |= LLDB_LOG_OPTION_VERBOSE;
+ break;
+ case 's':
+ log_options |= LLDB_LOG_OPTION_PREPEND_SEQUENCE;
+ break;
+ case 'T':
+ log_options |= LLDB_LOG_OPTION_PREPEND_TIMESTAMP;
+ break;
+ case 'p':
+ log_options |= LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD;
+ break;
+ case 'n':
+ log_options |= LLDB_LOG_OPTION_PREPEND_THREAD_NAME;
+ break;
+ case 'S':
+ log_options |= LLDB_LOG_OPTION_BACKTRACE;
+ break;
+ case 'a':
+ log_options |= LLDB_LOG_OPTION_APPEND;
+ break;
+ case 'F':
+ log_options |= LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ log_file.Clear();
+ buffer_size.Clear();
+ handler = eLogHandlerStream;
+ log_options = 0;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_log_enable_options);
+ }
+
+ FileSpec log_file;
+ OptionValueUInt64 buffer_size;
+ LogHandlerKind handler = eLogHandlerStream;
+ uint32_t log_options = 0;
+ };
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CompleteEnableDisable(request);
+ }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ if (args.GetArgumentCount() < 2) {
+ result.AppendErrorWithFormat(
+ "%s takes a log channel and one or more log types.\n",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ if (m_options.handler == eLogHandlerCircular &&
+ m_options.buffer_size.GetCurrentValue() == 0) {
+ result.AppendError(
+ "the circular buffer handler requires a non-zero buffer size.\n");
+ return false;
+ }
+
+ // Store into a std::string since we're about to shift the channel off.
+ const std::string channel = std::string(args[0].ref());
+ args.Shift(); // Shift off the channel
+ char log_file[PATH_MAX];
+ if (m_options.log_file)
+ m_options.log_file.GetPath(log_file, sizeof(log_file));
+ else
+ log_file[0] = '\0';
+
+ std::string error;
+ llvm::raw_string_ostream error_stream(error);
+ bool success = GetDebugger().EnableLog(
+ channel, args.GetArgumentArrayRef(), log_file, m_options.log_options,
+ m_options.buffer_size.GetCurrentValue(), m_options.handler,
+ error_stream);
+ result.GetErrorStream() << error_stream.str();
+
+ if (success)
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+class CommandObjectLogDisable : public CommandObjectParsed {
+public:
+ // Constructors and Destructors
+ CommandObjectLogDisable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "log disable",
+ "Disable one or more log channel categories.",
+ nullptr) {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentData channel_arg;
+ CommandArgumentData category_arg;
+
+ // Define the first (and only) variant of this arg.
+ channel_arg.arg_type = eArgTypeLogChannel;
+ channel_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(channel_arg);
+
+ category_arg.arg_type = eArgTypeLogCategory;
+ category_arg.arg_repetition = eArgRepeatPlus;
+
+ arg2.push_back(category_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+ }
+
+ ~CommandObjectLogDisable() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CompleteEnableDisable(request);
+ }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ if (args.empty()) {
+ result.AppendErrorWithFormat(
+ "%s takes a log channel and one or more log types.\n",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ const std::string channel = std::string(args[0].ref());
+ args.Shift(); // Shift off the channel
+ if (channel == "all") {
+ Log::DisableAllLogChannels();
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ std::string error;
+ llvm::raw_string_ostream error_stream(error);
+ if (Log::DisableLogChannel(channel, args.GetArgumentArrayRef(),
+ error_stream))
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ result.GetErrorStream() << error_stream.str();
+ }
+ return result.Succeeded();
+ }
+};
+
+class CommandObjectLogList : public CommandObjectParsed {
+public:
+ // Constructors and Destructors
+ CommandObjectLogList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "log list",
+ "List the log categories for one or more log "
+ "channels. If none specified, lists them all.",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandArgumentData channel_arg;
+
+ // Define the first (and only) variant of this arg.
+ channel_arg.arg_type = eArgTypeLogChannel;
+ channel_arg.arg_repetition = eArgRepeatStar;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(channel_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectLogList() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ for (llvm::StringRef channel : Log::ListChannels())
+ request.TryCompleteCurrentArg(channel);
+ }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ std::string output;
+ llvm::raw_string_ostream output_stream(output);
+ if (args.empty()) {
+ Log::ListAllLogChannels(output_stream);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ bool success = true;
+ for (const auto &entry : args.entries())
+ success =
+ success && Log::ListChannelCategories(entry.ref(), output_stream);
+ if (success)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+ result.GetOutputStream() << output_stream.str();
+ return result.Succeeded();
+ }
+};
+class CommandObjectLogDump : public CommandObjectParsed {
+public:
+ CommandObjectLogDump(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "log dump",
+ "dump circular buffer logs", nullptr) {
+ CommandArgumentEntry arg1;
+ CommandArgumentData channel_arg;
+
+ // Define the first (and only) variant of this arg.
+ channel_arg.arg_type = eArgTypeLogChannel;
+ channel_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(channel_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ }
+
+ ~CommandObjectLogDump() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'f':
+ log_file.SetFile(option_arg, FileSpec::Style::native);
+ FileSystem::Instance().Resolve(log_file);
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ log_file.Clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_log_dump_options);
+ }
+
+ FileSpec log_file;
+ };
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CompleteEnableDisable(request);
+ }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ if (args.empty()) {
+ result.AppendErrorWithFormat(
+ "%s takes a log channel and one or more log types.\n",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ std::unique_ptr<llvm::raw_ostream> stream_up;
+ if (m_options.log_file) {
+ const File::OpenOptions flags = File::eOpenOptionWriteOnly |
+ File::eOpenOptionCanCreate |
+ File::eOpenOptionTruncate;
+ llvm::Expected<FileUP> file = FileSystem::Instance().Open(
+ m_options.log_file, flags, lldb::eFilePermissionsFileDefault, false);
+ if (!file) {
+ result.AppendErrorWithFormat("Unable to open log file '%s': %s",
+ m_options.log_file.GetCString(),
+ llvm::toString(file.takeError()).c_str());
+ return false;
+ }
+ stream_up = std::make_unique<llvm::raw_fd_ostream>(
+ (*file)->GetDescriptor(), /*shouldClose=*/true);
+ } else {
+ stream_up = std::make_unique<llvm::raw_fd_ostream>(
+ GetDebugger().GetOutputFile().GetDescriptor(), /*shouldClose=*/false);
+ }
+
+ const std::string channel = std::string(args[0].ref());
+ std::string error;
+ llvm::raw_string_ostream error_stream(error);
+ if (Log::DumpLogChannel(channel, *stream_up, error_stream)) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.SetStatus(eReturnStatusFailed);
+ result.GetErrorStream() << error_stream.str();
+ }
+
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+class CommandObjectLogTimerEnable : public CommandObjectParsed {
+public:
+ // Constructors and Destructors
+ CommandObjectLogTimerEnable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "log timers enable",
+ "enable LLDB internal performance timers",
+ "log timers enable <depth>") {
+ CommandArgumentEntry arg;
+ CommandArgumentData depth_arg;
+
+ // Define the first (and only) variant of this arg.
+ depth_arg.arg_type = eArgTypeCount;
+ depth_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(depth_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectLogTimerEnable() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ result.SetStatus(eReturnStatusFailed);
+
+ if (args.GetArgumentCount() == 0) {
+ Timer::SetDisplayDepth(UINT32_MAX);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else if (args.GetArgumentCount() == 1) {
+ uint32_t depth;
+ if (args[0].ref().consumeInteger(0, depth)) {
+ result.AppendError(
+ "Could not convert enable depth to an unsigned integer.");
+ } else {
+ Timer::SetDisplayDepth(depth);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+ }
+
+ if (!result.Succeeded()) {
+ result.AppendError("Missing subcommand");
+ result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
+ }
+ return result.Succeeded();
+ }
+};
+
+class CommandObjectLogTimerDisable : public CommandObjectParsed {
+public:
+ // Constructors and Destructors
+ CommandObjectLogTimerDisable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "log timers disable",
+ "disable LLDB internal performance timers",
+ nullptr) {}
+
+ ~CommandObjectLogTimerDisable() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Timer::DumpCategoryTimes(&result.GetOutputStream());
+ Timer::SetDisplayDepth(0);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ if (!result.Succeeded()) {
+ result.AppendError("Missing subcommand");
+ result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
+ }
+ return result.Succeeded();
+ }
+};
+
+class CommandObjectLogTimerDump : public CommandObjectParsed {
+public:
+ // Constructors and Destructors
+ CommandObjectLogTimerDump(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "log timers dump",
+ "dump LLDB internal performance timers", nullptr) {}
+
+ ~CommandObjectLogTimerDump() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Timer::DumpCategoryTimes(&result.GetOutputStream());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ if (!result.Succeeded()) {
+ result.AppendError("Missing subcommand");
+ result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
+ }
+ return result.Succeeded();
+ }
+};
+
+class CommandObjectLogTimerReset : public CommandObjectParsed {
+public:
+ // Constructors and Destructors
+ CommandObjectLogTimerReset(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "log timers reset",
+ "reset LLDB internal performance timers", nullptr) {
+ }
+
+ ~CommandObjectLogTimerReset() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Timer::ResetCategoryTimes();
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ if (!result.Succeeded()) {
+ result.AppendError("Missing subcommand");
+ result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
+ }
+ return result.Succeeded();
+ }
+};
+
+class CommandObjectLogTimerIncrement : public CommandObjectParsed {
+public:
+ // Constructors and Destructors
+ CommandObjectLogTimerIncrement(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "log timers increment",
+ "increment LLDB internal performance timers",
+ "log timers increment <bool>") {
+ CommandArgumentEntry arg;
+ CommandArgumentData bool_arg;
+
+ // Define the first (and only) variant of this arg.
+ bool_arg.arg_type = eArgTypeBoolean;
+ bool_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(bool_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectLogTimerIncrement() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ request.TryCompleteCurrentArg("true");
+ request.TryCompleteCurrentArg("false");
+ }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ result.SetStatus(eReturnStatusFailed);
+
+ if (args.GetArgumentCount() == 1) {
+ bool success;
+ bool increment =
+ OptionArgParser::ToBoolean(args[0].ref(), false, &success);
+
+ if (success) {
+ Timer::SetQuiet(!increment);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else
+ result.AppendError("Could not convert increment value to boolean.");
+ }
+
+ if (!result.Succeeded()) {
+ result.AppendError("Missing subcommand");
+ result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
+ }
+ return result.Succeeded();
+ }
+};
+
+class CommandObjectLogTimer : public CommandObjectMultiword {
+public:
+ CommandObjectLogTimer(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "log timers",
+ "Enable, disable, dump, and reset LLDB internal "
+ "performance timers.",
+ "log timers < enable <depth> | disable | dump | "
+ "increment <bool> | reset >") {
+ LoadSubCommand("enable", CommandObjectSP(
+ new CommandObjectLogTimerEnable(interpreter)));
+ LoadSubCommand("disable", CommandObjectSP(new CommandObjectLogTimerDisable(
+ interpreter)));
+ LoadSubCommand("dump",
+ CommandObjectSP(new CommandObjectLogTimerDump(interpreter)));
+ LoadSubCommand(
+ "reset", CommandObjectSP(new CommandObjectLogTimerReset(interpreter)));
+ LoadSubCommand(
+ "increment",
+ CommandObjectSP(new CommandObjectLogTimerIncrement(interpreter)));
+ }
+
+ ~CommandObjectLogTimer() override = default;
+};
+
+CommandObjectLog::CommandObjectLog(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "log",
+ "Commands controlling LLDB internal logging.",
+ "log <subcommand> [<command-options>]") {
+ LoadSubCommand("enable",
+ CommandObjectSP(new CommandObjectLogEnable(interpreter)));
+ LoadSubCommand("disable",
+ CommandObjectSP(new CommandObjectLogDisable(interpreter)));
+ LoadSubCommand("list",
+ CommandObjectSP(new CommandObjectLogList(interpreter)));
+ LoadSubCommand("dump",
+ CommandObjectSP(new CommandObjectLogDump(interpreter)));
+ LoadSubCommand("timers",
+ CommandObjectSP(new CommandObjectLogTimer(interpreter)));
+}
+
+CommandObjectLog::~CommandObjectLog() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectLog.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectLog.h
new file mode 100644
index 000000000000..8dc3f1b7b9e9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectLog.h
@@ -0,0 +1,33 @@
+//===-- CommandObjectLog.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTLOG_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTLOG_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+// CommandObjectLog
+
+class CommandObjectLog : public CommandObjectMultiword {
+public:
+ // Constructors and Destructors
+ CommandObjectLog(CommandInterpreter &interpreter);
+
+ ~CommandObjectLog() override;
+
+private:
+ // For CommandObjectLog only
+ CommandObjectLog(const CommandObjectLog &) = delete;
+ const CommandObjectLog &operator=(const CommandObjectLog &) = delete;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTLOG_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectMemory.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectMemory.cpp
new file mode 100644
index 000000000000..98fa38f25635
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectMemory.cpp
@@ -0,0 +1,1890 @@
+//===-- CommandObjectMemory.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectMemory.h"
+#include "CommandObjectMemoryTag.h"
+#include "lldb/Core/DumpDataExtractor.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Expression/ExpressionVariable.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionGroupFormat.h"
+#include "lldb/Interpreter/OptionGroupMemoryTag.h"
+#include "lldb/Interpreter/OptionGroupOutputFile.h"
+#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
+#include "lldb/Interpreter/OptionValueLanguage.h"
+#include "lldb/Interpreter/OptionValueString.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/MemoryHistory.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/StreamString.h"
+#include "llvm/Support/MathExtras.h"
+#include <cinttypes>
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+#define LLDB_OPTIONS_memory_read
+#include "CommandOptions.inc"
+
+class OptionGroupReadMemory : public OptionGroup {
+public:
+ OptionGroupReadMemory()
+ : m_num_per_line(1, 1), m_offset(0, 0),
+ m_language_for_type(eLanguageTypeUnknown) {}
+
+ ~OptionGroupReadMemory() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_memory_read_options);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = g_memory_read_options[option_idx].short_option;
+
+ switch (short_option) {
+ case 'l':
+ error = m_num_per_line.SetValueFromString(option_value);
+ if (m_num_per_line.GetCurrentValue() == 0)
+ error.SetErrorStringWithFormat(
+ "invalid value for --num-per-line option '%s'",
+ option_value.str().c_str());
+ break;
+
+ case 'b':
+ m_output_as_binary = true;
+ break;
+
+ case 't':
+ error = m_view_as_type.SetValueFromString(option_value);
+ break;
+
+ case 'r':
+ m_force = true;
+ break;
+
+ case 'x':
+ error = m_language_for_type.SetValueFromString(option_value);
+ break;
+
+ case 'E':
+ error = m_offset.SetValueFromString(option_value);
+ break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_num_per_line.Clear();
+ m_output_as_binary = false;
+ m_view_as_type.Clear();
+ m_force = false;
+ m_offset.Clear();
+ m_language_for_type.Clear();
+ }
+
+ Status FinalizeSettings(Target *target, OptionGroupFormat &format_options) {
+ Status error;
+ OptionValueUInt64 &byte_size_value = format_options.GetByteSizeValue();
+ OptionValueUInt64 &count_value = format_options.GetCountValue();
+ const bool byte_size_option_set = byte_size_value.OptionWasSet();
+ const bool num_per_line_option_set = m_num_per_line.OptionWasSet();
+ const bool count_option_set = format_options.GetCountValue().OptionWasSet();
+
+ switch (format_options.GetFormat()) {
+ default:
+ break;
+
+ case eFormatBoolean:
+ if (!byte_size_option_set)
+ byte_size_value = 1;
+ if (!num_per_line_option_set)
+ m_num_per_line = 1;
+ if (!count_option_set)
+ format_options.GetCountValue() = 8;
+ break;
+
+ case eFormatCString:
+ break;
+
+ case eFormatInstruction:
+ if (count_option_set)
+ byte_size_value = target->GetArchitecture().GetMaximumOpcodeByteSize();
+ m_num_per_line = 1;
+ break;
+
+ case eFormatAddressInfo:
+ if (!byte_size_option_set)
+ byte_size_value = target->GetArchitecture().GetAddressByteSize();
+ m_num_per_line = 1;
+ if (!count_option_set)
+ format_options.GetCountValue() = 8;
+ break;
+
+ case eFormatPointer:
+ byte_size_value = target->GetArchitecture().GetAddressByteSize();
+ if (!num_per_line_option_set)
+ m_num_per_line = 4;
+ if (!count_option_set)
+ format_options.GetCountValue() = 8;
+ break;
+
+ case eFormatBinary:
+ case eFormatFloat:
+ case eFormatOctal:
+ case eFormatDecimal:
+ case eFormatEnum:
+ case eFormatUnicode8:
+ case eFormatUnicode16:
+ case eFormatUnicode32:
+ case eFormatUnsigned:
+ case eFormatHexFloat:
+ if (!byte_size_option_set)
+ byte_size_value = 4;
+ if (!num_per_line_option_set)
+ m_num_per_line = 1;
+ if (!count_option_set)
+ format_options.GetCountValue() = 8;
+ break;
+
+ case eFormatBytes:
+ case eFormatBytesWithASCII:
+ if (byte_size_option_set) {
+ if (byte_size_value > 1)
+ error.SetErrorStringWithFormat(
+ "display format (bytes/bytes with ASCII) conflicts with the "
+ "specified byte size %" PRIu64 "\n"
+ "\tconsider using a different display format or don't specify "
+ "the byte size.",
+ byte_size_value.GetCurrentValue());
+ } else
+ byte_size_value = 1;
+ if (!num_per_line_option_set)
+ m_num_per_line = 16;
+ if (!count_option_set)
+ format_options.GetCountValue() = 32;
+ break;
+
+ case eFormatCharArray:
+ case eFormatChar:
+ case eFormatCharPrintable:
+ if (!byte_size_option_set)
+ byte_size_value = 1;
+ if (!num_per_line_option_set)
+ m_num_per_line = 32;
+ if (!count_option_set)
+ format_options.GetCountValue() = 64;
+ break;
+
+ case eFormatComplex:
+ if (!byte_size_option_set)
+ byte_size_value = 8;
+ if (!num_per_line_option_set)
+ m_num_per_line = 1;
+ if (!count_option_set)
+ format_options.GetCountValue() = 8;
+ break;
+
+ case eFormatComplexInteger:
+ if (!byte_size_option_set)
+ byte_size_value = 8;
+ if (!num_per_line_option_set)
+ m_num_per_line = 1;
+ if (!count_option_set)
+ format_options.GetCountValue() = 8;
+ break;
+
+ case eFormatHex:
+ if (!byte_size_option_set)
+ byte_size_value = 4;
+ if (!num_per_line_option_set) {
+ switch (byte_size_value) {
+ case 1:
+ case 2:
+ m_num_per_line = 8;
+ break;
+ case 4:
+ m_num_per_line = 4;
+ break;
+ case 8:
+ m_num_per_line = 2;
+ break;
+ default:
+ m_num_per_line = 1;
+ break;
+ }
+ }
+ if (!count_option_set)
+ count_value = 8;
+ break;
+
+ case eFormatVectorOfChar:
+ case eFormatVectorOfSInt8:
+ case eFormatVectorOfUInt8:
+ case eFormatVectorOfSInt16:
+ case eFormatVectorOfUInt16:
+ case eFormatVectorOfSInt32:
+ case eFormatVectorOfUInt32:
+ case eFormatVectorOfSInt64:
+ case eFormatVectorOfUInt64:
+ case eFormatVectorOfFloat16:
+ case eFormatVectorOfFloat32:
+ case eFormatVectorOfFloat64:
+ case eFormatVectorOfUInt128:
+ if (!byte_size_option_set)
+ byte_size_value = 128;
+ if (!num_per_line_option_set)
+ m_num_per_line = 1;
+ if (!count_option_set)
+ count_value = 4;
+ break;
+ }
+ return error;
+ }
+
+ bool AnyOptionWasSet() const {
+ return m_num_per_line.OptionWasSet() || m_output_as_binary ||
+ m_view_as_type.OptionWasSet() || m_offset.OptionWasSet() ||
+ m_language_for_type.OptionWasSet();
+ }
+
+ OptionValueUInt64 m_num_per_line;
+ bool m_output_as_binary = false;
+ OptionValueString m_view_as_type;
+ bool m_force;
+ OptionValueUInt64 m_offset;
+ OptionValueLanguage m_language_for_type;
+};
+
+// Read memory from the inferior process
+class CommandObjectMemoryRead : public CommandObjectParsed {
+public:
+ CommandObjectMemoryRead(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "memory read",
+ "Read from the memory of the current target process.", nullptr,
+ eCommandRequiresTarget | eCommandProcessMustBePaused),
+ m_format_options(eFormatBytesWithASCII, 1, 8),
+ m_memory_tag_options(/*note_binary=*/true),
+ m_prev_format_options(eFormatBytesWithASCII, 1, 8) {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentData start_addr_arg;
+ CommandArgumentData end_addr_arg;
+
+ // Define the first (and only) variant of this arg.
+ start_addr_arg.arg_type = eArgTypeAddressOrExpression;
+ start_addr_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(start_addr_arg);
+
+ // Define the first (and only) variant of this arg.
+ end_addr_arg.arg_type = eArgTypeAddressOrExpression;
+ end_addr_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg2.push_back(end_addr_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+
+ // Add the "--format" and "--count" options to group 1 and 3
+ m_option_group.Append(&m_format_options,
+ OptionGroupFormat::OPTION_GROUP_FORMAT |
+ OptionGroupFormat::OPTION_GROUP_COUNT,
+ LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3);
+ m_option_group.Append(&m_format_options,
+ OptionGroupFormat::OPTION_GROUP_GDB_FMT,
+ LLDB_OPT_SET_1 | LLDB_OPT_SET_3);
+ // Add the "--size" option to group 1 and 2
+ m_option_group.Append(&m_format_options,
+ OptionGroupFormat::OPTION_GROUP_SIZE,
+ LLDB_OPT_SET_1 | LLDB_OPT_SET_2);
+ m_option_group.Append(&m_memory_options);
+ m_option_group.Append(&m_outfile_options, LLDB_OPT_SET_ALL,
+ LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3);
+ m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_3);
+ m_option_group.Append(&m_memory_tag_options, LLDB_OPT_SET_ALL,
+ LLDB_OPT_SET_ALL);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectMemoryRead() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+ llvm::Optional<std::string> GetRepeatCommand(Args &current_command_args,
+ uint32_t index) override {
+ return m_cmd_name;
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ // No need to check "target" for validity as eCommandRequiresTarget ensures
+ // it is valid
+ Target *target = m_exe_ctx.GetTargetPtr();
+
+ const size_t argc = command.GetArgumentCount();
+
+ if ((argc == 0 && m_next_addr == LLDB_INVALID_ADDRESS) || argc > 2) {
+ result.AppendErrorWithFormat("%s takes a start address expression with "
+ "an optional end address expression.\n",
+ m_cmd_name.c_str());
+ result.AppendWarning("Expressions should be quoted if they contain "
+ "spaces or other special characters.");
+ return false;
+ }
+
+ CompilerType compiler_type;
+ Status error;
+
+ const char *view_as_type_cstr =
+ m_memory_options.m_view_as_type.GetCurrentValue();
+ if (view_as_type_cstr && view_as_type_cstr[0]) {
+ // We are viewing memory as a type
+
+ const bool exact_match = false;
+ TypeList type_list;
+ uint32_t reference_count = 0;
+ uint32_t pointer_count = 0;
+ size_t idx;
+
+#define ALL_KEYWORDS \
+ KEYWORD("const") \
+ KEYWORD("volatile") \
+ KEYWORD("restrict") \
+ KEYWORD("struct") \
+ KEYWORD("class") \
+ KEYWORD("union")
+
+#define KEYWORD(s) s,
+ static const char *g_keywords[] = {ALL_KEYWORDS};
+#undef KEYWORD
+
+#define KEYWORD(s) (sizeof(s) - 1),
+ static const int g_keyword_lengths[] = {ALL_KEYWORDS};
+#undef KEYWORD
+
+#undef ALL_KEYWORDS
+
+ static size_t g_num_keywords = sizeof(g_keywords) / sizeof(const char *);
+ std::string type_str(view_as_type_cstr);
+
+ // Remove all instances of g_keywords that are followed by spaces
+ for (size_t i = 0; i < g_num_keywords; ++i) {
+ const char *keyword = g_keywords[i];
+ int keyword_len = g_keyword_lengths[i];
+
+ idx = 0;
+ while ((idx = type_str.find(keyword, idx)) != std::string::npos) {
+ if (type_str[idx + keyword_len] == ' ' ||
+ type_str[idx + keyword_len] == '\t') {
+ type_str.erase(idx, keyword_len + 1);
+ idx = 0;
+ } else {
+ idx += keyword_len;
+ }
+ }
+ }
+ bool done = type_str.empty();
+ //
+ idx = type_str.find_first_not_of(" \t");
+ if (idx > 0 && idx != std::string::npos)
+ type_str.erase(0, idx);
+ while (!done) {
+ // Strip trailing spaces
+ if (type_str.empty())
+ done = true;
+ else {
+ switch (type_str[type_str.size() - 1]) {
+ case '*':
+ ++pointer_count;
+ LLVM_FALLTHROUGH;
+ case ' ':
+ case '\t':
+ type_str.erase(type_str.size() - 1);
+ break;
+
+ case '&':
+ if (reference_count == 0) {
+ reference_count = 1;
+ type_str.erase(type_str.size() - 1);
+ } else {
+ result.AppendErrorWithFormat("invalid type string: '%s'\n",
+ view_as_type_cstr);
+ return false;
+ }
+ break;
+
+ default:
+ done = true;
+ break;
+ }
+ }
+ }
+
+ llvm::DenseSet<lldb_private::SymbolFile *> searched_symbol_files;
+ ConstString lookup_type_name(type_str.c_str());
+ StackFrame *frame = m_exe_ctx.GetFramePtr();
+ ModuleSP search_first;
+ if (frame) {
+ search_first = frame->GetSymbolContext(eSymbolContextModule).module_sp;
+ }
+ target->GetImages().FindTypes(search_first.get(), lookup_type_name,
+ exact_match, 1, searched_symbol_files,
+ type_list);
+
+ if (type_list.GetSize() == 0 && lookup_type_name.GetCString()) {
+ LanguageType language_for_type =
+ m_memory_options.m_language_for_type.GetCurrentValue();
+ std::set<LanguageType> languages_to_check;
+ if (language_for_type != eLanguageTypeUnknown) {
+ languages_to_check.insert(language_for_type);
+ } else {
+ languages_to_check = Language::GetSupportedLanguages();
+ }
+
+ std::set<CompilerType> user_defined_types;
+ for (auto lang : languages_to_check) {
+ if (auto *persistent_vars =
+ target->GetPersistentExpressionStateForLanguage(lang)) {
+ if (llvm::Optional<CompilerType> type =
+ persistent_vars->GetCompilerTypeFromPersistentDecl(
+ lookup_type_name)) {
+ user_defined_types.emplace(*type);
+ }
+ }
+ }
+
+ if (user_defined_types.size() > 1) {
+ result.AppendErrorWithFormat(
+ "Mutiple types found matching raw type '%s', please disambiguate "
+ "by specifying the language with -x",
+ lookup_type_name.GetCString());
+ return false;
+ }
+
+ if (user_defined_types.size() == 1) {
+ compiler_type = *user_defined_types.begin();
+ }
+ }
+
+ if (!compiler_type.IsValid()) {
+ if (type_list.GetSize() == 0) {
+ result.AppendErrorWithFormat("unable to find any types that match "
+ "the raw type '%s' for full type '%s'\n",
+ lookup_type_name.GetCString(),
+ view_as_type_cstr);
+ return false;
+ } else {
+ TypeSP type_sp(type_list.GetTypeAtIndex(0));
+ compiler_type = type_sp->GetFullCompilerType();
+ }
+ }
+
+ while (pointer_count > 0) {
+ CompilerType pointer_type = compiler_type.GetPointerType();
+ if (pointer_type.IsValid())
+ compiler_type = pointer_type;
+ else {
+ result.AppendError("unable make a pointer type\n");
+ return false;
+ }
+ --pointer_count;
+ }
+
+ llvm::Optional<uint64_t> size = compiler_type.GetByteSize(nullptr);
+ if (!size) {
+ result.AppendErrorWithFormat(
+ "unable to get the byte size of the type '%s'\n",
+ view_as_type_cstr);
+ return false;
+ }
+ m_format_options.GetByteSizeValue() = *size;
+
+ if (!m_format_options.GetCountValue().OptionWasSet())
+ m_format_options.GetCountValue() = 1;
+ } else {
+ error = m_memory_options.FinalizeSettings(target, m_format_options);
+ }
+
+ // Look for invalid combinations of settings
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ return false;
+ }
+
+ lldb::addr_t addr;
+ size_t total_byte_size = 0;
+ if (argc == 0) {
+ // Use the last address and byte size and all options as they were if no
+ // options have been set
+ addr = m_next_addr;
+ total_byte_size = m_prev_byte_size;
+ compiler_type = m_prev_compiler_type;
+ if (!m_format_options.AnyOptionWasSet() &&
+ !m_memory_options.AnyOptionWasSet() &&
+ !m_outfile_options.AnyOptionWasSet() &&
+ !m_varobj_options.AnyOptionWasSet() &&
+ !m_memory_tag_options.AnyOptionWasSet()) {
+ m_format_options = m_prev_format_options;
+ m_memory_options = m_prev_memory_options;
+ m_outfile_options = m_prev_outfile_options;
+ m_varobj_options = m_prev_varobj_options;
+ m_memory_tag_options = m_prev_memory_tag_options;
+ }
+ }
+
+ size_t item_count = m_format_options.GetCountValue().GetCurrentValue();
+
+ // TODO For non-8-bit byte addressable architectures this needs to be
+ // revisited to fully support all lldb's range of formatting options.
+ // Furthermore code memory reads (for those architectures) will not be
+ // correctly formatted even w/o formatting options.
+ size_t item_byte_size =
+ target->GetArchitecture().GetDataByteSize() > 1
+ ? target->GetArchitecture().GetDataByteSize()
+ : m_format_options.GetByteSizeValue().GetCurrentValue();
+
+ const size_t num_per_line =
+ m_memory_options.m_num_per_line.GetCurrentValue();
+
+ if (total_byte_size == 0) {
+ total_byte_size = item_count * item_byte_size;
+ if (total_byte_size == 0)
+ total_byte_size = 32;
+ }
+
+ if (argc > 0)
+ addr = OptionArgParser::ToAddress(&m_exe_ctx, command[0].ref(),
+ LLDB_INVALID_ADDRESS, &error);
+
+ if (addr == LLDB_INVALID_ADDRESS) {
+ result.AppendError("invalid start address expression.");
+ result.AppendError(error.AsCString());
+ return false;
+ }
+
+ ABISP abi;
+ if (Process *proc = m_exe_ctx.GetProcessPtr())
+ abi = proc->GetABI();
+
+ if (abi)
+ addr = abi->FixDataAddress(addr);
+
+ if (argc == 2) {
+ lldb::addr_t end_addr = OptionArgParser::ToAddress(
+ &m_exe_ctx, command[1].ref(), LLDB_INVALID_ADDRESS, nullptr);
+ if (end_addr != LLDB_INVALID_ADDRESS && abi)
+ end_addr = abi->FixDataAddress(end_addr);
+
+ if (end_addr == LLDB_INVALID_ADDRESS) {
+ result.AppendError("invalid end address expression.");
+ result.AppendError(error.AsCString());
+ return false;
+ } else if (end_addr <= addr) {
+ result.AppendErrorWithFormat(
+ "end address (0x%" PRIx64
+ ") must be greater than the start address (0x%" PRIx64 ").\n",
+ end_addr, addr);
+ return false;
+ } else if (m_format_options.GetCountValue().OptionWasSet()) {
+ result.AppendErrorWithFormat(
+ "specify either the end address (0x%" PRIx64
+ ") or the count (--count %" PRIu64 "), not both.\n",
+ end_addr, (uint64_t)item_count);
+ return false;
+ }
+
+ total_byte_size = end_addr - addr;
+ item_count = total_byte_size / item_byte_size;
+ }
+
+ uint32_t max_unforced_size = target->GetMaximumMemReadSize();
+
+ if (total_byte_size > max_unforced_size && !m_memory_options.m_force) {
+ result.AppendErrorWithFormat(
+ "Normally, \'memory read\' will not read over %" PRIu32
+ " bytes of data.\n",
+ max_unforced_size);
+ result.AppendErrorWithFormat(
+ "Please use --force to override this restriction just once.\n");
+ result.AppendErrorWithFormat("or set target.max-memory-read-size if you "
+ "will often need a larger limit.\n");
+ return false;
+ }
+
+ WritableDataBufferSP data_sp;
+ size_t bytes_read = 0;
+ if (compiler_type.GetOpaqueQualType()) {
+ // Make sure we don't display our type as ASCII bytes like the default
+ // memory read
+ if (!m_format_options.GetFormatValue().OptionWasSet())
+ m_format_options.GetFormatValue().SetCurrentValue(eFormatDefault);
+
+ llvm::Optional<uint64_t> size = compiler_type.GetByteSize(nullptr);
+ if (!size) {
+ result.AppendError("can't get size of type");
+ return false;
+ }
+ bytes_read = *size * m_format_options.GetCountValue().GetCurrentValue();
+
+ if (argc > 0)
+ addr = addr + (*size * m_memory_options.m_offset.GetCurrentValue());
+ } else if (m_format_options.GetFormatValue().GetCurrentValue() !=
+ eFormatCString) {
+ data_sp = std::make_shared<DataBufferHeap>(total_byte_size, '\0');
+ if (data_sp->GetBytes() == nullptr) {
+ result.AppendErrorWithFormat(
+ "can't allocate 0x%" PRIx32
+ " bytes for the memory read buffer, specify a smaller size to read",
+ (uint32_t)total_byte_size);
+ return false;
+ }
+
+ Address address(addr, nullptr);
+ bytes_read = target->ReadMemory(address, data_sp->GetBytes(),
+ data_sp->GetByteSize(), error, true);
+ if (bytes_read == 0) {
+ const char *error_cstr = error.AsCString();
+ if (error_cstr && error_cstr[0]) {
+ result.AppendError(error_cstr);
+ } else {
+ result.AppendErrorWithFormat(
+ "failed to read memory from 0x%" PRIx64 ".\n", addr);
+ }
+ return false;
+ }
+
+ if (bytes_read < total_byte_size)
+ result.AppendWarningWithFormat(
+ "Not all bytes (%" PRIu64 "/%" PRIu64
+ ") were able to be read from 0x%" PRIx64 ".\n",
+ (uint64_t)bytes_read, (uint64_t)total_byte_size, addr);
+ } else {
+ // we treat c-strings as a special case because they do not have a fixed
+ // size
+ if (m_format_options.GetByteSizeValue().OptionWasSet() &&
+ !m_format_options.HasGDBFormat())
+ item_byte_size = m_format_options.GetByteSizeValue().GetCurrentValue();
+ else
+ item_byte_size = target->GetMaximumSizeOfStringSummary();
+ if (!m_format_options.GetCountValue().OptionWasSet())
+ item_count = 1;
+ data_sp = std::make_shared<DataBufferHeap>(
+ (item_byte_size + 1) * item_count,
+ '\0'); // account for NULLs as necessary
+ if (data_sp->GetBytes() == nullptr) {
+ result.AppendErrorWithFormat(
+ "can't allocate 0x%" PRIx64
+ " bytes for the memory read buffer, specify a smaller size to read",
+ (uint64_t)((item_byte_size + 1) * item_count));
+ return false;
+ }
+ uint8_t *data_ptr = data_sp->GetBytes();
+ auto data_addr = addr;
+ auto count = item_count;
+ item_count = 0;
+ bool break_on_no_NULL = false;
+ while (item_count < count) {
+ std::string buffer;
+ buffer.resize(item_byte_size + 1, 0);
+ Status error;
+ size_t read = target->ReadCStringFromMemory(data_addr, &buffer[0],
+ item_byte_size + 1, error);
+ if (error.Fail()) {
+ result.AppendErrorWithFormat(
+ "failed to read memory from 0x%" PRIx64 ".\n", addr);
+ return false;
+ }
+
+ if (item_byte_size == read) {
+ result.AppendWarningWithFormat(
+ "unable to find a NULL terminated string at 0x%" PRIx64
+ ". Consider increasing the maximum read length.\n",
+ data_addr);
+ --read;
+ break_on_no_NULL = true;
+ } else
+ ++read; // account for final NULL byte
+
+ memcpy(data_ptr, &buffer[0], read);
+ data_ptr += read;
+ data_addr += read;
+ bytes_read += read;
+ item_count++; // if we break early we know we only read item_count
+ // strings
+
+ if (break_on_no_NULL)
+ break;
+ }
+ data_sp =
+ std::make_shared<DataBufferHeap>(data_sp->GetBytes(), bytes_read + 1);
+ }
+
+ m_next_addr = addr + bytes_read;
+ m_prev_byte_size = bytes_read;
+ m_prev_format_options = m_format_options;
+ m_prev_memory_options = m_memory_options;
+ m_prev_outfile_options = m_outfile_options;
+ m_prev_varobj_options = m_varobj_options;
+ m_prev_memory_tag_options = m_memory_tag_options;
+ m_prev_compiler_type = compiler_type;
+
+ std::unique_ptr<Stream> output_stream_storage;
+ Stream *output_stream_p = nullptr;
+ const FileSpec &outfile_spec =
+ m_outfile_options.GetFile().GetCurrentValue();
+
+ std::string path = outfile_spec.GetPath();
+ if (outfile_spec) {
+
+ File::OpenOptions open_options =
+ File::eOpenOptionWriteOnly | File::eOpenOptionCanCreate;
+ const bool append = m_outfile_options.GetAppend().GetCurrentValue();
+ open_options |=
+ append ? File::eOpenOptionAppend : File::eOpenOptionTruncate;
+
+ auto outfile = FileSystem::Instance().Open(outfile_spec, open_options);
+
+ if (outfile) {
+ auto outfile_stream_up =
+ std::make_unique<StreamFile>(std::move(outfile.get()));
+ if (m_memory_options.m_output_as_binary) {
+ const size_t bytes_written =
+ outfile_stream_up->Write(data_sp->GetBytes(), bytes_read);
+ if (bytes_written > 0) {
+ result.GetOutputStream().Printf(
+ "%zi bytes %s to '%s'\n", bytes_written,
+ append ? "appended" : "written", path.c_str());
+ return true;
+ } else {
+ result.AppendErrorWithFormat("Failed to write %" PRIu64
+ " bytes to '%s'.\n",
+ (uint64_t)bytes_read, path.c_str());
+ return false;
+ }
+ } else {
+ // We are going to write ASCII to the file just point the
+ // output_stream to our outfile_stream...
+ output_stream_storage = std::move(outfile_stream_up);
+ output_stream_p = output_stream_storage.get();
+ }
+ } else {
+ result.AppendErrorWithFormat("Failed to open file '%s' for %s:\n",
+ path.c_str(), append ? "append" : "write");
+
+ result.AppendError(llvm::toString(outfile.takeError()));
+ return false;
+ }
+ } else {
+ output_stream_p = &result.GetOutputStream();
+ }
+
+ ExecutionContextScope *exe_scope = m_exe_ctx.GetBestExecutionContextScope();
+ if (compiler_type.GetOpaqueQualType()) {
+ for (uint32_t i = 0; i < item_count; ++i) {
+ addr_t item_addr = addr + (i * item_byte_size);
+ Address address(item_addr);
+ StreamString name_strm;
+ name_strm.Printf("0x%" PRIx64, item_addr);
+ ValueObjectSP valobj_sp(ValueObjectMemory::Create(
+ exe_scope, name_strm.GetString(), address, compiler_type));
+ if (valobj_sp) {
+ Format format = m_format_options.GetFormat();
+ if (format != eFormatDefault)
+ valobj_sp->SetFormat(format);
+
+ DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(
+ eLanguageRuntimeDescriptionDisplayVerbosityFull, format));
+
+ valobj_sp->Dump(*output_stream_p, options);
+ } else {
+ result.AppendErrorWithFormat(
+ "failed to create a value object for: (%s) %s\n",
+ view_as_type_cstr, name_strm.GetData());
+ return false;
+ }
+ }
+ return true;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ DataExtractor data(data_sp, target->GetArchitecture().GetByteOrder(),
+ target->GetArchitecture().GetAddressByteSize(),
+ target->GetArchitecture().GetDataByteSize());
+
+ Format format = m_format_options.GetFormat();
+ if (((format == eFormatChar) || (format == eFormatCharPrintable)) &&
+ (item_byte_size != 1)) {
+ // if a count was not passed, or it is 1
+ if (!m_format_options.GetCountValue().OptionWasSet() || item_count == 1) {
+ // this turns requests such as
+ // memory read -fc -s10 -c1 *charPtrPtr
+ // which make no sense (what is a char of size 10?) into a request for
+ // fetching 10 chars of size 1 from the same memory location
+ format = eFormatCharArray;
+ item_count = item_byte_size;
+ item_byte_size = 1;
+ } else {
+ // here we passed a count, and it was not 1 so we have a byte_size and
+ // a count we could well multiply those, but instead let's just fail
+ result.AppendErrorWithFormat(
+ "reading memory as characters of size %" PRIu64 " is not supported",
+ (uint64_t)item_byte_size);
+ return false;
+ }
+ }
+
+ assert(output_stream_p);
+ size_t bytes_dumped = DumpDataExtractor(
+ data, output_stream_p, 0, format, item_byte_size, item_count,
+ num_per_line / target->GetArchitecture().GetDataByteSize(), addr, 0, 0,
+ exe_scope, m_memory_tag_options.GetShowTags().GetCurrentValue());
+ m_next_addr = addr + bytes_dumped;
+ output_stream_p->EOL();
+ return true;
+ }
+
+ OptionGroupOptions m_option_group;
+ OptionGroupFormat m_format_options;
+ OptionGroupReadMemory m_memory_options;
+ OptionGroupOutputFile m_outfile_options;
+ OptionGroupValueObjectDisplay m_varobj_options;
+ OptionGroupMemoryTag m_memory_tag_options;
+ lldb::addr_t m_next_addr = LLDB_INVALID_ADDRESS;
+ lldb::addr_t m_prev_byte_size = 0;
+ OptionGroupFormat m_prev_format_options;
+ OptionGroupReadMemory m_prev_memory_options;
+ OptionGroupOutputFile m_prev_outfile_options;
+ OptionGroupValueObjectDisplay m_prev_varobj_options;
+ OptionGroupMemoryTag m_prev_memory_tag_options;
+ CompilerType m_prev_compiler_type;
+};
+
+#define LLDB_OPTIONS_memory_find
+#include "CommandOptions.inc"
+
+// Find the specified data in memory
+class CommandObjectMemoryFind : public CommandObjectParsed {
+public:
+ class OptionGroupFindMemory : public OptionGroup {
+ public:
+ OptionGroupFindMemory() : m_count(1), m_offset(0) {}
+
+ ~OptionGroupFindMemory() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_memory_find_options);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = g_memory_find_options[option_idx].short_option;
+
+ switch (short_option) {
+ case 'e':
+ m_expr.SetValueFromString(option_value);
+ break;
+
+ case 's':
+ m_string.SetValueFromString(option_value);
+ break;
+
+ case 'c':
+ if (m_count.SetValueFromString(option_value).Fail())
+ error.SetErrorString("unrecognized value for count");
+ break;
+
+ case 'o':
+ if (m_offset.SetValueFromString(option_value).Fail())
+ error.SetErrorString("unrecognized value for dump-offset");
+ break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_expr.Clear();
+ m_string.Clear();
+ m_count.Clear();
+ }
+
+ OptionValueString m_expr;
+ OptionValueString m_string;
+ OptionValueUInt64 m_count;
+ OptionValueUInt64 m_offset;
+ };
+
+ CommandObjectMemoryFind(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "memory find",
+ "Find a value in the memory of the current target process.",
+ nullptr, eCommandRequiresProcess | eCommandProcessMustBeLaunched) {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentData addr_arg;
+ CommandArgumentData value_arg;
+
+ // Define the first (and only) variant of this arg.
+ addr_arg.arg_type = eArgTypeAddressOrExpression;
+ addr_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(addr_arg);
+
+ // Define the first (and only) variant of this arg.
+ value_arg.arg_type = eArgTypeAddressOrExpression;
+ value_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg2.push_back(value_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+
+ m_option_group.Append(&m_memory_options);
+ m_option_group.Append(&m_memory_tag_options, LLDB_OPT_SET_ALL,
+ LLDB_OPT_SET_ALL);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectMemoryFind() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ class ProcessMemoryIterator {
+ public:
+ ProcessMemoryIterator(ProcessSP process_sp, lldb::addr_t base)
+ : m_process_sp(process_sp), m_base_addr(base) {
+ lldbassert(process_sp.get() != nullptr);
+ }
+
+ bool IsValid() { return m_is_valid; }
+
+ uint8_t operator[](lldb::addr_t offset) {
+ if (!IsValid())
+ return 0;
+
+ uint8_t retval = 0;
+ Status error;
+ if (0 ==
+ m_process_sp->ReadMemory(m_base_addr + offset, &retval, 1, error)) {
+ m_is_valid = false;
+ return 0;
+ }
+
+ return retval;
+ }
+
+ private:
+ ProcessSP m_process_sp;
+ lldb::addr_t m_base_addr;
+ bool m_is_valid = true;
+ };
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ // No need to check "process" for validity as eCommandRequiresProcess
+ // ensures it is valid
+ Process *process = m_exe_ctx.GetProcessPtr();
+
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc != 2) {
+ result.AppendError("two addresses needed for memory find");
+ return false;
+ }
+
+ Status error;
+ lldb::addr_t low_addr = OptionArgParser::ToAddress(
+ &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error);
+ if (low_addr == LLDB_INVALID_ADDRESS || error.Fail()) {
+ result.AppendError("invalid low address");
+ return false;
+ }
+ lldb::addr_t high_addr = OptionArgParser::ToAddress(
+ &m_exe_ctx, command[1].ref(), LLDB_INVALID_ADDRESS, &error);
+ if (high_addr == LLDB_INVALID_ADDRESS || error.Fail()) {
+ result.AppendError("invalid high address");
+ return false;
+ }
+
+ ABISP abi = m_exe_ctx.GetProcessPtr()->GetABI();
+ if (abi) {
+ low_addr = abi->FixDataAddress(low_addr);
+ high_addr = abi->FixDataAddress(high_addr);
+ }
+
+ if (high_addr <= low_addr) {
+ result.AppendError(
+ "starting address must be smaller than ending address");
+ return false;
+ }
+
+ lldb::addr_t found_location = LLDB_INVALID_ADDRESS;
+
+ DataBufferHeap buffer;
+
+ if (m_memory_options.m_string.OptionWasSet()) {
+ llvm::StringRef str = m_memory_options.m_string.GetStringValue();
+ if (str.empty()) {
+ result.AppendError("search string must have non-zero length.");
+ return false;
+ }
+ buffer.CopyData(str);
+ } else if (m_memory_options.m_expr.OptionWasSet()) {
+ StackFrame *frame = m_exe_ctx.GetFramePtr();
+ ValueObjectSP result_sp;
+ if ((eExpressionCompleted ==
+ process->GetTarget().EvaluateExpression(
+ m_memory_options.m_expr.GetStringValue(), frame, result_sp)) &&
+ result_sp) {
+ uint64_t value = result_sp->GetValueAsUnsigned(0);
+ llvm::Optional<uint64_t> size =
+ result_sp->GetCompilerType().GetByteSize(nullptr);
+ if (!size)
+ return false;
+ switch (*size) {
+ case 1: {
+ uint8_t byte = (uint8_t)value;
+ buffer.CopyData(&byte, 1);
+ } break;
+ case 2: {
+ uint16_t word = (uint16_t)value;
+ buffer.CopyData(&word, 2);
+ } break;
+ case 4: {
+ uint32_t lword = (uint32_t)value;
+ buffer.CopyData(&lword, 4);
+ } break;
+ case 8: {
+ buffer.CopyData(&value, 8);
+ } break;
+ case 3:
+ case 5:
+ case 6:
+ case 7:
+ result.AppendError("unknown type. pass a string instead");
+ return false;
+ default:
+ result.AppendError(
+ "result size larger than 8 bytes. pass a string instead");
+ return false;
+ }
+ } else {
+ result.AppendError(
+ "expression evaluation failed. pass a string instead");
+ return false;
+ }
+ } else {
+ result.AppendError(
+ "please pass either a block of text, or an expression to evaluate.");
+ return false;
+ }
+
+ size_t count = m_memory_options.m_count.GetCurrentValue();
+ found_location = low_addr;
+ bool ever_found = false;
+ while (count) {
+ found_location = FastSearch(found_location, high_addr, buffer.GetBytes(),
+ buffer.GetByteSize());
+ if (found_location == LLDB_INVALID_ADDRESS) {
+ if (!ever_found) {
+ result.AppendMessage("data not found within the range.\n");
+ result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
+ } else
+ result.AppendMessage("no more matches within the range.\n");
+ break;
+ }
+ result.AppendMessageWithFormat("data found at location: 0x%" PRIx64 "\n",
+ found_location);
+
+ DataBufferHeap dumpbuffer(32, 0);
+ process->ReadMemory(
+ found_location + m_memory_options.m_offset.GetCurrentValue(),
+ dumpbuffer.GetBytes(), dumpbuffer.GetByteSize(), error);
+ if (!error.Fail()) {
+ DataExtractor data(dumpbuffer.GetBytes(), dumpbuffer.GetByteSize(),
+ process->GetByteOrder(),
+ process->GetAddressByteSize());
+ DumpDataExtractor(
+ data, &result.GetOutputStream(), 0, lldb::eFormatBytesWithASCII, 1,
+ dumpbuffer.GetByteSize(), 16,
+ found_location + m_memory_options.m_offset.GetCurrentValue(), 0, 0,
+ m_exe_ctx.GetBestExecutionContextScope(),
+ m_memory_tag_options.GetShowTags().GetCurrentValue());
+ result.GetOutputStream().EOL();
+ }
+
+ --count;
+ found_location++;
+ ever_found = true;
+ }
+
+ result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
+ return true;
+ }
+
+ lldb::addr_t FastSearch(lldb::addr_t low, lldb::addr_t high, uint8_t *buffer,
+ size_t buffer_size) {
+ const size_t region_size = high - low;
+
+ if (region_size < buffer_size)
+ return LLDB_INVALID_ADDRESS;
+
+ std::vector<size_t> bad_char_heuristic(256, buffer_size);
+ ProcessSP process_sp = m_exe_ctx.GetProcessSP();
+ ProcessMemoryIterator iterator(process_sp, low);
+
+ for (size_t idx = 0; idx < buffer_size - 1; idx++) {
+ decltype(bad_char_heuristic)::size_type bcu_idx = buffer[idx];
+ bad_char_heuristic[bcu_idx] = buffer_size - idx - 1;
+ }
+ for (size_t s = 0; s <= (region_size - buffer_size);) {
+ int64_t j = buffer_size - 1;
+ while (j >= 0 && buffer[j] == iterator[s + j])
+ j--;
+ if (j < 0)
+ return low + s;
+ else
+ s += bad_char_heuristic[iterator[s + buffer_size - 1]];
+ }
+
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ OptionGroupOptions m_option_group;
+ OptionGroupFindMemory m_memory_options;
+ OptionGroupMemoryTag m_memory_tag_options;
+};
+
+#define LLDB_OPTIONS_memory_write
+#include "CommandOptions.inc"
+
+// Write memory to the inferior process
+class CommandObjectMemoryWrite : public CommandObjectParsed {
+public:
+ class OptionGroupWriteMemory : public OptionGroup {
+ public:
+ OptionGroupWriteMemory() = default;
+
+ ~OptionGroupWriteMemory() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_memory_write_options);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = g_memory_write_options[option_idx].short_option;
+
+ switch (short_option) {
+ case 'i':
+ m_infile.SetFile(option_value, FileSpec::Style::native);
+ FileSystem::Instance().Resolve(m_infile);
+ if (!FileSystem::Instance().Exists(m_infile)) {
+ m_infile.Clear();
+ error.SetErrorStringWithFormat("input file does not exist: '%s'",
+ option_value.str().c_str());
+ }
+ break;
+
+ case 'o': {
+ if (option_value.getAsInteger(0, m_infile_offset)) {
+ m_infile_offset = 0;
+ error.SetErrorStringWithFormat("invalid offset string '%s'",
+ option_value.str().c_str());
+ }
+ } break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_infile.Clear();
+ m_infile_offset = 0;
+ }
+
+ FileSpec m_infile;
+ off_t m_infile_offset;
+ };
+
+ CommandObjectMemoryWrite(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "memory write",
+ "Write to the memory of the current target process.", nullptr,
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched),
+ m_format_options(
+ eFormatBytes, 1, UINT64_MAX,
+ {std::make_tuple(
+ eArgTypeFormat,
+ "The format to use for each of the value to be written."),
+ std::make_tuple(eArgTypeByteSize,
+ "The size in bytes to write from input file or "
+ "each value.")}) {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentData addr_arg;
+ CommandArgumentData value_arg;
+
+ // Define the first (and only) variant of this arg.
+ addr_arg.arg_type = eArgTypeAddress;
+ addr_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(addr_arg);
+
+ // Define the first (and only) variant of this arg.
+ value_arg.arg_type = eArgTypeValue;
+ value_arg.arg_repetition = eArgRepeatPlus;
+ value_arg.arg_opt_set_association = LLDB_OPT_SET_1;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg2.push_back(value_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+
+ m_option_group.Append(&m_format_options,
+ OptionGroupFormat::OPTION_GROUP_FORMAT,
+ LLDB_OPT_SET_1);
+ m_option_group.Append(&m_format_options,
+ OptionGroupFormat::OPTION_GROUP_SIZE,
+ LLDB_OPT_SET_1 | LLDB_OPT_SET_2);
+ m_option_group.Append(&m_memory_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_2);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectMemoryWrite() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ // No need to check "process" for validity as eCommandRequiresProcess
+ // ensures it is valid
+ Process *process = m_exe_ctx.GetProcessPtr();
+
+ const size_t argc = command.GetArgumentCount();
+
+ if (m_memory_options.m_infile) {
+ if (argc < 1) {
+ result.AppendErrorWithFormat(
+ "%s takes a destination address when writing file contents.\n",
+ m_cmd_name.c_str());
+ return false;
+ }
+ if (argc > 1) {
+ result.AppendErrorWithFormat(
+ "%s takes only a destination address when writing file contents.\n",
+ m_cmd_name.c_str());
+ return false;
+ }
+ } else if (argc < 2) {
+ result.AppendErrorWithFormat(
+ "%s takes a destination address and at least one value.\n",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ StreamString buffer(
+ Stream::eBinary,
+ process->GetTarget().GetArchitecture().GetAddressByteSize(),
+ process->GetTarget().GetArchitecture().GetByteOrder());
+
+ OptionValueUInt64 &byte_size_value = m_format_options.GetByteSizeValue();
+ size_t item_byte_size = byte_size_value.GetCurrentValue();
+
+ Status error;
+ lldb::addr_t addr = OptionArgParser::ToAddress(
+ &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error);
+
+ if (addr == LLDB_INVALID_ADDRESS) {
+ result.AppendError("invalid address expression\n");
+ result.AppendError(error.AsCString());
+ return false;
+ }
+
+ if (m_memory_options.m_infile) {
+ size_t length = SIZE_MAX;
+ if (item_byte_size > 1)
+ length = item_byte_size;
+ auto data_sp = FileSystem::Instance().CreateDataBuffer(
+ m_memory_options.m_infile.GetPath(), length,
+ m_memory_options.m_infile_offset);
+ if (data_sp) {
+ length = data_sp->GetByteSize();
+ if (length > 0) {
+ Status error;
+ size_t bytes_written =
+ process->WriteMemory(addr, data_sp->GetBytes(), length, error);
+
+ if (bytes_written == length) {
+ // All bytes written
+ result.GetOutputStream().Printf(
+ "%" PRIu64 " bytes were written to 0x%" PRIx64 "\n",
+ (uint64_t)bytes_written, addr);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else if (bytes_written > 0) {
+ // Some byte written
+ result.GetOutputStream().Printf(
+ "%" PRIu64 " bytes of %" PRIu64
+ " requested were written to 0x%" PRIx64 "\n",
+ (uint64_t)bytes_written, (uint64_t)length, addr);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("Memory write to 0x%" PRIx64
+ " failed: %s.\n",
+ addr, error.AsCString());
+ }
+ }
+ } else {
+ result.AppendErrorWithFormat("Unable to read contents of file.\n");
+ }
+ return result.Succeeded();
+ } else if (item_byte_size == 0) {
+ if (m_format_options.GetFormat() == eFormatPointer)
+ item_byte_size = buffer.GetAddressByteSize();
+ else
+ item_byte_size = 1;
+ }
+
+ command.Shift(); // shift off the address argument
+ uint64_t uval64;
+ int64_t sval64;
+ bool success = false;
+ for (auto &entry : command) {
+ switch (m_format_options.GetFormat()) {
+ case kNumFormats:
+ case eFormatFloat: // TODO: add support for floats soon
+ case eFormatCharPrintable:
+ case eFormatBytesWithASCII:
+ case eFormatComplex:
+ case eFormatEnum:
+ case eFormatUnicode8:
+ case eFormatUnicode16:
+ case eFormatUnicode32:
+ case eFormatVectorOfChar:
+ case eFormatVectorOfSInt8:
+ case eFormatVectorOfUInt8:
+ case eFormatVectorOfSInt16:
+ case eFormatVectorOfUInt16:
+ case eFormatVectorOfSInt32:
+ case eFormatVectorOfUInt32:
+ case eFormatVectorOfSInt64:
+ case eFormatVectorOfUInt64:
+ case eFormatVectorOfFloat16:
+ case eFormatVectorOfFloat32:
+ case eFormatVectorOfFloat64:
+ case eFormatVectorOfUInt128:
+ case eFormatOSType:
+ case eFormatComplexInteger:
+ case eFormatAddressInfo:
+ case eFormatHexFloat:
+ case eFormatInstruction:
+ case eFormatVoid:
+ result.AppendError("unsupported format for writing memory");
+ return false;
+
+ case eFormatDefault:
+ case eFormatBytes:
+ case eFormatHex:
+ case eFormatHexUppercase:
+ case eFormatPointer: {
+ // Decode hex bytes
+ // Be careful, getAsInteger with a radix of 16 rejects "0xab" so we
+ // have to special case that:
+ bool success = false;
+ if (entry.ref().startswith("0x"))
+ success = !entry.ref().getAsInteger(0, uval64);
+ if (!success)
+ success = !entry.ref().getAsInteger(16, uval64);
+ if (!success) {
+ result.AppendErrorWithFormat(
+ "'%s' is not a valid hex string value.\n", entry.c_str());
+ return false;
+ } else if (!llvm::isUIntN(item_byte_size * 8, uval64)) {
+ result.AppendErrorWithFormat("Value 0x%" PRIx64
+ " is too large to fit in a %" PRIu64
+ " byte unsigned integer value.\n",
+ uval64, (uint64_t)item_byte_size);
+ return false;
+ }
+ buffer.PutMaxHex64(uval64, item_byte_size);
+ break;
+ }
+ case eFormatBoolean:
+ uval64 = OptionArgParser::ToBoolean(entry.ref(), false, &success);
+ if (!success) {
+ result.AppendErrorWithFormat(
+ "'%s' is not a valid boolean string value.\n", entry.c_str());
+ return false;
+ }
+ buffer.PutMaxHex64(uval64, item_byte_size);
+ break;
+
+ case eFormatBinary:
+ if (entry.ref().getAsInteger(2, uval64)) {
+ result.AppendErrorWithFormat(
+ "'%s' is not a valid binary string value.\n", entry.c_str());
+ return false;
+ } else if (!llvm::isUIntN(item_byte_size * 8, uval64)) {
+ result.AppendErrorWithFormat("Value 0x%" PRIx64
+ " is too large to fit in a %" PRIu64
+ " byte unsigned integer value.\n",
+ uval64, (uint64_t)item_byte_size);
+ return false;
+ }
+ buffer.PutMaxHex64(uval64, item_byte_size);
+ break;
+
+ case eFormatCharArray:
+ case eFormatChar:
+ case eFormatCString: {
+ if (entry.ref().empty())
+ break;
+
+ size_t len = entry.ref().size();
+ // Include the NULL for C strings...
+ if (m_format_options.GetFormat() == eFormatCString)
+ ++len;
+ Status error;
+ if (process->WriteMemory(addr, entry.c_str(), len, error) == len) {
+ addr += len;
+ } else {
+ result.AppendErrorWithFormat("Memory write to 0x%" PRIx64
+ " failed: %s.\n",
+ addr, error.AsCString());
+ return false;
+ }
+ break;
+ }
+ case eFormatDecimal:
+ if (entry.ref().getAsInteger(0, sval64)) {
+ result.AppendErrorWithFormat(
+ "'%s' is not a valid signed decimal value.\n", entry.c_str());
+ return false;
+ } else if (!llvm::isIntN(item_byte_size * 8, sval64)) {
+ result.AppendErrorWithFormat(
+ "Value %" PRIi64 " is too large or small to fit in a %" PRIu64
+ " byte signed integer value.\n",
+ sval64, (uint64_t)item_byte_size);
+ return false;
+ }
+ buffer.PutMaxHex64(sval64, item_byte_size);
+ break;
+
+ case eFormatUnsigned:
+
+ if (entry.ref().getAsInteger(0, uval64)) {
+ result.AppendErrorWithFormat(
+ "'%s' is not a valid unsigned decimal string value.\n",
+ entry.c_str());
+ return false;
+ } else if (!llvm::isUIntN(item_byte_size * 8, uval64)) {
+ result.AppendErrorWithFormat("Value %" PRIu64
+ " is too large to fit in a %" PRIu64
+ " byte unsigned integer value.\n",
+ uval64, (uint64_t)item_byte_size);
+ return false;
+ }
+ buffer.PutMaxHex64(uval64, item_byte_size);
+ break;
+
+ case eFormatOctal:
+ if (entry.ref().getAsInteger(8, uval64)) {
+ result.AppendErrorWithFormat(
+ "'%s' is not a valid octal string value.\n", entry.c_str());
+ return false;
+ } else if (!llvm::isUIntN(item_byte_size * 8, uval64)) {
+ result.AppendErrorWithFormat("Value %" PRIo64
+ " is too large to fit in a %" PRIu64
+ " byte unsigned integer value.\n",
+ uval64, (uint64_t)item_byte_size);
+ return false;
+ }
+ buffer.PutMaxHex64(uval64, item_byte_size);
+ break;
+ }
+ }
+
+ if (!buffer.GetString().empty()) {
+ Status error;
+ if (process->WriteMemory(addr, buffer.GetString().data(),
+ buffer.GetString().size(),
+ error) == buffer.GetString().size())
+ return true;
+ else {
+ result.AppendErrorWithFormat("Memory write to 0x%" PRIx64
+ " failed: %s.\n",
+ addr, error.AsCString());
+ return false;
+ }
+ }
+ return true;
+ }
+
+ OptionGroupOptions m_option_group;
+ OptionGroupFormat m_format_options;
+ OptionGroupWriteMemory m_memory_options;
+};
+
+// Get malloc/free history of a memory address.
+class CommandObjectMemoryHistory : public CommandObjectParsed {
+public:
+ CommandObjectMemoryHistory(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "memory history",
+ "Print recorded stack traces for "
+ "allocation/deallocation events "
+ "associated with an address.",
+ nullptr,
+ eCommandRequiresTarget | eCommandRequiresProcess |
+ eCommandProcessMustBePaused |
+ eCommandProcessMustBeLaunched) {
+ CommandArgumentEntry arg1;
+ CommandArgumentData addr_arg;
+
+ // Define the first (and only) variant of this arg.
+ addr_arg.arg_type = eArgTypeAddress;
+ addr_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(addr_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ }
+
+ ~CommandObjectMemoryHistory() override = default;
+
+ llvm::Optional<std::string> GetRepeatCommand(Args &current_command_args,
+ uint32_t index) override {
+ return m_cmd_name;
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc == 0 || argc > 1) {
+ result.AppendErrorWithFormat("%s takes an address expression",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ Status error;
+ lldb::addr_t addr = OptionArgParser::ToAddress(
+ &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error);
+
+ if (addr == LLDB_INVALID_ADDRESS) {
+ result.AppendError("invalid address expression");
+ result.AppendError(error.AsCString());
+ return false;
+ }
+
+ Stream *output_stream = &result.GetOutputStream();
+
+ const ProcessSP &process_sp = m_exe_ctx.GetProcessSP();
+ const MemoryHistorySP &memory_history =
+ MemoryHistory::FindPlugin(process_sp);
+
+ if (!memory_history) {
+ result.AppendError("no available memory history provider");
+ return false;
+ }
+
+ HistoryThreads thread_list = memory_history->GetHistoryThreads(addr);
+
+ const bool stop_format = false;
+ for (auto thread : thread_list) {
+ thread->GetStatus(*output_stream, 0, UINT32_MAX, 0, stop_format);
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ return true;
+ }
+};
+
+// CommandObjectMemoryRegion
+#pragma mark CommandObjectMemoryRegion
+
+#define LLDB_OPTIONS_memory_region
+#include "CommandOptions.inc"
+
+class CommandObjectMemoryRegion : public CommandObjectParsed {
+public:
+ class OptionGroupMemoryRegion : public OptionGroup {
+ public:
+ OptionGroupMemoryRegion() : OptionGroup(), m_all(false, false) {}
+
+ ~OptionGroupMemoryRegion() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_memory_region_options);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
+ ExecutionContext *execution_context) override {
+ Status status;
+ const int short_option = g_memory_region_options[option_idx].short_option;
+
+ switch (short_option) {
+ case 'a':
+ m_all.SetCurrentValue(true);
+ m_all.SetOptionWasSet();
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return status;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_all.Clear();
+ }
+
+ OptionValueBoolean m_all;
+ };
+
+ CommandObjectMemoryRegion(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "memory region",
+ "Get information on the memory region containing "
+ "an address in the current target process.",
+ "memory region <address-expression> (or --all)",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched) {
+ // Address in option set 1.
+ m_arguments.push_back(CommandArgumentEntry{CommandArgumentData(
+ eArgTypeAddressOrExpression, eArgRepeatPlain, LLDB_OPT_SET_1)});
+ // "--all" will go in option set 2.
+ m_option_group.Append(&m_memory_region_options);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectMemoryRegion() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ void DumpRegion(CommandReturnObject &result, Target &target,
+ const MemoryRegionInfo &range_info, lldb::addr_t load_addr) {
+ lldb_private::Address addr;
+ ConstString section_name;
+ if (target.ResolveLoadAddress(load_addr, addr)) {
+ SectionSP section_sp(addr.GetSection());
+ if (section_sp) {
+ // Got the top most section, not the deepest section
+ while (section_sp->GetParent())
+ section_sp = section_sp->GetParent();
+ section_name = section_sp->GetName();
+ }
+ }
+
+ ConstString name = range_info.GetName();
+ result.AppendMessageWithFormatv(
+ "[{0:x16}-{1:x16}) {2:r}{3:w}{4:x}{5}{6}{7}{8}",
+ range_info.GetRange().GetRangeBase(),
+ range_info.GetRange().GetRangeEnd(), range_info.GetReadable(),
+ range_info.GetWritable(), range_info.GetExecutable(), name ? " " : "",
+ name, section_name ? " " : "", section_name);
+ MemoryRegionInfo::OptionalBool memory_tagged = range_info.GetMemoryTagged();
+ if (memory_tagged == MemoryRegionInfo::OptionalBool::eYes)
+ result.AppendMessage("memory tagging: enabled");
+
+ const llvm::Optional<std::vector<addr_t>> &dirty_page_list =
+ range_info.GetDirtyPageList();
+ if (dirty_page_list) {
+ const size_t page_count = dirty_page_list.getValue().size();
+ result.AppendMessageWithFormat(
+ "Modified memory (dirty) page list provided, %zu entries.\n",
+ page_count);
+ if (page_count > 0) {
+ bool print_comma = false;
+ result.AppendMessageWithFormat("Dirty pages: ");
+ for (size_t i = 0; i < page_count; i++) {
+ if (print_comma)
+ result.AppendMessageWithFormat(", ");
+ else
+ print_comma = true;
+ result.AppendMessageWithFormat("0x%" PRIx64, (*dirty_page_list)[i]);
+ }
+ result.AppendMessageWithFormat(".\n");
+ }
+ }
+ }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ ProcessSP process_sp = m_exe_ctx.GetProcessSP();
+ if (!process_sp) {
+ m_prev_end_addr = LLDB_INVALID_ADDRESS;
+ result.AppendError("invalid process");
+ return false;
+ }
+
+ Status error;
+ lldb::addr_t load_addr = m_prev_end_addr;
+ m_prev_end_addr = LLDB_INVALID_ADDRESS;
+
+ const size_t argc = command.GetArgumentCount();
+ const lldb::ABISP &abi = process_sp->GetABI();
+
+ if (argc == 1) {
+ if (m_memory_region_options.m_all) {
+ result.AppendError(
+ "The \"--all\" option cannot be used when an address "
+ "argument is given");
+ return false;
+ }
+
+ auto load_addr_str = command[0].ref();
+ // Non-address bits in this will be handled later by GetMemoryRegion
+ load_addr = OptionArgParser::ToAddress(&m_exe_ctx, load_addr_str,
+ LLDB_INVALID_ADDRESS, &error);
+ if (error.Fail() || load_addr == LLDB_INVALID_ADDRESS) {
+ result.AppendErrorWithFormat("invalid address argument \"%s\": %s\n",
+ command[0].c_str(), error.AsCString());
+ return false;
+ }
+ } else if (argc > 1 ||
+ // When we're repeating the command, the previous end address is
+ // used for load_addr. If that was 0xF...F then we must have
+ // reached the end of memory.
+ (argc == 0 && !m_memory_region_options.m_all &&
+ load_addr == LLDB_INVALID_ADDRESS) ||
+ // If the target has non-address bits (tags, limited virtual
+ // address size, etc.), the end of mappable memory will be lower
+ // than that. So if we find any non-address bit set, we must be
+ // at the end of the mappable range.
+ (abi && (abi->FixAnyAddress(load_addr) != load_addr))) {
+ result.AppendErrorWithFormat(
+ "'%s' takes one argument or \"--all\" option:\nUsage: %s\n",
+ m_cmd_name.c_str(), m_cmd_syntax.c_str());
+ return false;
+ }
+
+ // Is is important that we track the address used to request the region as
+ // this will give the correct section name in the case that regions overlap.
+ // On Windows we get mutliple regions that start at the same place but are
+ // different sizes and refer to different sections.
+ std::vector<std::pair<lldb_private::MemoryRegionInfo, lldb::addr_t>>
+ region_list;
+ if (m_memory_region_options.m_all) {
+ // We don't use GetMemoryRegions here because it doesn't include unmapped
+ // areas like repeating the command would. So instead, emulate doing that.
+ lldb::addr_t addr = 0;
+ while (error.Success() && addr != LLDB_INVALID_ADDRESS &&
+ // When there are non-address bits the last range will not extend
+ // to LLDB_INVALID_ADDRESS but to the max virtual address.
+ // This prevents us looping forever if that is the case.
+ (abi && (abi->FixAnyAddress(addr) == addr))) {
+ lldb_private::MemoryRegionInfo region_info;
+ error = process_sp->GetMemoryRegionInfo(addr, region_info);
+
+ if (error.Success()) {
+ region_list.push_back({region_info, addr});
+ addr = region_info.GetRange().GetRangeEnd();
+ }
+ }
+
+ // Even if we read nothing, don't error for --all
+ error.Clear();
+ } else {
+ lldb_private::MemoryRegionInfo region_info;
+ error = process_sp->GetMemoryRegionInfo(load_addr, region_info);
+ if (error.Success())
+ region_list.push_back({region_info, load_addr});
+ }
+
+ if (error.Success()) {
+ for (std::pair<MemoryRegionInfo, addr_t> &range : region_list) {
+ DumpRegion(result, process_sp->GetTarget(), range.first, range.second);
+ m_prev_end_addr = range.first.GetRange().GetRangeEnd();
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+
+ result.AppendErrorWithFormat("%s\n", error.AsCString());
+ return false;
+ }
+
+ llvm::Optional<std::string> GetRepeatCommand(Args &current_command_args,
+ uint32_t index) override {
+ // If we repeat this command, repeat it without any arguments so we can
+ // show the next memory range
+ return m_cmd_name;
+ }
+
+ lldb::addr_t m_prev_end_addr = LLDB_INVALID_ADDRESS;
+
+ OptionGroupOptions m_option_group;
+ OptionGroupMemoryRegion m_memory_region_options;
+};
+
+// CommandObjectMemory
+
+CommandObjectMemory::CommandObjectMemory(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "memory",
+ "Commands for operating on memory in the current target process.",
+ "memory <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("find",
+ CommandObjectSP(new CommandObjectMemoryFind(interpreter)));
+ LoadSubCommand("read",
+ CommandObjectSP(new CommandObjectMemoryRead(interpreter)));
+ LoadSubCommand("write",
+ CommandObjectSP(new CommandObjectMemoryWrite(interpreter)));
+ LoadSubCommand("history",
+ CommandObjectSP(new CommandObjectMemoryHistory(interpreter)));
+ LoadSubCommand("region",
+ CommandObjectSP(new CommandObjectMemoryRegion(interpreter)));
+ LoadSubCommand("tag",
+ CommandObjectSP(new CommandObjectMemoryTag(interpreter)));
+}
+
+CommandObjectMemory::~CommandObjectMemory() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectMemory.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectMemory.h
new file mode 100644
index 000000000000..5f7f6bb30b88
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectMemory.h
@@ -0,0 +1,25 @@
+//===-- CommandObjectMemory.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTMEMORY_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTMEMORY_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+class CommandObjectMemory : public CommandObjectMultiword {
+public:
+ CommandObjectMemory(CommandInterpreter &interpreter);
+
+ ~CommandObjectMemory() override;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTMEMORY_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectMemoryTag.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectMemoryTag.cpp
new file mode 100644
index 000000000000..67461cac5a29
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectMemoryTag.cpp
@@ -0,0 +1,320 @@
+//===-- CommandObjectMemoryTag.cpp ----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectMemoryTag.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionGroupFormat.h"
+#include "lldb/Interpreter/OptionValueString.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/Process.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#define LLDB_OPTIONS_memory_tag_read
+#include "CommandOptions.inc"
+
+class CommandObjectMemoryTagRead : public CommandObjectParsed {
+public:
+ CommandObjectMemoryTagRead(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "tag",
+ "Read memory tags for the given range of memory."
+ " Mismatched tags will be marked.",
+ nullptr,
+ eCommandRequiresTarget | eCommandRequiresProcess |
+ eCommandProcessMustBePaused) {
+ // Address
+ m_arguments.push_back(
+ CommandArgumentEntry{CommandArgumentData(eArgTypeAddressOrExpression)});
+ // Optional end address
+ m_arguments.push_back(CommandArgumentEntry{
+ CommandArgumentData(eArgTypeAddressOrExpression, eArgRepeatOptional)});
+ }
+
+ ~CommandObjectMemoryTagRead() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if ((command.GetArgumentCount() < 1) || (command.GetArgumentCount() > 2)) {
+ result.AppendError(
+ "wrong number of arguments; expected at least <address-expression>, "
+ "at most <address-expression> <end-address-expression>");
+ return false;
+ }
+
+ Status error;
+ addr_t start_addr = OptionArgParser::ToAddress(
+ &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error);
+ if (start_addr == LLDB_INVALID_ADDRESS) {
+ result.AppendErrorWithFormatv("Invalid address expression, {0}",
+ error.AsCString());
+ return false;
+ }
+
+ // Default 1 byte beyond start, rounds up to at most 1 granule later
+ addr_t end_addr = start_addr + 1;
+
+ if (command.GetArgumentCount() > 1) {
+ end_addr = OptionArgParser::ToAddress(&m_exe_ctx, command[1].ref(),
+ LLDB_INVALID_ADDRESS, &error);
+ if (end_addr == LLDB_INVALID_ADDRESS) {
+ result.AppendErrorWithFormatv("Invalid end address expression, {0}",
+ error.AsCString());
+ return false;
+ }
+ }
+
+ Process *process = m_exe_ctx.GetProcessPtr();
+ llvm::Expected<const MemoryTagManager *> tag_manager_or_err =
+ process->GetMemoryTagManager();
+
+ if (!tag_manager_or_err) {
+ result.SetError(Status(tag_manager_or_err.takeError()));
+ return false;
+ }
+
+ const MemoryTagManager *tag_manager = *tag_manager_or_err;
+
+ MemoryRegionInfos memory_regions;
+ // If this fails the list of regions is cleared, so we don't need to read
+ // the return status here.
+ process->GetMemoryRegions(memory_regions);
+
+ lldb::addr_t logical_tag = tag_manager->GetLogicalTag(start_addr);
+
+ // The tag manager only removes tag bits. These addresses may include other
+ // non-address bits that must also be ignored.
+ ABISP abi = process->GetABI();
+ if (abi) {
+ start_addr = abi->FixDataAddress(start_addr);
+ end_addr = abi->FixDataAddress(end_addr);
+ }
+
+ llvm::Expected<MemoryTagManager::TagRange> tagged_range =
+ tag_manager->MakeTaggedRange(start_addr, end_addr, memory_regions);
+
+ if (!tagged_range) {
+ result.SetError(Status(tagged_range.takeError()));
+ return false;
+ }
+
+ llvm::Expected<std::vector<lldb::addr_t>> tags = process->ReadMemoryTags(
+ tagged_range->GetRangeBase(), tagged_range->GetByteSize());
+
+ if (!tags) {
+ result.SetError(Status(tags.takeError()));
+ return false;
+ }
+
+ result.AppendMessageWithFormatv("Logical tag: {0:x}", logical_tag);
+ result.AppendMessage("Allocation tags:");
+
+ addr_t addr = tagged_range->GetRangeBase();
+ for (auto tag : *tags) {
+ addr_t next_addr = addr + tag_manager->GetGranuleSize();
+ // Showing tagged adresses here until we have non address bit handling
+ result.AppendMessageWithFormatv("[{0:x}, {1:x}): {2:x}{3}", addr,
+ next_addr, tag,
+ logical_tag == tag ? "" : " (mismatch)");
+ addr = next_addr;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+#define LLDB_OPTIONS_memory_tag_write
+#include "CommandOptions.inc"
+
+class CommandObjectMemoryTagWrite : public CommandObjectParsed {
+public:
+ class OptionGroupTagWrite : public OptionGroup {
+ public:
+ OptionGroupTagWrite() = default;
+
+ ~OptionGroupTagWrite() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_memory_tag_write_options);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
+ ExecutionContext *execution_context) override {
+ Status status;
+ const int short_option =
+ g_memory_tag_write_options[option_idx].short_option;
+
+ switch (short_option) {
+ case 'e':
+ m_end_addr = OptionArgParser::ToAddress(execution_context, option_value,
+ LLDB_INVALID_ADDRESS, &status);
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return status;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_end_addr = LLDB_INVALID_ADDRESS;
+ }
+
+ lldb::addr_t m_end_addr = LLDB_INVALID_ADDRESS;
+ };
+
+ CommandObjectMemoryTagWrite(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "tag",
+ "Write memory tags starting from the granule that "
+ "contains the given address.",
+ nullptr,
+ eCommandRequiresTarget | eCommandRequiresProcess |
+ eCommandProcessMustBePaused) {
+ // Address
+ m_arguments.push_back(
+ CommandArgumentEntry{CommandArgumentData(eArgTypeAddressOrExpression)});
+ // One or more tag values
+ m_arguments.push_back(CommandArgumentEntry{
+ CommandArgumentData(eArgTypeValue, eArgRepeatPlus)});
+
+ m_option_group.Append(&m_tag_write_options);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectMemoryTagWrite() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (command.GetArgumentCount() < 2) {
+ result.AppendError("wrong number of arguments; expected "
+ "<address-expression> <tag> [<tag> [...]]");
+ return false;
+ }
+
+ Status error;
+ addr_t start_addr = OptionArgParser::ToAddress(
+ &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error);
+ if (start_addr == LLDB_INVALID_ADDRESS) {
+ result.AppendErrorWithFormatv("Invalid address expression, {0}",
+ error.AsCString());
+ return false;
+ }
+
+ command.Shift(); // shift off start address
+
+ std::vector<lldb::addr_t> tags;
+ for (auto &entry : command) {
+ lldb::addr_t tag_value;
+ // getAsInteger returns true on failure
+ if (entry.ref().getAsInteger(0, tag_value)) {
+ result.AppendErrorWithFormat(
+ "'%s' is not a valid unsigned decimal string value.\n",
+ entry.c_str());
+ return false;
+ }
+ tags.push_back(tag_value);
+ }
+
+ Process *process = m_exe_ctx.GetProcessPtr();
+ llvm::Expected<const MemoryTagManager *> tag_manager_or_err =
+ process->GetMemoryTagManager();
+
+ if (!tag_manager_or_err) {
+ result.SetError(Status(tag_manager_or_err.takeError()));
+ return false;
+ }
+
+ const MemoryTagManager *tag_manager = *tag_manager_or_err;
+
+ MemoryRegionInfos memory_regions;
+ // If this fails the list of regions is cleared, so we don't need to read
+ // the return status here.
+ process->GetMemoryRegions(memory_regions);
+
+ // The tag manager only removes tag bits. These addresses may include other
+ // non-address bits that must also be ignored.
+ ABISP abi = process->GetABI();
+ if (abi)
+ start_addr = abi->FixDataAddress(start_addr);
+
+ // We have to assume start_addr is not granule aligned.
+ // So if we simply made a range:
+ // (start_addr, start_addr + (N * granule_size))
+ // We would end up with a range that isn't N granules but N+1
+ // granules. To avoid this we'll align the start first using the method that
+ // doesn't check memory attributes. (if the final range is untagged we'll
+ // handle that error later)
+ lldb::addr_t aligned_start_addr =
+ tag_manager->ExpandToGranule(MemoryTagManager::TagRange(start_addr, 1))
+ .GetRangeBase();
+
+ lldb::addr_t end_addr = 0;
+ // When you have an end address you want to align the range like tag read
+ // does. Meaning, align the start down (which we've done) and align the end
+ // up.
+ if (m_tag_write_options.m_end_addr != LLDB_INVALID_ADDRESS)
+ end_addr = m_tag_write_options.m_end_addr;
+ else
+ // Without an end address assume number of tags matches number of granules
+ // to write to
+ end_addr =
+ aligned_start_addr + (tags.size() * tag_manager->GetGranuleSize());
+
+ // Remove non-address bits that aren't memory tags
+ if (abi)
+ end_addr = abi->FixDataAddress(end_addr);
+
+ // Now we've aligned the start address so if we ask for another range
+ // using the number of tags N, we'll get back a range that is also N
+ // granules in size.
+ llvm::Expected<MemoryTagManager::TagRange> tagged_range =
+ tag_manager->MakeTaggedRange(aligned_start_addr, end_addr,
+ memory_regions);
+
+ if (!tagged_range) {
+ result.SetError(Status(tagged_range.takeError()));
+ return false;
+ }
+
+ Status status = process->WriteMemoryTags(tagged_range->GetRangeBase(),
+ tagged_range->GetByteSize(), tags);
+
+ if (status.Fail()) {
+ result.SetError(status);
+ return false;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+
+ OptionGroupOptions m_option_group;
+ OptionGroupTagWrite m_tag_write_options;
+};
+
+CommandObjectMemoryTag::CommandObjectMemoryTag(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "tag", "Commands for manipulating memory tags",
+ "memory tag <sub-command> [<sub-command-options>]") {
+ CommandObjectSP read_command_object(
+ new CommandObjectMemoryTagRead(interpreter));
+ read_command_object->SetCommandName("memory tag read");
+ LoadSubCommand("read", read_command_object);
+
+ CommandObjectSP write_command_object(
+ new CommandObjectMemoryTagWrite(interpreter));
+ write_command_object->SetCommandName("memory tag write");
+ LoadSubCommand("write", write_command_object);
+}
+
+CommandObjectMemoryTag::~CommandObjectMemoryTag() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectMemoryTag.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectMemoryTag.h
new file mode 100644
index 000000000000..54909ac90811
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectMemoryTag.h
@@ -0,0 +1,25 @@
+//===-- CommandObjectMemoryTag.h --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTMEMORYTAG_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTMEMORYTAG_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+class CommandObjectMemoryTag : public CommandObjectMultiword {
+public:
+ CommandObjectMemoryTag(CommandInterpreter &interpreter);
+
+ ~CommandObjectMemoryTag() override;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTMEMORYTAG_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectMultiword.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectMultiword.cpp
new file mode 100644
index 000000000000..e4fdb7365e72
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectMultiword.cpp
@@ -0,0 +1,443 @@
+//===-- CommandObjectMultiword.cpp ----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/Options.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// CommandObjectMultiword
+
+CommandObjectMultiword::CommandObjectMultiword(CommandInterpreter &interpreter,
+ const char *name,
+ const char *help,
+ const char *syntax,
+ uint32_t flags)
+ : CommandObject(interpreter, name, help, syntax, flags),
+ m_can_be_removed(false) {}
+
+CommandObjectMultiword::~CommandObjectMultiword() = default;
+
+CommandObjectSP
+CommandObjectMultiword::GetSubcommandSPExact(llvm::StringRef sub_cmd) {
+ if (m_subcommand_dict.empty())
+ return {};
+
+ auto pos = m_subcommand_dict.find(std::string(sub_cmd));
+ if (pos == m_subcommand_dict.end())
+ return {};
+
+ return pos->second;
+}
+
+CommandObjectSP CommandObjectMultiword::GetSubcommandSP(llvm::StringRef sub_cmd,
+ StringList *matches) {
+ if (m_subcommand_dict.empty())
+ return {};
+
+ CommandObjectSP return_cmd_sp = GetSubcommandSPExact(sub_cmd);
+ if (return_cmd_sp) {
+ if (matches)
+ matches->AppendString(sub_cmd);
+ return return_cmd_sp;
+ }
+
+ CommandObject::CommandMap::iterator pos;
+
+ StringList local_matches;
+ if (matches == nullptr)
+ matches = &local_matches;
+ int num_matches =
+ AddNamesMatchingPartialString(m_subcommand_dict, sub_cmd, *matches);
+
+ if (num_matches == 1) {
+ // Cleaner, but slightly less efficient would be to call back into this
+ // function, since I now know I have an exact match...
+
+ sub_cmd = matches->GetStringAtIndex(0);
+ pos = m_subcommand_dict.find(std::string(sub_cmd));
+ if (pos != m_subcommand_dict.end())
+ return_cmd_sp = pos->second;
+ }
+
+ return return_cmd_sp;
+}
+
+CommandObject *
+CommandObjectMultiword::GetSubcommandObject(llvm::StringRef sub_cmd,
+ StringList *matches) {
+ return GetSubcommandSP(sub_cmd, matches).get();
+}
+
+bool CommandObjectMultiword::LoadSubCommand(llvm::StringRef name,
+ const CommandObjectSP &cmd_obj_sp) {
+ if (cmd_obj_sp)
+ lldbassert((&GetCommandInterpreter() == &cmd_obj_sp->GetCommandInterpreter()) &&
+ "tried to add a CommandObject from a different interpreter");
+
+ CommandMap::iterator pos;
+ bool success = true;
+
+ pos = m_subcommand_dict.find(std::string(name));
+ if (pos == m_subcommand_dict.end()) {
+ m_subcommand_dict[std::string(name)] = cmd_obj_sp;
+ } else
+ success = false;
+
+ return success;
+}
+
+llvm::Error CommandObjectMultiword::LoadUserSubcommand(
+ llvm::StringRef name, const CommandObjectSP &cmd_obj_sp, bool can_replace) {
+ Status result;
+ if (cmd_obj_sp)
+ lldbassert((&GetCommandInterpreter() == &cmd_obj_sp->GetCommandInterpreter()) &&
+ "tried to add a CommandObject from a different interpreter");
+ if (!IsUserCommand()) {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "can't add a user subcommand to a builtin container command.");
+ }
+ // Make sure this a user command if it isn't already:
+ cmd_obj_sp->SetIsUserCommand(true);
+
+ std::string str_name(name);
+
+ auto pos = m_subcommand_dict.find(str_name);
+ if (pos == m_subcommand_dict.end()) {
+ m_subcommand_dict[str_name] = cmd_obj_sp;
+ return llvm::Error::success();
+ }
+
+ const char *error_str = nullptr;
+ if (!can_replace)
+ error_str = "sub-command already exists";
+ if (!(*pos).second->IsUserCommand())
+ error_str = "can't replace a builtin subcommand";
+
+ if (error_str) {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(), error_str);
+ }
+ m_subcommand_dict[str_name] = cmd_obj_sp;
+ return llvm::Error::success();
+}
+
+llvm::Error CommandObjectMultiword::RemoveUserSubcommand(llvm::StringRef cmd_name,
+ bool must_be_multiword) {
+ CommandMap::iterator pos;
+ std::string str_name(cmd_name);
+
+ pos = m_subcommand_dict.find(str_name);
+ if (pos == m_subcommand_dict.end()) {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),"subcommand '%s' not found.",
+ str_name.c_str());
+ }
+ if (!(*pos).second->IsUserCommand()) {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),"subcommand '%s' not a user command.",
+ str_name.c_str());
+ }
+
+ if (must_be_multiword && !(*pos).second->IsMultiwordObject()) {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),"subcommand '%s' is not a container command",
+ str_name.c_str());
+ }
+ if (!must_be_multiword && (*pos).second->IsMultiwordObject()) {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),"subcommand '%s' is not a user command",
+ str_name.c_str());
+ }
+
+ m_subcommand_dict.erase(pos);
+
+ return llvm::Error::success();
+}
+
+bool CommandObjectMultiword::Execute(const char *args_string,
+ CommandReturnObject &result) {
+ Args args(args_string);
+ const size_t argc = args.GetArgumentCount();
+ if (argc == 0) {
+ this->CommandObject::GenerateHelpText(result);
+ return result.Succeeded();
+ }
+
+ auto sub_command = args[0].ref();
+ if (sub_command.empty()) {
+ result.AppendError("Need to specify a non-empty subcommand.");
+ return result.Succeeded();
+ }
+
+ if (sub_command.equals_insensitive("help")) {
+ this->CommandObject::GenerateHelpText(result);
+ return result.Succeeded();
+ }
+
+ if (m_subcommand_dict.empty()) {
+ result.AppendErrorWithFormat("'%s' does not have any subcommands.\n",
+ GetCommandName().str().c_str());
+ return false;
+ }
+
+ StringList matches;
+ CommandObject *sub_cmd_obj = GetSubcommandObject(sub_command, &matches);
+ if (sub_cmd_obj != nullptr) {
+ // Now call CommandObject::Execute to process options in `rest_of_line`.
+ // From there the command-specific version of Execute will be called, with
+ // the processed arguments.
+
+ args.Shift();
+ sub_cmd_obj->Execute(args_string, result);
+ return result.Succeeded();
+ }
+
+ std::string error_msg;
+ const size_t num_subcmd_matches = matches.GetSize();
+ if (num_subcmd_matches > 0)
+ error_msg.assign("ambiguous command ");
+ else
+ error_msg.assign("invalid command ");
+
+ error_msg.append("'");
+ error_msg.append(std::string(GetCommandName()));
+ error_msg.append(" ");
+ error_msg.append(std::string(sub_command));
+ error_msg.append("'.");
+
+ if (num_subcmd_matches > 0) {
+ error_msg.append(" Possible completions:");
+ for (const std::string &match : matches) {
+ error_msg.append("\n\t");
+ error_msg.append(match);
+ }
+ }
+ error_msg.append("\n");
+ result.AppendRawError(error_msg.c_str());
+ return false;
+}
+
+void CommandObjectMultiword::GenerateHelpText(Stream &output_stream) {
+ // First time through here, generate the help text for the object and push it
+ // to the return result object as well
+
+ CommandObject::GenerateHelpText(output_stream);
+ output_stream.PutCString("\nThe following subcommands are supported:\n\n");
+
+ CommandMap::iterator pos;
+ uint32_t max_len = FindLongestCommandWord(m_subcommand_dict);
+
+ if (max_len)
+ max_len += 4; // Indent the output by 4 spaces.
+
+ for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos) {
+ std::string indented_command(" ");
+ indented_command.append(pos->first);
+ if (pos->second->WantsRawCommandString()) {
+ std::string help_text(std::string(pos->second->GetHelp()));
+ help_text.append(" Expects 'raw' input (see 'help raw-input'.)");
+ m_interpreter.OutputFormattedHelpText(output_stream, indented_command,
+ "--", help_text, max_len);
+ } else
+ m_interpreter.OutputFormattedHelpText(output_stream, indented_command,
+ "--", pos->second->GetHelp(),
+ max_len);
+ }
+
+ output_stream.PutCString("\nFor more help on any particular subcommand, type "
+ "'help <command> <subcommand>'.\n");
+}
+
+void CommandObjectMultiword::HandleCompletion(CompletionRequest &request) {
+ auto arg0 = request.GetParsedLine()[0].ref();
+ if (request.GetCursorIndex() == 0) {
+ StringList new_matches, descriptions;
+ AddNamesMatchingPartialString(m_subcommand_dict, arg0, new_matches,
+ &descriptions);
+ request.AddCompletions(new_matches, descriptions);
+
+ if (new_matches.GetSize() == 1 &&
+ new_matches.GetStringAtIndex(0) != nullptr &&
+ (arg0 == new_matches.GetStringAtIndex(0))) {
+ StringList temp_matches;
+ CommandObject *cmd_obj = GetSubcommandObject(arg0, &temp_matches);
+ if (cmd_obj != nullptr) {
+ if (request.GetParsedLine().GetArgumentCount() != 1) {
+ request.GetParsedLine().Shift();
+ request.AppendEmptyArgument();
+ cmd_obj->HandleCompletion(request);
+ }
+ }
+ }
+ return;
+ }
+
+ StringList new_matches;
+ CommandObject *sub_command_object = GetSubcommandObject(arg0, &new_matches);
+ if (sub_command_object == nullptr) {
+ request.AddCompletions(new_matches);
+ return;
+ }
+
+ // Remove the one match that we got from calling GetSubcommandObject.
+ new_matches.DeleteStringAtIndex(0);
+ request.AddCompletions(new_matches);
+ request.ShiftArguments();
+ sub_command_object->HandleCompletion(request);
+}
+
+llvm::Optional<std::string>
+CommandObjectMultiword::GetRepeatCommand(Args &current_command_args,
+ uint32_t index) {
+ index++;
+ if (current_command_args.GetArgumentCount() <= index)
+ return llvm::None;
+ CommandObject *sub_command_object =
+ GetSubcommandObject(current_command_args[index].ref());
+ if (sub_command_object == nullptr)
+ return llvm::None;
+ return sub_command_object->GetRepeatCommand(current_command_args, index);
+}
+
+CommandObjectProxy::CommandObjectProxy(CommandInterpreter &interpreter,
+ const char *name, const char *help,
+ const char *syntax, uint32_t flags)
+ : CommandObject(interpreter, name, help, syntax, flags) {}
+
+CommandObjectProxy::~CommandObjectProxy() = default;
+
+Options *CommandObjectProxy::GetOptions() {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->GetOptions();
+ return CommandObject::GetOptions();
+}
+
+llvm::StringRef CommandObjectProxy::GetHelp() {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->GetHelp();
+ return CommandObject::GetHelp();
+}
+
+llvm::StringRef CommandObjectProxy::GetSyntax() {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->GetSyntax();
+ return CommandObject::GetSyntax();
+}
+
+llvm::StringRef CommandObjectProxy::GetHelpLong() {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->GetHelpLong();
+ return CommandObject::GetHelpLong();
+}
+
+bool CommandObjectProxy::IsRemovable() const {
+ const CommandObject *proxy_command =
+ const_cast<CommandObjectProxy *>(this)->GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->IsRemovable();
+ return false;
+}
+
+bool CommandObjectProxy::IsMultiwordObject() {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->IsMultiwordObject();
+ return false;
+}
+
+CommandObjectMultiword *CommandObjectProxy::GetAsMultiwordCommand() {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->GetAsMultiwordCommand();
+ return nullptr;
+}
+
+void CommandObjectProxy::GenerateHelpText(Stream &result) {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ proxy_command->GenerateHelpText(result);
+ else
+ CommandObject::GenerateHelpText(result);
+}
+
+lldb::CommandObjectSP
+CommandObjectProxy::GetSubcommandSP(llvm::StringRef sub_cmd,
+ StringList *matches) {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->GetSubcommandSP(sub_cmd, matches);
+ return lldb::CommandObjectSP();
+}
+
+CommandObject *CommandObjectProxy::GetSubcommandObject(llvm::StringRef sub_cmd,
+ StringList *matches) {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->GetSubcommandObject(sub_cmd, matches);
+ return nullptr;
+}
+
+bool CommandObjectProxy::LoadSubCommand(
+ llvm::StringRef cmd_name, const lldb::CommandObjectSP &command_sp) {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->LoadSubCommand(cmd_name, command_sp);
+ return false;
+}
+
+bool CommandObjectProxy::WantsRawCommandString() {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->WantsRawCommandString();
+ return false;
+}
+
+bool CommandObjectProxy::WantsCompletion() {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->WantsCompletion();
+ return false;
+}
+
+void CommandObjectProxy::HandleCompletion(CompletionRequest &request) {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ proxy_command->HandleCompletion(request);
+}
+
+void CommandObjectProxy::HandleArgumentCompletion(
+ CompletionRequest &request, OptionElementVector &opt_element_vector) {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ proxy_command->HandleArgumentCompletion(request, opt_element_vector);
+}
+
+llvm::Optional<std::string>
+CommandObjectProxy::GetRepeatCommand(Args &current_command_args,
+ uint32_t index) {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->GetRepeatCommand(current_command_args, index);
+ return llvm::None;
+}
+
+llvm::StringRef CommandObjectProxy::GetUnsupportedError() {
+ return "command is not implemented";
+}
+
+bool CommandObjectProxy::Execute(const char *args_string,
+ CommandReturnObject &result) {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->Execute(args_string, result);
+ result.AppendError(GetUnsupportedError());
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectPlatform.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectPlatform.cpp
new file mode 100644
index 000000000000..42b19db5efc4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectPlatform.cpp
@@ -0,0 +1,1940 @@
+//===-- CommandObjectPlatform.cpp -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectPlatform.h"
+#include "CommandOptionsProcessLaunch.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandOptionValidators.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionGroupFile.h"
+#include "lldb/Interpreter/OptionGroupPlatform.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/Args.h"
+
+#include "llvm/ADT/SmallString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static mode_t ParsePermissionString(const char *) = delete;
+
+static mode_t ParsePermissionString(llvm::StringRef permissions) {
+ if (permissions.size() != 9)
+ return (mode_t)(-1);
+ bool user_r, user_w, user_x, group_r, group_w, group_x, world_r, world_w,
+ world_x;
+
+ user_r = (permissions[0] == 'r');
+ user_w = (permissions[1] == 'w');
+ user_x = (permissions[2] == 'x');
+
+ group_r = (permissions[3] == 'r');
+ group_w = (permissions[4] == 'w');
+ group_x = (permissions[5] == 'x');
+
+ world_r = (permissions[6] == 'r');
+ world_w = (permissions[7] == 'w');
+ world_x = (permissions[8] == 'x');
+
+ mode_t user, group, world;
+ user = (user_r ? 4 : 0) | (user_w ? 2 : 0) | (user_x ? 1 : 0);
+ group = (group_r ? 4 : 0) | (group_w ? 2 : 0) | (group_x ? 1 : 0);
+ world = (world_r ? 4 : 0) | (world_w ? 2 : 0) | (world_x ? 1 : 0);
+
+ return user | group | world;
+}
+
+#define LLDB_OPTIONS_permissions
+#include "CommandOptions.inc"
+
+class OptionPermissions : public OptionGroup {
+public:
+ OptionPermissions() = default;
+
+ ~OptionPermissions() override = default;
+
+ lldb_private::Status
+ SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ char short_option = (char)GetDefinitions()[option_idx].short_option;
+ switch (short_option) {
+ case 'v': {
+ if (option_arg.getAsInteger(8, m_permissions)) {
+ m_permissions = 0777;
+ error.SetErrorStringWithFormat("invalid value for permissions: %s",
+ option_arg.str().c_str());
+ }
+
+ } break;
+ case 's': {
+ mode_t perms = ParsePermissionString(option_arg);
+ if (perms == (mode_t)-1)
+ error.SetErrorStringWithFormat("invalid value for permissions: %s",
+ option_arg.str().c_str());
+ else
+ m_permissions = perms;
+ } break;
+ case 'r':
+ m_permissions |= lldb::eFilePermissionsUserRead;
+ break;
+ case 'w':
+ m_permissions |= lldb::eFilePermissionsUserWrite;
+ break;
+ case 'x':
+ m_permissions |= lldb::eFilePermissionsUserExecute;
+ break;
+ case 'R':
+ m_permissions |= lldb::eFilePermissionsGroupRead;
+ break;
+ case 'W':
+ m_permissions |= lldb::eFilePermissionsGroupWrite;
+ break;
+ case 'X':
+ m_permissions |= lldb::eFilePermissionsGroupExecute;
+ break;
+ case 'd':
+ m_permissions |= lldb::eFilePermissionsWorldRead;
+ break;
+ case 't':
+ m_permissions |= lldb::eFilePermissionsWorldWrite;
+ break;
+ case 'e':
+ m_permissions |= lldb::eFilePermissionsWorldExecute;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_permissions = 0;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_permissions_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ uint32_t m_permissions;
+
+private:
+ OptionPermissions(const OptionPermissions &) = delete;
+ const OptionPermissions &operator=(const OptionPermissions &) = delete;
+};
+
+// "platform select <platform-name>"
+class CommandObjectPlatformSelect : public CommandObjectParsed {
+public:
+ CommandObjectPlatformSelect(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform select",
+ "Create a platform if needed and select it as the "
+ "current platform.",
+ "platform select <platform-name>", 0),
+ m_platform_options(
+ false) // Don't include the "--platform" option by passing false
+ {
+ m_option_group.Append(&m_platform_options, LLDB_OPT_SET_ALL, 1);
+ m_option_group.Finalize();
+ CommandArgumentData platform_arg{eArgTypePlatform, eArgRepeatPlain};
+ m_arguments.push_back({platform_arg});
+ }
+
+ ~CommandObjectPlatformSelect() override = default;
+
+ void HandleCompletion(CompletionRequest &request) override {
+ CommandCompletions::PlatformPluginNames(GetCommandInterpreter(), request,
+ nullptr);
+ }
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ if (args.GetArgumentCount() == 1) {
+ const char *platform_name = args.GetArgumentAtIndex(0);
+ if (platform_name && platform_name[0]) {
+ const bool select = true;
+ m_platform_options.SetPlatformName(platform_name);
+ Status error;
+ ArchSpec platform_arch;
+ PlatformSP platform_sp(m_platform_options.CreatePlatformWithOptions(
+ m_interpreter, ArchSpec(), select, error, platform_arch));
+ if (platform_sp) {
+ GetDebugger().GetPlatformList().SetSelectedPlatform(platform_sp);
+
+ platform_sp->GetStatus(result.GetOutputStream());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError(error.AsCString());
+ }
+ } else {
+ result.AppendError("invalid platform name");
+ }
+ } else {
+ result.AppendError(
+ "platform create takes a platform name as an argument\n");
+ }
+ return result.Succeeded();
+ }
+
+ OptionGroupOptions m_option_group;
+ OptionGroupPlatform m_platform_options;
+};
+
+// "platform list"
+class CommandObjectPlatformList : public CommandObjectParsed {
+public:
+ CommandObjectPlatformList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform list",
+ "List all platforms that are available.", nullptr,
+ 0) {}
+
+ ~CommandObjectPlatformList() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Stream &ostrm = result.GetOutputStream();
+ ostrm.Printf("Available platforms:\n");
+
+ PlatformSP host_platform_sp(Platform::GetHostPlatform());
+ ostrm.Format("{0}: {1}\n", host_platform_sp->GetPluginName(),
+ host_platform_sp->GetDescription());
+
+ uint32_t idx;
+ for (idx = 0; true; ++idx) {
+ llvm::StringRef plugin_name =
+ PluginManager::GetPlatformPluginNameAtIndex(idx);
+ if (plugin_name.empty())
+ break;
+ llvm::StringRef plugin_desc =
+ PluginManager::GetPlatformPluginDescriptionAtIndex(idx);
+ ostrm.Format("{0}: {1}\n", plugin_name, plugin_desc);
+ }
+
+ if (idx == 0) {
+ result.AppendError("no platforms are available\n");
+ } else
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+// "platform status"
+class CommandObjectPlatformStatus : public CommandObjectParsed {
+public:
+ CommandObjectPlatformStatus(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform status",
+ "Display status for the current platform.", nullptr,
+ 0) {}
+
+ ~CommandObjectPlatformStatus() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Stream &ostrm = result.GetOutputStream();
+
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ PlatformSP platform_sp;
+ if (target) {
+ platform_sp = target->GetPlatform();
+ }
+ if (!platform_sp) {
+ platform_sp = GetDebugger().GetPlatformList().GetSelectedPlatform();
+ }
+ if (platform_sp) {
+ platform_sp->GetStatus(ostrm);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError("no platform is currently selected\n");
+ }
+ return result.Succeeded();
+ }
+};
+
+// "platform connect <connect-url>"
+class CommandObjectPlatformConnect : public CommandObjectParsed {
+public:
+ CommandObjectPlatformConnect(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "platform connect",
+ "Select the current platform by providing a connection URL.",
+ "platform connect <connect-url>", 0) {
+ CommandArgumentData platform_arg{eArgTypeConnectURL, eArgRepeatPlain};
+ m_arguments.push_back({platform_arg});
+ }
+
+ ~CommandObjectPlatformConnect() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Stream &ostrm = result.GetOutputStream();
+
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ Status error(platform_sp->ConnectRemote(args));
+ if (error.Success()) {
+ platform_sp->GetStatus(ostrm);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ platform_sp->ConnectToWaitingProcesses(GetDebugger(), error);
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ }
+ } else {
+ result.AppendErrorWithFormat("%s\n", error.AsCString());
+ }
+ } else {
+ result.AppendError("no platform is currently selected\n");
+ }
+ return result.Succeeded();
+ }
+
+ Options *GetOptions() override {
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ OptionGroupOptions *m_platform_options = nullptr;
+ if (platform_sp) {
+ m_platform_options = platform_sp->GetConnectionOptions(m_interpreter);
+ if (m_platform_options != nullptr && !m_platform_options->m_did_finalize)
+ m_platform_options->Finalize();
+ }
+ return m_platform_options;
+ }
+};
+
+// "platform disconnect"
+class CommandObjectPlatformDisconnect : public CommandObjectParsed {
+public:
+ CommandObjectPlatformDisconnect(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform disconnect",
+ "Disconnect from the current platform.",
+ "platform disconnect", 0) {}
+
+ ~CommandObjectPlatformDisconnect() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ if (args.GetArgumentCount() == 0) {
+ Status error;
+
+ if (platform_sp->IsConnected()) {
+ // Cache the instance name if there is one since we are about to
+ // disconnect and the name might go with it.
+ const char *hostname_cstr = platform_sp->GetHostname();
+ std::string hostname;
+ if (hostname_cstr)
+ hostname.assign(hostname_cstr);
+
+ error = platform_sp->DisconnectRemote();
+ if (error.Success()) {
+ Stream &ostrm = result.GetOutputStream();
+ if (hostname.empty())
+ ostrm.Format("Disconnected from \"{0}\"\n",
+ platform_sp->GetPluginName());
+ else
+ ostrm.Printf("Disconnected from \"%s\"\n", hostname.c_str());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("%s", error.AsCString());
+ }
+ } else {
+ // Not connected...
+ result.AppendErrorWithFormatv("not connected to '{0}'",
+ platform_sp->GetPluginName());
+ }
+ } else {
+ // Bad args
+ result.AppendError(
+ "\"platform disconnect\" doesn't take any arguments");
+ }
+ } else {
+ result.AppendError("no platform is currently selected");
+ }
+ return result.Succeeded();
+ }
+};
+
+// "platform settings"
+class CommandObjectPlatformSettings : public CommandObjectParsed {
+public:
+ CommandObjectPlatformSettings(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform settings",
+ "Set settings for the current target's platform, "
+ "or for a platform by name.",
+ "platform settings", 0),
+ m_option_working_dir(LLDB_OPT_SET_1, false, "working-dir", 'w',
+ CommandCompletions::eRemoteDiskDirectoryCompletion,
+ eArgTypePath,
+ "The working directory for the platform.") {
+ m_options.Append(&m_option_working_dir, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ }
+
+ ~CommandObjectPlatformSettings() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ if (m_option_working_dir.GetOptionValue().OptionWasSet())
+ platform_sp->SetWorkingDirectory(
+ m_option_working_dir.GetOptionValue().GetCurrentValue());
+ } else {
+ result.AppendError("no platform is currently selected");
+ }
+ return result.Succeeded();
+ }
+
+ Options *GetOptions() override {
+ if (!m_options.DidFinalize())
+ m_options.Finalize();
+ return &m_options;
+ }
+
+ OptionGroupOptions m_options;
+ OptionGroupFile m_option_working_dir;
+};
+
+// "platform mkdir"
+class CommandObjectPlatformMkDir : public CommandObjectParsed {
+public:
+ CommandObjectPlatformMkDir(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform mkdir",
+ "Make a new directory on the remote end.", nullptr,
+ 0) {
+ CommandArgumentData thread_arg{eArgTypePath, eArgRepeatPlain};
+ m_arguments.push_back({thread_arg});
+ }
+
+ ~CommandObjectPlatformMkDir() override = default;
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ std::string cmd_line;
+ args.GetCommandString(cmd_line);
+ uint32_t mode;
+ const OptionPermissions *options_permissions =
+ (const OptionPermissions *)m_options.GetGroupWithOption('r');
+ if (options_permissions)
+ mode = options_permissions->m_permissions;
+ else
+ mode = lldb::eFilePermissionsUserRWX | lldb::eFilePermissionsGroupRWX |
+ lldb::eFilePermissionsWorldRX;
+ Status error = platform_sp->MakeDirectory(FileSpec(cmd_line), mode);
+ if (error.Success()) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError(error.AsCString());
+ }
+ } else {
+ result.AppendError("no platform currently selected\n");
+ }
+ return result.Succeeded();
+ }
+
+ Options *GetOptions() override {
+ if (!m_options.DidFinalize()) {
+ m_options.Append(new OptionPermissions());
+ m_options.Finalize();
+ }
+ return &m_options;
+ }
+
+ OptionGroupOptions m_options;
+};
+
+// "platform fopen"
+class CommandObjectPlatformFOpen : public CommandObjectParsed {
+public:
+ CommandObjectPlatformFOpen(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform file open",
+ "Open a file on the remote end.", nullptr, 0) {
+ CommandArgumentData path_arg{eArgTypePath, eArgRepeatPlain};
+ m_arguments.push_back({path_arg});
+ }
+
+ ~CommandObjectPlatformFOpen() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (request.GetCursorIndex() == 0)
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(),
+ CommandCompletions::eRemoteDiskFileCompletion, request, nullptr);
+ }
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ Status error;
+ std::string cmd_line;
+ args.GetCommandString(cmd_line);
+ mode_t perms;
+ const OptionPermissions *options_permissions =
+ (const OptionPermissions *)m_options.GetGroupWithOption('r');
+ if (options_permissions)
+ perms = options_permissions->m_permissions;
+ else
+ perms = lldb::eFilePermissionsUserRW | lldb::eFilePermissionsGroupRW |
+ lldb::eFilePermissionsWorldRead;
+ lldb::user_id_t fd = platform_sp->OpenFile(
+ FileSpec(cmd_line),
+ File::eOpenOptionReadWrite | File::eOpenOptionCanCreate,
+ perms, error);
+ if (error.Success()) {
+ result.AppendMessageWithFormat("File Descriptor = %" PRIu64 "\n", fd);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError(error.AsCString());
+ }
+ } else {
+ result.AppendError("no platform currently selected\n");
+ }
+ return result.Succeeded();
+ }
+
+ Options *GetOptions() override {
+ if (!m_options.DidFinalize()) {
+ m_options.Append(new OptionPermissions());
+ m_options.Finalize();
+ }
+ return &m_options;
+ }
+
+ OptionGroupOptions m_options;
+};
+
+// "platform fclose"
+class CommandObjectPlatformFClose : public CommandObjectParsed {
+public:
+ CommandObjectPlatformFClose(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform file close",
+ "Close a file on the remote end.", nullptr, 0) {
+ CommandArgumentData path_arg{eArgTypeUnsignedInteger, eArgRepeatPlain};
+ m_arguments.push_back({path_arg});
+ }
+
+ ~CommandObjectPlatformFClose() override = default;
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ std::string cmd_line;
+ args.GetCommandString(cmd_line);
+ lldb::user_id_t fd;
+ if (!llvm::to_integer(cmd_line, fd)) {
+ result.AppendErrorWithFormatv("'{0}' is not a valid file descriptor.\n",
+ cmd_line);
+ return result.Succeeded();
+ }
+ Status error;
+ bool success = platform_sp->CloseFile(fd, error);
+ if (success) {
+ result.AppendMessageWithFormat("file %" PRIu64 " closed.\n", fd);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError(error.AsCString());
+ }
+ } else {
+ result.AppendError("no platform currently selected\n");
+ }
+ return result.Succeeded();
+ }
+};
+
+// "platform fread"
+
+#define LLDB_OPTIONS_platform_fread
+#include "CommandOptions.inc"
+
+class CommandObjectPlatformFRead : public CommandObjectParsed {
+public:
+ CommandObjectPlatformFRead(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform file read",
+ "Read data from a file on the remote end.", nullptr,
+ 0) {
+ CommandArgumentData path_arg{eArgTypeUnsignedInteger, eArgRepeatPlain};
+ m_arguments.push_back({path_arg});
+ }
+
+ ~CommandObjectPlatformFRead() override = default;
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ std::string cmd_line;
+ args.GetCommandString(cmd_line);
+ lldb::user_id_t fd;
+ if (!llvm::to_integer(cmd_line, fd)) {
+ result.AppendErrorWithFormatv("'{0}' is not a valid file descriptor.\n",
+ cmd_line);
+ return result.Succeeded();
+ }
+ std::string buffer(m_options.m_count, 0);
+ Status error;
+ uint64_t retcode = platform_sp->ReadFile(
+ fd, m_options.m_offset, &buffer[0], m_options.m_count, error);
+ if (retcode != UINT64_MAX) {
+ result.AppendMessageWithFormat("Return = %" PRIu64 "\n", retcode);
+ result.AppendMessageWithFormat("Data = \"%s\"\n", buffer.c_str());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError(error.AsCString());
+ }
+ } else {
+ result.AppendError("no platform currently selected\n");
+ }
+ return result.Succeeded();
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ char short_option = (char)m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'o':
+ if (option_arg.getAsInteger(0, m_offset))
+ error.SetErrorStringWithFormat("invalid offset: '%s'",
+ option_arg.str().c_str());
+ break;
+ case 'c':
+ if (option_arg.getAsInteger(0, m_count))
+ error.SetErrorStringWithFormat("invalid offset: '%s'",
+ option_arg.str().c_str());
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_offset = 0;
+ m_count = 1;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_platform_fread_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ uint32_t m_offset;
+ uint32_t m_count;
+ };
+
+ CommandOptions m_options;
+};
+
+// "platform fwrite"
+
+#define LLDB_OPTIONS_platform_fwrite
+#include "CommandOptions.inc"
+
+class CommandObjectPlatformFWrite : public CommandObjectParsed {
+public:
+ CommandObjectPlatformFWrite(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform file write",
+ "Write data to a file on the remote end.", nullptr,
+ 0) {
+ CommandArgumentData path_arg{eArgTypeUnsignedInteger, eArgRepeatPlain};
+ m_arguments.push_back({path_arg});
+ }
+
+ ~CommandObjectPlatformFWrite() override = default;
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ std::string cmd_line;
+ args.GetCommandString(cmd_line);
+ Status error;
+ lldb::user_id_t fd;
+ if (!llvm::to_integer(cmd_line, fd)) {
+ result.AppendErrorWithFormatv("'{0}' is not a valid file descriptor.",
+ cmd_line);
+ return result.Succeeded();
+ }
+ uint64_t retcode =
+ platform_sp->WriteFile(fd, m_options.m_offset, &m_options.m_data[0],
+ m_options.m_data.size(), error);
+ if (retcode != UINT64_MAX) {
+ result.AppendMessageWithFormat("Return = %" PRIu64 "\n", retcode);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError(error.AsCString());
+ }
+ } else {
+ result.AppendError("no platform currently selected\n");
+ }
+ return result.Succeeded();
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ char short_option = (char)m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'o':
+ if (option_arg.getAsInteger(0, m_offset))
+ error.SetErrorStringWithFormat("invalid offset: '%s'",
+ option_arg.str().c_str());
+ break;
+ case 'd':
+ m_data.assign(std::string(option_arg));
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_offset = 0;
+ m_data.clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_platform_fwrite_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ uint32_t m_offset;
+ std::string m_data;
+ };
+
+ CommandOptions m_options;
+};
+
+class CommandObjectPlatformFile : public CommandObjectMultiword {
+public:
+ // Constructors and Destructors
+ CommandObjectPlatformFile(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "platform file",
+ "Commands to access files on the current platform.",
+ "platform file [open|close|read|write] ...") {
+ LoadSubCommand(
+ "open", CommandObjectSP(new CommandObjectPlatformFOpen(interpreter)));
+ LoadSubCommand(
+ "close", CommandObjectSP(new CommandObjectPlatformFClose(interpreter)));
+ LoadSubCommand(
+ "read", CommandObjectSP(new CommandObjectPlatformFRead(interpreter)));
+ LoadSubCommand(
+ "write", CommandObjectSP(new CommandObjectPlatformFWrite(interpreter)));
+ }
+
+ ~CommandObjectPlatformFile() override = default;
+
+private:
+ // For CommandObjectPlatform only
+ CommandObjectPlatformFile(const CommandObjectPlatformFile &) = delete;
+ const CommandObjectPlatformFile &
+ operator=(const CommandObjectPlatformFile &) = delete;
+};
+
+// "platform get-file remote-file-path host-file-path"
+class CommandObjectPlatformGetFile : public CommandObjectParsed {
+public:
+ CommandObjectPlatformGetFile(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "platform get-file",
+ "Transfer a file from the remote end to the local host.",
+ "platform get-file <remote-file-spec> <local-file-spec>", 0) {
+ SetHelpLong(
+ R"(Examples:
+
+(lldb) platform get-file /the/remote/file/path /the/local/file/path
+
+ Transfer a file from the remote end with file path /the/remote/file/path to the local host.)");
+
+ CommandArgumentEntry arg1, arg2;
+ CommandArgumentData file_arg_remote, file_arg_host;
+
+ // Define the first (and only) variant of this arg.
+ file_arg_remote.arg_type = eArgTypeFilename;
+ file_arg_remote.arg_repetition = eArgRepeatPlain;
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(file_arg_remote);
+
+ // Define the second (and only) variant of this arg.
+ file_arg_host.arg_type = eArgTypeFilename;
+ file_arg_host.arg_repetition = eArgRepeatPlain;
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg2.push_back(file_arg_host);
+
+ // Push the data for the first and the second arguments into the
+ // m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+ }
+
+ ~CommandObjectPlatformGetFile() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (request.GetCursorIndex() == 0)
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(),
+ CommandCompletions::eRemoteDiskFileCompletion, request, nullptr);
+ else if (request.GetCursorIndex() == 1)
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+ request, nullptr);
+ }
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ // If the number of arguments is incorrect, issue an error message.
+ if (args.GetArgumentCount() != 2) {
+ result.AppendError("required arguments missing; specify both the "
+ "source and destination file paths");
+ return false;
+ }
+
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ const char *remote_file_path = args.GetArgumentAtIndex(0);
+ const char *local_file_path = args.GetArgumentAtIndex(1);
+ Status error = platform_sp->GetFile(FileSpec(remote_file_path),
+ FileSpec(local_file_path));
+ if (error.Success()) {
+ result.AppendMessageWithFormat(
+ "successfully get-file from %s (remote) to %s (host)\n",
+ remote_file_path, local_file_path);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendMessageWithFormat("get-file failed: %s\n",
+ error.AsCString());
+ }
+ } else {
+ result.AppendError("no platform currently selected\n");
+ }
+ return result.Succeeded();
+ }
+};
+
+// "platform get-size remote-file-path"
+class CommandObjectPlatformGetSize : public CommandObjectParsed {
+public:
+ CommandObjectPlatformGetSize(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform get-size",
+ "Get the file size from the remote end.",
+ "platform get-size <remote-file-spec>", 0) {
+ SetHelpLong(
+ R"(Examples:
+
+(lldb) platform get-size /the/remote/file/path
+
+ Get the file size from the remote end with path /the/remote/file/path.)");
+
+ CommandArgumentEntry arg1;
+ CommandArgumentData file_arg_remote;
+
+ // Define the first (and only) variant of this arg.
+ file_arg_remote.arg_type = eArgTypeFilename;
+ file_arg_remote.arg_repetition = eArgRepeatPlain;
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(file_arg_remote);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ }
+
+ ~CommandObjectPlatformGetSize() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (request.GetCursorIndex() != 0)
+ return;
+
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eRemoteDiskFileCompletion,
+ request, nullptr);
+ }
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ // If the number of arguments is incorrect, issue an error message.
+ if (args.GetArgumentCount() != 1) {
+ result.AppendError("required argument missing; specify the source file "
+ "path as the only argument");
+ return false;
+ }
+
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ std::string remote_file_path(args.GetArgumentAtIndex(0));
+ user_id_t size = platform_sp->GetFileSize(FileSpec(remote_file_path));
+ if (size != UINT64_MAX) {
+ result.AppendMessageWithFormat("File size of %s (remote): %" PRIu64
+ "\n",
+ remote_file_path.c_str(), size);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendMessageWithFormat(
+ "Error getting file size of %s (remote)\n",
+ remote_file_path.c_str());
+ }
+ } else {
+ result.AppendError("no platform currently selected\n");
+ }
+ return result.Succeeded();
+ }
+};
+
+// "platform get-permissions remote-file-path"
+class CommandObjectPlatformGetPermissions : public CommandObjectParsed {
+public:
+ CommandObjectPlatformGetPermissions(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform get-permissions",
+ "Get the file permission bits from the remote end.",
+ "platform get-permissions <remote-file-spec>", 0) {
+ SetHelpLong(
+ R"(Examples:
+
+(lldb) platform get-permissions /the/remote/file/path
+
+ Get the file permissions from the remote end with path /the/remote/file/path.)");
+
+ CommandArgumentEntry arg1;
+ CommandArgumentData file_arg_remote;
+
+ // Define the first (and only) variant of this arg.
+ file_arg_remote.arg_type = eArgTypeFilename;
+ file_arg_remote.arg_repetition = eArgRepeatPlain;
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(file_arg_remote);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ }
+
+ ~CommandObjectPlatformGetPermissions() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (request.GetCursorIndex() != 0)
+ return;
+
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eRemoteDiskFileCompletion,
+ request, nullptr);
+ }
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ // If the number of arguments is incorrect, issue an error message.
+ if (args.GetArgumentCount() != 1) {
+ result.AppendError("required argument missing; specify the source file "
+ "path as the only argument");
+ return false;
+ }
+
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ std::string remote_file_path(args.GetArgumentAtIndex(0));
+ uint32_t permissions;
+ Status error = platform_sp->GetFilePermissions(FileSpec(remote_file_path),
+ permissions);
+ if (error.Success()) {
+ result.AppendMessageWithFormat(
+ "File permissions of %s (remote): 0o%04" PRIo32 "\n",
+ remote_file_path.c_str(), permissions);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else
+ result.AppendError(error.AsCString());
+ } else {
+ result.AppendError("no platform currently selected\n");
+ }
+ return result.Succeeded();
+ }
+};
+
+// "platform file-exists remote-file-path"
+class CommandObjectPlatformFileExists : public CommandObjectParsed {
+public:
+ CommandObjectPlatformFileExists(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform file-exists",
+ "Check if the file exists on the remote end.",
+ "platform file-exists <remote-file-spec>", 0) {
+ SetHelpLong(
+ R"(Examples:
+
+(lldb) platform file-exists /the/remote/file/path
+
+ Check if /the/remote/file/path exists on the remote end.)");
+
+ CommandArgumentEntry arg1;
+ CommandArgumentData file_arg_remote;
+
+ // Define the first (and only) variant of this arg.
+ file_arg_remote.arg_type = eArgTypeFilename;
+ file_arg_remote.arg_repetition = eArgRepeatPlain;
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(file_arg_remote);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ }
+
+ ~CommandObjectPlatformFileExists() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (request.GetCursorIndex() != 0)
+ return;
+
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eRemoteDiskFileCompletion,
+ request, nullptr);
+ }
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ // If the number of arguments is incorrect, issue an error message.
+ if (args.GetArgumentCount() != 1) {
+ result.AppendError("required argument missing; specify the source file "
+ "path as the only argument");
+ return false;
+ }
+
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ std::string remote_file_path(args.GetArgumentAtIndex(0));
+ bool exists = platform_sp->GetFileExists(FileSpec(remote_file_path));
+ result.AppendMessageWithFormat(
+ "File %s (remote) %s\n",
+ remote_file_path.c_str(), exists ? "exists" : "does not exist");
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError("no platform currently selected\n");
+ }
+ return result.Succeeded();
+ }
+};
+
+// "platform put-file"
+class CommandObjectPlatformPutFile : public CommandObjectParsed {
+public:
+ CommandObjectPlatformPutFile(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "platform put-file",
+ "Transfer a file from this system to the remote end.",
+ "platform put-file <source> [<destination>]", 0) {
+ SetHelpLong(
+ R"(Examples:
+
+(lldb) platform put-file /source/foo.txt /destination/bar.txt
+
+(lldb) platform put-file /source/foo.txt
+
+ Relative source file paths are resolved against lldb's local working directory.
+
+ Omitting the destination places the file in the platform working directory.)");
+ CommandArgumentData source_arg{eArgTypePath, eArgRepeatPlain};
+ CommandArgumentData path_arg{eArgTypePath, eArgRepeatOptional};
+ m_arguments.push_back({source_arg});
+ m_arguments.push_back({path_arg});
+ }
+
+ ~CommandObjectPlatformPutFile() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (request.GetCursorIndex() == 0)
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+ request, nullptr);
+ else if (request.GetCursorIndex() == 1)
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(),
+ CommandCompletions::eRemoteDiskFileCompletion, request, nullptr);
+ }
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ const char *src = args.GetArgumentAtIndex(0);
+ const char *dst = args.GetArgumentAtIndex(1);
+
+ FileSpec src_fs(src);
+ FileSystem::Instance().Resolve(src_fs);
+ FileSpec dst_fs(dst ? dst : src_fs.GetFilename().GetCString());
+
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ Status error(platform_sp->PutFile(src_fs, dst_fs));
+ if (error.Success()) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendError(error.AsCString());
+ }
+ } else {
+ result.AppendError("no platform currently selected\n");
+ }
+ return result.Succeeded();
+ }
+};
+
+// "platform process launch"
+class CommandObjectPlatformProcessLaunch : public CommandObjectParsed {
+public:
+ CommandObjectPlatformProcessLaunch(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform process launch",
+ "Launch a new process on a remote platform.",
+ "platform process launch program",
+ eCommandRequiresTarget | eCommandTryTargetAPILock) {
+ m_all_options.Append(&m_options);
+ m_all_options.Finalize();
+ CommandArgumentData run_arg_arg{eArgTypeRunArgs, eArgRepeatStar};
+ m_arguments.push_back({run_arg_arg});
+ }
+
+ ~CommandObjectPlatformProcessLaunch() override = default;
+
+ Options *GetOptions() override { return &m_all_options; }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ PlatformSP platform_sp;
+ if (target) {
+ platform_sp = target->GetPlatform();
+ }
+ if (!platform_sp) {
+ platform_sp = GetDebugger().GetPlatformList().GetSelectedPlatform();
+ }
+
+ if (platform_sp) {
+ Status error;
+ const size_t argc = args.GetArgumentCount();
+ Target *target = m_exe_ctx.GetTargetPtr();
+ Module *exe_module = target->GetExecutableModulePointer();
+ if (exe_module) {
+ m_options.launch_info.GetExecutableFile() = exe_module->GetFileSpec();
+ llvm::SmallString<128> exe_path;
+ m_options.launch_info.GetExecutableFile().GetPath(exe_path);
+ if (!exe_path.empty())
+ m_options.launch_info.GetArguments().AppendArgument(exe_path);
+ m_options.launch_info.GetArchitecture() = exe_module->GetArchitecture();
+ }
+
+ if (argc > 0) {
+ if (m_options.launch_info.GetExecutableFile()) {
+ // We already have an executable file, so we will use this and all
+ // arguments to this function are extra arguments
+ m_options.launch_info.GetArguments().AppendArguments(args);
+ } else {
+ // We don't have any file yet, so the first argument is our
+ // executable, and the rest are program arguments
+ const bool first_arg_is_executable = true;
+ m_options.launch_info.SetArguments(args, first_arg_is_executable);
+ }
+ }
+
+ if (m_options.launch_info.GetExecutableFile()) {
+ Debugger &debugger = GetDebugger();
+
+ if (argc == 0)
+ target->GetRunArguments(m_options.launch_info.GetArguments());
+
+ ProcessSP process_sp(platform_sp->DebugProcess(
+ m_options.launch_info, debugger, *target, error));
+ if (process_sp && process_sp->IsAlive()) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+
+ if (error.Success())
+ result.AppendError("process launch failed");
+ else
+ result.AppendError(error.AsCString());
+ } else {
+ result.AppendError("'platform process launch' uses the current target "
+ "file and arguments, or the executable and its "
+ "arguments can be specified in this command");
+ return false;
+ }
+ } else {
+ result.AppendError("no platform is selected\n");
+ }
+ return result.Succeeded();
+ }
+
+ CommandOptionsProcessLaunch m_options;
+ OptionGroupOptions m_all_options;
+};
+
+// "platform process list"
+
+static PosixPlatformCommandOptionValidator posix_validator;
+#define LLDB_OPTIONS_platform_process_list
+#include "CommandOptions.inc"
+
+class CommandObjectPlatformProcessList : public CommandObjectParsed {
+public:
+ CommandObjectPlatformProcessList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform process list",
+ "List processes on a remote platform by name, pid, "
+ "or many other matching attributes.",
+ "platform process list", 0) {}
+
+ ~CommandObjectPlatformProcessList() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ PlatformSP platform_sp;
+ if (target) {
+ platform_sp = target->GetPlatform();
+ }
+ if (!platform_sp) {
+ platform_sp = GetDebugger().GetPlatformList().GetSelectedPlatform();
+ }
+
+ if (platform_sp) {
+ Status error;
+ if (platform_sp) {
+ Stream &ostrm = result.GetOutputStream();
+
+ lldb::pid_t pid = m_options.match_info.GetProcessInfo().GetProcessID();
+ if (pid != LLDB_INVALID_PROCESS_ID) {
+ ProcessInstanceInfo proc_info;
+ if (platform_sp->GetProcessInfo(pid, proc_info)) {
+ ProcessInstanceInfo::DumpTableHeader(ostrm, m_options.show_args,
+ m_options.verbose);
+ proc_info.DumpAsTableRow(ostrm, platform_sp->GetUserIDResolver(),
+ m_options.show_args, m_options.verbose);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat(
+ "no process found with pid = %" PRIu64 "\n", pid);
+ }
+ } else {
+ ProcessInstanceInfoList proc_infos;
+ const uint32_t matches =
+ platform_sp->FindProcesses(m_options.match_info, proc_infos);
+ const char *match_desc = nullptr;
+ const char *match_name =
+ m_options.match_info.GetProcessInfo().GetName();
+ if (match_name && match_name[0]) {
+ switch (m_options.match_info.GetNameMatchType()) {
+ case NameMatch::Ignore:
+ break;
+ case NameMatch::Equals:
+ match_desc = "matched";
+ break;
+ case NameMatch::Contains:
+ match_desc = "contained";
+ break;
+ case NameMatch::StartsWith:
+ match_desc = "started with";
+ break;
+ case NameMatch::EndsWith:
+ match_desc = "ended with";
+ break;
+ case NameMatch::RegularExpression:
+ match_desc = "matched the regular expression";
+ break;
+ }
+ }
+
+ if (matches == 0) {
+ if (match_desc)
+ result.AppendErrorWithFormatv(
+ "no processes were found that {0} \"{1}\" on the \"{2}\" "
+ "platform\n",
+ match_desc, match_name, platform_sp->GetName());
+ else
+ result.AppendErrorWithFormatv(
+ "no processes were found on the \"{0}\" platform\n",
+ platform_sp->GetName());
+ } else {
+ result.AppendMessageWithFormatv(
+ "{0} matching process{1} found on \"{2}\"", matches,
+ matches > 1 ? "es were" : " was", platform_sp->GetName());
+ if (match_desc)
+ result.AppendMessageWithFormat(" whose name %s \"%s\"",
+ match_desc, match_name);
+ result.AppendMessageWithFormat("\n");
+ ProcessInstanceInfo::DumpTableHeader(ostrm, m_options.show_args,
+ m_options.verbose);
+ for (uint32_t i = 0; i < matches; ++i) {
+ proc_infos[i].DumpAsTableRow(
+ ostrm, platform_sp->GetUserIDResolver(), m_options.show_args,
+ m_options.verbose);
+ }
+ }
+ }
+ }
+ } else {
+ result.AppendError("no platform is selected\n");
+ }
+ return result.Succeeded();
+ }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+ bool success = false;
+
+ uint32_t id = LLDB_INVALID_PROCESS_ID;
+ success = !option_arg.getAsInteger(0, id);
+ switch (short_option) {
+ case 'p': {
+ match_info.GetProcessInfo().SetProcessID(id);
+ if (!success)
+ error.SetErrorStringWithFormat("invalid process ID string: '%s'",
+ option_arg.str().c_str());
+ break;
+ }
+ case 'P':
+ match_info.GetProcessInfo().SetParentProcessID(id);
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "invalid parent process ID string: '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'u':
+ match_info.GetProcessInfo().SetUserID(success ? id : UINT32_MAX);
+ if (!success)
+ error.SetErrorStringWithFormat("invalid user ID string: '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'U':
+ match_info.GetProcessInfo().SetEffectiveUserID(success ? id
+ : UINT32_MAX);
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "invalid effective user ID string: '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'g':
+ match_info.GetProcessInfo().SetGroupID(success ? id : UINT32_MAX);
+ if (!success)
+ error.SetErrorStringWithFormat("invalid group ID string: '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'G':
+ match_info.GetProcessInfo().SetEffectiveGroupID(success ? id
+ : UINT32_MAX);
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "invalid effective group ID string: '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'a': {
+ TargetSP target_sp =
+ execution_context ? execution_context->GetTargetSP() : TargetSP();
+ DebuggerSP debugger_sp =
+ target_sp ? target_sp->GetDebugger().shared_from_this()
+ : DebuggerSP();
+ PlatformSP platform_sp =
+ debugger_sp ? debugger_sp->GetPlatformList().GetSelectedPlatform()
+ : PlatformSP();
+ match_info.GetProcessInfo().GetArchitecture() =
+ Platform::GetAugmentedArchSpec(platform_sp.get(), option_arg);
+ } break;
+
+ case 'n':
+ match_info.GetProcessInfo().GetExecutableFile().SetFile(
+ option_arg, FileSpec::Style::native);
+ match_info.SetNameMatchType(NameMatch::Equals);
+ break;
+
+ case 'e':
+ match_info.GetProcessInfo().GetExecutableFile().SetFile(
+ option_arg, FileSpec::Style::native);
+ match_info.SetNameMatchType(NameMatch::EndsWith);
+ break;
+
+ case 's':
+ match_info.GetProcessInfo().GetExecutableFile().SetFile(
+ option_arg, FileSpec::Style::native);
+ match_info.SetNameMatchType(NameMatch::StartsWith);
+ break;
+
+ case 'c':
+ match_info.GetProcessInfo().GetExecutableFile().SetFile(
+ option_arg, FileSpec::Style::native);
+ match_info.SetNameMatchType(NameMatch::Contains);
+ break;
+
+ case 'r':
+ match_info.GetProcessInfo().GetExecutableFile().SetFile(
+ option_arg, FileSpec::Style::native);
+ match_info.SetNameMatchType(NameMatch::RegularExpression);
+ break;
+
+ case 'A':
+ show_args = true;
+ break;
+
+ case 'v':
+ verbose = true;
+ break;
+
+ case 'x':
+ match_info.SetMatchAllUsers(true);
+ break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ match_info.Clear();
+ show_args = false;
+ verbose = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_platform_process_list_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ ProcessInstanceInfoMatch match_info;
+ bool show_args = false;
+ bool verbose = false;
+ };
+
+ CommandOptions m_options;
+};
+
+// "platform process info"
+class CommandObjectPlatformProcessInfo : public CommandObjectParsed {
+public:
+ CommandObjectPlatformProcessInfo(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "platform process info",
+ "Get detailed information for one or more process by process ID.",
+ "platform process info <pid> [<pid> <pid> ...]", 0) {
+ CommandArgumentEntry arg;
+ CommandArgumentData pid_args;
+
+ // Define the first (and only) variant of this arg.
+ pid_args.arg_type = eArgTypePid;
+ pid_args.arg_repetition = eArgRepeatStar;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(pid_args);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectPlatformProcessInfo() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eProcessIDCompletion,
+ request, nullptr);
+ }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ PlatformSP platform_sp;
+ if (target) {
+ platform_sp = target->GetPlatform();
+ }
+ if (!platform_sp) {
+ platform_sp = GetDebugger().GetPlatformList().GetSelectedPlatform();
+ }
+
+ if (platform_sp) {
+ const size_t argc = args.GetArgumentCount();
+ if (argc > 0) {
+ Status error;
+
+ if (platform_sp->IsConnected()) {
+ Stream &ostrm = result.GetOutputStream();
+ for (auto &entry : args.entries()) {
+ lldb::pid_t pid;
+ if (entry.ref().getAsInteger(0, pid)) {
+ result.AppendErrorWithFormat("invalid process ID argument '%s'",
+ entry.ref().str().c_str());
+ break;
+ } else {
+ ProcessInstanceInfo proc_info;
+ if (platform_sp->GetProcessInfo(pid, proc_info)) {
+ ostrm.Printf("Process information for process %" PRIu64 ":\n",
+ pid);
+ proc_info.Dump(ostrm, platform_sp->GetUserIDResolver());
+ } else {
+ ostrm.Printf("error: no process information is available for "
+ "process %" PRIu64 "\n",
+ pid);
+ }
+ ostrm.EOL();
+ }
+ }
+ } else {
+ // Not connected...
+ result.AppendErrorWithFormatv("not connected to '{0}'",
+ platform_sp->GetPluginName());
+ }
+ } else {
+ // No args
+ result.AppendError("one or more process id(s) must be specified");
+ }
+ } else {
+ result.AppendError("no platform is currently selected");
+ }
+ return result.Succeeded();
+ }
+};
+
+#define LLDB_OPTIONS_platform_process_attach
+#include "CommandOptions.inc"
+
+class CommandObjectPlatformProcessAttach : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() {
+ // Keep default values of all options in one place: OptionParsingStarting
+ // ()
+ OptionParsingStarting(nullptr);
+ }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ char short_option = (char)m_getopt_table[option_idx].val;
+ switch (short_option) {
+ case 'p': {
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+ if (option_arg.getAsInteger(0, pid)) {
+ error.SetErrorStringWithFormat("invalid process ID '%s'",
+ option_arg.str().c_str());
+ } else {
+ attach_info.SetProcessID(pid);
+ }
+ } break;
+
+ case 'P':
+ attach_info.SetProcessPluginName(option_arg);
+ break;
+
+ case 'n':
+ attach_info.GetExecutableFile().SetFile(option_arg,
+ FileSpec::Style::native);
+ break;
+
+ case 'w':
+ attach_info.SetWaitForLaunch(true);
+ break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ attach_info.Clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_platform_process_attach_options);
+ }
+
+ // Options table: Required for subclasses of Options.
+
+ static OptionDefinition g_option_table[];
+
+ // Instance variables to hold the values for command options.
+
+ ProcessAttachInfo attach_info;
+ };
+
+ CommandObjectPlatformProcessAttach(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform process attach",
+ "Attach to a process.",
+ "platform process attach <cmd-options>") {}
+
+ ~CommandObjectPlatformProcessAttach() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ Status err;
+ ProcessSP remote_process_sp = platform_sp->Attach(
+ m_options.attach_info, GetDebugger(), nullptr, err);
+ if (err.Fail()) {
+ result.AppendError(err.AsCString());
+ } else if (!remote_process_sp) {
+ result.AppendError("could not attach: unknown reason");
+ } else
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError("no platform is currently selected");
+ }
+ return result.Succeeded();
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ CommandOptions m_options;
+};
+
+class CommandObjectPlatformProcess : public CommandObjectMultiword {
+public:
+ // Constructors and Destructors
+ CommandObjectPlatformProcess(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "platform process",
+ "Commands to query, launch and attach to "
+ "processes on the current platform.",
+ "platform process [attach|launch|list] ...") {
+ LoadSubCommand(
+ "attach",
+ CommandObjectSP(new CommandObjectPlatformProcessAttach(interpreter)));
+ LoadSubCommand(
+ "launch",
+ CommandObjectSP(new CommandObjectPlatformProcessLaunch(interpreter)));
+ LoadSubCommand("info", CommandObjectSP(new CommandObjectPlatformProcessInfo(
+ interpreter)));
+ LoadSubCommand("list", CommandObjectSP(new CommandObjectPlatformProcessList(
+ interpreter)));
+ }
+
+ ~CommandObjectPlatformProcess() override = default;
+
+private:
+ // For CommandObjectPlatform only
+ CommandObjectPlatformProcess(const CommandObjectPlatformProcess &) = delete;
+ const CommandObjectPlatformProcess &
+ operator=(const CommandObjectPlatformProcess &) = delete;
+};
+
+// "platform shell"
+#define LLDB_OPTIONS_platform_shell
+#include "CommandOptions.inc"
+
+class CommandObjectPlatformShell : public CommandObjectRaw {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_platform_shell_options);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+
+ const char short_option = (char)GetDefinitions()[option_idx].short_option;
+
+ switch (short_option) {
+ case 'h':
+ m_use_host_platform = true;
+ break;
+ case 't':
+ uint32_t timeout_sec;
+ if (option_arg.getAsInteger(10, timeout_sec))
+ error.SetErrorStringWithFormat(
+ "could not convert \"%s\" to a numeric value.",
+ option_arg.str().c_str());
+ else
+ m_timeout = std::chrono::seconds(timeout_sec);
+ break;
+ case 's': {
+ if (option_arg.empty()) {
+ error.SetErrorStringWithFormat(
+ "missing shell interpreter path for option -i|--interpreter.");
+ return error;
+ }
+
+ m_shell_interpreter = option_arg.str();
+ break;
+ }
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_timeout.reset();
+ m_use_host_platform = false;
+ m_shell_interpreter.clear();
+ }
+
+ Timeout<std::micro> m_timeout = std::chrono::seconds(10);
+ bool m_use_host_platform;
+ std::string m_shell_interpreter;
+ };
+
+ CommandObjectPlatformShell(CommandInterpreter &interpreter)
+ : CommandObjectRaw(interpreter, "platform shell",
+ "Run a shell command on the current platform.",
+ "platform shell <shell-command>", 0) {
+ CommandArgumentData thread_arg{eArgTypeNone, eArgRepeatStar};
+ m_arguments.push_back({thread_arg});
+ }
+
+ ~CommandObjectPlatformShell() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ bool DoExecute(llvm::StringRef raw_command_line,
+ CommandReturnObject &result) override {
+ ExecutionContext exe_ctx = GetCommandInterpreter().GetExecutionContext();
+ m_options.NotifyOptionParsingStarting(&exe_ctx);
+
+ // Print out an usage syntax on an empty command line.
+ if (raw_command_line.empty()) {
+ result.GetOutputStream().Printf("%s\n", this->GetSyntax().str().c_str());
+ return true;
+ }
+
+ const bool is_alias = !raw_command_line.contains("platform");
+ OptionsWithRaw args(raw_command_line);
+
+ if (args.HasArgs())
+ if (!ParseOptions(args.GetArgs(), result))
+ return false;
+
+ if (args.GetRawPart().empty()) {
+ result.GetOutputStream().Printf("%s <shell-command>\n",
+ is_alias ? "shell" : "platform shell");
+ return false;
+ }
+
+ llvm::StringRef cmd = args.GetRawPart();
+
+ PlatformSP platform_sp(
+ m_options.m_use_host_platform
+ ? Platform::GetHostPlatform()
+ : GetDebugger().GetPlatformList().GetSelectedPlatform());
+ Status error;
+ if (platform_sp) {
+ FileSpec working_dir{};
+ std::string output;
+ int status = -1;
+ int signo = -1;
+ error = (platform_sp->RunShellCommand(m_options.m_shell_interpreter, cmd,
+ working_dir, &status, &signo,
+ &output, m_options.m_timeout));
+ if (!output.empty())
+ result.GetOutputStream().PutCString(output);
+ if (status > 0) {
+ if (signo > 0) {
+ const char *signo_cstr = Host::GetSignalAsCString(signo);
+ if (signo_cstr)
+ result.GetOutputStream().Printf(
+ "error: command returned with status %i and signal %s\n",
+ status, signo_cstr);
+ else
+ result.GetOutputStream().Printf(
+ "error: command returned with status %i and signal %i\n",
+ status, signo);
+ } else
+ result.GetOutputStream().Printf(
+ "error: command returned with status %i\n", status);
+ }
+ } else {
+ result.GetOutputStream().Printf(
+ "error: cannot run remote shell commands without a platform\n");
+ error.SetErrorString(
+ "error: cannot run remote shell commands without a platform");
+ }
+
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ } else {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+ return true;
+ }
+
+ CommandOptions m_options;
+};
+
+// "platform install" - install a target to a remote end
+class CommandObjectPlatformInstall : public CommandObjectParsed {
+public:
+ CommandObjectPlatformInstall(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "platform target-install",
+ "Install a target (bundle or executable file) to the remote end.",
+ "platform target-install <local-thing> <remote-sandbox>", 0) {
+ CommandArgumentData local_arg{eArgTypePath, eArgRepeatPlain};
+ CommandArgumentData remote_arg{eArgTypePath, eArgRepeatPlain};
+ m_arguments.push_back({local_arg});
+ m_arguments.push_back({remote_arg});
+ }
+
+ ~CommandObjectPlatformInstall() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (request.GetCursorIndex())
+ return;
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+ request, nullptr);
+ }
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ if (args.GetArgumentCount() != 2) {
+ result.AppendError("platform target-install takes two arguments");
+ return false;
+ }
+ // TODO: move the bulk of this code over to the platform itself
+ FileSpec src(args.GetArgumentAtIndex(0));
+ FileSystem::Instance().Resolve(src);
+ FileSpec dst(args.GetArgumentAtIndex(1));
+ if (!FileSystem::Instance().Exists(src)) {
+ result.AppendError("source location does not exist or is not accessible");
+ return false;
+ }
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (!platform_sp) {
+ result.AppendError("no platform currently selected");
+ return false;
+ }
+
+ Status error = platform_sp->Install(src, dst);
+ if (error.Success()) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendErrorWithFormat("install failed: %s", error.AsCString());
+ }
+ return result.Succeeded();
+ }
+};
+
+CommandObjectPlatform::CommandObjectPlatform(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "platform", "Commands to manage and create platforms.",
+ "platform [connect|disconnect|info|list|status|select] ...") {
+ LoadSubCommand("select",
+ CommandObjectSP(new CommandObjectPlatformSelect(interpreter)));
+ LoadSubCommand("list",
+ CommandObjectSP(new CommandObjectPlatformList(interpreter)));
+ LoadSubCommand("status",
+ CommandObjectSP(new CommandObjectPlatformStatus(interpreter)));
+ LoadSubCommand("connect", CommandObjectSP(
+ new CommandObjectPlatformConnect(interpreter)));
+ LoadSubCommand(
+ "disconnect",
+ CommandObjectSP(new CommandObjectPlatformDisconnect(interpreter)));
+ LoadSubCommand("settings", CommandObjectSP(new CommandObjectPlatformSettings(
+ interpreter)));
+ LoadSubCommand("mkdir",
+ CommandObjectSP(new CommandObjectPlatformMkDir(interpreter)));
+ LoadSubCommand("file",
+ CommandObjectSP(new CommandObjectPlatformFile(interpreter)));
+ LoadSubCommand("file-exists",
+ CommandObjectSP(new CommandObjectPlatformFileExists(interpreter)));
+ LoadSubCommand("get-file", CommandObjectSP(new CommandObjectPlatformGetFile(
+ interpreter)));
+ LoadSubCommand("get-permissions",
+ CommandObjectSP(new CommandObjectPlatformGetPermissions(interpreter)));
+ LoadSubCommand("get-size", CommandObjectSP(new CommandObjectPlatformGetSize(
+ interpreter)));
+ LoadSubCommand("put-file", CommandObjectSP(new CommandObjectPlatformPutFile(
+ interpreter)));
+ LoadSubCommand("process", CommandObjectSP(
+ new CommandObjectPlatformProcess(interpreter)));
+ LoadSubCommand("shell",
+ CommandObjectSP(new CommandObjectPlatformShell(interpreter)));
+ LoadSubCommand(
+ "target-install",
+ CommandObjectSP(new CommandObjectPlatformInstall(interpreter)));
+}
+
+CommandObjectPlatform::~CommandObjectPlatform() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectPlatform.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectPlatform.h
new file mode 100644
index 000000000000..86f55c7d9b08
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectPlatform.h
@@ -0,0 +1,32 @@
+//===-- CommandObjectPlatform.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTPLATFORM_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTPLATFORM_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+// CommandObjectPlatform
+
+class CommandObjectPlatform : public CommandObjectMultiword {
+public:
+ CommandObjectPlatform(CommandInterpreter &interpreter);
+
+ ~CommandObjectPlatform() override;
+
+private:
+ CommandObjectPlatform(const CommandObjectPlatform &) = delete;
+ const CommandObjectPlatform &
+ operator=(const CommandObjectPlatform &) = delete;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTPLATFORM_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectPlugin.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectPlugin.cpp
new file mode 100644
index 000000000000..881415a49472
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectPlugin.cpp
@@ -0,0 +1,79 @@
+//===-- CommandObjectPlugin.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectPlugin.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+class CommandObjectPluginLoad : public CommandObjectParsed {
+public:
+ CommandObjectPluginLoad(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "plugin load",
+ "Import a dylib that implements an LLDB plugin.",
+ nullptr) {
+ CommandArgumentEntry arg1;
+ CommandArgumentData cmd_arg;
+
+ // Define the first (and only) variant of this arg.
+ cmd_arg.arg_type = eArgTypeFilename;
+ cmd_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(cmd_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ }
+
+ ~CommandObjectPluginLoad() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+ request, nullptr);
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ size_t argc = command.GetArgumentCount();
+
+ if (argc != 1) {
+ result.AppendError("'plugin load' requires one argument");
+ return false;
+ }
+
+ Status error;
+
+ FileSpec dylib_fspec(command[0].ref());
+ FileSystem::Instance().Resolve(dylib_fspec);
+
+ if (GetDebugger().LoadPlugin(dylib_fspec, error))
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else {
+ result.AppendError(error.AsCString());
+ }
+
+ return result.Succeeded();
+ }
+};
+
+CommandObjectPlugin::CommandObjectPlugin(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "plugin",
+ "Commands for managing LLDB plugins.",
+ "plugin <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("load",
+ CommandObjectSP(new CommandObjectPluginLoad(interpreter)));
+}
+
+CommandObjectPlugin::~CommandObjectPlugin() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectPlugin.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectPlugin.h
new file mode 100644
index 000000000000..6db9f0a40a40
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectPlugin.h
@@ -0,0 +1,25 @@
+//===-- CommandObjectPlugin.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTPLUGIN_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTPLUGIN_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+class CommandObjectPlugin : public CommandObjectMultiword {
+public:
+ CommandObjectPlugin(CommandInterpreter &interpreter);
+
+ ~CommandObjectPlugin() override;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTPLUGIN_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.cpp
new file mode 100644
index 000000000000..d36a574aba7d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.cpp
@@ -0,0 +1,1999 @@
+//===-- CommandObjectProcess.cpp ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectProcess.h"
+#include "CommandObjectTrace.h"
+#include "CommandObjectBreakpoint.h"
+#include "CommandOptionsProcessLaunch.h"
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointIDList.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/BreakpointName.h"
+#include "lldb/Breakpoint/BreakpointSite.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionGroupPythonClassWithDict.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/State.h"
+
+#include "llvm/ADT/ScopeExit.h"
+
+#include <bitset>
+
+using namespace lldb;
+using namespace lldb_private;
+
+class CommandObjectProcessLaunchOrAttach : public CommandObjectParsed {
+public:
+ CommandObjectProcessLaunchOrAttach(CommandInterpreter &interpreter,
+ const char *name, const char *help,
+ const char *syntax, uint32_t flags,
+ const char *new_process_action)
+ : CommandObjectParsed(interpreter, name, help, syntax, flags),
+ m_new_process_action(new_process_action) {}
+
+ ~CommandObjectProcessLaunchOrAttach() override = default;
+
+protected:
+ bool StopProcessIfNecessary(Process *process, StateType &state,
+ CommandReturnObject &result) {
+ state = eStateInvalid;
+ if (process) {
+ state = process->GetState();
+
+ if (process->IsAlive() && state != eStateConnected) {
+ std::string message;
+ if (process->GetState() == eStateAttaching)
+ message =
+ llvm::formatv("There is a pending attach, abort it and {0}?",
+ m_new_process_action);
+ else if (process->GetShouldDetach())
+ message = llvm::formatv(
+ "There is a running process, detach from it and {0}?",
+ m_new_process_action);
+ else
+ message =
+ llvm::formatv("There is a running process, kill it and {0}?",
+ m_new_process_action);
+
+ if (!m_interpreter.Confirm(message, true)) {
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else {
+ if (process->GetShouldDetach()) {
+ bool keep_stopped = false;
+ Status detach_error(process->Detach(keep_stopped));
+ if (detach_error.Success()) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ process = nullptr;
+ } else {
+ result.AppendErrorWithFormat(
+ "Failed to detach from process: %s\n",
+ detach_error.AsCString());
+ }
+ } else {
+ Status destroy_error(process->Destroy(false));
+ if (destroy_error.Success()) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ process = nullptr;
+ } else {
+ result.AppendErrorWithFormat("Failed to kill process: %s\n",
+ destroy_error.AsCString());
+ }
+ }
+ }
+ }
+ }
+ return result.Succeeded();
+ }
+
+ std::string m_new_process_action;
+};
+
+// CommandObjectProcessLaunch
+#pragma mark CommandObjectProcessLaunch
+class CommandObjectProcessLaunch : public CommandObjectProcessLaunchOrAttach {
+public:
+ CommandObjectProcessLaunch(CommandInterpreter &interpreter)
+ : CommandObjectProcessLaunchOrAttach(
+ interpreter, "process launch",
+ "Launch the executable in the debugger.", nullptr,
+ eCommandRequiresTarget, "restart"),
+
+ m_class_options("scripted process", true, 'C', 'k', 'v', 0) {
+ m_all_options.Append(&m_options);
+ m_all_options.Append(&m_class_options, LLDB_OPT_SET_1 | LLDB_OPT_SET_2,
+ LLDB_OPT_SET_ALL);
+ m_all_options.Finalize();
+
+ CommandArgumentEntry arg;
+ CommandArgumentData run_args_arg;
+
+ // Define the first (and only) variant of this arg.
+ run_args_arg.arg_type = eArgTypeRunArgs;
+ run_args_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(run_args_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectProcessLaunch() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+ request, nullptr);
+ }
+
+ Options *GetOptions() override { return &m_all_options; }
+
+ llvm::Optional<std::string> GetRepeatCommand(Args &current_command_args,
+ uint32_t index) override {
+ // No repeat for "process launch"...
+ return std::string("");
+ }
+
+protected:
+ bool DoExecute(Args &launch_args, CommandReturnObject &result) override {
+ Debugger &debugger = GetDebugger();
+ Target *target = debugger.GetSelectedTarget().get();
+ // If our listener is nullptr, users aren't allows to launch
+ ModuleSP exe_module_sp = target->GetExecutableModule();
+
+ // If the target already has an executable module, then use that. If it
+ // doesn't then someone must be trying to launch using a path that will
+ // make sense to the remote stub, but doesn't exist on the local host.
+ // In that case use the ExecutableFile that was set in the target's
+ // ProcessLaunchInfo.
+ if (exe_module_sp == nullptr && !target->GetProcessLaunchInfo().GetExecutableFile()) {
+ result.AppendError("no file in target, create a debug target using the "
+ "'target create' command");
+ return false;
+ }
+
+ StateType state = eStateInvalid;
+
+ if (!StopProcessIfNecessary(m_exe_ctx.GetProcessPtr(), state, result))
+ return false;
+
+ // Determine whether we will disable ASLR or leave it in the default state
+ // (i.e. enabled if the platform supports it). First check if the process
+ // launch options explicitly turn on/off
+ // disabling ASLR. If so, use that setting;
+ // otherwise, use the 'settings target.disable-aslr' setting.
+ bool disable_aslr = false;
+ if (m_options.disable_aslr != eLazyBoolCalculate) {
+ // The user specified an explicit setting on the process launch line.
+ // Use it.
+ disable_aslr = (m_options.disable_aslr == eLazyBoolYes);
+ } else {
+ // The user did not explicitly specify whether to disable ASLR. Fall
+ // back to the target.disable-aslr setting.
+ disable_aslr = target->GetDisableASLR();
+ }
+
+ if (!m_class_options.GetName().empty()) {
+ m_options.launch_info.SetProcessPluginName("ScriptedProcess");
+ m_options.launch_info.SetScriptedProcessClassName(
+ m_class_options.GetName());
+ m_options.launch_info.SetScriptedProcessDictionarySP(
+ m_class_options.GetStructuredData());
+ target->SetProcessLaunchInfo(m_options.launch_info);
+ }
+
+ if (disable_aslr)
+ m_options.launch_info.GetFlags().Set(eLaunchFlagDisableASLR);
+ else
+ m_options.launch_info.GetFlags().Clear(eLaunchFlagDisableASLR);
+
+ if (target->GetInheritTCC())
+ m_options.launch_info.GetFlags().Set(eLaunchFlagInheritTCCFromParent);
+
+ if (target->GetDetachOnError())
+ m_options.launch_info.GetFlags().Set(eLaunchFlagDetachOnError);
+
+ if (target->GetDisableSTDIO())
+ m_options.launch_info.GetFlags().Set(eLaunchFlagDisableSTDIO);
+
+ // Merge the launch info environment with the target environment.
+ Environment target_env = target->GetEnvironment();
+ m_options.launch_info.GetEnvironment().insert(target_env.begin(),
+ target_env.end());
+
+ llvm::StringRef target_settings_argv0 = target->GetArg0();
+
+ if (!target_settings_argv0.empty()) {
+ m_options.launch_info.GetArguments().AppendArgument(
+ target_settings_argv0);
+ if (exe_module_sp)
+ m_options.launch_info.SetExecutableFile(
+ exe_module_sp->GetPlatformFileSpec(), false);
+ else
+ m_options.launch_info.SetExecutableFile(target->GetProcessLaunchInfo().GetExecutableFile(), false);
+ } else {
+ if (exe_module_sp)
+ m_options.launch_info.SetExecutableFile(
+ exe_module_sp->GetPlatformFileSpec(), true);
+ else
+ m_options.launch_info.SetExecutableFile(target->GetProcessLaunchInfo().GetExecutableFile(), true);
+ }
+
+ if (launch_args.GetArgumentCount() == 0) {
+ m_options.launch_info.GetArguments().AppendArguments(
+ target->GetProcessLaunchInfo().GetArguments());
+ } else {
+ m_options.launch_info.GetArguments().AppendArguments(launch_args);
+ // Save the arguments for subsequent runs in the current target.
+ target->SetRunArguments(launch_args);
+ }
+
+ StreamString stream;
+ Status error = target->Launch(m_options.launch_info, &stream);
+
+ if (error.Success()) {
+ ProcessSP process_sp(target->GetProcessSP());
+ if (process_sp) {
+ // There is a race condition where this thread will return up the call
+ // stack to the main command handler and show an (lldb) prompt before
+ // HandlePrivateEvent (from PrivateStateThread) has a chance to call
+ // PushProcessIOHandler().
+ process_sp->SyncIOHandler(0, std::chrono::seconds(2));
+
+ llvm::StringRef data = stream.GetString();
+ if (!data.empty())
+ result.AppendMessage(data);
+ // If we didn't have a local executable, then we wouldn't have had an
+ // executable module before launch.
+ if (!exe_module_sp)
+ exe_module_sp = target->GetExecutableModule();
+ if (!exe_module_sp) {
+ result.AppendWarning("Could not get executable module after launch.");
+ } else {
+
+ const char *archname =
+ exe_module_sp->GetArchitecture().GetArchitectureName();
+ result.AppendMessageWithFormat(
+ "Process %" PRIu64 " launched: '%s' (%s)\n", process_sp->GetID(),
+ exe_module_sp->GetFileSpec().GetPath().c_str(), archname);
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ result.SetDidChangeProcessState(true);
+ } else {
+ result.AppendError(
+ "no error returned from Target::Launch, and target has no process");
+ }
+ } else {
+ result.AppendError(error.AsCString());
+ }
+ return result.Succeeded();
+ }
+
+ CommandOptionsProcessLaunch m_options;
+ OptionGroupPythonClassWithDict m_class_options;
+ OptionGroupOptions m_all_options;
+};
+
+#define LLDB_OPTIONS_process_attach
+#include "CommandOptions.inc"
+
+#pragma mark CommandObjectProcessAttach
+class CommandObjectProcessAttach : public CommandObjectProcessLaunchOrAttach {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() {
+ // Keep default values of all options in one place: OptionParsingStarting
+ // ()
+ OptionParsingStarting(nullptr);
+ }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+ switch (short_option) {
+ case 'c':
+ attach_info.SetContinueOnceAttached(true);
+ break;
+
+ case 'p': {
+ lldb::pid_t pid;
+ if (option_arg.getAsInteger(0, pid)) {
+ error.SetErrorStringWithFormat("invalid process ID '%s'",
+ option_arg.str().c_str());
+ } else {
+ attach_info.SetProcessID(pid);
+ }
+ } break;
+
+ case 'P':
+ attach_info.SetProcessPluginName(option_arg);
+ break;
+
+ case 'n':
+ attach_info.GetExecutableFile().SetFile(option_arg,
+ FileSpec::Style::native);
+ break;
+
+ case 'w':
+ attach_info.SetWaitForLaunch(true);
+ break;
+
+ case 'i':
+ attach_info.SetIgnoreExisting(false);
+ break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ attach_info.Clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_process_attach_options);
+ }
+
+ ProcessAttachInfo attach_info;
+ };
+
+ CommandObjectProcessAttach(CommandInterpreter &interpreter)
+ : CommandObjectProcessLaunchOrAttach(
+ interpreter, "process attach", "Attach to a process.",
+ "process attach <cmd-options>", 0, "attach") {}
+
+ ~CommandObjectProcessAttach() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ // N.B. The attach should be synchronous. It doesn't help much to get the
+ // prompt back between initiating the attach and the target actually
+ // stopping. So even if the interpreter is set to be asynchronous, we wait
+ // for the stop ourselves here.
+
+ StateType state = eStateInvalid;
+ Process *process = m_exe_ctx.GetProcessPtr();
+
+ if (!StopProcessIfNecessary(process, state, result))
+ return false;
+
+ if (target == nullptr) {
+ // If there isn't a current target create one.
+ TargetSP new_target_sp;
+ Status error;
+
+ error = GetDebugger().GetTargetList().CreateTarget(
+ GetDebugger(), "", "", eLoadDependentsNo,
+ nullptr, // No platform options
+ new_target_sp);
+ target = new_target_sp.get();
+ if (target == nullptr || error.Fail()) {
+ result.AppendError(error.AsCString("Error creating target"));
+ return false;
+ }
+ }
+
+ // Record the old executable module, we want to issue a warning if the
+ // process of attaching changed the current executable (like somebody said
+ // "file foo" then attached to a PID whose executable was bar.)
+
+ ModuleSP old_exec_module_sp = target->GetExecutableModule();
+ ArchSpec old_arch_spec = target->GetArchitecture();
+
+ StreamString stream;
+ ProcessSP process_sp;
+ const auto error = target->Attach(m_options.attach_info, &stream);
+ if (error.Success()) {
+ process_sp = target->GetProcessSP();
+ if (process_sp) {
+ result.AppendMessage(stream.GetString());
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ result.SetDidChangeProcessState(true);
+ } else {
+ result.AppendError(
+ "no error returned from Target::Attach, and target has no process");
+ }
+ } else {
+ result.AppendErrorWithFormat("attach failed: %s\n", error.AsCString());
+ }
+
+ if (!result.Succeeded())
+ return false;
+
+ // Okay, we're done. Last step is to warn if the executable module has
+ // changed:
+ char new_path[PATH_MAX];
+ ModuleSP new_exec_module_sp(target->GetExecutableModule());
+ if (!old_exec_module_sp) {
+ // We might not have a module if we attached to a raw pid...
+ if (new_exec_module_sp) {
+ new_exec_module_sp->GetFileSpec().GetPath(new_path, PATH_MAX);
+ result.AppendMessageWithFormat("Executable module set to \"%s\".\n",
+ new_path);
+ }
+ } else if (old_exec_module_sp->GetFileSpec() !=
+ new_exec_module_sp->GetFileSpec()) {
+ char old_path[PATH_MAX];
+
+ old_exec_module_sp->GetFileSpec().GetPath(old_path, PATH_MAX);
+ new_exec_module_sp->GetFileSpec().GetPath(new_path, PATH_MAX);
+
+ result.AppendWarningWithFormat(
+ "Executable module changed from \"%s\" to \"%s\".\n", old_path,
+ new_path);
+ }
+
+ if (!old_arch_spec.IsValid()) {
+ result.AppendMessageWithFormat(
+ "Architecture set to: %s.\n",
+ target->GetArchitecture().GetTriple().getTriple().c_str());
+ } else if (!old_arch_spec.IsExactMatch(target->GetArchitecture())) {
+ result.AppendWarningWithFormat(
+ "Architecture changed from %s to %s.\n",
+ old_arch_spec.GetTriple().getTriple().c_str(),
+ target->GetArchitecture().GetTriple().getTriple().c_str());
+ }
+
+ // This supports the use-case scenario of immediately continuing the
+ // process once attached.
+ if (m_options.attach_info.GetContinueOnceAttached()) {
+ // We have made a process but haven't told the interpreter about it yet,
+ // so CheckRequirements will fail for "process continue". Set the override
+ // here:
+ ExecutionContext exe_ctx(process_sp);
+ m_interpreter.HandleCommand("process continue", eLazyBoolNo, exe_ctx, result);
+ }
+
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectProcessContinue
+
+#define LLDB_OPTIONS_process_continue
+#include "CommandOptions.inc"
+
+#pragma mark CommandObjectProcessContinue
+
+class CommandObjectProcessContinue : public CommandObjectParsed {
+public:
+ CommandObjectProcessContinue(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "process continue",
+ "Continue execution of all threads in the current process.",
+ "process continue",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {}
+
+ ~CommandObjectProcessContinue() override = default;
+
+protected:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() {
+ // Keep default values of all options in one place: OptionParsingStarting
+ // ()
+ OptionParsingStarting(nullptr);
+ }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *exe_ctx) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+ switch (short_option) {
+ case 'i':
+ if (option_arg.getAsInteger(0, m_ignore))
+ error.SetErrorStringWithFormat(
+ "invalid value for ignore option: \"%s\", should be a number.",
+ option_arg.str().c_str());
+ break;
+ case 'b':
+ m_run_to_bkpt_args.AppendArgument(option_arg);
+ m_any_bkpts_specified = true;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_ignore = 0;
+ m_run_to_bkpt_args.Clear();
+ m_any_bkpts_specified = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_process_continue_options);
+ }
+
+ uint32_t m_ignore = 0;
+ Args m_run_to_bkpt_args;
+ bool m_any_bkpts_specified = false;
+ };
+
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+ bool synchronous_execution = m_interpreter.GetSynchronous();
+ StateType state = process->GetState();
+ if (state == eStateStopped) {
+ if (m_options.m_ignore > 0) {
+ ThreadSP sel_thread_sp(GetDefaultThread()->shared_from_this());
+ if (sel_thread_sp) {
+ StopInfoSP stop_info_sp = sel_thread_sp->GetStopInfo();
+ if (stop_info_sp &&
+ stop_info_sp->GetStopReason() == eStopReasonBreakpoint) {
+ lldb::break_id_t bp_site_id =
+ (lldb::break_id_t)stop_info_sp->GetValue();
+ BreakpointSiteSP bp_site_sp(
+ process->GetBreakpointSiteList().FindByID(bp_site_id));
+ if (bp_site_sp) {
+ const size_t num_owners = bp_site_sp->GetNumberOfOwners();
+ for (size_t i = 0; i < num_owners; i++) {
+ Breakpoint &bp_ref =
+ bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint();
+ if (!bp_ref.IsInternal()) {
+ bp_ref.SetIgnoreCount(m_options.m_ignore);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Target *target = m_exe_ctx.GetTargetPtr();
+ BreakpointIDList run_to_bkpt_ids;
+ // Don't pass an empty run_to_breakpoint list, as Verify will look for the
+ // default breakpoint.
+ if (m_options.m_run_to_bkpt_args.GetArgumentCount() > 0)
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
+ m_options.m_run_to_bkpt_args, target, result, &run_to_bkpt_ids,
+ BreakpointName::Permissions::disablePerm);
+ if (!result.Succeeded()) {
+ return false;
+ }
+ result.Clear();
+ if (m_options.m_any_bkpts_specified && run_to_bkpt_ids.GetSize() == 0) {
+ result.AppendError("continue-to breakpoints did not specify any actual "
+ "breakpoints or locations");
+ return false;
+ }
+
+ // First figure out which breakpoints & locations were specified by the
+ // user:
+ size_t num_run_to_bkpt_ids = run_to_bkpt_ids.GetSize();
+ std::vector<break_id_t> bkpts_disabled;
+ std::vector<BreakpointID> locs_disabled;
+ if (num_run_to_bkpt_ids != 0) {
+ // Go through the ID's specified, and separate the breakpoints from are
+ // the breakpoint.location specifications since the latter require
+ // special handling. We also figure out whether there's at least one
+ // specifier in the set that is enabled.
+ BreakpointList &bkpt_list = target->GetBreakpointList();
+ std::unordered_set<break_id_t> bkpts_seen;
+ std::unordered_set<break_id_t> bkpts_with_locs_seen;
+ BreakpointIDList with_locs;
+ bool any_enabled = false;
+
+ for (size_t idx = 0; idx < num_run_to_bkpt_ids; idx++) {
+ BreakpointID bkpt_id = run_to_bkpt_ids.GetBreakpointIDAtIndex(idx);
+ break_id_t bp_id = bkpt_id.GetBreakpointID();
+ break_id_t loc_id = bkpt_id.GetLocationID();
+ BreakpointSP bp_sp
+ = bkpt_list.FindBreakpointByID(bp_id);
+ // Note, VerifyBreakpointOrLocationIDs checks for existence, so we
+ // don't need to do it again here.
+ if (bp_sp->IsEnabled()) {
+ if (loc_id == LLDB_INVALID_BREAK_ID) {
+ // A breakpoint (without location) was specified. Make sure that
+ // at least one of the locations is enabled.
+ size_t num_locations = bp_sp->GetNumLocations();
+ for (size_t loc_idx = 0; loc_idx < num_locations; loc_idx++) {
+ BreakpointLocationSP loc_sp
+ = bp_sp->GetLocationAtIndex(loc_idx);
+ if (loc_sp->IsEnabled()) {
+ any_enabled = true;
+ break;
+ }
+ }
+ } else {
+ // A location was specified, check if it was enabled:
+ BreakpointLocationSP loc_sp = bp_sp->FindLocationByID(loc_id);
+ if (loc_sp->IsEnabled())
+ any_enabled = true;
+ }
+
+ // Then sort the bp & bp.loc entries for later use:
+ if (bkpt_id.GetLocationID() == LLDB_INVALID_BREAK_ID)
+ bkpts_seen.insert(bkpt_id.GetBreakpointID());
+ else {
+ bkpts_with_locs_seen.insert(bkpt_id.GetBreakpointID());
+ with_locs.AddBreakpointID(bkpt_id);
+ }
+ }
+ }
+ // Do all the error checking here so once we start disabling we don't
+ // have to back out half-way through.
+
+ // Make sure at least one of the specified breakpoints is enabled.
+ if (!any_enabled) {
+ result.AppendError("at least one of the continue-to breakpoints must "
+ "be enabled.");
+ return false;
+ }
+
+ // Also, if you specify BOTH a breakpoint and one of it's locations,
+ // we flag that as an error, since it won't do what you expect, the
+ // breakpoint directive will mean "run to all locations", which is not
+ // what the location directive means...
+ for (break_id_t bp_id : bkpts_with_locs_seen) {
+ if (bkpts_seen.count(bp_id)) {
+ result.AppendErrorWithFormatv("can't specify both a breakpoint and "
+ "one of its locations: {0}", bp_id);
+ }
+ }
+
+ // Now go through the breakpoints in the target, disabling all the ones
+ // that the user didn't mention:
+ for (BreakpointSP bp_sp : bkpt_list.Breakpoints()) {
+ break_id_t bp_id = bp_sp->GetID();
+ // Handle the case where no locations were specified. Note we don't
+ // have to worry about the case where a breakpoint and one of its
+ // locations are both in the lists, we've already disallowed that.
+ if (!bkpts_with_locs_seen.count(bp_id)) {
+ if (!bkpts_seen.count(bp_id) && bp_sp->IsEnabled()) {
+ bkpts_disabled.push_back(bp_id);
+ bp_sp->SetEnabled(false);
+ }
+ continue;
+ }
+ // Next, handle the case where a location was specified:
+ // Run through all the locations of this breakpoint and disable
+ // the ones that aren't on our "with locations" BreakpointID list:
+ size_t num_locations = bp_sp->GetNumLocations();
+ BreakpointID tmp_id(bp_id, LLDB_INVALID_BREAK_ID);
+ for (size_t loc_idx = 0; loc_idx < num_locations; loc_idx++) {
+ BreakpointLocationSP loc_sp = bp_sp->GetLocationAtIndex(loc_idx);
+ tmp_id.SetBreakpointLocationID(loc_idx);
+ size_t position = 0;
+ if (!with_locs.FindBreakpointID(tmp_id, &position)
+ && loc_sp->IsEnabled()) {
+ locs_disabled.push_back(tmp_id);
+ loc_sp->SetEnabled(false);
+ }
+ }
+ }
+ }
+
+ { // Scope for thread list mutex:
+ std::lock_guard<std::recursive_mutex> guard(
+ process->GetThreadList().GetMutex());
+ const uint32_t num_threads = process->GetThreadList().GetSize();
+
+ // Set the actions that the threads should each take when resuming
+ for (uint32_t idx = 0; idx < num_threads; ++idx) {
+ const bool override_suspend = false;
+ process->GetThreadList().GetThreadAtIndex(idx)->SetResumeState(
+ eStateRunning, override_suspend);
+ }
+ }
+
+ const uint32_t iohandler_id = process->GetIOHandlerID();
+
+ StreamString stream;
+ Status error;
+ // For now we can only do -b with synchronous:
+ bool old_sync = GetDebugger().GetAsyncExecution();
+
+ if (run_to_bkpt_ids.GetSize() != 0) {
+ GetDebugger().SetAsyncExecution(false);
+ synchronous_execution = true;
+ }
+ if (synchronous_execution)
+ error = process->ResumeSynchronous(&stream);
+ else
+ error = process->Resume();
+
+ if (run_to_bkpt_ids.GetSize() != 0) {
+ GetDebugger().SetAsyncExecution(old_sync);
+ }
+
+ // Now re-enable the breakpoints we disabled:
+ BreakpointList &bkpt_list = target->GetBreakpointList();
+ for (break_id_t bp_id : bkpts_disabled) {
+ BreakpointSP bp_sp = bkpt_list.FindBreakpointByID(bp_id);
+ if (bp_sp)
+ bp_sp->SetEnabled(true);
+ }
+ for (const BreakpointID &bkpt_id : locs_disabled) {
+ BreakpointSP bp_sp
+ = bkpt_list.FindBreakpointByID(bkpt_id.GetBreakpointID());
+ if (bp_sp) {
+ BreakpointLocationSP loc_sp
+ = bp_sp->FindLocationByID(bkpt_id.GetLocationID());
+ if (loc_sp)
+ loc_sp->SetEnabled(true);
+ }
+ }
+
+ if (error.Success()) {
+ // There is a race condition where this thread will return up the call
+ // stack to the main command handler and show an (lldb) prompt before
+ // HandlePrivateEvent (from PrivateStateThread) has a chance to call
+ // PushProcessIOHandler().
+ process->SyncIOHandler(iohandler_id, std::chrono::seconds(2));
+
+ result.AppendMessageWithFormat("Process %" PRIu64 " resuming\n",
+ process->GetID());
+ if (synchronous_execution) {
+ // If any state changed events had anything to say, add that to the
+ // result
+ result.AppendMessage(stream.GetString());
+
+ result.SetDidChangeProcessState(true);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.SetStatus(eReturnStatusSuccessContinuingNoResult);
+ }
+ } else {
+ result.AppendErrorWithFormat("Failed to resume process: %s.\n",
+ error.AsCString());
+ }
+ } else {
+ result.AppendErrorWithFormat(
+ "Process cannot be continued from its current state (%s).\n",
+ StateAsCString(state));
+ }
+ return result.Succeeded();
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectProcessDetach
+#define LLDB_OPTIONS_process_detach
+#include "CommandOptions.inc"
+
+#pragma mark CommandObjectProcessDetach
+
+class CommandObjectProcessDetach : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() { OptionParsingStarting(nullptr); }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 's':
+ bool tmp_result;
+ bool success;
+ tmp_result = OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (!success)
+ error.SetErrorStringWithFormat("invalid boolean option: \"%s\"",
+ option_arg.str().c_str());
+ else {
+ if (tmp_result)
+ m_keep_stopped = eLazyBoolYes;
+ else
+ m_keep_stopped = eLazyBoolNo;
+ }
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_keep_stopped = eLazyBoolCalculate;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_process_detach_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ LazyBool m_keep_stopped;
+ };
+
+ CommandObjectProcessDetach(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process detach",
+ "Detach from the current target process.",
+ "process detach",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched) {}
+
+ ~CommandObjectProcessDetach() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+ // FIXME: This will be a Command Option:
+ bool keep_stopped;
+ if (m_options.m_keep_stopped == eLazyBoolCalculate) {
+ // Check the process default:
+ keep_stopped = process->GetDetachKeepsStopped();
+ } else if (m_options.m_keep_stopped == eLazyBoolYes)
+ keep_stopped = true;
+ else
+ keep_stopped = false;
+
+ Status error(process->Detach(keep_stopped));
+ if (error.Success()) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("Detach failed: %s\n", error.AsCString());
+ return false;
+ }
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectProcessConnect
+#define LLDB_OPTIONS_process_connect
+#include "CommandOptions.inc"
+
+#pragma mark CommandObjectProcessConnect
+
+class CommandObjectProcessConnect : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() {
+ // Keep default values of all options in one place: OptionParsingStarting
+ // ()
+ OptionParsingStarting(nullptr);
+ }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'p':
+ plugin_name.assign(std::string(option_arg));
+ break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ plugin_name.clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_process_connect_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ std::string plugin_name;
+ };
+
+ CommandObjectProcessConnect(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process connect",
+ "Connect to a remote debug service.",
+ "process connect <remote-url>", 0) {
+ CommandArgumentData connect_arg{eArgTypeConnectURL, eArgRepeatPlain};
+ m_arguments.push_back({connect_arg});
+ }
+
+ ~CommandObjectProcessConnect() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (command.GetArgumentCount() != 1) {
+ result.AppendErrorWithFormat(
+ "'%s' takes exactly one argument:\nUsage: %s\n", m_cmd_name.c_str(),
+ m_cmd_syntax.c_str());
+ return false;
+ }
+
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (process && process->IsAlive()) {
+ result.AppendErrorWithFormat(
+ "Process %" PRIu64
+ " is currently being debugged, kill the process before connecting.\n",
+ process->GetID());
+ return false;
+ }
+
+ const char *plugin_name = nullptr;
+ if (!m_options.plugin_name.empty())
+ plugin_name = m_options.plugin_name.c_str();
+
+ Status error;
+ Debugger &debugger = GetDebugger();
+ PlatformSP platform_sp = m_interpreter.GetPlatform(true);
+ ProcessSP process_sp =
+ debugger.GetAsyncExecution()
+ ? platform_sp->ConnectProcess(
+ command.GetArgumentAtIndex(0), plugin_name, debugger,
+ debugger.GetSelectedTarget().get(), error)
+ : platform_sp->ConnectProcessSynchronous(
+ command.GetArgumentAtIndex(0), plugin_name, debugger,
+ result.GetOutputStream(), debugger.GetSelectedTarget().get(),
+ error);
+ if (error.Fail() || process_sp == nullptr) {
+ result.AppendError(error.AsCString("Error connecting to the process"));
+ return false;
+ }
+ return true;
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectProcessPlugin
+#pragma mark CommandObjectProcessPlugin
+
+class CommandObjectProcessPlugin : public CommandObjectProxy {
+public:
+ CommandObjectProcessPlugin(CommandInterpreter &interpreter)
+ : CommandObjectProxy(
+ interpreter, "process plugin",
+ "Send a custom command to the current target process plug-in.",
+ "process plugin <args>", 0) {}
+
+ ~CommandObjectProcessPlugin() override = default;
+
+ CommandObject *GetProxyCommandObject() override {
+ Process *process = m_interpreter.GetExecutionContext().GetProcessPtr();
+ if (process)
+ return process->GetPluginCommandObject();
+ return nullptr;
+ }
+};
+
+// CommandObjectProcessLoad
+#define LLDB_OPTIONS_process_load
+#include "CommandOptions.inc"
+
+#pragma mark CommandObjectProcessLoad
+
+class CommandObjectProcessLoad : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() {
+ // Keep default values of all options in one place: OptionParsingStarting
+ // ()
+ OptionParsingStarting(nullptr);
+ }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+ switch (short_option) {
+ case 'i':
+ do_install = true;
+ if (!option_arg.empty())
+ install_path.SetFile(option_arg, FileSpec::Style::native);
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ do_install = false;
+ install_path.Clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_process_load_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ bool do_install;
+ FileSpec install_path;
+ };
+
+ CommandObjectProcessLoad(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process load",
+ "Load a shared library into the current process.",
+ "process load <filename> [<filename> ...]",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused) {
+ CommandArgumentData file_arg{eArgTypePath, eArgRepeatPlus};
+ m_arguments.push_back({file_arg});
+ }
+
+ ~CommandObjectProcessLoad() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (!m_exe_ctx.HasProcessScope())
+ return;
+
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+ request, nullptr);
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+
+ for (auto &entry : command.entries()) {
+ Status error;
+ PlatformSP platform = process->GetTarget().GetPlatform();
+ llvm::StringRef image_path = entry.ref();
+ uint32_t image_token = LLDB_INVALID_IMAGE_TOKEN;
+
+ if (!m_options.do_install) {
+ FileSpec image_spec(image_path);
+ platform->ResolveRemotePath(image_spec, image_spec);
+ image_token =
+ platform->LoadImage(process, FileSpec(), image_spec, error);
+ } else if (m_options.install_path) {
+ FileSpec image_spec(image_path);
+ FileSystem::Instance().Resolve(image_spec);
+ platform->ResolveRemotePath(m_options.install_path,
+ m_options.install_path);
+ image_token = platform->LoadImage(process, image_spec,
+ m_options.install_path, error);
+ } else {
+ FileSpec image_spec(image_path);
+ FileSystem::Instance().Resolve(image_spec);
+ image_token =
+ platform->LoadImage(process, image_spec, FileSpec(), error);
+ }
+
+ if (image_token != LLDB_INVALID_IMAGE_TOKEN) {
+ result.AppendMessageWithFormat(
+ "Loading \"%s\"...ok\nImage %u loaded.\n", image_path.str().c_str(),
+ image_token);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("failed to load '%s': %s",
+ image_path.str().c_str(),
+ error.AsCString());
+ }
+ }
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectProcessUnload
+#pragma mark CommandObjectProcessUnload
+
+class CommandObjectProcessUnload : public CommandObjectParsed {
+public:
+ CommandObjectProcessUnload(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "process unload",
+ "Unload a shared library from the current process using the index "
+ "returned by a previous call to \"process load\".",
+ "process unload <index>",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {
+ CommandArgumentData load_idx_arg{eArgTypeUnsignedInteger, eArgRepeatPlain};
+ m_arguments.push_back({load_idx_arg});
+ }
+
+ ~CommandObjectProcessUnload() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+
+ if (request.GetCursorIndex() || !m_exe_ctx.HasProcessScope())
+ return;
+
+ Process *process = m_exe_ctx.GetProcessPtr();
+
+ const std::vector<lldb::addr_t> &tokens = process->GetImageTokens();
+ const size_t token_num = tokens.size();
+ for (size_t i = 0; i < token_num; ++i) {
+ if (tokens[i] == LLDB_INVALID_IMAGE_TOKEN)
+ continue;
+ request.TryCompleteCurrentArg(std::to_string(i));
+ }
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+
+ for (auto &entry : command.entries()) {
+ uint32_t image_token;
+ if (entry.ref().getAsInteger(0, image_token)) {
+ result.AppendErrorWithFormat("invalid image index argument '%s'",
+ entry.ref().str().c_str());
+ break;
+ } else {
+ Status error(process->GetTarget().GetPlatform()->UnloadImage(
+ process, image_token));
+ if (error.Success()) {
+ result.AppendMessageWithFormat(
+ "Unloading shared library with index %u...ok\n", image_token);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("failed to unload image: %s",
+ error.AsCString());
+ break;
+ }
+ }
+ }
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectProcessSignal
+#pragma mark CommandObjectProcessSignal
+
+class CommandObjectProcessSignal : public CommandObjectParsed {
+public:
+ CommandObjectProcessSignal(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "process signal",
+ "Send a UNIX signal to the current target process.", nullptr,
+ eCommandRequiresProcess | eCommandTryTargetAPILock) {
+ CommandArgumentEntry arg;
+ CommandArgumentData signal_arg;
+
+ // Define the first (and only) variant of this arg.
+ signal_arg.arg_type = eArgTypeUnixSignal;
+ signal_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(signal_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectProcessSignal() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (!m_exe_ctx.HasProcessScope() || request.GetCursorIndex() != 0)
+ return;
+
+ UnixSignalsSP signals = m_exe_ctx.GetProcessPtr()->GetUnixSignals();
+ int signo = signals->GetFirstSignalNumber();
+ while (signo != LLDB_INVALID_SIGNAL_NUMBER) {
+ request.TryCompleteCurrentArg(signals->GetSignalAsCString(signo));
+ signo = signals->GetNextSignalNumber(signo);
+ }
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+
+ if (command.GetArgumentCount() == 1) {
+ int signo = LLDB_INVALID_SIGNAL_NUMBER;
+
+ const char *signal_name = command.GetArgumentAtIndex(0);
+ if (::isxdigit(signal_name[0])) {
+ if (!llvm::to_integer(signal_name, signo))
+ signo = LLDB_INVALID_SIGNAL_NUMBER;
+ } else
+ signo = process->GetUnixSignals()->GetSignalNumberFromName(signal_name);
+
+ if (signo == LLDB_INVALID_SIGNAL_NUMBER) {
+ result.AppendErrorWithFormat("Invalid signal argument '%s'.\n",
+ command.GetArgumentAtIndex(0));
+ } else {
+ Status error(process->Signal(signo));
+ if (error.Success()) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("Failed to send signal %i: %s\n", signo,
+ error.AsCString());
+ }
+ }
+ } else {
+ result.AppendErrorWithFormat(
+ "'%s' takes exactly one signal number argument:\nUsage: %s\n",
+ m_cmd_name.c_str(), m_cmd_syntax.c_str());
+ }
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectProcessInterrupt
+#pragma mark CommandObjectProcessInterrupt
+
+class CommandObjectProcessInterrupt : public CommandObjectParsed {
+public:
+ CommandObjectProcessInterrupt(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process interrupt",
+ "Interrupt the current target process.",
+ "process interrupt",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched) {}
+
+ ~CommandObjectProcessInterrupt() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (process == nullptr) {
+ result.AppendError("no process to halt");
+ return false;
+ }
+
+ bool clear_thread_plans = true;
+ Status error(process->Halt(clear_thread_plans));
+ if (error.Success()) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("Failed to halt process: %s\n",
+ error.AsCString());
+ }
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectProcessKill
+#pragma mark CommandObjectProcessKill
+
+class CommandObjectProcessKill : public CommandObjectParsed {
+public:
+ CommandObjectProcessKill(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process kill",
+ "Terminate the current target process.",
+ "process kill",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched) {}
+
+ ~CommandObjectProcessKill() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (process == nullptr) {
+ result.AppendError("no process to kill");
+ return false;
+ }
+
+ Status error(process->Destroy(true));
+ if (error.Success()) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("Failed to kill process: %s\n",
+ error.AsCString());
+ }
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectProcessSaveCore
+#pragma mark CommandObjectProcessSaveCore
+
+static constexpr OptionEnumValueElement g_corefile_save_style[] = {
+ {eSaveCoreFull, "full", "Create a core file with all memory saved"},
+ {eSaveCoreDirtyOnly, "modified-memory",
+ "Create a corefile with only modified memory saved"},
+ {eSaveCoreStackOnly, "stack",
+ "Create a corefile with only stack memory saved"}};
+
+static constexpr OptionEnumValues SaveCoreStyles() {
+ return OptionEnumValues(g_corefile_save_style);
+}
+
+#define LLDB_OPTIONS_process_save_core
+#include "CommandOptions.inc"
+
+class CommandObjectProcessSaveCore : public CommandObjectParsed {
+public:
+ CommandObjectProcessSaveCore(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "process save-core",
+ "Save the current process as a core file using an "
+ "appropriate file type.",
+ "process save-core [-s corefile-style -p plugin-name] FILE",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched) {
+ CommandArgumentData file_arg{eArgTypePath, eArgRepeatPlain};
+ m_arguments.push_back({file_arg});
+ }
+
+ ~CommandObjectProcessSaveCore() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_process_save_core_options);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ const int short_option = m_getopt_table[option_idx].val;
+ Status error;
+
+ switch (short_option) {
+ case 'p':
+ m_requested_plugin_name = option_arg.str();
+ break;
+ case 's':
+ m_requested_save_core_style =
+ (lldb::SaveCoreStyle)OptionArgParser::ToOptionEnum(
+ option_arg, GetDefinitions()[option_idx].enum_values,
+ eSaveCoreUnspecified, error);
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return {};
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_requested_save_core_style = eSaveCoreUnspecified;
+ m_requested_plugin_name.clear();
+ }
+
+ // Instance variables to hold the values for command options.
+ SaveCoreStyle m_requested_save_core_style = eSaveCoreUnspecified;
+ std::string m_requested_plugin_name;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ ProcessSP process_sp = m_exe_ctx.GetProcessSP();
+ if (process_sp) {
+ if (command.GetArgumentCount() == 1) {
+ FileSpec output_file(command.GetArgumentAtIndex(0));
+ SaveCoreStyle corefile_style = m_options.m_requested_save_core_style;
+ Status error =
+ PluginManager::SaveCore(process_sp, output_file, corefile_style,
+ m_options.m_requested_plugin_name);
+ if (error.Success()) {
+ if (corefile_style == SaveCoreStyle::eSaveCoreDirtyOnly ||
+ corefile_style == SaveCoreStyle::eSaveCoreStackOnly) {
+ result.AppendMessageWithFormat(
+ "\nModified-memory or stack-memory only corefile "
+ "created. This corefile may \n"
+ "not show library/framework/app binaries "
+ "on a different system, or when \n"
+ "those binaries have "
+ "been updated/modified. Copies are not included\n"
+ "in this corefile. Use --style full to include all "
+ "process memory.\n");
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat(
+ "Failed to save core file for process: %s\n", error.AsCString());
+ }
+ } else {
+ result.AppendErrorWithFormat("'%s' takes one arguments:\nUsage: %s\n",
+ m_cmd_name.c_str(), m_cmd_syntax.c_str());
+ }
+ } else {
+ result.AppendError("invalid process");
+ return false;
+ }
+
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectProcessStatus
+#pragma mark CommandObjectProcessStatus
+#define LLDB_OPTIONS_process_status
+#include "CommandOptions.inc"
+
+class CommandObjectProcessStatus : public CommandObjectParsed {
+public:
+ CommandObjectProcessStatus(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "process status",
+ "Show status and stop location for the current target process.",
+ "process status",
+ eCommandRequiresProcess | eCommandTryTargetAPILock) {}
+
+ ~CommandObjectProcessStatus() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'v':
+ m_verbose = true;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return {};
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_verbose = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_process_status_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ bool m_verbose = false;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Stream &strm = result.GetOutputStream();
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+
+ // No need to check "process" for validity as eCommandRequiresProcess
+ // ensures it is valid
+ Process *process = m_exe_ctx.GetProcessPtr();
+ const bool only_threads_with_stop_reason = true;
+ const uint32_t start_frame = 0;
+ const uint32_t num_frames = 1;
+ const uint32_t num_frames_with_source = 1;
+ const bool stop_format = true;
+ process->GetStatus(strm);
+ process->GetThreadStatus(strm, only_threads_with_stop_reason, start_frame,
+ num_frames, num_frames_with_source, stop_format);
+
+ if (m_options.m_verbose) {
+ addr_t code_mask = process->GetCodeAddressMask();
+ addr_t data_mask = process->GetDataAddressMask();
+ if (code_mask != 0) {
+ int bits = std::bitset<64>(~code_mask).count();
+ result.AppendMessageWithFormat(
+ "Addressable code address mask: 0x%" PRIx64 "\n", code_mask);
+ result.AppendMessageWithFormat(
+ "Addressable data address mask: 0x%" PRIx64 "\n", data_mask);
+ result.AppendMessageWithFormat(
+ "Number of bits used in addressing (code): %d\n", bits);
+ }
+
+ PlatformSP platform_sp = process->GetTarget().GetPlatform();
+ if (!platform_sp) {
+ result.AppendError("Couldn'retrieve the target's platform");
+ return result.Succeeded();
+ }
+
+ auto expected_crash_info =
+ platform_sp->FetchExtendedCrashInformation(*process);
+
+ if (!expected_crash_info) {
+ result.AppendError(llvm::toString(expected_crash_info.takeError()));
+ return result.Succeeded();
+ }
+
+ StructuredData::DictionarySP crash_info_sp = *expected_crash_info;
+
+ if (crash_info_sp) {
+ strm.PutCString("Extended Crash Information:\n");
+ crash_info_sp->Dump(strm);
+ }
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectProcessHandle
+#define LLDB_OPTIONS_process_handle
+#include "CommandOptions.inc"
+
+#pragma mark CommandObjectProcessHandle
+
+class CommandObjectProcessHandle : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() { OptionParsingStarting(nullptr); }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'c':
+ do_clear = true;
+ break;
+ case 'd':
+ dummy = true;
+ break;
+ case 's':
+ stop = std::string(option_arg);
+ break;
+ case 'n':
+ notify = std::string(option_arg);
+ break;
+ case 'p':
+ pass = std::string(option_arg);
+ break;
+ case 't':
+ only_target_values = true;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ stop.clear();
+ notify.clear();
+ pass.clear();
+ only_target_values = false;
+ do_clear = false;
+ dummy = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_process_handle_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ std::string stop;
+ std::string notify;
+ std::string pass;
+ bool only_target_values = false;
+ bool do_clear = false;
+ bool dummy = false;
+ };
+
+ CommandObjectProcessHandle(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process handle",
+ "Manage LLDB handling of OS signals for the "
+ "current target process. Defaults to showing "
+ "current policy.",
+ nullptr) {
+ SetHelpLong("\nIf no signals are specified but one or more actions are, "
+ "and there is a live process, update them all. If no action "
+ "is specified, list the current values.\n"
+ "If you specify actions with no target (e.g. in an init file) "
+ "or in a target with no process "
+ "the values will get copied into subsequent targets, but "
+ "lldb won't be able to spell-check the options since it can't "
+ "know which signal set will later be in force."
+ "\nYou can see the signal modifications held by the target"
+ "by passing the -t option."
+ "\nYou can also clear the target modification for a signal"
+ "by passing the -c option");
+ CommandArgumentEntry arg;
+ CommandArgumentData signal_arg;
+
+ signal_arg.arg_type = eArgTypeUnixSignal;
+ signal_arg.arg_repetition = eArgRepeatStar;
+
+ arg.push_back(signal_arg);
+
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectProcessHandle() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ bool VerifyCommandOptionValue(const std::string &option, int &real_value) {
+ bool okay = true;
+ bool success = false;
+ bool tmp_value = OptionArgParser::ToBoolean(option, false, &success);
+
+ if (success && tmp_value)
+ real_value = 1;
+ else if (success && !tmp_value)
+ real_value = 0;
+ else {
+ // If the value isn't 'true' or 'false', it had better be 0 or 1.
+ if (!llvm::to_integer(option, real_value))
+ real_value = 3;
+ if (real_value != 0 && real_value != 1)
+ okay = false;
+ }
+
+ return okay;
+ }
+
+ void PrintSignalHeader(Stream &str) {
+ str.Printf("NAME PASS STOP NOTIFY\n");
+ str.Printf("=========== ===== ===== ======\n");
+ }
+
+ void PrintSignal(Stream &str, int32_t signo, const char *sig_name,
+ const UnixSignalsSP &signals_sp) {
+ bool stop;
+ bool suppress;
+ bool notify;
+
+ str.Printf("%-11s ", sig_name);
+ if (signals_sp->GetSignalInfo(signo, suppress, stop, notify)) {
+ bool pass = !suppress;
+ str.Printf("%s %s %s", (pass ? "true " : "false"),
+ (stop ? "true " : "false"), (notify ? "true " : "false"));
+ }
+ str.Printf("\n");
+ }
+
+ void PrintSignalInformation(Stream &str, Args &signal_args,
+ int num_valid_signals,
+ const UnixSignalsSP &signals_sp) {
+ PrintSignalHeader(str);
+
+ if (num_valid_signals > 0) {
+ size_t num_args = signal_args.GetArgumentCount();
+ for (size_t i = 0; i < num_args; ++i) {
+ int32_t signo = signals_sp->GetSignalNumberFromName(
+ signal_args.GetArgumentAtIndex(i));
+ if (signo != LLDB_INVALID_SIGNAL_NUMBER)
+ PrintSignal(str, signo, signal_args.GetArgumentAtIndex(i),
+ signals_sp);
+ }
+ } else // Print info for ALL signals
+ {
+ int32_t signo = signals_sp->GetFirstSignalNumber();
+ while (signo != LLDB_INVALID_SIGNAL_NUMBER) {
+ PrintSignal(str, signo, signals_sp->GetSignalAsCString(signo),
+ signals_sp);
+ signo = signals_sp->GetNextSignalNumber(signo);
+ }
+ }
+ }
+
+protected:
+ bool DoExecute(Args &signal_args, CommandReturnObject &result) override {
+ Target &target = GetSelectedOrDummyTarget();
+
+ // Any signals that are being set should be added to the Target's
+ // DummySignals so they will get applied on rerun, etc.
+ // If we have a process, however, we can do a more accurate job of vetting
+ // the user's options.
+ ProcessSP process_sp = target.GetProcessSP();
+
+ int stop_action = -1; // -1 means leave the current setting alone
+ int pass_action = -1; // -1 means leave the current setting alone
+ int notify_action = -1; // -1 means leave the current setting alone
+
+ if (!m_options.stop.empty() &&
+ !VerifyCommandOptionValue(m_options.stop, stop_action)) {
+ result.AppendError("Invalid argument for command option --stop; must be "
+ "true or false.\n");
+ return false;
+ }
+
+ if (!m_options.notify.empty() &&
+ !VerifyCommandOptionValue(m_options.notify, notify_action)) {
+ result.AppendError("Invalid argument for command option --notify; must "
+ "be true or false.\n");
+ return false;
+ }
+
+ if (!m_options.pass.empty() &&
+ !VerifyCommandOptionValue(m_options.pass, pass_action)) {
+ result.AppendError("Invalid argument for command option --pass; must be "
+ "true or false.\n");
+ return false;
+ }
+
+ bool no_actions = (stop_action == -1 && pass_action == -1
+ && notify_action == -1);
+ if (m_options.only_target_values && !no_actions) {
+ result.AppendError("-t is for reporting, not setting, target values.");
+ return false;
+ }
+
+ size_t num_args = signal_args.GetArgumentCount();
+ UnixSignalsSP signals_sp;
+ if (process_sp)
+ signals_sp = process_sp->GetUnixSignals();
+
+ int num_signals_set = 0;
+
+ // If we were just asked to print the target values, do that here and
+ // return:
+ if (m_options.only_target_values) {
+ target.PrintDummySignals(result.GetOutputStream(), signal_args);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+
+ // This handles clearing values:
+ if (m_options.do_clear) {
+ target.ClearDummySignals(signal_args);
+ if (m_options.dummy)
+ GetDummyTarget().ClearDummySignals(signal_args);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+
+ // This rest handles setting values:
+ if (num_args > 0) {
+ for (const auto &arg : signal_args) {
+ // Do the process first. If we have a process we can catch
+ // invalid signal names, which we do here.
+ if (signals_sp) {
+ int32_t signo = signals_sp->GetSignalNumberFromName(arg.c_str());
+ if (signo != LLDB_INVALID_SIGNAL_NUMBER) {
+ // Casting the actions as bools here should be okay, because
+ // VerifyCommandOptionValue guarantees the value is either 0 or 1.
+ if (stop_action != -1)
+ signals_sp->SetShouldStop(signo, stop_action);
+ if (pass_action != -1) {
+ bool suppress = !pass_action;
+ signals_sp->SetShouldSuppress(signo, suppress);
+ }
+ if (notify_action != -1)
+ signals_sp->SetShouldNotify(signo, notify_action);
+ ++num_signals_set;
+ } else {
+ result.AppendErrorWithFormat("Invalid signal name '%s'\n",
+ arg.c_str());
+ continue;
+ }
+ } else {
+ // If there's no process we can't check, so we just set them all.
+ // But since the map signal name -> signal number across all platforms
+ // is not 1-1, we can't sensibly set signal actions by number before
+ // we have a process. Check that here:
+ int32_t signo;
+ if (llvm::to_integer(arg.c_str(), signo)) {
+ result.AppendErrorWithFormat("Can't set signal handling by signal "
+ "number with no process");
+ return false;
+ }
+ num_signals_set = num_args;
+ }
+ auto set_lazy_bool = [] (int action) -> LazyBool {
+ LazyBool lazy;
+ if (action == -1)
+ lazy = eLazyBoolCalculate;
+ else if (action)
+ lazy = eLazyBoolYes;
+ else
+ lazy = eLazyBoolNo;
+ return lazy;
+ };
+
+ // If there were no actions, we're just listing, don't add the dummy:
+ if (!no_actions)
+ target.AddDummySignal(arg.ref(),
+ set_lazy_bool(pass_action),
+ set_lazy_bool(notify_action),
+ set_lazy_bool(stop_action));
+ }
+ } else {
+ // No signal specified, if any command options were specified, update ALL
+ // signals. But we can't do this without a process since we don't know
+ // all the possible signals that might be valid for this target.
+ if (((notify_action != -1) || (stop_action != -1) || (pass_action != -1))
+ && process_sp) {
+ if (m_interpreter.Confirm(
+ "Do you really want to update all the signals?", false)) {
+ int32_t signo = signals_sp->GetFirstSignalNumber();
+ while (signo != LLDB_INVALID_SIGNAL_NUMBER) {
+ if (notify_action != -1)
+ signals_sp->SetShouldNotify(signo, notify_action);
+ if (stop_action != -1)
+ signals_sp->SetShouldStop(signo, stop_action);
+ if (pass_action != -1) {
+ bool suppress = !pass_action;
+ signals_sp->SetShouldSuppress(signo, suppress);
+ }
+ signo = signals_sp->GetNextSignalNumber(signo);
+ }
+ }
+ }
+ }
+
+ if (signals_sp)
+ PrintSignalInformation(result.GetOutputStream(), signal_args,
+ num_signals_set, signals_sp);
+ else
+ target.PrintDummySignals(result.GetOutputStream(),
+ signal_args);
+
+ if (num_signals_set > 0)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+// Next are the subcommands of CommandObjectMultiwordProcessTrace
+
+// CommandObjectProcessTraceStart
+class CommandObjectProcessTraceStart : public CommandObjectTraceProxy {
+public:
+ CommandObjectProcessTraceStart(CommandInterpreter &interpreter)
+ : CommandObjectTraceProxy(
+ /*live_debug_session_only*/ true, interpreter,
+ "process trace start",
+ "Start tracing this process with the corresponding trace "
+ "plug-in.",
+ "process trace start [<trace-options>]") {}
+
+protected:
+ lldb::CommandObjectSP GetDelegateCommand(Trace &trace) override {
+ return trace.GetProcessTraceStartCommand(m_interpreter);
+ }
+};
+
+// CommandObjectProcessTraceStop
+class CommandObjectProcessTraceStop : public CommandObjectParsed {
+public:
+ CommandObjectProcessTraceStop(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process trace stop",
+ "Stop tracing this process. This does not affect "
+ "traces started with the "
+ "\"thread trace start\" command.",
+ "process trace stop",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused |
+ eCommandProcessMustBeTraced) {}
+
+ ~CommandObjectProcessTraceStop() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ ProcessSP process_sp = m_exe_ctx.GetProcessSP();
+
+ TraceSP trace_sp = process_sp->GetTarget().GetTrace();
+
+ if (llvm::Error err = trace_sp->Stop())
+ result.AppendError(toString(std::move(err)));
+ else
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectMultiwordProcessTrace
+class CommandObjectMultiwordProcessTrace : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordProcessTrace(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "trace", "Commands for tracing the current process.",
+ "process trace <subcommand> [<subcommand objects>]") {
+ LoadSubCommand("start", CommandObjectSP(new CommandObjectProcessTraceStart(
+ interpreter)));
+ LoadSubCommand("stop", CommandObjectSP(
+ new CommandObjectProcessTraceStop(interpreter)));
+ }
+
+ ~CommandObjectMultiwordProcessTrace() override = default;
+};
+
+// CommandObjectMultiwordProcess
+
+CommandObjectMultiwordProcess::CommandObjectMultiwordProcess(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "process",
+ "Commands for interacting with processes on the current platform.",
+ "process <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("attach",
+ CommandObjectSP(new CommandObjectProcessAttach(interpreter)));
+ LoadSubCommand("launch",
+ CommandObjectSP(new CommandObjectProcessLaunch(interpreter)));
+ LoadSubCommand("continue", CommandObjectSP(new CommandObjectProcessContinue(
+ interpreter)));
+ LoadSubCommand("connect",
+ CommandObjectSP(new CommandObjectProcessConnect(interpreter)));
+ LoadSubCommand("detach",
+ CommandObjectSP(new CommandObjectProcessDetach(interpreter)));
+ LoadSubCommand("load",
+ CommandObjectSP(new CommandObjectProcessLoad(interpreter)));
+ LoadSubCommand("unload",
+ CommandObjectSP(new CommandObjectProcessUnload(interpreter)));
+ LoadSubCommand("signal",
+ CommandObjectSP(new CommandObjectProcessSignal(interpreter)));
+ LoadSubCommand("handle",
+ CommandObjectSP(new CommandObjectProcessHandle(interpreter)));
+ LoadSubCommand("status",
+ CommandObjectSP(new CommandObjectProcessStatus(interpreter)));
+ LoadSubCommand("interrupt", CommandObjectSP(new CommandObjectProcessInterrupt(
+ interpreter)));
+ LoadSubCommand("kill",
+ CommandObjectSP(new CommandObjectProcessKill(interpreter)));
+ LoadSubCommand("plugin",
+ CommandObjectSP(new CommandObjectProcessPlugin(interpreter)));
+ LoadSubCommand("save-core", CommandObjectSP(new CommandObjectProcessSaveCore(
+ interpreter)));
+ LoadSubCommand(
+ "trace",
+ CommandObjectSP(new CommandObjectMultiwordProcessTrace(interpreter)));
+}
+
+CommandObjectMultiwordProcess::~CommandObjectMultiwordProcess() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.h
new file mode 100644
index 000000000000..55d445142e72
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.h
@@ -0,0 +1,27 @@
+//===-- CommandObjectProcess.h ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTPROCESS_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTPROCESS_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+// CommandObjectMultiwordProcess
+
+class CommandObjectMultiwordProcess : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordProcess(CommandInterpreter &interpreter);
+
+ ~CommandObjectMultiwordProcess() override;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTPROCESS_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectQuit.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectQuit.cpp
new file mode 100644
index 000000000000..650cfca2c050
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectQuit.cpp
@@ -0,0 +1,108 @@
+//===-- CommandObjectQuit.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectQuit.h"
+
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// CommandObjectQuit
+
+CommandObjectQuit::CommandObjectQuit(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "quit", "Quit the LLDB debugger.",
+ "quit [exit-code]") {
+ CommandArgumentData exit_code_arg{eArgTypeUnsignedInteger, eArgRepeatPlain};
+ m_arguments.push_back({exit_code_arg});
+}
+
+CommandObjectQuit::~CommandObjectQuit() = default;
+
+// returns true if there is at least one alive process is_a_detach will be true
+// if all alive processes will be detached when you quit and false if at least
+// one process will be killed instead
+bool CommandObjectQuit::ShouldAskForConfirmation(bool &is_a_detach) {
+ if (!m_interpreter.GetPromptOnQuit())
+ return false;
+ bool should_prompt = false;
+ is_a_detach = true;
+ for (uint32_t debugger_idx = 0; debugger_idx < Debugger::GetNumDebuggers();
+ debugger_idx++) {
+ DebuggerSP debugger_sp(Debugger::GetDebuggerAtIndex(debugger_idx));
+ if (!debugger_sp)
+ continue;
+ const TargetList &target_list(debugger_sp->GetTargetList());
+ for (uint32_t target_idx = 0;
+ target_idx < static_cast<uint32_t>(target_list.GetNumTargets());
+ target_idx++) {
+ TargetSP target_sp(target_list.GetTargetAtIndex(target_idx));
+ if (!target_sp)
+ continue;
+ ProcessSP process_sp(target_sp->GetProcessSP());
+ if (process_sp && process_sp->IsValid() && process_sp->IsAlive() &&
+ process_sp->WarnBeforeDetach()) {
+ should_prompt = true;
+ if (!process_sp->GetShouldDetach()) {
+ // if we need to kill at least one process, just say so and return
+ is_a_detach = false;
+ return should_prompt;
+ }
+ }
+ }
+ }
+ return should_prompt;
+}
+
+bool CommandObjectQuit::DoExecute(Args &command, CommandReturnObject &result) {
+ bool is_a_detach = true;
+ if (ShouldAskForConfirmation(is_a_detach)) {
+ StreamString message;
+ message.Printf("Quitting LLDB will %s one or more processes. Do you really "
+ "want to proceed",
+ (is_a_detach ? "detach from" : "kill"));
+ if (!m_interpreter.Confirm(message.GetString(), true)) {
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ if (command.GetArgumentCount() > 1) {
+ result.AppendError("Too many arguments for 'quit'. Only an optional exit "
+ "code is allowed");
+ return false;
+ }
+
+ // We parse the exit code argument if there is one.
+ if (command.GetArgumentCount() == 1) {
+ llvm::StringRef arg = command.GetArgumentAtIndex(0);
+ int exit_code;
+ if (arg.getAsInteger(/*autodetect radix*/ 0, exit_code)) {
+ lldb_private::StreamString s;
+ std::string arg_str = arg.str();
+ s.Printf("Couldn't parse '%s' as integer for exit code.", arg_str.data());
+ result.AppendError(s.GetString());
+ return false;
+ }
+ if (!m_interpreter.SetQuitExitCode(exit_code)) {
+ result.AppendError("The current driver doesn't allow custom exit codes"
+ " for the quit command.");
+ return false;
+ }
+ }
+
+ const uint32_t event_type =
+ CommandInterpreter::eBroadcastBitQuitCommandReceived;
+ m_interpreter.BroadcastEvent(event_type);
+ result.SetStatus(eReturnStatusQuit);
+
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectQuit.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectQuit.h
new file mode 100644
index 000000000000..ccbd863cd6f5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectQuit.h
@@ -0,0 +1,32 @@
+//===-- CommandObjectQuit.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTQUIT_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTQUIT_H
+
+#include "lldb/Interpreter/CommandObject.h"
+
+namespace lldb_private {
+
+// CommandObjectQuit
+
+class CommandObjectQuit : public CommandObjectParsed {
+public:
+ CommandObjectQuit(CommandInterpreter &interpreter);
+
+ ~CommandObjectQuit() override;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override;
+
+ bool ShouldAskForConfirmation(bool &is_a_detach);
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTQUIT_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectRegexCommand.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectRegexCommand.cpp
new file mode 100644
index 000000000000..857193036e39
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectRegexCommand.cpp
@@ -0,0 +1,110 @@
+//===-- CommandObjectRegexCommand.cpp -------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectRegexCommand.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// CommandObjectRegexCommand constructor
+CommandObjectRegexCommand::CommandObjectRegexCommand(
+ CommandInterpreter &interpreter, llvm::StringRef name, llvm::StringRef help,
+ llvm::StringRef syntax, uint32_t max_matches, uint32_t completion_type_mask,
+ bool is_removable)
+ : CommandObjectRaw(interpreter, name, help, syntax),
+ m_max_matches(max_matches), m_completion_type_mask(completion_type_mask),
+ m_is_removable(is_removable) {}
+
+// Destructor
+CommandObjectRegexCommand::~CommandObjectRegexCommand() = default;
+
+llvm::Expected<std::string> CommandObjectRegexCommand::SubstituteVariables(
+ llvm::StringRef input,
+ const llvm::SmallVectorImpl<llvm::StringRef> &replacements) {
+ std::string buffer;
+ llvm::raw_string_ostream output(buffer);
+
+ llvm::SmallVector<llvm::StringRef, 4> parts;
+ input.split(parts, '%');
+
+ output << parts[0];
+ for (llvm::StringRef part : drop_begin(parts)) {
+ size_t idx = 0;
+ if (part.consumeInteger(10, idx))
+ output << '%';
+ else if (idx < replacements.size())
+ output << replacements[idx];
+ else
+ return llvm::make_error<llvm::StringError>(
+ llvm::formatv("%{0} is out of range: not enough arguments specified",
+ idx),
+ llvm::errc::invalid_argument);
+ output << part;
+ }
+
+ return output.str();
+}
+
+bool CommandObjectRegexCommand::DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) {
+ EntryCollection::const_iterator pos, end = m_entries.end();
+ for (pos = m_entries.begin(); pos != end; ++pos) {
+ llvm::SmallVector<llvm::StringRef, 4> matches;
+ if (pos->regex.Execute(command, &matches)) {
+ llvm::Expected<std::string> new_command =
+ SubstituteVariables(pos->command, matches);
+ if (!new_command) {
+ result.SetError(new_command.takeError());
+ return false;
+ }
+
+ // Interpret the new command and return this as the result!
+ if (m_interpreter.GetExpandRegexAliases())
+ result.GetOutputStream().Printf("%s\n", new_command->c_str());
+ // We don't have to pass an override_context here, as the command that
+ // called us should have set up the context appropriately.
+ return m_interpreter.HandleCommand(new_command->c_str(),
+ eLazyBoolNo, result);
+ }
+ }
+ result.SetStatus(eReturnStatusFailed);
+ if (!GetSyntax().empty())
+ result.AppendError(GetSyntax());
+ else
+ result.GetErrorStream() << "Command contents '" << command
+ << "' failed to match any "
+ "regular expression in the '"
+ << m_cmd_name << "' regex ";
+ return false;
+}
+
+bool CommandObjectRegexCommand::AddRegexCommand(llvm::StringRef re_cstr,
+ llvm::StringRef command_cstr) {
+ m_entries.resize(m_entries.size() + 1);
+ // Only add the regular expression if it compiles
+ m_entries.back().regex = RegularExpression(re_cstr);
+ if (m_entries.back().regex.IsValid()) {
+ m_entries.back().command = command_cstr.str();
+ return true;
+ }
+ // The regex didn't compile...
+ m_entries.pop_back();
+ return false;
+}
+
+void CommandObjectRegexCommand::HandleCompletion(CompletionRequest &request) {
+ if (m_completion_type_mask) {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), m_completion_type_mask, request, nullptr);
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectRegexCommand.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectRegexCommand.h
new file mode 100644
index 000000000000..b2a375c63a76
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectRegexCommand.h
@@ -0,0 +1,66 @@
+//===-- CommandObjectRegexCommand.h -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_INTERPRETER_COMMANDOBJECTREGEXCOMMAND_H
+#define LLDB_INTERPRETER_COMMANDOBJECTREGEXCOMMAND_H
+
+#include <list>
+
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Utility/CompletionRequest.h"
+#include "lldb/Utility/RegularExpression.h"
+
+namespace lldb_private {
+
+// CommandObjectRegexCommand
+
+class CommandObjectRegexCommand : public CommandObjectRaw {
+public:
+ CommandObjectRegexCommand(CommandInterpreter &interpreter,
+ llvm::StringRef name, llvm::StringRef help,
+ llvm::StringRef syntax, uint32_t max_matches,
+ uint32_t completion_type_mask, bool is_removable);
+
+ ~CommandObjectRegexCommand() override;
+
+ bool IsRemovable() const override { return m_is_removable; }
+
+ bool AddRegexCommand(llvm::StringRef re_cstr, llvm::StringRef command_cstr);
+
+ bool HasRegexEntries() const { return !m_entries.empty(); }
+
+ void HandleCompletion(CompletionRequest &request) override;
+
+protected:
+ bool DoExecute(llvm::StringRef command, CommandReturnObject &result) override;
+
+ /// Substitute variables of the format %\d+ in the input string.
+ static llvm::Expected<std::string> SubstituteVariables(
+ llvm::StringRef input,
+ const llvm::SmallVectorImpl<llvm::StringRef> &replacements);
+
+ struct Entry {
+ RegularExpression regex;
+ std::string command;
+ };
+
+ typedef std::list<Entry> EntryCollection;
+ const uint32_t m_max_matches;
+ const uint32_t m_completion_type_mask;
+ EntryCollection m_entries;
+ bool m_is_removable;
+
+private:
+ CommandObjectRegexCommand(const CommandObjectRegexCommand &) = delete;
+ const CommandObjectRegexCommand &
+ operator=(const CommandObjectRegexCommand &) = delete;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_INTERPRETER_COMMANDOBJECTREGEXCOMMAND_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectRegister.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectRegister.cpp
new file mode 100644
index 000000000000..933c243dedd5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectRegister.cpp
@@ -0,0 +1,407 @@
+//===-- CommandObjectRegister.cpp -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectRegister.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/DumpRegisterValue.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionGroupFormat.h"
+#include "lldb/Interpreter/OptionValueArray.h"
+#include "lldb/Interpreter/OptionValueBoolean.h"
+#include "lldb/Interpreter/OptionValueUInt64.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "llvm/Support/Errno.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// "register read"
+#define LLDB_OPTIONS_register_read
+#include "CommandOptions.inc"
+
+class CommandObjectRegisterRead : public CommandObjectParsed {
+public:
+ CommandObjectRegisterRead(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "register read",
+ "Dump the contents of one or more register values from the current "
+ "frame. If no register is specified, dumps them all.",
+ nullptr,
+ eCommandRequiresFrame | eCommandRequiresRegContext |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused),
+ m_format_options(eFormatDefault) {
+ CommandArgumentEntry arg;
+ CommandArgumentData register_arg;
+
+ // Define the first (and only) variant of this arg.
+ register_arg.arg_type = eArgTypeRegisterName;
+ register_arg.arg_repetition = eArgRepeatStar;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(register_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+
+ // Add the "--format"
+ m_option_group.Append(&m_format_options,
+ OptionGroupFormat::OPTION_GROUP_FORMAT |
+ OptionGroupFormat::OPTION_GROUP_GDB_FMT,
+ LLDB_OPT_SET_ALL);
+ m_option_group.Append(&m_command_options);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectRegisterRead() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (!m_exe_ctx.HasProcessScope())
+ return;
+
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eRegisterCompletion,
+ request, nullptr);
+ }
+
+ Options *GetOptions() override { return &m_option_group; }
+
+ bool DumpRegister(const ExecutionContext &exe_ctx, Stream &strm,
+ RegisterContext *reg_ctx, const RegisterInfo *reg_info) {
+ if (reg_info) {
+ RegisterValue reg_value;
+
+ if (reg_ctx->ReadRegister(reg_info, reg_value)) {
+ strm.Indent();
+
+ bool prefix_with_altname = (bool)m_command_options.alternate_name;
+ bool prefix_with_name = !prefix_with_altname;
+ DumpRegisterValue(reg_value, &strm, reg_info, prefix_with_name,
+ prefix_with_altname, m_format_options.GetFormat(), 8);
+ if ((reg_info->encoding == eEncodingUint) ||
+ (reg_info->encoding == eEncodingSint)) {
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process && reg_info->byte_size == process->GetAddressByteSize()) {
+ addr_t reg_addr = reg_value.GetAsUInt64(LLDB_INVALID_ADDRESS);
+ if (reg_addr != LLDB_INVALID_ADDRESS) {
+ Address so_reg_addr;
+ if (exe_ctx.GetTargetRef()
+ .GetSectionLoadList()
+ .ResolveLoadAddress(reg_addr, so_reg_addr)) {
+ strm.PutCString(" ");
+ so_reg_addr.Dump(&strm, exe_ctx.GetBestExecutionContextScope(),
+ Address::DumpStyleResolvedDescription);
+ }
+ }
+ }
+ }
+ strm.EOL();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool DumpRegisterSet(const ExecutionContext &exe_ctx, Stream &strm,
+ RegisterContext *reg_ctx, size_t set_idx,
+ bool primitive_only = false) {
+ uint32_t unavailable_count = 0;
+ uint32_t available_count = 0;
+
+ if (!reg_ctx)
+ return false; // thread has no registers (i.e. core files are corrupt,
+ // incomplete crash logs...)
+
+ const RegisterSet *const reg_set = reg_ctx->GetRegisterSet(set_idx);
+ if (reg_set) {
+ strm.Printf("%s:\n", (reg_set->name ? reg_set->name : "unknown"));
+ strm.IndentMore();
+ const size_t num_registers = reg_set->num_registers;
+ for (size_t reg_idx = 0; reg_idx < num_registers; ++reg_idx) {
+ const uint32_t reg = reg_set->registers[reg_idx];
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg);
+ // Skip the dumping of derived register if primitive_only is true.
+ if (primitive_only && reg_info && reg_info->value_regs)
+ continue;
+
+ if (DumpRegister(exe_ctx, strm, reg_ctx, reg_info))
+ ++available_count;
+ else
+ ++unavailable_count;
+ }
+ strm.IndentLess();
+ if (unavailable_count) {
+ strm.Indent();
+ strm.Printf("%u registers were unavailable.\n", unavailable_count);
+ }
+ strm.EOL();
+ }
+ return available_count > 0;
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Stream &strm = result.GetOutputStream();
+ RegisterContext *reg_ctx = m_exe_ctx.GetRegisterContext();
+
+ const RegisterInfo *reg_info = nullptr;
+ if (command.GetArgumentCount() == 0) {
+ size_t set_idx;
+
+ size_t num_register_sets = 1;
+ const size_t set_array_size = m_command_options.set_indexes.GetSize();
+ if (set_array_size > 0) {
+ for (size_t i = 0; i < set_array_size; ++i) {
+ set_idx = m_command_options.set_indexes[i]->GetUInt64Value(UINT32_MAX,
+ nullptr);
+ if (set_idx < reg_ctx->GetRegisterSetCount()) {
+ if (!DumpRegisterSet(m_exe_ctx, strm, reg_ctx, set_idx)) {
+ if (errno)
+ result.AppendErrorWithFormatv("register read failed: {0}\n",
+ llvm::sys::StrError());
+ else
+ result.AppendError("unknown error while reading registers.\n");
+ break;
+ }
+ } else {
+ result.AppendErrorWithFormat(
+ "invalid register set index: %" PRIu64 "\n", (uint64_t)set_idx);
+ break;
+ }
+ }
+ } else {
+ if (m_command_options.dump_all_sets)
+ num_register_sets = reg_ctx->GetRegisterSetCount();
+
+ for (set_idx = 0; set_idx < num_register_sets; ++set_idx) {
+ // When dump_all_sets option is set, dump primitive as well as
+ // derived registers.
+ DumpRegisterSet(m_exe_ctx, strm, reg_ctx, set_idx,
+ !m_command_options.dump_all_sets.GetCurrentValue());
+ }
+ }
+ } else {
+ if (m_command_options.dump_all_sets) {
+ result.AppendError("the --all option can't be used when registers "
+ "names are supplied as arguments\n");
+ } else if (m_command_options.set_indexes.GetSize() > 0) {
+ result.AppendError("the --set <set> option can't be used when "
+ "registers names are supplied as arguments\n");
+ } else {
+ for (auto &entry : command) {
+ // in most LLDB commands we accept $rbx as the name for register RBX
+ // - and here we would reject it and non-existant. we should be more
+ // consistent towards the user and allow them to say reg read $rbx -
+ // internally, however, we should be strict and not allow ourselves
+ // to call our registers $rbx in our own API
+ auto arg_str = entry.ref();
+ arg_str.consume_front("$");
+
+ reg_info = reg_ctx->GetRegisterInfoByName(arg_str);
+
+ if (reg_info) {
+ if (!DumpRegister(m_exe_ctx, strm, reg_ctx, reg_info))
+ strm.Printf("%-12s = error: unavailable\n", reg_info->name);
+ } else {
+ result.AppendErrorWithFormat("Invalid register name '%s'.\n",
+ arg_str.str().c_str());
+ }
+ }
+ }
+ }
+ return result.Succeeded();
+ }
+
+ class CommandOptions : public OptionGroup {
+ public:
+ CommandOptions()
+ : set_indexes(OptionValue::ConvertTypeToMask(OptionValue::eTypeUInt64)),
+ dump_all_sets(false, false), // Initial and default values are false
+ alternate_name(false, false) {}
+
+ ~CommandOptions() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_register_read_options);
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ set_indexes.Clear();
+ dump_all_sets.Clear();
+ alternate_name.Clear();
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = GetDefinitions()[option_idx].short_option;
+ switch (short_option) {
+ case 's': {
+ OptionValueSP value_sp(OptionValueUInt64::Create(option_value, error));
+ if (value_sp)
+ set_indexes.AppendValue(value_sp);
+ } break;
+
+ case 'a':
+ // When we don't use OptionValue::SetValueFromCString(const char *) to
+ // set an option value, it won't be marked as being set in the options
+ // so we make a call to let users know the value was set via option
+ dump_all_sets.SetCurrentValue(true);
+ dump_all_sets.SetOptionWasSet();
+ break;
+
+ case 'A':
+ // When we don't use OptionValue::SetValueFromCString(const char *) to
+ // set an option value, it won't be marked as being set in the options
+ // so we make a call to let users know the value was set via option
+ alternate_name.SetCurrentValue(true);
+ dump_all_sets.SetOptionWasSet();
+ break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ // Instance variables to hold the values for command options.
+ OptionValueArray set_indexes;
+ OptionValueBoolean dump_all_sets;
+ OptionValueBoolean alternate_name;
+ };
+
+ OptionGroupOptions m_option_group;
+ OptionGroupFormat m_format_options;
+ CommandOptions m_command_options;
+};
+
+// "register write"
+class CommandObjectRegisterWrite : public CommandObjectParsed {
+public:
+ CommandObjectRegisterWrite(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "register write",
+ "Modify a single register value.", nullptr,
+ eCommandRequiresFrame | eCommandRequiresRegContext |
+ eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused) {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentData register_arg;
+ CommandArgumentData value_arg;
+
+ // Define the first (and only) variant of this arg.
+ register_arg.arg_type = eArgTypeRegisterName;
+ register_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(register_arg);
+
+ // Define the first (and only) variant of this arg.
+ value_arg.arg_type = eArgTypeValue;
+ value_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg2.push_back(value_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+ }
+
+ ~CommandObjectRegisterWrite() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (!m_exe_ctx.HasProcessScope() || request.GetCursorIndex() != 0)
+ return;
+
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eRegisterCompletion,
+ request, nullptr);
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ DataExtractor reg_data;
+ RegisterContext *reg_ctx = m_exe_ctx.GetRegisterContext();
+
+ if (command.GetArgumentCount() != 2) {
+ result.AppendError(
+ "register write takes exactly 2 arguments: <reg-name> <value>");
+ } else {
+ auto reg_name = command[0].ref();
+ auto value_str = command[1].ref();
+
+ // in most LLDB commands we accept $rbx as the name for register RBX -
+ // and here we would reject it and non-existant. we should be more
+ // consistent towards the user and allow them to say reg write $rbx -
+ // internally, however, we should be strict and not allow ourselves to
+ // call our registers $rbx in our own API
+ reg_name.consume_front("$");
+
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
+
+ if (reg_info) {
+ RegisterValue reg_value;
+
+ Status error(reg_value.SetValueFromString(reg_info, value_str));
+ if (error.Success()) {
+ if (reg_ctx->WriteRegister(reg_info, reg_value)) {
+ // Toss all frames and anything else in the thread after a register
+ // has been written.
+ m_exe_ctx.GetThreadRef().Flush();
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+ }
+ if (error.AsCString()) {
+ result.AppendErrorWithFormat(
+ "Failed to write register '%s' with value '%s': %s\n",
+ reg_name.str().c_str(), value_str.str().c_str(),
+ error.AsCString());
+ } else {
+ result.AppendErrorWithFormat(
+ "Failed to write register '%s' with value '%s'",
+ reg_name.str().c_str(), value_str.str().c_str());
+ }
+ } else {
+ result.AppendErrorWithFormat("Register not found for '%s'.\n",
+ reg_name.str().c_str());
+ }
+ }
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectRegister constructor
+CommandObjectRegister::CommandObjectRegister(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "register",
+ "Commands to access registers for the current "
+ "thread and stack frame.",
+ "register [read|write] ...") {
+ LoadSubCommand("read",
+ CommandObjectSP(new CommandObjectRegisterRead(interpreter)));
+ LoadSubCommand("write",
+ CommandObjectSP(new CommandObjectRegisterWrite(interpreter)));
+}
+
+CommandObjectRegister::~CommandObjectRegister() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectRegister.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectRegister.h
new file mode 100644
index 000000000000..671ffc570147
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectRegister.h
@@ -0,0 +1,34 @@
+//===-- CommandObjectRegister.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTREGISTER_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTREGISTER_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+// CommandObjectRegister
+
+class CommandObjectRegister : public CommandObjectMultiword {
+public:
+ // Constructors and Destructors
+ CommandObjectRegister(CommandInterpreter &interpreter);
+
+ ~CommandObjectRegister() override;
+
+private:
+ // For CommandObjectRegister only
+ CommandObjectRegister(const CommandObjectRegister &) = delete;
+ const CommandObjectRegister &
+ operator=(const CommandObjectRegister &) = delete;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTREGISTER_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectReproducer.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectReproducer.cpp
new file mode 100644
index 000000000000..8d12decba974
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectReproducer.cpp
@@ -0,0 +1,588 @@
+//===-- CommandObjectReproducer.cpp ---------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectReproducer.h"
+
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Utility/GDBRemote.h"
+#include "lldb/Utility/ProcessInfo.h"
+#include "lldb/Utility/Reproducer.h"
+
+#include <csignal>
+
+using namespace lldb;
+using namespace llvm;
+using namespace lldb_private;
+using namespace lldb_private::repro;
+
+enum ReproducerProvider {
+ eReproducerProviderCommands,
+ eReproducerProviderFiles,
+ eReproducerProviderSymbolFiles,
+ eReproducerProviderGDB,
+ eReproducerProviderProcessInfo,
+ eReproducerProviderVersion,
+ eReproducerProviderWorkingDirectory,
+ eReproducerProviderHomeDirectory,
+ eReproducerProviderNone
+};
+
+static constexpr OptionEnumValueElement g_reproducer_provider_type[] = {
+ {
+ eReproducerProviderCommands,
+ "commands",
+ "Command Interpreter Commands",
+ },
+ {
+ eReproducerProviderFiles,
+ "files",
+ "Files",
+ },
+ {
+ eReproducerProviderSymbolFiles,
+ "symbol-files",
+ "Symbol Files",
+ },
+ {
+ eReproducerProviderGDB,
+ "gdb",
+ "GDB Remote Packets",
+ },
+ {
+ eReproducerProviderProcessInfo,
+ "processes",
+ "Process Info",
+ },
+ {
+ eReproducerProviderVersion,
+ "version",
+ "Version",
+ },
+ {
+ eReproducerProviderWorkingDirectory,
+ "cwd",
+ "Working Directory",
+ },
+ {
+ eReproducerProviderHomeDirectory,
+ "home",
+ "Home Directory",
+ },
+ {
+ eReproducerProviderNone,
+ "none",
+ "None",
+ },
+};
+
+static constexpr OptionEnumValues ReproducerProviderType() {
+ return OptionEnumValues(g_reproducer_provider_type);
+}
+
+#define LLDB_OPTIONS_reproducer_dump
+#include "CommandOptions.inc"
+
+enum ReproducerCrashSignal {
+ eReproducerCrashSigill,
+ eReproducerCrashSigsegv,
+};
+
+static constexpr OptionEnumValueElement g_reproducer_signaltype[] = {
+ {
+ eReproducerCrashSigill,
+ "SIGILL",
+ "Illegal instruction",
+ },
+ {
+ eReproducerCrashSigsegv,
+ "SIGSEGV",
+ "Segmentation fault",
+ },
+};
+
+static constexpr OptionEnumValues ReproducerSignalType() {
+ return OptionEnumValues(g_reproducer_signaltype);
+}
+
+#define LLDB_OPTIONS_reproducer_xcrash
+#include "CommandOptions.inc"
+
+#define LLDB_OPTIONS_reproducer_verify
+#include "CommandOptions.inc"
+
+template <typename T>
+llvm::Expected<T> static ReadFromYAML(StringRef filename) {
+ auto error_or_file = MemoryBuffer::getFile(filename);
+ if (auto err = error_or_file.getError()) {
+ return errorCodeToError(err);
+ }
+
+ T t;
+ yaml::Input yin((*error_or_file)->getBuffer());
+ yin >> t;
+
+ if (auto err = yin.error()) {
+ return errorCodeToError(err);
+ }
+
+ return t;
+}
+
+static void SetError(CommandReturnObject &result, Error err) {
+ result.AppendError(toString(std::move(err)));
+}
+
+/// Create a loader from the given path if specified. Otherwise use the current
+/// loader used for replay.
+static Loader *
+GetLoaderFromPathOrCurrent(llvm::Optional<Loader> &loader_storage,
+ CommandReturnObject &result,
+ FileSpec reproducer_path) {
+ if (reproducer_path) {
+ loader_storage.emplace(reproducer_path);
+ Loader *loader = &(*loader_storage);
+ if (Error err = loader->LoadIndex()) {
+ // This is a hard error and will set the result to eReturnStatusFailed.
+ SetError(result, std::move(err));
+ return nullptr;
+ }
+ return loader;
+ }
+
+ if (Loader *loader = Reproducer::Instance().GetLoader())
+ return loader;
+
+ // This is a soft error because this is expected to fail during capture.
+ result.AppendError(
+ "Not specifying a reproducer is only support during replay.");
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return nullptr;
+}
+
+class CommandObjectReproducerGenerate : public CommandObjectParsed {
+public:
+ CommandObjectReproducerGenerate(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "reproducer generate",
+ "Generate reproducer on disk. When the debugger is in capture "
+ "mode, this command will output the reproducer to a directory on "
+ "disk and quit. In replay mode this command in a no-op.",
+ nullptr) {}
+
+ ~CommandObjectReproducerGenerate() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ auto &r = Reproducer::Instance();
+ if (auto generator = r.GetGenerator()) {
+ generator->Keep();
+ } else {
+ result.AppendErrorWithFormat("Unable to get the reproducer generator");
+ return false;
+ }
+
+ result.GetOutputStream()
+ << "Reproducer written to '" << r.GetReproducerPath() << "'\n";
+ result.GetOutputStream()
+ << "Please have a look at the directory to assess if you're willing to "
+ "share the contained information.\n";
+
+ m_interpreter.BroadcastEvent(
+ CommandInterpreter::eBroadcastBitQuitCommandReceived);
+ result.SetStatus(eReturnStatusQuit);
+ return result.Succeeded();
+ }
+};
+
+class CommandObjectReproducerXCrash : public CommandObjectParsed {
+public:
+ CommandObjectReproducerXCrash(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "reproducer xcrash",
+ "Intentionally force the debugger to crash in "
+ "order to trigger and test reproducer generation.",
+ nullptr) {}
+
+ ~CommandObjectReproducerXCrash() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 's':
+ signal = (ReproducerCrashSignal)OptionArgParser::ToOptionEnum(
+ option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
+ if (!error.Success())
+ error.SetErrorStringWithFormat("unrecognized value for signal '%s'",
+ option_arg.str().c_str());
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ signal = eReproducerCrashSigsegv;
+ }
+
+ ArrayRef<OptionDefinition> GetDefinitions() override {
+ return makeArrayRef(g_reproducer_xcrash_options);
+ }
+
+ ReproducerCrashSignal signal = eReproducerCrashSigsegv;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ auto &r = Reproducer::Instance();
+
+ if (!r.IsCapturing()) {
+ result.AppendError(
+ "forcing a crash is only supported when capturing a reproducer.");
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return false;
+ }
+
+ switch (m_options.signal) {
+ case eReproducerCrashSigill:
+ std::raise(SIGILL);
+ break;
+ case eReproducerCrashSigsegv:
+ std::raise(SIGSEGV);
+ break;
+ }
+
+ result.SetStatus(eReturnStatusQuit);
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+class CommandObjectReproducerStatus : public CommandObjectParsed {
+public:
+ CommandObjectReproducerStatus(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "reproducer status",
+ "Show the current reproducer status. In capture mode the "
+ "debugger "
+ "is collecting all the information it needs to create a "
+ "reproducer. In replay mode the reproducer is replaying a "
+ "reproducer. When the reproducers are off, no data is collected "
+ "and no reproducer can be generated.",
+ nullptr) {}
+
+ ~CommandObjectReproducerStatus() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ auto &r = Reproducer::Instance();
+ if (r.IsCapturing()) {
+ result.GetOutputStream() << "Reproducer is in capture mode.\n";
+ result.GetOutputStream()
+ << "Path: " << r.GetReproducerPath().GetPath() << '\n';
+ } else {
+ result.GetOutputStream() << "Reproducer is off.\n";
+ }
+
+ // Auto generate is hidden unless enabled because this is mostly for
+ // development and testing.
+ if (Generator *g = r.GetGenerator()) {
+ if (g->IsAutoGenerate())
+ result.GetOutputStream() << "Auto generate: on\n";
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+class CommandObjectReproducerDump : public CommandObjectParsed {
+public:
+ CommandObjectReproducerDump(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "reproducer dump",
+ "Dump the information contained in a reproducer. "
+ "If no reproducer is specified during replay, it "
+ "dumps the content of the current reproducer.",
+ nullptr) {}
+
+ ~CommandObjectReproducerDump() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'f':
+ file.SetFile(option_arg, FileSpec::Style::native);
+ FileSystem::Instance().Resolve(file);
+ break;
+ case 'p':
+ provider = (ReproducerProvider)OptionArgParser::ToOptionEnum(
+ option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
+ if (!error.Success())
+ error.SetErrorStringWithFormat("unrecognized value for provider '%s'",
+ option_arg.str().c_str());
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ file.Clear();
+ provider = eReproducerProviderNone;
+ }
+
+ ArrayRef<OptionDefinition> GetDefinitions() override {
+ return makeArrayRef(g_reproducer_dump_options);
+ }
+
+ FileSpec file;
+ ReproducerProvider provider = eReproducerProviderNone;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ llvm::Optional<Loader> loader_storage;
+ Loader *loader =
+ GetLoaderFromPathOrCurrent(loader_storage, result, m_options.file);
+ if (!loader)
+ return false;
+
+ switch (m_options.provider) {
+ case eReproducerProviderFiles: {
+ FileSpec vfs_mapping = loader->GetFile<FileProvider::Info>();
+
+ // Read the VFS mapping.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> buffer =
+ vfs::getRealFileSystem()->getBufferForFile(vfs_mapping.GetPath());
+ if (!buffer) {
+ SetError(result, errorCodeToError(buffer.getError()));
+ return false;
+ }
+
+ // Initialize a VFS from the given mapping.
+ IntrusiveRefCntPtr<vfs::FileSystem> vfs = vfs::getVFSFromYAML(
+ std::move(buffer.get()), nullptr, vfs_mapping.GetPath());
+
+ // Dump the VFS to a buffer.
+ std::string str;
+ raw_string_ostream os(str);
+ static_cast<vfs::RedirectingFileSystem &>(*vfs).print(os);
+ os.flush();
+
+ // Return the string.
+ result.AppendMessage(str);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ case eReproducerProviderSymbolFiles: {
+ Expected<std::string> symbol_files =
+ loader->LoadBuffer<SymbolFileProvider>();
+ if (!symbol_files) {
+ SetError(result, symbol_files.takeError());
+ return false;
+ }
+
+ std::vector<SymbolFileProvider::Entry> entries;
+ llvm::yaml::Input yin(*symbol_files);
+ yin >> entries;
+
+ for (const auto &entry : entries) {
+ result.AppendMessageWithFormat("- uuid: %s\n",
+ entry.uuid.c_str());
+ result.AppendMessageWithFormat(" module path: %s\n",
+ entry.module_path.c_str());
+ result.AppendMessageWithFormat(" symbol path: %s\n",
+ entry.symbol_path.c_str());
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ case eReproducerProviderVersion: {
+ Expected<std::string> version = loader->LoadBuffer<VersionProvider>();
+ if (!version) {
+ SetError(result, version.takeError());
+ return false;
+ }
+ result.AppendMessage(*version);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ case eReproducerProviderWorkingDirectory: {
+ Expected<std::string> cwd =
+ repro::GetDirectoryFrom<WorkingDirectoryProvider>(loader);
+ if (!cwd) {
+ SetError(result, cwd.takeError());
+ return false;
+ }
+ result.AppendMessage(*cwd);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ case eReproducerProviderHomeDirectory: {
+ Expected<std::string> home =
+ repro::GetDirectoryFrom<HomeDirectoryProvider>(loader);
+ if (!home) {
+ SetError(result, home.takeError());
+ return false;
+ }
+ result.AppendMessage(*home);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ case eReproducerProviderCommands: {
+ std::unique_ptr<repro::MultiLoader<repro::CommandProvider>> multi_loader =
+ repro::MultiLoader<repro::CommandProvider>::Create(loader);
+ if (!multi_loader) {
+ SetError(result,
+ make_error<StringError>("Unable to create command loader.",
+ llvm::inconvertibleErrorCode()));
+ return false;
+ }
+
+ // Iterate over the command files and dump them.
+ llvm::Optional<std::string> command_file;
+ while ((command_file = multi_loader->GetNextFile())) {
+ if (!command_file)
+ break;
+
+ auto command_buffer = llvm::MemoryBuffer::getFile(*command_file);
+ if (auto err = command_buffer.getError()) {
+ SetError(result, errorCodeToError(err));
+ return false;
+ }
+ result.AppendMessage((*command_buffer)->getBuffer());
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ case eReproducerProviderGDB: {
+ std::unique_ptr<repro::MultiLoader<repro::GDBRemoteProvider>>
+ multi_loader =
+ repro::MultiLoader<repro::GDBRemoteProvider>::Create(loader);
+
+ if (!multi_loader) {
+ SetError(result,
+ make_error<StringError>("Unable to create GDB loader.",
+ llvm::inconvertibleErrorCode()));
+ return false;
+ }
+
+ llvm::Optional<std::string> gdb_file;
+ while ((gdb_file = multi_loader->GetNextFile())) {
+ if (llvm::Expected<std::vector<GDBRemotePacket>> packets =
+ ReadFromYAML<std::vector<GDBRemotePacket>>(*gdb_file)) {
+ for (GDBRemotePacket &packet : *packets) {
+ packet.Dump(result.GetOutputStream());
+ }
+ } else {
+ SetError(result, packets.takeError());
+ return false;
+ }
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ case eReproducerProviderProcessInfo: {
+ std::unique_ptr<repro::MultiLoader<repro::ProcessInfoProvider>>
+ multi_loader =
+ repro::MultiLoader<repro::ProcessInfoProvider>::Create(loader);
+
+ if (!multi_loader) {
+ SetError(result, make_error<StringError>(
+ llvm::inconvertibleErrorCode(),
+ "Unable to create process info loader."));
+ return false;
+ }
+
+ llvm::Optional<std::string> process_file;
+ while ((process_file = multi_loader->GetNextFile())) {
+ if (llvm::Expected<ProcessInstanceInfoList> infos =
+ ReadFromYAML<ProcessInstanceInfoList>(*process_file)) {
+ for (ProcessInstanceInfo info : *infos)
+ info.Dump(result.GetOutputStream(), HostInfo::GetUserIDResolver());
+ } else {
+ SetError(result, infos.takeError());
+ return false;
+ }
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ case eReproducerProviderNone:
+ result.AppendError("No valid provider specified.");
+ return false;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+CommandObjectReproducer::CommandObjectReproducer(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "reproducer",
+ "Commands for manipulating reproducers. Reproducers make it "
+ "possible "
+ "to capture full debug sessions with all its dependencies. The "
+ "resulting reproducer is used to replay the debug session while "
+ "debugging the debugger.\n"
+ "Because reproducers need the whole the debug session from "
+ "beginning to end, you need to launch the debugger in capture or "
+ "replay mode, commonly though the command line driver.\n"
+ "Reproducers are unrelated record-replay debugging, as you cannot "
+ "interact with the debugger during replay.\n",
+ "reproducer <subcommand> [<subcommand-options>]") {
+ LoadSubCommand(
+ "generate",
+ CommandObjectSP(new CommandObjectReproducerGenerate(interpreter)));
+ LoadSubCommand("status", CommandObjectSP(
+ new CommandObjectReproducerStatus(interpreter)));
+ LoadSubCommand("dump",
+ CommandObjectSP(new CommandObjectReproducerDump(interpreter)));
+ LoadSubCommand("xcrash", CommandObjectSP(
+ new CommandObjectReproducerXCrash(interpreter)));
+}
+
+CommandObjectReproducer::~CommandObjectReproducer() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectReproducer.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectReproducer.h
new file mode 100644
index 000000000000..bdee8053549f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectReproducer.h
@@ -0,0 +1,27 @@
+//===-- CommandObjectReproducer.h -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTREPRODUCER_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTREPRODUCER_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+// CommandObjectReproducer
+
+class CommandObjectReproducer : public CommandObjectMultiword {
+public:
+ CommandObjectReproducer(CommandInterpreter &interpreter);
+
+ ~CommandObjectReproducer() override;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTREPRODUCER_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectScript.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectScript.cpp
new file mode 100644
index 000000000000..f53d6540bc04
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectScript.cpp
@@ -0,0 +1,136 @@
+//===-- CommandObjectScript.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectScript.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/DataFormatters/DataVisualization.h"
+#include "lldb/Host/Config.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Utility/Args.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static constexpr OptionEnumValueElement g_script_option_enumeration[] = {
+ {
+ eScriptLanguagePython,
+ "python",
+ "Python",
+ },
+ {
+ eScriptLanguageLua,
+ "lua",
+ "Lua",
+ },
+ {
+ eScriptLanguageNone,
+ "default",
+ "The default scripting language.",
+ },
+};
+
+static constexpr OptionEnumValues ScriptOptionEnum() {
+ return OptionEnumValues(g_script_option_enumeration);
+}
+
+#define LLDB_OPTIONS_script
+#include "CommandOptions.inc"
+
+Status CommandObjectScript::CommandOptions::SetOptionValue(
+ uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'l':
+ language = (lldb::ScriptLanguage)OptionArgParser::ToOptionEnum(
+ option_arg, GetDefinitions()[option_idx].enum_values,
+ eScriptLanguageNone, error);
+ if (!error.Success())
+ error.SetErrorStringWithFormat("unrecognized value for language '%s'",
+ option_arg.str().c_str());
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+}
+
+void CommandObjectScript::CommandOptions::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ language = lldb::eScriptLanguageNone;
+}
+
+llvm::ArrayRef<OptionDefinition>
+CommandObjectScript::CommandOptions::GetDefinitions() {
+ return llvm::makeArrayRef(g_script_options);
+}
+
+CommandObjectScript::CommandObjectScript(CommandInterpreter &interpreter)
+ : CommandObjectRaw(
+ interpreter, "script",
+ "Invoke the script interpreter with provided code and display any "
+ "results. Start the interactive interpreter if no code is supplied.",
+ "script [--language <scripting-language> --] [<script-code>]") {}
+
+CommandObjectScript::~CommandObjectScript() = default;
+
+bool CommandObjectScript::DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) {
+ // Try parsing the language option but when the command contains a raw part
+ // separated by the -- delimiter.
+ OptionsWithRaw raw_args(command);
+ if (raw_args.HasArgs()) {
+ if (!ParseOptions(raw_args.GetArgs(), result))
+ return false;
+ command = raw_args.GetRawPart();
+ }
+
+ lldb::ScriptLanguage language =
+ (m_options.language == lldb::eScriptLanguageNone)
+ ? m_interpreter.GetDebugger().GetScriptLanguage()
+ : m_options.language;
+
+ if (language == lldb::eScriptLanguageNone) {
+ result.AppendError(
+ "the script-lang setting is set to none - scripting not available");
+ return false;
+ }
+
+ ScriptInterpreter *script_interpreter =
+ GetDebugger().GetScriptInterpreter(true, language);
+
+ if (script_interpreter == nullptr) {
+ result.AppendError("no script interpreter");
+ return false;
+ }
+
+ // Script might change Python code we use for formatting. Make sure we keep
+ // up to date with it.
+ DataVisualization::ForceUpdate();
+
+ if (command.empty()) {
+ script_interpreter->ExecuteInterpreterLoop();
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+ }
+
+ // We can do better when reporting the status of one-liner script execution.
+ if (script_interpreter->ExecuteOneLine(command, &result))
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+
+ return result.Succeeded();
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectScript.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectScript.h
new file mode 100644
index 000000000000..9d164e864a8b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectScript.h
@@ -0,0 +1,42 @@
+//===-- CommandObjectScript.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_INTERPRETER_COMMANDOBJECTSCRIPT_H
+#define LLDB_SOURCE_INTERPRETER_COMMANDOBJECTSCRIPT_H
+
+#include "lldb/Interpreter/CommandObject.h"
+
+namespace lldb_private {
+
+class CommandObjectScript : public CommandObjectRaw {
+public:
+ CommandObjectScript(CommandInterpreter &interpreter);
+ ~CommandObjectScript() override;
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+ ~CommandOptions() override = default;
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override;
+ void OptionParsingStarting(ExecutionContext *execution_context) override;
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override;
+ lldb::ScriptLanguage language = lldb::eScriptLanguageNone;
+ };
+
+protected:
+ bool DoExecute(llvm::StringRef command, CommandReturnObject &result) override;
+
+private:
+ CommandOptions m_options;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_INTERPRETER_COMMANDOBJECTSCRIPT_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectSession.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectSession.cpp
new file mode 100644
index 000000000000..c11839a48de0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectSession.cpp
@@ -0,0 +1,206 @@
+#include "CommandObjectSession.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionValue.h"
+#include "lldb/Interpreter/OptionValueBoolean.h"
+#include "lldb/Interpreter/OptionValueString.h"
+#include "lldb/Interpreter/OptionValueUInt64.h"
+#include "lldb/Interpreter/Options.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+class CommandObjectSessionSave : public CommandObjectParsed {
+public:
+ CommandObjectSessionSave(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "session save",
+ "Save the current session transcripts to a file.\n"
+ "If no file if specified, transcripts will be "
+ "saved to a temporary file.",
+ "session save [file]") {
+ CommandArgumentEntry arg1;
+ arg1.emplace_back(eArgTypePath, eArgRepeatOptional);
+ m_arguments.push_back(arg1);
+ }
+
+ ~CommandObjectSessionSave() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+ request, nullptr);
+ }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ llvm::StringRef file_path;
+
+ if (!args.empty())
+ file_path = args[0].ref();
+
+ if (m_interpreter.SaveTranscript(result, file_path.str()))
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+ return result.Succeeded();
+ }
+};
+
+#define LLDB_OPTIONS_history
+#include "CommandOptions.inc"
+
+class CommandObjectSessionHistory : public CommandObjectParsed {
+public:
+ CommandObjectSessionHistory(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "session history",
+ "Dump the history of commands in this session.\n"
+ "Commands in the history list can be run again "
+ "using \"!<INDEX>\". \"!-<OFFSET>\" will re-run "
+ "the command that is <OFFSET> commands from the end"
+ " of the list (counting the current command).",
+ nullptr) {}
+
+ ~CommandObjectSessionHistory() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions()
+ : m_start_idx(0), m_stop_idx(0), m_count(0), m_clear(false) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'c':
+ error = m_count.SetValueFromString(option_arg, eVarSetOperationAssign);
+ break;
+ case 's':
+ if (option_arg == "end") {
+ m_start_idx.SetCurrentValue(UINT64_MAX);
+ m_start_idx.SetOptionWasSet();
+ } else
+ error = m_start_idx.SetValueFromString(option_arg,
+ eVarSetOperationAssign);
+ break;
+ case 'e':
+ error =
+ m_stop_idx.SetValueFromString(option_arg, eVarSetOperationAssign);
+ break;
+ case 'C':
+ m_clear.SetCurrentValue(true);
+ m_clear.SetOptionWasSet();
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_start_idx.Clear();
+ m_stop_idx.Clear();
+ m_count.Clear();
+ m_clear.Clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_history_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ OptionValueUInt64 m_start_idx;
+ OptionValueUInt64 m_stop_idx;
+ OptionValueUInt64 m_count;
+ OptionValueBoolean m_clear;
+ };
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (m_options.m_clear.GetCurrentValue() &&
+ m_options.m_clear.OptionWasSet()) {
+ m_interpreter.GetCommandHistory().Clear();
+ result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
+ } else {
+ if (m_options.m_start_idx.OptionWasSet() &&
+ m_options.m_stop_idx.OptionWasSet() &&
+ m_options.m_count.OptionWasSet()) {
+ result.AppendError("--count, --start-index and --end-index cannot be "
+ "all specified in the same invocation");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ } else {
+ std::pair<bool, uint64_t> start_idx(
+ m_options.m_start_idx.OptionWasSet(),
+ m_options.m_start_idx.GetCurrentValue());
+ std::pair<bool, uint64_t> stop_idx(
+ m_options.m_stop_idx.OptionWasSet(),
+ m_options.m_stop_idx.GetCurrentValue());
+ std::pair<bool, uint64_t> count(m_options.m_count.OptionWasSet(),
+ m_options.m_count.GetCurrentValue());
+
+ const CommandHistory &history(m_interpreter.GetCommandHistory());
+
+ if (start_idx.first && start_idx.second == UINT64_MAX) {
+ if (count.first) {
+ start_idx.second = history.GetSize() - count.second;
+ stop_idx.second = history.GetSize() - 1;
+ } else if (stop_idx.first) {
+ start_idx.second = stop_idx.second;
+ stop_idx.second = history.GetSize() - 1;
+ } else {
+ start_idx.second = 0;
+ stop_idx.second = history.GetSize() - 1;
+ }
+ } else {
+ if (!start_idx.first && !stop_idx.first && !count.first) {
+ start_idx.second = 0;
+ stop_idx.second = history.GetSize() - 1;
+ } else if (start_idx.first) {
+ if (count.first) {
+ stop_idx.second = start_idx.second + count.second - 1;
+ } else if (!stop_idx.first) {
+ stop_idx.second = history.GetSize() - 1;
+ }
+ } else if (stop_idx.first) {
+ if (count.first) {
+ if (stop_idx.second >= count.second)
+ start_idx.second = stop_idx.second - count.second + 1;
+ else
+ start_idx.second = 0;
+ }
+ } else /* if (count.first) */
+ {
+ start_idx.second = 0;
+ stop_idx.second = count.second - 1;
+ }
+ }
+ history.Dump(result.GetOutputStream(), start_idx.second,
+ stop_idx.second);
+ }
+ }
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+CommandObjectSession::CommandObjectSession(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "session",
+ "Commands controlling LLDB session.",
+ "session <subcommand> [<command-options>]") {
+ LoadSubCommand("save",
+ CommandObjectSP(new CommandObjectSessionSave(interpreter)));
+ LoadSubCommand("history",
+ CommandObjectSP(new CommandObjectSessionHistory(interpreter)));
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectSession.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectSession.h
new file mode 100644
index 000000000000..0af0e279f47e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectSession.h
@@ -0,0 +1,23 @@
+//===-- CommandObjectSession.h ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTSESSION_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTSESSION_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+class CommandObjectSession : public CommandObjectMultiword {
+public:
+ CommandObjectSession(CommandInterpreter &interpreter);
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTSESSION_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectSettings.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectSettings.cpp
new file mode 100644
index 000000000000..e4162e56dc2c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectSettings.cpp
@@ -0,0 +1,1164 @@
+//===-- CommandObjectSettings.cpp -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectSettings.h"
+
+#include "llvm/ADT/StringRef.h"
+
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandCompletions.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// CommandObjectSettingsSet
+#define LLDB_OPTIONS_settings_set
+#include "CommandOptions.inc"
+
+class CommandObjectSettingsSet : public CommandObjectRaw {
+public:
+ CommandObjectSettingsSet(CommandInterpreter &interpreter)
+ : CommandObjectRaw(interpreter, "settings set",
+ "Set the value of the specified debugger setting.") {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentData var_name_arg;
+ CommandArgumentData value_arg;
+
+ // Define the first (and only) variant of this arg.
+ var_name_arg.arg_type = eArgTypeSettingVariableName;
+ var_name_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(var_name_arg);
+
+ // Define the first (and only) variant of this arg.
+ value_arg.arg_type = eArgTypeValue;
+ value_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg2.push_back(value_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+
+ SetHelpLong(
+ "\nWhen setting a dictionary or array variable, you can set multiple entries \
+at once by giving the values to the set command. For example:"
+ R"(
+
+(lldb) settings set target.run-args value1 value2 value3
+(lldb) settings set target.env-vars MYPATH=~/.:/usr/bin SOME_ENV_VAR=12345
+
+(lldb) settings show target.run-args
+ [0]: 'value1'
+ [1]: 'value2'
+ [3]: 'value3'
+(lldb) settings show target.env-vars
+ 'MYPATH=~/.:/usr/bin'
+ 'SOME_ENV_VAR=12345'
+
+)"
+ "Warning: The 'set' command re-sets the entire array or dictionary. If you \
+just want to add, remove or update individual values (or add something to \
+the end), use one of the other settings sub-commands: append, replace, \
+insert-before or insert-after.");
+ }
+
+ ~CommandObjectSettingsSet() override = default;
+
+ // Overrides base class's behavior where WantsCompletion =
+ // !WantsRawCommandString.
+ bool WantsCompletion() override { return true; }
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'f':
+ m_force = true;
+ break;
+ case 'g':
+ m_global = true;
+ break;
+ case 'e':
+ m_exists = true;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_global = false;
+ m_force = false;
+ m_exists = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_settings_set_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ bool m_global = false;
+ bool m_force = false;
+ bool m_exists = false;
+ };
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+
+ const size_t argc = request.GetParsedLine().GetArgumentCount();
+ const char *arg = nullptr;
+ size_t setting_var_idx;
+ for (setting_var_idx = 0; setting_var_idx < argc; ++setting_var_idx) {
+ arg = request.GetParsedLine().GetArgumentAtIndex(setting_var_idx);
+ if (arg && arg[0] != '-')
+ break; // We found our setting variable name index
+ }
+ if (request.GetCursorIndex() == setting_var_idx) {
+ // Attempting to complete setting variable name
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion,
+ request, nullptr);
+ return;
+ }
+ arg = request.GetParsedLine().GetArgumentAtIndex(request.GetCursorIndex());
+
+ if (!arg)
+ return;
+
+ // Complete option name
+ if (arg[0] != '-')
+ return;
+
+ // Complete setting value
+ const char *setting_var_name =
+ request.GetParsedLine().GetArgumentAtIndex(setting_var_idx);
+ Status error;
+ lldb::OptionValueSP value_sp(GetDebugger().GetPropertyValue(
+ &m_exe_ctx, setting_var_name, false, error));
+ if (!value_sp)
+ return;
+ value_sp->AutoComplete(m_interpreter, request);
+ }
+
+protected:
+ bool DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) override {
+ Args cmd_args(command);
+
+ // Process possible options.
+ if (!ParseOptions(cmd_args, result))
+ return false;
+
+ const size_t min_argc = m_options.m_force ? 1 : 2;
+ const size_t argc = cmd_args.GetArgumentCount();
+
+ if ((argc < min_argc) && (!m_options.m_global)) {
+ result.AppendError("'settings set' takes more arguments");
+ return false;
+ }
+
+ const char *var_name = cmd_args.GetArgumentAtIndex(0);
+ if ((var_name == nullptr) || (var_name[0] == '\0')) {
+ result.AppendError(
+ "'settings set' command requires a valid variable name");
+ return false;
+ }
+
+ // A missing value corresponds to clearing the setting when "force" is
+ // specified.
+ if (argc == 1 && m_options.m_force) {
+ Status error(GetDebugger().SetPropertyValue(
+ &m_exe_ctx, eVarSetOperationClear, var_name, llvm::StringRef()));
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ return false;
+ }
+ return result.Succeeded();
+ }
+
+ // Split the raw command into var_name and value pair.
+ llvm::StringRef var_value(command);
+ var_value = var_value.split(var_name).second.ltrim();
+
+ Status error;
+ if (m_options.m_global)
+ error = GetDebugger().SetPropertyValue(nullptr, eVarSetOperationAssign,
+ var_name, var_value);
+
+ if (error.Success()) {
+ // FIXME this is the same issue as the one in commands script import
+ // we could be setting target.load-script-from-symbol-file which would
+ // cause Python scripts to be loaded, which could run LLDB commands (e.g.
+ // settings set target.process.python-os-plugin-path) and cause a crash
+ // if we did not clear the command's exe_ctx first
+ ExecutionContext exe_ctx(m_exe_ctx);
+ m_exe_ctx.Clear();
+ error = GetDebugger().SetPropertyValue(&exe_ctx, eVarSetOperationAssign,
+ var_name, var_value);
+ }
+
+ if (error.Fail() && !m_options.m_exists) {
+ result.AppendError(error.AsCString());
+ return false;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectSettingsShow -- Show current values
+
+class CommandObjectSettingsShow : public CommandObjectParsed {
+public:
+ CommandObjectSettingsShow(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "settings show",
+ "Show matching debugger settings and their current "
+ "values. Defaults to showing all settings.",
+ nullptr) {
+ CommandArgumentEntry arg1;
+ CommandArgumentData var_name_arg;
+
+ // Define the first (and only) variant of this arg.
+ var_name_arg.arg_type = eArgTypeSettingVariableName;
+ var_name_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(var_name_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ }
+
+ ~CommandObjectSettingsShow() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion,
+ request, nullptr);
+ }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ if (!args.empty()) {
+ for (const auto &arg : args) {
+ Status error(GetDebugger().DumpPropertyValue(
+ &m_exe_ctx, result.GetOutputStream(), arg.ref(),
+ OptionValue::eDumpGroupValue));
+ if (error.Success()) {
+ result.GetOutputStream().EOL();
+ } else {
+ result.AppendError(error.AsCString());
+ }
+ }
+ } else {
+ GetDebugger().DumpAllPropertyValues(&m_exe_ctx, result.GetOutputStream(),
+ OptionValue::eDumpGroupValue);
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectSettingsWrite -- Write settings to file
+#define LLDB_OPTIONS_settings_write
+#include "CommandOptions.inc"
+
+class CommandObjectSettingsWrite : public CommandObjectParsed {
+public:
+ CommandObjectSettingsWrite(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "settings export",
+ "Write matching debugger settings and their "
+ "current values to a file that can be read in with "
+ "\"settings read\". Defaults to writing all settings.",
+ nullptr) {
+ CommandArgumentEntry arg1;
+ CommandArgumentData var_name_arg;
+
+ // Define the first (and only) variant of this arg.
+ var_name_arg.arg_type = eArgTypeSettingVariableName;
+ var_name_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(var_name_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ }
+
+ ~CommandObjectSettingsWrite() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'f':
+ m_filename.assign(std::string(option_arg));
+ break;
+ case 'a':
+ m_append = true;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_filename.clear();
+ m_append = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_settings_write_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ std::string m_filename;
+ bool m_append = false;
+ };
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ FileSpec file_spec(m_options.m_filename);
+ FileSystem::Instance().Resolve(file_spec);
+ std::string path(file_spec.GetPath());
+ auto options = File::eOpenOptionWriteOnly | File::eOpenOptionCanCreate;
+ if (m_options.m_append)
+ options |= File::eOpenOptionAppend;
+ else
+ options |= File::eOpenOptionTruncate;
+
+ StreamFile out_file(path.c_str(), options,
+ lldb::eFilePermissionsFileDefault);
+
+ if (!out_file.GetFile().IsValid()) {
+ result.AppendErrorWithFormat("%s: unable to write to file", path.c_str());
+ return false;
+ }
+
+ // Exporting should not be context sensitive.
+ ExecutionContext clean_ctx;
+
+ if (args.empty()) {
+ GetDebugger().DumpAllPropertyValues(&clean_ctx, out_file,
+ OptionValue::eDumpGroupExport);
+ return result.Succeeded();
+ }
+
+ for (const auto &arg : args) {
+ Status error(GetDebugger().DumpPropertyValue(
+ &clean_ctx, out_file, arg.ref(), OptionValue::eDumpGroupExport));
+ if (!error.Success()) {
+ result.AppendError(error.AsCString());
+ }
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectSettingsRead -- Read settings from file
+#define LLDB_OPTIONS_settings_read
+#include "CommandOptions.inc"
+
+class CommandObjectSettingsRead : public CommandObjectParsed {
+public:
+ CommandObjectSettingsRead(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "settings read",
+ "Read settings previously saved to a file with \"settings write\".",
+ nullptr) {}
+
+ ~CommandObjectSettingsRead() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'f':
+ m_filename.assign(std::string(option_arg));
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_filename.clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_settings_read_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ std::string m_filename;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ FileSpec file(m_options.m_filename);
+ FileSystem::Instance().Resolve(file);
+ CommandInterpreterRunOptions options;
+ options.SetAddToHistory(false);
+ options.SetEchoCommands(false);
+ options.SetPrintResults(true);
+ options.SetPrintErrors(true);
+ options.SetStopOnError(false);
+ m_interpreter.HandleCommandsFromFile(file, options, result);
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectSettingsList -- List settable variables
+
+class CommandObjectSettingsList : public CommandObjectParsed {
+public:
+ CommandObjectSettingsList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "settings list",
+ "List and describe matching debugger settings. "
+ "Defaults to all listing all settings.",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandArgumentData var_name_arg;
+ CommandArgumentData prefix_name_arg;
+
+ // Define the first variant of this arg.
+ var_name_arg.arg_type = eArgTypeSettingVariableName;
+ var_name_arg.arg_repetition = eArgRepeatOptional;
+
+ // Define the second variant of this arg.
+ prefix_name_arg.arg_type = eArgTypeSettingPrefix;
+ prefix_name_arg.arg_repetition = eArgRepeatOptional;
+
+ arg.push_back(var_name_arg);
+ arg.push_back(prefix_name_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectSettingsList() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion,
+ request, nullptr);
+ }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ const bool will_modify = false;
+ const size_t argc = args.GetArgumentCount();
+ if (argc > 0) {
+ const bool dump_qualified_name = true;
+
+ for (const Args::ArgEntry &arg : args) {
+ const char *property_path = arg.c_str();
+
+ const Property *property =
+ GetDebugger().GetValueProperties()->GetPropertyAtPath(
+ &m_exe_ctx, will_modify, property_path);
+
+ if (property) {
+ property->DumpDescription(m_interpreter, result.GetOutputStream(), 0,
+ dump_qualified_name);
+ } else {
+ result.AppendErrorWithFormat("invalid property path '%s'",
+ property_path);
+ }
+ }
+ } else {
+ GetDebugger().DumpAllDescriptions(m_interpreter,
+ result.GetOutputStream());
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectSettingsRemove
+
+class CommandObjectSettingsRemove : public CommandObjectRaw {
+public:
+ CommandObjectSettingsRemove(CommandInterpreter &interpreter)
+ : CommandObjectRaw(interpreter, "settings remove",
+ "Remove a value from a setting, specified by array "
+ "index or dictionary key.") {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentData var_name_arg;
+ CommandArgumentData index_arg;
+ CommandArgumentData key_arg;
+
+ // Define the first (and only) variant of this arg.
+ var_name_arg.arg_type = eArgTypeSettingVariableName;
+ var_name_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(var_name_arg);
+
+ // Define the first variant of this arg.
+ index_arg.arg_type = eArgTypeSettingIndex;
+ index_arg.arg_repetition = eArgRepeatPlain;
+
+ // Define the second variant of this arg.
+ key_arg.arg_type = eArgTypeSettingKey;
+ key_arg.arg_repetition = eArgRepeatPlain;
+
+ // Push both variants into this arg
+ arg2.push_back(index_arg);
+ arg2.push_back(key_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+ }
+
+ ~CommandObjectSettingsRemove() override = default;
+
+ bool WantsCompletion() override { return true; }
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (request.GetCursorIndex() < 2)
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion,
+ request, nullptr);
+ }
+
+protected:
+ bool DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) override {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+
+ Args cmd_args(command);
+
+ // Process possible options.
+ if (!ParseOptions(cmd_args, result))
+ return false;
+
+ const size_t argc = cmd_args.GetArgumentCount();
+ if (argc == 0) {
+ result.AppendError("'settings remove' takes an array or dictionary item, "
+ "or an array followed by one or more indexes, or a "
+ "dictionary followed by one or more key names to "
+ "remove");
+ return false;
+ }
+
+ const char *var_name = cmd_args.GetArgumentAtIndex(0);
+ if ((var_name == nullptr) || (var_name[0] == '\0')) {
+ result.AppendError(
+ "'settings remove' command requires a valid variable name");
+ return false;
+ }
+
+ // Split the raw command into var_name and value pair.
+ llvm::StringRef var_value(command);
+ var_value = var_value.split(var_name).second.trim();
+
+ Status error(GetDebugger().SetPropertyValue(
+ &m_exe_ctx, eVarSetOperationRemove, var_name, var_value));
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ return false;
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectSettingsReplace
+
+class CommandObjectSettingsReplace : public CommandObjectRaw {
+public:
+ CommandObjectSettingsReplace(CommandInterpreter &interpreter)
+ : CommandObjectRaw(interpreter, "settings replace",
+ "Replace the debugger setting value specified by "
+ "array index or dictionary key.") {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentEntry arg3;
+ CommandArgumentData var_name_arg;
+ CommandArgumentData index_arg;
+ CommandArgumentData key_arg;
+ CommandArgumentData value_arg;
+
+ // Define the first (and only) variant of this arg.
+ var_name_arg.arg_type = eArgTypeSettingVariableName;
+ var_name_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(var_name_arg);
+
+ // Define the first (variant of this arg.
+ index_arg.arg_type = eArgTypeSettingIndex;
+ index_arg.arg_repetition = eArgRepeatPlain;
+
+ // Define the second (variant of this arg.
+ key_arg.arg_type = eArgTypeSettingKey;
+ key_arg.arg_repetition = eArgRepeatPlain;
+
+ // Put both variants into this arg
+ arg2.push_back(index_arg);
+ arg2.push_back(key_arg);
+
+ // Define the first (and only) variant of this arg.
+ value_arg.arg_type = eArgTypeValue;
+ value_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg3.push_back(value_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+ m_arguments.push_back(arg3);
+ }
+
+ ~CommandObjectSettingsReplace() override = default;
+
+ // Overrides base class's behavior where WantsCompletion =
+ // !WantsRawCommandString.
+ bool WantsCompletion() override { return true; }
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ // Attempting to complete variable name
+ if (request.GetCursorIndex() < 2)
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion,
+ request, nullptr);
+ }
+
+protected:
+ bool DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) override {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+
+ Args cmd_args(command);
+ const char *var_name = cmd_args.GetArgumentAtIndex(0);
+ if ((var_name == nullptr) || (var_name[0] == '\0')) {
+ result.AppendError("'settings replace' command requires a valid variable "
+ "name; No value supplied");
+ return false;
+ }
+
+ // Split the raw command into var_name, index_value, and value triple.
+ llvm::StringRef var_value(command);
+ var_value = var_value.split(var_name).second.trim();
+
+ Status error(GetDebugger().SetPropertyValue(
+ &m_exe_ctx, eVarSetOperationReplace, var_name, var_value));
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ return false;
+ } else {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectSettingsInsertBefore
+
+class CommandObjectSettingsInsertBefore : public CommandObjectRaw {
+public:
+ CommandObjectSettingsInsertBefore(CommandInterpreter &interpreter)
+ : CommandObjectRaw(interpreter, "settings insert-before",
+ "Insert one or more values into an debugger array "
+ "setting immediately before the specified element "
+ "index.") {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentEntry arg3;
+ CommandArgumentData var_name_arg;
+ CommandArgumentData index_arg;
+ CommandArgumentData value_arg;
+
+ // Define the first (and only) variant of this arg.
+ var_name_arg.arg_type = eArgTypeSettingVariableName;
+ var_name_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(var_name_arg);
+
+ // Define the first (variant of this arg.
+ index_arg.arg_type = eArgTypeSettingIndex;
+ index_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg2.push_back(index_arg);
+
+ // Define the first (and only) variant of this arg.
+ value_arg.arg_type = eArgTypeValue;
+ value_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg3.push_back(value_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+ m_arguments.push_back(arg3);
+ }
+
+ ~CommandObjectSettingsInsertBefore() override = default;
+
+ // Overrides base class's behavior where WantsCompletion =
+ // !WantsRawCommandString.
+ bool WantsCompletion() override { return true; }
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ // Attempting to complete variable name
+ if (request.GetCursorIndex() < 2)
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion,
+ request, nullptr);
+ }
+
+protected:
+ bool DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) override {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+
+ Args cmd_args(command);
+ const size_t argc = cmd_args.GetArgumentCount();
+
+ if (argc < 3) {
+ result.AppendError("'settings insert-before' takes more arguments");
+ return false;
+ }
+
+ const char *var_name = cmd_args.GetArgumentAtIndex(0);
+ if ((var_name == nullptr) || (var_name[0] == '\0')) {
+ result.AppendError("'settings insert-before' command requires a valid "
+ "variable name; No value supplied");
+ return false;
+ }
+
+ // Split the raw command into var_name, index_value, and value triple.
+ llvm::StringRef var_value(command);
+ var_value = var_value.split(var_name).second.trim();
+
+ Status error(GetDebugger().SetPropertyValue(
+ &m_exe_ctx, eVarSetOperationInsertBefore, var_name, var_value));
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ return false;
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectSettingInsertAfter
+
+class CommandObjectSettingsInsertAfter : public CommandObjectRaw {
+public:
+ CommandObjectSettingsInsertAfter(CommandInterpreter &interpreter)
+ : CommandObjectRaw(interpreter, "settings insert-after",
+ "Insert one or more values into a debugger array "
+ "settings after the specified element index.") {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentEntry arg3;
+ CommandArgumentData var_name_arg;
+ CommandArgumentData index_arg;
+ CommandArgumentData value_arg;
+
+ // Define the first (and only) variant of this arg.
+ var_name_arg.arg_type = eArgTypeSettingVariableName;
+ var_name_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(var_name_arg);
+
+ // Define the first (variant of this arg.
+ index_arg.arg_type = eArgTypeSettingIndex;
+ index_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg2.push_back(index_arg);
+
+ // Define the first (and only) variant of this arg.
+ value_arg.arg_type = eArgTypeValue;
+ value_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg3.push_back(value_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+ m_arguments.push_back(arg3);
+ }
+
+ ~CommandObjectSettingsInsertAfter() override = default;
+
+ // Overrides base class's behavior where WantsCompletion =
+ // !WantsRawCommandString.
+ bool WantsCompletion() override { return true; }
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ // Attempting to complete variable name
+ if (request.GetCursorIndex() < 2)
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion,
+ request, nullptr);
+ }
+
+protected:
+ bool DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) override {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+
+ Args cmd_args(command);
+ const size_t argc = cmd_args.GetArgumentCount();
+
+ if (argc < 3) {
+ result.AppendError("'settings insert-after' takes more arguments");
+ return false;
+ }
+
+ const char *var_name = cmd_args.GetArgumentAtIndex(0);
+ if ((var_name == nullptr) || (var_name[0] == '\0')) {
+ result.AppendError("'settings insert-after' command requires a valid "
+ "variable name; No value supplied");
+ return false;
+ }
+
+ // Split the raw command into var_name, index_value, and value triple.
+ llvm::StringRef var_value(command);
+ var_value = var_value.split(var_name).second.trim();
+
+ Status error(GetDebugger().SetPropertyValue(
+ &m_exe_ctx, eVarSetOperationInsertAfter, var_name, var_value));
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ return false;
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectSettingsAppend
+
+class CommandObjectSettingsAppend : public CommandObjectRaw {
+public:
+ CommandObjectSettingsAppend(CommandInterpreter &interpreter)
+ : CommandObjectRaw(interpreter, "settings append",
+ "Append one or more values to a debugger array, "
+ "dictionary, or string setting.") {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentData var_name_arg;
+ CommandArgumentData value_arg;
+
+ // Define the first (and only) variant of this arg.
+ var_name_arg.arg_type = eArgTypeSettingVariableName;
+ var_name_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(var_name_arg);
+
+ // Define the first (and only) variant of this arg.
+ value_arg.arg_type = eArgTypeValue;
+ value_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg2.push_back(value_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+ }
+
+ ~CommandObjectSettingsAppend() override = default;
+
+ // Overrides base class's behavior where WantsCompletion =
+ // !WantsRawCommandString.
+ bool WantsCompletion() override { return true; }
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ // Attempting to complete variable name
+ if (request.GetCursorIndex() < 2)
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion,
+ request, nullptr);
+ }
+
+protected:
+ bool DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) override {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ Args cmd_args(command);
+ const size_t argc = cmd_args.GetArgumentCount();
+
+ if (argc < 2) {
+ result.AppendError("'settings append' takes more arguments");
+ return false;
+ }
+
+ const char *var_name = cmd_args.GetArgumentAtIndex(0);
+ if ((var_name == nullptr) || (var_name[0] == '\0')) {
+ result.AppendError("'settings append' command requires a valid variable "
+ "name; No value supplied");
+ return false;
+ }
+
+ // Do not perform cmd_args.Shift() since StringRef is manipulating the raw
+ // character string later on.
+
+ // Split the raw command into var_name and value pair.
+ llvm::StringRef var_value(command);
+ var_value = var_value.split(var_name).second.trim();
+
+ Status error(GetDebugger().SetPropertyValue(
+ &m_exe_ctx, eVarSetOperationAppend, var_name, var_value));
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ return false;
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectSettingsClear
+#define LLDB_OPTIONS_settings_clear
+#include "CommandOptions.inc"
+
+class CommandObjectSettingsClear : public CommandObjectParsed {
+public:
+ CommandObjectSettingsClear(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "settings clear",
+ "Clear a debugger setting array, dictionary, or string. "
+ "If '-a' option is specified, it clears all settings.", nullptr) {
+ CommandArgumentEntry arg;
+ CommandArgumentData var_name_arg;
+
+ // Define the first (and only) variant of this arg.
+ var_name_arg.arg_type = eArgTypeSettingVariableName;
+ var_name_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(var_name_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectSettingsClear() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ // Attempting to complete variable name
+ if (request.GetCursorIndex() < 2)
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion,
+ request, nullptr);
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ const int short_option = m_getopt_table[option_idx].val;
+ switch (short_option) {
+ case 'a':
+ m_clear_all = true;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return Status();
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_clear_all = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_settings_clear_options);
+ }
+
+ bool m_clear_all = false;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ const size_t argc = command.GetArgumentCount();
+
+ if (m_options.m_clear_all) {
+ if (argc != 0) {
+ result.AppendError("'settings clear --all' doesn't take any arguments");
+ return false;
+ }
+ GetDebugger().GetValueProperties()->Clear();
+ return result.Succeeded();
+ }
+
+ if (argc != 1) {
+ result.AppendError("'settings clear' takes exactly one argument");
+ return false;
+ }
+
+ const char *var_name = command.GetArgumentAtIndex(0);
+ if ((var_name == nullptr) || (var_name[0] == '\0')) {
+ result.AppendError("'settings clear' command requires a valid variable "
+ "name; No value supplied");
+ return false;
+ }
+
+ Status error(GetDebugger().SetPropertyValue(
+ &m_exe_ctx, eVarSetOperationClear, var_name, llvm::StringRef()));
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ return false;
+ }
+
+ return result.Succeeded();
+ }
+
+ private:
+ CommandOptions m_options;
+};
+
+// CommandObjectMultiwordSettings
+
+CommandObjectMultiwordSettings::CommandObjectMultiwordSettings(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "settings",
+ "Commands for managing LLDB settings.",
+ "settings <subcommand> [<command-options>]") {
+ LoadSubCommand("set",
+ CommandObjectSP(new CommandObjectSettingsSet(interpreter)));
+ LoadSubCommand("show",
+ CommandObjectSP(new CommandObjectSettingsShow(interpreter)));
+ LoadSubCommand("list",
+ CommandObjectSP(new CommandObjectSettingsList(interpreter)));
+ LoadSubCommand("remove",
+ CommandObjectSP(new CommandObjectSettingsRemove(interpreter)));
+ LoadSubCommand("replace", CommandObjectSP(
+ new CommandObjectSettingsReplace(interpreter)));
+ LoadSubCommand(
+ "insert-before",
+ CommandObjectSP(new CommandObjectSettingsInsertBefore(interpreter)));
+ LoadSubCommand(
+ "insert-after",
+ CommandObjectSP(new CommandObjectSettingsInsertAfter(interpreter)));
+ LoadSubCommand("append",
+ CommandObjectSP(new CommandObjectSettingsAppend(interpreter)));
+ LoadSubCommand("clear",
+ CommandObjectSP(new CommandObjectSettingsClear(interpreter)));
+ LoadSubCommand("write",
+ CommandObjectSP(new CommandObjectSettingsWrite(interpreter)));
+ LoadSubCommand("read",
+ CommandObjectSP(new CommandObjectSettingsRead(interpreter)));
+}
+
+CommandObjectMultiwordSettings::~CommandObjectMultiwordSettings() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectSettings.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectSettings.h
new file mode 100644
index 000000000000..31ec1d3bef16
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectSettings.h
@@ -0,0 +1,27 @@
+//===-- CommandObjectSettings.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTSETTINGS_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTSETTINGS_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+// CommandObjectMultiwordSettings
+
+class CommandObjectMultiwordSettings : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordSettings(CommandInterpreter &interpreter);
+
+ ~CommandObjectMultiwordSettings() override;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTSETTINGS_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectSource.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectSource.cpp
new file mode 100644
index 000000000000..f87981859844
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectSource.cpp
@@ -0,0 +1,1256 @@
+//===-- CommandObjectSource.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectSource.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/FileLineResolver.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/SourceManager.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionValueFileColonLine.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Utility/FileSpec.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#pragma mark CommandObjectSourceInfo
+// CommandObjectSourceInfo - debug line entries dumping command
+#define LLDB_OPTIONS_source_info
+#include "CommandOptions.inc"
+
+class CommandObjectSourceInfo : public CommandObjectParsed {
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = GetDefinitions()[option_idx].short_option;
+ switch (short_option) {
+ case 'l':
+ if (option_arg.getAsInteger(0, start_line))
+ error.SetErrorStringWithFormat("invalid line number: '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'e':
+ if (option_arg.getAsInteger(0, end_line))
+ error.SetErrorStringWithFormat("invalid line number: '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'c':
+ if (option_arg.getAsInteger(0, num_lines))
+ error.SetErrorStringWithFormat("invalid line count: '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'f':
+ file_name = std::string(option_arg);
+ break;
+
+ case 'n':
+ symbol_name = std::string(option_arg);
+ break;
+
+ case 'a': {
+ address = OptionArgParser::ToAddress(execution_context, option_arg,
+ LLDB_INVALID_ADDRESS, &error);
+ } break;
+ case 's':
+ modules.push_back(std::string(option_arg));
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ file_spec.Clear();
+ file_name.clear();
+ symbol_name.clear();
+ address = LLDB_INVALID_ADDRESS;
+ start_line = 0;
+ end_line = 0;
+ num_lines = 0;
+ modules.clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_source_info_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ FileSpec file_spec;
+ std::string file_name;
+ std::string symbol_name;
+ lldb::addr_t address;
+ uint32_t start_line;
+ uint32_t end_line;
+ uint32_t num_lines;
+ std::vector<std::string> modules;
+ };
+
+public:
+ CommandObjectSourceInfo(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "source info",
+ "Display source line information for the current target "
+ "process. Defaults to instruction pointer in current stack "
+ "frame.",
+ nullptr, eCommandRequiresTarget) {}
+
+ ~CommandObjectSourceInfo() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ // Dump the line entries in each symbol context. Return the number of entries
+ // found. If module_list is set, only dump lines contained in one of the
+ // modules. If file_spec is set, only dump lines in the file. If the
+ // start_line option was specified, don't print lines less than start_line.
+ // If the end_line option was specified, don't print lines greater than
+ // end_line. If the num_lines option was specified, dont print more than
+ // num_lines entries.
+ uint32_t DumpLinesInSymbolContexts(Stream &strm,
+ const SymbolContextList &sc_list,
+ const ModuleList &module_list,
+ const FileSpec &file_spec) {
+ uint32_t start_line = m_options.start_line;
+ uint32_t end_line = m_options.end_line;
+ uint32_t num_lines = m_options.num_lines;
+ Target *target = m_exe_ctx.GetTargetPtr();
+
+ uint32_t num_matches = 0;
+ // Dump all the line entries for the file in the list.
+ ConstString last_module_file_name;
+ uint32_t num_scs = sc_list.GetSize();
+ for (uint32_t i = 0; i < num_scs; ++i) {
+ SymbolContext sc;
+ sc_list.GetContextAtIndex(i, sc);
+ if (sc.comp_unit) {
+ Module *module = sc.module_sp.get();
+ CompileUnit *cu = sc.comp_unit;
+ const LineEntry &line_entry = sc.line_entry;
+ assert(module && cu);
+
+ // Are we looking for specific modules, files or lines?
+ if (module_list.GetSize() &&
+ module_list.GetIndexForModule(module) == LLDB_INVALID_INDEX32)
+ continue;
+ if (!FileSpec::Match(file_spec, line_entry.file))
+ continue;
+ if (start_line > 0 && line_entry.line < start_line)
+ continue;
+ if (end_line > 0 && line_entry.line > end_line)
+ continue;
+ if (num_lines > 0 && num_matches > num_lines)
+ continue;
+
+ // Print a new header if the module changed.
+ ConstString module_file_name = module->GetFileSpec().GetFilename();
+ assert(module_file_name);
+ if (module_file_name != last_module_file_name) {
+ if (num_matches > 0)
+ strm << "\n\n";
+ strm << "Lines found in module `" << module_file_name << "\n";
+ }
+ // Dump the line entry.
+ line_entry.GetDescription(&strm, lldb::eDescriptionLevelBrief, cu,
+ target, /*show_address_only=*/false);
+ strm << "\n";
+ last_module_file_name = module_file_name;
+ num_matches++;
+ }
+ }
+ return num_matches;
+ }
+
+ // Dump the requested line entries for the file in the compilation unit.
+ // Return the number of entries found. If module_list is set, only dump lines
+ // contained in one of the modules. If the start_line option was specified,
+ // don't print lines less than start_line. If the end_line option was
+ // specified, don't print lines greater than end_line. If the num_lines
+ // option was specified, dont print more than num_lines entries.
+ uint32_t DumpFileLinesInCompUnit(Stream &strm, Module *module,
+ CompileUnit *cu, const FileSpec &file_spec) {
+ uint32_t start_line = m_options.start_line;
+ uint32_t end_line = m_options.end_line;
+ uint32_t num_lines = m_options.num_lines;
+ Target *target = m_exe_ctx.GetTargetPtr();
+
+ uint32_t num_matches = 0;
+ assert(module);
+ if (cu) {
+ assert(file_spec.GetFilename().AsCString());
+ bool has_path = (file_spec.GetDirectory().AsCString() != nullptr);
+ const FileSpecList &cu_file_list = cu->GetSupportFiles();
+ size_t file_idx = cu_file_list.FindFileIndex(0, file_spec, has_path);
+ if (file_idx != UINT32_MAX) {
+ // Update the file to how it appears in the CU.
+ const FileSpec &cu_file_spec =
+ cu_file_list.GetFileSpecAtIndex(file_idx);
+
+ // Dump all matching lines at or above start_line for the file in the
+ // CU.
+ ConstString file_spec_name = file_spec.GetFilename();
+ ConstString module_file_name = module->GetFileSpec().GetFilename();
+ bool cu_header_printed = false;
+ uint32_t line = start_line;
+ while (true) {
+ LineEntry line_entry;
+
+ // Find the lowest index of a line entry with a line equal to or
+ // higher than 'line'.
+ uint32_t start_idx = 0;
+ start_idx = cu->FindLineEntry(start_idx, line, &cu_file_spec,
+ /*exact=*/false, &line_entry);
+ if (start_idx == UINT32_MAX)
+ // No more line entries for our file in this CU.
+ break;
+
+ if (end_line > 0 && line_entry.line > end_line)
+ break;
+
+ // Loop through to find any other entries for this line, dumping
+ // each.
+ line = line_entry.line;
+ do {
+ num_matches++;
+ if (num_lines > 0 && num_matches > num_lines)
+ break;
+ assert(cu_file_spec == line_entry.file);
+ if (!cu_header_printed) {
+ if (num_matches > 0)
+ strm << "\n\n";
+ strm << "Lines found for file " << file_spec_name
+ << " in compilation unit "
+ << cu->GetPrimaryFile().GetFilename() << " in `"
+ << module_file_name << "\n";
+ cu_header_printed = true;
+ }
+ line_entry.GetDescription(&strm, lldb::eDescriptionLevelBrief, cu,
+ target, /*show_address_only=*/false);
+ strm << "\n";
+
+ // Anymore after this one?
+ start_idx++;
+ start_idx = cu->FindLineEntry(start_idx, line, &cu_file_spec,
+ /*exact=*/true, &line_entry);
+ } while (start_idx != UINT32_MAX);
+
+ // Try the next higher line, starting over at start_idx 0.
+ line++;
+ }
+ }
+ }
+ return num_matches;
+ }
+
+ // Dump the requested line entries for the file in the module. Return the
+ // number of entries found. If module_list is set, only dump lines contained
+ // in one of the modules. If the start_line option was specified, don't print
+ // lines less than start_line. If the end_line option was specified, don't
+ // print lines greater than end_line. If the num_lines option was specified,
+ // dont print more than num_lines entries.
+ uint32_t DumpFileLinesInModule(Stream &strm, Module *module,
+ const FileSpec &file_spec) {
+ uint32_t num_matches = 0;
+ if (module) {
+ // Look through all the compilation units (CUs) in this module for ones
+ // that contain lines of code from this source file.
+ for (size_t i = 0; i < module->GetNumCompileUnits(); i++) {
+ // Look for a matching source file in this CU.
+ CompUnitSP cu_sp(module->GetCompileUnitAtIndex(i));
+ if (cu_sp) {
+ num_matches +=
+ DumpFileLinesInCompUnit(strm, module, cu_sp.get(), file_spec);
+ }
+ }
+ }
+ return num_matches;
+ }
+
+ // Given an address and a list of modules, append the symbol contexts of all
+ // line entries containing the address found in the modules and return the
+ // count of matches. If none is found, return an error in 'error_strm'.
+ size_t GetSymbolContextsForAddress(const ModuleList &module_list,
+ lldb::addr_t addr,
+ SymbolContextList &sc_list,
+ StreamString &error_strm) {
+ Address so_addr;
+ size_t num_matches = 0;
+ assert(module_list.GetSize() > 0);
+ Target *target = m_exe_ctx.GetTargetPtr();
+ if (target->GetSectionLoadList().IsEmpty()) {
+ // The target isn't loaded yet, we need to lookup the file address in all
+ // modules. Note: the module list option does not apply to addresses.
+ const size_t num_modules = module_list.GetSize();
+ for (size_t i = 0; i < num_modules; ++i) {
+ ModuleSP module_sp(module_list.GetModuleAtIndex(i));
+ if (!module_sp)
+ continue;
+ if (module_sp->ResolveFileAddress(addr, so_addr)) {
+ SymbolContext sc;
+ sc.Clear(true);
+ if (module_sp->ResolveSymbolContextForAddress(
+ so_addr, eSymbolContextEverything, sc) &
+ eSymbolContextLineEntry) {
+ sc_list.AppendIfUnique(sc, /*merge_symbol_into_function=*/false);
+ ++num_matches;
+ }
+ }
+ }
+ if (num_matches == 0)
+ error_strm.Printf("Source information for file address 0x%" PRIx64
+ " not found in any modules.\n",
+ addr);
+ } else {
+ // The target has some things loaded, resolve this address to a compile
+ // unit + file + line and display
+ if (target->GetSectionLoadList().ResolveLoadAddress(addr, so_addr)) {
+ ModuleSP module_sp(so_addr.GetModule());
+ // Check to make sure this module is in our list.
+ if (module_sp && module_list.GetIndexForModule(module_sp.get()) !=
+ LLDB_INVALID_INDEX32) {
+ SymbolContext sc;
+ sc.Clear(true);
+ if (module_sp->ResolveSymbolContextForAddress(
+ so_addr, eSymbolContextEverything, sc) &
+ eSymbolContextLineEntry) {
+ sc_list.AppendIfUnique(sc, /*merge_symbol_into_function=*/false);
+ ++num_matches;
+ } else {
+ StreamString addr_strm;
+ so_addr.Dump(&addr_strm, nullptr,
+ Address::DumpStyleModuleWithFileAddress);
+ error_strm.Printf(
+ "Address 0x%" PRIx64 " resolves to %s, but there is"
+ " no source information available for this address.\n",
+ addr, addr_strm.GetData());
+ }
+ } else {
+ StreamString addr_strm;
+ so_addr.Dump(&addr_strm, nullptr,
+ Address::DumpStyleModuleWithFileAddress);
+ error_strm.Printf("Address 0x%" PRIx64
+ " resolves to %s, but it cannot"
+ " be found in any modules.\n",
+ addr, addr_strm.GetData());
+ }
+ } else
+ error_strm.Printf("Unable to resolve address 0x%" PRIx64 ".\n", addr);
+ }
+ return num_matches;
+ }
+
+ // Dump the line entries found in functions matching the name specified in
+ // the option.
+ bool DumpLinesInFunctions(CommandReturnObject &result) {
+ SymbolContextList sc_list_funcs;
+ ConstString name(m_options.symbol_name.c_str());
+ SymbolContextList sc_list_lines;
+ Target *target = m_exe_ctx.GetTargetPtr();
+ uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
+
+ ModuleFunctionSearchOptions function_options;
+ function_options.include_symbols = false;
+ function_options.include_inlines = true;
+
+ // Note: module_list can't be const& because FindFunctionSymbols isn't
+ // const.
+ ModuleList module_list =
+ (m_module_list.GetSize() > 0) ? m_module_list : target->GetImages();
+ module_list.FindFunctions(name, eFunctionNameTypeAuto, function_options,
+ sc_list_funcs);
+ size_t num_matches = sc_list_funcs.GetSize();
+
+ if (!num_matches) {
+ // If we didn't find any functions with that name, try searching for
+ // symbols that line up exactly with function addresses.
+ SymbolContextList sc_list_symbols;
+ module_list.FindFunctionSymbols(name, eFunctionNameTypeAuto,
+ sc_list_symbols);
+ size_t num_symbol_matches = sc_list_symbols.GetSize();
+ for (size_t i = 0; i < num_symbol_matches; i++) {
+ SymbolContext sc;
+ sc_list_symbols.GetContextAtIndex(i, sc);
+ if (sc.symbol && sc.symbol->ValueIsAddress()) {
+ const Address &base_address = sc.symbol->GetAddressRef();
+ Function *function = base_address.CalculateSymbolContextFunction();
+ if (function) {
+ sc_list_funcs.Append(SymbolContext(function));
+ num_matches++;
+ }
+ }
+ }
+ }
+ if (num_matches == 0) {
+ result.AppendErrorWithFormat("Could not find function named \'%s\'.\n",
+ m_options.symbol_name.c_str());
+ return false;
+ }
+ for (size_t i = 0; i < num_matches; i++) {
+ SymbolContext sc;
+ sc_list_funcs.GetContextAtIndex(i, sc);
+ bool context_found_for_symbol = false;
+ // Loop through all the ranges in the function.
+ AddressRange range;
+ for (uint32_t r = 0;
+ sc.GetAddressRange(eSymbolContextEverything, r,
+ /*use_inline_block_range=*/true, range);
+ ++r) {
+ // Append the symbol contexts for each address in the range to
+ // sc_list_lines.
+ const Address &base_address = range.GetBaseAddress();
+ const addr_t size = range.GetByteSize();
+ lldb::addr_t start_addr = base_address.GetLoadAddress(target);
+ if (start_addr == LLDB_INVALID_ADDRESS)
+ start_addr = base_address.GetFileAddress();
+ lldb::addr_t end_addr = start_addr + size;
+ for (lldb::addr_t addr = start_addr; addr < end_addr;
+ addr += addr_byte_size) {
+ StreamString error_strm;
+ if (!GetSymbolContextsForAddress(module_list, addr, sc_list_lines,
+ error_strm))
+ result.AppendWarningWithFormat("in symbol '%s': %s",
+ sc.GetFunctionName().AsCString(),
+ error_strm.GetData());
+ else
+ context_found_for_symbol = true;
+ }
+ }
+ if (!context_found_for_symbol)
+ result.AppendWarningWithFormat("Unable to find line information"
+ " for matching symbol '%s'.\n",
+ sc.GetFunctionName().AsCString());
+ }
+ if (sc_list_lines.GetSize() == 0) {
+ result.AppendErrorWithFormat("No line information could be found"
+ " for any symbols matching '%s'.\n",
+ name.AsCString());
+ return false;
+ }
+ FileSpec file_spec;
+ if (!DumpLinesInSymbolContexts(result.GetOutputStream(), sc_list_lines,
+ module_list, file_spec)) {
+ result.AppendErrorWithFormat(
+ "Unable to dump line information for symbol '%s'.\n",
+ name.AsCString());
+ return false;
+ }
+ return true;
+ }
+
+ // Dump the line entries found for the address specified in the option.
+ bool DumpLinesForAddress(CommandReturnObject &result) {
+ Target *target = m_exe_ctx.GetTargetPtr();
+ SymbolContextList sc_list;
+
+ StreamString error_strm;
+ if (!GetSymbolContextsForAddress(target->GetImages(), m_options.address,
+ sc_list, error_strm)) {
+ result.AppendErrorWithFormat("%s.\n", error_strm.GetData());
+ return false;
+ }
+ ModuleList module_list;
+ FileSpec file_spec;
+ if (!DumpLinesInSymbolContexts(result.GetOutputStream(), sc_list,
+ module_list, file_spec)) {
+ result.AppendErrorWithFormat("No modules contain load address 0x%" PRIx64
+ ".\n",
+ m_options.address);
+ return false;
+ }
+ return true;
+ }
+
+ // Dump the line entries found in the file specified in the option.
+ bool DumpLinesForFile(CommandReturnObject &result) {
+ FileSpec file_spec(m_options.file_name);
+ const char *filename = m_options.file_name.c_str();
+ Target *target = m_exe_ctx.GetTargetPtr();
+ const ModuleList &module_list =
+ (m_module_list.GetSize() > 0) ? m_module_list : target->GetImages();
+
+ bool displayed_something = false;
+ const size_t num_modules = module_list.GetSize();
+ for (uint32_t i = 0; i < num_modules; ++i) {
+ // Dump lines for this module.
+ Module *module = module_list.GetModulePointerAtIndex(i);
+ assert(module);
+ if (DumpFileLinesInModule(result.GetOutputStream(), module, file_spec))
+ displayed_something = true;
+ }
+ if (!displayed_something) {
+ result.AppendErrorWithFormat("No source filenames matched '%s'.\n",
+ filename);
+ return false;
+ }
+ return true;
+ }
+
+ // Dump the line entries for the current frame.
+ bool DumpLinesForFrame(CommandReturnObject &result) {
+ StackFrame *cur_frame = m_exe_ctx.GetFramePtr();
+ if (cur_frame == nullptr) {
+ result.AppendError(
+ "No selected frame to use to find the default source.");
+ return false;
+ } else if (!cur_frame->HasDebugInformation()) {
+ result.AppendError("No debug info for the selected frame.");
+ return false;
+ } else {
+ const SymbolContext &sc =
+ cur_frame->GetSymbolContext(eSymbolContextLineEntry);
+ SymbolContextList sc_list;
+ sc_list.Append(sc);
+ ModuleList module_list;
+ FileSpec file_spec;
+ if (!DumpLinesInSymbolContexts(result.GetOutputStream(), sc_list,
+ module_list, file_spec)) {
+ result.AppendError(
+ "No source line info available for the selected frame.");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = m_exe_ctx.GetTargetPtr();
+ if (target == nullptr) {
+ target = GetDebugger().GetSelectedTarget().get();
+ if (target == nullptr) {
+ result.AppendError("invalid target, create a debug target using the "
+ "'target create' command.");
+ return false;
+ }
+ }
+
+ uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
+ result.GetOutputStream().SetAddressByteSize(addr_byte_size);
+ result.GetErrorStream().SetAddressByteSize(addr_byte_size);
+
+ // Collect the list of modules to search.
+ m_module_list.Clear();
+ if (!m_options.modules.empty()) {
+ for (size_t i = 0, e = m_options.modules.size(); i < e; ++i) {
+ FileSpec module_file_spec(m_options.modules[i]);
+ if (module_file_spec) {
+ ModuleSpec module_spec(module_file_spec);
+ target->GetImages().FindModules(module_spec, m_module_list);
+ if (m_module_list.IsEmpty())
+ result.AppendWarningWithFormat("No module found for '%s'.\n",
+ m_options.modules[i].c_str());
+ }
+ }
+ if (!m_module_list.GetSize()) {
+ result.AppendError("No modules match the input.");
+ return false;
+ }
+ } else if (target->GetImages().GetSize() == 0) {
+ result.AppendError("The target has no associated executable images.");
+ return false;
+ }
+
+ // Check the arguments to see what lines we should dump.
+ if (!m_options.symbol_name.empty()) {
+ // Print lines for symbol.
+ if (DumpLinesInFunctions(result))
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+ } else if (m_options.address != LLDB_INVALID_ADDRESS) {
+ // Print lines for an address.
+ if (DumpLinesForAddress(result))
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+ } else if (!m_options.file_name.empty()) {
+ // Dump lines for a file.
+ if (DumpLinesForFile(result))
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+ } else {
+ // Dump the line for the current frame.
+ if (DumpLinesForFrame(result))
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+ ModuleList m_module_list;
+};
+
+#pragma mark CommandObjectSourceList
+// CommandObjectSourceList
+#define LLDB_OPTIONS_source_list
+#include "CommandOptions.inc"
+
+class CommandObjectSourceList : public CommandObjectParsed {
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = GetDefinitions()[option_idx].short_option;
+ switch (short_option) {
+ case 'l':
+ if (option_arg.getAsInteger(0, start_line))
+ error.SetErrorStringWithFormat("invalid line number: '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'c':
+ if (option_arg.getAsInteger(0, num_lines))
+ error.SetErrorStringWithFormat("invalid line count: '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'f':
+ file_name = std::string(option_arg);
+ break;
+
+ case 'n':
+ symbol_name = std::string(option_arg);
+ break;
+
+ case 'a': {
+ address = OptionArgParser::ToAddress(execution_context, option_arg,
+ LLDB_INVALID_ADDRESS, &error);
+ } break;
+ case 's':
+ modules.push_back(std::string(option_arg));
+ break;
+
+ case 'b':
+ show_bp_locs = true;
+ break;
+ case 'r':
+ reverse = true;
+ break;
+ case 'y':
+ {
+ OptionValueFileColonLine value;
+ Status fcl_err = value.SetValueFromString(option_arg);
+ if (!fcl_err.Success()) {
+ error.SetErrorStringWithFormat(
+ "Invalid value for file:line specifier: %s",
+ fcl_err.AsCString());
+ } else {
+ file_name = value.GetFileSpec().GetPath();
+ start_line = value.GetLineNumber();
+ // I don't see anything useful to do with a column number, but I don't
+ // want to complain since someone may well have cut and pasted a
+ // listing from somewhere that included a column.
+ }
+ } break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ file_spec.Clear();
+ file_name.clear();
+ symbol_name.clear();
+ address = LLDB_INVALID_ADDRESS;
+ start_line = 0;
+ num_lines = 0;
+ show_bp_locs = false;
+ reverse = false;
+ modules.clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_source_list_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ FileSpec file_spec;
+ std::string file_name;
+ std::string symbol_name;
+ lldb::addr_t address;
+ uint32_t start_line;
+ uint32_t num_lines;
+ std::vector<std::string> modules;
+ bool show_bp_locs;
+ bool reverse;
+ };
+
+public:
+ CommandObjectSourceList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "source list",
+ "Display source code for the current target "
+ "process as specified by options.",
+ nullptr, eCommandRequiresTarget) {}
+
+ ~CommandObjectSourceList() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ llvm::Optional<std::string> GetRepeatCommand(Args &current_command_args,
+ uint32_t index) override {
+ // This is kind of gross, but the command hasn't been parsed yet so we
+ // can't look at the option values for this invocation... I have to scan
+ // the arguments directly.
+ auto iter =
+ llvm::find_if(current_command_args, [](const Args::ArgEntry &e) {
+ return e.ref() == "-r" || e.ref() == "--reverse";
+ });
+ if (iter == current_command_args.end())
+ return m_cmd_name;
+
+ if (m_reverse_name.empty()) {
+ m_reverse_name = m_cmd_name;
+ m_reverse_name.append(" -r");
+ }
+ return m_reverse_name;
+ }
+
+protected:
+ struct SourceInfo {
+ ConstString function;
+ LineEntry line_entry;
+
+ SourceInfo(ConstString name, const LineEntry &line_entry)
+ : function(name), line_entry(line_entry) {}
+
+ SourceInfo() = default;
+
+ bool IsValid() const { return (bool)function && line_entry.IsValid(); }
+
+ bool operator==(const SourceInfo &rhs) const {
+ return function == rhs.function &&
+ line_entry.original_file == rhs.line_entry.original_file &&
+ line_entry.line == rhs.line_entry.line;
+ }
+
+ bool operator!=(const SourceInfo &rhs) const {
+ return function != rhs.function ||
+ line_entry.original_file != rhs.line_entry.original_file ||
+ line_entry.line != rhs.line_entry.line;
+ }
+
+ bool operator<(const SourceInfo &rhs) const {
+ if (function.GetCString() < rhs.function.GetCString())
+ return true;
+ if (line_entry.file.GetDirectory().GetCString() <
+ rhs.line_entry.file.GetDirectory().GetCString())
+ return true;
+ if (line_entry.file.GetFilename().GetCString() <
+ rhs.line_entry.file.GetFilename().GetCString())
+ return true;
+ if (line_entry.line < rhs.line_entry.line)
+ return true;
+ return false;
+ }
+ };
+
+ size_t DisplayFunctionSource(const SymbolContext &sc, SourceInfo &source_info,
+ CommandReturnObject &result) {
+ if (!source_info.IsValid()) {
+ source_info.function = sc.GetFunctionName();
+ source_info.line_entry = sc.GetFunctionStartLineEntry();
+ }
+
+ if (sc.function) {
+ Target *target = m_exe_ctx.GetTargetPtr();
+
+ FileSpec start_file;
+ uint32_t start_line;
+ uint32_t end_line;
+ FileSpec end_file;
+
+ if (sc.block == nullptr) {
+ // Not an inlined function
+ sc.function->GetStartLineSourceInfo(start_file, start_line);
+ if (start_line == 0) {
+ result.AppendErrorWithFormat("Could not find line information for "
+ "start of function: \"%s\".\n",
+ source_info.function.GetCString());
+ return 0;
+ }
+ sc.function->GetEndLineSourceInfo(end_file, end_line);
+ } else {
+ // We have an inlined function
+ start_file = source_info.line_entry.file;
+ start_line = source_info.line_entry.line;
+ end_line = start_line + m_options.num_lines;
+ }
+
+ // This is a little hacky, but the first line table entry for a function
+ // points to the "{" that starts the function block. It would be nice to
+ // actually get the function declaration in there too. So back up a bit,
+ // but not further than what you're going to display.
+ uint32_t extra_lines;
+ if (m_options.num_lines >= 10)
+ extra_lines = 5;
+ else
+ extra_lines = m_options.num_lines / 2;
+ uint32_t line_no;
+ if (start_line <= extra_lines)
+ line_no = 1;
+ else
+ line_no = start_line - extra_lines;
+
+ // For fun, if the function is shorter than the number of lines we're
+ // supposed to display, only display the function...
+ if (end_line != 0) {
+ if (m_options.num_lines > end_line - line_no)
+ m_options.num_lines = end_line - line_no + extra_lines;
+ }
+
+ m_breakpoint_locations.Clear();
+
+ if (m_options.show_bp_locs) {
+ const bool show_inlines = true;
+ m_breakpoint_locations.Reset(start_file, 0, show_inlines);
+ SearchFilterForUnconstrainedSearches target_search_filter(
+ m_exe_ctx.GetTargetSP());
+ target_search_filter.Search(m_breakpoint_locations);
+ }
+
+ result.AppendMessageWithFormat("File: %s\n",
+ start_file.GetPath().c_str());
+ // We don't care about the column here.
+ const uint32_t column = 0;
+ return target->GetSourceManager().DisplaySourceLinesWithLineNumbers(
+ start_file, line_no, column, 0, m_options.num_lines, "",
+ &result.GetOutputStream(), GetBreakpointLocations());
+ } else {
+ result.AppendErrorWithFormat(
+ "Could not find function info for: \"%s\".\n",
+ m_options.symbol_name.c_str());
+ }
+ return 0;
+ }
+
+ // From Jim: The FindMatchingFunctions / FindMatchingFunctionSymbols
+ // functions "take a possibly empty vector of strings which are names of
+ // modules, and run the two search functions on the subset of the full module
+ // list that matches the strings in the input vector". If we wanted to put
+ // these somewhere, there should probably be a module-filter-list that can be
+ // passed to the various ModuleList::Find* calls, which would either be a
+ // vector of string names or a ModuleSpecList.
+ void FindMatchingFunctions(Target *target, ConstString name,
+ SymbolContextList &sc_list) {
+ // Displaying the source for a symbol:
+ if (m_options.num_lines == 0)
+ m_options.num_lines = 10;
+
+ ModuleFunctionSearchOptions function_options;
+ function_options.include_symbols = true;
+ function_options.include_inlines = false;
+
+ const size_t num_modules = m_options.modules.size();
+ if (num_modules > 0) {
+ ModuleList matching_modules;
+ for (size_t i = 0; i < num_modules; ++i) {
+ FileSpec module_file_spec(m_options.modules[i]);
+ if (module_file_spec) {
+ ModuleSpec module_spec(module_file_spec);
+ matching_modules.Clear();
+ target->GetImages().FindModules(module_spec, matching_modules);
+
+ matching_modules.FindFunctions(name, eFunctionNameTypeAuto,
+ function_options, sc_list);
+ }
+ }
+ } else {
+ target->GetImages().FindFunctions(name, eFunctionNameTypeAuto,
+ function_options, sc_list);
+ }
+ }
+
+ void FindMatchingFunctionSymbols(Target *target, ConstString name,
+ SymbolContextList &sc_list) {
+ const size_t num_modules = m_options.modules.size();
+ if (num_modules > 0) {
+ ModuleList matching_modules;
+ for (size_t i = 0; i < num_modules; ++i) {
+ FileSpec module_file_spec(m_options.modules[i]);
+ if (module_file_spec) {
+ ModuleSpec module_spec(module_file_spec);
+ matching_modules.Clear();
+ target->GetImages().FindModules(module_spec, matching_modules);
+ matching_modules.FindFunctionSymbols(name, eFunctionNameTypeAuto,
+ sc_list);
+ }
+ }
+ } else {
+ target->GetImages().FindFunctionSymbols(name, eFunctionNameTypeAuto,
+ sc_list);
+ }
+ }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = m_exe_ctx.GetTargetPtr();
+
+ if (!m_options.symbol_name.empty()) {
+ SymbolContextList sc_list;
+ ConstString name(m_options.symbol_name.c_str());
+
+ // Displaying the source for a symbol. Search for function named name.
+ FindMatchingFunctions(target, name, sc_list);
+ size_t num_matches = sc_list.GetSize();
+ if (!num_matches) {
+ // If we didn't find any functions with that name, try searching for
+ // symbols that line up exactly with function addresses.
+ SymbolContextList sc_list_symbols;
+ FindMatchingFunctionSymbols(target, name, sc_list_symbols);
+ size_t num_symbol_matches = sc_list_symbols.GetSize();
+
+ for (size_t i = 0; i < num_symbol_matches; i++) {
+ SymbolContext sc;
+ sc_list_symbols.GetContextAtIndex(i, sc);
+ if (sc.symbol && sc.symbol->ValueIsAddress()) {
+ const Address &base_address = sc.symbol->GetAddressRef();
+ Function *function = base_address.CalculateSymbolContextFunction();
+ if (function) {
+ sc_list.Append(SymbolContext(function));
+ num_matches++;
+ break;
+ }
+ }
+ }
+ }
+
+ if (num_matches == 0) {
+ result.AppendErrorWithFormat("Could not find function named: \"%s\".\n",
+ m_options.symbol_name.c_str());
+ return false;
+ }
+
+ if (num_matches > 1) {
+ std::set<SourceInfo> source_match_set;
+
+ bool displayed_something = false;
+ for (size_t i = 0; i < num_matches; i++) {
+ SymbolContext sc;
+ sc_list.GetContextAtIndex(i, sc);
+ SourceInfo source_info(sc.GetFunctionName(),
+ sc.GetFunctionStartLineEntry());
+
+ if (source_info.IsValid()) {
+ if (source_match_set.find(source_info) == source_match_set.end()) {
+ source_match_set.insert(source_info);
+ if (DisplayFunctionSource(sc, source_info, result))
+ displayed_something = true;
+ }
+ }
+ }
+
+ if (displayed_something)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+ } else {
+ SymbolContext sc;
+ sc_list.GetContextAtIndex(0, sc);
+ SourceInfo source_info;
+
+ if (DisplayFunctionSource(sc, source_info, result)) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ return result.Succeeded();
+ } else if (m_options.address != LLDB_INVALID_ADDRESS) {
+ Address so_addr;
+ StreamString error_strm;
+ SymbolContextList sc_list;
+
+ if (target->GetSectionLoadList().IsEmpty()) {
+ // The target isn't loaded yet, we need to lookup the file address in
+ // all modules
+ const ModuleList &module_list = target->GetImages();
+ const size_t num_modules = module_list.GetSize();
+ for (size_t i = 0; i < num_modules; ++i) {
+ ModuleSP module_sp(module_list.GetModuleAtIndex(i));
+ if (module_sp &&
+ module_sp->ResolveFileAddress(m_options.address, so_addr)) {
+ SymbolContext sc;
+ sc.Clear(true);
+ if (module_sp->ResolveSymbolContextForAddress(
+ so_addr, eSymbolContextEverything, sc) &
+ eSymbolContextLineEntry)
+ sc_list.Append(sc);
+ }
+ }
+
+ if (sc_list.GetSize() == 0) {
+ result.AppendErrorWithFormat(
+ "no modules have source information for file address 0x%" PRIx64
+ ".\n",
+ m_options.address);
+ return false;
+ }
+ } else {
+ // The target has some things loaded, resolve this address to a compile
+ // unit + file + line and display
+ if (target->GetSectionLoadList().ResolveLoadAddress(m_options.address,
+ so_addr)) {
+ ModuleSP module_sp(so_addr.GetModule());
+ if (module_sp) {
+ SymbolContext sc;
+ sc.Clear(true);
+ if (module_sp->ResolveSymbolContextForAddress(
+ so_addr, eSymbolContextEverything, sc) &
+ eSymbolContextLineEntry) {
+ sc_list.Append(sc);
+ } else {
+ so_addr.Dump(&error_strm, nullptr,
+ Address::DumpStyleModuleWithFileAddress);
+ result.AppendErrorWithFormat("address resolves to %s, but there "
+ "is no line table information "
+ "available for this address.\n",
+ error_strm.GetData());
+ return false;
+ }
+ }
+ }
+
+ if (sc_list.GetSize() == 0) {
+ result.AppendErrorWithFormat(
+ "no modules contain load address 0x%" PRIx64 ".\n",
+ m_options.address);
+ return false;
+ }
+ }
+ uint32_t num_matches = sc_list.GetSize();
+ for (uint32_t i = 0; i < num_matches; ++i) {
+ SymbolContext sc;
+ sc_list.GetContextAtIndex(i, sc);
+ if (sc.comp_unit) {
+ if (m_options.show_bp_locs) {
+ m_breakpoint_locations.Clear();
+ const bool show_inlines = true;
+ m_breakpoint_locations.Reset(sc.comp_unit->GetPrimaryFile(), 0,
+ show_inlines);
+ SearchFilterForUnconstrainedSearches target_search_filter(
+ target->shared_from_this());
+ target_search_filter.Search(m_breakpoint_locations);
+ }
+
+ bool show_fullpaths = true;
+ bool show_module = true;
+ bool show_inlined_frames = true;
+ const bool show_function_arguments = true;
+ const bool show_function_name = true;
+ sc.DumpStopContext(&result.GetOutputStream(),
+ m_exe_ctx.GetBestExecutionContextScope(),
+ sc.line_entry.range.GetBaseAddress(),
+ show_fullpaths, show_module, show_inlined_frames,
+ show_function_arguments, show_function_name);
+ result.GetOutputStream().EOL();
+
+ if (m_options.num_lines == 0)
+ m_options.num_lines = 10;
+
+ size_t lines_to_back_up =
+ m_options.num_lines >= 10 ? 5 : m_options.num_lines / 2;
+
+ const uint32_t column =
+ (GetDebugger().GetStopShowColumn() != eStopShowColumnNone)
+ ? sc.line_entry.column
+ : 0;
+ target->GetSourceManager().DisplaySourceLinesWithLineNumbers(
+ sc.comp_unit->GetPrimaryFile(), sc.line_entry.line, column,
+ lines_to_back_up, m_options.num_lines - lines_to_back_up, "->",
+ &result.GetOutputStream(), GetBreakpointLocations());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+ }
+ } else if (m_options.file_name.empty()) {
+ // Last valid source manager context, or the current frame if no valid
+ // last context in source manager. One little trick here, if you type the
+ // exact same list command twice in a row, it is more likely because you
+ // typed it once, then typed it again
+ if (m_options.start_line == 0) {
+ if (target->GetSourceManager().DisplayMoreWithLineNumbers(
+ &result.GetOutputStream(), m_options.num_lines,
+ m_options.reverse, GetBreakpointLocations())) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+ } else {
+ if (m_options.num_lines == 0)
+ m_options.num_lines = 10;
+
+ if (m_options.show_bp_locs) {
+ SourceManager::FileSP last_file_sp(
+ target->GetSourceManager().GetLastFile());
+ if (last_file_sp) {
+ const bool show_inlines = true;
+ m_breakpoint_locations.Reset(last_file_sp->GetFileSpec(), 0,
+ show_inlines);
+ SearchFilterForUnconstrainedSearches target_search_filter(
+ target->shared_from_this());
+ target_search_filter.Search(m_breakpoint_locations);
+ }
+ } else
+ m_breakpoint_locations.Clear();
+
+ const uint32_t column = 0;
+ if (target->GetSourceManager()
+ .DisplaySourceLinesWithLineNumbersUsingLastFile(
+ m_options.start_line, // Line to display
+ m_options.num_lines, // Lines after line to
+ UINT32_MAX, // Don't mark "line"
+ column,
+ "", // Don't mark "line"
+ &result.GetOutputStream(), GetBreakpointLocations())) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+ }
+ } else {
+ const char *filename = m_options.file_name.c_str();
+
+ bool check_inlines = false;
+ SymbolContextList sc_list;
+ size_t num_matches = 0;
+
+ if (!m_options.modules.empty()) {
+ ModuleList matching_modules;
+ for (size_t i = 0, e = m_options.modules.size(); i < e; ++i) {
+ FileSpec module_file_spec(m_options.modules[i]);
+ if (module_file_spec) {
+ ModuleSpec module_spec(module_file_spec);
+ matching_modules.Clear();
+ target->GetImages().FindModules(module_spec, matching_modules);
+ num_matches += matching_modules.ResolveSymbolContextForFilePath(
+ filename, 0, check_inlines,
+ SymbolContextItem(eSymbolContextModule |
+ eSymbolContextCompUnit),
+ sc_list);
+ }
+ }
+ } else {
+ num_matches = target->GetImages().ResolveSymbolContextForFilePath(
+ filename, 0, check_inlines,
+ eSymbolContextModule | eSymbolContextCompUnit, sc_list);
+ }
+
+ if (num_matches == 0) {
+ result.AppendErrorWithFormat("Could not find source file \"%s\".\n",
+ m_options.file_name.c_str());
+ return false;
+ }
+
+ if (num_matches > 1) {
+ bool got_multiple = false;
+ CompileUnit *test_cu = nullptr;
+
+ for (unsigned i = 0; i < num_matches; i++) {
+ SymbolContext sc;
+ sc_list.GetContextAtIndex(i, sc);
+ if (sc.comp_unit) {
+ if (test_cu) {
+ if (test_cu != sc.comp_unit)
+ got_multiple = true;
+ break;
+ } else
+ test_cu = sc.comp_unit;
+ }
+ }
+ if (got_multiple) {
+ result.AppendErrorWithFormat(
+ "Multiple source files found matching: \"%s.\"\n",
+ m_options.file_name.c_str());
+ return false;
+ }
+ }
+
+ SymbolContext sc;
+ if (sc_list.GetContextAtIndex(0, sc)) {
+ if (sc.comp_unit) {
+ if (m_options.show_bp_locs) {
+ const bool show_inlines = true;
+ m_breakpoint_locations.Reset(sc.comp_unit->GetPrimaryFile(), 0,
+ show_inlines);
+ SearchFilterForUnconstrainedSearches target_search_filter(
+ target->shared_from_this());
+ target_search_filter.Search(m_breakpoint_locations);
+ } else
+ m_breakpoint_locations.Clear();
+
+ if (m_options.num_lines == 0)
+ m_options.num_lines = 10;
+ const uint32_t column = 0;
+ target->GetSourceManager().DisplaySourceLinesWithLineNumbers(
+ sc.comp_unit->GetPrimaryFile(), m_options.start_line, column, 0,
+ m_options.num_lines, "", &result.GetOutputStream(),
+ GetBreakpointLocations());
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("No comp unit found for: \"%s.\"\n",
+ m_options.file_name.c_str());
+ return false;
+ }
+ }
+ }
+ return result.Succeeded();
+ }
+
+ const SymbolContextList *GetBreakpointLocations() {
+ if (m_breakpoint_locations.GetFileLineMatches().GetSize() > 0)
+ return &m_breakpoint_locations.GetFileLineMatches();
+ return nullptr;
+ }
+
+ CommandOptions m_options;
+ FileLineResolver m_breakpoint_locations;
+ std::string m_reverse_name;
+};
+
+#pragma mark CommandObjectMultiwordSource
+// CommandObjectMultiwordSource
+
+CommandObjectMultiwordSource::CommandObjectMultiwordSource(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "source",
+ "Commands for examining "
+ "source code described by "
+ "debug information for the "
+ "current target process.",
+ "source <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("info",
+ CommandObjectSP(new CommandObjectSourceInfo(interpreter)));
+ LoadSubCommand("list",
+ CommandObjectSP(new CommandObjectSourceList(interpreter)));
+}
+
+CommandObjectMultiwordSource::~CommandObjectMultiwordSource() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectSource.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectSource.h
new file mode 100644
index 000000000000..f2117bd6ca3f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectSource.h
@@ -0,0 +1,28 @@
+//===-- CommandObjectSource.h.h -----------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTSOURCE_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTSOURCE_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+// CommandObjectMultiwordSource
+
+class CommandObjectMultiwordSource : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordSource(CommandInterpreter &interpreter);
+
+ ~CommandObjectMultiwordSource() override;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTSOURCE_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectStats.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectStats.cpp
new file mode 100644
index 000000000000..63aa36b39f4d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectStats.cpp
@@ -0,0 +1,133 @@
+//===-- CommandObjectStats.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectStats.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+class CommandObjectStatsEnable : public CommandObjectParsed {
+public:
+ CommandObjectStatsEnable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "enable",
+ "Enable statistics collection", nullptr,
+ eCommandProcessMustBePaused) {}
+
+ ~CommandObjectStatsEnable() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (DebuggerStats::GetCollectingStats()) {
+ result.AppendError("statistics already enabled");
+ return false;
+ }
+
+ DebuggerStats::SetCollectingStats(true);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+class CommandObjectStatsDisable : public CommandObjectParsed {
+public:
+ CommandObjectStatsDisable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "disable",
+ "Disable statistics collection", nullptr,
+ eCommandProcessMustBePaused) {}
+
+ ~CommandObjectStatsDisable() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (!DebuggerStats::GetCollectingStats()) {
+ result.AppendError("need to enable statistics before disabling them");
+ return false;
+ }
+
+ DebuggerStats::SetCollectingStats(false);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+#define LLDB_OPTIONS_statistics_dump
+#include "CommandOptions.inc"
+
+class CommandObjectStatsDump : public CommandObjectParsed {
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() { OptionParsingStarting(nullptr); }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'a':
+ m_all_targets = true;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_all_targets = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_statistics_dump_options);
+ }
+
+ bool m_all_targets = false;
+ };
+
+public:
+ CommandObjectStatsDump(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "statistics dump", "Dump metrics in JSON format",
+ "statistics dump [<options>]", eCommandRequiresTarget) {}
+
+ ~CommandObjectStatsDump() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = nullptr;
+ if (!m_options.m_all_targets)
+ target = m_exe_ctx.GetTargetPtr();
+
+ result.AppendMessageWithFormatv(
+ "{0:2}", DebuggerStats::ReportStatistics(GetDebugger(), target));
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+
+ CommandOptions m_options;
+};
+
+CommandObjectStats::CommandObjectStats(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "statistics",
+ "Print statistics about a debugging session",
+ "statistics <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("enable",
+ CommandObjectSP(new CommandObjectStatsEnable(interpreter)));
+ LoadSubCommand("disable",
+ CommandObjectSP(new CommandObjectStatsDisable(interpreter)));
+ LoadSubCommand("dump",
+ CommandObjectSP(new CommandObjectStatsDump(interpreter)));
+}
+
+CommandObjectStats::~CommandObjectStats() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectStats.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectStats.h
new file mode 100644
index 000000000000..c4cd6340f67b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectStats.h
@@ -0,0 +1,23 @@
+//===-- CommandObjectStats.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTSTATS_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTSTATS_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+class CommandObjectStats : public CommandObjectMultiword {
+public:
+ CommandObjectStats(CommandInterpreter &interpreter);
+
+ ~CommandObjectStats() override;
+};
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTSTATS_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectTarget.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectTarget.cpp
new file mode 100644
index 000000000000..51978878c8b9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectTarget.cpp
@@ -0,0 +1,5180 @@
+//===-- CommandObjectTarget.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectTarget.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/IOHandler.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/DataFormatters/ValueObjectPrinter.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionGroupArchitecture.h"
+#include "lldb/Interpreter/OptionGroupBoolean.h"
+#include "lldb/Interpreter/OptionGroupFile.h"
+#include "lldb/Interpreter/OptionGroupFormat.h"
+#include "lldb/Interpreter/OptionGroupPlatform.h"
+#include "lldb/Interpreter/OptionGroupPythonClassWithDict.h"
+#include "lldb/Interpreter/OptionGroupString.h"
+#include "lldb/Interpreter/OptionGroupUInt64.h"
+#include "lldb/Interpreter/OptionGroupUUID.h"
+#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
+#include "lldb/Interpreter/OptionGroupVariable.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/FuncUnwinders.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Symbol/LocateSymbolFile.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/Timer.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-private-enumerations.h"
+
+#include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "llvm/ADT/ScopeExit.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FormatAdapters.h"
+
+
+using namespace lldb;
+using namespace lldb_private;
+
+static void DumpTargetInfo(uint32_t target_idx, Target *target,
+ const char *prefix_cstr,
+ bool show_stopped_process_status, Stream &strm) {
+ const ArchSpec &target_arch = target->GetArchitecture();
+
+ Module *exe_module = target->GetExecutableModulePointer();
+ char exe_path[PATH_MAX];
+ bool exe_valid = false;
+ if (exe_module)
+ exe_valid = exe_module->GetFileSpec().GetPath(exe_path, sizeof(exe_path));
+
+ if (!exe_valid)
+ ::strcpy(exe_path, "<none>");
+
+ strm.Printf("%starget #%u: %s", prefix_cstr ? prefix_cstr : "", target_idx,
+ exe_path);
+
+ uint32_t properties = 0;
+ if (target_arch.IsValid()) {
+ strm.Printf("%sarch=", properties++ > 0 ? ", " : " ( ");
+ target_arch.DumpTriple(strm.AsRawOstream());
+ properties++;
+ }
+ PlatformSP platform_sp(target->GetPlatform());
+ if (platform_sp)
+ strm.Format("{0}platform={1}", properties++ > 0 ? ", " : " ( ",
+ platform_sp->GetName());
+
+ ProcessSP process_sp(target->GetProcessSP());
+ bool show_process_status = false;
+ if (process_sp) {
+ lldb::pid_t pid = process_sp->GetID();
+ StateType state = process_sp->GetState();
+ if (show_stopped_process_status)
+ show_process_status = StateIsStoppedState(state, true);
+ const char *state_cstr = StateAsCString(state);
+ if (pid != LLDB_INVALID_PROCESS_ID)
+ strm.Printf("%spid=%" PRIu64, properties++ > 0 ? ", " : " ( ", pid);
+ strm.Printf("%sstate=%s", properties++ > 0 ? ", " : " ( ", state_cstr);
+ }
+ if (properties > 0)
+ strm.PutCString(" )\n");
+ else
+ strm.EOL();
+ if (show_process_status) {
+ const bool only_threads_with_stop_reason = true;
+ const uint32_t start_frame = 0;
+ const uint32_t num_frames = 1;
+ const uint32_t num_frames_with_source = 1;
+ const bool stop_format = false;
+ process_sp->GetStatus(strm);
+ process_sp->GetThreadStatus(strm, only_threads_with_stop_reason,
+ start_frame, num_frames, num_frames_with_source,
+ stop_format);
+ }
+}
+
+static uint32_t DumpTargetList(TargetList &target_list,
+ bool show_stopped_process_status, Stream &strm) {
+ const uint32_t num_targets = target_list.GetNumTargets();
+ if (num_targets) {
+ TargetSP selected_target_sp(target_list.GetSelectedTarget());
+ strm.PutCString("Current targets:\n");
+ for (uint32_t i = 0; i < num_targets; ++i) {
+ TargetSP target_sp(target_list.GetTargetAtIndex(i));
+ if (target_sp) {
+ bool is_selected = target_sp.get() == selected_target_sp.get();
+ DumpTargetInfo(i, target_sp.get(), is_selected ? "* " : " ",
+ show_stopped_process_status, strm);
+ }
+ }
+ }
+ return num_targets;
+}
+
+// Note that the negation in the argument name causes a slightly confusing
+// mapping of the enum values.
+static constexpr OptionEnumValueElement g_dependents_enumeration[] = {
+ {
+ eLoadDependentsDefault,
+ "default",
+ "Only load dependents when the target is an executable.",
+ },
+ {
+ eLoadDependentsNo,
+ "true",
+ "Don't load dependents, even if the target is an executable.",
+ },
+ {
+ eLoadDependentsYes,
+ "false",
+ "Load dependents, even if the target is not an executable.",
+ },
+};
+
+#define LLDB_OPTIONS_target_dependents
+#include "CommandOptions.inc"
+
+class OptionGroupDependents : public OptionGroup {
+public:
+ OptionGroupDependents() = default;
+
+ ~OptionGroupDependents() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_target_dependents_options);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
+ ExecutionContext *execution_context) override {
+ Status error;
+
+ // For compatibility no value means don't load dependents.
+ if (option_value.empty()) {
+ m_load_dependent_files = eLoadDependentsNo;
+ return error;
+ }
+
+ const char short_option =
+ g_target_dependents_options[option_idx].short_option;
+ if (short_option == 'd') {
+ LoadDependentFiles tmp_load_dependents;
+ tmp_load_dependents = (LoadDependentFiles)OptionArgParser::ToOptionEnum(
+ option_value, g_target_dependents_options[option_idx].enum_values, 0,
+ error);
+ if (error.Success())
+ m_load_dependent_files = tmp_load_dependents;
+ } else {
+ error.SetErrorStringWithFormat("unrecognized short option '%c'",
+ short_option);
+ }
+
+ return error;
+ }
+
+ Status SetOptionValue(uint32_t, const char *, ExecutionContext *) = delete;
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_load_dependent_files = eLoadDependentsDefault;
+ }
+
+ LoadDependentFiles m_load_dependent_files;
+
+private:
+ OptionGroupDependents(const OptionGroupDependents &) = delete;
+ const OptionGroupDependents &
+ operator=(const OptionGroupDependents &) = delete;
+};
+
+#pragma mark CommandObjectTargetCreate
+
+class CommandObjectTargetCreate : public CommandObjectParsed {
+public:
+ CommandObjectTargetCreate(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "target create",
+ "Create a target using the argument as the main executable.",
+ nullptr),
+ m_platform_options(true), // Include the --platform option.
+ m_core_file(LLDB_OPT_SET_1, false, "core", 'c', 0, eArgTypeFilename,
+ "Fullpath to a core file to use for this target."),
+ m_symbol_file(LLDB_OPT_SET_1, false, "symfile", 's', 0,
+ eArgTypeFilename,
+ "Fullpath to a stand alone debug "
+ "symbols file for when debug symbols "
+ "are not in the executable."),
+ m_remote_file(
+ LLDB_OPT_SET_1, false, "remote-file", 'r', 0, eArgTypeFilename,
+ "Fullpath to the file on the remote host if debugging remotely.") {
+ CommandArgumentEntry arg;
+ CommandArgumentData file_arg;
+
+ // Define the first (and only) variant of this arg.
+ file_arg.arg_type = eArgTypeFilename;
+ file_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(file_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+
+ m_option_group.Append(&m_arch_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_platform_options, LLDB_OPT_SET_ALL, 1);
+ m_option_group.Append(&m_core_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_symbol_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_remote_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_add_dependents, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectTargetCreate() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+ request, nullptr);
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ FileSpec core_file(m_core_file.GetOptionValue().GetCurrentValue());
+ FileSpec remote_file(m_remote_file.GetOptionValue().GetCurrentValue());
+
+ if (core_file) {
+ auto file = FileSystem::Instance().Open(
+ core_file, lldb_private::File::eOpenOptionReadOnly);
+
+ if (!file) {
+ result.AppendErrorWithFormatv("Cannot open '{0}': {1}.",
+ core_file.GetPath(),
+ llvm::toString(file.takeError()));
+ return false;
+ }
+ }
+
+ if (argc == 1 || core_file || remote_file) {
+ FileSpec symfile(m_symbol_file.GetOptionValue().GetCurrentValue());
+ if (symfile) {
+ auto file = FileSystem::Instance().Open(
+ symfile, lldb_private::File::eOpenOptionReadOnly);
+
+ if (!file) {
+ result.AppendErrorWithFormatv("Cannot open '{0}': {1}.",
+ symfile.GetPath(),
+ llvm::toString(file.takeError()));
+ return false;
+ }
+ }
+
+ const char *file_path = command.GetArgumentAtIndex(0);
+ LLDB_SCOPED_TIMERF("(lldb) target create '%s'", file_path);
+
+ bool must_set_platform_path = false;
+
+ Debugger &debugger = GetDebugger();
+
+ TargetSP target_sp;
+ llvm::StringRef arch_cstr = m_arch_option.GetArchitectureName();
+ Status error(debugger.GetTargetList().CreateTarget(
+ debugger, file_path, arch_cstr,
+ m_add_dependents.m_load_dependent_files, &m_platform_options,
+ target_sp));
+
+ if (!target_sp) {
+ result.AppendError(error.AsCString());
+ return false;
+ }
+
+ auto on_error = llvm::make_scope_exit(
+ [&target_list = debugger.GetTargetList(), &target_sp]() {
+ target_list.DeleteTarget(target_sp);
+ });
+
+ // Only get the platform after we create the target because we might
+ // have switched platforms depending on what the arguments were to
+ // CreateTarget() we can't rely on the selected platform.
+
+ PlatformSP platform_sp = target_sp->GetPlatform();
+
+ FileSpec file_spec;
+ if (file_path) {
+ file_spec.SetFile(file_path, FileSpec::Style::native);
+ FileSystem::Instance().Resolve(file_spec);
+
+ // Try to resolve the exe based on PATH and/or platform-specific
+ // suffixes, but only if using the host platform.
+ if (platform_sp && platform_sp->IsHost() &&
+ !FileSystem::Instance().Exists(file_spec))
+ FileSystem::Instance().ResolveExecutableLocation(file_spec);
+ }
+
+ if (remote_file) {
+ if (platform_sp) {
+ // I have a remote file.. two possible cases
+ if (file_spec && FileSystem::Instance().Exists(file_spec)) {
+ // if the remote file does not exist, push it there
+ if (!platform_sp->GetFileExists(remote_file)) {
+ Status err = platform_sp->PutFile(file_spec, remote_file);
+ if (err.Fail()) {
+ result.AppendError(err.AsCString());
+ return false;
+ }
+ }
+ } else {
+ // there is no local file and we need one
+ // in order to make the remote ---> local transfer we need a
+ // platform
+ // TODO: if the user has passed in a --platform argument, use it
+ // to fetch the right platform
+ if (file_path) {
+ // copy the remote file to the local file
+ Status err = platform_sp->GetFile(remote_file, file_spec);
+ if (err.Fail()) {
+ result.AppendError(err.AsCString());
+ return false;
+ }
+ } else {
+ // If the remote file exists, we can debug reading that out of
+ // memory. If the platform is already connected to an lldb-server
+ // then we can at least check the file exists remotely. Otherwise
+ // we'll just have to trust that it will be there when we do
+ // process connect.
+ // I don't do this for the host platform because it seems odd to
+ // support supplying a remote file but no local file for a local
+ // debug session.
+ if (platform_sp->IsHost()) {
+ result.AppendError("Supply a local file, not a remote file, "
+ "when debugging on the host.");
+ return false;
+ }
+ if (platform_sp->IsConnected() && !platform_sp->GetFileExists(remote_file)) {
+ result.AppendError("remote --> local transfer without local "
+ "path is not implemented yet");
+ return false;
+ }
+ // Since there's only a remote file, we need to set the executable
+ // file spec to the remote one.
+ ProcessLaunchInfo launch_info = target_sp->GetProcessLaunchInfo();
+ launch_info.SetExecutableFile(FileSpec(remote_file), true);
+ target_sp->SetProcessLaunchInfo(launch_info);
+ }
+ }
+ } else {
+ result.AppendError("no platform found for target");
+ return false;
+ }
+ }
+
+ if (symfile || remote_file) {
+ ModuleSP module_sp(target_sp->GetExecutableModule());
+ if (module_sp) {
+ if (symfile)
+ module_sp->SetSymbolFileFileSpec(symfile);
+ if (remote_file) {
+ std::string remote_path = remote_file.GetPath();
+ target_sp->SetArg0(remote_path.c_str());
+ module_sp->SetPlatformFileSpec(remote_file);
+ }
+ }
+ }
+
+ if (must_set_platform_path) {
+ ModuleSpec main_module_spec(file_spec);
+ ModuleSP module_sp =
+ target_sp->GetOrCreateModule(main_module_spec, true /* notify */);
+ if (module_sp)
+ module_sp->SetPlatformFileSpec(remote_file);
+ }
+
+ if (core_file) {
+ FileSpec core_file_dir;
+ core_file_dir.GetDirectory() = core_file.GetDirectory();
+ target_sp->AppendExecutableSearchPaths(core_file_dir);
+
+ ProcessSP process_sp(target_sp->CreateProcess(
+ GetDebugger().GetListener(), llvm::StringRef(), &core_file, false));
+
+ if (process_sp) {
+ // Seems weird that we Launch a core file, but that is what we
+ // do!
+ error = process_sp->LoadCore();
+
+ if (error.Fail()) {
+ result.AppendError(
+ error.AsCString("can't find plug-in for core file"));
+ return false;
+ } else {
+ result.AppendMessageWithFormatv(
+ "Core file '{0}' ({1}) was loaded.\n", core_file.GetPath(),
+ target_sp->GetArchitecture().GetArchitectureName());
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ on_error.release();
+ }
+ } else {
+ result.AppendErrorWithFormatv(
+ "Unable to find process plug-in for core file '{0}'\n",
+ core_file.GetPath());
+ }
+ } else {
+ result.AppendMessageWithFormat(
+ "Current executable set to '%s' (%s).\n",
+ file_spec.GetPath().c_str(),
+ target_sp->GetArchitecture().GetArchitectureName());
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ on_error.release();
+ }
+ } else {
+ result.AppendErrorWithFormat("'%s' takes exactly one executable path "
+ "argument, or use the --core option.\n",
+ m_cmd_name.c_str());
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ OptionGroupOptions m_option_group;
+ OptionGroupArchitecture m_arch_option;
+ OptionGroupPlatform m_platform_options;
+ OptionGroupFile m_core_file;
+ OptionGroupFile m_symbol_file;
+ OptionGroupFile m_remote_file;
+ OptionGroupDependents m_add_dependents;
+};
+
+#pragma mark CommandObjectTargetList
+
+class CommandObjectTargetList : public CommandObjectParsed {
+public:
+ CommandObjectTargetList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "target list",
+ "List all current targets in the current debug session.", nullptr) {
+ }
+
+ ~CommandObjectTargetList() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Stream &strm = result.GetOutputStream();
+
+ bool show_stopped_process_status = false;
+ if (DumpTargetList(GetDebugger().GetTargetList(),
+ show_stopped_process_status, strm) == 0) {
+ strm.PutCString("No targets.\n");
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectTargetSelect
+
+class CommandObjectTargetSelect : public CommandObjectParsed {
+public:
+ CommandObjectTargetSelect(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "target select",
+ "Select a target as the current target by target index.", nullptr) {
+ CommandArgumentData target_arg{eArgTypeTargetID, eArgRepeatPlain};
+ m_arguments.push_back({target_arg});
+ }
+
+ ~CommandObjectTargetSelect() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ if (args.GetArgumentCount() == 1) {
+ const char *target_idx_arg = args.GetArgumentAtIndex(0);
+ uint32_t target_idx;
+ if (llvm::to_integer(target_idx_arg, target_idx)) {
+ TargetList &target_list = GetDebugger().GetTargetList();
+ const uint32_t num_targets = target_list.GetNumTargets();
+ if (target_idx < num_targets) {
+ target_list.SetSelectedTarget(target_idx);
+ Stream &strm = result.GetOutputStream();
+ bool show_stopped_process_status = false;
+ DumpTargetList(target_list, show_stopped_process_status, strm);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ if (num_targets > 0) {
+ result.AppendErrorWithFormat(
+ "index %u is out of range, valid target indexes are 0 - %u\n",
+ target_idx, num_targets - 1);
+ } else {
+ result.AppendErrorWithFormat(
+ "index %u is out of range since there are no active targets\n",
+ target_idx);
+ }
+ }
+ } else {
+ result.AppendErrorWithFormat("invalid index string value '%s'\n",
+ target_idx_arg);
+ }
+ } else {
+ result.AppendError(
+ "'target select' takes a single argument: a target index\n");
+ }
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectTargetDelete
+
+class CommandObjectTargetDelete : public CommandObjectParsed {
+public:
+ CommandObjectTargetDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "target delete",
+ "Delete one or more targets by target index.",
+ nullptr),
+ m_all_option(LLDB_OPT_SET_1, false, "all", 'a', "Delete all targets.",
+ false, true),
+ m_cleanup_option(
+ LLDB_OPT_SET_1, false, "clean", 'c',
+ "Perform extra cleanup to minimize memory consumption after "
+ "deleting the target. "
+ "By default, LLDB will keep in memory any modules previously "
+ "loaded by the target as well "
+ "as all of its debug info. Specifying --clean will unload all of "
+ "these shared modules and "
+ "cause them to be reparsed again the next time the target is run",
+ false, true) {
+ m_option_group.Append(&m_all_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_cleanup_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Finalize();
+ CommandArgumentData target_arg{eArgTypeTargetID, eArgRepeatStar};
+ m_arguments.push_back({target_arg});
+ }
+
+ ~CommandObjectTargetDelete() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ const size_t argc = args.GetArgumentCount();
+ std::vector<TargetSP> delete_target_list;
+ TargetList &target_list = GetDebugger().GetTargetList();
+ TargetSP target_sp;
+
+ if (m_all_option.GetOptionValue()) {
+ for (int i = 0; i < target_list.GetNumTargets(); ++i)
+ delete_target_list.push_back(target_list.GetTargetAtIndex(i));
+ } else if (argc > 0) {
+ const uint32_t num_targets = target_list.GetNumTargets();
+ // Bail out if don't have any targets.
+ if (num_targets == 0) {
+ result.AppendError("no targets to delete");
+ return false;
+ }
+
+ for (auto &entry : args.entries()) {
+ uint32_t target_idx;
+ if (entry.ref().getAsInteger(0, target_idx)) {
+ result.AppendErrorWithFormat("invalid target index '%s'\n",
+ entry.c_str());
+ return false;
+ }
+ if (target_idx < num_targets) {
+ target_sp = target_list.GetTargetAtIndex(target_idx);
+ if (target_sp) {
+ delete_target_list.push_back(target_sp);
+ continue;
+ }
+ }
+ if (num_targets > 1)
+ result.AppendErrorWithFormat("target index %u is out of range, valid "
+ "target indexes are 0 - %u\n",
+ target_idx, num_targets - 1);
+ else
+ result.AppendErrorWithFormat(
+ "target index %u is out of range, the only valid index is 0\n",
+ target_idx);
+
+ return false;
+ }
+ } else {
+ target_sp = target_list.GetSelectedTarget();
+ if (!target_sp) {
+ result.AppendErrorWithFormat("no target is currently selected\n");
+ return false;
+ }
+ delete_target_list.push_back(target_sp);
+ }
+
+ const size_t num_targets_to_delete = delete_target_list.size();
+ for (size_t idx = 0; idx < num_targets_to_delete; ++idx) {
+ target_sp = delete_target_list[idx];
+ target_list.DeleteTarget(target_sp);
+ target_sp->Destroy();
+ }
+ // If "--clean" was specified, prune any orphaned shared modules from the
+ // global shared module list
+ if (m_cleanup_option.GetOptionValue()) {
+ const bool mandatory = true;
+ ModuleList::RemoveOrphanSharedModules(mandatory);
+ }
+ result.GetOutputStream().Printf("%u targets deleted.\n",
+ (uint32_t)num_targets_to_delete);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ return true;
+ }
+
+ OptionGroupOptions m_option_group;
+ OptionGroupBoolean m_all_option;
+ OptionGroupBoolean m_cleanup_option;
+};
+
+class CommandObjectTargetShowLaunchEnvironment : public CommandObjectParsed {
+public:
+ CommandObjectTargetShowLaunchEnvironment(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "target show-launch-environment",
+ "Shows the environment being passed to the process when launched, "
+ "taking info account 3 settings: target.env-vars, "
+ "target.inherit-env and target.unset-env-vars.",
+ nullptr, eCommandRequiresTarget) {}
+
+ ~CommandObjectTargetShowLaunchEnvironment() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Target *target = m_exe_ctx.GetTargetPtr();
+ Environment env = target->GetEnvironment();
+
+ std::vector<Environment::value_type *> env_vector;
+ env_vector.reserve(env.size());
+ for (auto &KV : env)
+ env_vector.push_back(&KV);
+ std::sort(env_vector.begin(), env_vector.end(),
+ [](Environment::value_type *a, Environment::value_type *b) {
+ return a->first() < b->first();
+ });
+
+ auto &strm = result.GetOutputStream();
+ for (auto &KV : env_vector)
+ strm.Format("{0}={1}\n", KV->first(), KV->second);
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectTargetVariable
+
+class CommandObjectTargetVariable : public CommandObjectParsed {
+ static const uint32_t SHORT_OPTION_FILE = 0x66696c65; // 'file'
+ static const uint32_t SHORT_OPTION_SHLB = 0x73686c62; // 'shlb'
+
+public:
+ CommandObjectTargetVariable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "target variable",
+ "Read global variables for the current target, "
+ "before or while running a process.",
+ nullptr, eCommandRequiresTarget),
+ m_option_variable(false), // Don't include frame options
+ m_option_format(eFormatDefault),
+ m_option_compile_units(LLDB_OPT_SET_1, false, "file", SHORT_OPTION_FILE,
+ 0, eArgTypeFilename,
+ "A basename or fullpath to a file that contains "
+ "global variables. This option can be "
+ "specified multiple times."),
+ m_option_shared_libraries(
+ LLDB_OPT_SET_1, false, "shlib", SHORT_OPTION_SHLB, 0,
+ eArgTypeFilename,
+ "A basename or fullpath to a shared library to use in the search "
+ "for global "
+ "variables. This option can be specified multiple times.") {
+ CommandArgumentEntry arg;
+ CommandArgumentData var_name_arg;
+
+ // Define the first (and only) variant of this arg.
+ var_name_arg.arg_type = eArgTypeVarName;
+ var_name_arg.arg_repetition = eArgRepeatPlus;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(var_name_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+
+ m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_option_variable, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_option_format,
+ OptionGroupFormat::OPTION_GROUP_FORMAT |
+ OptionGroupFormat::OPTION_GROUP_GDB_FMT,
+ LLDB_OPT_SET_1);
+ m_option_group.Append(&m_option_compile_units, LLDB_OPT_SET_ALL,
+ LLDB_OPT_SET_1);
+ m_option_group.Append(&m_option_shared_libraries, LLDB_OPT_SET_ALL,
+ LLDB_OPT_SET_1);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectTargetVariable() override = default;
+
+ void DumpValueObject(Stream &s, VariableSP &var_sp, ValueObjectSP &valobj_sp,
+ const char *root_name) {
+ DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions());
+
+ if (!valobj_sp->GetTargetSP()->GetDisplayRuntimeSupportValues() &&
+ valobj_sp->IsRuntimeSupportValue())
+ return;
+
+ switch (var_sp->GetScope()) {
+ case eValueTypeVariableGlobal:
+ if (m_option_variable.show_scope)
+ s.PutCString("GLOBAL: ");
+ break;
+
+ case eValueTypeVariableStatic:
+ if (m_option_variable.show_scope)
+ s.PutCString("STATIC: ");
+ break;
+
+ case eValueTypeVariableArgument:
+ if (m_option_variable.show_scope)
+ s.PutCString(" ARG: ");
+ break;
+
+ case eValueTypeVariableLocal:
+ if (m_option_variable.show_scope)
+ s.PutCString(" LOCAL: ");
+ break;
+
+ case eValueTypeVariableThreadLocal:
+ if (m_option_variable.show_scope)
+ s.PutCString("THREAD: ");
+ break;
+
+ default:
+ break;
+ }
+
+ if (m_option_variable.show_decl) {
+ bool show_fullpaths = false;
+ bool show_module = true;
+ if (var_sp->DumpDeclaration(&s, show_fullpaths, show_module))
+ s.PutCString(": ");
+ }
+
+ const Format format = m_option_format.GetFormat();
+ if (format != eFormatDefault)
+ options.SetFormat(format);
+
+ options.SetRootValueObjectName(root_name);
+
+ valobj_sp->Dump(s, options);
+ }
+
+ static size_t GetVariableCallback(void *baton, const char *name,
+ VariableList &variable_list) {
+ size_t old_size = variable_list.GetSize();
+ Target *target = static_cast<Target *>(baton);
+ if (target)
+ target->GetImages().FindGlobalVariables(ConstString(name), UINT32_MAX,
+ variable_list);
+ return variable_list.GetSize() - old_size;
+ }
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ void DumpGlobalVariableList(const ExecutionContext &exe_ctx,
+ const SymbolContext &sc,
+ const VariableList &variable_list, Stream &s) {
+ if (variable_list.Empty())
+ return;
+ if (sc.module_sp) {
+ if (sc.comp_unit) {
+ s.Format("Global variables for {0} in {1}:\n",
+ sc.comp_unit->GetPrimaryFile(), sc.module_sp->GetFileSpec());
+ } else {
+ s.Printf("Global variables for %s\n",
+ sc.module_sp->GetFileSpec().GetPath().c_str());
+ }
+ } else if (sc.comp_unit) {
+ s.Format("Global variables for {0}\n", sc.comp_unit->GetPrimaryFile());
+ }
+
+ for (VariableSP var_sp : variable_list) {
+ if (!var_sp)
+ continue;
+ ValueObjectSP valobj_sp(ValueObjectVariable::Create(
+ exe_ctx.GetBestExecutionContextScope(), var_sp));
+
+ if (valobj_sp)
+ DumpValueObject(s, var_sp, valobj_sp, var_sp->GetName().GetCString());
+ }
+ }
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Target *target = m_exe_ctx.GetTargetPtr();
+ const size_t argc = args.GetArgumentCount();
+ Stream &s = result.GetOutputStream();
+
+ if (argc > 0) {
+ for (const Args::ArgEntry &arg : args) {
+ VariableList variable_list;
+ ValueObjectList valobj_list;
+
+ size_t matches = 0;
+ bool use_var_name = false;
+ if (m_option_variable.use_regex) {
+ RegularExpression regex(arg.ref());
+ if (!regex.IsValid()) {
+ result.GetErrorStream().Printf(
+ "error: invalid regular expression: '%s'\n", arg.c_str());
+ return false;
+ }
+ use_var_name = true;
+ target->GetImages().FindGlobalVariables(regex, UINT32_MAX,
+ variable_list);
+ matches = variable_list.GetSize();
+ } else {
+ Status error(Variable::GetValuesForVariableExpressionPath(
+ arg.c_str(), m_exe_ctx.GetBestExecutionContextScope(),
+ GetVariableCallback, target, variable_list, valobj_list));
+ matches = variable_list.GetSize();
+ }
+
+ if (matches == 0) {
+ result.AppendErrorWithFormat("can't find global variable '%s'",
+ arg.c_str());
+ return false;
+ } else {
+ for (uint32_t global_idx = 0; global_idx < matches; ++global_idx) {
+ VariableSP var_sp(variable_list.GetVariableAtIndex(global_idx));
+ if (var_sp) {
+ ValueObjectSP valobj_sp(
+ valobj_list.GetValueObjectAtIndex(global_idx));
+ if (!valobj_sp)
+ valobj_sp = ValueObjectVariable::Create(
+ m_exe_ctx.GetBestExecutionContextScope(), var_sp);
+
+ if (valobj_sp)
+ DumpValueObject(s, var_sp, valobj_sp,
+ use_var_name ? var_sp->GetName().GetCString()
+ : arg.c_str());
+ }
+ }
+ }
+ }
+ } else {
+ const FileSpecList &compile_units =
+ m_option_compile_units.GetOptionValue().GetCurrentValue();
+ const FileSpecList &shlibs =
+ m_option_shared_libraries.GetOptionValue().GetCurrentValue();
+ SymbolContextList sc_list;
+ const size_t num_compile_units = compile_units.GetSize();
+ const size_t num_shlibs = shlibs.GetSize();
+ if (num_compile_units == 0 && num_shlibs == 0) {
+ bool success = false;
+ StackFrame *frame = m_exe_ctx.GetFramePtr();
+ CompileUnit *comp_unit = nullptr;
+ if (frame) {
+ SymbolContext sc = frame->GetSymbolContext(eSymbolContextCompUnit);
+ comp_unit = sc.comp_unit;
+ if (sc.comp_unit) {
+ const bool can_create = true;
+ VariableListSP comp_unit_varlist_sp(
+ sc.comp_unit->GetVariableList(can_create));
+ if (comp_unit_varlist_sp) {
+ size_t count = comp_unit_varlist_sp->GetSize();
+ if (count > 0) {
+ DumpGlobalVariableList(m_exe_ctx, sc, *comp_unit_varlist_sp, s);
+ success = true;
+ }
+ }
+ }
+ }
+ if (!success) {
+ if (frame) {
+ if (comp_unit)
+ result.AppendErrorWithFormatv(
+ "no global variables in current compile unit: {0}\n",
+ comp_unit->GetPrimaryFile());
+ else
+ result.AppendErrorWithFormat(
+ "no debug information for frame %u\n",
+ frame->GetFrameIndex());
+ } else
+ result.AppendError("'target variable' takes one or more global "
+ "variable names as arguments\n");
+ }
+ } else {
+ SymbolContextList sc_list;
+ // We have one or more compile unit or shlib
+ if (num_shlibs > 0) {
+ for (size_t shlib_idx = 0; shlib_idx < num_shlibs; ++shlib_idx) {
+ const FileSpec module_file(shlibs.GetFileSpecAtIndex(shlib_idx));
+ ModuleSpec module_spec(module_file);
+
+ ModuleSP module_sp(
+ target->GetImages().FindFirstModule(module_spec));
+ if (module_sp) {
+ if (num_compile_units > 0) {
+ for (size_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx)
+ module_sp->FindCompileUnits(
+ compile_units.GetFileSpecAtIndex(cu_idx), sc_list);
+ } else {
+ SymbolContext sc;
+ sc.module_sp = module_sp;
+ sc_list.Append(sc);
+ }
+ } else {
+ // Didn't find matching shlib/module in target...
+ result.AppendErrorWithFormat(
+ "target doesn't contain the specified shared library: %s\n",
+ module_file.GetPath().c_str());
+ }
+ }
+ } else {
+ // No shared libraries, we just want to find globals for the compile
+ // units files that were specified
+ for (size_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx)
+ target->GetImages().FindCompileUnits(
+ compile_units.GetFileSpecAtIndex(cu_idx), sc_list);
+ }
+
+ const uint32_t num_scs = sc_list.GetSize();
+ if (num_scs > 0) {
+ SymbolContext sc;
+ for (uint32_t sc_idx = 0; sc_idx < num_scs; ++sc_idx) {
+ if (sc_list.GetContextAtIndex(sc_idx, sc)) {
+ if (sc.comp_unit) {
+ const bool can_create = true;
+ VariableListSP comp_unit_varlist_sp(
+ sc.comp_unit->GetVariableList(can_create));
+ if (comp_unit_varlist_sp)
+ DumpGlobalVariableList(m_exe_ctx, sc, *comp_unit_varlist_sp,
+ s);
+ } else if (sc.module_sp) {
+ // Get all global variables for this module
+ lldb_private::RegularExpression all_globals_regex(
+ llvm::StringRef(
+ ".")); // Any global with at least one character
+ VariableList variable_list;
+ sc.module_sp->FindGlobalVariables(all_globals_regex, UINT32_MAX,
+ variable_list);
+ DumpGlobalVariableList(m_exe_ctx, sc, variable_list, s);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ m_interpreter.PrintWarningsIfNecessary(result.GetOutputStream(),
+ m_cmd_name);
+
+ return result.Succeeded();
+ }
+
+ OptionGroupOptions m_option_group;
+ OptionGroupVariable m_option_variable;
+ OptionGroupFormat m_option_format;
+ OptionGroupFileList m_option_compile_units;
+ OptionGroupFileList m_option_shared_libraries;
+ OptionGroupValueObjectDisplay m_varobj_options;
+};
+
+#pragma mark CommandObjectTargetModulesSearchPathsAdd
+
+class CommandObjectTargetModulesSearchPathsAdd : public CommandObjectParsed {
+public:
+ CommandObjectTargetModulesSearchPathsAdd(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "target modules search-paths add",
+ "Add new image search paths substitution pairs to "
+ "the current target.",
+ nullptr, eCommandRequiresTarget) {
+ CommandArgumentEntry arg;
+ CommandArgumentData old_prefix_arg;
+ CommandArgumentData new_prefix_arg;
+
+ // Define the first variant of this arg pair.
+ old_prefix_arg.arg_type = eArgTypeOldPathPrefix;
+ old_prefix_arg.arg_repetition = eArgRepeatPairPlus;
+
+ // Define the first variant of this arg pair.
+ new_prefix_arg.arg_type = eArgTypeNewPathPrefix;
+ new_prefix_arg.arg_repetition = eArgRepeatPairPlus;
+
+ // There are two required arguments that must always occur together, i.e.
+ // an argument "pair". Because they must always occur together, they are
+ // treated as two variants of one argument rather than two independent
+ // arguments. Push them both into the first argument position for
+ // m_arguments...
+
+ arg.push_back(old_prefix_arg);
+ arg.push_back(new_prefix_arg);
+
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectTargetModulesSearchPathsAdd() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+ const size_t argc = command.GetArgumentCount();
+ if (argc & 1) {
+ result.AppendError("add requires an even number of arguments\n");
+ } else {
+ for (size_t i = 0; i < argc; i += 2) {
+ const char *from = command.GetArgumentAtIndex(i);
+ const char *to = command.GetArgumentAtIndex(i + 1);
+
+ if (from[0] && to[0]) {
+ Log *log = GetLog(LLDBLog::Host);
+ if (log) {
+ LLDB_LOGF(log,
+ "target modules search path adding ImageSearchPath "
+ "pair: '%s' -> '%s'",
+ from, to);
+ }
+ bool last_pair = ((argc - i) == 2);
+ target->GetImageSearchPathList().Append(
+ from, to, last_pair); // Notify if this is the last pair
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ if (from[0])
+ result.AppendError("<path-prefix> can't be empty\n");
+ else
+ result.AppendError("<new-path-prefix> can't be empty\n");
+ }
+ }
+ }
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectTargetModulesSearchPathsClear
+
+class CommandObjectTargetModulesSearchPathsClear : public CommandObjectParsed {
+public:
+ CommandObjectTargetModulesSearchPathsClear(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "target modules search-paths clear",
+ "Clear all current image search path substitution "
+ "pairs from the current target.",
+ "target modules search-paths clear",
+ eCommandRequiresTarget) {}
+
+ ~CommandObjectTargetModulesSearchPathsClear() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+ bool notify = true;
+ target->GetImageSearchPathList().Clear(notify);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectTargetModulesSearchPathsInsert
+
+class CommandObjectTargetModulesSearchPathsInsert : public CommandObjectParsed {
+public:
+ CommandObjectTargetModulesSearchPathsInsert(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "target modules search-paths insert",
+ "Insert a new image search path substitution pair "
+ "into the current target at the specified index.",
+ nullptr, eCommandRequiresTarget) {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentData index_arg;
+ CommandArgumentData old_prefix_arg;
+ CommandArgumentData new_prefix_arg;
+
+ // Define the first and only variant of this arg.
+ index_arg.arg_type = eArgTypeIndex;
+ index_arg.arg_repetition = eArgRepeatPlain;
+
+ // Put the one and only variant into the first arg for m_arguments:
+ arg1.push_back(index_arg);
+
+ // Define the first variant of this arg pair.
+ old_prefix_arg.arg_type = eArgTypeOldPathPrefix;
+ old_prefix_arg.arg_repetition = eArgRepeatPairPlus;
+
+ // Define the first variant of this arg pair.
+ new_prefix_arg.arg_type = eArgTypeNewPathPrefix;
+ new_prefix_arg.arg_repetition = eArgRepeatPairPlus;
+
+ // There are two required arguments that must always occur together, i.e.
+ // an argument "pair". Because they must always occur together, they are
+ // treated as two variants of one argument rather than two independent
+ // arguments. Push them both into the same argument position for
+ // m_arguments...
+
+ arg2.push_back(old_prefix_arg);
+ arg2.push_back(new_prefix_arg);
+
+ // Add arguments to m_arguments.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+ }
+
+ ~CommandObjectTargetModulesSearchPathsInsert() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (!m_exe_ctx.HasTargetScope() || request.GetCursorIndex() != 0)
+ return;
+
+ Target *target = m_exe_ctx.GetTargetPtr();
+ const PathMappingList &list = target->GetImageSearchPathList();
+ const size_t num = list.GetSize();
+ ConstString old_path, new_path;
+ for (size_t i = 0; i < num; ++i) {
+ if (!list.GetPathsAtIndex(i, old_path, new_path))
+ break;
+ StreamString strm;
+ strm << old_path << " -> " << new_path;
+ request.TryCompleteCurrentArg(std::to_string(i), strm.GetString());
+ }
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+ size_t argc = command.GetArgumentCount();
+ // check for at least 3 arguments and an odd number of parameters
+ if (argc >= 3 && argc & 1) {
+ uint32_t insert_idx;
+
+ if (!llvm::to_integer(command.GetArgumentAtIndex(0), insert_idx)) {
+ result.AppendErrorWithFormat(
+ "<index> parameter is not an integer: '%s'.\n",
+ command.GetArgumentAtIndex(0));
+ return result.Succeeded();
+ }
+
+ // shift off the index
+ command.Shift();
+ argc = command.GetArgumentCount();
+
+ for (uint32_t i = 0; i < argc; i += 2, ++insert_idx) {
+ const char *from = command.GetArgumentAtIndex(i);
+ const char *to = command.GetArgumentAtIndex(i + 1);
+
+ if (from[0] && to[0]) {
+ bool last_pair = ((argc - i) == 2);
+ target->GetImageSearchPathList().Insert(from, to, insert_idx,
+ last_pair);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ if (from[0])
+ result.AppendError("<path-prefix> can't be empty\n");
+ else
+ result.AppendError("<new-path-prefix> can't be empty\n");
+ return false;
+ }
+ }
+ } else {
+ result.AppendError("insert requires at least three arguments\n");
+ return result.Succeeded();
+ }
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectTargetModulesSearchPathsList
+
+class CommandObjectTargetModulesSearchPathsList : public CommandObjectParsed {
+public:
+ CommandObjectTargetModulesSearchPathsList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "target modules search-paths list",
+ "List all current image search path substitution "
+ "pairs in the current target.",
+ "target modules search-paths list",
+ eCommandRequiresTarget) {}
+
+ ~CommandObjectTargetModulesSearchPathsList() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+
+ target->GetImageSearchPathList().Dump(&result.GetOutputStream());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectTargetModulesSearchPathsQuery
+
+class CommandObjectTargetModulesSearchPathsQuery : public CommandObjectParsed {
+public:
+ CommandObjectTargetModulesSearchPathsQuery(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "target modules search-paths query",
+ "Transform a path using the first applicable image search path.",
+ nullptr, eCommandRequiresTarget) {
+ CommandArgumentEntry arg;
+ CommandArgumentData path_arg;
+
+ // Define the first (and only) variant of this arg.
+ path_arg.arg_type = eArgTypeDirectoryName;
+ path_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(path_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectTargetModulesSearchPathsQuery() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+ if (command.GetArgumentCount() != 1) {
+ result.AppendError("query requires one argument\n");
+ return result.Succeeded();
+ }
+
+ ConstString orig(command.GetArgumentAtIndex(0));
+ ConstString transformed;
+ if (target->GetImageSearchPathList().RemapPath(orig, transformed))
+ result.GetOutputStream().Printf("%s\n", transformed.GetCString());
+ else
+ result.GetOutputStream().Printf("%s\n", orig.GetCString());
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+// Static Helper functions
+static void DumpModuleArchitecture(Stream &strm, Module *module,
+ bool full_triple, uint32_t width) {
+ if (module) {
+ StreamString arch_strm;
+
+ if (full_triple)
+ module->GetArchitecture().DumpTriple(arch_strm.AsRawOstream());
+ else
+ arch_strm.PutCString(module->GetArchitecture().GetArchitectureName());
+ std::string arch_str = std::string(arch_strm.GetString());
+
+ if (width)
+ strm.Printf("%-*s", width, arch_str.c_str());
+ else
+ strm.PutCString(arch_str);
+ }
+}
+
+static void DumpModuleUUID(Stream &strm, Module *module) {
+ if (module && module->GetUUID().IsValid())
+ module->GetUUID().Dump(&strm);
+ else
+ strm.PutCString(" ");
+}
+
+static uint32_t DumpCompileUnitLineTable(CommandInterpreter &interpreter,
+ Stream &strm, Module *module,
+ const FileSpec &file_spec,
+ lldb::DescriptionLevel desc_level) {
+ uint32_t num_matches = 0;
+ if (module) {
+ SymbolContextList sc_list;
+ num_matches = module->ResolveSymbolContextsForFileSpec(
+ file_spec, 0, false, eSymbolContextCompUnit, sc_list);
+
+ for (uint32_t i = 0; i < num_matches; ++i) {
+ SymbolContext sc;
+ if (sc_list.GetContextAtIndex(i, sc)) {
+ if (i > 0)
+ strm << "\n\n";
+
+ strm << "Line table for " << sc.comp_unit->GetPrimaryFile() << " in `"
+ << module->GetFileSpec().GetFilename() << "\n";
+ LineTable *line_table = sc.comp_unit->GetLineTable();
+ if (line_table)
+ line_table->GetDescription(
+ &strm, interpreter.GetExecutionContext().GetTargetPtr(),
+ desc_level);
+ else
+ strm << "No line table";
+ }
+ }
+ }
+ return num_matches;
+}
+
+static void DumpFullpath(Stream &strm, const FileSpec *file_spec_ptr,
+ uint32_t width) {
+ if (file_spec_ptr) {
+ if (width > 0) {
+ std::string fullpath = file_spec_ptr->GetPath();
+ strm.Printf("%-*s", width, fullpath.c_str());
+ return;
+ } else {
+ file_spec_ptr->Dump(strm.AsRawOstream());
+ return;
+ }
+ }
+ // Keep the width spacing correct if things go wrong...
+ if (width > 0)
+ strm.Printf("%-*s", width, "");
+}
+
+static void DumpDirectory(Stream &strm, const FileSpec *file_spec_ptr,
+ uint32_t width) {
+ if (file_spec_ptr) {
+ if (width > 0)
+ strm.Printf("%-*s", width, file_spec_ptr->GetDirectory().AsCString(""));
+ else
+ file_spec_ptr->GetDirectory().Dump(&strm);
+ return;
+ }
+ // Keep the width spacing correct if things go wrong...
+ if (width > 0)
+ strm.Printf("%-*s", width, "");
+}
+
+static void DumpBasename(Stream &strm, const FileSpec *file_spec_ptr,
+ uint32_t width) {
+ if (file_spec_ptr) {
+ if (width > 0)
+ strm.Printf("%-*s", width, file_spec_ptr->GetFilename().AsCString(""));
+ else
+ file_spec_ptr->GetFilename().Dump(&strm);
+ return;
+ }
+ // Keep the width spacing correct if things go wrong...
+ if (width > 0)
+ strm.Printf("%-*s", width, "");
+}
+
+static size_t DumpModuleObjfileHeaders(Stream &strm, ModuleList &module_list) {
+ std::lock_guard<std::recursive_mutex> guard(module_list.GetMutex());
+ const size_t num_modules = module_list.GetSize();
+ if (num_modules == 0)
+ return 0;
+
+ size_t num_dumped = 0;
+ strm.Format("Dumping headers for {0} module(s).\n", num_modules);
+ strm.IndentMore();
+ for (ModuleSP module_sp : module_list.ModulesNoLocking()) {
+ if (module_sp) {
+ if (num_dumped++ > 0) {
+ strm.EOL();
+ strm.EOL();
+ }
+ ObjectFile *objfile = module_sp->GetObjectFile();
+ if (objfile)
+ objfile->Dump(&strm);
+ else {
+ strm.Format("No object file for module: {0:F}\n",
+ module_sp->GetFileSpec());
+ }
+ }
+ }
+ strm.IndentLess();
+ return num_dumped;
+}
+
+static void DumpModuleSymtab(CommandInterpreter &interpreter, Stream &strm,
+ Module *module, SortOrder sort_order,
+ Mangled::NamePreference name_preference) {
+ if (!module)
+ return;
+ if (Symtab *symtab = module->GetSymtab())
+ symtab->Dump(&strm, interpreter.GetExecutionContext().GetTargetPtr(),
+ sort_order, name_preference);
+}
+
+static void DumpModuleSections(CommandInterpreter &interpreter, Stream &strm,
+ Module *module) {
+ if (module) {
+ SectionList *section_list = module->GetSectionList();
+ if (section_list) {
+ strm.Printf("Sections for '%s' (%s):\n",
+ module->GetSpecificationDescription().c_str(),
+ module->GetArchitecture().GetArchitectureName());
+ section_list->Dump(strm.AsRawOstream(), strm.GetIndentLevel() + 2,
+ interpreter.GetExecutionContext().GetTargetPtr(), true,
+ UINT32_MAX);
+ }
+ }
+}
+
+static bool DumpModuleSymbolFile(Stream &strm, Module *module) {
+ if (module) {
+ if (SymbolFile *symbol_file = module->GetSymbolFile(true)) {
+ symbol_file->Dump(strm);
+ return true;
+ }
+ }
+ return false;
+}
+
+static void DumpAddress(ExecutionContextScope *exe_scope,
+ const Address &so_addr, bool verbose, bool all_ranges,
+ Stream &strm) {
+ strm.IndentMore();
+ strm.Indent(" Address: ");
+ so_addr.Dump(&strm, exe_scope, Address::DumpStyleModuleWithFileAddress);
+ strm.PutCString(" (");
+ so_addr.Dump(&strm, exe_scope, Address::DumpStyleSectionNameOffset);
+ strm.PutCString(")\n");
+ strm.Indent(" Summary: ");
+ const uint32_t save_indent = strm.GetIndentLevel();
+ strm.SetIndentLevel(save_indent + 13);
+ so_addr.Dump(&strm, exe_scope, Address::DumpStyleResolvedDescription);
+ strm.SetIndentLevel(save_indent);
+ // Print out detailed address information when verbose is enabled
+ if (verbose) {
+ strm.EOL();
+ so_addr.Dump(&strm, exe_scope, Address::DumpStyleDetailedSymbolContext,
+ Address::DumpStyleInvalid, UINT32_MAX, all_ranges);
+ }
+ strm.IndentLess();
+}
+
+static bool LookupAddressInModule(CommandInterpreter &interpreter, Stream &strm,
+ Module *module, uint32_t resolve_mask,
+ lldb::addr_t raw_addr, lldb::addr_t offset,
+ bool verbose, bool all_ranges) {
+ if (module) {
+ lldb::addr_t addr = raw_addr - offset;
+ Address so_addr;
+ SymbolContext sc;
+ Target *target = interpreter.GetExecutionContext().GetTargetPtr();
+ if (target && !target->GetSectionLoadList().IsEmpty()) {
+ if (!target->GetSectionLoadList().ResolveLoadAddress(addr, so_addr))
+ return false;
+ else if (so_addr.GetModule().get() != module)
+ return false;
+ } else {
+ if (!module->ResolveFileAddress(addr, so_addr))
+ return false;
+ }
+
+ ExecutionContextScope *exe_scope =
+ interpreter.GetExecutionContext().GetBestExecutionContextScope();
+ DumpAddress(exe_scope, so_addr, verbose, all_ranges, strm);
+ // strm.IndentMore();
+ // strm.Indent (" Address: ");
+ // so_addr.Dump (&strm, exe_scope,
+ // Address::DumpStyleModuleWithFileAddress);
+ // strm.PutCString (" (");
+ // so_addr.Dump (&strm, exe_scope,
+ // Address::DumpStyleSectionNameOffset);
+ // strm.PutCString (")\n");
+ // strm.Indent (" Summary: ");
+ // const uint32_t save_indent = strm.GetIndentLevel ();
+ // strm.SetIndentLevel (save_indent + 13);
+ // so_addr.Dump (&strm, exe_scope,
+ // Address::DumpStyleResolvedDescription);
+ // strm.SetIndentLevel (save_indent);
+ // // Print out detailed address information when verbose is enabled
+ // if (verbose)
+ // {
+ // strm.EOL();
+ // so_addr.Dump (&strm, exe_scope,
+ // Address::DumpStyleDetailedSymbolContext);
+ // }
+ // strm.IndentLess();
+ return true;
+ }
+
+ return false;
+}
+
+static uint32_t LookupSymbolInModule(CommandInterpreter &interpreter,
+ Stream &strm, Module *module,
+ const char *name, bool name_is_regex,
+ bool verbose, bool all_ranges) {
+ if (!module)
+ return 0;
+
+ Symtab *symtab = module->GetSymtab();
+ if (!symtab)
+ return 0;
+
+ SymbolContext sc;
+ std::vector<uint32_t> match_indexes;
+ ConstString symbol_name(name);
+ uint32_t num_matches = 0;
+ if (name_is_regex) {
+ RegularExpression name_regexp(symbol_name.GetStringRef());
+ num_matches = symtab->AppendSymbolIndexesMatchingRegExAndType(
+ name_regexp, eSymbolTypeAny, match_indexes);
+ } else {
+ num_matches =
+ symtab->AppendSymbolIndexesWithName(symbol_name, match_indexes);
+ }
+
+ if (num_matches > 0) {
+ strm.Indent();
+ strm.Printf("%u symbols match %s'%s' in ", num_matches,
+ name_is_regex ? "the regular expression " : "", name);
+ DumpFullpath(strm, &module->GetFileSpec(), 0);
+ strm.PutCString(":\n");
+ strm.IndentMore();
+ for (uint32_t i = 0; i < num_matches; ++i) {
+ Symbol *symbol = symtab->SymbolAtIndex(match_indexes[i]);
+ if (symbol && symbol->ValueIsAddress()) {
+ DumpAddress(
+ interpreter.GetExecutionContext().GetBestExecutionContextScope(),
+ symbol->GetAddressRef(), verbose, all_ranges, strm);
+ }
+ }
+ strm.IndentLess();
+ }
+ return num_matches;
+}
+
+static void DumpSymbolContextList(ExecutionContextScope *exe_scope,
+ Stream &strm, SymbolContextList &sc_list,
+ bool verbose, bool all_ranges) {
+ strm.IndentMore();
+
+ const uint32_t num_matches = sc_list.GetSize();
+
+ for (uint32_t i = 0; i < num_matches; ++i) {
+ SymbolContext sc;
+ if (sc_list.GetContextAtIndex(i, sc)) {
+ AddressRange range;
+
+ sc.GetAddressRange(eSymbolContextEverything, 0, true, range);
+
+ DumpAddress(exe_scope, range.GetBaseAddress(), verbose, all_ranges, strm);
+ }
+ }
+ strm.IndentLess();
+}
+
+static size_t LookupFunctionInModule(CommandInterpreter &interpreter,
+ Stream &strm, Module *module,
+ const char *name, bool name_is_regex,
+ const ModuleFunctionSearchOptions &options,
+ bool verbose, bool all_ranges) {
+ if (module && name && name[0]) {
+ SymbolContextList sc_list;
+ size_t num_matches = 0;
+ if (name_is_regex) {
+ RegularExpression function_name_regex((llvm::StringRef(name)));
+ module->FindFunctions(function_name_regex, options, sc_list);
+ } else {
+ ConstString function_name(name);
+ module->FindFunctions(function_name, CompilerDeclContext(),
+ eFunctionNameTypeAuto, options, sc_list);
+ }
+ num_matches = sc_list.GetSize();
+ if (num_matches) {
+ strm.Indent();
+ strm.Printf("%" PRIu64 " match%s found in ", (uint64_t)num_matches,
+ num_matches > 1 ? "es" : "");
+ DumpFullpath(strm, &module->GetFileSpec(), 0);
+ strm.PutCString(":\n");
+ DumpSymbolContextList(
+ interpreter.GetExecutionContext().GetBestExecutionContextScope(),
+ strm, sc_list, verbose, all_ranges);
+ }
+ return num_matches;
+ }
+ return 0;
+}
+
+static size_t LookupTypeInModule(Target *target,
+ CommandInterpreter &interpreter, Stream &strm,
+ Module *module, const char *name_cstr,
+ bool name_is_regex) {
+ TypeList type_list;
+ if (module && name_cstr && name_cstr[0]) {
+ const uint32_t max_num_matches = UINT32_MAX;
+ bool name_is_fully_qualified = false;
+
+ ConstString name(name_cstr);
+ llvm::DenseSet<lldb_private::SymbolFile *> searched_symbol_files;
+ module->FindTypes(name, name_is_fully_qualified, max_num_matches,
+ searched_symbol_files, type_list);
+
+ if (type_list.Empty())
+ return 0;
+
+ const uint64_t num_matches = type_list.GetSize();
+
+ strm.Indent();
+ strm.Printf("%" PRIu64 " match%s found in ", num_matches,
+ num_matches > 1 ? "es" : "");
+ DumpFullpath(strm, &module->GetFileSpec(), 0);
+ strm.PutCString(":\n");
+ for (TypeSP type_sp : type_list.Types()) {
+ if (!type_sp)
+ continue;
+ // Resolve the clang type so that any forward references to types
+ // that haven't yet been parsed will get parsed.
+ type_sp->GetFullCompilerType();
+ type_sp->GetDescription(&strm, eDescriptionLevelFull, true, target);
+ // Print all typedef chains
+ TypeSP typedef_type_sp(type_sp);
+ TypeSP typedefed_type_sp(typedef_type_sp->GetTypedefType());
+ while (typedefed_type_sp) {
+ strm.EOL();
+ strm.Printf(" typedef '%s': ",
+ typedef_type_sp->GetName().GetCString());
+ typedefed_type_sp->GetFullCompilerType();
+ typedefed_type_sp->GetDescription(&strm, eDescriptionLevelFull, true,
+ target);
+ typedef_type_sp = typedefed_type_sp;
+ typedefed_type_sp = typedef_type_sp->GetTypedefType();
+ }
+ }
+ strm.EOL();
+ }
+ return type_list.GetSize();
+}
+
+static size_t LookupTypeHere(Target *target, CommandInterpreter &interpreter,
+ Stream &strm, Module &module,
+ const char *name_cstr, bool name_is_regex) {
+ TypeList type_list;
+ const uint32_t max_num_matches = UINT32_MAX;
+ bool name_is_fully_qualified = false;
+
+ ConstString name(name_cstr);
+ llvm::DenseSet<SymbolFile *> searched_symbol_files;
+ module.FindTypes(name, name_is_fully_qualified, max_num_matches,
+ searched_symbol_files, type_list);
+
+ if (type_list.Empty())
+ return 0;
+
+ strm.Indent();
+ strm.PutCString("Best match found in ");
+ DumpFullpath(strm, &module.GetFileSpec(), 0);
+ strm.PutCString(":\n");
+
+ TypeSP type_sp(type_list.GetTypeAtIndex(0));
+ if (type_sp) {
+ // Resolve the clang type so that any forward references to types that
+ // haven't yet been parsed will get parsed.
+ type_sp->GetFullCompilerType();
+ type_sp->GetDescription(&strm, eDescriptionLevelFull, true, target);
+ // Print all typedef chains.
+ TypeSP typedef_type_sp(type_sp);
+ TypeSP typedefed_type_sp(typedef_type_sp->GetTypedefType());
+ while (typedefed_type_sp) {
+ strm.EOL();
+ strm.Printf(" typedef '%s': ",
+ typedef_type_sp->GetName().GetCString());
+ typedefed_type_sp->GetFullCompilerType();
+ typedefed_type_sp->GetDescription(&strm, eDescriptionLevelFull, true,
+ target);
+ typedef_type_sp = typedefed_type_sp;
+ typedefed_type_sp = typedef_type_sp->GetTypedefType();
+ }
+ }
+ strm.EOL();
+ return type_list.GetSize();
+}
+
+static uint32_t LookupFileAndLineInModule(CommandInterpreter &interpreter,
+ Stream &strm, Module *module,
+ const FileSpec &file_spec,
+ uint32_t line, bool check_inlines,
+ bool verbose, bool all_ranges) {
+ if (module && file_spec) {
+ SymbolContextList sc_list;
+ const uint32_t num_matches = module->ResolveSymbolContextsForFileSpec(
+ file_spec, line, check_inlines, eSymbolContextEverything, sc_list);
+ if (num_matches > 0) {
+ strm.Indent();
+ strm.Printf("%u match%s found in ", num_matches,
+ num_matches > 1 ? "es" : "");
+ strm << file_spec;
+ if (line > 0)
+ strm.Printf(":%u", line);
+ strm << " in ";
+ DumpFullpath(strm, &module->GetFileSpec(), 0);
+ strm.PutCString(":\n");
+ DumpSymbolContextList(
+ interpreter.GetExecutionContext().GetBestExecutionContextScope(),
+ strm, sc_list, verbose, all_ranges);
+ return num_matches;
+ }
+ }
+ return 0;
+}
+
+static size_t FindModulesByName(Target *target, const char *module_name,
+ ModuleList &module_list,
+ bool check_global_list) {
+ FileSpec module_file_spec(module_name);
+ ModuleSpec module_spec(module_file_spec);
+
+ const size_t initial_size = module_list.GetSize();
+
+ if (check_global_list) {
+ // Check the global list
+ std::lock_guard<std::recursive_mutex> guard(
+ Module::GetAllocationModuleCollectionMutex());
+ const size_t num_modules = Module::GetNumberAllocatedModules();
+ ModuleSP module_sp;
+ for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
+ Module *module = Module::GetAllocatedModuleAtIndex(image_idx);
+
+ if (module) {
+ if (module->MatchesModuleSpec(module_spec)) {
+ module_sp = module->shared_from_this();
+ module_list.AppendIfNeeded(module_sp);
+ }
+ }
+ }
+ } else {
+ if (target) {
+ target->GetImages().FindModules(module_spec, module_list);
+ const size_t num_matches = module_list.GetSize();
+
+ // Not found in our module list for our target, check the main shared
+ // module list in case it is a extra file used somewhere else
+ if (num_matches == 0) {
+ module_spec.GetArchitecture() = target->GetArchitecture();
+ ModuleList::FindSharedModules(module_spec, module_list);
+ }
+ } else {
+ ModuleList::FindSharedModules(module_spec, module_list);
+ }
+ }
+
+ return module_list.GetSize() - initial_size;
+}
+
+#pragma mark CommandObjectTargetModulesModuleAutoComplete
+
+// A base command object class that can auto complete with module file
+// paths
+
+class CommandObjectTargetModulesModuleAutoComplete
+ : public CommandObjectParsed {
+public:
+ CommandObjectTargetModulesModuleAutoComplete(CommandInterpreter &interpreter,
+ const char *name,
+ const char *help,
+ const char *syntax,
+ uint32_t flags = 0)
+ : CommandObjectParsed(interpreter, name, help, syntax, flags) {
+ CommandArgumentEntry arg;
+ CommandArgumentData file_arg;
+
+ // Define the first (and only) variant of this arg.
+ file_arg.arg_type = eArgTypeFilename;
+ file_arg.arg_repetition = eArgRepeatStar;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(file_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectTargetModulesModuleAutoComplete() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eModuleCompletion, request,
+ nullptr);
+ }
+};
+
+#pragma mark CommandObjectTargetModulesSourceFileAutoComplete
+
+// A base command object class that can auto complete with module source
+// file paths
+
+class CommandObjectTargetModulesSourceFileAutoComplete
+ : public CommandObjectParsed {
+public:
+ CommandObjectTargetModulesSourceFileAutoComplete(
+ CommandInterpreter &interpreter, const char *name, const char *help,
+ const char *syntax, uint32_t flags)
+ : CommandObjectParsed(interpreter, name, help, syntax, flags) {
+ CommandArgumentEntry arg;
+ CommandArgumentData source_file_arg;
+
+ // Define the first (and only) variant of this arg.
+ source_file_arg.arg_type = eArgTypeSourceFile;
+ source_file_arg.arg_repetition = eArgRepeatPlus;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(source_file_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectTargetModulesSourceFileAutoComplete() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eSourceFileCompletion,
+ request, nullptr);
+ }
+};
+
+#pragma mark CommandObjectTargetModulesDumpObjfile
+
+class CommandObjectTargetModulesDumpObjfile
+ : public CommandObjectTargetModulesModuleAutoComplete {
+public:
+ CommandObjectTargetModulesDumpObjfile(CommandInterpreter &interpreter)
+ : CommandObjectTargetModulesModuleAutoComplete(
+ interpreter, "target modules dump objfile",
+ "Dump the object file headers from one or more target modules.",
+ nullptr, eCommandRequiresTarget) {}
+
+ ~CommandObjectTargetModulesDumpObjfile() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+
+ uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
+ result.GetOutputStream().SetAddressByteSize(addr_byte_size);
+ result.GetErrorStream().SetAddressByteSize(addr_byte_size);
+
+ size_t num_dumped = 0;
+ if (command.GetArgumentCount() == 0) {
+ // Dump all headers for all modules images
+ num_dumped = DumpModuleObjfileHeaders(result.GetOutputStream(),
+ target->GetImages());
+ if (num_dumped == 0) {
+ result.AppendError("the target has no associated executable images");
+ }
+ } else {
+ // Find the modules that match the basename or full path.
+ ModuleList module_list;
+ const char *arg_cstr;
+ for (int arg_idx = 0;
+ (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr;
+ ++arg_idx) {
+ size_t num_matched =
+ FindModulesByName(target, arg_cstr, module_list, true);
+ if (num_matched == 0) {
+ result.AppendWarningWithFormat(
+ "Unable to find an image that matches '%s'.\n", arg_cstr);
+ }
+ }
+ // Dump all the modules we found.
+ num_dumped =
+ DumpModuleObjfileHeaders(result.GetOutputStream(), module_list);
+ }
+
+ if (num_dumped > 0) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError("no matching executable images found");
+ }
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectTargetModulesDumpSymtab
+
+static constexpr OptionEnumValueElement g_sort_option_enumeration[] = {
+ {
+ eSortOrderNone,
+ "none",
+ "No sorting, use the original symbol table order.",
+ },
+ {
+ eSortOrderByAddress,
+ "address",
+ "Sort output by symbol address.",
+ },
+ {
+ eSortOrderByName,
+ "name",
+ "Sort output by symbol name.",
+ },
+};
+
+#define LLDB_OPTIONS_target_modules_dump_symtab
+#include "CommandOptions.inc"
+
+class CommandObjectTargetModulesDumpSymtab
+ : public CommandObjectTargetModulesModuleAutoComplete {
+public:
+ CommandObjectTargetModulesDumpSymtab(CommandInterpreter &interpreter)
+ : CommandObjectTargetModulesModuleAutoComplete(
+ interpreter, "target modules dump symtab",
+ "Dump the symbol table from one or more target modules.", nullptr,
+ eCommandRequiresTarget) {}
+
+ ~CommandObjectTargetModulesDumpSymtab() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'm':
+ m_prefer_mangled.SetCurrentValue(true);
+ m_prefer_mangled.SetOptionWasSet();
+ break;
+
+ case 's':
+ m_sort_order = (SortOrder)OptionArgParser::ToOptionEnum(
+ option_arg, GetDefinitions()[option_idx].enum_values,
+ eSortOrderNone, error);
+ break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_sort_order = eSortOrderNone;
+ m_prefer_mangled.Clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_target_modules_dump_symtab_options);
+ }
+
+ SortOrder m_sort_order = eSortOrderNone;
+ OptionValueBoolean m_prefer_mangled = {false, false};
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+ uint32_t num_dumped = 0;
+ Mangled::NamePreference name_preference =
+ (m_options.m_prefer_mangled ? Mangled::ePreferMangled
+ : Mangled::ePreferDemangled);
+
+ uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
+ result.GetOutputStream().SetAddressByteSize(addr_byte_size);
+ result.GetErrorStream().SetAddressByteSize(addr_byte_size);
+
+ if (command.GetArgumentCount() == 0) {
+ // Dump all sections for all modules images
+ const ModuleList &module_list = target->GetImages();
+ std::lock_guard<std::recursive_mutex> guard(module_list.GetMutex());
+ const size_t num_modules = module_list.GetSize();
+ if (num_modules > 0) {
+ result.GetOutputStream().Format(
+ "Dumping symbol table for {0} modules.\n", num_modules);
+ for (ModuleSP module_sp : module_list.ModulesNoLocking()) {
+ if (num_dumped > 0) {
+ result.GetOutputStream().EOL();
+ result.GetOutputStream().EOL();
+ }
+ if (m_interpreter.WasInterrupted())
+ break;
+ num_dumped++;
+ DumpModuleSymtab(m_interpreter, result.GetOutputStream(),
+ module_sp.get(), m_options.m_sort_order,
+ name_preference);
+ }
+ } else {
+ result.AppendError("the target has no associated executable images");
+ return false;
+ }
+ } else {
+ // Dump specified images (by basename or fullpath)
+ const char *arg_cstr;
+ for (int arg_idx = 0;
+ (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr;
+ ++arg_idx) {
+ ModuleList module_list;
+ const size_t num_matches =
+ FindModulesByName(target, arg_cstr, module_list, true);
+ if (num_matches > 0) {
+ for (ModuleSP module_sp : module_list.Modules()) {
+ if (module_sp) {
+ if (num_dumped > 0) {
+ result.GetOutputStream().EOL();
+ result.GetOutputStream().EOL();
+ }
+ if (m_interpreter.WasInterrupted())
+ break;
+ num_dumped++;
+ DumpModuleSymtab(m_interpreter, result.GetOutputStream(),
+ module_sp.get(), m_options.m_sort_order,
+ name_preference);
+ }
+ }
+ } else
+ result.AppendWarningWithFormat(
+ "Unable to find an image that matches '%s'.\n", arg_cstr);
+ }
+ }
+
+ if (num_dumped > 0)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else {
+ result.AppendError("no matching executable images found");
+ }
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+#pragma mark CommandObjectTargetModulesDumpSections
+
+// Image section dumping command
+
+class CommandObjectTargetModulesDumpSections
+ : public CommandObjectTargetModulesModuleAutoComplete {
+public:
+ CommandObjectTargetModulesDumpSections(CommandInterpreter &interpreter)
+ : CommandObjectTargetModulesModuleAutoComplete(
+ interpreter, "target modules dump sections",
+ "Dump the sections from one or more target modules.",
+ //"target modules dump sections [<file1> ...]")
+ nullptr, eCommandRequiresTarget) {}
+
+ ~CommandObjectTargetModulesDumpSections() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+ uint32_t num_dumped = 0;
+
+ uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
+ result.GetOutputStream().SetAddressByteSize(addr_byte_size);
+ result.GetErrorStream().SetAddressByteSize(addr_byte_size);
+
+ if (command.GetArgumentCount() == 0) {
+ // Dump all sections for all modules images
+ const size_t num_modules = target->GetImages().GetSize();
+ if (num_modules == 0) {
+ result.AppendError("the target has no associated executable images");
+ return false;
+ }
+
+ result.GetOutputStream().Format("Dumping sections for {0} modules.\n",
+ num_modules);
+ for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
+ if (m_interpreter.WasInterrupted())
+ break;
+ num_dumped++;
+ DumpModuleSections(
+ m_interpreter, result.GetOutputStream(),
+ target->GetImages().GetModulePointerAtIndex(image_idx));
+ }
+ } else {
+ // Dump specified images (by basename or fullpath)
+ const char *arg_cstr;
+ for (int arg_idx = 0;
+ (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr;
+ ++arg_idx) {
+ ModuleList module_list;
+ const size_t num_matches =
+ FindModulesByName(target, arg_cstr, module_list, true);
+ if (num_matches > 0) {
+ for (size_t i = 0; i < num_matches; ++i) {
+ if (m_interpreter.WasInterrupted())
+ break;
+ Module *module = module_list.GetModulePointerAtIndex(i);
+ if (module) {
+ num_dumped++;
+ DumpModuleSections(m_interpreter, result.GetOutputStream(),
+ module);
+ }
+ }
+ } else {
+ // Check the global list
+ std::lock_guard<std::recursive_mutex> guard(
+ Module::GetAllocationModuleCollectionMutex());
+
+ result.AppendWarningWithFormat(
+ "Unable to find an image that matches '%s'.\n", arg_cstr);
+ }
+ }
+ }
+
+ if (num_dumped > 0)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else {
+ result.AppendError("no matching executable images found");
+ }
+ return result.Succeeded();
+ }
+};
+
+class CommandObjectTargetModulesDumpClangPCMInfo : public CommandObjectParsed {
+public:
+ CommandObjectTargetModulesDumpClangPCMInfo(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "target modules dump pcm-info",
+ "Dump information about the given clang module (pcm).") {
+ // Take a single file argument.
+ CommandArgumentData arg{eArgTypeFilename, eArgRepeatPlain};
+ m_arguments.push_back({arg});
+ }
+
+ ~CommandObjectTargetModulesDumpClangPCMInfo() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (command.GetArgumentCount() != 1) {
+ result.AppendErrorWithFormat("'%s' takes exactly one pcm path argument.",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ const char *pcm_path = command.GetArgumentAtIndex(0);
+ FileSpec pcm_file{pcm_path};
+
+ if (pcm_file.GetFileNameExtension().GetStringRef() != ".pcm") {
+ result.AppendError("file must have a .pcm extension");
+ return false;
+ }
+
+ if (!FileSystem::Instance().Exists(pcm_file)) {
+ result.AppendError("pcm file does not exist");
+ return false;
+ }
+
+ clang::CompilerInstance compiler;
+ compiler.createDiagnostics();
+
+ const char *clang_args[] = {"clang", pcm_path};
+ compiler.setInvocation(clang::createInvocation(clang_args));
+
+ clang::DumpModuleInfoAction dump_module_info;
+ dump_module_info.OutputStream = &result.GetOutputStream().AsRawOstream();
+ // DumpModuleInfoAction requires ObjectFilePCHContainerReader.
+ compiler.getPCHContainerOperations()->registerReader(
+ std::make_unique<clang::ObjectFilePCHContainerReader>());
+
+ if (compiler.ExecuteAction(dump_module_info))
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectTargetModulesDumpClangAST
+
+// Clang AST dumping command
+
+class CommandObjectTargetModulesDumpClangAST
+ : public CommandObjectTargetModulesModuleAutoComplete {
+public:
+ CommandObjectTargetModulesDumpClangAST(CommandInterpreter &interpreter)
+ : CommandObjectTargetModulesModuleAutoComplete(
+ interpreter, "target modules dump ast",
+ "Dump the clang ast for a given module's symbol file.",
+ //"target modules dump ast [<file1> ...]")
+ nullptr, eCommandRequiresTarget) {}
+
+ ~CommandObjectTargetModulesDumpClangAST() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+
+ const ModuleList &module_list = target->GetImages();
+ const size_t num_modules = module_list.GetSize();
+ if (num_modules == 0) {
+ result.AppendError("the target has no associated executable images");
+ return false;
+ }
+
+ if (command.GetArgumentCount() == 0) {
+ // Dump all ASTs for all modules images
+ result.GetOutputStream().Format("Dumping clang ast for {0} modules.\n",
+ num_modules);
+ for (ModuleSP module_sp : module_list.ModulesNoLocking()) {
+ if (m_interpreter.WasInterrupted())
+ break;
+ if (SymbolFile *sf = module_sp->GetSymbolFile())
+ sf->DumpClangAST(result.GetOutputStream());
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+
+ // Dump specified ASTs (by basename or fullpath)
+ for (const Args::ArgEntry &arg : command.entries()) {
+ ModuleList module_list;
+ const size_t num_matches =
+ FindModulesByName(target, arg.c_str(), module_list, true);
+ if (num_matches == 0) {
+ // Check the global list
+ std::lock_guard<std::recursive_mutex> guard(
+ Module::GetAllocationModuleCollectionMutex());
+
+ result.AppendWarningWithFormat(
+ "Unable to find an image that matches '%s'.\n", arg.c_str());
+ continue;
+ }
+
+ for (size_t i = 0; i < num_matches; ++i) {
+ if (m_interpreter.WasInterrupted())
+ break;
+ Module *m = module_list.GetModulePointerAtIndex(i);
+ if (SymbolFile *sf = m->GetSymbolFile())
+ sf->DumpClangAST(result.GetOutputStream());
+ }
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+#pragma mark CommandObjectTargetModulesDumpSymfile
+
+// Image debug symbol dumping command
+
+class CommandObjectTargetModulesDumpSymfile
+ : public CommandObjectTargetModulesModuleAutoComplete {
+public:
+ CommandObjectTargetModulesDumpSymfile(CommandInterpreter &interpreter)
+ : CommandObjectTargetModulesModuleAutoComplete(
+ interpreter, "target modules dump symfile",
+ "Dump the debug symbol file for one or more target modules.",
+ //"target modules dump symfile [<file1> ...]")
+ nullptr, eCommandRequiresTarget) {}
+
+ ~CommandObjectTargetModulesDumpSymfile() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+ uint32_t num_dumped = 0;
+
+ uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
+ result.GetOutputStream().SetAddressByteSize(addr_byte_size);
+ result.GetErrorStream().SetAddressByteSize(addr_byte_size);
+
+ if (command.GetArgumentCount() == 0) {
+ // Dump all sections for all modules images
+ const ModuleList &target_modules = target->GetImages();
+ std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
+ const size_t num_modules = target_modules.GetSize();
+ if (num_modules == 0) {
+ result.AppendError("the target has no associated executable images");
+ return false;
+ }
+ result.GetOutputStream().Format(
+ "Dumping debug symbols for {0} modules.\n", num_modules);
+ for (ModuleSP module_sp : target_modules.ModulesNoLocking()) {
+ if (m_interpreter.WasInterrupted())
+ break;
+ if (DumpModuleSymbolFile(result.GetOutputStream(), module_sp.get()))
+ num_dumped++;
+ }
+ } else {
+ // Dump specified images (by basename or fullpath)
+ const char *arg_cstr;
+ for (int arg_idx = 0;
+ (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr;
+ ++arg_idx) {
+ ModuleList module_list;
+ const size_t num_matches =
+ FindModulesByName(target, arg_cstr, module_list, true);
+ if (num_matches > 0) {
+ for (size_t i = 0; i < num_matches; ++i) {
+ if (m_interpreter.WasInterrupted())
+ break;
+ Module *module = module_list.GetModulePointerAtIndex(i);
+ if (module) {
+ if (DumpModuleSymbolFile(result.GetOutputStream(), module))
+ num_dumped++;
+ }
+ }
+ } else
+ result.AppendWarningWithFormat(
+ "Unable to find an image that matches '%s'.\n", arg_cstr);
+ }
+ }
+
+ if (num_dumped > 0)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else {
+ result.AppendError("no matching executable images found");
+ }
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectTargetModulesDumpLineTable
+#define LLDB_OPTIONS_target_modules_dump
+#include "CommandOptions.inc"
+
+// Image debug line table dumping command
+
+class CommandObjectTargetModulesDumpLineTable
+ : public CommandObjectTargetModulesSourceFileAutoComplete {
+public:
+ CommandObjectTargetModulesDumpLineTable(CommandInterpreter &interpreter)
+ : CommandObjectTargetModulesSourceFileAutoComplete(
+ interpreter, "target modules dump line-table",
+ "Dump the line table for one or more compilation units.", nullptr,
+ eCommandRequiresTarget) {}
+
+ ~CommandObjectTargetModulesDumpLineTable() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = m_exe_ctx.GetTargetPtr();
+ uint32_t total_num_dumped = 0;
+
+ uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
+ result.GetOutputStream().SetAddressByteSize(addr_byte_size);
+ result.GetErrorStream().SetAddressByteSize(addr_byte_size);
+
+ if (command.GetArgumentCount() == 0) {
+ result.AppendError("file option must be specified.");
+ return result.Succeeded();
+ } else {
+ // Dump specified images (by basename or fullpath)
+ const char *arg_cstr;
+ for (int arg_idx = 0;
+ (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr;
+ ++arg_idx) {
+ FileSpec file_spec(arg_cstr);
+
+ const ModuleList &target_modules = target->GetImages();
+ std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
+ if (target_modules.GetSize() > 0) {
+ uint32_t num_dumped = 0;
+ for (ModuleSP module_sp : target_modules.ModulesNoLocking()) {
+ if (m_interpreter.WasInterrupted())
+ break;
+ if (DumpCompileUnitLineTable(
+ m_interpreter, result.GetOutputStream(), module_sp.get(),
+ file_spec,
+ m_options.m_verbose ? eDescriptionLevelFull
+ : eDescriptionLevelBrief))
+ num_dumped++;
+ }
+ if (num_dumped == 0)
+ result.AppendWarningWithFormat(
+ "No source filenames matched '%s'.\n", arg_cstr);
+ else
+ total_num_dumped += num_dumped;
+ }
+ }
+ }
+
+ if (total_num_dumped > 0)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else {
+ result.AppendError("no source filenames matched any command arguments");
+ }
+ return result.Succeeded();
+ }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() { OptionParsingStarting(nullptr); }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ assert(option_idx == 0 && "We only have one option.");
+ m_verbose = true;
+
+ return Status();
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_verbose = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_target_modules_dump_options);
+ }
+
+ bool m_verbose;
+ };
+
+ CommandOptions m_options;
+};
+
+#pragma mark CommandObjectTargetModulesDump
+
+// Dump multi-word command for target modules
+
+class CommandObjectTargetModulesDump : public CommandObjectMultiword {
+public:
+ // Constructors and Destructors
+ CommandObjectTargetModulesDump(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "target modules dump",
+ "Commands for dumping information about one or more target "
+ "modules.",
+ "target modules dump "
+ "[objfile|symtab|sections|ast|symfile|line-table|pcm-info] "
+ "[<file1> <file2> ...]") {
+ LoadSubCommand("objfile",
+ CommandObjectSP(
+ new CommandObjectTargetModulesDumpObjfile(interpreter)));
+ LoadSubCommand(
+ "symtab",
+ CommandObjectSP(new CommandObjectTargetModulesDumpSymtab(interpreter)));
+ LoadSubCommand("sections",
+ CommandObjectSP(new CommandObjectTargetModulesDumpSections(
+ interpreter)));
+ LoadSubCommand("symfile",
+ CommandObjectSP(
+ new CommandObjectTargetModulesDumpSymfile(interpreter)));
+ LoadSubCommand(
+ "ast", CommandObjectSP(
+ new CommandObjectTargetModulesDumpClangAST(interpreter)));
+ LoadSubCommand("line-table",
+ CommandObjectSP(new CommandObjectTargetModulesDumpLineTable(
+ interpreter)));
+ LoadSubCommand(
+ "pcm-info",
+ CommandObjectSP(
+ new CommandObjectTargetModulesDumpClangPCMInfo(interpreter)));
+ }
+
+ ~CommandObjectTargetModulesDump() override = default;
+};
+
+class CommandObjectTargetModulesAdd : public CommandObjectParsed {
+public:
+ CommandObjectTargetModulesAdd(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "target modules add",
+ "Add a new module to the current target's modules.",
+ "target modules add [<module>]",
+ eCommandRequiresTarget),
+ m_symbol_file(LLDB_OPT_SET_1, false, "symfile", 's', 0,
+ eArgTypeFilename,
+ "Fullpath to a stand alone debug "
+ "symbols file for when debug symbols "
+ "are not in the executable.") {
+ m_option_group.Append(&m_uuid_option_group, LLDB_OPT_SET_ALL,
+ LLDB_OPT_SET_1);
+ m_option_group.Append(&m_symbol_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Finalize();
+ CommandArgumentData module_arg{eArgTypePath, eArgRepeatStar};
+ m_arguments.push_back({module_arg});
+ }
+
+ ~CommandObjectTargetModulesAdd() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+ request, nullptr);
+ }
+
+protected:
+ OptionGroupOptions m_option_group;
+ OptionGroupUUID m_uuid_option_group;
+ OptionGroupFile m_symbol_file;
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+ bool flush = false;
+
+ const size_t argc = args.GetArgumentCount();
+ if (argc == 0) {
+ if (m_uuid_option_group.GetOptionValue().OptionWasSet()) {
+ // We are given a UUID only, go locate the file
+ ModuleSpec module_spec;
+ module_spec.GetUUID() =
+ m_uuid_option_group.GetOptionValue().GetCurrentValue();
+ if (m_symbol_file.GetOptionValue().OptionWasSet())
+ module_spec.GetSymbolFileSpec() =
+ m_symbol_file.GetOptionValue().GetCurrentValue();
+ Status error;
+ if (Symbols::DownloadObjectAndSymbolFile(module_spec, error)) {
+ ModuleSP module_sp(
+ target->GetOrCreateModule(module_spec, true /* notify */));
+ if (module_sp) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ } else {
+ StreamString strm;
+ module_spec.GetUUID().Dump(&strm);
+ if (module_spec.GetFileSpec()) {
+ if (module_spec.GetSymbolFileSpec()) {
+ result.AppendErrorWithFormat(
+ "Unable to create the executable or symbol file with "
+ "UUID %s with path %s and symbol file %s",
+ strm.GetData(), module_spec.GetFileSpec().GetPath().c_str(),
+ module_spec.GetSymbolFileSpec().GetPath().c_str());
+ } else {
+ result.AppendErrorWithFormat(
+ "Unable to create the executable or symbol file with "
+ "UUID %s with path %s",
+ strm.GetData(),
+ module_spec.GetFileSpec().GetPath().c_str());
+ }
+ } else {
+ result.AppendErrorWithFormat("Unable to create the executable "
+ "or symbol file with UUID %s",
+ strm.GetData());
+ }
+ return false;
+ }
+ } else {
+ StreamString strm;
+ module_spec.GetUUID().Dump(&strm);
+ result.AppendErrorWithFormat(
+ "Unable to locate the executable or symbol file with UUID %s",
+ strm.GetData());
+ result.SetError(error);
+ return false;
+ }
+ } else {
+ result.AppendError(
+ "one or more executable image paths must be specified");
+ return false;
+ }
+ } else {
+ for (auto &entry : args.entries()) {
+ if (entry.ref().empty())
+ continue;
+
+ FileSpec file_spec(entry.ref());
+ if (FileSystem::Instance().Exists(file_spec)) {
+ ModuleSpec module_spec(file_spec);
+ if (m_uuid_option_group.GetOptionValue().OptionWasSet())
+ module_spec.GetUUID() =
+ m_uuid_option_group.GetOptionValue().GetCurrentValue();
+ if (m_symbol_file.GetOptionValue().OptionWasSet())
+ module_spec.GetSymbolFileSpec() =
+ m_symbol_file.GetOptionValue().GetCurrentValue();
+ if (!module_spec.GetArchitecture().IsValid())
+ module_spec.GetArchitecture() = target->GetArchitecture();
+ Status error;
+ ModuleSP module_sp(target->GetOrCreateModule(
+ module_spec, true /* notify */, &error));
+ if (!module_sp) {
+ const char *error_cstr = error.AsCString();
+ if (error_cstr)
+ result.AppendError(error_cstr);
+ else
+ result.AppendErrorWithFormat("unsupported module: %s",
+ entry.c_str());
+ return false;
+ } else {
+ flush = true;
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ std::string resolved_path = file_spec.GetPath();
+ if (resolved_path != entry.ref()) {
+ result.AppendErrorWithFormat(
+ "invalid module path '%s' with resolved path '%s'\n",
+ entry.ref().str().c_str(), resolved_path.c_str());
+ break;
+ }
+ result.AppendErrorWithFormat("invalid module path '%s'\n",
+ entry.c_str());
+ break;
+ }
+ }
+ }
+
+ if (flush) {
+ ProcessSP process = target->GetProcessSP();
+ if (process)
+ process->Flush();
+ }
+
+ return result.Succeeded();
+ }
+};
+
+class CommandObjectTargetModulesLoad
+ : public CommandObjectTargetModulesModuleAutoComplete {
+public:
+ CommandObjectTargetModulesLoad(CommandInterpreter &interpreter)
+ : CommandObjectTargetModulesModuleAutoComplete(
+ interpreter, "target modules load",
+ "Set the load addresses for one or more sections in a target "
+ "module.",
+ "target modules load [--file <module> --uuid <uuid>] <sect-name> "
+ "<address> [<sect-name> <address> ....]",
+ eCommandRequiresTarget),
+ m_file_option(LLDB_OPT_SET_1, false, "file", 'f', 0, eArgTypeName,
+ "Fullpath or basename for module to load.", ""),
+ m_load_option(LLDB_OPT_SET_1, false, "load", 'l',
+ "Write file contents to the memory.", false, true),
+ m_pc_option(LLDB_OPT_SET_1, false, "set-pc-to-entry", 'p',
+ "Set PC to the entry point."
+ " Only applicable with '--load' option.",
+ false, true),
+ m_slide_option(LLDB_OPT_SET_1, false, "slide", 's', 0, eArgTypeOffset,
+ "Set the load address for all sections to be the "
+ "virtual address in the file plus the offset.",
+ 0) {
+ m_option_group.Append(&m_uuid_option_group, LLDB_OPT_SET_ALL,
+ LLDB_OPT_SET_1);
+ m_option_group.Append(&m_file_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_load_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_pc_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_slide_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectTargetModulesLoad() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+ const bool load = m_load_option.GetOptionValue().GetCurrentValue();
+ const bool set_pc = m_pc_option.GetOptionValue().GetCurrentValue();
+
+ const size_t argc = args.GetArgumentCount();
+ ModuleSpec module_spec;
+ bool search_using_module_spec = false;
+
+ // Allow "load" option to work without --file or --uuid option.
+ if (load) {
+ if (!m_file_option.GetOptionValue().OptionWasSet() &&
+ !m_uuid_option_group.GetOptionValue().OptionWasSet()) {
+ ModuleList &module_list = target->GetImages();
+ if (module_list.GetSize() == 1) {
+ search_using_module_spec = true;
+ module_spec.GetFileSpec() =
+ module_list.GetModuleAtIndex(0)->GetFileSpec();
+ }
+ }
+ }
+
+ if (m_file_option.GetOptionValue().OptionWasSet()) {
+ search_using_module_spec = true;
+ const char *arg_cstr = m_file_option.GetOptionValue().GetCurrentValue();
+ const bool use_global_module_list = true;
+ ModuleList module_list;
+ const size_t num_matches = FindModulesByName(
+ target, arg_cstr, module_list, use_global_module_list);
+ if (num_matches == 1) {
+ module_spec.GetFileSpec() =
+ module_list.GetModuleAtIndex(0)->GetFileSpec();
+ } else if (num_matches > 1) {
+ search_using_module_spec = false;
+ result.AppendErrorWithFormat(
+ "more than 1 module matched by name '%s'\n", arg_cstr);
+ } else {
+ search_using_module_spec = false;
+ result.AppendErrorWithFormat("no object file for module '%s'\n",
+ arg_cstr);
+ }
+ }
+
+ if (m_uuid_option_group.GetOptionValue().OptionWasSet()) {
+ search_using_module_spec = true;
+ module_spec.GetUUID() =
+ m_uuid_option_group.GetOptionValue().GetCurrentValue();
+ }
+
+ if (search_using_module_spec) {
+ ModuleList matching_modules;
+ target->GetImages().FindModules(module_spec, matching_modules);
+ const size_t num_matches = matching_modules.GetSize();
+
+ char path[PATH_MAX];
+ if (num_matches == 1) {
+ Module *module = matching_modules.GetModulePointerAtIndex(0);
+ if (module) {
+ ObjectFile *objfile = module->GetObjectFile();
+ if (objfile) {
+ SectionList *section_list = module->GetSectionList();
+ if (section_list) {
+ bool changed = false;
+ if (argc == 0) {
+ if (m_slide_option.GetOptionValue().OptionWasSet()) {
+ const addr_t slide =
+ m_slide_option.GetOptionValue().GetCurrentValue();
+ const bool slide_is_offset = true;
+ module->SetLoadAddress(*target, slide, slide_is_offset,
+ changed);
+ } else {
+ result.AppendError("one or more section name + load "
+ "address pair must be specified");
+ return false;
+ }
+ } else {
+ if (m_slide_option.GetOptionValue().OptionWasSet()) {
+ result.AppendError("The \"--slide <offset>\" option can't "
+ "be used in conjunction with setting "
+ "section load addresses.\n");
+ return false;
+ }
+
+ for (size_t i = 0; i < argc; i += 2) {
+ const char *sect_name = args.GetArgumentAtIndex(i);
+ const char *load_addr_cstr = args.GetArgumentAtIndex(i + 1);
+ if (sect_name && load_addr_cstr) {
+ ConstString const_sect_name(sect_name);
+ addr_t load_addr;
+ if (llvm::to_integer(load_addr_cstr, load_addr)) {
+ SectionSP section_sp(
+ section_list->FindSectionByName(const_sect_name));
+ if (section_sp) {
+ if (section_sp->IsThreadSpecific()) {
+ result.AppendErrorWithFormat(
+ "thread specific sections are not yet "
+ "supported (section '%s')\n",
+ sect_name);
+ break;
+ } else {
+ if (target->GetSectionLoadList()
+ .SetSectionLoadAddress(section_sp, load_addr))
+ changed = true;
+ result.AppendMessageWithFormat(
+ "section '%s' loaded at 0x%" PRIx64 "\n",
+ sect_name, load_addr);
+ }
+ } else {
+ result.AppendErrorWithFormat("no section found that "
+ "matches the section "
+ "name '%s'\n",
+ sect_name);
+ break;
+ }
+ } else {
+ result.AppendErrorWithFormat(
+ "invalid load address string '%s'\n", load_addr_cstr);
+ break;
+ }
+ } else {
+ if (sect_name)
+ result.AppendError("section names must be followed by "
+ "a load address.\n");
+ else
+ result.AppendError("one or more section name + load "
+ "address pair must be specified.\n");
+ break;
+ }
+ }
+ }
+
+ if (changed) {
+ target->ModulesDidLoad(matching_modules);
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (process)
+ process->Flush();
+ }
+ if (load) {
+ ProcessSP process = target->CalculateProcess();
+ Address file_entry = objfile->GetEntryPointAddress();
+ if (!process) {
+ result.AppendError("No process");
+ return false;
+ }
+ if (set_pc && !file_entry.IsValid()) {
+ result.AppendError("No entry address in object file");
+ return false;
+ }
+ std::vector<ObjectFile::LoadableData> loadables(
+ objfile->GetLoadableData(*target));
+ if (loadables.size() == 0) {
+ result.AppendError("No loadable sections");
+ return false;
+ }
+ Status error = process->WriteObjectFile(std::move(loadables));
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ return false;
+ }
+ if (set_pc) {
+ ThreadList &thread_list = process->GetThreadList();
+ RegisterContextSP reg_context(
+ thread_list.GetSelectedThread()->GetRegisterContext());
+ addr_t file_entry_addr = file_entry.GetLoadAddress(target);
+ if (!reg_context->SetPC(file_entry_addr)) {
+ result.AppendErrorWithFormat("failed to set PC value to "
+ "0x%" PRIx64 "\n",
+ file_entry_addr);
+ }
+ }
+ }
+ } else {
+ module->GetFileSpec().GetPath(path, sizeof(path));
+ result.AppendErrorWithFormat("no sections in object file '%s'\n",
+ path);
+ }
+ } else {
+ module->GetFileSpec().GetPath(path, sizeof(path));
+ result.AppendErrorWithFormat("no object file for module '%s'\n",
+ path);
+ }
+ } else {
+ FileSpec *module_spec_file = module_spec.GetFileSpecPtr();
+ if (module_spec_file) {
+ module_spec_file->GetPath(path, sizeof(path));
+ result.AppendErrorWithFormat("invalid module '%s'.\n", path);
+ } else
+ result.AppendError("no module spec");
+ }
+ } else {
+ std::string uuid_str;
+
+ if (module_spec.GetFileSpec())
+ module_spec.GetFileSpec().GetPath(path, sizeof(path));
+ else
+ path[0] = '\0';
+
+ if (module_spec.GetUUIDPtr())
+ uuid_str = module_spec.GetUUID().GetAsString();
+ if (num_matches > 1) {
+ result.AppendErrorWithFormat(
+ "multiple modules match%s%s%s%s:\n", path[0] ? " file=" : "",
+ path, !uuid_str.empty() ? " uuid=" : "", uuid_str.c_str());
+ for (size_t i = 0; i < num_matches; ++i) {
+ if (matching_modules.GetModulePointerAtIndex(i)
+ ->GetFileSpec()
+ .GetPath(path, sizeof(path)))
+ result.AppendMessageWithFormat("%s\n", path);
+ }
+ } else {
+ result.AppendErrorWithFormat(
+ "no modules were found that match%s%s%s%s.\n",
+ path[0] ? " file=" : "", path, !uuid_str.empty() ? " uuid=" : "",
+ uuid_str.c_str());
+ }
+ }
+ } else {
+ result.AppendError("either the \"--file <module>\" or the \"--uuid "
+ "<uuid>\" option must be specified.\n");
+ return false;
+ }
+ return result.Succeeded();
+ }
+
+ OptionGroupOptions m_option_group;
+ OptionGroupUUID m_uuid_option_group;
+ OptionGroupString m_file_option;
+ OptionGroupBoolean m_load_option;
+ OptionGroupBoolean m_pc_option;
+ OptionGroupUInt64 m_slide_option;
+};
+
+#pragma mark CommandObjectTargetModulesList
+// List images with associated information
+#define LLDB_OPTIONS_target_modules_list
+#include "CommandOptions.inc"
+
+class CommandObjectTargetModulesList : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+
+ const int short_option = m_getopt_table[option_idx].val;
+ if (short_option == 'g') {
+ m_use_global_module_list = true;
+ } else if (short_option == 'a') {
+ m_module_addr = OptionArgParser::ToAddress(
+ execution_context, option_arg, LLDB_INVALID_ADDRESS, &error);
+ } else {
+ unsigned long width = 0;
+ option_arg.getAsInteger(0, width);
+ m_format_array.push_back(std::make_pair(short_option, width));
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_format_array.clear();
+ m_use_global_module_list = false;
+ m_module_addr = LLDB_INVALID_ADDRESS;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_target_modules_list_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ typedef std::vector<std::pair<char, uint32_t>> FormatWidthCollection;
+ FormatWidthCollection m_format_array;
+ bool m_use_global_module_list = false;
+ lldb::addr_t m_module_addr = LLDB_INVALID_ADDRESS;
+ };
+
+ CommandObjectTargetModulesList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "target modules list",
+ "List current executable and dependent shared library images.") {
+ CommandArgumentData module_arg{eArgTypeShlibName, eArgRepeatStar};
+ m_arguments.push_back({module_arg});
+ }
+
+ ~CommandObjectTargetModulesList() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ const bool use_global_module_list = m_options.m_use_global_module_list;
+ // Define a local module list here to ensure it lives longer than any
+ // "locker" object which might lock its contents below (through the
+ // "module_list_ptr" variable).
+ ModuleList module_list;
+ if (target == nullptr && !use_global_module_list) {
+ result.AppendError("invalid target, create a debug target using the "
+ "'target create' command");
+ return false;
+ } else {
+ if (target) {
+ uint32_t addr_byte_size =
+ target->GetArchitecture().GetAddressByteSize();
+ result.GetOutputStream().SetAddressByteSize(addr_byte_size);
+ result.GetErrorStream().SetAddressByteSize(addr_byte_size);
+ }
+ // Dump all sections for all modules images
+ Stream &strm = result.GetOutputStream();
+
+ if (m_options.m_module_addr != LLDB_INVALID_ADDRESS) {
+ if (target) {
+ Address module_address;
+ if (module_address.SetLoadAddress(m_options.m_module_addr, target)) {
+ ModuleSP module_sp(module_address.GetModule());
+ if (module_sp) {
+ PrintModule(target, module_sp.get(), 0, strm);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat(
+ "Couldn't find module matching address: 0x%" PRIx64 ".",
+ m_options.m_module_addr);
+ }
+ } else {
+ result.AppendErrorWithFormat(
+ "Couldn't find module containing address: 0x%" PRIx64 ".",
+ m_options.m_module_addr);
+ }
+ } else {
+ result.AppendError(
+ "Can only look up modules by address with a valid target.");
+ }
+ return result.Succeeded();
+ }
+
+ size_t num_modules = 0;
+
+ // This locker will be locked on the mutex in module_list_ptr if it is
+ // non-nullptr. Otherwise it will lock the
+ // AllocationModuleCollectionMutex when accessing the global module list
+ // directly.
+ std::unique_lock<std::recursive_mutex> guard(
+ Module::GetAllocationModuleCollectionMutex(), std::defer_lock);
+
+ const ModuleList *module_list_ptr = nullptr;
+ const size_t argc = command.GetArgumentCount();
+ if (argc == 0) {
+ if (use_global_module_list) {
+ guard.lock();
+ num_modules = Module::GetNumberAllocatedModules();
+ } else {
+ module_list_ptr = &target->GetImages();
+ }
+ } else {
+ for (const Args::ArgEntry &arg : command) {
+ // Dump specified images (by basename or fullpath)
+ const size_t num_matches = FindModulesByName(
+ target, arg.c_str(), module_list, use_global_module_list);
+ if (num_matches == 0) {
+ if (argc == 1) {
+ result.AppendErrorWithFormat("no modules found that match '%s'",
+ arg.c_str());
+ return false;
+ }
+ }
+ }
+
+ module_list_ptr = &module_list;
+ }
+
+ std::unique_lock<std::recursive_mutex> lock;
+ if (module_list_ptr != nullptr) {
+ lock =
+ std::unique_lock<std::recursive_mutex>(module_list_ptr->GetMutex());
+
+ num_modules = module_list_ptr->GetSize();
+ }
+
+ if (num_modules > 0) {
+ for (uint32_t image_idx = 0; image_idx < num_modules; ++image_idx) {
+ ModuleSP module_sp;
+ Module *module;
+ if (module_list_ptr) {
+ module_sp = module_list_ptr->GetModuleAtIndexUnlocked(image_idx);
+ module = module_sp.get();
+ } else {
+ module = Module::GetAllocatedModuleAtIndex(image_idx);
+ module_sp = module->shared_from_this();
+ }
+
+ const size_t indent = strm.Printf("[%3u] ", image_idx);
+ PrintModule(target, module, indent, strm);
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ if (argc) {
+ if (use_global_module_list)
+ result.AppendError(
+ "the global module list has no matching modules");
+ else
+ result.AppendError("the target has no matching modules");
+ } else {
+ if (use_global_module_list)
+ result.AppendError("the global module list is empty");
+ else
+ result.AppendError(
+ "the target has no associated executable images");
+ }
+ return false;
+ }
+ }
+ return result.Succeeded();
+ }
+
+ void PrintModule(Target *target, Module *module, int indent, Stream &strm) {
+ if (module == nullptr) {
+ strm.PutCString("Null module");
+ return;
+ }
+
+ bool dump_object_name = false;
+ if (m_options.m_format_array.empty()) {
+ m_options.m_format_array.push_back(std::make_pair('u', 0));
+ m_options.m_format_array.push_back(std::make_pair('h', 0));
+ m_options.m_format_array.push_back(std::make_pair('f', 0));
+ m_options.m_format_array.push_back(std::make_pair('S', 0));
+ }
+ const size_t num_entries = m_options.m_format_array.size();
+ bool print_space = false;
+ for (size_t i = 0; i < num_entries; ++i) {
+ if (print_space)
+ strm.PutChar(' ');
+ print_space = true;
+ const char format_char = m_options.m_format_array[i].first;
+ uint32_t width = m_options.m_format_array[i].second;
+ switch (format_char) {
+ case 'A':
+ DumpModuleArchitecture(strm, module, false, width);
+ break;
+
+ case 't':
+ DumpModuleArchitecture(strm, module, true, width);
+ break;
+
+ case 'f':
+ DumpFullpath(strm, &module->GetFileSpec(), width);
+ dump_object_name = true;
+ break;
+
+ case 'd':
+ DumpDirectory(strm, &module->GetFileSpec(), width);
+ break;
+
+ case 'b':
+ DumpBasename(strm, &module->GetFileSpec(), width);
+ dump_object_name = true;
+ break;
+
+ case 'h':
+ case 'o':
+ // Image header address
+ {
+ uint32_t addr_nibble_width =
+ target ? (target->GetArchitecture().GetAddressByteSize() * 2)
+ : 16;
+
+ ObjectFile *objfile = module->GetObjectFile();
+ if (objfile) {
+ Address base_addr(objfile->GetBaseAddress());
+ if (base_addr.IsValid()) {
+ if (target && !target->GetSectionLoadList().IsEmpty()) {
+ lldb::addr_t load_addr = base_addr.GetLoadAddress(target);
+ if (load_addr == LLDB_INVALID_ADDRESS) {
+ base_addr.Dump(&strm, target,
+ Address::DumpStyleModuleWithFileAddress,
+ Address::DumpStyleFileAddress);
+ } else {
+ if (format_char == 'o') {
+ // Show the offset of slide for the image
+ strm.Printf("0x%*.*" PRIx64, addr_nibble_width,
+ addr_nibble_width,
+ load_addr - base_addr.GetFileAddress());
+ } else {
+ // Show the load address of the image
+ strm.Printf("0x%*.*" PRIx64, addr_nibble_width,
+ addr_nibble_width, load_addr);
+ }
+ }
+ break;
+ }
+ // The address was valid, but the image isn't loaded, output the
+ // address in an appropriate format
+ base_addr.Dump(&strm, target, Address::DumpStyleFileAddress);
+ break;
+ }
+ }
+ strm.Printf("%*s", addr_nibble_width + 2, "");
+ }
+ break;
+
+ case 'r': {
+ size_t ref_count = 0;
+ ModuleSP module_sp(module->shared_from_this());
+ if (module_sp) {
+ // Take one away to make sure we don't count our local "module_sp"
+ ref_count = module_sp.use_count() - 1;
+ }
+ if (width)
+ strm.Printf("{%*" PRIu64 "}", width, (uint64_t)ref_count);
+ else
+ strm.Printf("{%" PRIu64 "}", (uint64_t)ref_count);
+ } break;
+
+ case 's':
+ case 'S': {
+ if (const SymbolFile *symbol_file = module->GetSymbolFile()) {
+ const FileSpec symfile_spec =
+ symbol_file->GetObjectFile()->GetFileSpec();
+ if (format_char == 'S') {
+ // Dump symbol file only if different from module file
+ if (!symfile_spec || symfile_spec == module->GetFileSpec()) {
+ print_space = false;
+ break;
+ }
+ // Add a newline and indent past the index
+ strm.Printf("\n%*s", indent, "");
+ }
+ DumpFullpath(strm, &symfile_spec, width);
+ dump_object_name = true;
+ break;
+ }
+ strm.Printf("%.*s", width, "<NONE>");
+ } break;
+
+ case 'm':
+ strm.Format("{0:%c}", llvm::fmt_align(module->GetModificationTime(),
+ llvm::AlignStyle::Left, width));
+ break;
+
+ case 'p':
+ strm.Printf("%p", static_cast<void *>(module));
+ break;
+
+ case 'u':
+ DumpModuleUUID(strm, module);
+ break;
+
+ default:
+ break;
+ }
+ }
+ if (dump_object_name) {
+ const char *object_name = module->GetObjectName().GetCString();
+ if (object_name)
+ strm.Printf("(%s)", object_name);
+ }
+ strm.EOL();
+ }
+
+ CommandOptions m_options;
+};
+
+#pragma mark CommandObjectTargetModulesShowUnwind
+
+// Lookup unwind information in images
+#define LLDB_OPTIONS_target_modules_show_unwind
+#include "CommandOptions.inc"
+
+class CommandObjectTargetModulesShowUnwind : public CommandObjectParsed {
+public:
+ enum {
+ eLookupTypeInvalid = -1,
+ eLookupTypeAddress = 0,
+ eLookupTypeSymbol,
+ eLookupTypeFunction,
+ eLookupTypeFunctionOrSymbol,
+ kNumLookupTypes
+ };
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'a': {
+ m_str = std::string(option_arg);
+ m_type = eLookupTypeAddress;
+ m_addr = OptionArgParser::ToAddress(execution_context, option_arg,
+ LLDB_INVALID_ADDRESS, &error);
+ if (m_addr == LLDB_INVALID_ADDRESS)
+ error.SetErrorStringWithFormat("invalid address string '%s'",
+ option_arg.str().c_str());
+ break;
+ }
+
+ case 'n':
+ m_str = std::string(option_arg);
+ m_type = eLookupTypeFunctionOrSymbol;
+ break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_type = eLookupTypeInvalid;
+ m_str.clear();
+ m_addr = LLDB_INVALID_ADDRESS;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_target_modules_show_unwind_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ int m_type = eLookupTypeInvalid; // Should be a eLookupTypeXXX enum after
+ // parsing options
+ std::string m_str; // Holds name lookup
+ lldb::addr_t m_addr = LLDB_INVALID_ADDRESS; // Holds the address to lookup
+ };
+
+ CommandObjectTargetModulesShowUnwind(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "target modules show-unwind",
+ "Show synthesized unwind instructions for a function.", nullptr,
+ eCommandRequiresTarget | eCommandRequiresProcess |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {}
+
+ ~CommandObjectTargetModulesShowUnwind() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = m_exe_ctx.GetTargetPtr();
+ Process *process = m_exe_ctx.GetProcessPtr();
+ ABI *abi = nullptr;
+ if (process)
+ abi = process->GetABI().get();
+
+ if (process == nullptr) {
+ result.AppendError(
+ "You must have a process running to use this command.");
+ return false;
+ }
+
+ ThreadList threads(process->GetThreadList());
+ if (threads.GetSize() == 0) {
+ result.AppendError("The process must be paused to use this command.");
+ return false;
+ }
+
+ ThreadSP thread(threads.GetThreadAtIndex(0));
+ if (!thread) {
+ result.AppendError("The process must be paused to use this command.");
+ return false;
+ }
+
+ SymbolContextList sc_list;
+
+ if (m_options.m_type == eLookupTypeFunctionOrSymbol) {
+ ConstString function_name(m_options.m_str.c_str());
+ ModuleFunctionSearchOptions function_options;
+ function_options.include_symbols = true;
+ function_options.include_inlines = false;
+ target->GetImages().FindFunctions(function_name, eFunctionNameTypeAuto,
+ function_options, sc_list);
+ } else if (m_options.m_type == eLookupTypeAddress && target) {
+ Address addr;
+ if (target->GetSectionLoadList().ResolveLoadAddress(m_options.m_addr,
+ addr)) {
+ SymbolContext sc;
+ ModuleSP module_sp(addr.GetModule());
+ module_sp->ResolveSymbolContextForAddress(addr,
+ eSymbolContextEverything, sc);
+ if (sc.function || sc.symbol) {
+ sc_list.Append(sc);
+ }
+ }
+ } else {
+ result.AppendError(
+ "address-expression or function name option must be specified.");
+ return false;
+ }
+
+ size_t num_matches = sc_list.GetSize();
+ if (num_matches == 0) {
+ result.AppendErrorWithFormat("no unwind data found that matches '%s'.",
+ m_options.m_str.c_str());
+ return false;
+ }
+
+ for (uint32_t idx = 0; idx < num_matches; idx++) {
+ SymbolContext sc;
+ sc_list.GetContextAtIndex(idx, sc);
+ if (sc.symbol == nullptr && sc.function == nullptr)
+ continue;
+ if (!sc.module_sp || sc.module_sp->GetObjectFile() == nullptr)
+ continue;
+ AddressRange range;
+ if (!sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0,
+ false, range))
+ continue;
+ if (!range.GetBaseAddress().IsValid())
+ continue;
+ ConstString funcname(sc.GetFunctionName());
+ if (funcname.IsEmpty())
+ continue;
+ addr_t start_addr = range.GetBaseAddress().GetLoadAddress(target);
+ if (abi)
+ start_addr = abi->FixCodeAddress(start_addr);
+
+ FuncUnwindersSP func_unwinders_sp(
+ sc.module_sp->GetUnwindTable()
+ .GetUncachedFuncUnwindersContainingAddress(start_addr, sc));
+ if (!func_unwinders_sp)
+ continue;
+
+ result.GetOutputStream().Printf(
+ "UNWIND PLANS for %s`%s (start addr 0x%" PRIx64 ")\n",
+ sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(),
+ funcname.AsCString(), start_addr);
+
+ Args args;
+ target->GetUserSpecifiedTrapHandlerNames(args);
+ size_t count = args.GetArgumentCount();
+ for (size_t i = 0; i < count; i++) {
+ const char *trap_func_name = args.GetArgumentAtIndex(i);
+ if (strcmp(funcname.GetCString(), trap_func_name) == 0)
+ result.GetOutputStream().Printf(
+ "This function is "
+ "treated as a trap handler function via user setting.\n");
+ }
+ PlatformSP platform_sp(target->GetPlatform());
+ if (platform_sp) {
+ const std::vector<ConstString> trap_handler_names(
+ platform_sp->GetTrapHandlerSymbolNames());
+ for (ConstString trap_name : trap_handler_names) {
+ if (trap_name == funcname) {
+ result.GetOutputStream().Printf(
+ "This function's "
+ "name is listed by the platform as a trap handler.\n");
+ }
+ }
+ }
+
+ result.GetOutputStream().Printf("\n");
+
+ UnwindPlanSP non_callsite_unwind_plan =
+ func_unwinders_sp->GetUnwindPlanAtNonCallSite(*target, *thread);
+ if (non_callsite_unwind_plan) {
+ result.GetOutputStream().Printf(
+ "Asynchronous (not restricted to call-sites) UnwindPlan is '%s'\n",
+ non_callsite_unwind_plan->GetSourceName().AsCString());
+ }
+ UnwindPlanSP callsite_unwind_plan =
+ func_unwinders_sp->GetUnwindPlanAtCallSite(*target, *thread);
+ if (callsite_unwind_plan) {
+ result.GetOutputStream().Printf(
+ "Synchronous (restricted to call-sites) UnwindPlan is '%s'\n",
+ callsite_unwind_plan->GetSourceName().AsCString());
+ }
+ UnwindPlanSP fast_unwind_plan =
+ func_unwinders_sp->GetUnwindPlanFastUnwind(*target, *thread);
+ if (fast_unwind_plan) {
+ result.GetOutputStream().Printf(
+ "Fast UnwindPlan is '%s'\n",
+ fast_unwind_plan->GetSourceName().AsCString());
+ }
+
+ result.GetOutputStream().Printf("\n");
+
+ UnwindPlanSP assembly_sp =
+ func_unwinders_sp->GetAssemblyUnwindPlan(*target, *thread);
+ if (assembly_sp) {
+ result.GetOutputStream().Printf(
+ "Assembly language inspection UnwindPlan:\n");
+ assembly_sp->Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+
+ UnwindPlanSP of_unwind_sp =
+ func_unwinders_sp->GetObjectFileUnwindPlan(*target);
+ if (of_unwind_sp) {
+ result.GetOutputStream().Printf("object file UnwindPlan:\n");
+ of_unwind_sp->Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+
+ UnwindPlanSP of_unwind_augmented_sp =
+ func_unwinders_sp->GetObjectFileAugmentedUnwindPlan(*target, *thread);
+ if (of_unwind_augmented_sp) {
+ result.GetOutputStream().Printf("object file augmented UnwindPlan:\n");
+ of_unwind_augmented_sp->Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+
+ UnwindPlanSP ehframe_sp =
+ func_unwinders_sp->GetEHFrameUnwindPlan(*target);
+ if (ehframe_sp) {
+ result.GetOutputStream().Printf("eh_frame UnwindPlan:\n");
+ ehframe_sp->Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+
+ UnwindPlanSP ehframe_augmented_sp =
+ func_unwinders_sp->GetEHFrameAugmentedUnwindPlan(*target, *thread);
+ if (ehframe_augmented_sp) {
+ result.GetOutputStream().Printf("eh_frame augmented UnwindPlan:\n");
+ ehframe_augmented_sp->Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+
+ if (UnwindPlanSP plan_sp =
+ func_unwinders_sp->GetDebugFrameUnwindPlan(*target)) {
+ result.GetOutputStream().Printf("debug_frame UnwindPlan:\n");
+ plan_sp->Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+
+ if (UnwindPlanSP plan_sp =
+ func_unwinders_sp->GetDebugFrameAugmentedUnwindPlan(*target,
+ *thread)) {
+ result.GetOutputStream().Printf("debug_frame augmented UnwindPlan:\n");
+ plan_sp->Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+
+ UnwindPlanSP arm_unwind_sp =
+ func_unwinders_sp->GetArmUnwindUnwindPlan(*target);
+ if (arm_unwind_sp) {
+ result.GetOutputStream().Printf("ARM.exidx unwind UnwindPlan:\n");
+ arm_unwind_sp->Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+
+ if (UnwindPlanSP symfile_plan_sp =
+ func_unwinders_sp->GetSymbolFileUnwindPlan(*thread)) {
+ result.GetOutputStream().Printf("Symbol file UnwindPlan:\n");
+ symfile_plan_sp->Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+
+ UnwindPlanSP compact_unwind_sp =
+ func_unwinders_sp->GetCompactUnwindUnwindPlan(*target);
+ if (compact_unwind_sp) {
+ result.GetOutputStream().Printf("Compact unwind UnwindPlan:\n");
+ compact_unwind_sp->Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+
+ if (fast_unwind_plan) {
+ result.GetOutputStream().Printf("Fast UnwindPlan:\n");
+ fast_unwind_plan->Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+
+ ABISP abi_sp = process->GetABI();
+ if (abi_sp) {
+ UnwindPlan arch_default(lldb::eRegisterKindGeneric);
+ if (abi_sp->CreateDefaultUnwindPlan(arch_default)) {
+ result.GetOutputStream().Printf("Arch default UnwindPlan:\n");
+ arch_default.Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+
+ UnwindPlan arch_entry(lldb::eRegisterKindGeneric);
+ if (abi_sp->CreateFunctionEntryUnwindPlan(arch_entry)) {
+ result.GetOutputStream().Printf(
+ "Arch default at entry point UnwindPlan:\n");
+ arch_entry.Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+ }
+
+ result.GetOutputStream().Printf("\n");
+ }
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+// Lookup information in images
+#define LLDB_OPTIONS_target_modules_lookup
+#include "CommandOptions.inc"
+
+class CommandObjectTargetModulesLookup : public CommandObjectParsed {
+public:
+ enum {
+ eLookupTypeInvalid = -1,
+ eLookupTypeAddress = 0,
+ eLookupTypeSymbol,
+ eLookupTypeFileLine, // Line is optional
+ eLookupTypeFunction,
+ eLookupTypeFunctionOrSymbol,
+ eLookupTypeType,
+ kNumLookupTypes
+ };
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() { OptionParsingStarting(nullptr); }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'a': {
+ m_type = eLookupTypeAddress;
+ m_addr = OptionArgParser::ToAddress(execution_context, option_arg,
+ LLDB_INVALID_ADDRESS, &error);
+ } break;
+
+ case 'o':
+ if (option_arg.getAsInteger(0, m_offset))
+ error.SetErrorStringWithFormat("invalid offset string '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 's':
+ m_str = std::string(option_arg);
+ m_type = eLookupTypeSymbol;
+ break;
+
+ case 'f':
+ m_file.SetFile(option_arg, FileSpec::Style::native);
+ m_type = eLookupTypeFileLine;
+ break;
+
+ case 'i':
+ m_include_inlines = false;
+ break;
+
+ case 'l':
+ if (option_arg.getAsInteger(0, m_line_number))
+ error.SetErrorStringWithFormat("invalid line number string '%s'",
+ option_arg.str().c_str());
+ else if (m_line_number == 0)
+ error.SetErrorString("zero is an invalid line number");
+ m_type = eLookupTypeFileLine;
+ break;
+
+ case 'F':
+ m_str = std::string(option_arg);
+ m_type = eLookupTypeFunction;
+ break;
+
+ case 'n':
+ m_str = std::string(option_arg);
+ m_type = eLookupTypeFunctionOrSymbol;
+ break;
+
+ case 't':
+ m_str = std::string(option_arg);
+ m_type = eLookupTypeType;
+ break;
+
+ case 'v':
+ m_verbose = true;
+ break;
+
+ case 'A':
+ m_print_all = true;
+ break;
+
+ case 'r':
+ m_use_regex = true;
+ break;
+
+ case '\x01':
+ m_all_ranges = true;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_type = eLookupTypeInvalid;
+ m_str.clear();
+ m_file.Clear();
+ m_addr = LLDB_INVALID_ADDRESS;
+ m_offset = 0;
+ m_line_number = 0;
+ m_use_regex = false;
+ m_include_inlines = true;
+ m_all_ranges = false;
+ m_verbose = false;
+ m_print_all = false;
+ }
+
+ Status OptionParsingFinished(ExecutionContext *execution_context) override {
+ Status status;
+ if (m_all_ranges && !m_verbose) {
+ status.SetErrorString("--show-variable-ranges must be used in "
+ "conjunction with --verbose.");
+ }
+ return status;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_target_modules_lookup_options);
+ }
+
+ int m_type; // Should be a eLookupTypeXXX enum after parsing options
+ std::string m_str; // Holds name lookup
+ FileSpec m_file; // Files for file lookups
+ lldb::addr_t m_addr; // Holds the address to lookup
+ lldb::addr_t
+ m_offset; // Subtract this offset from m_addr before doing lookups.
+ uint32_t m_line_number; // Line number for file+line lookups
+ bool m_use_regex; // Name lookups in m_str are regular expressions.
+ bool m_include_inlines; // Check for inline entries when looking up by
+ // file/line.
+ bool m_all_ranges; // Print all ranges or single range.
+ bool m_verbose; // Enable verbose lookup info
+ bool m_print_all; // Print all matches, even in cases where there's a best
+ // match.
+ };
+
+ CommandObjectTargetModulesLookup(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "target modules lookup",
+ "Look up information within executable and "
+ "dependent shared library images.",
+ nullptr, eCommandRequiresTarget) {
+ CommandArgumentEntry arg;
+ CommandArgumentData file_arg;
+
+ // Define the first (and only) variant of this arg.
+ file_arg.arg_type = eArgTypeFilename;
+ file_arg.arg_repetition = eArgRepeatStar;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(file_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectTargetModulesLookup() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ bool LookupHere(CommandInterpreter &interpreter, CommandReturnObject &result,
+ bool &syntax_error) {
+ switch (m_options.m_type) {
+ case eLookupTypeAddress:
+ case eLookupTypeFileLine:
+ case eLookupTypeFunction:
+ case eLookupTypeFunctionOrSymbol:
+ case eLookupTypeSymbol:
+ default:
+ return false;
+ case eLookupTypeType:
+ break;
+ }
+
+ StackFrameSP frame = m_exe_ctx.GetFrameSP();
+
+ if (!frame)
+ return false;
+
+ const SymbolContext &sym_ctx(frame->GetSymbolContext(eSymbolContextModule));
+
+ if (!sym_ctx.module_sp)
+ return false;
+
+ switch (m_options.m_type) {
+ default:
+ return false;
+ case eLookupTypeType:
+ if (!m_options.m_str.empty()) {
+ if (LookupTypeHere(&GetSelectedTarget(), m_interpreter,
+ result.GetOutputStream(), *sym_ctx.module_sp,
+ m_options.m_str.c_str(), m_options.m_use_regex)) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ }
+ break;
+ }
+
+ return false;
+ }
+
+ bool LookupInModule(CommandInterpreter &interpreter, Module *module,
+ CommandReturnObject &result, bool &syntax_error) {
+ switch (m_options.m_type) {
+ case eLookupTypeAddress:
+ if (m_options.m_addr != LLDB_INVALID_ADDRESS) {
+ if (LookupAddressInModule(
+ m_interpreter, result.GetOutputStream(), module,
+ eSymbolContextEverything |
+ (m_options.m_verbose
+ ? static_cast<int>(eSymbolContextVariable)
+ : 0),
+ m_options.m_addr, m_options.m_offset, m_options.m_verbose,
+ m_options.m_all_ranges)) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ }
+ break;
+
+ case eLookupTypeSymbol:
+ if (!m_options.m_str.empty()) {
+ if (LookupSymbolInModule(m_interpreter, result.GetOutputStream(),
+ module, m_options.m_str.c_str(),
+ m_options.m_use_regex, m_options.m_verbose,
+ m_options.m_all_ranges)) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ }
+ break;
+
+ case eLookupTypeFileLine:
+ if (m_options.m_file) {
+ if (LookupFileAndLineInModule(
+ m_interpreter, result.GetOutputStream(), module,
+ m_options.m_file, m_options.m_line_number,
+ m_options.m_include_inlines, m_options.m_verbose,
+ m_options.m_all_ranges)) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ }
+ break;
+
+ case eLookupTypeFunctionOrSymbol:
+ case eLookupTypeFunction:
+ if (!m_options.m_str.empty()) {
+ ModuleFunctionSearchOptions function_options;
+ function_options.include_symbols =
+ m_options.m_type == eLookupTypeFunctionOrSymbol;
+ function_options.include_inlines = m_options.m_include_inlines;
+
+ if (LookupFunctionInModule(m_interpreter, result.GetOutputStream(),
+ module, m_options.m_str.c_str(),
+ m_options.m_use_regex, function_options,
+ m_options.m_verbose,
+ m_options.m_all_ranges)) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ }
+ break;
+
+ case eLookupTypeType:
+ if (!m_options.m_str.empty()) {
+ if (LookupTypeInModule(
+ &GetSelectedTarget(), m_interpreter, result.GetOutputStream(),
+ module, m_options.m_str.c_str(), m_options.m_use_regex)) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ }
+ break;
+
+ default:
+ m_options.GenerateOptionUsage(
+ result.GetErrorStream(), *this,
+ GetCommandInterpreter().GetDebugger().GetTerminalWidth());
+ syntax_error = true;
+ break;
+ }
+
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+ bool syntax_error = false;
+ uint32_t i;
+ uint32_t num_successful_lookups = 0;
+ uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
+ result.GetOutputStream().SetAddressByteSize(addr_byte_size);
+ result.GetErrorStream().SetAddressByteSize(addr_byte_size);
+ // Dump all sections for all modules images
+
+ if (command.GetArgumentCount() == 0) {
+ ModuleSP current_module;
+
+ // Where it is possible to look in the current symbol context first,
+ // try that. If this search was successful and --all was not passed,
+ // don't print anything else.
+ if (LookupHere(m_interpreter, result, syntax_error)) {
+ result.GetOutputStream().EOL();
+ num_successful_lookups++;
+ if (!m_options.m_print_all) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+ }
+
+ // Dump all sections for all other modules
+
+ const ModuleList &target_modules = target->GetImages();
+ std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
+ if (target_modules.GetSize() == 0) {
+ result.AppendError("the target has no associated executable images");
+ return false;
+ }
+
+ for (ModuleSP module_sp : target_modules.ModulesNoLocking()) {
+ if (module_sp != current_module &&
+ LookupInModule(m_interpreter, module_sp.get(), result,
+ syntax_error)) {
+ result.GetOutputStream().EOL();
+ num_successful_lookups++;
+ }
+ }
+ } else {
+ // Dump specified images (by basename or fullpath)
+ const char *arg_cstr;
+ for (i = 0; (arg_cstr = command.GetArgumentAtIndex(i)) != nullptr &&
+ !syntax_error;
+ ++i) {
+ ModuleList module_list;
+ const size_t num_matches =
+ FindModulesByName(target, arg_cstr, module_list, false);
+ if (num_matches > 0) {
+ for (size_t j = 0; j < num_matches; ++j) {
+ Module *module = module_list.GetModulePointerAtIndex(j);
+ if (module) {
+ if (LookupInModule(m_interpreter, module, result, syntax_error)) {
+ result.GetOutputStream().EOL();
+ num_successful_lookups++;
+ }
+ }
+ }
+ } else
+ result.AppendWarningWithFormat(
+ "Unable to find an image that matches '%s'.\n", arg_cstr);
+ }
+ }
+
+ if (num_successful_lookups > 0)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+#pragma mark CommandObjectMultiwordImageSearchPaths
+
+// CommandObjectMultiwordImageSearchPaths
+
+class CommandObjectTargetModulesImageSearchPaths
+ : public CommandObjectMultiword {
+public:
+ CommandObjectTargetModulesImageSearchPaths(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "target modules search-paths",
+ "Commands for managing module search paths for a target.",
+ "target modules search-paths <subcommand> [<subcommand-options>]") {
+ LoadSubCommand(
+ "add", CommandObjectSP(
+ new CommandObjectTargetModulesSearchPathsAdd(interpreter)));
+ LoadSubCommand(
+ "clear", CommandObjectSP(new CommandObjectTargetModulesSearchPathsClear(
+ interpreter)));
+ LoadSubCommand(
+ "insert",
+ CommandObjectSP(
+ new CommandObjectTargetModulesSearchPathsInsert(interpreter)));
+ LoadSubCommand(
+ "list", CommandObjectSP(new CommandObjectTargetModulesSearchPathsList(
+ interpreter)));
+ LoadSubCommand(
+ "query", CommandObjectSP(new CommandObjectTargetModulesSearchPathsQuery(
+ interpreter)));
+ }
+
+ ~CommandObjectTargetModulesImageSearchPaths() override = default;
+};
+
+#pragma mark CommandObjectTargetModules
+
+// CommandObjectTargetModules
+
+class CommandObjectTargetModules : public CommandObjectMultiword {
+public:
+ // Constructors and Destructors
+ CommandObjectTargetModules(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "target modules",
+ "Commands for accessing information for one or "
+ "more target modules.",
+ "target modules <sub-command> ...") {
+ LoadSubCommand(
+ "add", CommandObjectSP(new CommandObjectTargetModulesAdd(interpreter)));
+ LoadSubCommand("load", CommandObjectSP(new CommandObjectTargetModulesLoad(
+ interpreter)));
+ LoadSubCommand("dump", CommandObjectSP(new CommandObjectTargetModulesDump(
+ interpreter)));
+ LoadSubCommand("list", CommandObjectSP(new CommandObjectTargetModulesList(
+ interpreter)));
+ LoadSubCommand(
+ "lookup",
+ CommandObjectSP(new CommandObjectTargetModulesLookup(interpreter)));
+ LoadSubCommand(
+ "search-paths",
+ CommandObjectSP(
+ new CommandObjectTargetModulesImageSearchPaths(interpreter)));
+ LoadSubCommand(
+ "show-unwind",
+ CommandObjectSP(new CommandObjectTargetModulesShowUnwind(interpreter)));
+ }
+
+ ~CommandObjectTargetModules() override = default;
+
+private:
+ // For CommandObjectTargetModules only
+ CommandObjectTargetModules(const CommandObjectTargetModules &) = delete;
+ const CommandObjectTargetModules &
+ operator=(const CommandObjectTargetModules &) = delete;
+};
+
+class CommandObjectTargetSymbolsAdd : public CommandObjectParsed {
+public:
+ CommandObjectTargetSymbolsAdd(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "target symbols add",
+ "Add a debug symbol file to one of the target's current modules by "
+ "specifying a path to a debug symbols file or by using the options "
+ "to specify a module.",
+ "target symbols add <cmd-options> [<symfile>]",
+ eCommandRequiresTarget),
+ m_file_option(
+ LLDB_OPT_SET_1, false, "shlib", 's',
+ CommandCompletions::eModuleCompletion, eArgTypeShlibName,
+ "Locate the debug symbols for the shared library specified by "
+ "name."),
+ m_current_frame_option(
+ LLDB_OPT_SET_2, false, "frame", 'F',
+ "Locate the debug symbols for the currently selected frame.", false,
+ true),
+ m_current_stack_option(LLDB_OPT_SET_2, false, "stack", 'S',
+ "Locate the debug symbols for every frame in "
+ "the current call stack.",
+ false, true)
+
+ {
+ m_option_group.Append(&m_uuid_option_group, LLDB_OPT_SET_ALL,
+ LLDB_OPT_SET_1);
+ m_option_group.Append(&m_file_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_current_frame_option, LLDB_OPT_SET_2,
+ LLDB_OPT_SET_2);
+ m_option_group.Append(&m_current_stack_option, LLDB_OPT_SET_2,
+ LLDB_OPT_SET_2);
+ m_option_group.Finalize();
+ CommandArgumentData module_arg{eArgTypeShlibName, eArgRepeatPlain};
+ m_arguments.push_back({module_arg});
+ }
+
+ ~CommandObjectTargetSymbolsAdd() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+ request, nullptr);
+ }
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ bool AddModuleSymbols(Target *target, ModuleSpec &module_spec, bool &flush,
+ CommandReturnObject &result) {
+ const FileSpec &symbol_fspec = module_spec.GetSymbolFileSpec();
+ if (!symbol_fspec) {
+ result.AppendError(
+ "one or more executable image paths must be specified");
+ return false;
+ }
+
+ char symfile_path[PATH_MAX];
+ symbol_fspec.GetPath(symfile_path, sizeof(symfile_path));
+
+ if (!module_spec.GetUUID().IsValid()) {
+ if (!module_spec.GetFileSpec() && !module_spec.GetPlatformFileSpec())
+ module_spec.GetFileSpec().GetFilename() = symbol_fspec.GetFilename();
+ }
+
+ // Now module_spec represents a symbol file for a module that might exist
+ // in the current target. Let's find possible matches.
+ ModuleList matching_modules;
+
+ // First extract all module specs from the symbol file
+ lldb_private::ModuleSpecList symfile_module_specs;
+ if (ObjectFile::GetModuleSpecifications(module_spec.GetSymbolFileSpec(),
+ 0, 0, symfile_module_specs)) {
+ // Now extract the module spec that matches the target architecture
+ ModuleSpec target_arch_module_spec;
+ ModuleSpec symfile_module_spec;
+ target_arch_module_spec.GetArchitecture() = target->GetArchitecture();
+ if (symfile_module_specs.FindMatchingModuleSpec(target_arch_module_spec,
+ symfile_module_spec)) {
+ if (symfile_module_spec.GetUUID().IsValid()) {
+ // It has a UUID, look for this UUID in the target modules
+ ModuleSpec symfile_uuid_module_spec;
+ symfile_uuid_module_spec.GetUUID() = symfile_module_spec.GetUUID();
+ target->GetImages().FindModules(symfile_uuid_module_spec,
+ matching_modules);
+ }
+ }
+
+ if (matching_modules.IsEmpty()) {
+ // No matches yet. Iterate through the module specs to find a UUID
+ // value that we can match up to an image in our target.
+ const size_t num_symfile_module_specs = symfile_module_specs.GetSize();
+ for (size_t i = 0;
+ i < num_symfile_module_specs && matching_modules.IsEmpty(); ++i) {
+ if (symfile_module_specs.GetModuleSpecAtIndex(
+ i, symfile_module_spec)) {
+ if (symfile_module_spec.GetUUID().IsValid()) {
+ // It has a UUID. Look for this UUID in the target modules.
+ ModuleSpec symfile_uuid_module_spec;
+ symfile_uuid_module_spec.GetUUID() =
+ symfile_module_spec.GetUUID();
+ target->GetImages().FindModules(symfile_uuid_module_spec,
+ matching_modules);
+ }
+ }
+ }
+ }
+ }
+
+ // Just try to match up the file by basename if we have no matches at
+ // this point. For example, module foo might have symbols in foo.debug.
+ if (matching_modules.IsEmpty())
+ target->GetImages().FindModules(module_spec, matching_modules);
+
+ while (matching_modules.IsEmpty()) {
+ ConstString filename_no_extension(
+ module_spec.GetFileSpec().GetFileNameStrippingExtension());
+ // Empty string returned, let's bail
+ if (!filename_no_extension)
+ break;
+
+ // Check if there was no extension to strip and the basename is the same
+ if (filename_no_extension == module_spec.GetFileSpec().GetFilename())
+ break;
+
+ // Replace basename with one fewer extension
+ module_spec.GetFileSpec().GetFilename() = filename_no_extension;
+ target->GetImages().FindModules(module_spec, matching_modules);
+ }
+
+ if (matching_modules.GetSize() > 1) {
+ result.AppendErrorWithFormat("multiple modules match symbol file '%s', "
+ "use the --uuid option to resolve the "
+ "ambiguity.\n",
+ symfile_path);
+ return false;
+ }
+
+ if (matching_modules.GetSize() == 1) {
+ ModuleSP module_sp(matching_modules.GetModuleAtIndex(0));
+
+ // The module has not yet created its symbol vendor, we can just give
+ // the existing target module the symfile path to use for when it
+ // decides to create it!
+ module_sp->SetSymbolFileFileSpec(symbol_fspec);
+
+ SymbolFile *symbol_file =
+ module_sp->GetSymbolFile(true, &result.GetErrorStream());
+ if (symbol_file) {
+ ObjectFile *object_file = symbol_file->GetObjectFile();
+ if (object_file && object_file->GetFileSpec() == symbol_fspec) {
+ // Provide feedback that the symfile has been successfully added.
+ const FileSpec &module_fs = module_sp->GetFileSpec();
+ result.AppendMessageWithFormat(
+ "symbol file '%s' has been added to '%s'\n", symfile_path,
+ module_fs.GetPath().c_str());
+
+ // Let clients know something changed in the module if it is
+ // currently loaded
+ ModuleList module_list;
+ module_list.Append(module_sp);
+ target->SymbolsDidLoad(module_list);
+
+ // Make sure we load any scripting resources that may be embedded
+ // in the debug info files in case the platform supports that.
+ Status error;
+ StreamString feedback_stream;
+ module_sp->LoadScriptingResourceInTarget(target, error,
+ &feedback_stream);
+ if (error.Fail() && error.AsCString())
+ result.AppendWarningWithFormat(
+ "unable to load scripting data for module %s - error "
+ "reported was %s",
+ module_sp->GetFileSpec()
+ .GetFileNameStrippingExtension()
+ .GetCString(),
+ error.AsCString());
+ else if (feedback_stream.GetSize())
+ result.AppendWarning(feedback_stream.GetData());
+
+ flush = true;
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ }
+ // Clear the symbol file spec if anything went wrong
+ module_sp->SetSymbolFileFileSpec(FileSpec());
+ }
+
+ StreamString ss_symfile_uuid;
+ if (module_spec.GetUUID().IsValid()) {
+ ss_symfile_uuid << " (";
+ module_spec.GetUUID().Dump(&ss_symfile_uuid);
+ ss_symfile_uuid << ')';
+ }
+ result.AppendErrorWithFormat(
+ "symbol file '%s'%s does not match any existing module%s\n",
+ symfile_path, ss_symfile_uuid.GetData(),
+ !llvm::sys::fs::is_regular_file(symbol_fspec.GetPath())
+ ? "\n please specify the full path to the symbol file"
+ : "");
+ return false;
+ }
+
+ bool DownloadObjectAndSymbolFile(ModuleSpec &module_spec,
+ CommandReturnObject &result, bool &flush) {
+ Status error;
+ if (Symbols::DownloadObjectAndSymbolFile(module_spec, error)) {
+ if (module_spec.GetSymbolFileSpec())
+ return AddModuleSymbols(m_exe_ctx.GetTargetPtr(), module_spec, flush,
+ result);
+ } else {
+ result.SetError(error);
+ }
+ return false;
+ }
+
+ bool AddSymbolsForUUID(CommandReturnObject &result, bool &flush) {
+ assert(m_uuid_option_group.GetOptionValue().OptionWasSet());
+
+ ModuleSpec module_spec;
+ module_spec.GetUUID() =
+ m_uuid_option_group.GetOptionValue().GetCurrentValue();
+
+ if (!DownloadObjectAndSymbolFile(module_spec, result, flush)) {
+ StreamString error_strm;
+ error_strm.PutCString("unable to find debug symbols for UUID ");
+ module_spec.GetUUID().Dump(&error_strm);
+ result.AppendError(error_strm.GetString());
+ return false;
+ }
+
+ return true;
+ }
+
+ bool AddSymbolsForFile(CommandReturnObject &result, bool &flush) {
+ assert(m_file_option.GetOptionValue().OptionWasSet());
+
+ ModuleSpec module_spec;
+ module_spec.GetFileSpec() =
+ m_file_option.GetOptionValue().GetCurrentValue();
+
+ Target *target = m_exe_ctx.GetTargetPtr();
+ ModuleSP module_sp(target->GetImages().FindFirstModule(module_spec));
+ if (module_sp) {
+ module_spec.GetFileSpec() = module_sp->GetFileSpec();
+ module_spec.GetPlatformFileSpec() = module_sp->GetPlatformFileSpec();
+ module_spec.GetUUID() = module_sp->GetUUID();
+ module_spec.GetArchitecture() = module_sp->GetArchitecture();
+ } else {
+ module_spec.GetArchitecture() = target->GetArchitecture();
+ }
+
+ if (!DownloadObjectAndSymbolFile(module_spec, result, flush)) {
+ StreamString error_strm;
+ error_strm.PutCString(
+ "unable to find debug symbols for the executable file ");
+ error_strm << module_spec.GetFileSpec();
+ result.AppendError(error_strm.GetString());
+ return false;
+ }
+
+ return true;
+ }
+
+ bool AddSymbolsForFrame(CommandReturnObject &result, bool &flush) {
+ assert(m_current_frame_option.GetOptionValue().OptionWasSet());
+
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (!process) {
+ result.AppendError(
+ "a process must exist in order to use the --frame option");
+ return false;
+ }
+
+ const StateType process_state = process->GetState();
+ if (!StateIsStoppedState(process_state, true)) {
+ result.AppendErrorWithFormat("process is not stopped: %s",
+ StateAsCString(process_state));
+ return false;
+ }
+
+ StackFrame *frame = m_exe_ctx.GetFramePtr();
+ if (!frame) {
+ result.AppendError("invalid current frame");
+ return false;
+ }
+
+ ModuleSP frame_module_sp(
+ frame->GetSymbolContext(eSymbolContextModule).module_sp);
+ if (!frame_module_sp) {
+ result.AppendError("frame has no module");
+ return false;
+ }
+
+ ModuleSpec module_spec;
+ module_spec.GetUUID() = frame_module_sp->GetUUID();
+
+ if (FileSystem::Instance().Exists(frame_module_sp->GetPlatformFileSpec())) {
+ module_spec.GetArchitecture() = frame_module_sp->GetArchitecture();
+ module_spec.GetFileSpec() = frame_module_sp->GetPlatformFileSpec();
+ }
+
+ if (!DownloadObjectAndSymbolFile(module_spec, result, flush)) {
+ result.AppendError("unable to find debug symbols for the current frame");
+ return false;
+ }
+
+ return true;
+ }
+
+ bool AddSymbolsForStack(CommandReturnObject &result, bool &flush) {
+ assert(m_current_stack_option.GetOptionValue().OptionWasSet());
+
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (!process) {
+ result.AppendError(
+ "a process must exist in order to use the --stack option");
+ return false;
+ }
+
+ const StateType process_state = process->GetState();
+ if (!StateIsStoppedState(process_state, true)) {
+ result.AppendErrorWithFormat("process is not stopped: %s",
+ StateAsCString(process_state));
+ return false;
+ }
+
+ Thread *thread = m_exe_ctx.GetThreadPtr();
+ if (!thread) {
+ result.AppendError("invalid current thread");
+ return false;
+ }
+
+ bool symbols_found = false;
+ uint32_t frame_count = thread->GetStackFrameCount();
+ for (uint32_t i = 0; i < frame_count; ++i) {
+ lldb::StackFrameSP frame_sp = thread->GetStackFrameAtIndex(i);
+
+ ModuleSP frame_module_sp(
+ frame_sp->GetSymbolContext(eSymbolContextModule).module_sp);
+ if (!frame_module_sp)
+ continue;
+
+ ModuleSpec module_spec;
+ module_spec.GetUUID() = frame_module_sp->GetUUID();
+
+ if (FileSystem::Instance().Exists(
+ frame_module_sp->GetPlatformFileSpec())) {
+ module_spec.GetArchitecture() = frame_module_sp->GetArchitecture();
+ module_spec.GetFileSpec() = frame_module_sp->GetPlatformFileSpec();
+ }
+
+ bool current_frame_flush = false;
+ if (DownloadObjectAndSymbolFile(module_spec, result, current_frame_flush))
+ symbols_found = true;
+ flush |= current_frame_flush;
+ }
+
+ if (!symbols_found) {
+ result.AppendError(
+ "unable to find debug symbols in the current call stack");
+ return false;
+ }
+
+ return true;
+ }
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Target *target = m_exe_ctx.GetTargetPtr();
+ result.SetStatus(eReturnStatusFailed);
+ bool flush = false;
+ ModuleSpec module_spec;
+ const bool uuid_option_set =
+ m_uuid_option_group.GetOptionValue().OptionWasSet();
+ const bool file_option_set = m_file_option.GetOptionValue().OptionWasSet();
+ const bool frame_option_set =
+ m_current_frame_option.GetOptionValue().OptionWasSet();
+ const bool stack_option_set =
+ m_current_stack_option.GetOptionValue().OptionWasSet();
+ const size_t argc = args.GetArgumentCount();
+
+ if (argc == 0) {
+ if (uuid_option_set)
+ AddSymbolsForUUID(result, flush);
+ else if (file_option_set)
+ AddSymbolsForFile(result, flush);
+ else if (frame_option_set)
+ AddSymbolsForFrame(result, flush);
+ else if (stack_option_set)
+ AddSymbolsForStack(result, flush);
+ else
+ result.AppendError("one or more symbol file paths must be specified, "
+ "or options must be specified");
+ } else {
+ if (uuid_option_set) {
+ result.AppendError("specify either one or more paths to symbol files "
+ "or use the --uuid option without arguments");
+ } else if (frame_option_set) {
+ result.AppendError("specify either one or more paths to symbol files "
+ "or use the --frame option without arguments");
+ } else if (file_option_set && argc > 1) {
+ result.AppendError("specify at most one symbol file path when "
+ "--shlib option is set");
+ } else {
+ PlatformSP platform_sp(target->GetPlatform());
+
+ for (auto &entry : args.entries()) {
+ if (!entry.ref().empty()) {
+ auto &symbol_file_spec = module_spec.GetSymbolFileSpec();
+ symbol_file_spec.SetFile(entry.ref(), FileSpec::Style::native);
+ FileSystem::Instance().Resolve(symbol_file_spec);
+ if (file_option_set) {
+ module_spec.GetFileSpec() =
+ m_file_option.GetOptionValue().GetCurrentValue();
+ }
+ if (platform_sp) {
+ FileSpec symfile_spec;
+ if (platform_sp
+ ->ResolveSymbolFile(*target, module_spec, symfile_spec)
+ .Success())
+ module_spec.GetSymbolFileSpec() = symfile_spec;
+ }
+
+ bool symfile_exists =
+ FileSystem::Instance().Exists(module_spec.GetSymbolFileSpec());
+
+ if (symfile_exists) {
+ if (!AddModuleSymbols(target, module_spec, flush, result))
+ break;
+ } else {
+ std::string resolved_symfile_path =
+ module_spec.GetSymbolFileSpec().GetPath();
+ if (resolved_symfile_path != entry.ref()) {
+ result.AppendErrorWithFormat(
+ "invalid module path '%s' with resolved path '%s'\n",
+ entry.c_str(), resolved_symfile_path.c_str());
+ break;
+ }
+ result.AppendErrorWithFormat("invalid module path '%s'\n",
+ entry.c_str());
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (flush) {
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (process)
+ process->Flush();
+ }
+ return result.Succeeded();
+ }
+
+ OptionGroupOptions m_option_group;
+ OptionGroupUUID m_uuid_option_group;
+ OptionGroupFile m_file_option;
+ OptionGroupBoolean m_current_frame_option;
+ OptionGroupBoolean m_current_stack_option;
+};
+
+#pragma mark CommandObjectTargetSymbols
+
+// CommandObjectTargetSymbols
+
+class CommandObjectTargetSymbols : public CommandObjectMultiword {
+public:
+ // Constructors and Destructors
+ CommandObjectTargetSymbols(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "target symbols",
+ "Commands for adding and managing debug symbol files.",
+ "target symbols <sub-command> ...") {
+ LoadSubCommand(
+ "add", CommandObjectSP(new CommandObjectTargetSymbolsAdd(interpreter)));
+ }
+
+ ~CommandObjectTargetSymbols() override = default;
+
+private:
+ // For CommandObjectTargetModules only
+ CommandObjectTargetSymbols(const CommandObjectTargetSymbols &) = delete;
+ const CommandObjectTargetSymbols &
+ operator=(const CommandObjectTargetSymbols &) = delete;
+};
+
+#pragma mark CommandObjectTargetStopHookAdd
+
+// CommandObjectTargetStopHookAdd
+#define LLDB_OPTIONS_target_stop_hook_add
+#include "CommandOptions.inc"
+
+class CommandObjectTargetStopHookAdd : public CommandObjectParsed,
+ public IOHandlerDelegateMultiline {
+public:
+ class CommandOptions : public OptionGroup {
+ public:
+ CommandOptions() : m_line_end(UINT_MAX) {}
+
+ ~CommandOptions() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_target_stop_hook_add_options);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option =
+ g_target_stop_hook_add_options[option_idx].short_option;
+
+ switch (short_option) {
+ case 'c':
+ m_class_name = std::string(option_arg);
+ m_sym_ctx_specified = true;
+ break;
+
+ case 'e':
+ if (option_arg.getAsInteger(0, m_line_end)) {
+ error.SetErrorStringWithFormat("invalid end line number: \"%s\"",
+ option_arg.str().c_str());
+ break;
+ }
+ m_sym_ctx_specified = true;
+ break;
+
+ case 'G': {
+ bool value, success;
+ value = OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (success) {
+ m_auto_continue = value;
+ } else
+ error.SetErrorStringWithFormat(
+ "invalid boolean value '%s' passed for -G option",
+ option_arg.str().c_str());
+ } break;
+ case 'l':
+ if (option_arg.getAsInteger(0, m_line_start)) {
+ error.SetErrorStringWithFormat("invalid start line number: \"%s\"",
+ option_arg.str().c_str());
+ break;
+ }
+ m_sym_ctx_specified = true;
+ break;
+
+ case 'i':
+ m_no_inlines = true;
+ break;
+
+ case 'n':
+ m_function_name = std::string(option_arg);
+ m_func_name_type_mask |= eFunctionNameTypeAuto;
+ m_sym_ctx_specified = true;
+ break;
+
+ case 'f':
+ m_file_name = std::string(option_arg);
+ m_sym_ctx_specified = true;
+ break;
+
+ case 's':
+ m_module_name = std::string(option_arg);
+ m_sym_ctx_specified = true;
+ break;
+
+ case 't':
+ if (option_arg.getAsInteger(0, m_thread_id))
+ error.SetErrorStringWithFormat("invalid thread id string '%s'",
+ option_arg.str().c_str());
+ m_thread_specified = true;
+ break;
+
+ case 'T':
+ m_thread_name = std::string(option_arg);
+ m_thread_specified = true;
+ break;
+
+ case 'q':
+ m_queue_name = std::string(option_arg);
+ m_thread_specified = true;
+ break;
+
+ case 'x':
+ if (option_arg.getAsInteger(0, m_thread_index))
+ error.SetErrorStringWithFormat("invalid thread index string '%s'",
+ option_arg.str().c_str());
+ m_thread_specified = true;
+ break;
+
+ case 'o':
+ m_use_one_liner = true;
+ m_one_liner.push_back(std::string(option_arg));
+ break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_class_name.clear();
+ m_function_name.clear();
+ m_line_start = 0;
+ m_line_end = UINT_MAX;
+ m_file_name.clear();
+ m_module_name.clear();
+ m_func_name_type_mask = eFunctionNameTypeAuto;
+ m_thread_id = LLDB_INVALID_THREAD_ID;
+ m_thread_index = UINT32_MAX;
+ m_thread_name.clear();
+ m_queue_name.clear();
+
+ m_no_inlines = false;
+ m_sym_ctx_specified = false;
+ m_thread_specified = false;
+
+ m_use_one_liner = false;
+ m_one_liner.clear();
+ m_auto_continue = false;
+ }
+
+ std::string m_class_name;
+ std::string m_function_name;
+ uint32_t m_line_start = 0;
+ uint32_t m_line_end;
+ std::string m_file_name;
+ std::string m_module_name;
+ uint32_t m_func_name_type_mask =
+ eFunctionNameTypeAuto; // A pick from lldb::FunctionNameType.
+ lldb::tid_t m_thread_id;
+ uint32_t m_thread_index;
+ std::string m_thread_name;
+ std::string m_queue_name;
+ bool m_sym_ctx_specified = false;
+ bool m_no_inlines;
+ bool m_thread_specified = false;
+ // Instance variables to hold the values for one_liner options.
+ bool m_use_one_liner = false;
+ std::vector<std::string> m_one_liner;
+
+ bool m_auto_continue;
+ };
+
+ CommandObjectTargetStopHookAdd(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "target stop-hook add",
+ "Add a hook to be executed when the target stops."
+ "The hook can either be a list of commands or an "
+ "appropriately defined Python class. You can also "
+ "add filters so the hook only runs a certain stop "
+ "points.",
+ "target stop-hook add"),
+ IOHandlerDelegateMultiline("DONE",
+ IOHandlerDelegate::Completion::LLDBCommand),
+ m_python_class_options("scripted stop-hook", true, 'P') {
+ SetHelpLong(
+ R"(
+Command Based stop-hooks:
+-------------------------
+ Stop hooks can run a list of lldb commands by providing one or more
+ --one-line-command options. The commands will get run in the order they are
+ added. Or you can provide no commands, in which case you will enter a
+ command editor where you can enter the commands to be run.
+
+Python Based Stop Hooks:
+------------------------
+ Stop hooks can be implemented with a suitably defined Python class, whose name
+ is passed in the --python-class option.
+
+ When the stop hook is added, the class is initialized by calling:
+
+ def __init__(self, target, extra_args, internal_dict):
+
+ target: The target that the stop hook is being added to.
+ extra_args: An SBStructuredData Dictionary filled with the -key -value
+ option pairs passed to the command.
+ dict: An implementation detail provided by lldb.
+
+ Then when the stop-hook triggers, lldb will run the 'handle_stop' method.
+ The method has the signature:
+
+ def handle_stop(self, exe_ctx, stream):
+
+ exe_ctx: An SBExecutionContext for the thread that has stopped.
+ stream: An SBStream, anything written to this stream will be printed in the
+ the stop message when the process stops.
+
+ Return Value: The method returns "should_stop". If should_stop is false
+ from all the stop hook executions on threads that stopped
+ with a reason, then the process will continue. Note that this
+ will happen only after all the stop hooks are run.
+
+Filter Options:
+---------------
+ Stop hooks can be set to always run, or to only run when the stopped thread
+ matches the filter options passed on the command line. The available filter
+ options include a shared library or a thread or queue specification,
+ a line range in a source file, a function name or a class name.
+ )");
+ m_all_options.Append(&m_python_class_options,
+ LLDB_OPT_SET_1 | LLDB_OPT_SET_2,
+ LLDB_OPT_SET_FROM_TO(4, 6));
+ m_all_options.Append(&m_options);
+ m_all_options.Finalize();
+ }
+
+ ~CommandObjectTargetStopHookAdd() override = default;
+
+ Options *GetOptions() override { return &m_all_options; }
+
+protected:
+ void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
+ StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
+ if (output_sp && interactive) {
+ output_sp->PutCString(
+ "Enter your stop hook command(s). Type 'DONE' to end.\n");
+ output_sp->Flush();
+ }
+ }
+
+ void IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &line) override {
+ if (m_stop_hook_sp) {
+ if (line.empty()) {
+ StreamFileSP error_sp(io_handler.GetErrorStreamFileSP());
+ if (error_sp) {
+ error_sp->Printf("error: stop hook #%" PRIu64
+ " aborted, no commands.\n",
+ m_stop_hook_sp->GetID());
+ error_sp->Flush();
+ }
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ if (target) {
+ target->UndoCreateStopHook(m_stop_hook_sp->GetID());
+ }
+ } else {
+ // The IOHandler editor is only for command lines stop hooks:
+ Target::StopHookCommandLine *hook_ptr =
+ static_cast<Target::StopHookCommandLine *>(m_stop_hook_sp.get());
+
+ hook_ptr->SetActionFromString(line);
+ StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
+ if (output_sp) {
+ output_sp->Printf("Stop hook #%" PRIu64 " added.\n",
+ m_stop_hook_sp->GetID());
+ output_sp->Flush();
+ }
+ }
+ m_stop_hook_sp.reset();
+ }
+ io_handler.SetIsDone(true);
+ }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ m_stop_hook_sp.reset();
+
+ Target &target = GetSelectedOrDummyTarget();
+ Target::StopHookSP new_hook_sp =
+ target.CreateStopHook(m_python_class_options.GetName().empty() ?
+ Target::StopHook::StopHookKind::CommandBased
+ : Target::StopHook::StopHookKind::ScriptBased);
+
+ // First step, make the specifier.
+ std::unique_ptr<SymbolContextSpecifier> specifier_up;
+ if (m_options.m_sym_ctx_specified) {
+ specifier_up = std::make_unique<SymbolContextSpecifier>(
+ GetDebugger().GetSelectedTarget());
+
+ if (!m_options.m_module_name.empty()) {
+ specifier_up->AddSpecification(
+ m_options.m_module_name.c_str(),
+ SymbolContextSpecifier::eModuleSpecified);
+ }
+
+ if (!m_options.m_class_name.empty()) {
+ specifier_up->AddSpecification(
+ m_options.m_class_name.c_str(),
+ SymbolContextSpecifier::eClassOrNamespaceSpecified);
+ }
+
+ if (!m_options.m_file_name.empty()) {
+ specifier_up->AddSpecification(m_options.m_file_name.c_str(),
+ SymbolContextSpecifier::eFileSpecified);
+ }
+
+ if (m_options.m_line_start != 0) {
+ specifier_up->AddLineSpecification(
+ m_options.m_line_start,
+ SymbolContextSpecifier::eLineStartSpecified);
+ }
+
+ if (m_options.m_line_end != UINT_MAX) {
+ specifier_up->AddLineSpecification(
+ m_options.m_line_end, SymbolContextSpecifier::eLineEndSpecified);
+ }
+
+ if (!m_options.m_function_name.empty()) {
+ specifier_up->AddSpecification(
+ m_options.m_function_name.c_str(),
+ SymbolContextSpecifier::eFunctionSpecified);
+ }
+ }
+
+ if (specifier_up)
+ new_hook_sp->SetSpecifier(specifier_up.release());
+
+ // Next see if any of the thread options have been entered:
+
+ if (m_options.m_thread_specified) {
+ ThreadSpec *thread_spec = new ThreadSpec();
+
+ if (m_options.m_thread_id != LLDB_INVALID_THREAD_ID) {
+ thread_spec->SetTID(m_options.m_thread_id);
+ }
+
+ if (m_options.m_thread_index != UINT32_MAX)
+ thread_spec->SetIndex(m_options.m_thread_index);
+
+ if (!m_options.m_thread_name.empty())
+ thread_spec->SetName(m_options.m_thread_name.c_str());
+
+ if (!m_options.m_queue_name.empty())
+ thread_spec->SetQueueName(m_options.m_queue_name.c_str());
+
+ new_hook_sp->SetThreadSpecifier(thread_spec);
+ }
+
+ new_hook_sp->SetAutoContinue(m_options.m_auto_continue);
+ if (m_options.m_use_one_liner) {
+ // This is a command line stop hook:
+ Target::StopHookCommandLine *hook_ptr =
+ static_cast<Target::StopHookCommandLine *>(new_hook_sp.get());
+ hook_ptr->SetActionFromStrings(m_options.m_one_liner);
+ result.AppendMessageWithFormat("Stop hook #%" PRIu64 " added.\n",
+ new_hook_sp->GetID());
+ } else if (!m_python_class_options.GetName().empty()) {
+ // This is a scripted stop hook:
+ Target::StopHookScripted *hook_ptr =
+ static_cast<Target::StopHookScripted *>(new_hook_sp.get());
+ Status error = hook_ptr->SetScriptCallback(
+ m_python_class_options.GetName(),
+ m_python_class_options.GetStructuredData());
+ if (error.Success())
+ result.AppendMessageWithFormat("Stop hook #%" PRIu64 " added.\n",
+ new_hook_sp->GetID());
+ else {
+ // FIXME: Set the stop hook ID counter back.
+ result.AppendErrorWithFormat("Couldn't add stop hook: %s",
+ error.AsCString());
+ target.UndoCreateStopHook(new_hook_sp->GetID());
+ return false;
+ }
+ } else {
+ m_stop_hook_sp = new_hook_sp;
+ m_interpreter.GetLLDBCommandsFromIOHandler("> ", // Prompt
+ *this); // IOHandlerDelegate
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+ OptionGroupPythonClassWithDict m_python_class_options;
+ OptionGroupOptions m_all_options;
+
+ Target::StopHookSP m_stop_hook_sp;
+};
+
+#pragma mark CommandObjectTargetStopHookDelete
+
+// CommandObjectTargetStopHookDelete
+
+class CommandObjectTargetStopHookDelete : public CommandObjectParsed {
+public:
+ CommandObjectTargetStopHookDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "target stop-hook delete",
+ "Delete a stop-hook.",
+ "target stop-hook delete [<idx>]") {
+ CommandArgumentData hook_arg{eArgTypeStopHookID, eArgRepeatStar};
+ m_arguments.push_back({hook_arg});
+ }
+
+ ~CommandObjectTargetStopHookDelete() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eStopHookIDCompletion,
+ request, nullptr);
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target &target = GetSelectedOrDummyTarget();
+ // FIXME: see if we can use the breakpoint id style parser?
+ size_t num_args = command.GetArgumentCount();
+ if (num_args == 0) {
+ if (!m_interpreter.Confirm("Delete all stop hooks?", true)) {
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else {
+ target.RemoveAllStopHooks();
+ }
+ } else {
+ for (size_t i = 0; i < num_args; i++) {
+ lldb::user_id_t user_id;
+ if (!llvm::to_integer(command.GetArgumentAtIndex(i), user_id)) {
+ result.AppendErrorWithFormat("invalid stop hook id: \"%s\".\n",
+ command.GetArgumentAtIndex(i));
+ return false;
+ }
+ if (!target.RemoveStopHookByID(user_id)) {
+ result.AppendErrorWithFormat("unknown stop hook id: \"%s\".\n",
+ command.GetArgumentAtIndex(i));
+ return false;
+ }
+ }
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectTargetStopHookEnableDisable
+
+// CommandObjectTargetStopHookEnableDisable
+
+class CommandObjectTargetStopHookEnableDisable : public CommandObjectParsed {
+public:
+ CommandObjectTargetStopHookEnableDisable(CommandInterpreter &interpreter,
+ bool enable, const char *name,
+ const char *help, const char *syntax)
+ : CommandObjectParsed(interpreter, name, help, syntax), m_enable(enable) {
+ CommandArgumentData hook_arg{eArgTypeStopHookID, eArgRepeatStar};
+ m_arguments.push_back({hook_arg});
+ }
+
+ ~CommandObjectTargetStopHookEnableDisable() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (request.GetCursorIndex())
+ return;
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eStopHookIDCompletion,
+ request, nullptr);
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target &target = GetSelectedOrDummyTarget();
+ // FIXME: see if we can use the breakpoint id style parser?
+ size_t num_args = command.GetArgumentCount();
+ bool success;
+
+ if (num_args == 0) {
+ target.SetAllStopHooksActiveState(m_enable);
+ } else {
+ for (size_t i = 0; i < num_args; i++) {
+ lldb::user_id_t user_id;
+ if (!llvm::to_integer(command.GetArgumentAtIndex(i), user_id)) {
+ result.AppendErrorWithFormat("invalid stop hook id: \"%s\".\n",
+ command.GetArgumentAtIndex(i));
+ return false;
+ }
+ success = target.SetStopHookActiveStateByID(user_id, m_enable);
+ if (!success) {
+ result.AppendErrorWithFormat("unknown stop hook id: \"%s\".\n",
+ command.GetArgumentAtIndex(i));
+ return false;
+ }
+ }
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+ }
+
+private:
+ bool m_enable;
+};
+
+#pragma mark CommandObjectTargetStopHookList
+
+// CommandObjectTargetStopHookList
+
+class CommandObjectTargetStopHookList : public CommandObjectParsed {
+public:
+ CommandObjectTargetStopHookList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "target stop-hook list",
+ "List all stop-hooks.", "target stop-hook list") {}
+
+ ~CommandObjectTargetStopHookList() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target &target = GetSelectedOrDummyTarget();
+
+ size_t num_hooks = target.GetNumStopHooks();
+ if (num_hooks == 0) {
+ result.GetOutputStream().PutCString("No stop hooks.\n");
+ } else {
+ for (size_t i = 0; i < num_hooks; i++) {
+ Target::StopHookSP this_hook = target.GetStopHookAtIndex(i);
+ if (i > 0)
+ result.GetOutputStream().PutCString("\n");
+ this_hook->GetDescription(&(result.GetOutputStream()),
+ eDescriptionLevelFull);
+ }
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectMultiwordTargetStopHooks
+
+// CommandObjectMultiwordTargetStopHooks
+
+class CommandObjectMultiwordTargetStopHooks : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordTargetStopHooks(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "target stop-hook",
+ "Commands for operating on debugger target stop-hooks.",
+ "target stop-hook <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("add", CommandObjectSP(
+ new CommandObjectTargetStopHookAdd(interpreter)));
+ LoadSubCommand(
+ "delete",
+ CommandObjectSP(new CommandObjectTargetStopHookDelete(interpreter)));
+ LoadSubCommand("disable",
+ CommandObjectSP(new CommandObjectTargetStopHookEnableDisable(
+ interpreter, false, "target stop-hook disable [<id>]",
+ "Disable a stop-hook.", "target stop-hook disable")));
+ LoadSubCommand("enable",
+ CommandObjectSP(new CommandObjectTargetStopHookEnableDisable(
+ interpreter, true, "target stop-hook enable [<id>]",
+ "Enable a stop-hook.", "target stop-hook enable")));
+ LoadSubCommand("list", CommandObjectSP(new CommandObjectTargetStopHookList(
+ interpreter)));
+ }
+
+ ~CommandObjectMultiwordTargetStopHooks() override = default;
+};
+
+#pragma mark CommandObjectTargetDumpTypesystem
+
+/// Dumps the TypeSystem of the selected Target.
+class CommandObjectTargetDumpTypesystem : public CommandObjectParsed {
+public:
+ CommandObjectTargetDumpTypesystem(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "target dump typesystem",
+ "Dump the state of the target's internal type system.\n"
+ "Intended to be used for debugging LLDB itself.",
+ nullptr, eCommandRequiresTarget) {}
+
+ ~CommandObjectTargetDumpTypesystem() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ // Go over every scratch TypeSystem and dump to the command output.
+ for (TypeSystem *ts : GetSelectedTarget().GetScratchTypeSystems())
+ ts->Dump(result.GetOutputStream().AsRawOstream());
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectTargetDump
+
+/// Multi-word command for 'target dump'.
+class CommandObjectTargetDump : public CommandObjectMultiword {
+public:
+ // Constructors and Destructors
+ CommandObjectTargetDump(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "target dump",
+ "Commands for dumping information about the target.",
+ "target dump [typesystem]") {
+ LoadSubCommand(
+ "typesystem",
+ CommandObjectSP(new CommandObjectTargetDumpTypesystem(interpreter)));
+ }
+
+ ~CommandObjectTargetDump() override = default;
+};
+
+#pragma mark CommandObjectMultiwordTarget
+
+// CommandObjectMultiwordTarget
+
+CommandObjectMultiwordTarget::CommandObjectMultiwordTarget(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "target",
+ "Commands for operating on debugger targets.",
+ "target <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("create",
+ CommandObjectSP(new CommandObjectTargetCreate(interpreter)));
+ LoadSubCommand("delete",
+ CommandObjectSP(new CommandObjectTargetDelete(interpreter)));
+ LoadSubCommand("dump",
+ CommandObjectSP(new CommandObjectTargetDump(interpreter)));
+ LoadSubCommand("list",
+ CommandObjectSP(new CommandObjectTargetList(interpreter)));
+ LoadSubCommand("select",
+ CommandObjectSP(new CommandObjectTargetSelect(interpreter)));
+ LoadSubCommand("show-launch-environment",
+ CommandObjectSP(new CommandObjectTargetShowLaunchEnvironment(
+ interpreter)));
+ LoadSubCommand(
+ "stop-hook",
+ CommandObjectSP(new CommandObjectMultiwordTargetStopHooks(interpreter)));
+ LoadSubCommand("modules",
+ CommandObjectSP(new CommandObjectTargetModules(interpreter)));
+ LoadSubCommand("symbols",
+ CommandObjectSP(new CommandObjectTargetSymbols(interpreter)));
+ LoadSubCommand("variable",
+ CommandObjectSP(new CommandObjectTargetVariable(interpreter)));
+}
+
+CommandObjectMultiwordTarget::~CommandObjectMultiwordTarget() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectTarget.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectTarget.h
new file mode 100644
index 000000000000..94afc00064c8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectTarget.h
@@ -0,0 +1,27 @@
+//===-- CommandObjectTarget.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTTARGET_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTTARGET_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+// CommandObjectMultiwordTarget
+
+class CommandObjectMultiwordTarget : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordTarget(CommandInterpreter &interpreter);
+
+ ~CommandObjectMultiwordTarget() override;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTTARGET_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectThread.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectThread.cpp
new file mode 100644
index 000000000000..ad49d27bb9a7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectThread.cpp
@@ -0,0 +1,2527 @@
+//===-- CommandObjectThread.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectThread.h"
+
+#include <memory>
+#include <sstream>
+
+#include "CommandObjectThreadUtil.h"
+#include "CommandObjectTrace.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionGroupPythonClassWithDict.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/LineEntry.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/SystemRuntime.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/ThreadPlanStepInRange.h"
+#include "lldb/Target/Trace.h"
+#include "lldb/Target/TraceDumper.h"
+#include "lldb/Utility/State.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// CommandObjectThreadBacktrace
+#define LLDB_OPTIONS_thread_backtrace
+#include "CommandOptions.inc"
+
+class CommandObjectThreadBacktrace : public CommandObjectIterateOverThreads {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() {
+ // Keep default values of all options in one place: OptionParsingStarting
+ // ()
+ OptionParsingStarting(nullptr);
+ }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'c': {
+ int32_t input_count = 0;
+ if (option_arg.getAsInteger(0, m_count)) {
+ m_count = UINT32_MAX;
+ error.SetErrorStringWithFormat(
+ "invalid integer value for option '%c'", short_option);
+ } else if (input_count < 0)
+ m_count = UINT32_MAX;
+ } break;
+ case 's':
+ if (option_arg.getAsInteger(0, m_start))
+ error.SetErrorStringWithFormat(
+ "invalid integer value for option '%c'", short_option);
+ break;
+ case 'e': {
+ bool success;
+ m_extended_backtrace =
+ OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "invalid boolean value for option '%c'", short_option);
+ } break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_count = UINT32_MAX;
+ m_start = 0;
+ m_extended_backtrace = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_thread_backtrace_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ uint32_t m_count;
+ uint32_t m_start;
+ bool m_extended_backtrace;
+ };
+
+ CommandObjectThreadBacktrace(CommandInterpreter &interpreter)
+ : CommandObjectIterateOverThreads(
+ interpreter, "thread backtrace",
+ "Show thread call stacks. Defaults to the current thread, thread "
+ "indexes can be specified as arguments.\n"
+ "Use the thread-index \"all\" to see all threads.\n"
+ "Use the thread-index \"unique\" to see threads grouped by unique "
+ "call stacks.\n"
+ "Use 'settings set frame-format' to customize the printing of "
+ "frames in the backtrace and 'settings set thread-format' to "
+ "customize the thread header.",
+ nullptr,
+ eCommandRequiresProcess | eCommandRequiresThread |
+ eCommandTryTargetAPILock | eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused) {}
+
+ ~CommandObjectThreadBacktrace() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ llvm::Optional<std::string> GetRepeatCommand(Args &current_args,
+ uint32_t idx) override {
+ llvm::StringRef count_opt("--count");
+ llvm::StringRef start_opt("--start");
+
+ // If no "count" was provided, we are dumping the entire backtrace, so
+ // there isn't a repeat command. So we search for the count option in
+ // the args, and if we find it, we make a copy and insert or modify the
+ // start option's value to start count indices greater.
+
+ Args copy_args(current_args);
+ size_t num_entries = copy_args.GetArgumentCount();
+ // These two point at the index of the option value if found.
+ size_t count_idx = 0;
+ size_t start_idx = 0;
+ size_t count_val = 0;
+ size_t start_val = 0;
+
+ for (size_t idx = 0; idx < num_entries; idx++) {
+ llvm::StringRef arg_string = copy_args[idx].ref();
+ if (arg_string.equals("-c") || count_opt.startswith(arg_string)) {
+ idx++;
+ if (idx == num_entries)
+ return llvm::None;
+ count_idx = idx;
+ if (copy_args[idx].ref().getAsInteger(0, count_val))
+ return llvm::None;
+ } else if (arg_string.equals("-s") || start_opt.startswith(arg_string)) {
+ idx++;
+ if (idx == num_entries)
+ return llvm::None;
+ start_idx = idx;
+ if (copy_args[idx].ref().getAsInteger(0, start_val))
+ return llvm::None;
+ }
+ }
+ if (count_idx == 0)
+ return llvm::None;
+
+ std::string new_start_val = llvm::formatv("{0}", start_val + count_val);
+ if (start_idx == 0) {
+ copy_args.AppendArgument(start_opt);
+ copy_args.AppendArgument(new_start_val);
+ } else {
+ copy_args.ReplaceArgumentAtIndex(start_idx, new_start_val);
+ }
+ std::string repeat_command;
+ if (!copy_args.GetQuotedCommandString(repeat_command))
+ return llvm::None;
+ return repeat_command;
+ }
+
+protected:
+ void DoExtendedBacktrace(Thread *thread, CommandReturnObject &result) {
+ SystemRuntime *runtime = thread->GetProcess()->GetSystemRuntime();
+ if (runtime) {
+ Stream &strm = result.GetOutputStream();
+ const std::vector<ConstString> &types =
+ runtime->GetExtendedBacktraceTypes();
+ for (auto type : types) {
+ ThreadSP ext_thread_sp = runtime->GetExtendedBacktraceThread(
+ thread->shared_from_this(), type);
+ if (ext_thread_sp && ext_thread_sp->IsValid()) {
+ const uint32_t num_frames_with_source = 0;
+ const bool stop_format = false;
+ if (ext_thread_sp->GetStatus(strm, m_options.m_start,
+ m_options.m_count,
+ num_frames_with_source, stop_format)) {
+ DoExtendedBacktrace(ext_thread_sp.get(), result);
+ }
+ }
+ }
+ }
+ }
+
+ bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override {
+ ThreadSP thread_sp =
+ m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid);
+ if (!thread_sp) {
+ result.AppendErrorWithFormat(
+ "thread disappeared while computing backtraces: 0x%" PRIx64 "\n",
+ tid);
+ return false;
+ }
+
+ Thread *thread = thread_sp.get();
+
+ Stream &strm = result.GetOutputStream();
+
+ // Only dump stack info if we processing unique stacks.
+ const bool only_stacks = m_unique_stacks;
+
+ // Don't show source context when doing backtraces.
+ const uint32_t num_frames_with_source = 0;
+ const bool stop_format = true;
+ if (!thread->GetStatus(strm, m_options.m_start, m_options.m_count,
+ num_frames_with_source, stop_format, only_stacks)) {
+ result.AppendErrorWithFormat(
+ "error displaying backtrace for thread: \"0x%4.4x\"\n",
+ thread->GetIndexID());
+ return false;
+ }
+ if (m_options.m_extended_backtrace) {
+ DoExtendedBacktrace(thread, result);
+ }
+
+ return true;
+ }
+
+ CommandOptions m_options;
+};
+
+enum StepScope { eStepScopeSource, eStepScopeInstruction };
+
+static constexpr OptionEnumValueElement g_tri_running_mode[] = {
+ {eOnlyThisThread, "this-thread", "Run only this thread"},
+ {eAllThreads, "all-threads", "Run all threads"},
+ {eOnlyDuringStepping, "while-stepping",
+ "Run only this thread while stepping"}};
+
+static constexpr OptionEnumValues TriRunningModes() {
+ return OptionEnumValues(g_tri_running_mode);
+}
+
+#define LLDB_OPTIONS_thread_step_scope
+#include "CommandOptions.inc"
+
+class ThreadStepScopeOptionGroup : public OptionGroup {
+public:
+ ThreadStepScopeOptionGroup() {
+ // Keep default values of all options in one place: OptionParsingStarting
+ // ()
+ OptionParsingStarting(nullptr);
+ }
+
+ ~ThreadStepScopeOptionGroup() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_thread_step_scope_options);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option =
+ g_thread_step_scope_options[option_idx].short_option;
+
+ switch (short_option) {
+ case 'a': {
+ bool success;
+ bool avoid_no_debug =
+ OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (!success)
+ error.SetErrorStringWithFormat("invalid boolean value for option '%c'",
+ short_option);
+ else {
+ m_step_in_avoid_no_debug = avoid_no_debug ? eLazyBoolYes : eLazyBoolNo;
+ }
+ } break;
+
+ case 'A': {
+ bool success;
+ bool avoid_no_debug =
+ OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (!success)
+ error.SetErrorStringWithFormat("invalid boolean value for option '%c'",
+ short_option);
+ else {
+ m_step_out_avoid_no_debug = avoid_no_debug ? eLazyBoolYes : eLazyBoolNo;
+ }
+ } break;
+
+ case 'c':
+ if (option_arg.getAsInteger(0, m_step_count))
+ error.SetErrorStringWithFormat("invalid step count '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'm': {
+ auto enum_values = GetDefinitions()[option_idx].enum_values;
+ m_run_mode = (lldb::RunMode)OptionArgParser::ToOptionEnum(
+ option_arg, enum_values, eOnlyDuringStepping, error);
+ } break;
+
+ case 'e':
+ if (option_arg == "block") {
+ m_end_line_is_block_end = true;
+ break;
+ }
+ if (option_arg.getAsInteger(0, m_end_line))
+ error.SetErrorStringWithFormat("invalid end line number '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'r':
+ m_avoid_regexp.clear();
+ m_avoid_regexp.assign(std::string(option_arg));
+ break;
+
+ case 't':
+ m_step_in_target.clear();
+ m_step_in_target.assign(std::string(option_arg));
+ break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_step_in_avoid_no_debug = eLazyBoolCalculate;
+ m_step_out_avoid_no_debug = eLazyBoolCalculate;
+ m_run_mode = eOnlyDuringStepping;
+
+ // Check if we are in Non-Stop mode
+ TargetSP target_sp =
+ execution_context ? execution_context->GetTargetSP() : TargetSP();
+ ProcessSP process_sp =
+ execution_context ? execution_context->GetProcessSP() : ProcessSP();
+ if (process_sp && process_sp->GetSteppingRunsAllThreads())
+ m_run_mode = eAllThreads;
+
+ m_avoid_regexp.clear();
+ m_step_in_target.clear();
+ m_step_count = 1;
+ m_end_line = LLDB_INVALID_LINE_NUMBER;
+ m_end_line_is_block_end = false;
+ }
+
+ // Instance variables to hold the values for command options.
+ LazyBool m_step_in_avoid_no_debug;
+ LazyBool m_step_out_avoid_no_debug;
+ RunMode m_run_mode;
+ std::string m_avoid_regexp;
+ std::string m_step_in_target;
+ uint32_t m_step_count;
+ uint32_t m_end_line;
+ bool m_end_line_is_block_end;
+};
+
+class CommandObjectThreadStepWithTypeAndScope : public CommandObjectParsed {
+public:
+ CommandObjectThreadStepWithTypeAndScope(CommandInterpreter &interpreter,
+ const char *name, const char *help,
+ const char *syntax,
+ StepType step_type,
+ StepScope step_scope)
+ : CommandObjectParsed(interpreter, name, help, syntax,
+ eCommandRequiresProcess | eCommandRequiresThread |
+ eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused),
+ m_step_type(step_type), m_step_scope(step_scope),
+ m_class_options("scripted step") {
+ CommandArgumentEntry arg;
+ CommandArgumentData thread_id_arg;
+
+ // Define the first (and only) variant of this arg.
+ thread_id_arg.arg_type = eArgTypeThreadID;
+ thread_id_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(thread_id_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+
+ if (step_type == eStepTypeScripted) {
+ m_all_options.Append(&m_class_options, LLDB_OPT_SET_1 | LLDB_OPT_SET_2,
+ LLDB_OPT_SET_1);
+ }
+ m_all_options.Append(&m_options);
+ m_all_options.Finalize();
+ }
+
+ ~CommandObjectThreadStepWithTypeAndScope() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (request.GetCursorIndex())
+ return;
+
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eThreadIndexCompletion,
+ request, nullptr);
+ }
+
+ Options *GetOptions() override { return &m_all_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+ bool synchronous_execution = m_interpreter.GetSynchronous();
+
+ const uint32_t num_threads = process->GetThreadList().GetSize();
+ Thread *thread = nullptr;
+
+ if (command.GetArgumentCount() == 0) {
+ thread = GetDefaultThread();
+
+ if (thread == nullptr) {
+ result.AppendError("no selected thread in process");
+ return false;
+ }
+ } else {
+ const char *thread_idx_cstr = command.GetArgumentAtIndex(0);
+ uint32_t step_thread_idx;
+
+ if (!llvm::to_integer(thread_idx_cstr, step_thread_idx)) {
+ result.AppendErrorWithFormat("invalid thread index '%s'.\n",
+ thread_idx_cstr);
+ return false;
+ }
+ thread =
+ process->GetThreadList().FindThreadByIndexID(step_thread_idx).get();
+ if (thread == nullptr) {
+ result.AppendErrorWithFormat(
+ "Thread index %u is out of range (valid values are 0 - %u).\n",
+ step_thread_idx, num_threads);
+ return false;
+ }
+ }
+
+ if (m_step_type == eStepTypeScripted) {
+ if (m_class_options.GetName().empty()) {
+ result.AppendErrorWithFormat("empty class name for scripted step.");
+ return false;
+ } else if (!GetDebugger().GetScriptInterpreter()->CheckObjectExists(
+ m_class_options.GetName().c_str())) {
+ result.AppendErrorWithFormat(
+ "class for scripted step: \"%s\" does not exist.",
+ m_class_options.GetName().c_str());
+ return false;
+ }
+ }
+
+ if (m_options.m_end_line != LLDB_INVALID_LINE_NUMBER &&
+ m_step_type != eStepTypeInto) {
+ result.AppendErrorWithFormat(
+ "end line option is only valid for step into");
+ return false;
+ }
+
+ const bool abort_other_plans = false;
+ const lldb::RunMode stop_other_threads = m_options.m_run_mode;
+
+ // This is a bit unfortunate, but not all the commands in this command
+ // object support only while stepping, so I use the bool for them.
+ bool bool_stop_other_threads;
+ if (m_options.m_run_mode == eAllThreads)
+ bool_stop_other_threads = false;
+ else if (m_options.m_run_mode == eOnlyDuringStepping)
+ bool_stop_other_threads = (m_step_type != eStepTypeOut);
+ else
+ bool_stop_other_threads = true;
+
+ ThreadPlanSP new_plan_sp;
+ Status new_plan_status;
+
+ if (m_step_type == eStepTypeInto) {
+ StackFrame *frame = thread->GetStackFrameAtIndex(0).get();
+ assert(frame != nullptr);
+
+ if (frame->HasDebugInformation()) {
+ AddressRange range;
+ SymbolContext sc = frame->GetSymbolContext(eSymbolContextEverything);
+ if (m_options.m_end_line != LLDB_INVALID_LINE_NUMBER) {
+ Status error;
+ if (!sc.GetAddressRangeFromHereToEndLine(m_options.m_end_line, range,
+ error)) {
+ result.AppendErrorWithFormat("invalid end-line option: %s.",
+ error.AsCString());
+ return false;
+ }
+ } else if (m_options.m_end_line_is_block_end) {
+ Status error;
+ Block *block = frame->GetSymbolContext(eSymbolContextBlock).block;
+ if (!block) {
+ result.AppendErrorWithFormat("Could not find the current block.");
+ return false;
+ }
+
+ AddressRange block_range;
+ Address pc_address = frame->GetFrameCodeAddress();
+ block->GetRangeContainingAddress(pc_address, block_range);
+ if (!block_range.GetBaseAddress().IsValid()) {
+ result.AppendErrorWithFormat(
+ "Could not find the current block address.");
+ return false;
+ }
+ lldb::addr_t pc_offset_in_block =
+ pc_address.GetFileAddress() -
+ block_range.GetBaseAddress().GetFileAddress();
+ lldb::addr_t range_length =
+ block_range.GetByteSize() - pc_offset_in_block;
+ range = AddressRange(pc_address, range_length);
+ } else {
+ range = sc.line_entry.range;
+ }
+
+ new_plan_sp = thread->QueueThreadPlanForStepInRange(
+ abort_other_plans, range,
+ frame->GetSymbolContext(eSymbolContextEverything),
+ m_options.m_step_in_target.c_str(), stop_other_threads,
+ new_plan_status, m_options.m_step_in_avoid_no_debug,
+ m_options.m_step_out_avoid_no_debug);
+
+ if (new_plan_sp && !m_options.m_avoid_regexp.empty()) {
+ ThreadPlanStepInRange *step_in_range_plan =
+ static_cast<ThreadPlanStepInRange *>(new_plan_sp.get());
+ step_in_range_plan->SetAvoidRegexp(m_options.m_avoid_regexp.c_str());
+ }
+ } else
+ new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction(
+ false, abort_other_plans, bool_stop_other_threads, new_plan_status);
+ } else if (m_step_type == eStepTypeOver) {
+ StackFrame *frame = thread->GetStackFrameAtIndex(0).get();
+
+ if (frame->HasDebugInformation())
+ new_plan_sp = thread->QueueThreadPlanForStepOverRange(
+ abort_other_plans,
+ frame->GetSymbolContext(eSymbolContextEverything).line_entry,
+ frame->GetSymbolContext(eSymbolContextEverything),
+ stop_other_threads, new_plan_status,
+ m_options.m_step_out_avoid_no_debug);
+ else
+ new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction(
+ true, abort_other_plans, bool_stop_other_threads, new_plan_status);
+ } else if (m_step_type == eStepTypeTrace) {
+ new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction(
+ false, abort_other_plans, bool_stop_other_threads, new_plan_status);
+ } else if (m_step_type == eStepTypeTraceOver) {
+ new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction(
+ true, abort_other_plans, bool_stop_other_threads, new_plan_status);
+ } else if (m_step_type == eStepTypeOut) {
+ new_plan_sp = thread->QueueThreadPlanForStepOut(
+ abort_other_plans, nullptr, false, bool_stop_other_threads, eVoteYes,
+ eVoteNoOpinion, thread->GetSelectedFrameIndex(), new_plan_status,
+ m_options.m_step_out_avoid_no_debug);
+ } else if (m_step_type == eStepTypeScripted) {
+ new_plan_sp = thread->QueueThreadPlanForStepScripted(
+ abort_other_plans, m_class_options.GetName().c_str(),
+ m_class_options.GetStructuredData(), bool_stop_other_threads,
+ new_plan_status);
+ } else {
+ result.AppendError("step type is not supported");
+ return false;
+ }
+
+ // If we got a new plan, then set it to be a controlling plan (User level
+ // Plans should be controlling plans so that they can be interruptible).
+ // Then resume the process.
+
+ if (new_plan_sp) {
+ new_plan_sp->SetIsControllingPlan(true);
+ new_plan_sp->SetOkayToDiscard(false);
+
+ if (m_options.m_step_count > 1) {
+ if (!new_plan_sp->SetIterationCount(m_options.m_step_count)) {
+ result.AppendWarning(
+ "step operation does not support iteration count.");
+ }
+ }
+
+ process->GetThreadList().SetSelectedThreadByID(thread->GetID());
+
+ const uint32_t iohandler_id = process->GetIOHandlerID();
+
+ StreamString stream;
+ Status error;
+ if (synchronous_execution)
+ error = process->ResumeSynchronous(&stream);
+ else
+ error = process->Resume();
+
+ if (!error.Success()) {
+ result.AppendMessage(error.AsCString());
+ return false;
+ }
+
+ // There is a race condition where this thread will return up the call
+ // stack to the main command handler and show an (lldb) prompt before
+ // HandlePrivateEvent (from PrivateStateThread) has a chance to call
+ // PushProcessIOHandler().
+ process->SyncIOHandler(iohandler_id, std::chrono::seconds(2));
+
+ if (synchronous_execution) {
+ // If any state changed events had anything to say, add that to the
+ // result
+ if (stream.GetSize() > 0)
+ result.AppendMessage(stream.GetString());
+
+ process->GetThreadList().SetSelectedThreadByID(thread->GetID());
+ result.SetDidChangeProcessState(true);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.SetStatus(eReturnStatusSuccessContinuingNoResult);
+ }
+ } else {
+ result.SetError(new_plan_status);
+ }
+ return result.Succeeded();
+ }
+
+ StepType m_step_type;
+ StepScope m_step_scope;
+ ThreadStepScopeOptionGroup m_options;
+ OptionGroupPythonClassWithDict m_class_options;
+ OptionGroupOptions m_all_options;
+};
+
+// CommandObjectThreadContinue
+
+class CommandObjectThreadContinue : public CommandObjectParsed {
+public:
+ CommandObjectThreadContinue(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "thread continue",
+ "Continue execution of the current target process. One "
+ "or more threads may be specified, by default all "
+ "threads continue.",
+ nullptr,
+ eCommandRequiresThread | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {
+ CommandArgumentEntry arg;
+ CommandArgumentData thread_idx_arg;
+
+ // Define the first (and only) variant of this arg.
+ thread_idx_arg.arg_type = eArgTypeThreadIndex;
+ thread_idx_arg.arg_repetition = eArgRepeatPlus;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(thread_idx_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectThreadContinue() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eThreadIndexCompletion,
+ request, nullptr);
+ }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ bool synchronous_execution = m_interpreter.GetSynchronous();
+
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (process == nullptr) {
+ result.AppendError("no process exists. Cannot continue");
+ return false;
+ }
+
+ StateType state = process->GetState();
+ if ((state == eStateCrashed) || (state == eStateStopped) ||
+ (state == eStateSuspended)) {
+ const size_t argc = command.GetArgumentCount();
+ if (argc > 0) {
+ // These two lines appear at the beginning of both blocks in this
+ // if..else, but that is because we need to release the lock before
+ // calling process->Resume below.
+ std::lock_guard<std::recursive_mutex> guard(
+ process->GetThreadList().GetMutex());
+ const uint32_t num_threads = process->GetThreadList().GetSize();
+ std::vector<Thread *> resume_threads;
+ for (auto &entry : command.entries()) {
+ uint32_t thread_idx;
+ if (entry.ref().getAsInteger(0, thread_idx)) {
+ result.AppendErrorWithFormat(
+ "invalid thread index argument: \"%s\".\n", entry.c_str());
+ return false;
+ }
+ Thread *thread =
+ process->GetThreadList().FindThreadByIndexID(thread_idx).get();
+
+ if (thread) {
+ resume_threads.push_back(thread);
+ } else {
+ result.AppendErrorWithFormat("invalid thread index %u.\n",
+ thread_idx);
+ return false;
+ }
+ }
+
+ if (resume_threads.empty()) {
+ result.AppendError("no valid thread indexes were specified");
+ return false;
+ } else {
+ if (resume_threads.size() == 1)
+ result.AppendMessageWithFormat("Resuming thread: ");
+ else
+ result.AppendMessageWithFormat("Resuming threads: ");
+
+ for (uint32_t idx = 0; idx < num_threads; ++idx) {
+ Thread *thread =
+ process->GetThreadList().GetThreadAtIndex(idx).get();
+ std::vector<Thread *>::iterator this_thread_pos =
+ find(resume_threads.begin(), resume_threads.end(), thread);
+
+ if (this_thread_pos != resume_threads.end()) {
+ resume_threads.erase(this_thread_pos);
+ if (!resume_threads.empty())
+ result.AppendMessageWithFormat("%u, ", thread->GetIndexID());
+ else
+ result.AppendMessageWithFormat("%u ", thread->GetIndexID());
+
+ const bool override_suspend = true;
+ thread->SetResumeState(eStateRunning, override_suspend);
+ } else {
+ thread->SetResumeState(eStateSuspended);
+ }
+ }
+ result.AppendMessageWithFormat("in process %" PRIu64 "\n",
+ process->GetID());
+ }
+ } else {
+ // These two lines appear at the beginning of both blocks in this
+ // if..else, but that is because we need to release the lock before
+ // calling process->Resume below.
+ std::lock_guard<std::recursive_mutex> guard(
+ process->GetThreadList().GetMutex());
+ const uint32_t num_threads = process->GetThreadList().GetSize();
+ Thread *current_thread = GetDefaultThread();
+ if (current_thread == nullptr) {
+ result.AppendError("the process doesn't have a current thread");
+ return false;
+ }
+ // Set the actions that the threads should each take when resuming
+ for (uint32_t idx = 0; idx < num_threads; ++idx) {
+ Thread *thread = process->GetThreadList().GetThreadAtIndex(idx).get();
+ if (thread == current_thread) {
+ result.AppendMessageWithFormat("Resuming thread 0x%4.4" PRIx64
+ " in process %" PRIu64 "\n",
+ thread->GetID(), process->GetID());
+ const bool override_suspend = true;
+ thread->SetResumeState(eStateRunning, override_suspend);
+ } else {
+ thread->SetResumeState(eStateSuspended);
+ }
+ }
+ }
+
+ StreamString stream;
+ Status error;
+ if (synchronous_execution)
+ error = process->ResumeSynchronous(&stream);
+ else
+ error = process->Resume();
+
+ // We should not be holding the thread list lock when we do this.
+ if (error.Success()) {
+ result.AppendMessageWithFormat("Process %" PRIu64 " resuming\n",
+ process->GetID());
+ if (synchronous_execution) {
+ // If any state changed events had anything to say, add that to the
+ // result
+ if (stream.GetSize() > 0)
+ result.AppendMessage(stream.GetString());
+
+ result.SetDidChangeProcessState(true);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.SetStatus(eReturnStatusSuccessContinuingNoResult);
+ }
+ } else {
+ result.AppendErrorWithFormat("Failed to resume process: %s\n",
+ error.AsCString());
+ }
+ } else {
+ result.AppendErrorWithFormat(
+ "Process cannot be continued from its current state (%s).\n",
+ StateAsCString(state));
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectThreadUntil
+
+static constexpr OptionEnumValueElement g_duo_running_mode[] = {
+ {eOnlyThisThread, "this-thread", "Run only this thread"},
+ {eAllThreads, "all-threads", "Run all threads"}};
+
+static constexpr OptionEnumValues DuoRunningModes() {
+ return OptionEnumValues(g_duo_running_mode);
+}
+
+#define LLDB_OPTIONS_thread_until
+#include "CommandOptions.inc"
+
+class CommandObjectThreadUntil : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ uint32_t m_thread_idx = LLDB_INVALID_THREAD_ID;
+ uint32_t m_frame_idx = LLDB_INVALID_FRAME_ID;
+
+ CommandOptions() {
+ // Keep default values of all options in one place: OptionParsingStarting
+ // ()
+ OptionParsingStarting(nullptr);
+ }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'a': {
+ lldb::addr_t tmp_addr = OptionArgParser::ToAddress(
+ execution_context, option_arg, LLDB_INVALID_ADDRESS, &error);
+ if (error.Success())
+ m_until_addrs.push_back(tmp_addr);
+ } break;
+ case 't':
+ if (option_arg.getAsInteger(0, m_thread_idx)) {
+ m_thread_idx = LLDB_INVALID_INDEX32;
+ error.SetErrorStringWithFormat("invalid thread index '%s'",
+ option_arg.str().c_str());
+ }
+ break;
+ case 'f':
+ if (option_arg.getAsInteger(0, m_frame_idx)) {
+ m_frame_idx = LLDB_INVALID_FRAME_ID;
+ error.SetErrorStringWithFormat("invalid frame index '%s'",
+ option_arg.str().c_str());
+ }
+ break;
+ case 'm': {
+ auto enum_values = GetDefinitions()[option_idx].enum_values;
+ lldb::RunMode run_mode = (lldb::RunMode)OptionArgParser::ToOptionEnum(
+ option_arg, enum_values, eOnlyDuringStepping, error);
+
+ if (error.Success()) {
+ if (run_mode == eAllThreads)
+ m_stop_others = false;
+ else
+ m_stop_others = true;
+ }
+ } break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_thread_idx = LLDB_INVALID_THREAD_ID;
+ m_frame_idx = 0;
+ m_stop_others = false;
+ m_until_addrs.clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_thread_until_options);
+ }
+
+ uint32_t m_step_thread_idx;
+ bool m_stop_others;
+ std::vector<lldb::addr_t> m_until_addrs;
+
+ // Instance variables to hold the values for command options.
+ };
+
+ CommandObjectThreadUntil(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "thread until",
+ "Continue until a line number or address is reached by the "
+ "current or specified thread. Stops when returning from "
+ "the current function as a safety measure. "
+ "The target line number(s) are given as arguments, and if more "
+ "than one"
+ " is provided, stepping will stop when the first one is hit.",
+ nullptr,
+ eCommandRequiresThread | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {
+ CommandArgumentEntry arg;
+ CommandArgumentData line_num_arg;
+
+ // Define the first (and only) variant of this arg.
+ line_num_arg.arg_type = eArgTypeLineNum;
+ line_num_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(line_num_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectThreadUntil() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ bool synchronous_execution = m_interpreter.GetSynchronous();
+
+ Target *target = &GetSelectedTarget();
+
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (process == nullptr) {
+ result.AppendError("need a valid process to step");
+ } else {
+ Thread *thread = nullptr;
+ std::vector<uint32_t> line_numbers;
+
+ if (command.GetArgumentCount() >= 1) {
+ size_t num_args = command.GetArgumentCount();
+ for (size_t i = 0; i < num_args; i++) {
+ uint32_t line_number;
+ if (!llvm::to_integer(command.GetArgumentAtIndex(i), line_number)) {
+ result.AppendErrorWithFormat("invalid line number: '%s'.\n",
+ command.GetArgumentAtIndex(i));
+ return false;
+ } else
+ line_numbers.push_back(line_number);
+ }
+ } else if (m_options.m_until_addrs.empty()) {
+ result.AppendErrorWithFormat("No line number or address provided:\n%s",
+ GetSyntax().str().c_str());
+ return false;
+ }
+
+ if (m_options.m_thread_idx == LLDB_INVALID_THREAD_ID) {
+ thread = GetDefaultThread();
+ } else {
+ thread = process->GetThreadList()
+ .FindThreadByIndexID(m_options.m_thread_idx)
+ .get();
+ }
+
+ if (thread == nullptr) {
+ const uint32_t num_threads = process->GetThreadList().GetSize();
+ result.AppendErrorWithFormat(
+ "Thread index %u is out of range (valid values are 0 - %u).\n",
+ m_options.m_thread_idx, num_threads);
+ return false;
+ }
+
+ const bool abort_other_plans = false;
+
+ StackFrame *frame =
+ thread->GetStackFrameAtIndex(m_options.m_frame_idx).get();
+ if (frame == nullptr) {
+ result.AppendErrorWithFormat(
+ "Frame index %u is out of range for thread id %" PRIu64 ".\n",
+ m_options.m_frame_idx, thread->GetID());
+ return false;
+ }
+
+ ThreadPlanSP new_plan_sp;
+ Status new_plan_status;
+
+ if (frame->HasDebugInformation()) {
+ // Finally we got here... Translate the given line number to a bunch
+ // of addresses:
+ SymbolContext sc(frame->GetSymbolContext(eSymbolContextCompUnit));
+ LineTable *line_table = nullptr;
+ if (sc.comp_unit)
+ line_table = sc.comp_unit->GetLineTable();
+
+ if (line_table == nullptr) {
+ result.AppendErrorWithFormat("Failed to resolve the line table for "
+ "frame %u of thread id %" PRIu64 ".\n",
+ m_options.m_frame_idx, thread->GetID());
+ return false;
+ }
+
+ LineEntry function_start;
+ uint32_t index_ptr = 0, end_ptr;
+ std::vector<addr_t> address_list;
+
+ // Find the beginning & end index of the function, but first make
+ // sure it is valid:
+ if (!sc.function) {
+ result.AppendErrorWithFormat("Have debug information but no "
+ "function info - can't get until range.");
+ return false;
+ }
+
+ AddressRange fun_addr_range = sc.function->GetAddressRange();
+ Address fun_start_addr = fun_addr_range.GetBaseAddress();
+ line_table->FindLineEntryByAddress(fun_start_addr, function_start,
+ &index_ptr);
+
+ Address fun_end_addr(fun_start_addr.GetSection(),
+ fun_start_addr.GetOffset() +
+ fun_addr_range.GetByteSize());
+
+ bool all_in_function = true;
+
+ line_table->FindLineEntryByAddress(fun_end_addr, function_start,
+ &end_ptr);
+
+ // Since not all source lines will contribute code, check if we are
+ // setting the breakpoint on the exact line number or the nearest
+ // subsequent line number and set breakpoints at all the line table
+ // entries of the chosen line number (exact or nearest subsequent).
+ for (uint32_t line_number : line_numbers) {
+ LineEntry line_entry;
+ bool exact = false;
+ uint32_t start_idx_ptr = index_ptr;
+ start_idx_ptr = sc.comp_unit->FindLineEntry(
+ index_ptr, line_number, nullptr, exact, &line_entry);
+ if (start_idx_ptr != UINT32_MAX)
+ line_number = line_entry.line;
+ exact = true;
+ start_idx_ptr = index_ptr;
+ while (start_idx_ptr <= end_ptr) {
+ start_idx_ptr = sc.comp_unit->FindLineEntry(
+ start_idx_ptr, line_number, nullptr, exact, &line_entry);
+ if (start_idx_ptr == UINT32_MAX)
+ break;
+
+ addr_t address =
+ line_entry.range.GetBaseAddress().GetLoadAddress(target);
+ if (address != LLDB_INVALID_ADDRESS) {
+ if (fun_addr_range.ContainsLoadAddress(address, target))
+ address_list.push_back(address);
+ else
+ all_in_function = false;
+ }
+ start_idx_ptr++;
+ }
+ }
+
+ for (lldb::addr_t address : m_options.m_until_addrs) {
+ if (fun_addr_range.ContainsLoadAddress(address, target))
+ address_list.push_back(address);
+ else
+ all_in_function = false;
+ }
+
+ if (address_list.empty()) {
+ if (all_in_function)
+ result.AppendErrorWithFormat(
+ "No line entries matching until target.\n");
+ else
+ result.AppendErrorWithFormat(
+ "Until target outside of the current function.\n");
+
+ return false;
+ }
+
+ new_plan_sp = thread->QueueThreadPlanForStepUntil(
+ abort_other_plans, &address_list.front(), address_list.size(),
+ m_options.m_stop_others, m_options.m_frame_idx, new_plan_status);
+ if (new_plan_sp) {
+ // User level plans should be controlling plans so they can be
+ // interrupted
+ // (e.g. by hitting a breakpoint) and other plans executed by the
+ // user (stepping around the breakpoint) and then a "continue" will
+ // resume the original plan.
+ new_plan_sp->SetIsControllingPlan(true);
+ new_plan_sp->SetOkayToDiscard(false);
+ } else {
+ result.SetError(new_plan_status);
+ return false;
+ }
+ } else {
+ result.AppendErrorWithFormat("Frame index %u of thread id %" PRIu64
+ " has no debug information.\n",
+ m_options.m_frame_idx, thread->GetID());
+ return false;
+ }
+
+ if (!process->GetThreadList().SetSelectedThreadByID(thread->GetID())) {
+ result.AppendErrorWithFormat(
+ "Failed to set the selected thread to thread id %" PRIu64 ".\n",
+ thread->GetID());
+ return false;
+ }
+
+ StreamString stream;
+ Status error;
+ if (synchronous_execution)
+ error = process->ResumeSynchronous(&stream);
+ else
+ error = process->Resume();
+
+ if (error.Success()) {
+ result.AppendMessageWithFormat("Process %" PRIu64 " resuming\n",
+ process->GetID());
+ if (synchronous_execution) {
+ // If any state changed events had anything to say, add that to the
+ // result
+ if (stream.GetSize() > 0)
+ result.AppendMessage(stream.GetString());
+
+ result.SetDidChangeProcessState(true);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.SetStatus(eReturnStatusSuccessContinuingNoResult);
+ }
+ } else {
+ result.AppendErrorWithFormat("Failed to resume process: %s.\n",
+ error.AsCString());
+ }
+ }
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectThreadSelect
+
+class CommandObjectThreadSelect : public CommandObjectParsed {
+public:
+ CommandObjectThreadSelect(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "thread select",
+ "Change the currently selected thread.", nullptr,
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused) {
+ CommandArgumentEntry arg;
+ CommandArgumentData thread_idx_arg;
+
+ // Define the first (and only) variant of this arg.
+ thread_idx_arg.arg_type = eArgTypeThreadIndex;
+ thread_idx_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(thread_idx_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectThreadSelect() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (request.GetCursorIndex())
+ return;
+
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eThreadIndexCompletion,
+ request, nullptr);
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (process == nullptr) {
+ result.AppendError("no process");
+ return false;
+ } else if (command.GetArgumentCount() != 1) {
+ result.AppendErrorWithFormat(
+ "'%s' takes exactly one thread index argument:\nUsage: %s\n",
+ m_cmd_name.c_str(), m_cmd_syntax.c_str());
+ return false;
+ }
+
+ uint32_t index_id;
+ if (!llvm::to_integer(command.GetArgumentAtIndex(0), index_id)) {
+ result.AppendErrorWithFormat("Invalid thread index '%s'",
+ command.GetArgumentAtIndex(0));
+ return false;
+ }
+
+ Thread *new_thread =
+ process->GetThreadList().FindThreadByIndexID(index_id).get();
+ if (new_thread == nullptr) {
+ result.AppendErrorWithFormat("invalid thread #%s.\n",
+ command.GetArgumentAtIndex(0));
+ return false;
+ }
+
+ process->GetThreadList().SetSelectedThreadByID(new_thread->GetID(), true);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectThreadList
+
+class CommandObjectThreadList : public CommandObjectParsed {
+public:
+ CommandObjectThreadList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "thread list",
+ "Show a summary of each thread in the current target process. "
+ "Use 'settings set thread-format' to customize the individual "
+ "thread listings.",
+ "thread list",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {}
+
+ ~CommandObjectThreadList() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Stream &strm = result.GetOutputStream();
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ Process *process = m_exe_ctx.GetProcessPtr();
+ const bool only_threads_with_stop_reason = false;
+ const uint32_t start_frame = 0;
+ const uint32_t num_frames = 0;
+ const uint32_t num_frames_with_source = 0;
+ process->GetStatus(strm);
+ process->GetThreadStatus(strm, only_threads_with_stop_reason, start_frame,
+ num_frames, num_frames_with_source, false);
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectThreadInfo
+#define LLDB_OPTIONS_thread_info
+#include "CommandOptions.inc"
+
+class CommandObjectThreadInfo : public CommandObjectIterateOverThreads {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() { OptionParsingStarting(nullptr); }
+
+ ~CommandOptions() override = default;
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_json_thread = false;
+ m_json_stopinfo = false;
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ const int short_option = m_getopt_table[option_idx].val;
+ Status error;
+
+ switch (short_option) {
+ case 'j':
+ m_json_thread = true;
+ break;
+
+ case 's':
+ m_json_stopinfo = true;
+ break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_thread_info_options);
+ }
+
+ bool m_json_thread;
+ bool m_json_stopinfo;
+ };
+
+ CommandObjectThreadInfo(CommandInterpreter &interpreter)
+ : CommandObjectIterateOverThreads(
+ interpreter, "thread info",
+ "Show an extended summary of one or "
+ "more threads. Defaults to the "
+ "current thread.",
+ "thread info",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {
+ m_add_return = false;
+ }
+
+ ~CommandObjectThreadInfo() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eThreadIndexCompletion,
+ request, nullptr);
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+ bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override {
+ ThreadSP thread_sp =
+ m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid);
+ if (!thread_sp) {
+ result.AppendErrorWithFormat("thread no longer exists: 0x%" PRIx64 "\n",
+ tid);
+ return false;
+ }
+
+ Thread *thread = thread_sp.get();
+
+ Stream &strm = result.GetOutputStream();
+ if (!thread->GetDescription(strm, eDescriptionLevelFull,
+ m_options.m_json_thread,
+ m_options.m_json_stopinfo)) {
+ result.AppendErrorWithFormat("error displaying info for thread: \"%d\"\n",
+ thread->GetIndexID());
+ return false;
+ }
+ return true;
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectThreadException
+
+class CommandObjectThreadException : public CommandObjectIterateOverThreads {
+public:
+ CommandObjectThreadException(CommandInterpreter &interpreter)
+ : CommandObjectIterateOverThreads(
+ interpreter, "thread exception",
+ "Display the current exception object for a thread. Defaults to "
+ "the current thread.",
+ "thread exception",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {}
+
+ ~CommandObjectThreadException() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eThreadIndexCompletion,
+ request, nullptr);
+ }
+
+ bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override {
+ ThreadSP thread_sp =
+ m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid);
+ if (!thread_sp) {
+ result.AppendErrorWithFormat("thread no longer exists: 0x%" PRIx64 "\n",
+ tid);
+ return false;
+ }
+
+ Stream &strm = result.GetOutputStream();
+ ValueObjectSP exception_object_sp = thread_sp->GetCurrentException();
+ if (exception_object_sp) {
+ exception_object_sp->Dump(strm);
+ }
+
+ ThreadSP exception_thread_sp = thread_sp->GetCurrentExceptionBacktrace();
+ if (exception_thread_sp && exception_thread_sp->IsValid()) {
+ const uint32_t num_frames_with_source = 0;
+ const bool stop_format = false;
+ exception_thread_sp->GetStatus(strm, 0, UINT32_MAX,
+ num_frames_with_source, stop_format);
+ }
+
+ return true;
+ }
+};
+
+class CommandObjectThreadSiginfo : public CommandObjectIterateOverThreads {
+public:
+ CommandObjectThreadSiginfo(CommandInterpreter &interpreter)
+ : CommandObjectIterateOverThreads(
+ interpreter, "thread siginfo",
+ "Display the current siginfo object for a thread. Defaults to "
+ "the current thread.",
+ "thread siginfo",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {}
+
+ ~CommandObjectThreadSiginfo() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eThreadIndexCompletion,
+ request, nullptr);
+ }
+
+ bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override {
+ ThreadSP thread_sp =
+ m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid);
+ if (!thread_sp) {
+ result.AppendErrorWithFormat("thread no longer exists: 0x%" PRIx64 "\n",
+ tid);
+ return false;
+ }
+
+ Stream &strm = result.GetOutputStream();
+ if (!thread_sp->GetDescription(strm, eDescriptionLevelFull, false, false)) {
+ result.AppendErrorWithFormat("error displaying info for thread: \"%d\"\n",
+ thread_sp->GetIndexID());
+ return false;
+ }
+ ValueObjectSP exception_object_sp = thread_sp->GetSiginfoValue();
+ if (exception_object_sp)
+ exception_object_sp->Dump(strm);
+ else
+ strm.Printf("(no siginfo)\n");
+ strm.PutChar('\n');
+
+ return true;
+ }
+};
+
+// CommandObjectThreadReturn
+#define LLDB_OPTIONS_thread_return
+#include "CommandOptions.inc"
+
+class CommandObjectThreadReturn : public CommandObjectRaw {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() {
+ // Keep default values of all options in one place: OptionParsingStarting
+ // ()
+ OptionParsingStarting(nullptr);
+ }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'x': {
+ bool success;
+ bool tmp_value =
+ OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (success)
+ m_from_expression = tmp_value;
+ else {
+ error.SetErrorStringWithFormat(
+ "invalid boolean value '%s' for 'x' option",
+ option_arg.str().c_str());
+ }
+ } break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_from_expression = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_thread_return_options);
+ }
+
+ bool m_from_expression = false;
+
+ // Instance variables to hold the values for command options.
+ };
+
+ CommandObjectThreadReturn(CommandInterpreter &interpreter)
+ : CommandObjectRaw(interpreter, "thread return",
+ "Prematurely return from a stack frame, "
+ "short-circuiting execution of newer frames "
+ "and optionally yielding a specified value. Defaults "
+ "to the exiting the current stack "
+ "frame.",
+ "thread return",
+ eCommandRequiresFrame | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused) {
+ CommandArgumentEntry arg;
+ CommandArgumentData expression_arg;
+
+ // Define the first (and only) variant of this arg.
+ expression_arg.arg_type = eArgTypeExpression;
+ expression_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(expression_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectThreadReturn() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) override {
+ // I am going to handle this by hand, because I don't want you to have to
+ // say:
+ // "thread return -- -5".
+ if (command.startswith("-x")) {
+ if (command.size() != 2U)
+ result.AppendWarning("Return values ignored when returning from user "
+ "called expressions");
+
+ Thread *thread = m_exe_ctx.GetThreadPtr();
+ Status error;
+ error = thread->UnwindInnermostExpression();
+ if (!error.Success()) {
+ result.AppendErrorWithFormat("Unwinding expression failed - %s.",
+ error.AsCString());
+ } else {
+ bool success =
+ thread->SetSelectedFrameByIndexNoisily(0, result.GetOutputStream());
+ if (success) {
+ m_exe_ctx.SetFrameSP(thread->GetSelectedFrame());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat(
+ "Could not select 0th frame after unwinding expression.");
+ }
+ }
+ return result.Succeeded();
+ }
+
+ ValueObjectSP return_valobj_sp;
+
+ StackFrameSP frame_sp = m_exe_ctx.GetFrameSP();
+ uint32_t frame_idx = frame_sp->GetFrameIndex();
+
+ if (frame_sp->IsInlined()) {
+ result.AppendError("Don't know how to return from inlined frames.");
+ return false;
+ }
+
+ if (!command.empty()) {
+ Target *target = m_exe_ctx.GetTargetPtr();
+ EvaluateExpressionOptions options;
+
+ options.SetUnwindOnError(true);
+ options.SetUseDynamic(eNoDynamicValues);
+
+ ExpressionResults exe_results = eExpressionSetupError;
+ exe_results = target->EvaluateExpression(command, frame_sp.get(),
+ return_valobj_sp, options);
+ if (exe_results != eExpressionCompleted) {
+ if (return_valobj_sp)
+ result.AppendErrorWithFormat(
+ "Error evaluating result expression: %s",
+ return_valobj_sp->GetError().AsCString());
+ else
+ result.AppendErrorWithFormat(
+ "Unknown error evaluating result expression.");
+ return false;
+ }
+ }
+
+ Status error;
+ ThreadSP thread_sp = m_exe_ctx.GetThreadSP();
+ const bool broadcast = true;
+ error = thread_sp->ReturnFromFrame(frame_sp, return_valobj_sp, broadcast);
+ if (!error.Success()) {
+ result.AppendErrorWithFormat(
+ "Error returning from frame %d of thread %d: %s.", frame_idx,
+ thread_sp->GetIndexID(), error.AsCString());
+ return false;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectThreadJump
+#define LLDB_OPTIONS_thread_jump
+#include "CommandOptions.inc"
+
+class CommandObjectThreadJump : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() { OptionParsingStarting(nullptr); }
+
+ ~CommandOptions() override = default;
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_filenames.Clear();
+ m_line_num = 0;
+ m_line_offset = 0;
+ m_load_addr = LLDB_INVALID_ADDRESS;
+ m_force = false;
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ const int short_option = m_getopt_table[option_idx].val;
+ Status error;
+
+ switch (short_option) {
+ case 'f':
+ m_filenames.AppendIfUnique(FileSpec(option_arg));
+ if (m_filenames.GetSize() > 1)
+ return Status("only one source file expected.");
+ break;
+ case 'l':
+ if (option_arg.getAsInteger(0, m_line_num))
+ return Status("invalid line number: '%s'.", option_arg.str().c_str());
+ break;
+ case 'b':
+ if (option_arg.getAsInteger(0, m_line_offset))
+ return Status("invalid line offset: '%s'.", option_arg.str().c_str());
+ break;
+ case 'a':
+ m_load_addr = OptionArgParser::ToAddress(execution_context, option_arg,
+ LLDB_INVALID_ADDRESS, &error);
+ break;
+ case 'r':
+ m_force = true;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_thread_jump_options);
+ }
+
+ FileSpecList m_filenames;
+ uint32_t m_line_num;
+ int32_t m_line_offset;
+ lldb::addr_t m_load_addr;
+ bool m_force;
+ };
+
+ CommandObjectThreadJump(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "thread jump",
+ "Sets the program counter to a new address.", "thread jump",
+ eCommandRequiresFrame | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {}
+
+ ~CommandObjectThreadJump() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ RegisterContext *reg_ctx = m_exe_ctx.GetRegisterContext();
+ StackFrame *frame = m_exe_ctx.GetFramePtr();
+ Thread *thread = m_exe_ctx.GetThreadPtr();
+ Target *target = m_exe_ctx.GetTargetPtr();
+ const SymbolContext &sym_ctx =
+ frame->GetSymbolContext(eSymbolContextLineEntry);
+
+ if (m_options.m_load_addr != LLDB_INVALID_ADDRESS) {
+ // Use this address directly.
+ Address dest = Address(m_options.m_load_addr);
+
+ lldb::addr_t callAddr = dest.GetCallableLoadAddress(target);
+ if (callAddr == LLDB_INVALID_ADDRESS) {
+ result.AppendErrorWithFormat("Invalid destination address.");
+ return false;
+ }
+
+ if (!reg_ctx->SetPC(callAddr)) {
+ result.AppendErrorWithFormat("Error changing PC value for thread %d.",
+ thread->GetIndexID());
+ return false;
+ }
+ } else {
+ // Pick either the absolute line, or work out a relative one.
+ int32_t line = (int32_t)m_options.m_line_num;
+ if (line == 0)
+ line = sym_ctx.line_entry.line + m_options.m_line_offset;
+
+ // Try the current file, but override if asked.
+ FileSpec file = sym_ctx.line_entry.file;
+ if (m_options.m_filenames.GetSize() == 1)
+ file = m_options.m_filenames.GetFileSpecAtIndex(0);
+
+ if (!file) {
+ result.AppendErrorWithFormat(
+ "No source file available for the current location.");
+ return false;
+ }
+
+ std::string warnings;
+ Status err = thread->JumpToLine(file, line, m_options.m_force, &warnings);
+
+ if (err.Fail()) {
+ result.SetError(err);
+ return false;
+ }
+
+ if (!warnings.empty())
+ result.AppendWarning(warnings.c_str());
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+
+ CommandOptions m_options;
+};
+
+// Next are the subcommands of CommandObjectMultiwordThreadPlan
+
+// CommandObjectThreadPlanList
+#define LLDB_OPTIONS_thread_plan_list
+#include "CommandOptions.inc"
+
+class CommandObjectThreadPlanList : public CommandObjectIterateOverThreads {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() {
+ // Keep default values of all options in one place: OptionParsingStarting
+ // ()
+ OptionParsingStarting(nullptr);
+ }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'i':
+ m_internal = true;
+ break;
+ case 't':
+ lldb::tid_t tid;
+ if (option_arg.getAsInteger(0, tid))
+ return Status("invalid tid: '%s'.", option_arg.str().c_str());
+ m_tids.push_back(tid);
+ break;
+ case 'u':
+ m_unreported = false;
+ break;
+ case 'v':
+ m_verbose = true;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return {};
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_verbose = false;
+ m_internal = false;
+ m_unreported = true; // The variable is "skip unreported" and we want to
+ // skip unreported by default.
+ m_tids.clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_thread_plan_list_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ bool m_verbose;
+ bool m_internal;
+ bool m_unreported;
+ std::vector<lldb::tid_t> m_tids;
+ };
+
+ CommandObjectThreadPlanList(CommandInterpreter &interpreter)
+ : CommandObjectIterateOverThreads(
+ interpreter, "thread plan list",
+ "Show thread plans for one or more threads. If no threads are "
+ "specified, show the "
+ "current thread. Use the thread-index \"all\" to see all threads.",
+ nullptr,
+ eCommandRequiresProcess | eCommandRequiresThread |
+ eCommandTryTargetAPILock | eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused) {}
+
+ ~CommandObjectThreadPlanList() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ // If we are reporting all threads, dispatch to the Process to do that:
+ if (command.GetArgumentCount() == 0 && m_options.m_tids.empty()) {
+ Stream &strm = result.GetOutputStream();
+ DescriptionLevel desc_level = m_options.m_verbose
+ ? eDescriptionLevelVerbose
+ : eDescriptionLevelFull;
+ m_exe_ctx.GetProcessPtr()->DumpThreadPlans(
+ strm, desc_level, m_options.m_internal, true, m_options.m_unreported);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ } else {
+ // Do any TID's that the user may have specified as TID, then do any
+ // Thread Indexes...
+ if (!m_options.m_tids.empty()) {
+ Process *process = m_exe_ctx.GetProcessPtr();
+ StreamString tmp_strm;
+ for (lldb::tid_t tid : m_options.m_tids) {
+ bool success = process->DumpThreadPlansForTID(
+ tmp_strm, tid, eDescriptionLevelFull, m_options.m_internal,
+ true /* condense_trivial */, m_options.m_unreported);
+ // If we didn't find a TID, stop here and return an error.
+ if (!success) {
+ result.AppendError("Error dumping plans:");
+ result.AppendError(tmp_strm.GetString());
+ return false;
+ }
+ // Otherwise, add our data to the output:
+ result.GetOutputStream() << tmp_strm.GetString();
+ }
+ }
+ return CommandObjectIterateOverThreads::DoExecute(command, result);
+ }
+ }
+
+protected:
+ bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override {
+ // If we have already handled this from a -t option, skip it here.
+ if (llvm::is_contained(m_options.m_tids, tid))
+ return true;
+
+ Process *process = m_exe_ctx.GetProcessPtr();
+
+ Stream &strm = result.GetOutputStream();
+ DescriptionLevel desc_level = eDescriptionLevelFull;
+ if (m_options.m_verbose)
+ desc_level = eDescriptionLevelVerbose;
+
+ process->DumpThreadPlansForTID(strm, tid, desc_level, m_options.m_internal,
+ true /* condense_trivial */,
+ m_options.m_unreported);
+ return true;
+ }
+
+ CommandOptions m_options;
+};
+
+class CommandObjectThreadPlanDiscard : public CommandObjectParsed {
+public:
+ CommandObjectThreadPlanDiscard(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "thread plan discard",
+ "Discards thread plans up to and including the "
+ "specified index (see 'thread plan list'.) "
+ "Only user visible plans can be discarded.",
+ nullptr,
+ eCommandRequiresProcess | eCommandRequiresThread |
+ eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused) {
+ CommandArgumentEntry arg;
+ CommandArgumentData plan_index_arg;
+
+ // Define the first (and only) variant of this arg.
+ plan_index_arg.arg_type = eArgTypeUnsignedInteger;
+ plan_index_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(plan_index_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectThreadPlanDiscard() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (!m_exe_ctx.HasThreadScope() || request.GetCursorIndex())
+ return;
+
+ m_exe_ctx.GetThreadPtr()->AutoCompleteThreadPlans(request);
+ }
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Thread *thread = m_exe_ctx.GetThreadPtr();
+ if (args.GetArgumentCount() != 1) {
+ result.AppendErrorWithFormat("Too many arguments, expected one - the "
+ "thread plan index - but got %zu.",
+ args.GetArgumentCount());
+ return false;
+ }
+
+ uint32_t thread_plan_idx;
+ if (!llvm::to_integer(args.GetArgumentAtIndex(0), thread_plan_idx)) {
+ result.AppendErrorWithFormat(
+ "Invalid thread index: \"%s\" - should be unsigned int.",
+ args.GetArgumentAtIndex(0));
+ return false;
+ }
+
+ if (thread_plan_idx == 0) {
+ result.AppendErrorWithFormat(
+ "You wouldn't really want me to discard the base thread plan.");
+ return false;
+ }
+
+ if (thread->DiscardUserThreadPlansUpToIndex(thread_plan_idx)) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ } else {
+ result.AppendErrorWithFormat(
+ "Could not find User thread plan with index %s.",
+ args.GetArgumentAtIndex(0));
+ return false;
+ }
+ }
+};
+
+class CommandObjectThreadPlanPrune : public CommandObjectParsed {
+public:
+ CommandObjectThreadPlanPrune(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "thread plan prune",
+ "Removes any thread plans associated with "
+ "currently unreported threads. "
+ "Specify one or more TID's to remove, or if no "
+ "TID's are provides, remove threads for all "
+ "unreported threads",
+ nullptr,
+ eCommandRequiresProcess |
+ eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused) {
+ CommandArgumentEntry arg;
+ CommandArgumentData tid_arg;
+
+ // Define the first (and only) variant of this arg.
+ tid_arg.arg_type = eArgTypeThreadID;
+ tid_arg.arg_repetition = eArgRepeatStar;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(tid_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectThreadPlanPrune() override = default;
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+
+ if (args.GetArgumentCount() == 0) {
+ process->PruneThreadPlans();
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+
+ const size_t num_args = args.GetArgumentCount();
+
+ std::lock_guard<std::recursive_mutex> guard(
+ process->GetThreadList().GetMutex());
+
+ for (size_t i = 0; i < num_args; i++) {
+ lldb::tid_t tid;
+ if (!llvm::to_integer(args.GetArgumentAtIndex(i), tid)) {
+ result.AppendErrorWithFormat("invalid thread specification: \"%s\"\n",
+ args.GetArgumentAtIndex(i));
+ return false;
+ }
+ if (!process->PruneThreadPlansForTID(tid)) {
+ result.AppendErrorWithFormat("Could not find unreported tid: \"%s\"\n",
+ args.GetArgumentAtIndex(i));
+ return false;
+ }
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+};
+
+// CommandObjectMultiwordThreadPlan
+
+class CommandObjectMultiwordThreadPlan : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordThreadPlan(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "plan",
+ "Commands for managing thread plans that control execution.",
+ "thread plan <subcommand> [<subcommand objects]") {
+ LoadSubCommand(
+ "list", CommandObjectSP(new CommandObjectThreadPlanList(interpreter)));
+ LoadSubCommand(
+ "discard",
+ CommandObjectSP(new CommandObjectThreadPlanDiscard(interpreter)));
+ LoadSubCommand(
+ "prune",
+ CommandObjectSP(new CommandObjectThreadPlanPrune(interpreter)));
+ }
+
+ ~CommandObjectMultiwordThreadPlan() override = default;
+};
+
+// Next are the subcommands of CommandObjectMultiwordTrace
+
+// CommandObjectTraceExport
+
+class CommandObjectTraceExport : public CommandObjectMultiword {
+public:
+ CommandObjectTraceExport(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "trace thread export",
+ "Commands for exporting traces of the threads in the current "
+ "process to different formats.",
+ "thread trace export <export-plugin> [<subcommand objects>]") {
+
+ unsigned i = 0;
+ for (llvm::StringRef plugin_name =
+ PluginManager::GetTraceExporterPluginNameAtIndex(i);
+ !plugin_name.empty();
+ plugin_name = PluginManager::GetTraceExporterPluginNameAtIndex(i++)) {
+ if (ThreadTraceExportCommandCreator command_creator =
+ PluginManager::GetThreadTraceExportCommandCreatorAtIndex(i)) {
+ LoadSubCommand(plugin_name, command_creator(interpreter));
+ }
+ }
+ }
+};
+
+// CommandObjectTraceStart
+
+class CommandObjectTraceStart : public CommandObjectTraceProxy {
+public:
+ CommandObjectTraceStart(CommandInterpreter &interpreter)
+ : CommandObjectTraceProxy(
+ /*live_debug_session_only=*/true, interpreter, "thread trace start",
+ "Start tracing threads with the corresponding trace "
+ "plug-in for the current process.",
+ "thread trace start [<trace-options>]") {}
+
+protected:
+ lldb::CommandObjectSP GetDelegateCommand(Trace &trace) override {
+ return trace.GetThreadTraceStartCommand(m_interpreter);
+ }
+};
+
+// CommandObjectTraceStop
+
+class CommandObjectTraceStop : public CommandObjectMultipleThreads {
+public:
+ CommandObjectTraceStop(CommandInterpreter &interpreter)
+ : CommandObjectMultipleThreads(
+ interpreter, "thread trace stop",
+ "Stop tracing threads, including the ones traced with the "
+ "\"process trace start\" command."
+ "Defaults to the current thread. Thread indices can be "
+ "specified as arguments.\n Use the thread-index \"all\" to stop "
+ "tracing "
+ "for all existing threads.",
+ "thread trace stop [<thread-index> <thread-index> ...]",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused |
+ eCommandProcessMustBeTraced) {}
+
+ ~CommandObjectTraceStop() override = default;
+
+ bool DoExecuteOnThreads(Args &command, CommandReturnObject &result,
+ llvm::ArrayRef<lldb::tid_t> tids) override {
+ ProcessSP process_sp = m_exe_ctx.GetProcessSP();
+
+ TraceSP trace_sp = process_sp->GetTarget().GetTrace();
+
+ if (llvm::Error err = trace_sp->Stop(tids))
+ result.AppendError(toString(std::move(err)));
+ else
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectTraceDumpInstructions
+#define LLDB_OPTIONS_thread_trace_dump_instructions
+#include "CommandOptions.inc"
+
+class CommandObjectTraceDumpInstructions : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() { OptionParsingStarting(nullptr); }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'c': {
+ int32_t count;
+ if (option_arg.empty() || option_arg.getAsInteger(0, count) ||
+ count < 0)
+ error.SetErrorStringWithFormat(
+ "invalid integer value for option '%s'",
+ option_arg.str().c_str());
+ else
+ m_count = count;
+ break;
+ }
+ case 'a': {
+ m_count = std::numeric_limits<decltype(m_count)>::max();
+ break;
+ }
+ case 's': {
+ int32_t skip;
+ if (option_arg.empty() || option_arg.getAsInteger(0, skip) || skip < 0)
+ error.SetErrorStringWithFormat(
+ "invalid integer value for option '%s'",
+ option_arg.str().c_str());
+ else
+ m_dumper_options.skip = skip;
+ break;
+ }
+ case 'i': {
+ uint64_t id;
+ if (option_arg.empty() || option_arg.getAsInteger(0, id))
+ error.SetErrorStringWithFormat(
+ "invalid integer value for option '%s'",
+ option_arg.str().c_str());
+ else
+ m_dumper_options.id = id;
+ break;
+ }
+ case 'F': {
+ m_output_file.emplace(option_arg);
+ break;
+ }
+ case 'r': {
+ m_dumper_options.raw = true;
+ break;
+ }
+ case 'f': {
+ m_dumper_options.forwards = true;
+ break;
+ }
+ case 'k': {
+ m_dumper_options.show_control_flow_kind = true;
+ break;
+ }
+ case 't': {
+ m_dumper_options.show_tsc = true;
+ break;
+ }
+ case 'e': {
+ m_dumper_options.show_events = true;
+ break;
+ }
+ case 'j': {
+ m_dumper_options.json = true;
+ break;
+ }
+ case 'J': {
+ m_dumper_options.pretty_print_json = true;
+ m_dumper_options.json = true;
+ break;
+ }
+ case 'C': {
+ m_continue = true;
+ break;
+ }
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_count = kDefaultCount;
+ m_continue = false;
+ m_output_file = llvm::None;
+ m_dumper_options = {};
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_thread_trace_dump_instructions_options);
+ }
+
+ static const size_t kDefaultCount = 20;
+
+ // Instance variables to hold the values for command options.
+ size_t m_count;
+ size_t m_continue;
+ llvm::Optional<FileSpec> m_output_file;
+ TraceDumperOptions m_dumper_options;
+ };
+
+ CommandObjectTraceDumpInstructions(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "thread trace dump instructions",
+ "Dump the traced instructions for one thread. If no "
+ "thread is specified, show the current thread.",
+ nullptr,
+ eCommandRequiresProcess | eCommandRequiresThread |
+ eCommandTryTargetAPILock | eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused | eCommandProcessMustBeTraced) {
+ CommandArgumentData thread_arg{eArgTypeThreadIndex, eArgRepeatOptional};
+ m_arguments.push_back({thread_arg});
+ }
+
+ ~CommandObjectTraceDumpInstructions() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ llvm::Optional<std::string> GetRepeatCommand(Args &current_command_args,
+ uint32_t index) override {
+ std::string cmd;
+ current_command_args.GetCommandString(cmd);
+ if (cmd.find(" --continue") == std::string::npos)
+ cmd += " --continue";
+ return cmd;
+ }
+
+protected:
+ ThreadSP GetThread(Args &args, CommandReturnObject &result) {
+ if (args.GetArgumentCount() == 0)
+ return m_exe_ctx.GetThreadSP();
+
+ const char *arg = args.GetArgumentAtIndex(0);
+ uint32_t thread_idx;
+
+ if (!llvm::to_integer(arg, thread_idx)) {
+ result.AppendErrorWithFormat("invalid thread specification: \"%s\"\n",
+ arg);
+ return nullptr;
+ }
+ ThreadSP thread_sp =
+ m_exe_ctx.GetProcessRef().GetThreadList().FindThreadByIndexID(
+ thread_idx);
+ if (!thread_sp)
+ result.AppendErrorWithFormat("no thread with index: \"%s\"\n", arg);
+ return thread_sp;
+ }
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ ThreadSP thread_sp = GetThread(args, result);
+ if (!thread_sp) {
+ result.AppendError("invalid thread\n");
+ return false;
+ }
+
+ if (m_options.m_continue && m_last_id) {
+ // We set up the options to continue one instruction past where
+ // the previous iteration stopped.
+ m_options.m_dumper_options.skip = 1;
+ m_options.m_dumper_options.id = m_last_id;
+ }
+
+ llvm::Expected<TraceCursorUP> cursor_or_error =
+ m_exe_ctx.GetTargetSP()->GetTrace()->CreateNewCursor(*thread_sp);
+
+ if (!cursor_or_error) {
+ result.AppendError(llvm::toString(cursor_or_error.takeError()));
+ return false;
+ }
+ TraceCursorUP &cursor_up = *cursor_or_error;
+
+ if (m_options.m_dumper_options.id &&
+ !cursor_up->HasId(*m_options.m_dumper_options.id)) {
+ result.AppendError("invalid instruction id\n");
+ return false;
+ }
+
+ llvm::Optional<StreamFile> out_file;
+ if (m_options.m_output_file) {
+ out_file.emplace(m_options.m_output_file->GetPath().c_str(),
+ File::eOpenOptionWriteOnly | File::eOpenOptionCanCreate,
+ lldb::eFilePermissionsFileDefault);
+ }
+
+ if (m_options.m_continue && !m_last_id) {
+ // We need to stop processing data when we already ran out of instructions
+ // in a previous command. We can fake this by setting the cursor past the
+ // end of the trace.
+ cursor_up->Seek(1, TraceCursor::SeekType::End);
+ }
+
+ TraceDumper dumper(std::move(cursor_up),
+ out_file ? *out_file : result.GetOutputStream(),
+ m_options.m_dumper_options);
+
+ m_last_id = dumper.DumpInstructions(m_options.m_count);
+ return true;
+ }
+
+ CommandOptions m_options;
+ // Last traversed id used to continue a repeat command. None means
+ // that all the trace has been consumed.
+ llvm::Optional<lldb::user_id_t> m_last_id;
+};
+
+// CommandObjectTraceDumpInfo
+#define LLDB_OPTIONS_thread_trace_dump_info
+#include "CommandOptions.inc"
+
+class CommandObjectTraceDumpInfo : public CommandObjectIterateOverThreads {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() { OptionParsingStarting(nullptr); }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'v': {
+ m_verbose = true;
+ break;
+ }
+ case 'j': {
+ m_json = true;
+ break;
+ }
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_verbose = false;
+ m_json = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_thread_trace_dump_info_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ bool m_verbose;
+ bool m_json;
+ };
+
+ CommandObjectTraceDumpInfo(CommandInterpreter &interpreter)
+ : CommandObjectIterateOverThreads(
+ interpreter, "thread trace dump info",
+ "Dump the traced information for one or more threads. If no "
+ "threads are specified, show the current thread. Use the "
+ "thread-index \"all\" to see all threads.",
+ nullptr,
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused |
+ eCommandProcessMustBeTraced) {}
+
+ ~CommandObjectTraceDumpInfo() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override {
+ const TraceSP &trace_sp = m_exe_ctx.GetTargetSP()->GetTrace();
+ ThreadSP thread_sp =
+ m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid);
+ trace_sp->DumpTraceInfo(*thread_sp, result.GetOutputStream(),
+ m_options.m_verbose, m_options.m_json);
+ return true;
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectMultiwordTraceDump
+class CommandObjectMultiwordTraceDump : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordTraceDump(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "dump",
+ "Commands for displaying trace information of the threads "
+ "in the current process.",
+ "thread trace dump <subcommand> [<subcommand objects>]") {
+ LoadSubCommand(
+ "instructions",
+ CommandObjectSP(new CommandObjectTraceDumpInstructions(interpreter)));
+ LoadSubCommand(
+ "info", CommandObjectSP(new CommandObjectTraceDumpInfo(interpreter)));
+ }
+ ~CommandObjectMultiwordTraceDump() override = default;
+};
+
+// CommandObjectMultiwordTrace
+class CommandObjectMultiwordTrace : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordTrace(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "trace",
+ "Commands for operating on traces of the threads in the current "
+ "process.",
+ "thread trace <subcommand> [<subcommand objects>]") {
+ LoadSubCommand("dump", CommandObjectSP(new CommandObjectMultiwordTraceDump(
+ interpreter)));
+ LoadSubCommand("start",
+ CommandObjectSP(new CommandObjectTraceStart(interpreter)));
+ LoadSubCommand("stop",
+ CommandObjectSP(new CommandObjectTraceStop(interpreter)));
+ LoadSubCommand("export",
+ CommandObjectSP(new CommandObjectTraceExport(interpreter)));
+ }
+
+ ~CommandObjectMultiwordTrace() override = default;
+};
+
+// CommandObjectMultiwordThread
+
+CommandObjectMultiwordThread::CommandObjectMultiwordThread(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "thread",
+ "Commands for operating on "
+ "one or more threads in "
+ "the current process.",
+ "thread <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("backtrace", CommandObjectSP(new CommandObjectThreadBacktrace(
+ interpreter)));
+ LoadSubCommand("continue",
+ CommandObjectSP(new CommandObjectThreadContinue(interpreter)));
+ LoadSubCommand("list",
+ CommandObjectSP(new CommandObjectThreadList(interpreter)));
+ LoadSubCommand("return",
+ CommandObjectSP(new CommandObjectThreadReturn(interpreter)));
+ LoadSubCommand("jump",
+ CommandObjectSP(new CommandObjectThreadJump(interpreter)));
+ LoadSubCommand("select",
+ CommandObjectSP(new CommandObjectThreadSelect(interpreter)));
+ LoadSubCommand("until",
+ CommandObjectSP(new CommandObjectThreadUntil(interpreter)));
+ LoadSubCommand("info",
+ CommandObjectSP(new CommandObjectThreadInfo(interpreter)));
+ LoadSubCommand("exception", CommandObjectSP(new CommandObjectThreadException(
+ interpreter)));
+ LoadSubCommand("siginfo",
+ CommandObjectSP(new CommandObjectThreadSiginfo(interpreter)));
+ LoadSubCommand("step-in",
+ CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope(
+ interpreter, "thread step-in",
+ "Source level single step, stepping into calls. Defaults "
+ "to current thread unless specified.",
+ nullptr, eStepTypeInto, eStepScopeSource)));
+
+ LoadSubCommand("step-out",
+ CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope(
+ interpreter, "thread step-out",
+ "Finish executing the current stack frame and stop after "
+ "returning. Defaults to current thread unless specified.",
+ nullptr, eStepTypeOut, eStepScopeSource)));
+
+ LoadSubCommand("step-over",
+ CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope(
+ interpreter, "thread step-over",
+ "Source level single step, stepping over calls. Defaults "
+ "to current thread unless specified.",
+ nullptr, eStepTypeOver, eStepScopeSource)));
+
+ LoadSubCommand("step-inst",
+ CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope(
+ interpreter, "thread step-inst",
+ "Instruction level single step, stepping into calls. "
+ "Defaults to current thread unless specified.",
+ nullptr, eStepTypeTrace, eStepScopeInstruction)));
+
+ LoadSubCommand("step-inst-over",
+ CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope(
+ interpreter, "thread step-inst-over",
+ "Instruction level single step, stepping over calls. "
+ "Defaults to current thread unless specified.",
+ nullptr, eStepTypeTraceOver, eStepScopeInstruction)));
+
+ LoadSubCommand(
+ "step-scripted",
+ CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope(
+ interpreter, "thread step-scripted",
+ "Step as instructed by the script class passed in the -C option. "
+ "You can also specify a dictionary of key (-k) and value (-v) pairs "
+ "that will be used to populate an SBStructuredData Dictionary, which "
+ "will be passed to the constructor of the class implementing the "
+ "scripted step. See the Python Reference for more details.",
+ nullptr, eStepTypeScripted, eStepScopeSource)));
+
+ LoadSubCommand("plan", CommandObjectSP(new CommandObjectMultiwordThreadPlan(
+ interpreter)));
+ LoadSubCommand("trace",
+ CommandObjectSP(new CommandObjectMultiwordTrace(interpreter)));
+}
+
+CommandObjectMultiwordThread::~CommandObjectMultiwordThread() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectThread.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectThread.h
new file mode 100644
index 000000000000..3ca6a2159501
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectThread.h
@@ -0,0 +1,25 @@
+//===-- CommandObjectThread.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTTHREAD_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTTHREAD_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+class CommandObjectMultiwordThread : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordThread(CommandInterpreter &interpreter);
+
+ ~CommandObjectMultiwordThread() override;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTTHREAD_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectThreadUtil.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectThreadUtil.cpp
new file mode 100644
index 000000000000..504d5fa0118d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectThreadUtil.cpp
@@ -0,0 +1,210 @@
+//===-- CommandObjectThreadUtil.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectThreadUtil.h"
+
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm;
+
+CommandObjectIterateOverThreads::CommandObjectIterateOverThreads(
+ CommandInterpreter &interpreter, const char *name, const char *help,
+ const char *syntax, uint32_t flags)
+ : CommandObjectParsed(interpreter, name, help, syntax, flags) {
+ // These commands all take thread ID's as arguments.
+ CommandArgumentData thread_arg{eArgTypeThreadIndex, eArgRepeatStar};
+ m_arguments.push_back({thread_arg});
+}
+
+CommandObjectMultipleThreads::CommandObjectMultipleThreads(
+ CommandInterpreter &interpreter, const char *name, const char *help,
+ const char *syntax, uint32_t flags)
+ : CommandObjectParsed(interpreter, name, help, syntax, flags) {
+ // These commands all take thread ID's as arguments.
+ CommandArgumentData thread_arg{eArgTypeThreadIndex, eArgRepeatStar};
+ m_arguments.push_back({thread_arg});
+}
+
+bool CommandObjectIterateOverThreads::DoExecute(Args &command,
+ CommandReturnObject &result) {
+ result.SetStatus(m_success_return);
+
+ bool all_threads = false;
+ if (command.GetArgumentCount() == 0) {
+ Thread *thread = m_exe_ctx.GetThreadPtr();
+ if (!thread || !HandleOneThread(thread->GetID(), result))
+ return false;
+ return result.Succeeded();
+ } else if (command.GetArgumentCount() == 1) {
+ all_threads = ::strcmp(command.GetArgumentAtIndex(0), "all") == 0;
+ m_unique_stacks = ::strcmp(command.GetArgumentAtIndex(0), "unique") == 0;
+ }
+
+ // Use tids instead of ThreadSPs to prevent deadlocking problems which
+ // result from JIT-ing code while iterating over the (locked) ThreadSP
+ // list.
+ std::vector<lldb::tid_t> tids;
+
+ if (all_threads || m_unique_stacks) {
+ Process *process = m_exe_ctx.GetProcessPtr();
+
+ for (ThreadSP thread_sp : process->Threads())
+ tids.push_back(thread_sp->GetID());
+ } else {
+ const size_t num_args = command.GetArgumentCount();
+ Process *process = m_exe_ctx.GetProcessPtr();
+
+ std::lock_guard<std::recursive_mutex> guard(
+ process->GetThreadList().GetMutex());
+
+ for (size_t i = 0; i < num_args; i++) {
+ uint32_t thread_idx;
+ if (!llvm::to_integer(command.GetArgumentAtIndex(i), thread_idx)) {
+ result.AppendErrorWithFormat("invalid thread specification: \"%s\"\n",
+ command.GetArgumentAtIndex(i));
+ return false;
+ }
+
+ ThreadSP thread =
+ process->GetThreadList().FindThreadByIndexID(thread_idx);
+
+ if (!thread) {
+ result.AppendErrorWithFormat("no thread with index: \"%s\"\n",
+ command.GetArgumentAtIndex(i));
+ return false;
+ }
+
+ tids.push_back(thread->GetID());
+ }
+ }
+
+ if (m_unique_stacks) {
+ // Iterate over threads, finding unique stack buckets.
+ std::set<UniqueStack> unique_stacks;
+ for (const lldb::tid_t &tid : tids) {
+ if (!BucketThread(tid, unique_stacks, result)) {
+ return false;
+ }
+ }
+
+ // Write the thread id's and unique call stacks to the output stream
+ Stream &strm = result.GetOutputStream();
+ Process *process = m_exe_ctx.GetProcessPtr();
+ for (const UniqueStack &stack : unique_stacks) {
+ // List the common thread ID's
+ const std::vector<uint32_t> &thread_index_ids =
+ stack.GetUniqueThreadIndexIDs();
+ strm.Format("{0} thread(s) ", thread_index_ids.size());
+ for (const uint32_t &thread_index_id : thread_index_ids) {
+ strm.Format("#{0} ", thread_index_id);
+ }
+ strm.EOL();
+
+ // List the shared call stack for this set of threads
+ uint32_t representative_thread_id = stack.GetRepresentativeThread();
+ ThreadSP thread = process->GetThreadList().FindThreadByIndexID(
+ representative_thread_id);
+ if (!HandleOneThread(thread->GetID(), result)) {
+ return false;
+ }
+ }
+ } else {
+ uint32_t idx = 0;
+ for (const lldb::tid_t &tid : tids) {
+ if (idx != 0 && m_add_return)
+ result.AppendMessage("");
+
+ if (!HandleOneThread(tid, result))
+ return false;
+
+ ++idx;
+ }
+ }
+ return result.Succeeded();
+}
+
+bool CommandObjectIterateOverThreads::BucketThread(
+ lldb::tid_t tid, std::set<UniqueStack> &unique_stacks,
+ CommandReturnObject &result) {
+ // Grab the corresponding thread for the given thread id.
+ Process *process = m_exe_ctx.GetProcessPtr();
+ Thread *thread = process->GetThreadList().FindThreadByID(tid).get();
+ if (thread == nullptr) {
+ result.AppendErrorWithFormatv("Failed to process thread #{0}.\n", tid);
+ return false;
+ }
+
+ // Collect the each frame's address for this call-stack
+ std::stack<lldb::addr_t> stack_frames;
+ const uint32_t frame_count = thread->GetStackFrameCount();
+ for (uint32_t frame_index = 0; frame_index < frame_count; frame_index++) {
+ const lldb::StackFrameSP frame_sp =
+ thread->GetStackFrameAtIndex(frame_index);
+ const lldb::addr_t pc = frame_sp->GetStackID().GetPC();
+ stack_frames.push(pc);
+ }
+
+ uint32_t thread_index_id = thread->GetIndexID();
+ UniqueStack new_unique_stack(stack_frames, thread_index_id);
+
+ // Try to match the threads stack to and existing entry.
+ std::set<UniqueStack>::iterator matching_stack =
+ unique_stacks.find(new_unique_stack);
+ if (matching_stack != unique_stacks.end()) {
+ matching_stack->AddThread(thread_index_id);
+ } else {
+ unique_stacks.insert(new_unique_stack);
+ }
+ return true;
+}
+
+bool CommandObjectMultipleThreads::DoExecute(Args &command,
+ CommandReturnObject &result) {
+ Process &process = m_exe_ctx.GetProcessRef();
+
+ std::vector<lldb::tid_t> tids;
+ const size_t num_args = command.GetArgumentCount();
+
+ std::lock_guard<std::recursive_mutex> guard(
+ process.GetThreadList().GetMutex());
+
+ if (num_args > 0 && ::strcmp(command.GetArgumentAtIndex(0), "all") == 0) {
+ for (ThreadSP thread_sp : process.Threads())
+ tids.push_back(thread_sp->GetID());
+ } else {
+ if (num_args == 0) {
+ Thread &thread = m_exe_ctx.GetThreadRef();
+ tids.push_back(thread.GetID());
+ }
+
+ for (size_t i = 0; i < num_args; i++) {
+ uint32_t thread_idx;
+ if (!llvm::to_integer(command.GetArgumentAtIndex(i), thread_idx)) {
+ result.AppendErrorWithFormat("invalid thread specification: \"%s\"\n",
+ command.GetArgumentAtIndex(i));
+ return false;
+ }
+
+ ThreadSP thread = process.GetThreadList().FindThreadByIndexID(thread_idx);
+
+ if (!thread) {
+ result.AppendErrorWithFormat("no thread with index: \"%s\"\n",
+ command.GetArgumentAtIndex(i));
+ return false;
+ }
+
+ tids.push_back(thread->GetID());
+ }
+ }
+
+ return DoExecuteOnThreads(command, result, tids);
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectThreadUtil.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectThreadUtil.h
new file mode 100644
index 000000000000..c8f51eabc043
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectThreadUtil.h
@@ -0,0 +1,103 @@
+//===-- CommandObjectThreadUtil.h -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTTHREADUTIL_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTTHREADUTIL_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+class CommandObjectIterateOverThreads : public CommandObjectParsed {
+
+ class UniqueStack {
+ public:
+ UniqueStack(std::stack<lldb::addr_t> stack_frames, uint32_t thread_index_id)
+ : m_stack_frames(stack_frames) {
+ m_thread_index_ids.push_back(thread_index_id);
+ }
+
+ void AddThread(uint32_t thread_index_id) const {
+ m_thread_index_ids.push_back(thread_index_id);
+ }
+
+ const std::vector<uint32_t> &GetUniqueThreadIndexIDs() const {
+ return m_thread_index_ids;
+ }
+
+ lldb::tid_t GetRepresentativeThread() const {
+ return m_thread_index_ids.front();
+ }
+
+ friend bool inline operator<(const UniqueStack &lhs,
+ const UniqueStack &rhs) {
+ return lhs.m_stack_frames < rhs.m_stack_frames;
+ }
+
+ protected:
+ // Mark the thread index as mutable, as we don't care about it from a const
+ // perspective, we only care about m_stack_frames so we keep our std::set
+ // sorted.
+ mutable std::vector<uint32_t> m_thread_index_ids;
+ std::stack<lldb::addr_t> m_stack_frames;
+ };
+
+public:
+ CommandObjectIterateOverThreads(CommandInterpreter &interpreter,
+ const char *name, const char *help,
+ const char *syntax, uint32_t flags);
+
+ ~CommandObjectIterateOverThreads() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override;
+
+protected:
+ // Override this to do whatever you need to do for one thread.
+ //
+ // If you return false, the iteration will stop, otherwise it will proceed.
+ // The result is set to m_success_return (defaults to
+ // eReturnStatusSuccessFinishResult) before the iteration, so you only need
+ // to set the return status in HandleOneThread if you want to indicate an
+ // error. If m_add_return is true, a blank line will be inserted between each
+ // of the listings (except the last one.)
+
+ virtual bool HandleOneThread(lldb::tid_t, CommandReturnObject &result) = 0;
+
+ bool BucketThread(lldb::tid_t tid, std::set<UniqueStack> &unique_stacks,
+ CommandReturnObject &result);
+
+ lldb::ReturnStatus m_success_return = lldb::eReturnStatusSuccessFinishResult;
+ bool m_unique_stacks = false;
+ bool m_add_return = true;
+};
+
+/// Class similar to \a CommandObjectIterateOverThreads, but which performs
+/// an action on multiple threads at once instead of iterating over each thread.
+class CommandObjectMultipleThreads : public CommandObjectParsed {
+public:
+ CommandObjectMultipleThreads(CommandInterpreter &interpreter,
+ const char *name, const char *help,
+ const char *syntax, uint32_t flags);
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override;
+
+protected:
+ /// Method that handles the command after the main arguments have been parsed.
+ ///
+ /// \param[in] tids
+ /// The thread ids passed as arguments.
+ ///
+ /// \return
+ /// A boolean result similar to the one expected from \a DoExecute.
+ virtual bool DoExecuteOnThreads(Args &command, CommandReturnObject &result,
+ llvm::ArrayRef<lldb::tid_t> tids) = 0;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTTHREADUTIL_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectTrace.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectTrace.cpp
new file mode 100644
index 000000000000..227de2de7065
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectTrace.cpp
@@ -0,0 +1,433 @@
+//===-- CommandObjectTrace.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectTrace.h"
+
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionGroupFormat.h"
+#include "lldb/Interpreter/OptionValueBoolean.h"
+#include "lldb/Interpreter/OptionValueLanguage.h"
+#include "lldb/Interpreter/OptionValueString.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Trace.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm;
+
+// CommandObjectTraceSave
+#define LLDB_OPTIONS_trace_save
+#include "CommandOptions.inc"
+
+#pragma mark CommandObjectTraceSave
+
+class CommandObjectTraceSave : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() { OptionParsingStarting(nullptr); }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'c': {
+ m_compact = true;
+ break;
+ }
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_compact = false;
+ };
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_trace_save_options);
+ };
+
+ bool m_compact;
+ };
+
+ Options *GetOptions() override { return &m_options; }
+
+ CommandObjectTraceSave(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "trace save",
+ "Save the trace of the current target in the specified directory, "
+ "which will be created if needed. "
+ "This directory will contain a trace bundle, with all the "
+ "necessary files the reconstruct the trace session even on a "
+ "different computer. "
+ "Part of this bundle is the bundle description file with the name "
+ "trace.json. This file can be used by the \"trace load\" command "
+ "to load this trace in LLDB."
+ "Note: if the current target contains information of multiple "
+ "processes or targets, they all will be included in the bundle.",
+ "trace save [<cmd-options>] <bundle_directory>",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused |
+ eCommandProcessMustBeTraced) {
+ CommandArgumentData bundle_dir{eArgTypeDirectoryName, eArgRepeatPlain};
+ m_arguments.push_back({bundle_dir});
+ }
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+ request, nullptr);
+ }
+
+ ~CommandObjectTraceSave() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (command.size() != 1) {
+ result.AppendError("a single path to a directory where the trace bundle "
+ "will be created is required");
+ return false;
+ }
+
+ FileSpec bundle_dir(command[0].ref());
+ FileSystem::Instance().Resolve(bundle_dir);
+
+ ProcessSP process_sp = m_exe_ctx.GetProcessSP();
+
+ TraceSP trace_sp = process_sp->GetTarget().GetTrace();
+
+ if (llvm::Expected<FileSpec> desc_file =
+ trace_sp->SaveToDisk(bundle_dir, m_options.m_compact)) {
+ result.AppendMessageWithFormatv(
+ "Trace bundle description file written to: {0}", *desc_file);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError(toString(desc_file.takeError()));
+ }
+
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectTraceLoad
+#define LLDB_OPTIONS_trace_load
+#include "CommandOptions.inc"
+
+#pragma mark CommandObjectTraceLoad
+
+class CommandObjectTraceLoad : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() { OptionParsingStarting(nullptr); }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'v': {
+ m_verbose = true;
+ break;
+ }
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_verbose = false;
+ }
+
+ ArrayRef<OptionDefinition> GetDefinitions() override {
+ return makeArrayRef(g_trace_load_options);
+ }
+
+ bool m_verbose; // Enable verbose logging for debugging purposes.
+ };
+
+ CommandObjectTraceLoad(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "trace load",
+ "Load a post-mortem processor trace session from a trace bundle.",
+ "trace load <trace_description_file>") {
+ CommandArgumentData session_file_arg{eArgTypeFilename, eArgRepeatPlain};
+ m_arguments.push_back({session_file_arg});
+ }
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+ request, nullptr);
+ }
+
+ ~CommandObjectTraceLoad() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (command.size() != 1) {
+ result.AppendError("a single path to a JSON file containing a the "
+ "description of the trace bundle is required");
+ return false;
+ }
+
+ const FileSpec trace_description_file(command[0].ref());
+
+ llvm::Expected<lldb::TraceSP> trace_or_err =
+ Trace::LoadPostMortemTraceFromFile(GetDebugger(),
+ trace_description_file);
+
+ if (!trace_or_err) {
+ result.AppendErrorWithFormat(
+ "%s\n", llvm::toString(trace_or_err.takeError()).c_str());
+ return false;
+ }
+
+ if (m_options.m_verbose) {
+ result.AppendMessageWithFormatv("loading trace with plugin {0}\n",
+ trace_or_err.get()->GetPluginName());
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectTraceDump
+#define LLDB_OPTIONS_trace_dump
+#include "CommandOptions.inc"
+
+#pragma mark CommandObjectTraceDump
+
+class CommandObjectTraceDump : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() { OptionParsingStarting(nullptr); }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'v': {
+ m_verbose = true;
+ break;
+ }
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_verbose = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_trace_dump_options);
+ }
+
+ bool m_verbose; // Enable verbose logging for debugging purposes.
+ };
+
+ CommandObjectTraceDump(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "trace dump",
+ "Dump the loaded processor trace data.",
+ "trace dump") {}
+
+ ~CommandObjectTraceDump() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Status error;
+ // TODO: fill in the dumping code here!
+ if (error.Success()) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("%s\n", error.AsCString());
+ }
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectTraceSchema
+#define LLDB_OPTIONS_trace_schema
+#include "CommandOptions.inc"
+
+#pragma mark CommandObjectTraceSchema
+
+class CommandObjectTraceSchema : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() { OptionParsingStarting(nullptr); }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'v': {
+ m_verbose = true;
+ break;
+ }
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_verbose = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_trace_schema_options);
+ }
+
+ bool m_verbose; // Enable verbose logging for debugging purposes.
+ };
+
+ CommandObjectTraceSchema(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "trace schema",
+ "Show the schema of the given trace plugin.",
+ "trace schema <plug-in>. Use the plug-in name "
+ "\"all\" to see all schemas.\n") {
+ CommandArgumentData plugin_arg{eArgTypeNone, eArgRepeatPlain};
+ m_arguments.push_back({plugin_arg});
+ }
+
+ ~CommandObjectTraceSchema() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Status error;
+ if (command.empty()) {
+ result.AppendError(
+ "trace schema cannot be invoked without a plug-in as argument");
+ return false;
+ }
+
+ StringRef plugin_name(command[0].c_str());
+ if (plugin_name == "all") {
+ size_t index = 0;
+ while (true) {
+ StringRef schema = PluginManager::GetTraceSchema(index++);
+ if (schema.empty())
+ break;
+
+ result.AppendMessage(schema);
+ }
+ } else {
+ if (Expected<StringRef> schemaOrErr =
+ Trace::FindPluginSchema(plugin_name))
+ result.AppendMessage(*schemaOrErr);
+ else
+ error = schemaOrErr.takeError();
+ }
+
+ if (error.Success()) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("%s\n", error.AsCString());
+ }
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectTrace
+
+CommandObjectTrace::CommandObjectTrace(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "trace",
+ "Commands for loading and using processor "
+ "trace information.",
+ "trace [<sub-command-options>]") {
+ LoadSubCommand("load",
+ CommandObjectSP(new CommandObjectTraceLoad(interpreter)));
+ LoadSubCommand("dump",
+ CommandObjectSP(new CommandObjectTraceDump(interpreter)));
+ LoadSubCommand("save",
+ CommandObjectSP(new CommandObjectTraceSave(interpreter)));
+ LoadSubCommand("schema",
+ CommandObjectSP(new CommandObjectTraceSchema(interpreter)));
+}
+
+CommandObjectTrace::~CommandObjectTrace() = default;
+
+Expected<CommandObjectSP> CommandObjectTraceProxy::DoGetProxyCommandObject() {
+ ProcessSP process_sp = m_interpreter.GetExecutionContext().GetProcessSP();
+
+ if (!process_sp)
+ return createStringError(inconvertibleErrorCode(),
+ "Process not available.");
+ if (m_live_debug_session_only && !process_sp->IsLiveDebugSession())
+ return createStringError(inconvertibleErrorCode(),
+ "Process must be alive.");
+
+ if (Expected<TraceSP> trace_sp = process_sp->GetTarget().GetTraceOrCreate())
+ return GetDelegateCommand(**trace_sp);
+ else
+ return createStringError(inconvertibleErrorCode(),
+ "Tracing is not supported. %s",
+ toString(trace_sp.takeError()).c_str());
+}
+
+CommandObject *CommandObjectTraceProxy::GetProxyCommandObject() {
+ if (Expected<CommandObjectSP> delegate = DoGetProxyCommandObject()) {
+ m_delegate_sp = *delegate;
+ m_delegate_error.clear();
+ return m_delegate_sp.get();
+ } else {
+ m_delegate_sp.reset();
+ m_delegate_error = toString(delegate.takeError());
+ return nullptr;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectTrace.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectTrace.h
new file mode 100644
index 000000000000..b96a3094cefc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectTrace.h
@@ -0,0 +1,51 @@
+//===-- CommandObjectTrace.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTTRACE_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTTRACE_H
+
+#include "CommandObjectThreadUtil.h"
+
+namespace lldb_private {
+
+class CommandObjectTrace : public CommandObjectMultiword {
+public:
+ CommandObjectTrace(CommandInterpreter &interpreter);
+
+ ~CommandObjectTrace() override;
+};
+
+/// This class works by delegating the logic to the actual trace plug-in that
+/// can support the current process.
+class CommandObjectTraceProxy : public CommandObjectProxy {
+public:
+ CommandObjectTraceProxy(bool live_debug_session_only,
+ CommandInterpreter &interpreter, const char *name,
+ const char *help = nullptr,
+ const char *syntax = nullptr, uint32_t flags = 0)
+ : CommandObjectProxy(interpreter, name, help, syntax, flags),
+ m_live_debug_session_only(live_debug_session_only) {}
+
+protected:
+ virtual lldb::CommandObjectSP GetDelegateCommand(Trace &trace) = 0;
+
+ llvm::Expected<lldb::CommandObjectSP> DoGetProxyCommandObject();
+
+ CommandObject *GetProxyCommandObject() override;
+
+private:
+ llvm::StringRef GetUnsupportedError() override { return m_delegate_error; }
+
+ bool m_live_debug_session_only;
+ lldb::CommandObjectSP m_delegate_sp;
+ std::string m_delegate_error;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTTRACE_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectType.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectType.cpp
new file mode 100644
index 000000000000..3ad3571b390c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectType.cpp
@@ -0,0 +1,3065 @@
+//===-- CommandObjectType.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectType.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/IOHandler.h"
+#include "lldb/DataFormatters/DataVisualization.h"
+#include "lldb/Host/Config.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionGroupFormat.h"
+#include "lldb/Interpreter/OptionValueBoolean.h"
+#include "lldb/Interpreter/OptionValueLanguage.h"
+#include "lldb/Interpreter/OptionValueString.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/StringList.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+#include <algorithm>
+#include <functional>
+#include <memory>
+
+#define CHECK_FORMATTER_KIND_MASK(VAL) \
+ ((m_formatter_kind_mask & (VAL)) == (VAL))
+
+using namespace lldb;
+using namespace lldb_private;
+
+class ScriptAddOptions {
+public:
+ TypeSummaryImpl::Flags m_flags;
+ StringList m_target_types;
+ bool m_regex;
+ ConstString m_name;
+ std::string m_category;
+
+ ScriptAddOptions(const TypeSummaryImpl::Flags &flags, bool regx,
+ ConstString name, std::string catg)
+ : m_flags(flags), m_regex(regx), m_name(name), m_category(catg) {}
+
+ typedef std::shared_ptr<ScriptAddOptions> SharedPointer;
+};
+
+class SynthAddOptions {
+public:
+ bool m_skip_pointers;
+ bool m_skip_references;
+ bool m_cascade;
+ bool m_regex;
+ StringList m_target_types;
+ std::string m_category;
+
+ SynthAddOptions(bool sptr, bool sref, bool casc, bool regx, std::string catg)
+ : m_skip_pointers(sptr), m_skip_references(sref), m_cascade(casc),
+ m_regex(regx), m_category(catg) {}
+
+ typedef std::shared_ptr<SynthAddOptions> SharedPointer;
+};
+
+static bool WarnOnPotentialUnquotedUnsignedType(Args &command,
+ CommandReturnObject &result) {
+ if (command.empty())
+ return false;
+
+ for (auto entry : llvm::enumerate(command.entries().drop_back())) {
+ if (entry.value().ref() != "unsigned")
+ continue;
+ auto next = command.entries()[entry.index() + 1].ref();
+ if (next == "int" || next == "short" || next == "char" || next == "long") {
+ result.AppendWarningWithFormat(
+ "unsigned %s being treated as two types. if you meant the combined "
+ "type "
+ "name use quotes, as in \"unsigned %s\"\n",
+ next.str().c_str(), next.str().c_str());
+ return true;
+ }
+ }
+ return false;
+}
+
+#define LLDB_OPTIONS_type_summary_add
+#include "CommandOptions.inc"
+
+class CommandObjectTypeSummaryAdd : public CommandObjectParsed,
+ public IOHandlerDelegateMultiline {
+private:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions(CommandInterpreter &interpreter) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override;
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_type_summary_add_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ TypeSummaryImpl::Flags m_flags;
+ bool m_regex;
+ std::string m_format_string;
+ ConstString m_name;
+ std::string m_python_script;
+ std::string m_python_function;
+ bool m_is_add_script;
+ std::string m_category;
+ };
+
+ CommandOptions m_options;
+
+ Options *GetOptions() override { return &m_options; }
+
+ bool Execute_ScriptSummary(Args &command, CommandReturnObject &result);
+
+ bool Execute_StringSummary(Args &command, CommandReturnObject &result);
+
+public:
+ enum SummaryFormatType { eRegularSummary, eRegexSummary, eNamedSummary };
+
+ CommandObjectTypeSummaryAdd(CommandInterpreter &interpreter);
+
+ ~CommandObjectTypeSummaryAdd() override = default;
+
+ void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
+ static const char *g_summary_addreader_instructions =
+ "Enter your Python command(s). Type 'DONE' to end.\n"
+ "def function (valobj,internal_dict):\n"
+ " \"\"\"valobj: an SBValue which you want to provide a summary "
+ "for\n"
+ " internal_dict: an LLDB support object not to be used\"\"\"\n";
+
+ StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
+ if (output_sp && interactive) {
+ output_sp->PutCString(g_summary_addreader_instructions);
+ output_sp->Flush();
+ }
+ }
+
+ void IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &data) override {
+ StreamFileSP error_sp = io_handler.GetErrorStreamFileSP();
+
+#if LLDB_ENABLE_PYTHON
+ ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
+ if (interpreter) {
+ StringList lines;
+ lines.SplitIntoLines(data);
+ if (lines.GetSize() > 0) {
+ ScriptAddOptions *options_ptr =
+ ((ScriptAddOptions *)io_handler.GetUserData());
+ if (options_ptr) {
+ ScriptAddOptions::SharedPointer options(
+ options_ptr); // this will ensure that we get rid of the pointer
+ // when going out of scope
+
+ ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
+ if (interpreter) {
+ std::string funct_name_str;
+ if (interpreter->GenerateTypeScriptFunction(lines,
+ funct_name_str)) {
+ if (funct_name_str.empty()) {
+ error_sp->Printf("unable to obtain a valid function name from "
+ "the script interpreter.\n");
+ error_sp->Flush();
+ } else {
+ // now I have a valid function name, let's add this as script
+ // for every type in the list
+
+ TypeSummaryImplSP script_format;
+ script_format = std::make_shared<ScriptSummaryFormat>(
+ options->m_flags, funct_name_str.c_str(),
+ lines.CopyList(" ").c_str());
+
+ Status error;
+
+ for (const std::string &type_name : options->m_target_types) {
+ CommandObjectTypeSummaryAdd::AddSummary(
+ ConstString(type_name), script_format,
+ (options->m_regex
+ ? CommandObjectTypeSummaryAdd::eRegexSummary
+ : CommandObjectTypeSummaryAdd::eRegularSummary),
+ options->m_category, &error);
+ if (error.Fail()) {
+ error_sp->Printf("error: %s", error.AsCString());
+ error_sp->Flush();
+ }
+ }
+
+ if (options->m_name) {
+ CommandObjectTypeSummaryAdd::AddSummary(
+ options->m_name, script_format,
+ CommandObjectTypeSummaryAdd::eNamedSummary,
+ options->m_category, &error);
+ if (error.Fail()) {
+ CommandObjectTypeSummaryAdd::AddSummary(
+ options->m_name, script_format,
+ CommandObjectTypeSummaryAdd::eNamedSummary,
+ options->m_category, &error);
+ if (error.Fail()) {
+ error_sp->Printf("error: %s", error.AsCString());
+ error_sp->Flush();
+ }
+ } else {
+ error_sp->Printf("error: %s", error.AsCString());
+ error_sp->Flush();
+ }
+ } else {
+ if (error.AsCString()) {
+ error_sp->Printf("error: %s", error.AsCString());
+ error_sp->Flush();
+ }
+ }
+ }
+ } else {
+ error_sp->Printf("error: unable to generate a function.\n");
+ error_sp->Flush();
+ }
+ } else {
+ error_sp->Printf("error: no script interpreter.\n");
+ error_sp->Flush();
+ }
+ } else {
+ error_sp->Printf("error: internal synchronization information "
+ "missing or invalid.\n");
+ error_sp->Flush();
+ }
+ } else {
+ error_sp->Printf("error: empty function, didn't add python command.\n");
+ error_sp->Flush();
+ }
+ } else {
+ error_sp->Printf(
+ "error: script interpreter missing, didn't add python command.\n");
+ error_sp->Flush();
+ }
+#endif
+ io_handler.SetIsDone(true);
+ }
+
+ static bool AddSummary(ConstString type_name, lldb::TypeSummaryImplSP entry,
+ SummaryFormatType type, std::string category,
+ Status *error = nullptr);
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override;
+};
+
+static const char *g_synth_addreader_instructions =
+ "Enter your Python command(s). Type 'DONE' to end.\n"
+ "You must define a Python class with these methods:\n"
+ " def __init__(self, valobj, internal_dict):\n"
+ " def num_children(self):\n"
+ " def get_child_at_index(self, index):\n"
+ " def get_child_index(self, name):\n"
+ " def update(self):\n"
+ " '''Optional'''\n"
+ "class synthProvider:\n";
+
+#define LLDB_OPTIONS_type_synth_add
+#include "CommandOptions.inc"
+
+class CommandObjectTypeSynthAdd : public CommandObjectParsed,
+ public IOHandlerDelegateMultiline {
+private:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+ bool success;
+
+ switch (short_option) {
+ case 'C':
+ m_cascade = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (!success)
+ error.SetErrorStringWithFormat("invalid value for cascade: %s",
+ option_arg.str().c_str());
+ break;
+ case 'P':
+ handwrite_python = true;
+ break;
+ case 'l':
+ m_class_name = std::string(option_arg);
+ is_class_based = true;
+ break;
+ case 'p':
+ m_skip_pointers = true;
+ break;
+ case 'r':
+ m_skip_references = true;
+ break;
+ case 'w':
+ m_category = std::string(option_arg);
+ break;
+ case 'x':
+ m_regex = true;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_cascade = true;
+ m_class_name = "";
+ m_skip_pointers = false;
+ m_skip_references = false;
+ m_category = "default";
+ is_class_based = false;
+ handwrite_python = false;
+ m_regex = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_type_synth_add_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ bool m_cascade;
+ bool m_skip_references;
+ bool m_skip_pointers;
+ std::string m_class_name;
+ bool m_input_python;
+ std::string m_category;
+ bool is_class_based;
+ bool handwrite_python;
+ bool m_regex;
+ };
+
+ CommandOptions m_options;
+
+ Options *GetOptions() override { return &m_options; }
+
+ bool Execute_HandwritePython(Args &command, CommandReturnObject &result);
+
+ bool Execute_PythonClass(Args &command, CommandReturnObject &result);
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ WarnOnPotentialUnquotedUnsignedType(command, result);
+
+ if (m_options.handwrite_python)
+ return Execute_HandwritePython(command, result);
+ else if (m_options.is_class_based)
+ return Execute_PythonClass(command, result);
+ else {
+ result.AppendError("must either provide a children list, a Python class "
+ "name, or use -P and type a Python class "
+ "line-by-line");
+ return false;
+ }
+ }
+
+ void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
+ StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
+ if (output_sp && interactive) {
+ output_sp->PutCString(g_synth_addreader_instructions);
+ output_sp->Flush();
+ }
+ }
+
+ void IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &data) override {
+ StreamFileSP error_sp = io_handler.GetErrorStreamFileSP();
+
+#if LLDB_ENABLE_PYTHON
+ ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
+ if (interpreter) {
+ StringList lines;
+ lines.SplitIntoLines(data);
+ if (lines.GetSize() > 0) {
+ SynthAddOptions *options_ptr =
+ ((SynthAddOptions *)io_handler.GetUserData());
+ if (options_ptr) {
+ SynthAddOptions::SharedPointer options(
+ options_ptr); // this will ensure that we get rid of the pointer
+ // when going out of scope
+
+ ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
+ if (interpreter) {
+ std::string class_name_str;
+ if (interpreter->GenerateTypeSynthClass(lines, class_name_str)) {
+ if (class_name_str.empty()) {
+ error_sp->Printf(
+ "error: unable to obtain a proper name for the class.\n");
+ error_sp->Flush();
+ } else {
+ // everything should be fine now, let's add the synth provider
+ // class
+
+ SyntheticChildrenSP synth_provider;
+ synth_provider = std::make_shared<ScriptedSyntheticChildren>(
+ SyntheticChildren::Flags()
+ .SetCascades(options->m_cascade)
+ .SetSkipPointers(options->m_skip_pointers)
+ .SetSkipReferences(options->m_skip_references),
+ class_name_str.c_str());
+
+ lldb::TypeCategoryImplSP category;
+ DataVisualization::Categories::GetCategory(
+ ConstString(options->m_category.c_str()), category);
+
+ Status error;
+
+ for (const std::string &type_name : options->m_target_types) {
+ if (!type_name.empty()) {
+ if (!CommandObjectTypeSynthAdd::AddSynth(
+ ConstString(type_name), synth_provider,
+ options->m_regex
+ ? CommandObjectTypeSynthAdd::eRegexSynth
+ : CommandObjectTypeSynthAdd::eRegularSynth,
+ options->m_category, &error)) {
+ error_sp->Printf("error: %s\n", error.AsCString());
+ error_sp->Flush();
+ break;
+ }
+ } else {
+ error_sp->Printf("error: invalid type name.\n");
+ error_sp->Flush();
+ break;
+ }
+ }
+ }
+ } else {
+ error_sp->Printf("error: unable to generate a class.\n");
+ error_sp->Flush();
+ }
+ } else {
+ error_sp->Printf("error: no script interpreter.\n");
+ error_sp->Flush();
+ }
+ } else {
+ error_sp->Printf("error: internal synchronization data missing.\n");
+ error_sp->Flush();
+ }
+ } else {
+ error_sp->Printf("error: empty function, didn't add python command.\n");
+ error_sp->Flush();
+ }
+ } else {
+ error_sp->Printf(
+ "error: script interpreter missing, didn't add python command.\n");
+ error_sp->Flush();
+ }
+
+#endif
+ io_handler.SetIsDone(true);
+ }
+
+public:
+ enum SynthFormatType { eRegularSynth, eRegexSynth };
+
+ CommandObjectTypeSynthAdd(CommandInterpreter &interpreter);
+
+ ~CommandObjectTypeSynthAdd() override = default;
+
+ static bool AddSynth(ConstString type_name, lldb::SyntheticChildrenSP entry,
+ SynthFormatType type, std::string category_name,
+ Status *error);
+};
+
+// CommandObjectTypeFormatAdd
+
+#define LLDB_OPTIONS_type_format_add
+#include "CommandOptions.inc"
+
+class CommandObjectTypeFormatAdd : public CommandObjectParsed {
+private:
+ class CommandOptions : public OptionGroup {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_type_format_add_options);
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_cascade = true;
+ m_skip_pointers = false;
+ m_skip_references = false;
+ m_regex = false;
+ m_category.assign("default");
+ m_custom_type_name.clear();
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option =
+ g_type_format_add_options[option_idx].short_option;
+ bool success;
+
+ switch (short_option) {
+ case 'C':
+ m_cascade = OptionArgParser::ToBoolean(option_value, true, &success);
+ if (!success)
+ error.SetErrorStringWithFormat("invalid value for cascade: %s",
+ option_value.str().c_str());
+ break;
+ case 'p':
+ m_skip_pointers = true;
+ break;
+ case 'w':
+ m_category.assign(std::string(option_value));
+ break;
+ case 'r':
+ m_skip_references = true;
+ break;
+ case 'x':
+ m_regex = true;
+ break;
+ case 't':
+ m_custom_type_name.assign(std::string(option_value));
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ // Instance variables to hold the values for command options.
+
+ bool m_cascade;
+ bool m_skip_references;
+ bool m_skip_pointers;
+ bool m_regex;
+ std::string m_category;
+ std::string m_custom_type_name;
+ };
+
+ OptionGroupOptions m_option_group;
+ OptionGroupFormat m_format_options;
+ CommandOptions m_command_options;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+public:
+ CommandObjectTypeFormatAdd(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "type format add",
+ "Add a new formatting style for a type.", nullptr),
+ m_format_options(eFormatInvalid) {
+ CommandArgumentEntry type_arg;
+ CommandArgumentData type_style_arg;
+
+ type_style_arg.arg_type = eArgTypeName;
+ type_style_arg.arg_repetition = eArgRepeatPlus;
+
+ type_arg.push_back(type_style_arg);
+
+ m_arguments.push_back(type_arg);
+
+ SetHelpLong(
+ R"(
+The following examples of 'type format add' refer to this code snippet for context:
+
+ typedef int Aint;
+ typedef float Afloat;
+ typedef Aint Bint;
+ typedef Afloat Bfloat;
+
+ Aint ix = 5;
+ Bint iy = 5;
+
+ Afloat fx = 3.14;
+ BFloat fy = 3.14;
+
+Adding default formatting:
+
+(lldb) type format add -f hex AInt
+(lldb) frame variable iy
+
+)"
+ " Produces hexadecimal display of iy, because no formatter is available for Bint and \
+the one for Aint is used instead."
+ R"(
+
+To prevent this use the cascade option '-C no' to prevent evaluation of typedef chains:
+
+
+(lldb) type format add -f hex -C no AInt
+
+Similar reasoning applies to this:
+
+(lldb) type format add -f hex -C no float -p
+
+)"
+ " All float values and float references are now formatted as hexadecimal, but not \
+pointers to floats. Nor will it change the default display for Afloat and Bfloat objects.");
+
+ // Add the "--format" to all options groups
+ m_option_group.Append(&m_format_options,
+ OptionGroupFormat::OPTION_GROUP_FORMAT,
+ LLDB_OPT_SET_1);
+ m_option_group.Append(&m_command_options);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectTypeFormatAdd() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc < 1) {
+ result.AppendErrorWithFormat("%s takes one or more args.\n",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ const Format format = m_format_options.GetFormat();
+ if (format == eFormatInvalid &&
+ m_command_options.m_custom_type_name.empty()) {
+ result.AppendErrorWithFormat("%s needs a valid format.\n",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ TypeFormatImplSP entry;
+
+ if (m_command_options.m_custom_type_name.empty())
+ entry = std::make_shared<TypeFormatImpl_Format>(
+ format, TypeFormatImpl::Flags()
+ .SetCascades(m_command_options.m_cascade)
+ .SetSkipPointers(m_command_options.m_skip_pointers)
+ .SetSkipReferences(m_command_options.m_skip_references));
+ else
+ entry = std::make_shared<TypeFormatImpl_EnumType>(
+ ConstString(m_command_options.m_custom_type_name.c_str()),
+ TypeFormatImpl::Flags()
+ .SetCascades(m_command_options.m_cascade)
+ .SetSkipPointers(m_command_options.m_skip_pointers)
+ .SetSkipReferences(m_command_options.m_skip_references));
+
+ // now I have a valid format, let's add it to every type
+
+ TypeCategoryImplSP category_sp;
+ DataVisualization::Categories::GetCategory(
+ ConstString(m_command_options.m_category), category_sp);
+ if (!category_sp)
+ return false;
+
+ WarnOnPotentialUnquotedUnsignedType(command, result);
+
+ for (auto &arg_entry : command.entries()) {
+ if (arg_entry.ref().empty()) {
+ result.AppendError("empty typenames not allowed");
+ return false;
+ }
+
+ ConstString typeCS(arg_entry.ref());
+ if (m_command_options.m_regex) {
+ RegularExpression typeRX(arg_entry.ref());
+ if (!typeRX.IsValid()) {
+ result.AppendError(
+ "regex format error (maybe this is not really a regex?)");
+ return false;
+ }
+ category_sp->GetRegexTypeSummariesContainer()->Delete(typeCS);
+ category_sp->GetRegexTypeFormatsContainer()->Add(std::move(typeRX),
+ entry);
+ } else
+ category_sp->GetTypeFormatsContainer()->Add(std::move(typeCS), entry);
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+ }
+};
+
+#define LLDB_OPTIONS_type_formatter_delete
+#include "CommandOptions.inc"
+
+class CommandObjectTypeFormatterDelete : public CommandObjectParsed {
+protected:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'a':
+ m_delete_all = true;
+ break;
+ case 'w':
+ m_category = std::string(option_arg);
+ break;
+ case 'l':
+ m_language = Language::GetLanguageTypeFromString(option_arg);
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_delete_all = false;
+ m_category = "default";
+ m_language = lldb::eLanguageTypeUnknown;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_type_formatter_delete_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ bool m_delete_all;
+ std::string m_category;
+ lldb::LanguageType m_language;
+ };
+
+ CommandOptions m_options;
+ uint32_t m_formatter_kind_mask;
+
+ Options *GetOptions() override { return &m_options; }
+
+public:
+ CommandObjectTypeFormatterDelete(CommandInterpreter &interpreter,
+ uint32_t formatter_kind_mask,
+ const char *name, const char *help)
+ : CommandObjectParsed(interpreter, name, help, nullptr),
+ m_formatter_kind_mask(formatter_kind_mask) {
+ CommandArgumentEntry type_arg;
+ CommandArgumentData type_style_arg;
+
+ type_style_arg.arg_type = eArgTypeName;
+ type_style_arg.arg_repetition = eArgRepeatPlain;
+
+ type_arg.push_back(type_style_arg);
+
+ m_arguments.push_back(type_arg);
+ }
+
+ ~CommandObjectTypeFormatterDelete() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (request.GetCursorIndex())
+ return;
+
+ DataVisualization::Categories::ForEach(
+ [this, &request](const lldb::TypeCategoryImplSP &category_sp) {
+ if (CHECK_FORMATTER_KIND_MASK(eFormatCategoryItemValue))
+ category_sp->GetTypeFormatsContainer()->AutoComplete(request);
+ if (CHECK_FORMATTER_KIND_MASK(eFormatCategoryItemRegexValue))
+ category_sp->GetRegexTypeFormatsContainer()->AutoComplete(request);
+
+ if (CHECK_FORMATTER_KIND_MASK(eFormatCategoryItemSummary))
+ category_sp->GetTypeSummariesContainer()->AutoComplete(request);
+ if (CHECK_FORMATTER_KIND_MASK(eFormatCategoryItemRegexSummary))
+ category_sp->GetRegexTypeSummariesContainer()->AutoComplete(
+ request);
+
+ if (CHECK_FORMATTER_KIND_MASK(eFormatCategoryItemFilter))
+ category_sp->GetTypeFiltersContainer()->AutoComplete(request);
+ if (CHECK_FORMATTER_KIND_MASK(eFormatCategoryItemRegexFilter))
+ category_sp->GetRegexTypeFiltersContainer()->AutoComplete(request);
+
+ if (CHECK_FORMATTER_KIND_MASK(eFormatCategoryItemSynth))
+ category_sp->GetTypeSyntheticsContainer()->AutoComplete(request);
+ if (CHECK_FORMATTER_KIND_MASK(eFormatCategoryItemRegexSynth))
+ category_sp->GetRegexTypeSyntheticsContainer()->AutoComplete(
+ request);
+ return true;
+ });
+ }
+
+protected:
+ virtual bool FormatterSpecificDeletion(ConstString typeCS) { return false; }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc != 1) {
+ result.AppendErrorWithFormat("%s takes 1 arg.\n", m_cmd_name.c_str());
+ return false;
+ }
+
+ const char *typeA = command.GetArgumentAtIndex(0);
+ ConstString typeCS(typeA);
+
+ if (!typeCS) {
+ result.AppendError("empty typenames not allowed");
+ return false;
+ }
+
+ if (m_options.m_delete_all) {
+ DataVisualization::Categories::ForEach(
+ [this, typeCS](const lldb::TypeCategoryImplSP &category_sp) -> bool {
+ category_sp->Delete(typeCS, m_formatter_kind_mask);
+ return true;
+ });
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+ }
+
+ bool delete_category = false;
+ bool extra_deletion = false;
+
+ if (m_options.m_language != lldb::eLanguageTypeUnknown) {
+ lldb::TypeCategoryImplSP category;
+ DataVisualization::Categories::GetCategory(m_options.m_language,
+ category);
+ if (category)
+ delete_category = category->Delete(typeCS, m_formatter_kind_mask);
+ extra_deletion = FormatterSpecificDeletion(typeCS);
+ } else {
+ lldb::TypeCategoryImplSP category;
+ DataVisualization::Categories::GetCategory(
+ ConstString(m_options.m_category.c_str()), category);
+ if (category)
+ delete_category = category->Delete(typeCS, m_formatter_kind_mask);
+ extra_deletion = FormatterSpecificDeletion(typeCS);
+ }
+
+ if (delete_category || extra_deletion) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+ } else {
+ result.AppendErrorWithFormat("no custom formatter for %s.\n", typeA);
+ return false;
+ }
+ }
+};
+
+#define LLDB_OPTIONS_type_formatter_clear
+#include "CommandOptions.inc"
+
+class CommandObjectTypeFormatterClear : public CommandObjectParsed {
+private:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'a':
+ m_delete_all = true;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_delete_all = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_type_formatter_clear_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ bool m_delete_all;
+ };
+
+ CommandOptions m_options;
+ uint32_t m_formatter_kind_mask;
+
+ Options *GetOptions() override { return &m_options; }
+
+public:
+ CommandObjectTypeFormatterClear(CommandInterpreter &interpreter,
+ uint32_t formatter_kind_mask,
+ const char *name, const char *help)
+ : CommandObjectParsed(interpreter, name, help, nullptr),
+ m_formatter_kind_mask(formatter_kind_mask) {
+ CommandArgumentData category_arg{eArgTypeName, eArgRepeatOptional};
+ m_arguments.push_back({category_arg});
+ }
+
+ ~CommandObjectTypeFormatterClear() override = default;
+
+protected:
+ virtual void FormatterSpecificDeletion() {}
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (m_options.m_delete_all) {
+ DataVisualization::Categories::ForEach(
+ [this](const TypeCategoryImplSP &category_sp) -> bool {
+ category_sp->Clear(m_formatter_kind_mask);
+ return true;
+ });
+ } else {
+ lldb::TypeCategoryImplSP category;
+ if (command.GetArgumentCount() > 0) {
+ const char *cat_name = command.GetArgumentAtIndex(0);
+ ConstString cat_nameCS(cat_name);
+ DataVisualization::Categories::GetCategory(cat_nameCS, category);
+ } else {
+ DataVisualization::Categories::GetCategory(ConstString(nullptr),
+ category);
+ }
+ category->Clear(m_formatter_kind_mask);
+ }
+
+ FormatterSpecificDeletion();
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectTypeFormatDelete
+
+class CommandObjectTypeFormatDelete : public CommandObjectTypeFormatterDelete {
+public:
+ CommandObjectTypeFormatDelete(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterDelete(
+ interpreter,
+ eFormatCategoryItemValue | eFormatCategoryItemRegexValue,
+ "type format delete",
+ "Delete an existing formatting style for a type.") {}
+
+ ~CommandObjectTypeFormatDelete() override = default;
+};
+
+// CommandObjectTypeFormatClear
+
+class CommandObjectTypeFormatClear : public CommandObjectTypeFormatterClear {
+public:
+ CommandObjectTypeFormatClear(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterClear(
+ interpreter,
+ eFormatCategoryItemValue | eFormatCategoryItemRegexValue,
+ "type format clear", "Delete all existing format styles.") {}
+};
+
+#define LLDB_OPTIONS_type_formatter_list
+#include "CommandOptions.inc"
+
+template <typename FormatterType>
+class CommandObjectTypeFormatterList : public CommandObjectParsed {
+ typedef typename FormatterType::SharedPointer FormatterSharedPointer;
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions()
+ : Options(), m_category_regex("", ""),
+ m_category_language(lldb::eLanguageTypeUnknown,
+ lldb::eLanguageTypeUnknown) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+ switch (short_option) {
+ case 'w':
+ m_category_regex.SetCurrentValue(option_arg);
+ m_category_regex.SetOptionWasSet();
+ break;
+ case 'l':
+ error = m_category_language.SetValueFromString(option_arg);
+ if (error.Success())
+ m_category_language.SetOptionWasSet();
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_category_regex.Clear();
+ m_category_language.Clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_type_formatter_list_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ OptionValueString m_category_regex;
+ OptionValueLanguage m_category_language;
+ };
+
+ CommandOptions m_options;
+
+ Options *GetOptions() override { return &m_options; }
+
+public:
+ CommandObjectTypeFormatterList(CommandInterpreter &interpreter,
+ const char *name, const char *help)
+ : CommandObjectParsed(interpreter, name, help, nullptr), m_options() {
+ CommandArgumentEntry type_arg;
+ CommandArgumentData type_style_arg;
+
+ type_style_arg.arg_type = eArgTypeName;
+ type_style_arg.arg_repetition = eArgRepeatOptional;
+
+ type_arg.push_back(type_style_arg);
+
+ m_arguments.push_back(type_arg);
+ }
+
+ ~CommandObjectTypeFormatterList() override = default;
+
+protected:
+ virtual bool FormatterSpecificList(CommandReturnObject &result) {
+ return false;
+ }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+
+ std::unique_ptr<RegularExpression> category_regex;
+ std::unique_ptr<RegularExpression> formatter_regex;
+
+ if (m_options.m_category_regex.OptionWasSet()) {
+ category_regex = std::make_unique<RegularExpression>(
+ m_options.m_category_regex.GetCurrentValueAsRef());
+ if (!category_regex->IsValid()) {
+ result.AppendErrorWithFormat(
+ "syntax error in category regular expression '%s'",
+ m_options.m_category_regex.GetCurrentValueAsRef().str().c_str());
+ return false;
+ }
+ }
+
+ if (argc == 1) {
+ const char *arg = command.GetArgumentAtIndex(0);
+ formatter_regex = std::make_unique<RegularExpression>(arg);
+ if (!formatter_regex->IsValid()) {
+ result.AppendErrorWithFormat("syntax error in regular expression '%s'",
+ arg);
+ return false;
+ }
+ }
+
+ bool any_printed = false;
+
+ auto category_closure =
+ [&result, &formatter_regex,
+ &any_printed](const lldb::TypeCategoryImplSP &category) -> void {
+ result.GetOutputStream().Printf(
+ "-----------------------\nCategory: %s%s\n-----------------------\n",
+ category->GetName(), category->IsEnabled() ? "" : " (disabled)");
+
+ TypeCategoryImpl::ForEachCallbacks<FormatterType> foreach;
+ foreach
+ .SetExact([&result, &formatter_regex, &any_printed](
+ const TypeMatcher &type_matcher,
+ const FormatterSharedPointer &format_sp) -> bool {
+ if (formatter_regex) {
+ bool escape = true;
+ if (type_matcher.CreatedBySameMatchString(
+ ConstString(formatter_regex->GetText()))) {
+ escape = false;
+ } else if (formatter_regex->Execute(
+ type_matcher.GetMatchString().GetStringRef())) {
+ escape = false;
+ }
+
+ if (escape)
+ return true;
+ }
+
+ any_printed = true;
+ result.GetOutputStream().Printf(
+ "%s: %s\n", type_matcher.GetMatchString().GetCString(),
+ format_sp->GetDescription().c_str());
+ return true;
+ });
+
+ foreach
+ .SetWithRegex([&result, &formatter_regex, &any_printed](
+ const TypeMatcher &type_matcher,
+ const FormatterSharedPointer &format_sp) -> bool {
+ if (formatter_regex) {
+ bool escape = true;
+ if (type_matcher.CreatedBySameMatchString(
+ ConstString(formatter_regex->GetText()))) {
+ escape = false;
+ } else if (formatter_regex->Execute(
+ type_matcher.GetMatchString().GetStringRef())) {
+ escape = false;
+ }
+
+ if (escape)
+ return true;
+ }
+
+ any_printed = true;
+ result.GetOutputStream().Printf(
+ "%s: %s\n", type_matcher.GetMatchString().GetCString(),
+ format_sp->GetDescription().c_str());
+ return true;
+ });
+
+ category->ForEach(foreach);
+ };
+
+ if (m_options.m_category_language.OptionWasSet()) {
+ lldb::TypeCategoryImplSP category_sp;
+ DataVisualization::Categories::GetCategory(
+ m_options.m_category_language.GetCurrentValue(), category_sp);
+ if (category_sp)
+ category_closure(category_sp);
+ } else {
+ DataVisualization::Categories::ForEach(
+ [&category_regex, &category_closure](
+ const lldb::TypeCategoryImplSP &category) -> bool {
+ if (category_regex) {
+ bool escape = true;
+ if (category->GetName() == category_regex->GetText()) {
+ escape = false;
+ } else if (category_regex->Execute(category->GetName())) {
+ escape = false;
+ }
+
+ if (escape)
+ return true;
+ }
+
+ category_closure(category);
+
+ return true;
+ });
+
+ any_printed = FormatterSpecificList(result) | any_printed;
+ }
+
+ if (any_printed)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else {
+ result.GetOutputStream().PutCString("no matching results found.\n");
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectTypeFormatList
+
+class CommandObjectTypeFormatList
+ : public CommandObjectTypeFormatterList<TypeFormatImpl> {
+public:
+ CommandObjectTypeFormatList(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterList(interpreter, "type format list",
+ "Show a list of current formats.") {}
+};
+
+Status CommandObjectTypeSummaryAdd::CommandOptions::SetOptionValue(
+ uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+ bool success;
+
+ switch (short_option) {
+ case 'C':
+ m_flags.SetCascades(OptionArgParser::ToBoolean(option_arg, true, &success));
+ if (!success)
+ error.SetErrorStringWithFormat("invalid value for cascade: %s",
+ option_arg.str().c_str());
+ break;
+ case 'e':
+ m_flags.SetDontShowChildren(false);
+ break;
+ case 'h':
+ m_flags.SetHideEmptyAggregates(true);
+ break;
+ case 'v':
+ m_flags.SetDontShowValue(true);
+ break;
+ case 'c':
+ m_flags.SetShowMembersOneLiner(true);
+ break;
+ case 's':
+ m_format_string = std::string(option_arg);
+ break;
+ case 'p':
+ m_flags.SetSkipPointers(true);
+ break;
+ case 'r':
+ m_flags.SetSkipReferences(true);
+ break;
+ case 'x':
+ m_regex = true;
+ break;
+ case 'n':
+ m_name.SetString(option_arg);
+ break;
+ case 'o':
+ m_python_script = std::string(option_arg);
+ m_is_add_script = true;
+ break;
+ case 'F':
+ m_python_function = std::string(option_arg);
+ m_is_add_script = true;
+ break;
+ case 'P':
+ m_is_add_script = true;
+ break;
+ case 'w':
+ m_category = std::string(option_arg);
+ break;
+ case 'O':
+ m_flags.SetHideItemNames(true);
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+}
+
+void CommandObjectTypeSummaryAdd::CommandOptions::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ m_flags.Clear().SetCascades().SetDontShowChildren().SetDontShowValue(false);
+ m_flags.SetShowMembersOneLiner(false)
+ .SetSkipPointers(false)
+ .SetSkipReferences(false)
+ .SetHideItemNames(false);
+
+ m_regex = false;
+ m_name.Clear();
+ m_python_script = "";
+ m_python_function = "";
+ m_format_string = "";
+ m_is_add_script = false;
+ m_category = "default";
+}
+
+#if LLDB_ENABLE_PYTHON
+
+bool CommandObjectTypeSummaryAdd::Execute_ScriptSummary(
+ Args &command, CommandReturnObject &result) {
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc < 1 && !m_options.m_name) {
+ result.AppendErrorWithFormat("%s takes one or more args.\n",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ TypeSummaryImplSP script_format;
+
+ if (!m_options.m_python_function
+ .empty()) // we have a Python function ready to use
+ {
+ const char *funct_name = m_options.m_python_function.c_str();
+ if (!funct_name || !funct_name[0]) {
+ result.AppendError("function name empty.\n");
+ return false;
+ }
+
+ std::string code =
+ (" " + m_options.m_python_function + "(valobj,internal_dict)");
+
+ script_format = std::make_shared<ScriptSummaryFormat>(
+ m_options.m_flags, funct_name, code.c_str());
+
+ ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
+
+ if (interpreter && !interpreter->CheckObjectExists(funct_name))
+ result.AppendWarningWithFormat(
+ "The provided function \"%s\" does not exist - "
+ "please define it before attempting to use this summary.\n",
+ funct_name);
+ } else if (!m_options.m_python_script
+ .empty()) // we have a quick 1-line script, just use it
+ {
+ ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
+ if (!interpreter) {
+ result.AppendError("script interpreter missing - unable to generate "
+ "function wrapper.\n");
+ return false;
+ }
+ StringList funct_sl;
+ funct_sl << m_options.m_python_script.c_str();
+ std::string funct_name_str;
+ if (!interpreter->GenerateTypeScriptFunction(funct_sl, funct_name_str)) {
+ result.AppendError("unable to generate function wrapper.\n");
+ return false;
+ }
+ if (funct_name_str.empty()) {
+ result.AppendError(
+ "script interpreter failed to generate a valid function name.\n");
+ return false;
+ }
+
+ std::string code = " " + m_options.m_python_script;
+
+ script_format = std::make_shared<ScriptSummaryFormat>(
+ m_options.m_flags, funct_name_str.c_str(), code.c_str());
+ } else {
+ // Use an IOHandler to grab Python code from the user
+ auto options = std::make_unique<ScriptAddOptions>(
+ m_options.m_flags, m_options.m_regex, m_options.m_name,
+ m_options.m_category);
+
+ for (auto &entry : command.entries()) {
+ if (entry.ref().empty()) {
+ result.AppendError("empty typenames not allowed");
+ return false;
+ }
+
+ options->m_target_types << std::string(entry.ref());
+ }
+
+ m_interpreter.GetPythonCommandsFromIOHandler(
+ " ", // Prompt
+ *this, // IOHandlerDelegate
+ options.release()); // Baton for the "io_handler" that will be passed
+ // back into our IOHandlerDelegate functions
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+
+ return result.Succeeded();
+ }
+
+ // if I am here, script_format must point to something good, so I can add
+ // that as a script summary to all interested parties
+
+ Status error;
+
+ for (auto &entry : command.entries()) {
+ CommandObjectTypeSummaryAdd::AddSummary(
+ ConstString(entry.ref()), script_format,
+ (m_options.m_regex ? eRegexSummary : eRegularSummary),
+ m_options.m_category, &error);
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ return false;
+ }
+ }
+
+ if (m_options.m_name) {
+ AddSummary(m_options.m_name, script_format, eNamedSummary,
+ m_options.m_category, &error);
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ result.AppendError("added to types, but not given a name");
+ return false;
+ }
+ }
+
+ return result.Succeeded();
+}
+
+#endif
+
+bool CommandObjectTypeSummaryAdd::Execute_StringSummary(
+ Args &command, CommandReturnObject &result) {
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc < 1 && !m_options.m_name) {
+ result.AppendErrorWithFormat("%s takes one or more args.\n",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ if (!m_options.m_flags.GetShowMembersOneLiner() &&
+ m_options.m_format_string.empty()) {
+ result.AppendError("empty summary strings not allowed");
+ return false;
+ }
+
+ const char *format_cstr = (m_options.m_flags.GetShowMembersOneLiner()
+ ? ""
+ : m_options.m_format_string.c_str());
+
+ // ${var%S} is an endless recursion, prevent it
+ if (strcmp(format_cstr, "${var%S}") == 0) {
+ result.AppendError("recursive summary not allowed");
+ return false;
+ }
+
+ std::unique_ptr<StringSummaryFormat> string_format(
+ new StringSummaryFormat(m_options.m_flags, format_cstr));
+ if (!string_format) {
+ result.AppendError("summary creation failed");
+ return false;
+ }
+ if (string_format->m_error.Fail()) {
+ result.AppendErrorWithFormat("syntax error: %s",
+ string_format->m_error.AsCString("<unknown>"));
+ return false;
+ }
+ lldb::TypeSummaryImplSP entry(string_format.release());
+
+ // now I have a valid format, let's add it to every type
+ Status error;
+ for (auto &arg_entry : command.entries()) {
+ if (arg_entry.ref().empty()) {
+ result.AppendError("empty typenames not allowed");
+ return false;
+ }
+ ConstString typeCS(arg_entry.ref());
+
+ AddSummary(typeCS, entry,
+ (m_options.m_regex ? eRegexSummary : eRegularSummary),
+ m_options.m_category, &error);
+
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ return false;
+ }
+ }
+
+ if (m_options.m_name) {
+ AddSummary(m_options.m_name, entry, eNamedSummary, m_options.m_category,
+ &error);
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ result.AppendError("added to types, but not given a name");
+ return false;
+ }
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+}
+
+CommandObjectTypeSummaryAdd::CommandObjectTypeSummaryAdd(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "type summary add",
+ "Add a new summary style for a type.", nullptr),
+ IOHandlerDelegateMultiline("DONE"), m_options(interpreter) {
+ CommandArgumentEntry type_arg;
+ CommandArgumentData type_style_arg;
+
+ type_style_arg.arg_type = eArgTypeName;
+ type_style_arg.arg_repetition = eArgRepeatPlus;
+
+ type_arg.push_back(type_style_arg);
+
+ m_arguments.push_back(type_arg);
+
+ SetHelpLong(
+ R"(
+The following examples of 'type summary add' refer to this code snippet for context:
+
+ struct JustADemo
+ {
+ int* ptr;
+ float value;
+ JustADemo(int p = 1, float v = 0.1) : ptr(new int(p)), value(v) {}
+ };
+ JustADemo demo_instance(42, 3.14);
+
+ typedef JustADemo NewDemo;
+ NewDemo new_demo_instance(42, 3.14);
+
+(lldb) type summary add --summary-string "the answer is ${*var.ptr}" JustADemo
+
+ Subsequently displaying demo_instance with 'frame variable' or 'expression' will display "the answer is 42"
+
+(lldb) type summary add --summary-string "the answer is ${*var.ptr}, and the question is ${var.value}" JustADemo
+
+ Subsequently displaying demo_instance with 'frame variable' or 'expression' will display "the answer is 42 and the question is 3.14"
+
+)"
+ "Alternatively, you could define formatting for all pointers to integers and \
+rely on that when formatting JustADemo to obtain the same result:"
+ R"(
+
+(lldb) type summary add --summary-string "${var%V} -> ${*var}" "int *"
+(lldb) type summary add --summary-string "the answer is ${var.ptr}, and the question is ${var.value}" JustADemo
+
+)"
+ "Type summaries are automatically applied to derived typedefs, so the examples \
+above apply to both JustADemo and NewDemo. The cascade option can be used to \
+suppress this behavior:"
+ R"(
+
+(lldb) type summary add --summary-string "${var.ptr}, ${var.value},{${var.byte}}" JustADemo -C no
+
+ The summary will now be used for values of JustADemo but not NewDemo.
+
+)"
+ "By default summaries are shown for pointers and references to values of the \
+specified type. To suppress formatting for pointers use the -p option, or apply \
+the corresponding -r option to suppress formatting for references:"
+ R"(
+
+(lldb) type summary add -p -r --summary-string "${var.ptr}, ${var.value},{${var.byte}}" JustADemo
+
+)"
+ "One-line summaries including all fields in a type can be inferred without supplying an \
+explicit summary string by passing the -c option:"
+ R"(
+
+(lldb) type summary add -c JustADemo
+(lldb) frame variable demo_instance
+(ptr=<address>, value=3.14)
+
+)"
+ "Type summaries normally suppress the nested display of individual fields. To \
+supply a summary to supplement the default structure add the -e option:"
+ R"(
+
+(lldb) type summary add -e --summary-string "*ptr = ${*var.ptr}" JustADemo
+
+)"
+ "Now when displaying JustADemo values the int* is displayed, followed by the \
+standard LLDB sequence of children, one per line:"
+ R"(
+
+*ptr = 42 {
+ ptr = <address>
+ value = 3.14
+}
+
+)"
+ "You can also add summaries written in Python. These scripts use lldb public API to \
+gather information from your variables and produce a meaningful summary. To start a \
+multi-line script use the -P option. The function declaration will be displayed along with \
+a comment describing the two arguments. End your script with the word 'DONE' on a line by \
+itself:"
+ R"(
+
+(lldb) type summary add JustADemo -P
+def function (valobj,internal_dict):
+"""valobj: an SBValue which you want to provide a summary for
+internal_dict: an LLDB support object not to be used"""
+ value = valobj.GetChildMemberWithName('value');
+ return 'My value is ' + value.GetValue();
+ DONE
+
+Alternatively, the -o option can be used when providing a simple one-line Python script:
+
+(lldb) type summary add JustADemo -o "value = valobj.GetChildMemberWithName('value'); return 'My value is ' + value.GetValue();")");
+}
+
+bool CommandObjectTypeSummaryAdd::DoExecute(Args &command,
+ CommandReturnObject &result) {
+ WarnOnPotentialUnquotedUnsignedType(command, result);
+
+ if (m_options.m_is_add_script) {
+#if LLDB_ENABLE_PYTHON
+ return Execute_ScriptSummary(command, result);
+#else
+ result.AppendError("python is disabled");
+ return false;
+#endif
+ }
+
+ return Execute_StringSummary(command, result);
+}
+
+static bool FixArrayTypeNameWithRegex(ConstString &type_name) {
+ llvm::StringRef type_name_ref(type_name.GetStringRef());
+
+ if (type_name_ref.endswith("[]")) {
+ std::string type_name_str(type_name.GetCString());
+ type_name_str.resize(type_name_str.length() - 2);
+ if (type_name_str.back() != ' ')
+ type_name_str.append(" ?\\[[0-9]+\\]");
+ else
+ type_name_str.append("\\[[0-9]+\\]");
+ type_name.SetCString(type_name_str.c_str());
+ return true;
+ }
+ return false;
+}
+
+bool CommandObjectTypeSummaryAdd::AddSummary(ConstString type_name,
+ TypeSummaryImplSP entry,
+ SummaryFormatType type,
+ std::string category_name,
+ Status *error) {
+ lldb::TypeCategoryImplSP category;
+ DataVisualization::Categories::GetCategory(ConstString(category_name.c_str()),
+ category);
+
+ if (type == eRegularSummary) {
+ if (FixArrayTypeNameWithRegex(type_name))
+ type = eRegexSummary;
+ }
+
+ if (type == eRegexSummary) {
+ RegularExpression typeRX(type_name.GetStringRef());
+ if (!typeRX.IsValid()) {
+ if (error)
+ error->SetErrorString(
+ "regex format error (maybe this is not really a regex?)");
+ return false;
+ }
+
+ category->GetRegexTypeSummariesContainer()->Delete(type_name);
+ category->GetRegexTypeSummariesContainer()->Add(std::move(typeRX), entry);
+
+ return true;
+ } else if (type == eNamedSummary) {
+ // system named summaries do not exist (yet?)
+ DataVisualization::NamedSummaryFormats::Add(type_name, entry);
+ return true;
+ } else {
+ category->GetTypeSummariesContainer()->Add(std::move(type_name), entry);
+ return true;
+ }
+}
+
+// CommandObjectTypeSummaryDelete
+
+class CommandObjectTypeSummaryDelete : public CommandObjectTypeFormatterDelete {
+public:
+ CommandObjectTypeSummaryDelete(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterDelete(
+ interpreter,
+ eFormatCategoryItemSummary | eFormatCategoryItemRegexSummary,
+ "type summary delete", "Delete an existing summary for a type.") {}
+
+ ~CommandObjectTypeSummaryDelete() override = default;
+
+protected:
+ bool FormatterSpecificDeletion(ConstString typeCS) override {
+ if (m_options.m_language != lldb::eLanguageTypeUnknown)
+ return false;
+ return DataVisualization::NamedSummaryFormats::Delete(typeCS);
+ }
+};
+
+class CommandObjectTypeSummaryClear : public CommandObjectTypeFormatterClear {
+public:
+ CommandObjectTypeSummaryClear(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterClear(
+ interpreter,
+ eFormatCategoryItemSummary | eFormatCategoryItemRegexSummary,
+ "type summary clear", "Delete all existing summaries.") {}
+
+protected:
+ void FormatterSpecificDeletion() override {
+ DataVisualization::NamedSummaryFormats::Clear();
+ }
+};
+
+// CommandObjectTypeSummaryList
+
+class CommandObjectTypeSummaryList
+ : public CommandObjectTypeFormatterList<TypeSummaryImpl> {
+public:
+ CommandObjectTypeSummaryList(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterList(interpreter, "type summary list",
+ "Show a list of current summaries.") {}
+
+protected:
+ bool FormatterSpecificList(CommandReturnObject &result) override {
+ if (DataVisualization::NamedSummaryFormats::GetCount() > 0) {
+ result.GetOutputStream().Printf("Named summaries:\n");
+ DataVisualization::NamedSummaryFormats::ForEach(
+ [&result](const TypeMatcher &type_matcher,
+ const TypeSummaryImplSP &summary_sp) -> bool {
+ result.GetOutputStream().Printf(
+ "%s: %s\n", type_matcher.GetMatchString().GetCString(),
+ summary_sp->GetDescription().c_str());
+ return true;
+ });
+ return true;
+ }
+ return false;
+ }
+};
+
+// CommandObjectTypeCategoryDefine
+#define LLDB_OPTIONS_type_category_define
+#include "CommandOptions.inc"
+
+class CommandObjectTypeCategoryDefine : public CommandObjectParsed {
+ class CommandOptions : public Options {
+ public:
+ CommandOptions()
+ : m_define_enabled(false, false),
+ m_cate_language(eLanguageTypeUnknown, eLanguageTypeUnknown) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'e':
+ m_define_enabled.SetValueFromString(llvm::StringRef("true"));
+ break;
+ case 'l':
+ error = m_cate_language.SetValueFromString(option_arg);
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_define_enabled.Clear();
+ m_cate_language.Clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_type_category_define_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ OptionValueBoolean m_define_enabled;
+ OptionValueLanguage m_cate_language;
+ };
+
+ CommandOptions m_options;
+
+ Options *GetOptions() override { return &m_options; }
+
+public:
+ CommandObjectTypeCategoryDefine(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "type category define",
+ "Define a new category as a source of formatters.",
+ nullptr) {
+ CommandArgumentEntry type_arg;
+ CommandArgumentData type_style_arg;
+
+ type_style_arg.arg_type = eArgTypeName;
+ type_style_arg.arg_repetition = eArgRepeatPlus;
+
+ type_arg.push_back(type_style_arg);
+
+ m_arguments.push_back(type_arg);
+ }
+
+ ~CommandObjectTypeCategoryDefine() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(),
+ CommandCompletions::eTypeCategoryNameCompletion, request, nullptr);
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc < 1) {
+ result.AppendErrorWithFormat("%s takes 1 or more args.\n",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ for (auto &entry : command.entries()) {
+ TypeCategoryImplSP category_sp;
+ if (DataVisualization::Categories::GetCategory(ConstString(entry.ref()),
+ category_sp) &&
+ category_sp) {
+ category_sp->AddLanguage(m_options.m_cate_language.GetCurrentValue());
+ if (m_options.m_define_enabled.GetCurrentValue())
+ DataVisualization::Categories::Enable(category_sp,
+ TypeCategoryMap::Default);
+ }
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectTypeCategoryEnable
+#define LLDB_OPTIONS_type_category_enable
+#include "CommandOptions.inc"
+
+class CommandObjectTypeCategoryEnable : public CommandObjectParsed {
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'l':
+ if (!option_arg.empty()) {
+ m_language = Language::GetLanguageTypeFromString(option_arg);
+ if (m_language == lldb::eLanguageTypeUnknown)
+ error.SetErrorStringWithFormat("unrecognized language '%s'",
+ option_arg.str().c_str());
+ }
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_language = lldb::eLanguageTypeUnknown;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_type_category_enable_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ lldb::LanguageType m_language;
+ };
+
+ CommandOptions m_options;
+
+ Options *GetOptions() override { return &m_options; }
+
+public:
+ CommandObjectTypeCategoryEnable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "type category enable",
+ "Enable a category as a source of formatters.",
+ nullptr) {
+ CommandArgumentEntry type_arg;
+ CommandArgumentData type_style_arg;
+
+ type_style_arg.arg_type = eArgTypeName;
+ type_style_arg.arg_repetition = eArgRepeatPlus;
+
+ type_arg.push_back(type_style_arg);
+
+ m_arguments.push_back(type_arg);
+ }
+
+ ~CommandObjectTypeCategoryEnable() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(),
+ CommandCompletions::eTypeCategoryNameCompletion, request, nullptr);
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc < 1 && m_options.m_language == lldb::eLanguageTypeUnknown) {
+ result.AppendErrorWithFormat("%s takes arguments and/or a language",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ if (argc == 1 && strcmp(command.GetArgumentAtIndex(0), "*") == 0) {
+ DataVisualization::Categories::EnableStar();
+ } else if (argc > 0) {
+ for (int i = argc - 1; i >= 0; i--) {
+ const char *typeA = command.GetArgumentAtIndex(i);
+ ConstString typeCS(typeA);
+
+ if (!typeCS) {
+ result.AppendError("empty category name not allowed");
+ return false;
+ }
+ DataVisualization::Categories::Enable(typeCS);
+ lldb::TypeCategoryImplSP cate;
+ if (DataVisualization::Categories::GetCategory(typeCS, cate) && cate) {
+ if (cate->GetCount() == 0) {
+ result.AppendWarning("empty category enabled (typo?)");
+ }
+ }
+ }
+ }
+
+ if (m_options.m_language != lldb::eLanguageTypeUnknown)
+ DataVisualization::Categories::Enable(m_options.m_language);
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectTypeCategoryDelete
+
+class CommandObjectTypeCategoryDelete : public CommandObjectParsed {
+public:
+ CommandObjectTypeCategoryDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "type category delete",
+ "Delete a category and all associated formatters.",
+ nullptr) {
+ CommandArgumentEntry type_arg;
+ CommandArgumentData type_style_arg;
+
+ type_style_arg.arg_type = eArgTypeName;
+ type_style_arg.arg_repetition = eArgRepeatPlus;
+
+ type_arg.push_back(type_style_arg);
+
+ m_arguments.push_back(type_arg);
+ }
+
+ ~CommandObjectTypeCategoryDelete() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(),
+ CommandCompletions::eTypeCategoryNameCompletion, request, nullptr);
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc < 1) {
+ result.AppendErrorWithFormat("%s takes 1 or more arg.\n",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ bool success = true;
+
+ // the order is not relevant here
+ for (int i = argc - 1; i >= 0; i--) {
+ const char *typeA = command.GetArgumentAtIndex(i);
+ ConstString typeCS(typeA);
+
+ if (!typeCS) {
+ result.AppendError("empty category name not allowed");
+ return false;
+ }
+ if (!DataVisualization::Categories::Delete(typeCS))
+ success = false; // keep deleting even if we hit an error
+ }
+ if (success) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ } else {
+ result.AppendError("cannot delete one or more categories\n");
+ return false;
+ }
+ }
+};
+
+// CommandObjectTypeCategoryDisable
+#define LLDB_OPTIONS_type_category_disable
+#include "CommandOptions.inc"
+
+class CommandObjectTypeCategoryDisable : public CommandObjectParsed {
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'l':
+ if (!option_arg.empty()) {
+ m_language = Language::GetLanguageTypeFromString(option_arg);
+ if (m_language == lldb::eLanguageTypeUnknown)
+ error.SetErrorStringWithFormat("unrecognized language '%s'",
+ option_arg.str().c_str());
+ }
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_language = lldb::eLanguageTypeUnknown;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_type_category_disable_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ lldb::LanguageType m_language;
+ };
+
+ CommandOptions m_options;
+
+ Options *GetOptions() override { return &m_options; }
+
+public:
+ CommandObjectTypeCategoryDisable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "type category disable",
+ "Disable a category as a source of formatters.",
+ nullptr) {
+ CommandArgumentEntry type_arg;
+ CommandArgumentData type_style_arg;
+
+ type_style_arg.arg_type = eArgTypeName;
+ type_style_arg.arg_repetition = eArgRepeatPlus;
+
+ type_arg.push_back(type_style_arg);
+
+ m_arguments.push_back(type_arg);
+ }
+
+ ~CommandObjectTypeCategoryDisable() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(),
+ CommandCompletions::eTypeCategoryNameCompletion, request, nullptr);
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc < 1 && m_options.m_language == lldb::eLanguageTypeUnknown) {
+ result.AppendErrorWithFormat("%s takes arguments and/or a language",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ if (argc == 1 && strcmp(command.GetArgumentAtIndex(0), "*") == 0) {
+ DataVisualization::Categories::DisableStar();
+ } else if (argc > 0) {
+ // the order is not relevant here
+ for (int i = argc - 1; i >= 0; i--) {
+ const char *typeA = command.GetArgumentAtIndex(i);
+ ConstString typeCS(typeA);
+
+ if (!typeCS) {
+ result.AppendError("empty category name not allowed");
+ return false;
+ }
+ DataVisualization::Categories::Disable(typeCS);
+ }
+ }
+
+ if (m_options.m_language != lldb::eLanguageTypeUnknown)
+ DataVisualization::Categories::Disable(m_options.m_language);
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectTypeCategoryList
+
+class CommandObjectTypeCategoryList : public CommandObjectParsed {
+public:
+ CommandObjectTypeCategoryList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "type category list",
+ "Provide a list of all existing categories.",
+ nullptr) {
+ CommandArgumentEntry type_arg;
+ CommandArgumentData type_style_arg;
+
+ type_style_arg.arg_type = eArgTypeName;
+ type_style_arg.arg_repetition = eArgRepeatOptional;
+
+ type_arg.push_back(type_style_arg);
+
+ m_arguments.push_back(type_arg);
+ }
+
+ ~CommandObjectTypeCategoryList() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (request.GetCursorIndex())
+ return;
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(),
+ CommandCompletions::eTypeCategoryNameCompletion, request, nullptr);
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+
+ std::unique_ptr<RegularExpression> regex;
+
+ if (argc == 1) {
+ const char *arg = command.GetArgumentAtIndex(0);
+ regex = std::make_unique<RegularExpression>(arg);
+ if (!regex->IsValid()) {
+ result.AppendErrorWithFormat(
+ "syntax error in category regular expression '%s'", arg);
+ return false;
+ }
+ } else if (argc != 0) {
+ result.AppendErrorWithFormat("%s takes 0 or one arg.\n",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ DataVisualization::Categories::ForEach(
+ [&regex, &result](const lldb::TypeCategoryImplSP &category_sp) -> bool {
+ if (regex) {
+ bool escape = true;
+ if (regex->GetText() == category_sp->GetName()) {
+ escape = false;
+ } else if (regex->Execute(category_sp->GetName())) {
+ escape = false;
+ }
+
+ if (escape)
+ return true;
+ }
+
+ result.GetOutputStream().Printf(
+ "Category: %s\n", category_sp->GetDescription().c_str());
+
+ return true;
+ });
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectTypeFilterList
+
+class CommandObjectTypeFilterList
+ : public CommandObjectTypeFormatterList<TypeFilterImpl> {
+public:
+ CommandObjectTypeFilterList(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterList(interpreter, "type filter list",
+ "Show a list of current filters.") {}
+};
+
+#if LLDB_ENABLE_PYTHON
+
+// CommandObjectTypeSynthList
+
+class CommandObjectTypeSynthList
+ : public CommandObjectTypeFormatterList<SyntheticChildren> {
+public:
+ CommandObjectTypeSynthList(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterList(
+ interpreter, "type synthetic list",
+ "Show a list of current synthetic providers.") {}
+};
+
+#endif
+
+// CommandObjectTypeFilterDelete
+
+class CommandObjectTypeFilterDelete : public CommandObjectTypeFormatterDelete {
+public:
+ CommandObjectTypeFilterDelete(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterDelete(
+ interpreter,
+ eFormatCategoryItemFilter | eFormatCategoryItemRegexFilter,
+ "type filter delete", "Delete an existing filter for a type.") {}
+
+ ~CommandObjectTypeFilterDelete() override = default;
+};
+
+#if LLDB_ENABLE_PYTHON
+
+// CommandObjectTypeSynthDelete
+
+class CommandObjectTypeSynthDelete : public CommandObjectTypeFormatterDelete {
+public:
+ CommandObjectTypeSynthDelete(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterDelete(
+ interpreter,
+ eFormatCategoryItemSynth | eFormatCategoryItemRegexSynth,
+ "type synthetic delete",
+ "Delete an existing synthetic provider for a type.") {}
+
+ ~CommandObjectTypeSynthDelete() override = default;
+};
+
+#endif
+
+// CommandObjectTypeFilterClear
+
+class CommandObjectTypeFilterClear : public CommandObjectTypeFormatterClear {
+public:
+ CommandObjectTypeFilterClear(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterClear(
+ interpreter,
+ eFormatCategoryItemFilter | eFormatCategoryItemRegexFilter,
+ "type filter clear", "Delete all existing filter.") {}
+};
+
+#if LLDB_ENABLE_PYTHON
+// CommandObjectTypeSynthClear
+
+class CommandObjectTypeSynthClear : public CommandObjectTypeFormatterClear {
+public:
+ CommandObjectTypeSynthClear(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterClear(
+ interpreter,
+ eFormatCategoryItemSynth | eFormatCategoryItemRegexSynth,
+ "type synthetic clear",
+ "Delete all existing synthetic providers.") {}
+};
+
+bool CommandObjectTypeSynthAdd::Execute_HandwritePython(
+ Args &command, CommandReturnObject &result) {
+ auto options = std::make_unique<SynthAddOptions>(
+ m_options.m_skip_pointers, m_options.m_skip_references,
+ m_options.m_cascade, m_options.m_regex, m_options.m_category);
+
+ for (auto &entry : command.entries()) {
+ if (entry.ref().empty()) {
+ result.AppendError("empty typenames not allowed");
+ return false;
+ }
+
+ options->m_target_types << std::string(entry.ref());
+ }
+
+ m_interpreter.GetPythonCommandsFromIOHandler(
+ " ", // Prompt
+ *this, // IOHandlerDelegate
+ options.release()); // Baton for the "io_handler" that will be passed back
+ // into our IOHandlerDelegate functions
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+}
+
+bool CommandObjectTypeSynthAdd::Execute_PythonClass(
+ Args &command, CommandReturnObject &result) {
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc < 1) {
+ result.AppendErrorWithFormat("%s takes one or more args.\n",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ if (m_options.m_class_name.empty() && !m_options.m_input_python) {
+ result.AppendErrorWithFormat("%s needs either a Python class name or -P to "
+ "directly input Python code.\n",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ SyntheticChildrenSP entry;
+
+ ScriptedSyntheticChildren *impl = new ScriptedSyntheticChildren(
+ SyntheticChildren::Flags()
+ .SetCascades(m_options.m_cascade)
+ .SetSkipPointers(m_options.m_skip_pointers)
+ .SetSkipReferences(m_options.m_skip_references),
+ m_options.m_class_name.c_str());
+
+ entry.reset(impl);
+
+ ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
+
+ if (interpreter &&
+ !interpreter->CheckObjectExists(impl->GetPythonClassName()))
+ result.AppendWarning("The provided class does not exist - please define it "
+ "before attempting to use this synthetic provider");
+
+ // now I have a valid provider, let's add it to every type
+
+ lldb::TypeCategoryImplSP category;
+ DataVisualization::Categories::GetCategory(
+ ConstString(m_options.m_category.c_str()), category);
+
+ Status error;
+
+ for (auto &arg_entry : command.entries()) {
+ if (arg_entry.ref().empty()) {
+ result.AppendError("empty typenames not allowed");
+ return false;
+ }
+
+ ConstString typeCS(arg_entry.ref());
+ if (!AddSynth(typeCS, entry,
+ m_options.m_regex ? eRegexSynth : eRegularSynth,
+ m_options.m_category, &error)) {
+ result.AppendError(error.AsCString());
+ return false;
+ }
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+}
+
+CommandObjectTypeSynthAdd::CommandObjectTypeSynthAdd(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "type synthetic add",
+ "Add a new synthetic provider for a type.", nullptr),
+ IOHandlerDelegateMultiline("DONE"), m_options() {
+ CommandArgumentEntry type_arg;
+ CommandArgumentData type_style_arg;
+
+ type_style_arg.arg_type = eArgTypeName;
+ type_style_arg.arg_repetition = eArgRepeatPlus;
+
+ type_arg.push_back(type_style_arg);
+
+ m_arguments.push_back(type_arg);
+}
+
+bool CommandObjectTypeSynthAdd::AddSynth(ConstString type_name,
+ SyntheticChildrenSP entry,
+ SynthFormatType type,
+ std::string category_name,
+ Status *error) {
+ lldb::TypeCategoryImplSP category;
+ DataVisualization::Categories::GetCategory(ConstString(category_name.c_str()),
+ category);
+
+ if (type == eRegularSynth) {
+ if (FixArrayTypeNameWithRegex(type_name))
+ type = eRegexSynth;
+ }
+
+ if (category->AnyMatches(
+ type_name, eFormatCategoryItemFilter | eFormatCategoryItemRegexFilter,
+ false)) {
+ if (error)
+ error->SetErrorStringWithFormat("cannot add synthetic for type %s when "
+ "filter is defined in same category!",
+ type_name.AsCString());
+ return false;
+ }
+
+ if (type == eRegexSynth) {
+ RegularExpression typeRX(type_name.GetStringRef());
+ if (!typeRX.IsValid()) {
+ if (error)
+ error->SetErrorString(
+ "regex format error (maybe this is not really a regex?)");
+ return false;
+ }
+
+ category->GetRegexTypeSyntheticsContainer()->Delete(type_name);
+ category->GetRegexTypeSyntheticsContainer()->Add(std::move(typeRX), entry);
+
+ return true;
+ } else {
+ category->GetTypeSyntheticsContainer()->Add(std::move(type_name), entry);
+ return true;
+ }
+}
+
+#endif
+#define LLDB_OPTIONS_type_filter_add
+#include "CommandOptions.inc"
+
+class CommandObjectTypeFilterAdd : public CommandObjectParsed {
+private:
+ class CommandOptions : public Options {
+ typedef std::vector<std::string> option_vector;
+
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+ bool success;
+
+ switch (short_option) {
+ case 'C':
+ m_cascade = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (!success)
+ error.SetErrorStringWithFormat("invalid value for cascade: %s",
+ option_arg.str().c_str());
+ break;
+ case 'c':
+ m_expr_paths.push_back(std::string(option_arg));
+ has_child_list = true;
+ break;
+ case 'p':
+ m_skip_pointers = true;
+ break;
+ case 'r':
+ m_skip_references = true;
+ break;
+ case 'w':
+ m_category = std::string(option_arg);
+ break;
+ case 'x':
+ m_regex = true;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_cascade = true;
+ m_skip_pointers = false;
+ m_skip_references = false;
+ m_category = "default";
+ m_expr_paths.clear();
+ has_child_list = false;
+ m_regex = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_type_filter_add_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ bool m_cascade;
+ bool m_skip_references;
+ bool m_skip_pointers;
+ bool m_input_python;
+ option_vector m_expr_paths;
+ std::string m_category;
+ bool has_child_list;
+ bool m_regex;
+
+ typedef option_vector::iterator ExpressionPathsIterator;
+ };
+
+ CommandOptions m_options;
+
+ Options *GetOptions() override { return &m_options; }
+
+ enum FilterFormatType { eRegularFilter, eRegexFilter };
+
+ bool AddFilter(ConstString type_name, TypeFilterImplSP entry,
+ FilterFormatType type, std::string category_name,
+ Status *error) {
+ lldb::TypeCategoryImplSP category;
+ DataVisualization::Categories::GetCategory(
+ ConstString(category_name.c_str()), category);
+
+ if (type == eRegularFilter) {
+ if (FixArrayTypeNameWithRegex(type_name))
+ type = eRegexFilter;
+ }
+
+ if (category->AnyMatches(
+ type_name, eFormatCategoryItemSynth | eFormatCategoryItemRegexSynth,
+ false)) {
+ if (error)
+ error->SetErrorStringWithFormat("cannot add filter for type %s when "
+ "synthetic is defined in same "
+ "category!",
+ type_name.AsCString());
+ return false;
+ }
+
+ if (type == eRegexFilter) {
+ RegularExpression typeRX(type_name.GetStringRef());
+ if (!typeRX.IsValid()) {
+ if (error)
+ error->SetErrorString(
+ "regex format error (maybe this is not really a regex?)");
+ return false;
+ }
+
+ category->GetRegexTypeFiltersContainer()->Delete(type_name);
+ category->GetRegexTypeFiltersContainer()->Add(std::move(typeRX), entry);
+
+ return true;
+ } else {
+ category->GetTypeFiltersContainer()->Add(std::move(type_name), entry);
+ return true;
+ }
+ }
+
+public:
+ CommandObjectTypeFilterAdd(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "type filter add",
+ "Add a new filter for a type.", nullptr) {
+ CommandArgumentEntry type_arg;
+ CommandArgumentData type_style_arg;
+
+ type_style_arg.arg_type = eArgTypeName;
+ type_style_arg.arg_repetition = eArgRepeatPlus;
+
+ type_arg.push_back(type_style_arg);
+
+ m_arguments.push_back(type_arg);
+
+ SetHelpLong(
+ R"(
+The following examples of 'type filter add' refer to this code snippet for context:
+
+ class Foo {
+ int a;
+ int b;
+ int c;
+ int d;
+ int e;
+ int f;
+ int g;
+ int h;
+ int i;
+ }
+ Foo my_foo;
+
+Adding a simple filter:
+
+(lldb) type filter add --child a --child g Foo
+(lldb) frame variable my_foo
+
+)"
+ "Produces output where only a and g are displayed. Other children of my_foo \
+(b, c, d, e, f, h and i) are available by asking for them explicitly:"
+ R"(
+
+(lldb) frame variable my_foo.b my_foo.c my_foo.i
+
+)"
+ "The formatting option --raw on frame variable bypasses the filter, showing \
+all children of my_foo as if no filter was defined:"
+ R"(
+
+(lldb) frame variable my_foo --raw)");
+ }
+
+ ~CommandObjectTypeFilterAdd() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc < 1) {
+ result.AppendErrorWithFormat("%s takes one or more args.\n",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ if (m_options.m_expr_paths.empty()) {
+ result.AppendErrorWithFormat("%s needs one or more children.\n",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ TypeFilterImplSP entry(new TypeFilterImpl(
+ SyntheticChildren::Flags()
+ .SetCascades(m_options.m_cascade)
+ .SetSkipPointers(m_options.m_skip_pointers)
+ .SetSkipReferences(m_options.m_skip_references)));
+
+ // go through the expression paths
+ CommandOptions::ExpressionPathsIterator begin,
+ end = m_options.m_expr_paths.end();
+
+ for (begin = m_options.m_expr_paths.begin(); begin != end; begin++)
+ entry->AddExpressionPath(*begin);
+
+ // now I have a valid provider, let's add it to every type
+
+ lldb::TypeCategoryImplSP category;
+ DataVisualization::Categories::GetCategory(
+ ConstString(m_options.m_category.c_str()), category);
+
+ Status error;
+
+ WarnOnPotentialUnquotedUnsignedType(command, result);
+
+ for (auto &arg_entry : command.entries()) {
+ if (arg_entry.ref().empty()) {
+ result.AppendError("empty typenames not allowed");
+ return false;
+ }
+
+ ConstString typeCS(arg_entry.ref());
+ if (!AddFilter(typeCS, entry,
+ m_options.m_regex ? eRegexFilter : eRegularFilter,
+ m_options.m_category, &error)) {
+ result.AppendError(error.AsCString());
+ return false;
+ }
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+ }
+};
+
+// "type lookup"
+#define LLDB_OPTIONS_type_lookup
+#include "CommandOptions.inc"
+
+class CommandObjectTypeLookup : public CommandObjectRaw {
+protected:
+ // this function is allowed to do a more aggressive job at guessing languages
+ // than the expression parser is comfortable with - so leave the original
+ // call alone and add one that is specific to type lookup
+ lldb::LanguageType GuessLanguage(StackFrame *frame) {
+ lldb::LanguageType lang_type = lldb::eLanguageTypeUnknown;
+
+ if (!frame)
+ return lang_type;
+
+ lang_type = frame->GuessLanguage();
+ if (lang_type != lldb::eLanguageTypeUnknown)
+ return lang_type;
+
+ Symbol *s = frame->GetSymbolContext(eSymbolContextSymbol).symbol;
+ if (s)
+ lang_type = s->GetMangled().GuessLanguage();
+
+ return lang_type;
+ }
+
+ class CommandOptions : public OptionGroup {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_type_lookup_options);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
+ ExecutionContext *execution_context) override {
+ Status error;
+
+ const int short_option = g_type_lookup_options[option_idx].short_option;
+
+ switch (short_option) {
+ case 'h':
+ m_show_help = true;
+ break;
+
+ case 'l':
+ m_language = Language::GetLanguageTypeFromString(option_value);
+ break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_show_help = false;
+ m_language = eLanguageTypeUnknown;
+ }
+
+ // Options table: Required for subclasses of Options.
+
+ bool m_show_help = false;
+ lldb::LanguageType m_language = eLanguageTypeUnknown;
+ };
+
+ OptionGroupOptions m_option_group;
+ CommandOptions m_command_options;
+
+public:
+ CommandObjectTypeLookup(CommandInterpreter &interpreter)
+ : CommandObjectRaw(interpreter, "type lookup",
+ "Lookup types and declarations in the current target, "
+ "following language-specific naming conventions.",
+ "type lookup <type-specifier>",
+ eCommandRequiresTarget) {
+ m_option_group.Append(&m_command_options);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectTypeLookup() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+ llvm::StringRef GetHelpLong() override {
+ if (!m_cmd_help_long.empty())
+ return m_cmd_help_long;
+
+ StreamString stream;
+ Language::ForEach([&](Language *lang) {
+ if (const char *help = lang->GetLanguageSpecificTypeLookupHelp())
+ stream.Printf("%s\n", help);
+ return true;
+ });
+
+ m_cmd_help_long = std::string(stream.GetString());
+ return m_cmd_help_long;
+ }
+
+ bool DoExecute(llvm::StringRef raw_command_line,
+ CommandReturnObject &result) override {
+ if (raw_command_line.empty()) {
+ result.AppendError(
+ "type lookup cannot be invoked without a type name as argument");
+ return false;
+ }
+
+ auto exe_ctx = GetCommandInterpreter().GetExecutionContext();
+ m_option_group.NotifyOptionParsingStarting(&exe_ctx);
+
+ OptionsWithRaw args(raw_command_line);
+ const char *name_of_type = args.GetRawPart().c_str();
+
+ if (args.HasArgs())
+ if (!ParseOptionsAndNotify(args.GetArgs(), result, m_option_group,
+ exe_ctx))
+ return false;
+
+ ExecutionContextScope *best_scope = exe_ctx.GetBestExecutionContextScope();
+
+ bool any_found = false;
+
+ std::vector<Language *> languages;
+
+ bool is_global_search = false;
+ LanguageType guessed_language = lldb::eLanguageTypeUnknown;
+
+ if ((is_global_search =
+ (m_command_options.m_language == eLanguageTypeUnknown))) {
+ Language::ForEach([&](Language *lang) {
+ languages.push_back(lang);
+ return true;
+ });
+ } else {
+ languages.push_back(Language::FindPlugin(m_command_options.m_language));
+ }
+
+ // This is not the most efficient way to do this, but we support very few
+ // languages so the cost of the sort is going to be dwarfed by the actual
+ // lookup anyway
+ if (StackFrame *frame = m_exe_ctx.GetFramePtr()) {
+ guessed_language = GuessLanguage(frame);
+ if (guessed_language != eLanguageTypeUnknown) {
+ llvm::sort(
+ languages.begin(), languages.end(),
+ [guessed_language](Language *lang1, Language *lang2) -> bool {
+ if (!lang1 || !lang2)
+ return false;
+ LanguageType lt1 = lang1->GetLanguageType();
+ LanguageType lt2 = lang2->GetLanguageType();
+ if (lt1 == guessed_language)
+ return true; // make the selected frame's language come first
+ if (lt2 == guessed_language)
+ return false; // make the selected frame's language come first
+ return (lt1 < lt2); // normal comparison otherwise
+ });
+ }
+ }
+
+ bool is_first_language = true;
+
+ for (Language *language : languages) {
+ if (!language)
+ continue;
+
+ if (auto scavenger = language->GetTypeScavenger()) {
+ Language::TypeScavenger::ResultSet search_results;
+ if (scavenger->Find(best_scope, name_of_type, search_results) > 0) {
+ for (const auto &search_result : search_results) {
+ if (search_result && search_result->IsValid()) {
+ any_found = true;
+ search_result->DumpToStream(result.GetOutputStream(),
+ this->m_command_options.m_show_help);
+ }
+ }
+ }
+ }
+ // this is "type lookup SomeName" and we did find a match, so get out
+ if (any_found && is_global_search)
+ break;
+ else if (is_first_language && is_global_search &&
+ guessed_language != lldb::eLanguageTypeUnknown) {
+ is_first_language = false;
+ result.GetOutputStream().Printf(
+ "no type was found in the current language %s matching '%s'; "
+ "performing a global search across all languages\n",
+ Language::GetNameForLanguageType(guessed_language), name_of_type);
+ }
+ }
+
+ if (!any_found)
+ result.AppendMessageWithFormat("no type was found matching '%s'\n",
+ name_of_type);
+
+ result.SetStatus(any_found ? lldb::eReturnStatusSuccessFinishResult
+ : lldb::eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+};
+
+template <typename FormatterType>
+class CommandObjectFormatterInfo : public CommandObjectRaw {
+public:
+ typedef std::function<typename FormatterType::SharedPointer(ValueObject &)>
+ DiscoveryFunction;
+ CommandObjectFormatterInfo(CommandInterpreter &interpreter,
+ const char *formatter_name,
+ DiscoveryFunction discovery_func)
+ : CommandObjectRaw(interpreter, "", "", "", eCommandRequiresFrame),
+ m_formatter_name(formatter_name ? formatter_name : ""),
+ m_discovery_function(discovery_func) {
+ StreamString name;
+ name.Printf("type %s info", formatter_name);
+ SetCommandName(name.GetString());
+ StreamString help;
+ help.Printf("This command evaluates the provided expression and shows "
+ "which %s is applied to the resulting value (if any).",
+ formatter_name);
+ SetHelp(help.GetString());
+ StreamString syntax;
+ syntax.Printf("type %s info <expr>", formatter_name);
+ SetSyntax(syntax.GetString());
+ }
+
+ ~CommandObjectFormatterInfo() override = default;
+
+protected:
+ bool DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) override {
+ TargetSP target_sp = GetDebugger().GetSelectedTarget();
+ Thread *thread = GetDefaultThread();
+ if (!thread) {
+ result.AppendError("no default thread");
+ return false;
+ }
+
+ StackFrameSP frame_sp = thread->GetSelectedFrame();
+ ValueObjectSP result_valobj_sp;
+ EvaluateExpressionOptions options;
+ lldb::ExpressionResults expr_result = target_sp->EvaluateExpression(
+ command, frame_sp.get(), result_valobj_sp, options);
+ if (expr_result == eExpressionCompleted && result_valobj_sp) {
+ result_valobj_sp =
+ result_valobj_sp->GetQualifiedRepresentationIfAvailable(
+ target_sp->GetPreferDynamicValue(),
+ target_sp->GetEnableSyntheticValue());
+ typename FormatterType::SharedPointer formatter_sp =
+ m_discovery_function(*result_valobj_sp);
+ if (formatter_sp) {
+ std::string description(formatter_sp->GetDescription());
+ result.GetOutputStream()
+ << m_formatter_name << " applied to ("
+ << result_valobj_sp->GetDisplayTypeName().AsCString("<unknown>")
+ << ") " << command << " is: " << description << "\n";
+ result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
+ } else {
+ result.GetOutputStream()
+ << "no " << m_formatter_name << " applies to ("
+ << result_valobj_sp->GetDisplayTypeName().AsCString("<unknown>")
+ << ") " << command << "\n";
+ result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
+ }
+ return true;
+ } else {
+ result.AppendError("failed to evaluate expression");
+ return false;
+ }
+ }
+
+private:
+ std::string m_formatter_name;
+ DiscoveryFunction m_discovery_function;
+};
+
+class CommandObjectTypeFormat : public CommandObjectMultiword {
+public:
+ CommandObjectTypeFormat(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "type format",
+ "Commands for customizing value display formats.",
+ "type format [<sub-command-options>] ") {
+ LoadSubCommand(
+ "add", CommandObjectSP(new CommandObjectTypeFormatAdd(interpreter)));
+ LoadSubCommand("clear", CommandObjectSP(
+ new CommandObjectTypeFormatClear(interpreter)));
+ LoadSubCommand("delete", CommandObjectSP(new CommandObjectTypeFormatDelete(
+ interpreter)));
+ LoadSubCommand(
+ "list", CommandObjectSP(new CommandObjectTypeFormatList(interpreter)));
+ LoadSubCommand(
+ "info", CommandObjectSP(new CommandObjectFormatterInfo<TypeFormatImpl>(
+ interpreter, "format",
+ [](ValueObject &valobj) -> TypeFormatImpl::SharedPointer {
+ return valobj.GetValueFormat();
+ })));
+ }
+
+ ~CommandObjectTypeFormat() override = default;
+};
+
+#if LLDB_ENABLE_PYTHON
+
+class CommandObjectTypeSynth : public CommandObjectMultiword {
+public:
+ CommandObjectTypeSynth(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "type synthetic",
+ "Commands for operating on synthetic type representations.",
+ "type synthetic [<sub-command-options>] ") {
+ LoadSubCommand("add",
+ CommandObjectSP(new CommandObjectTypeSynthAdd(interpreter)));
+ LoadSubCommand(
+ "clear", CommandObjectSP(new CommandObjectTypeSynthClear(interpreter)));
+ LoadSubCommand("delete", CommandObjectSP(new CommandObjectTypeSynthDelete(
+ interpreter)));
+ LoadSubCommand(
+ "list", CommandObjectSP(new CommandObjectTypeSynthList(interpreter)));
+ LoadSubCommand(
+ "info",
+ CommandObjectSP(new CommandObjectFormatterInfo<SyntheticChildren>(
+ interpreter, "synthetic",
+ [](ValueObject &valobj) -> SyntheticChildren::SharedPointer {
+ return valobj.GetSyntheticChildren();
+ })));
+ }
+
+ ~CommandObjectTypeSynth() override = default;
+};
+
+#endif
+
+class CommandObjectTypeFilter : public CommandObjectMultiword {
+public:
+ CommandObjectTypeFilter(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "type filter",
+ "Commands for operating on type filters.",
+ "type filter [<sub-command-options>] ") {
+ LoadSubCommand(
+ "add", CommandObjectSP(new CommandObjectTypeFilterAdd(interpreter)));
+ LoadSubCommand("clear", CommandObjectSP(
+ new CommandObjectTypeFilterClear(interpreter)));
+ LoadSubCommand("delete", CommandObjectSP(new CommandObjectTypeFilterDelete(
+ interpreter)));
+ LoadSubCommand(
+ "list", CommandObjectSP(new CommandObjectTypeFilterList(interpreter)));
+ }
+
+ ~CommandObjectTypeFilter() override = default;
+};
+
+class CommandObjectTypeCategory : public CommandObjectMultiword {
+public:
+ CommandObjectTypeCategory(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "type category",
+ "Commands for operating on type categories.",
+ "type category [<sub-command-options>] ") {
+ LoadSubCommand(
+ "define",
+ CommandObjectSP(new CommandObjectTypeCategoryDefine(interpreter)));
+ LoadSubCommand(
+ "enable",
+ CommandObjectSP(new CommandObjectTypeCategoryEnable(interpreter)));
+ LoadSubCommand(
+ "disable",
+ CommandObjectSP(new CommandObjectTypeCategoryDisable(interpreter)));
+ LoadSubCommand(
+ "delete",
+ CommandObjectSP(new CommandObjectTypeCategoryDelete(interpreter)));
+ LoadSubCommand("list", CommandObjectSP(
+ new CommandObjectTypeCategoryList(interpreter)));
+ }
+
+ ~CommandObjectTypeCategory() override = default;
+};
+
+class CommandObjectTypeSummary : public CommandObjectMultiword {
+public:
+ CommandObjectTypeSummary(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "type summary",
+ "Commands for editing variable summary display options.",
+ "type summary [<sub-command-options>] ") {
+ LoadSubCommand(
+ "add", CommandObjectSP(new CommandObjectTypeSummaryAdd(interpreter)));
+ LoadSubCommand("clear", CommandObjectSP(new CommandObjectTypeSummaryClear(
+ interpreter)));
+ LoadSubCommand("delete", CommandObjectSP(new CommandObjectTypeSummaryDelete(
+ interpreter)));
+ LoadSubCommand(
+ "list", CommandObjectSP(new CommandObjectTypeSummaryList(interpreter)));
+ LoadSubCommand(
+ "info", CommandObjectSP(new CommandObjectFormatterInfo<TypeSummaryImpl>(
+ interpreter, "summary",
+ [](ValueObject &valobj) -> TypeSummaryImpl::SharedPointer {
+ return valobj.GetSummaryFormat();
+ })));
+ }
+
+ ~CommandObjectTypeSummary() override = default;
+};
+
+// CommandObjectType
+
+CommandObjectType::CommandObjectType(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "type",
+ "Commands for operating on the type system.",
+ "type [<sub-command-options>]") {
+ LoadSubCommand("category",
+ CommandObjectSP(new CommandObjectTypeCategory(interpreter)));
+ LoadSubCommand("filter",
+ CommandObjectSP(new CommandObjectTypeFilter(interpreter)));
+ LoadSubCommand("format",
+ CommandObjectSP(new CommandObjectTypeFormat(interpreter)));
+ LoadSubCommand("summary",
+ CommandObjectSP(new CommandObjectTypeSummary(interpreter)));
+#if LLDB_ENABLE_PYTHON
+ LoadSubCommand("synthetic",
+ CommandObjectSP(new CommandObjectTypeSynth(interpreter)));
+#endif
+ LoadSubCommand("lookup",
+ CommandObjectSP(new CommandObjectTypeLookup(interpreter)));
+}
+
+CommandObjectType::~CommandObjectType() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectType.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectType.h
new file mode 100644
index 000000000000..1c081abe2be4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectType.h
@@ -0,0 +1,25 @@
+//===-- CommandObjectType.h ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTTYPE_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTTYPE_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+class CommandObjectType : public CommandObjectMultiword {
+public:
+ CommandObjectType(CommandInterpreter &interpreter);
+
+ ~CommandObjectType() override;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTTYPE_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectVersion.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectVersion.cpp
new file mode 100644
index 000000000000..9b3c9e67a1a7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectVersion.cpp
@@ -0,0 +1,29 @@
+//===-- CommandObjectVersion.cpp ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectVersion.h"
+
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Version/Version.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// CommandObjectVersion
+
+CommandObjectVersion::CommandObjectVersion(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "version",
+ "Show the LLDB debugger version.", "version") {}
+
+CommandObjectVersion::~CommandObjectVersion() = default;
+
+bool CommandObjectVersion::DoExecute(Args &args, CommandReturnObject &result) {
+ result.AppendMessageWithFormat("%s\n", lldb_private::GetVersion());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectVersion.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectVersion.h
new file mode 100644
index 000000000000..dce1a8d67b88
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectVersion.h
@@ -0,0 +1,30 @@
+//===-- CommandObjectVersion.h ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTVERSION_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTVERSION_H
+
+#include "lldb/Interpreter/CommandObject.h"
+
+namespace lldb_private {
+
+// CommandObjectVersion
+
+class CommandObjectVersion : public CommandObjectParsed {
+public:
+ CommandObjectVersion(CommandInterpreter &interpreter);
+
+ ~CommandObjectVersion() override;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTVERSION_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpoint.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpoint.cpp
new file mode 100644
index 000000000000..6805ff7f50ae
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpoint.cpp
@@ -0,0 +1,1209 @@
+//===-- CommandObjectWatchpoint.cpp ---------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectWatchpoint.h"
+#include "CommandObjectWatchpointCommand.h"
+
+#include <vector>
+
+#include "llvm/ADT/StringRef.h"
+
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Breakpoint/WatchpointList.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static void AddWatchpointDescription(Stream *s, Watchpoint *wp,
+ lldb::DescriptionLevel level) {
+ s->IndentMore();
+ wp->GetDescription(s, level);
+ s->IndentLess();
+ s->EOL();
+}
+
+static bool CheckTargetForWatchpointOperations(Target *target,
+ CommandReturnObject &result) {
+ bool process_is_valid =
+ target->GetProcessSP() && target->GetProcessSP()->IsAlive();
+ if (!process_is_valid) {
+ result.AppendError("There's no process or it is not alive.");
+ return false;
+ }
+ // Target passes our checks, return true.
+ return true;
+}
+
+// Equivalent class: {"-", "to", "To", "TO"} of range specifier array.
+static const char *RSA[4] = {"-", "to", "To", "TO"};
+
+// Return the index to RSA if found; otherwise -1 is returned.
+static int32_t WithRSAIndex(llvm::StringRef Arg) {
+
+ uint32_t i;
+ for (i = 0; i < 4; ++i)
+ if (Arg.contains(RSA[i]))
+ return i;
+ return -1;
+}
+
+// Return true if wp_ids is successfully populated with the watch ids. False
+// otherwise.
+bool CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(
+ Target *target, Args &args, std::vector<uint32_t> &wp_ids) {
+ // Pre-condition: args.GetArgumentCount() > 0.
+ if (args.GetArgumentCount() == 0) {
+ if (target == nullptr)
+ return false;
+ WatchpointSP watch_sp = target->GetLastCreatedWatchpoint();
+ if (watch_sp) {
+ wp_ids.push_back(watch_sp->GetID());
+ return true;
+ } else
+ return false;
+ }
+
+ llvm::StringRef Minus("-");
+ std::vector<llvm::StringRef> StrRefArgs;
+ llvm::StringRef first;
+ llvm::StringRef second;
+ size_t i;
+ int32_t idx;
+ // Go through the arguments and make a canonical form of arg list containing
+ // only numbers with possible "-" in between.
+ for (auto &entry : args.entries()) {
+ if ((idx = WithRSAIndex(entry.ref())) == -1) {
+ StrRefArgs.push_back(entry.ref());
+ continue;
+ }
+ // The Arg contains the range specifier, split it, then.
+ std::tie(first, second) = entry.ref().split(RSA[idx]);
+ if (!first.empty())
+ StrRefArgs.push_back(first);
+ StrRefArgs.push_back(Minus);
+ if (!second.empty())
+ StrRefArgs.push_back(second);
+ }
+ // Now process the canonical list and fill in the vector of uint32_t's. If
+ // there is any error, return false and the client should ignore wp_ids.
+ uint32_t beg, end, id;
+ size_t size = StrRefArgs.size();
+ bool in_range = false;
+ for (i = 0; i < size; ++i) {
+ llvm::StringRef Arg = StrRefArgs[i];
+ if (in_range) {
+ // Look for the 'end' of the range. Note StringRef::getAsInteger()
+ // returns true to signify error while parsing.
+ if (Arg.getAsInteger(0, end))
+ return false;
+ // Found a range! Now append the elements.
+ for (id = beg; id <= end; ++id)
+ wp_ids.push_back(id);
+ in_range = false;
+ continue;
+ }
+ if (i < (size - 1) && StrRefArgs[i + 1] == Minus) {
+ if (Arg.getAsInteger(0, beg))
+ return false;
+ // Turn on the in_range flag, we are looking for end of range next.
+ ++i;
+ in_range = true;
+ continue;
+ }
+ // Otherwise, we have a simple ID. Just append it.
+ if (Arg.getAsInteger(0, beg))
+ return false;
+ wp_ids.push_back(beg);
+ }
+
+ // It is an error if after the loop, we're still in_range.
+ return !in_range;
+}
+
+// CommandObjectWatchpointList
+
+// CommandObjectWatchpointList::Options
+#pragma mark List::CommandOptions
+#define LLDB_OPTIONS_watchpoint_list
+#include "CommandOptions.inc"
+
+#pragma mark List
+
+class CommandObjectWatchpointList : public CommandObjectParsed {
+public:
+ CommandObjectWatchpointList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "watchpoint list",
+ "List all watchpoints at configurable levels of detail.", nullptr,
+ eCommandRequiresTarget) {
+ CommandArgumentEntry arg;
+ CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID,
+ eArgTypeWatchpointIDRange);
+ // Add the entry for the first argument for this command to the object's
+ // arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectWatchpointList() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'b':
+ m_level = lldb::eDescriptionLevelBrief;
+ break;
+ case 'f':
+ m_level = lldb::eDescriptionLevelFull;
+ break;
+ case 'v':
+ m_level = lldb::eDescriptionLevelVerbose;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_level = lldb::eDescriptionLevelFull;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_watchpoint_list_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ lldb::DescriptionLevel m_level = lldb::eDescriptionLevelBrief;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+
+ if (target->GetProcessSP() && target->GetProcessSP()->IsAlive()) {
+ uint32_t num_supported_hardware_watchpoints;
+ Status error = target->GetProcessSP()->GetWatchpointSupportInfo(
+ num_supported_hardware_watchpoints);
+ if (error.Success())
+ result.AppendMessageWithFormat(
+ "Number of supported hardware watchpoints: %u\n",
+ num_supported_hardware_watchpoints);
+ }
+
+ const WatchpointList &watchpoints = target->GetWatchpointList();
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetWatchpointList().GetListMutex(lock);
+
+ size_t num_watchpoints = watchpoints.GetSize();
+
+ if (num_watchpoints == 0) {
+ result.AppendMessage("No watchpoints currently set.");
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+
+ Stream &output_stream = result.GetOutputStream();
+
+ if (command.GetArgumentCount() == 0) {
+ // No watchpoint selected; show info about all currently set watchpoints.
+ result.AppendMessage("Current watchpoints:");
+ for (size_t i = 0; i < num_watchpoints; ++i) {
+ Watchpoint *wp = watchpoints.GetByIndex(i).get();
+ AddWatchpointDescription(&output_stream, wp, m_options.m_level);
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ // Particular watchpoints selected; enable them.
+ std::vector<uint32_t> wp_ids;
+ if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(
+ target, command, wp_ids)) {
+ result.AppendError("Invalid watchpoints specification.");
+ return false;
+ }
+
+ const size_t size = wp_ids.size();
+ for (size_t i = 0; i < size; ++i) {
+ Watchpoint *wp = watchpoints.FindByID(wp_ids[i]).get();
+ if (wp)
+ AddWatchpointDescription(&output_stream, wp, m_options.m_level);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectWatchpointEnable
+#pragma mark Enable
+
+class CommandObjectWatchpointEnable : public CommandObjectParsed {
+public:
+ CommandObjectWatchpointEnable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "enable",
+ "Enable the specified disabled watchpoint(s). If "
+ "no watchpoints are specified, enable all of them.",
+ nullptr, eCommandRequiresTarget) {
+ CommandArgumentEntry arg;
+ CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID,
+ eArgTypeWatchpointIDRange);
+ // Add the entry for the first argument for this command to the object's
+ // arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectWatchpointEnable() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eWatchPointIDCompletion,
+ request, nullptr);
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+ if (!CheckTargetForWatchpointOperations(target, result))
+ return false;
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetWatchpointList().GetListMutex(lock);
+
+ const WatchpointList &watchpoints = target->GetWatchpointList();
+
+ size_t num_watchpoints = watchpoints.GetSize();
+
+ if (num_watchpoints == 0) {
+ result.AppendError("No watchpoints exist to be enabled.");
+ return false;
+ }
+
+ if (command.GetArgumentCount() == 0) {
+ // No watchpoint selected; enable all currently set watchpoints.
+ target->EnableAllWatchpoints();
+ result.AppendMessageWithFormat("All watchpoints enabled. (%" PRIu64
+ " watchpoints)\n",
+ (uint64_t)num_watchpoints);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ // Particular watchpoints selected; enable them.
+ std::vector<uint32_t> wp_ids;
+ if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(
+ target, command, wp_ids)) {
+ result.AppendError("Invalid watchpoints specification.");
+ return false;
+ }
+
+ int count = 0;
+ const size_t size = wp_ids.size();
+ for (size_t i = 0; i < size; ++i)
+ if (target->EnableWatchpointByID(wp_ids[i]))
+ ++count;
+ result.AppendMessageWithFormat("%d watchpoints enabled.\n", count);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectWatchpointDisable
+#pragma mark Disable
+
+class CommandObjectWatchpointDisable : public CommandObjectParsed {
+public:
+ CommandObjectWatchpointDisable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "watchpoint disable",
+ "Disable the specified watchpoint(s) without "
+ "removing it/them. If no watchpoints are "
+ "specified, disable them all.",
+ nullptr, eCommandRequiresTarget) {
+ CommandArgumentEntry arg;
+ CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID,
+ eArgTypeWatchpointIDRange);
+ // Add the entry for the first argument for this command to the object's
+ // arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectWatchpointDisable() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eWatchPointIDCompletion,
+ request, nullptr);
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+ if (!CheckTargetForWatchpointOperations(target, result))
+ return false;
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetWatchpointList().GetListMutex(lock);
+
+ const WatchpointList &watchpoints = target->GetWatchpointList();
+ size_t num_watchpoints = watchpoints.GetSize();
+
+ if (num_watchpoints == 0) {
+ result.AppendError("No watchpoints exist to be disabled.");
+ return false;
+ }
+
+ if (command.GetArgumentCount() == 0) {
+ // No watchpoint selected; disable all currently set watchpoints.
+ if (target->DisableAllWatchpoints()) {
+ result.AppendMessageWithFormat("All watchpoints disabled. (%" PRIu64
+ " watchpoints)\n",
+ (uint64_t)num_watchpoints);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendError("Disable all watchpoints failed\n");
+ }
+ } else {
+ // Particular watchpoints selected; disable them.
+ std::vector<uint32_t> wp_ids;
+ if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(
+ target, command, wp_ids)) {
+ result.AppendError("Invalid watchpoints specification.");
+ return false;
+ }
+
+ int count = 0;
+ const size_t size = wp_ids.size();
+ for (size_t i = 0; i < size; ++i)
+ if (target->DisableWatchpointByID(wp_ids[i]))
+ ++count;
+ result.AppendMessageWithFormat("%d watchpoints disabled.\n", count);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectWatchpointDelete
+#define LLDB_OPTIONS_watchpoint_delete
+#include "CommandOptions.inc"
+
+// CommandObjectWatchpointDelete
+#pragma mark Delete
+
+class CommandObjectWatchpointDelete : public CommandObjectParsed {
+public:
+ CommandObjectWatchpointDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "watchpoint delete",
+ "Delete the specified watchpoint(s). If no "
+ "watchpoints are specified, delete them all.",
+ nullptr, eCommandRequiresTarget) {
+ CommandArgumentEntry arg;
+ CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID,
+ eArgTypeWatchpointIDRange);
+ // Add the entry for the first argument for this command to the object's
+ // arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectWatchpointDelete() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eWatchPointIDCompletion,
+ request, nullptr);
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'f':
+ m_force = true;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return {};
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_force = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_watchpoint_delete_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ bool m_force = false;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+ if (!CheckTargetForWatchpointOperations(target, result))
+ return false;
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetWatchpointList().GetListMutex(lock);
+
+ const WatchpointList &watchpoints = target->GetWatchpointList();
+
+ size_t num_watchpoints = watchpoints.GetSize();
+
+ if (num_watchpoints == 0) {
+ result.AppendError("No watchpoints exist to be deleted.");
+ return false;
+ }
+
+ if (command.empty()) {
+ if (!m_options.m_force &&
+ !m_interpreter.Confirm(
+ "About to delete all watchpoints, do you want to do that?",
+ true)) {
+ result.AppendMessage("Operation cancelled...");
+ } else {
+ target->RemoveAllWatchpoints();
+ result.AppendMessageWithFormat("All watchpoints removed. (%" PRIu64
+ " watchpoints)\n",
+ (uint64_t)num_watchpoints);
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+ }
+
+ // Particular watchpoints selected; delete them.
+ std::vector<uint32_t> wp_ids;
+ if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command,
+ wp_ids)) {
+ result.AppendError("Invalid watchpoints specification.");
+ return false;
+ }
+
+ int count = 0;
+ const size_t size = wp_ids.size();
+ for (size_t i = 0; i < size; ++i)
+ if (target->RemoveWatchpointByID(wp_ids[i]))
+ ++count;
+ result.AppendMessageWithFormat("%d watchpoints deleted.\n", count);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectWatchpointIgnore
+
+#pragma mark Ignore::CommandOptions
+#define LLDB_OPTIONS_watchpoint_ignore
+#include "CommandOptions.inc"
+
+class CommandObjectWatchpointIgnore : public CommandObjectParsed {
+public:
+ CommandObjectWatchpointIgnore(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "watchpoint ignore",
+ "Set ignore count on the specified watchpoint(s). "
+ "If no watchpoints are specified, set them all.",
+ nullptr, eCommandRequiresTarget) {
+ CommandArgumentEntry arg;
+ CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID,
+ eArgTypeWatchpointIDRange);
+ // Add the entry for the first argument for this command to the object's
+ // arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectWatchpointIgnore() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eWatchPointIDCompletion,
+ request, nullptr);
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'i':
+ if (option_arg.getAsInteger(0, m_ignore_count))
+ error.SetErrorStringWithFormat("invalid ignore count '%s'",
+ option_arg.str().c_str());
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_ignore_count = 0;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_watchpoint_ignore_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ uint32_t m_ignore_count = 0;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+ if (!CheckTargetForWatchpointOperations(target, result))
+ return false;
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetWatchpointList().GetListMutex(lock);
+
+ const WatchpointList &watchpoints = target->GetWatchpointList();
+
+ size_t num_watchpoints = watchpoints.GetSize();
+
+ if (num_watchpoints == 0) {
+ result.AppendError("No watchpoints exist to be ignored.");
+ return false;
+ }
+
+ if (command.GetArgumentCount() == 0) {
+ target->IgnoreAllWatchpoints(m_options.m_ignore_count);
+ result.AppendMessageWithFormat("All watchpoints ignored. (%" PRIu64
+ " watchpoints)\n",
+ (uint64_t)num_watchpoints);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ // Particular watchpoints selected; ignore them.
+ std::vector<uint32_t> wp_ids;
+ if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(
+ target, command, wp_ids)) {
+ result.AppendError("Invalid watchpoints specification.");
+ return false;
+ }
+
+ int count = 0;
+ const size_t size = wp_ids.size();
+ for (size_t i = 0; i < size; ++i)
+ if (target->IgnoreWatchpointByID(wp_ids[i], m_options.m_ignore_count))
+ ++count;
+ result.AppendMessageWithFormat("%d watchpoints ignored.\n", count);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectWatchpointModify
+
+#pragma mark Modify::CommandOptions
+#define LLDB_OPTIONS_watchpoint_modify
+#include "CommandOptions.inc"
+
+#pragma mark Modify
+
+class CommandObjectWatchpointModify : public CommandObjectParsed {
+public:
+ CommandObjectWatchpointModify(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "watchpoint modify",
+ "Modify the options on a watchpoint or set of watchpoints in the "
+ "executable. "
+ "If no watchpoint is specified, act on the last created "
+ "watchpoint. "
+ "Passing an empty argument clears the modification.",
+ nullptr, eCommandRequiresTarget) {
+ CommandArgumentEntry arg;
+ CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID,
+ eArgTypeWatchpointIDRange);
+ // Add the entry for the first argument for this command to the object's
+ // arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectWatchpointModify() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eWatchPointIDCompletion,
+ request, nullptr);
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'c':
+ m_condition = std::string(option_arg);
+ m_condition_passed = true;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_condition.clear();
+ m_condition_passed = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_watchpoint_modify_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ std::string m_condition;
+ bool m_condition_passed = false;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+ if (!CheckTargetForWatchpointOperations(target, result))
+ return false;
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetWatchpointList().GetListMutex(lock);
+
+ const WatchpointList &watchpoints = target->GetWatchpointList();
+
+ size_t num_watchpoints = watchpoints.GetSize();
+
+ if (num_watchpoints == 0) {
+ result.AppendError("No watchpoints exist to be modified.");
+ return false;
+ }
+
+ if (command.GetArgumentCount() == 0) {
+ WatchpointSP wp_sp = target->GetLastCreatedWatchpoint();
+ wp_sp->SetCondition(m_options.m_condition.c_str());
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ // Particular watchpoints selected; set condition on them.
+ std::vector<uint32_t> wp_ids;
+ if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(
+ target, command, wp_ids)) {
+ result.AppendError("Invalid watchpoints specification.");
+ return false;
+ }
+
+ int count = 0;
+ const size_t size = wp_ids.size();
+ for (size_t i = 0; i < size; ++i) {
+ WatchpointSP wp_sp = watchpoints.FindByID(wp_ids[i]);
+ if (wp_sp) {
+ wp_sp->SetCondition(m_options.m_condition.c_str());
+ ++count;
+ }
+ }
+ result.AppendMessageWithFormat("%d watchpoints modified.\n", count);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectWatchpointSetVariable
+#pragma mark SetVariable
+
+class CommandObjectWatchpointSetVariable : public CommandObjectParsed {
+public:
+ CommandObjectWatchpointSetVariable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "watchpoint set variable",
+ "Set a watchpoint on a variable. "
+ "Use the '-w' option to specify the type of watchpoint and "
+ "the '-s' option to specify the byte size to watch for. "
+ "If no '-w' option is specified, it defaults to write. "
+ "If no '-s' option is specified, it defaults to the variable's "
+ "byte size. "
+ "Note that there are limited hardware resources for watchpoints. "
+ "If watchpoint setting fails, consider disable/delete existing "
+ "ones "
+ "to free up resources.",
+ nullptr,
+ eCommandRequiresFrame | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {
+ SetHelpLong(
+ R"(
+Examples:
+
+(lldb) watchpoint set variable -w read_write my_global_var
+
+)"
+ " Watches my_global_var for read/write access, with the region to watch \
+corresponding to the byte size of the data type.");
+
+ CommandArgumentEntry arg;
+ CommandArgumentData var_name_arg;
+
+ // Define the only variant of this arg.
+ var_name_arg.arg_type = eArgTypeVarName;
+ var_name_arg.arg_repetition = eArgRepeatPlain;
+
+ // Push the variant into the argument entry.
+ arg.push_back(var_name_arg);
+
+ // Push the data for the only argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+
+ // Absorb the '-w' and '-s' options into our option group.
+ m_option_group.Append(&m_option_watchpoint, LLDB_OPT_SET_ALL,
+ LLDB_OPT_SET_1);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectWatchpointSetVariable() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (request.GetCursorIndex() != 0)
+ return;
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eVariablePathCompletion,
+ request, nullptr);
+ }
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ static size_t GetVariableCallback(void *baton, const char *name,
+ VariableList &variable_list) {
+ size_t old_size = variable_list.GetSize();
+ Target *target = static_cast<Target *>(baton);
+ if (target)
+ target->GetImages().FindGlobalVariables(ConstString(name), UINT32_MAX,
+ variable_list);
+ return variable_list.GetSize() - old_size;
+ }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ StackFrame *frame = m_exe_ctx.GetFramePtr();
+
+ // If no argument is present, issue an error message. There's no way to
+ // set a watchpoint.
+ if (command.GetArgumentCount() <= 0) {
+ result.AppendError("required argument missing; "
+ "specify your program variable to watch for");
+ return false;
+ }
+
+ // If no '-w' is specified, default to '-w write'.
+ if (!m_option_watchpoint.watch_type_specified) {
+ m_option_watchpoint.watch_type = OptionGroupWatchpoint::eWatchWrite;
+ }
+
+ // We passed the sanity check for the command. Proceed to set the
+ // watchpoint now.
+ lldb::addr_t addr = 0;
+ size_t size = 0;
+
+ VariableSP var_sp;
+ ValueObjectSP valobj_sp;
+ Stream &output_stream = result.GetOutputStream();
+
+ // A simple watch variable gesture allows only one argument.
+ if (command.GetArgumentCount() != 1) {
+ result.AppendError("specify exactly one variable to watch for");
+ return false;
+ }
+
+ // Things have checked out ok...
+ Status error;
+ uint32_t expr_path_options =
+ StackFrame::eExpressionPathOptionCheckPtrVsMember |
+ StackFrame::eExpressionPathOptionsAllowDirectIVarAccess;
+ valobj_sp = frame->GetValueForVariableExpressionPath(
+ command.GetArgumentAtIndex(0), eNoDynamicValues, expr_path_options,
+ var_sp, error);
+
+ if (!valobj_sp) {
+ // Not in the frame; let's check the globals.
+
+ VariableList variable_list;
+ ValueObjectList valobj_list;
+
+ Status error(Variable::GetValuesForVariableExpressionPath(
+ command.GetArgumentAtIndex(0),
+ m_exe_ctx.GetBestExecutionContextScope(), GetVariableCallback, target,
+ variable_list, valobj_list));
+
+ if (valobj_list.GetSize())
+ valobj_sp = valobj_list.GetValueObjectAtIndex(0);
+ }
+
+ CompilerType compiler_type;
+
+ if (valobj_sp) {
+ AddressType addr_type;
+ addr = valobj_sp->GetAddressOf(false, &addr_type);
+ if (addr_type == eAddressTypeLoad) {
+ // We're in business.
+ // Find out the size of this variable.
+ size = m_option_watchpoint.watch_size == 0
+ ? valobj_sp->GetByteSize().value_or(0)
+ : m_option_watchpoint.watch_size;
+ }
+ compiler_type = valobj_sp->GetCompilerType();
+ } else {
+ const char *error_cstr = error.AsCString(nullptr);
+ if (error_cstr)
+ result.AppendError(error_cstr);
+ else
+ result.AppendErrorWithFormat("unable to find any variable "
+ "expression path that matches '%s'",
+ command.GetArgumentAtIndex(0));
+ return false;
+ }
+
+ // Now it's time to create the watchpoint.
+ uint32_t watch_type = m_option_watchpoint.watch_type;
+
+ error.Clear();
+ Watchpoint *wp =
+ target->CreateWatchpoint(addr, size, &compiler_type, watch_type, error)
+ .get();
+ if (wp) {
+ wp->SetWatchSpec(command.GetArgumentAtIndex(0));
+ wp->SetWatchVariable(true);
+ if (var_sp && var_sp->GetDeclaration().GetFile()) {
+ StreamString ss;
+ // True to show fullpath for declaration file.
+ var_sp->GetDeclaration().DumpStopContext(&ss, true);
+ wp->SetDeclInfo(std::string(ss.GetString()));
+ }
+ output_stream.Printf("Watchpoint created: ");
+ wp->GetDescription(&output_stream, lldb::eDescriptionLevelFull);
+ output_stream.EOL();
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat(
+ "Watchpoint creation failed (addr=0x%" PRIx64 ", size=%" PRIu64
+ ", variable expression='%s').\n",
+ addr, (uint64_t)size, command.GetArgumentAtIndex(0));
+ if (error.AsCString(nullptr))
+ result.AppendError(error.AsCString());
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ OptionGroupOptions m_option_group;
+ OptionGroupWatchpoint m_option_watchpoint;
+};
+
+// CommandObjectWatchpointSetExpression
+#pragma mark Set
+
+class CommandObjectWatchpointSetExpression : public CommandObjectRaw {
+public:
+ CommandObjectWatchpointSetExpression(CommandInterpreter &interpreter)
+ : CommandObjectRaw(
+ interpreter, "watchpoint set expression",
+ "Set a watchpoint on an address by supplying an expression. "
+ "Use the '-w' option to specify the type of watchpoint and "
+ "the '-s' option to specify the byte size to watch for. "
+ "If no '-w' option is specified, it defaults to write. "
+ "If no '-s' option is specified, it defaults to the target's "
+ "pointer byte size. "
+ "Note that there are limited hardware resources for watchpoints. "
+ "If watchpoint setting fails, consider disable/delete existing "
+ "ones "
+ "to free up resources.",
+ "",
+ eCommandRequiresFrame | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {
+ SetHelpLong(
+ R"(
+Examples:
+
+(lldb) watchpoint set expression -w write -s 1 -- foo + 32
+
+ Watches write access for the 1-byte region pointed to by the address 'foo + 32')");
+
+ CommandArgumentEntry arg;
+ CommandArgumentData expression_arg;
+
+ // Define the only variant of this arg.
+ expression_arg.arg_type = eArgTypeExpression;
+ expression_arg.arg_repetition = eArgRepeatPlain;
+
+ // Push the only variant into the argument entry.
+ arg.push_back(expression_arg);
+
+ // Push the data for the only argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+
+ // Absorb the '-w' and '-s' options into our option group.
+ m_option_group.Append(&m_option_watchpoint, LLDB_OPT_SET_ALL,
+ LLDB_OPT_SET_1);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectWatchpointSetExpression() override = default;
+
+ // Overrides base class's behavior where WantsCompletion =
+ // !WantsRawCommandString.
+ bool WantsCompletion() override { return true; }
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ bool DoExecute(llvm::StringRef raw_command,
+ CommandReturnObject &result) override {
+ auto exe_ctx = GetCommandInterpreter().GetExecutionContext();
+ m_option_group.NotifyOptionParsingStarting(
+ &exe_ctx); // This is a raw command, so notify the option group
+
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ StackFrame *frame = m_exe_ctx.GetFramePtr();
+
+ OptionsWithRaw args(raw_command);
+
+ llvm::StringRef expr = args.GetRawPart();
+
+ if (args.HasArgs())
+ if (!ParseOptionsAndNotify(args.GetArgs(), result, m_option_group,
+ exe_ctx))
+ return false;
+
+ // If no argument is present, issue an error message. There's no way to
+ // set a watchpoint.
+ if (raw_command.trim().empty()) {
+ result.AppendError("required argument missing; specify an expression "
+ "to evaluate into the address to watch for");
+ return false;
+ }
+
+ // If no '-w' is specified, default to '-w write'.
+ if (!m_option_watchpoint.watch_type_specified) {
+ m_option_watchpoint.watch_type = OptionGroupWatchpoint::eWatchWrite;
+ }
+
+ // We passed the sanity check for the command. Proceed to set the
+ // watchpoint now.
+ lldb::addr_t addr = 0;
+ size_t size = 0;
+
+ ValueObjectSP valobj_sp;
+
+ // Use expression evaluation to arrive at the address to watch.
+ EvaluateExpressionOptions options;
+ options.SetCoerceToId(false);
+ options.SetUnwindOnError(true);
+ options.SetKeepInMemory(false);
+ options.SetTryAllThreads(true);
+ options.SetTimeout(llvm::None);
+
+ ExpressionResults expr_result =
+ target->EvaluateExpression(expr, frame, valobj_sp, options);
+ if (expr_result != eExpressionCompleted) {
+ result.AppendError("expression evaluation of address to watch failed");
+ result.AppendErrorWithFormat("expression evaluated: \n%s", expr.data());
+ if (valobj_sp && !valobj_sp->GetError().Success())
+ result.AppendError(valobj_sp->GetError().AsCString());
+ return false;
+ }
+
+ // Get the address to watch.
+ bool success = false;
+ addr = valobj_sp->GetValueAsUnsigned(0, &success);
+ if (!success) {
+ result.AppendError("expression did not evaluate to an address");
+ return false;
+ }
+
+ if (m_option_watchpoint.watch_size != 0)
+ size = m_option_watchpoint.watch_size;
+ else
+ size = target->GetArchitecture().GetAddressByteSize();
+
+ // Now it's time to create the watchpoint.
+ uint32_t watch_type = m_option_watchpoint.watch_type;
+
+ // Fetch the type from the value object, the type of the watched object is
+ // the pointee type
+ /// of the expression, so convert to that if we found a valid type.
+ CompilerType compiler_type(valobj_sp->GetCompilerType());
+
+ Status error;
+ Watchpoint *wp =
+ target->CreateWatchpoint(addr, size, &compiler_type, watch_type, error)
+ .get();
+ if (wp) {
+ Stream &output_stream = result.GetOutputStream();
+ output_stream.Printf("Watchpoint created: ");
+ wp->GetDescription(&output_stream, lldb::eDescriptionLevelFull);
+ output_stream.EOL();
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("Watchpoint creation failed (addr=0x%" PRIx64
+ ", size=%" PRIu64 ").\n",
+ addr, (uint64_t)size);
+ if (error.AsCString(nullptr))
+ result.AppendError(error.AsCString());
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ OptionGroupOptions m_option_group;
+ OptionGroupWatchpoint m_option_watchpoint;
+};
+
+// CommandObjectWatchpointSet
+#pragma mark Set
+
+class CommandObjectWatchpointSet : public CommandObjectMultiword {
+public:
+ CommandObjectWatchpointSet(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "watchpoint set", "Commands for setting a watchpoint.",
+ "watchpoint set <subcommand> [<subcommand-options>]") {
+
+ LoadSubCommand(
+ "variable",
+ CommandObjectSP(new CommandObjectWatchpointSetVariable(interpreter)));
+ LoadSubCommand(
+ "expression",
+ CommandObjectSP(new CommandObjectWatchpointSetExpression(interpreter)));
+ }
+
+ ~CommandObjectWatchpointSet() override = default;
+};
+
+// CommandObjectMultiwordWatchpoint
+#pragma mark MultiwordWatchpoint
+
+CommandObjectMultiwordWatchpoint::CommandObjectMultiwordWatchpoint(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "watchpoint",
+ "Commands for operating on watchpoints.",
+ "watchpoint <subcommand> [<command-options>]") {
+ CommandObjectSP list_command_object(
+ new CommandObjectWatchpointList(interpreter));
+ CommandObjectSP enable_command_object(
+ new CommandObjectWatchpointEnable(interpreter));
+ CommandObjectSP disable_command_object(
+ new CommandObjectWatchpointDisable(interpreter));
+ CommandObjectSP delete_command_object(
+ new CommandObjectWatchpointDelete(interpreter));
+ CommandObjectSP ignore_command_object(
+ new CommandObjectWatchpointIgnore(interpreter));
+ CommandObjectSP command_command_object(
+ new CommandObjectWatchpointCommand(interpreter));
+ CommandObjectSP modify_command_object(
+ new CommandObjectWatchpointModify(interpreter));
+ CommandObjectSP set_command_object(
+ new CommandObjectWatchpointSet(interpreter));
+
+ list_command_object->SetCommandName("watchpoint list");
+ enable_command_object->SetCommandName("watchpoint enable");
+ disable_command_object->SetCommandName("watchpoint disable");
+ delete_command_object->SetCommandName("watchpoint delete");
+ ignore_command_object->SetCommandName("watchpoint ignore");
+ command_command_object->SetCommandName("watchpoint command");
+ modify_command_object->SetCommandName("watchpoint modify");
+ set_command_object->SetCommandName("watchpoint set");
+
+ LoadSubCommand("list", list_command_object);
+ LoadSubCommand("enable", enable_command_object);
+ LoadSubCommand("disable", disable_command_object);
+ LoadSubCommand("delete", delete_command_object);
+ LoadSubCommand("ignore", ignore_command_object);
+ LoadSubCommand("command", command_command_object);
+ LoadSubCommand("modify", modify_command_object);
+ LoadSubCommand("set", set_command_object);
+}
+
+CommandObjectMultiwordWatchpoint::~CommandObjectMultiwordWatchpoint() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpoint.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpoint.h
new file mode 100644
index 000000000000..87f9f4383bd2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpoint.h
@@ -0,0 +1,31 @@
+//===-- CommandObjectWatchpoint.h -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTWATCHPOINT_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTWATCHPOINT_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/OptionGroupWatchpoint.h"
+
+namespace lldb_private {
+
+// CommandObjectMultiwordWatchpoint
+
+class CommandObjectMultiwordWatchpoint : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordWatchpoint(CommandInterpreter &interpreter);
+
+ ~CommandObjectMultiwordWatchpoint() override;
+
+ static bool VerifyWatchpointIDs(Target *target, Args &args,
+ std::vector<uint32_t> &wp_ids);
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTWATCHPOINT_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpointCommand.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpointCommand.cpp
new file mode 100644
index 000000000000..0cc83275d7ae
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpointCommand.cpp
@@ -0,0 +1,659 @@
+//===-- CommandObjectWatchpointCommand.cpp --------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <vector>
+
+#include "CommandObjectWatchpoint.h"
+#include "CommandObjectWatchpointCommand.h"
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Core/IOHandler.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// FIXME: "script-type" needs to have its contents determined dynamically, so
+// somebody can add a new scripting language to lldb and have it pickable here
+// without having to change this enumeration by hand and rebuild lldb proper.
+static constexpr OptionEnumValueElement g_script_option_enumeration[] = {
+ {
+ eScriptLanguageNone,
+ "command",
+ "Commands are in the lldb command interpreter language",
+ },
+ {
+ eScriptLanguagePython,
+ "python",
+ "Commands are in the Python language.",
+ },
+ {
+ eScriptLanguageLua,
+ "lua",
+ "Commands are in the Lua language.",
+ },
+ {
+ eSortOrderByName,
+ "default-script",
+ "Commands are in the default scripting language.",
+ },
+};
+
+static constexpr OptionEnumValues ScriptOptionEnum() {
+ return OptionEnumValues(g_script_option_enumeration);
+}
+
+#define LLDB_OPTIONS_watchpoint_command_add
+#include "CommandOptions.inc"
+
+class CommandObjectWatchpointCommandAdd : public CommandObjectParsed,
+ public IOHandlerDelegateMultiline {
+public:
+ CommandObjectWatchpointCommandAdd(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "add",
+ "Add a set of LLDB commands to a watchpoint, to be "
+ "executed whenever the watchpoint is hit. "
+ "The commands added to the watchpoint replace any "
+ "commands previously added to it.",
+ nullptr, eCommandRequiresTarget),
+ IOHandlerDelegateMultiline("DONE",
+ IOHandlerDelegate::Completion::LLDBCommand) {
+ SetHelpLong(
+ R"(
+General information about entering watchpoint commands
+------------------------------------------------------
+
+)"
+ "This command will prompt for commands to be executed when the specified \
+watchpoint is hit. Each command is typed on its own line following the '> ' \
+prompt until 'DONE' is entered."
+ R"(
+
+)"
+ "Syntactic errors may not be detected when initially entered, and many \
+malformed commands can silently fail when executed. If your watchpoint commands \
+do not appear to be executing, double-check the command syntax."
+ R"(
+
+)"
+ "Note: You may enter any debugger command exactly as you would at the debugger \
+prompt. There is no limit to the number of commands supplied, but do NOT enter \
+more than one command per line."
+ R"(
+
+Special information about PYTHON watchpoint commands
+----------------------------------------------------
+
+)"
+ "You may enter either one or more lines of Python, including function \
+definitions or calls to functions that will have been imported by the time \
+the code executes. Single line watchpoint commands will be interpreted 'as is' \
+when the watchpoint is hit. Multiple lines of Python will be wrapped in a \
+generated function, and a call to the function will be attached to the watchpoint."
+ R"(
+
+This auto-generated function is passed in three arguments:
+
+ frame: an lldb.SBFrame object for the frame which hit the watchpoint.
+
+ wp: the watchpoint that was hit.
+
+)"
+ "When specifying a python function with the --python-function option, you need \
+to supply the function name prepended by the module name:"
+ R"(
+
+ --python-function myutils.watchpoint_callback
+
+The function itself must have the following prototype:
+
+def watchpoint_callback(frame, wp):
+ # Your code goes here
+
+)"
+ "The arguments are the same as the arguments passed to generated functions as \
+described above. Note that the global variable 'lldb.frame' will NOT be updated when \
+this function is called, so be sure to use the 'frame' argument. The 'frame' argument \
+can get you to the thread via frame.GetThread(), the thread can get you to the \
+process via thread.GetProcess(), and the process can get you back to the target \
+via process.GetTarget()."
+ R"(
+
+)"
+ "Important Note: As Python code gets collected into functions, access to global \
+variables requires explicit scoping using the 'global' keyword. Be sure to use correct \
+Python syntax, including indentation, when entering Python watchpoint commands."
+ R"(
+
+Example Python one-line watchpoint command:
+
+(lldb) watchpoint command add -s python 1
+Enter your Python command(s). Type 'DONE' to end.
+> print "Hit this watchpoint!"
+> DONE
+
+As a convenience, this also works for a short Python one-liner:
+
+(lldb) watchpoint command add -s python 1 -o 'import time; print time.asctime()'
+(lldb) run
+Launching '.../a.out' (x86_64)
+(lldb) Fri Sep 10 12:17:45 2010
+Process 21778 Stopped
+* thread #1: tid = 0x2e03, 0x0000000100000de8 a.out`c + 7 at main.c:39, stop reason = watchpoint 1.1, queue = com.apple.main-thread
+ 36
+ 37 int c(int val)
+ 38 {
+ 39 -> return val + 3;
+ 40 }
+ 41
+ 42 int main (int argc, char const *argv[])
+
+Example multiple line Python watchpoint command, using function definition:
+
+(lldb) watchpoint command add -s python 1
+Enter your Python command(s). Type 'DONE' to end.
+> def watchpoint_output (wp_no):
+> out_string = "Hit watchpoint number " + repr (wp_no)
+> print out_string
+> return True
+> watchpoint_output (1)
+> DONE
+
+Example multiple line Python watchpoint command, using 'loose' Python:
+
+(lldb) watchpoint command add -s p 1
+Enter your Python command(s). Type 'DONE' to end.
+> global wp_count
+> wp_count = wp_count + 1
+> print "Hit this watchpoint " + repr(wp_count) + " times!"
+> DONE
+
+)"
+ "In this case, since there is a reference to a global variable, \
+'wp_count', you will also need to make sure 'wp_count' exists and is \
+initialized:"
+ R"(
+
+(lldb) script
+>>> wp_count = 0
+>>> quit()
+
+)"
+ "Final Note: A warning that no watchpoint command was generated when there \
+are no syntax errors may indicate that a function was declared but never called.");
+
+ CommandArgumentEntry arg;
+ CommandArgumentData wp_id_arg;
+
+ // Define the first (and only) variant of this arg.
+ wp_id_arg.arg_type = eArgTypeWatchpointID;
+ wp_id_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(wp_id_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectWatchpointCommandAdd() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
+ StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
+ if (output_sp && interactive) {
+ output_sp->PutCString(
+ "Enter your debugger command(s). Type 'DONE' to end.\n");
+ output_sp->Flush();
+ }
+ }
+
+ void IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &line) override {
+ io_handler.SetIsDone(true);
+
+ // The WatchpointOptions object is owned by the watchpoint or watchpoint
+ // location
+ WatchpointOptions *wp_options =
+ (WatchpointOptions *)io_handler.GetUserData();
+ if (wp_options) {
+ std::unique_ptr<WatchpointOptions::CommandData> data_up(
+ new WatchpointOptions::CommandData());
+ if (data_up) {
+ data_up->user_source.SplitIntoLines(line);
+ auto baton_sp = std::make_shared<WatchpointOptions::CommandBaton>(
+ std::move(data_up));
+ wp_options->SetCallback(WatchpointOptionsCallbackFunction, baton_sp);
+ }
+ }
+ }
+
+ void CollectDataForWatchpointCommandCallback(WatchpointOptions *wp_options,
+ CommandReturnObject &result) {
+ m_interpreter.GetLLDBCommandsFromIOHandler(
+ "> ", // Prompt
+ *this, // IOHandlerDelegate
+ wp_options); // Baton for the "io_handler" that will be passed back into
+ // our IOHandlerDelegate functions
+ }
+
+ /// Set a one-liner as the callback for the watchpoint.
+ void SetWatchpointCommandCallback(WatchpointOptions *wp_options,
+ const char *oneliner) {
+ std::unique_ptr<WatchpointOptions::CommandData> data_up(
+ new WatchpointOptions::CommandData());
+
+ // It's necessary to set both user_source and script_source to the
+ // oneliner. The former is used to generate callback description (as in
+ // watchpoint command list) while the latter is used for Python to
+ // interpret during the actual callback.
+ data_up->user_source.AppendString(oneliner);
+ data_up->script_source.assign(oneliner);
+ data_up->stop_on_error = m_options.m_stop_on_error;
+
+ auto baton_sp =
+ std::make_shared<WatchpointOptions::CommandBaton>(std::move(data_up));
+ wp_options->SetCallback(WatchpointOptionsCallbackFunction, baton_sp);
+ }
+
+ static bool
+ WatchpointOptionsCallbackFunction(void *baton,
+ StoppointCallbackContext *context,
+ lldb::user_id_t watch_id) {
+ bool ret_value = true;
+ if (baton == nullptr)
+ return true;
+
+ WatchpointOptions::CommandData *data =
+ (WatchpointOptions::CommandData *)baton;
+ StringList &commands = data->user_source;
+
+ if (commands.GetSize() > 0) {
+ ExecutionContext exe_ctx(context->exe_ctx_ref);
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ Debugger &debugger = target->GetDebugger();
+ CommandReturnObject result(debugger.GetUseColor());
+
+ // Rig up the results secondary output stream to the debugger's, so the
+ // output will come out synchronously if the debugger is set up that
+ // way.
+ StreamSP output_stream(debugger.GetAsyncOutputStream());
+ StreamSP error_stream(debugger.GetAsyncErrorStream());
+ result.SetImmediateOutputStream(output_stream);
+ result.SetImmediateErrorStream(error_stream);
+
+ CommandInterpreterRunOptions options;
+ options.SetStopOnContinue(true);
+ options.SetStopOnError(data->stop_on_error);
+ options.SetEchoCommands(false);
+ options.SetPrintResults(true);
+ options.SetPrintErrors(true);
+ options.SetAddToHistory(false);
+
+ debugger.GetCommandInterpreter().HandleCommands(commands, exe_ctx,
+ options, result);
+ result.GetImmediateOutputStream()->Flush();
+ result.GetImmediateErrorStream()->Flush();
+ }
+ }
+ return ret_value;
+ }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() = default;
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'o':
+ m_use_one_liner = true;
+ m_one_liner = std::string(option_arg);
+ break;
+
+ case 's':
+ m_script_language = (lldb::ScriptLanguage)OptionArgParser::ToOptionEnum(
+ option_arg, GetDefinitions()[option_idx].enum_values,
+ eScriptLanguageNone, error);
+
+ switch (m_script_language) {
+ case eScriptLanguagePython:
+ case eScriptLanguageLua:
+ m_use_script_language = true;
+ break;
+ case eScriptLanguageNone:
+ case eScriptLanguageUnknown:
+ m_use_script_language = false;
+ break;
+ }
+ break;
+
+ case 'e': {
+ bool success = false;
+ m_stop_on_error =
+ OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "invalid value for stop-on-error: \"%s\"",
+ option_arg.str().c_str());
+ } break;
+
+ case 'F':
+ m_use_one_liner = false;
+ m_function_name.assign(std::string(option_arg));
+ break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_use_commands = true;
+ m_use_script_language = false;
+ m_script_language = eScriptLanguageNone;
+
+ m_use_one_liner = false;
+ m_stop_on_error = true;
+ m_one_liner.clear();
+ m_function_name.clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_watchpoint_command_add_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ bool m_use_commands = false;
+ bool m_use_script_language = false;
+ lldb::ScriptLanguage m_script_language = eScriptLanguageNone;
+
+ // Instance variables to hold the values for one_liner options.
+ bool m_use_one_liner = false;
+ std::string m_one_liner;
+ bool m_stop_on_error;
+ std::string m_function_name;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+
+ const WatchpointList &watchpoints = target->GetWatchpointList();
+ size_t num_watchpoints = watchpoints.GetSize();
+
+ if (num_watchpoints == 0) {
+ result.AppendError("No watchpoints exist to have commands added");
+ return false;
+ }
+
+ if (!m_options.m_function_name.empty()) {
+ if (!m_options.m_use_script_language) {
+ m_options.m_script_language = GetDebugger().GetScriptLanguage();
+ m_options.m_use_script_language = true;
+ }
+ }
+
+ std::vector<uint32_t> valid_wp_ids;
+ if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command,
+ valid_wp_ids)) {
+ result.AppendError("Invalid watchpoints specification.");
+ return false;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ const size_t count = valid_wp_ids.size();
+ for (size_t i = 0; i < count; ++i) {
+ uint32_t cur_wp_id = valid_wp_ids.at(i);
+ if (cur_wp_id != LLDB_INVALID_WATCH_ID) {
+ Watchpoint *wp = target->GetWatchpointList().FindByID(cur_wp_id).get();
+ // Sanity check wp first.
+ if (wp == nullptr)
+ continue;
+
+ WatchpointOptions *wp_options = wp->GetOptions();
+ // Skip this watchpoint if wp_options is not good.
+ if (wp_options == nullptr)
+ continue;
+
+ // If we are using script language, get the script interpreter in order
+ // to set or collect command callback. Otherwise, call the methods
+ // associated with this object.
+ if (m_options.m_use_script_language) {
+ ScriptInterpreter *script_interp = GetDebugger().GetScriptInterpreter(
+ /*can_create=*/true, m_options.m_script_language);
+ // Special handling for one-liner specified inline.
+ if (m_options.m_use_one_liner) {
+ script_interp->SetWatchpointCommandCallback(
+ wp_options, m_options.m_one_liner.c_str());
+ }
+ // Special handling for using a Python function by name instead of
+ // extending the watchpoint callback data structures, we just
+ // automatize what the user would do manually: make their watchpoint
+ // command be a function call
+ else if (!m_options.m_function_name.empty()) {
+ std::string oneliner(m_options.m_function_name);
+ oneliner += "(frame, wp, internal_dict)";
+ script_interp->SetWatchpointCommandCallback(
+ wp_options, oneliner.c_str());
+ } else {
+ script_interp->CollectDataForWatchpointCommandCallback(wp_options,
+ result);
+ }
+ } else {
+ // Special handling for one-liner specified inline.
+ if (m_options.m_use_one_liner)
+ SetWatchpointCommandCallback(wp_options,
+ m_options.m_one_liner.c_str());
+ else
+ CollectDataForWatchpointCommandCallback(wp_options, result);
+ }
+ }
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectWatchpointCommandDelete
+
+class CommandObjectWatchpointCommandDelete : public CommandObjectParsed {
+public:
+ CommandObjectWatchpointCommandDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "delete",
+ "Delete the set of commands from a watchpoint.",
+ nullptr, eCommandRequiresTarget) {
+ CommandArgumentEntry arg;
+ CommandArgumentData wp_id_arg;
+
+ // Define the first (and only) variant of this arg.
+ wp_id_arg.arg_type = eArgTypeWatchpointID;
+ wp_id_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(wp_id_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectWatchpointCommandDelete() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+
+ const WatchpointList &watchpoints = target->GetWatchpointList();
+ size_t num_watchpoints = watchpoints.GetSize();
+
+ if (num_watchpoints == 0) {
+ result.AppendError("No watchpoints exist to have commands deleted");
+ return false;
+ }
+
+ if (command.GetArgumentCount() == 0) {
+ result.AppendError(
+ "No watchpoint specified from which to delete the commands");
+ return false;
+ }
+
+ std::vector<uint32_t> valid_wp_ids;
+ if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command,
+ valid_wp_ids)) {
+ result.AppendError("Invalid watchpoints specification.");
+ return false;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ const size_t count = valid_wp_ids.size();
+ for (size_t i = 0; i < count; ++i) {
+ uint32_t cur_wp_id = valid_wp_ids.at(i);
+ if (cur_wp_id != LLDB_INVALID_WATCH_ID) {
+ Watchpoint *wp = target->GetWatchpointList().FindByID(cur_wp_id).get();
+ if (wp)
+ wp->ClearCallback();
+ } else {
+ result.AppendErrorWithFormat("Invalid watchpoint ID: %u.\n", cur_wp_id);
+ return false;
+ }
+ }
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectWatchpointCommandList
+
+class CommandObjectWatchpointCommandList : public CommandObjectParsed {
+public:
+ CommandObjectWatchpointCommandList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "list",
+ "List the script or set of commands to be executed "
+ "when the watchpoint is hit.",
+ nullptr, eCommandRequiresTarget) {
+ CommandArgumentEntry arg;
+ CommandArgumentData wp_id_arg;
+
+ // Define the first (and only) variant of this arg.
+ wp_id_arg.arg_type = eArgTypeWatchpointID;
+ wp_id_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(wp_id_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectWatchpointCommandList() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+
+ const WatchpointList &watchpoints = target->GetWatchpointList();
+ size_t num_watchpoints = watchpoints.GetSize();
+
+ if (num_watchpoints == 0) {
+ result.AppendError("No watchpoints exist for which to list commands");
+ return false;
+ }
+
+ if (command.GetArgumentCount() == 0) {
+ result.AppendError(
+ "No watchpoint specified for which to list the commands");
+ return false;
+ }
+
+ std::vector<uint32_t> valid_wp_ids;
+ if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command,
+ valid_wp_ids)) {
+ result.AppendError("Invalid watchpoints specification.");
+ return false;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ const size_t count = valid_wp_ids.size();
+ for (size_t i = 0; i < count; ++i) {
+ uint32_t cur_wp_id = valid_wp_ids.at(i);
+ if (cur_wp_id != LLDB_INVALID_WATCH_ID) {
+ Watchpoint *wp = target->GetWatchpointList().FindByID(cur_wp_id).get();
+
+ if (wp) {
+ const WatchpointOptions *wp_options = wp->GetOptions();
+ if (wp_options) {
+ // Get the callback baton associated with the current watchpoint.
+ const Baton *baton = wp_options->GetBaton();
+ if (baton) {
+ result.GetOutputStream().Printf("Watchpoint %u:\n", cur_wp_id);
+ baton->GetDescription(result.GetOutputStream().AsRawOstream(),
+ eDescriptionLevelFull,
+ result.GetOutputStream().GetIndentLevel() +
+ 2);
+ } else {
+ result.AppendMessageWithFormat(
+ "Watchpoint %u does not have an associated command.\n",
+ cur_wp_id);
+ }
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("Invalid watchpoint ID: %u.\n",
+ cur_wp_id);
+ }
+ }
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectWatchpointCommand
+
+CommandObjectWatchpointCommand::CommandObjectWatchpointCommand(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "command",
+ "Commands for adding, removing and examining LLDB commands "
+ "executed when the watchpoint is hit (watchpoint 'commands').",
+ "command <sub-command> [<sub-command-options>] <watchpoint-id>") {
+ CommandObjectSP add_command_object(
+ new CommandObjectWatchpointCommandAdd(interpreter));
+ CommandObjectSP delete_command_object(
+ new CommandObjectWatchpointCommandDelete(interpreter));
+ CommandObjectSP list_command_object(
+ new CommandObjectWatchpointCommandList(interpreter));
+
+ add_command_object->SetCommandName("watchpoint command add");
+ delete_command_object->SetCommandName("watchpoint command delete");
+ list_command_object->SetCommandName("watchpoint command list");
+
+ LoadSubCommand("add", add_command_object);
+ LoadSubCommand("delete", delete_command_object);
+ LoadSubCommand("list", list_command_object);
+}
+
+CommandObjectWatchpointCommand::~CommandObjectWatchpointCommand() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpointCommand.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpointCommand.h
new file mode 100644
index 000000000000..b3cb70d08985
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpointCommand.h
@@ -0,0 +1,27 @@
+//===-- CommandObjectWatchpointCommand.h ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTWATCHPOINTCOMMAND_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTWATCHPOINTCOMMAND_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+// CommandObjectMultiwordWatchpoint
+
+class CommandObjectWatchpointCommand : public CommandObjectMultiword {
+public:
+ CommandObjectWatchpointCommand(CommandInterpreter &interpreter);
+
+ ~CommandObjectWatchpointCommand() override;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTWATCHPOINTCOMMAND_H
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandOptionsProcessLaunch.cpp b/contrib/llvm-project/lldb/source/Commands/CommandOptionsProcessLaunch.cpp
new file mode 100644
index 000000000000..a618796156a6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandOptionsProcessLaunch.cpp
@@ -0,0 +1,147 @@
+//===-- CommandOptionsProcessLaunch.cpp -----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandOptionsProcessLaunch.h"
+
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandCompletions.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Target.h"
+
+#include "llvm/ADT/ArrayRef.h"
+
+using namespace llvm;
+using namespace lldb;
+using namespace lldb_private;
+
+#define LLDB_OPTIONS_process_launch
+#include "CommandOptions.inc"
+
+Status CommandOptionsProcessLaunch::SetOptionValue(
+ uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+ const int short_option = g_process_launch_options[option_idx].short_option;
+
+ switch (short_option) {
+ case 's': // Stop at program entry point
+ launch_info.GetFlags().Set(eLaunchFlagStopAtEntry);
+ break;
+
+ case 'i': // STDIN for read only
+ {
+ FileAction action;
+ if (action.Open(STDIN_FILENO, FileSpec(option_arg), true, false))
+ launch_info.AppendFileAction(action);
+ break;
+ }
+
+ case 'o': // Open STDOUT for write only
+ {
+ FileAction action;
+ if (action.Open(STDOUT_FILENO, FileSpec(option_arg), false, true))
+ launch_info.AppendFileAction(action);
+ break;
+ }
+
+ case 'e': // STDERR for write only
+ {
+ FileAction action;
+ if (action.Open(STDERR_FILENO, FileSpec(option_arg), false, true))
+ launch_info.AppendFileAction(action);
+ break;
+ }
+
+ case 'P': // Process plug-in name
+ launch_info.SetProcessPluginName(option_arg);
+ break;
+
+ case 'n': // Disable STDIO
+ {
+ FileAction action;
+ const FileSpec dev_null(FileSystem::DEV_NULL);
+ if (action.Open(STDIN_FILENO, dev_null, true, false))
+ launch_info.AppendFileAction(action);
+ if (action.Open(STDOUT_FILENO, dev_null, false, true))
+ launch_info.AppendFileAction(action);
+ if (action.Open(STDERR_FILENO, dev_null, false, true))
+ launch_info.AppendFileAction(action);
+ break;
+ }
+
+ case 'w':
+ launch_info.SetWorkingDirectory(FileSpec(option_arg));
+ break;
+
+ case 't': // Open process in new terminal window
+ launch_info.GetFlags().Set(eLaunchFlagLaunchInTTY);
+ break;
+
+ case 'a': {
+ TargetSP target_sp =
+ execution_context ? execution_context->GetTargetSP() : TargetSP();
+ PlatformSP platform_sp =
+ target_sp ? target_sp->GetPlatform() : PlatformSP();
+ launch_info.GetArchitecture() =
+ Platform::GetAugmentedArchSpec(platform_sp.get(), option_arg);
+ } break;
+
+ case 'A': // Disable ASLR.
+ {
+ bool success;
+ const bool disable_aslr_arg =
+ OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (success)
+ disable_aslr = disable_aslr_arg ? eLazyBoolYes : eLazyBoolNo;
+ else
+ error.SetErrorStringWithFormat(
+ "Invalid boolean value for disable-aslr option: '%s'",
+ option_arg.empty() ? "<null>" : option_arg.str().c_str());
+ break;
+ }
+
+ case 'X': // shell expand args.
+ {
+ bool success;
+ const bool expand_args =
+ OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (success)
+ launch_info.SetShellExpandArguments(expand_args);
+ else
+ error.SetErrorStringWithFormat(
+ "Invalid boolean value for shell-expand-args option: '%s'",
+ option_arg.empty() ? "<null>" : option_arg.str().c_str());
+ break;
+ }
+
+ case 'c':
+ if (!option_arg.empty())
+ launch_info.SetShell(FileSpec(option_arg));
+ else
+ launch_info.SetShell(HostInfo::GetDefaultShell());
+ break;
+
+ case 'E':
+ launch_info.GetEnvironment().insert(option_arg);
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized short option character '%c'",
+ short_option);
+ break;
+ }
+ return error;
+}
+
+llvm::ArrayRef<OptionDefinition> CommandOptionsProcessLaunch::GetDefinitions() {
+ return llvm::makeArrayRef(g_process_launch_options);
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandOptionsProcessLaunch.h b/contrib/llvm-project/lldb/source/Commands/CommandOptionsProcessLaunch.h
new file mode 100644
index 000000000000..7ab7fabe1050
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandOptionsProcessLaunch.h
@@ -0,0 +1,49 @@
+//===-- CommandOptionsProcessLaunch.h ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOPTIONSPROCESSLAUNCH_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOPTIONSPROCESSLAUNCH_H
+
+#include "lldb/Host/ProcessLaunchInfo.h"
+#include "lldb/Interpreter/Options.h"
+
+namespace lldb_private {
+
+// CommandOptionsProcessLaunch
+
+class CommandOptionsProcessLaunch : public lldb_private::OptionGroup {
+public:
+ CommandOptionsProcessLaunch() {
+ // Keep default values of all options in one place: OptionParsingStarting
+ // ()
+ OptionParsingStarting(nullptr);
+ }
+
+ ~CommandOptionsProcessLaunch() override = default;
+
+ lldb_private::Status
+ SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ lldb_private::ExecutionContext *execution_context) override;
+
+ void OptionParsingStarting(
+ lldb_private::ExecutionContext *execution_context) override {
+ launch_info.Clear();
+ disable_aslr = lldb_private::eLazyBoolCalculate;
+ }
+
+ llvm::ArrayRef<lldb_private::OptionDefinition> GetDefinitions() override;
+
+ // Instance variables to hold the values for command options.
+
+ lldb_private::ProcessLaunchInfo launch_info;
+ lldb_private::LazyBool disable_aslr;
+}; // CommandOptionsProcessLaunch
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOPTIONSPROCESSLAUNCH_H
diff --git a/contrib/llvm-project/lldb/source/Commands/Options.td b/contrib/llvm-project/lldb/source/Commands/Options.td
new file mode 100644
index 000000000000..7981917fd8b5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/Options.td
@@ -0,0 +1,1377 @@
+include "OptionsBase.td"
+
+let Command = "target modules dump symtab" in {
+ def tm_sort : Option<"sort", "s">, Group<1>,
+ Desc<"Supply a sort order when dumping the symbol table.">,
+ EnumArg<"SortOrder", "OptionEnumValues(g_sort_option_enumeration)">;
+ def tm_smn : Option<"show-mangled-names", "m">, Group<1>,
+ Desc<"Do not demangle symbol names before showing them.">;
+}
+
+let Command = "help" in {
+ def help_hide_aliases : Option<"hide-aliases", "a">,
+ Desc<"Hide aliases in the command list.">;
+ def help_hide_user : Option<"hide-user-commands", "u">,
+ Desc<"Hide user-defined commands from the list.">;
+ def help_show_hidden : Option<"show-hidden-commands", "h">,
+ Desc<"Include commands prefixed with an underscore.">;
+}
+
+let Command = "settings set" in {
+ def setset_global : Option<"global", "g">,
+ Desc<"Apply the new value to the global default value.">;
+ def setset_force : Option<"force", "f">,
+ Desc<"Force an empty value to be accepted as the default.">;
+ def setset_exists : Option<"exists", "e">,
+ Desc<"Set the setting if it exists, but do not cause the command to raise "
+ "an error if it does not exist.">;
+}
+
+let Command = "settings write" in {
+ def setwrite_file : Option<"file", "f">, Required, Arg<"Filename">,
+ Completion<"DiskFile">,
+ Desc<"The file into which to write the settings.">;
+ def setwrite_append : Option<"append", "a">,
+ Desc<"Append to saved settings file if it exists.">;
+}
+
+let Command = "settings read" in {
+ def setread_file : Option<"file", "f">, Required, Arg<"Filename">,
+ Completion<"DiskFile">,
+ Desc<"The file from which to read the settings.">;
+}
+
+let Command = "settings clear" in {
+ def setclear_all : Option<"all", "a">,
+ Desc<"Clear all settings.">;
+}
+
+let Command = "breakpoint list" in {
+ // FIXME: We need to add an "internal" command, and then add this sort of
+ // thing to it. But I need to see it for now, and don't want to wait.
+ def blist_internal : Option<"internal", "i">,
+ Desc<"Show debugger internal breakpoints">;
+ def blist_brief : Option<"brief", "b">, Group<1>,
+ Desc<"Give a brief description of the breakpoint (no location info).">;
+ def blist_full : Option<"full", "f">, Group<2>,
+ Desc<"Give a full description of the breakpoint and its locations.">;
+ def blist_verbose : Option<"verbose", "v">, Group<3>,
+ Desc<"Explain everything we know about the breakpoint (for debugging "
+ "debugger bugs).">;
+ def blist_dummy_bp : Option<"dummy-breakpoints", "D">,
+ Desc<"List Dummy breakpoints - i.e. breakpoints set before a file is "
+ "provided, which prime new targets.">;
+}
+
+let Command = "breakpoint modify" in {
+ def breakpoint_modify_ignore_count : Option<"ignore-count", "i">, Group<1>,
+ Arg<"Count">,
+ Desc<"Set the number of times this breakpoint is skipped before stopping.">;
+ def breakpoint_modify_one_shot : Option<"one-shot", "o">, Group<1>,
+ Arg<"Boolean">,
+ Desc<"The breakpoint is deleted the first time it stop causes a stop.">;
+ def breakpoint_modify_thread_index : Option<"thread-index", "x">, Group<1>,
+ Arg<"ThreadIndex">, Desc<"The breakpoint stops only for the thread whose "
+ "index matches this argument.">;
+ def breakpoint_modify_thread_id : Option<"thread-id", "t">, Group<1>,
+ Arg<"ThreadID">, Desc<"The breakpoint stops only for the thread whose TID "
+ "matches this argument. The token 'current' resolves to the current thread's ID.">;
+ def breakpoint_modify_thread_name : Option<"thread-name", "T">, Group<1>,
+ Arg<"ThreadName">, Desc<"The breakpoint stops only for the thread whose "
+ "thread name matches this argument.">;
+ def breakpoint_modify_queue_name : Option<"queue-name", "q">, Group<1>,
+ Arg<"QueueName">, Desc<"The breakpoint stops only for threads in the queue "
+ "whose name is given by this argument.">;
+ def breakpoint_modify_condition : Option<"condition", "c">, Group<1>,
+ Arg<"Expression">, Desc<"The breakpoint stops only if this condition "
+ "expression evaluates to true.">;
+ def breakpoint_modify_auto_continue : Option<"auto-continue", "G">, Group<1>,
+ Arg<"Boolean">,
+ Desc<"The breakpoint will auto-continue after running its commands.">;
+ def breakpoint_modify_enable : Option<"enable", "e">, Group<2>,
+ Desc<"Enable the breakpoint.">;
+ def breakpoint_modify_disable : Option<"disable", "d">, Group<3>,
+ Desc<"Disable the breakpoint.">;
+ def breakpoint_modify_command : Option<"command", "C">, Group<4>,
+ Arg<"Command">,
+ Desc<"A command to run when the breakpoint is hit, can be provided more "
+ "than once, the commands will be run in left-to-right order.">;
+}
+
+let Command = "breakpoint dummy" in {
+ def breakpoint_dummy_options_dummy_breakpoints :
+ Option<"dummy-breakpoints", "D">, Group<1>,
+ Desc<"Act on Dummy breakpoints - i.e. breakpoints set before a file is "
+ "provided, which prime new targets.">;
+}
+
+let Command = "breakpoint set" in {
+ def breakpoint_set_shlib : Option<"shlib", "s">, Arg<"ShlibName">,
+ Completion<"Module">, Groups<[1,2,3,4,5,6,7,8,9,11,12]>, // *not* in group 10
+ Desc<"Set the breakpoint only in this shared library. Can repeat this "
+ "option multiple times to specify multiple shared libraries.">;
+ def breakpoint_set_hardware : Option<"hardware", "H">,
+ Desc<"Require the breakpoint to use hardware breakpoints.">;
+ def breakpoint_set_file : Option<"file", "f">, Arg<"Filename">,
+ Completion<"SourceFile">, Groups<[1,3,4,5,6,7,8,9,11]>,
+ Desc<"Specifies the source file in which to set this breakpoint. Note, by "
+ "default lldb only looks for files that are #included if they use the "
+ "standard include file extensions. To set breakpoints on .c/.cpp/.m/.mm "
+ "files that are #included, set target.inline-breakpoint-strategy to "
+ "\"always\".">;
+ def breakpoint_set_line : Option<"line", "l">, Group<1>, Arg<"LineNum">,
+ Required,
+ Desc<"Specifies the line number on which to set this breakpoint.">;
+ def breakpoint_set_column : Option<"column", "u">, Group<1>, Arg<"ColumnNum">,
+ Desc<"Specifies the column number on which to set this breakpoint.">;
+ def breakpoint_set_address : Option<"address", "a">, Group<2>,
+ Arg<"AddressOrExpression">, Required,
+ Desc<"Set the breakpoint at the specified address. If the address maps "
+ "uniquely to a particular binary, then the address will be converted to "
+ "a \"file\"address, so that the breakpoint will track that binary+offset "
+ "no matter where the binary eventually loads. Alternately, if you also "
+ "specify the module - with the -s option - then the address will be "
+ "treated as a file address in that module, and resolved accordingly. "
+ "Again, this will allow lldb to track that offset on subsequent reloads. "
+ "The module need not have been loaded at the time you specify this "
+ "breakpoint, and will get resolved when the module is loaded.">;
+ def breakpoint_set_name : Option<"name", "n">, Group<3>, Arg<"FunctionName">,
+ Completion<"Symbol">, Required,
+ Desc<"Set the breakpoint by function name. Can be repeated multiple times "
+ "to make one breakpoint for multiple names.">;
+ def breakpoint_set_source_regexp_function :
+ Option<"source-regexp-function", "X">, Group<9>, Arg<"FunctionName">,
+ Completion<"Symbol">,
+ Desc<"When used with '-p' limits the source regex to source contained in "
+ "the named functions. Can be repeated multiple times.">;
+ def breakpoint_set_fullname : Option<"fullname", "F">, Group<4>,
+ Arg<"FullName">, Required, Completion<"Symbol">,
+ Desc<"Set the breakpoint by fully qualified function names. For C++ this "
+ "means namespaces and all arguments, and for Objective-C this means a full "
+ "function prototype with class and selector. Can be repeated multiple times"
+ " to make one breakpoint for multiple names.">;
+ def breakpoint_set_selector : Option<"selector", "S">, Group<5>,
+ Arg<"Selector">, Required,
+ Desc<"Set the breakpoint by Objective-C selector name. Can be repeated "
+ "multiple times to make one breakpoint for multiple Selectors.">;
+ def breakpoint_set_method : Option<"method", "M">, Group<6>, Arg<"Method">,
+ Required, Desc<"Set the breakpoint by C++ method names. Can be repeated "
+ "multiple times to make one breakpoint for multiple methods.">;
+ def breakpoint_set_func_regex : Option<"func-regex", "r">, Group<7>,
+ Arg<"RegularExpression">, Required, Desc<"Set the breakpoint by function "
+ "name, evaluating a regular-expression to find the function name(s).">;
+ def breakpoint_set_basename : Option<"basename", "b">, Group<8>,
+ Arg<"FunctionName">, Required, Completion<"Symbol">,
+ Desc<"Set the breakpoint by function basename (C++ namespaces and arguments"
+ " will be ignored). Can be repeated multiple times to make one breakpoint "
+ "for multiple symbols.">;
+ def breakpoint_set_source_pattern_regexp :
+ Option<"source-pattern-regexp", "p">, Group<9>, Arg<"RegularExpression">,
+ Required, Desc<"Set the breakpoint by specifying a regular expression which"
+ " is matched against the source text in a source file or files specified "
+ "with the -f can be specified more than once. If no source files "
+ "are specified, uses the current \"default source file\". If you want to "
+ "match against all source files, pass the \"--all-files\" option.">;
+ def breakpoint_set_all_files : Option<"all-files", "A">, Group<9>,
+ Desc<"All files are searched for source pattern matches.">;
+ def breakpoint_set_language_exception : Option<"language-exception", "E">,
+ Group<10>, Arg<"Language">, Required,
+ Desc<"Set the breakpoint on exceptions thrown by the specified language "
+ "(without options, on throw but not catch.)">;
+ def breakpoint_set_on_throw : Option<"on-throw", "w">, Group<10>,
+ Arg<"Boolean">, Desc<"Set the breakpoint on exception throW.">;
+ def breakpoint_set_on_catch : Option<"on-catch", "h">, Group<10>,
+ Arg<"Boolean">, Desc<"Set the breakpoint on exception catcH.">;
+ def breakpoint_set_language : Option<"language", "L">, GroupRange<3, 8>,
+ Arg<"Language">,
+ Desc<"Specifies the Language to use when interpreting the breakpoint's "
+ "expression (note: currently only implemented for setting breakpoints on "
+ "identifiers). If not set the target.language setting is used.">;
+ def breakpoint_set_skip_prologue : Option<"skip-prologue", "K">,
+ Arg<"Boolean">, Groups<[1,3,4,5,6,7,8,12]>,
+ Desc<"sKip the prologue if the breakpoint is at the beginning of a "
+ "function. If not set the target.skip-prologue setting is used.">;
+ def breakpoint_set_breakpoint_name : Option<"breakpoint-name", "N">,
+ Arg<"BreakpointName">,
+ Desc<"Adds this to the list of names for this breakpoint.">;
+ def breakpoint_set_address_slide : Option<"address-slide", "R">,
+ Arg<"Address">, Groups<[1,3,4,5,6,7,8,12]>,
+ Desc<"Add the specified offset to whatever address(es) the breakpoint "
+ "resolves to. At present this applies the offset directly as given, and "
+ "doesn't try to align it to instruction boundaries.">;
+ def breakpoint_set_move_to_nearest_code : Option<"move-to-nearest-code", "m">,
+ Groups<[1,9,12]>, Arg<"Boolean">,
+ Desc<"Move breakpoints to nearest code. If not set the "
+ "target.move-to-nearest-code setting is used.">;
+ def breakpoint_set_file_colon_line : Option<"joint-specifier", "y">, Group<12>, Arg<"FileLineColumn">,
+ Required, Completion<"SourceFile">,
+ Desc<"A specifier in the form filename:line[:column] for setting file & line breakpoints.">;
+ /* Don't add this option till it actually does something useful...
+ def breakpoint_set_exception_typename : Option<"exception-typename", "O">,
+ Arg<"TypeName">, Desc<"The breakpoint will only stop if an "
+ "exception Object of this type is thrown. Can be repeated multiple times "
+ "to stop for multiple object types">;
+ */
+}
+
+let Command = "breakpoint clear" in {
+ def breakpoint_clear_file : Option<"file", "f">, Group<1>, Arg<"Filename">,
+ Completion<"SourceFile">,
+ Desc<"Specify the breakpoint by source location in this particular file.">;
+ def breakpoint_clear_line : Option<"line", "l">, Group<1>, Arg<"LineNum">,
+ Required,
+ Desc<"Specify the breakpoint by source location at this particular line.">;
+}
+
+let Command = "breakpoint delete" in {
+ def breakpoint_delete_force : Option<"force", "f">, Group<1>,
+ Desc<"Delete all breakpoints without querying for confirmation.">;
+ def breakpoint_delete_dummy_breakpoints : Option<"dummy-breakpoints", "D">,
+ Group<1>, Desc<"Delete Dummy breakpoints - i.e. breakpoints set before a "
+ "file is provided, which prime new targets.">;
+ def breakpoint_delete_disabled : Option<"disabled", "d">, Group<1>,
+ Desc<"Delete all breakpoints which are currently disabled. When using the disabled option "
+ "any breakpoints listed on the command line are EXCLUDED from deletion.">;
+}
+
+let Command = "breakpoint name" in {
+ def breakpoint_name_name : Option<"name", "N">, Group<1>,
+ Arg<"BreakpointName">, Desc<"Specifies a breakpoint name to use.">;
+ def breakpoint_name_breakpoint_id : Option<"breakpoint-id", "B">, Group<2>,
+ Arg<"BreakpointID">, Desc<"Specify a breakpoint ID to use.">;
+ def breakpoint_name_dummy_breakpoints : Option<"dummy-breakpoints", "D">,
+ Group<3>, Desc<"Operate on Dummy breakpoints - i.e. breakpoints set before "
+ "a file is provided, which prime new targets.">;
+ def breakpoint_name_help_string : Option<"help-string", "H">, Group<4>,
+ Arg<"None">, Desc<"A help string describing the purpose of this name.">;
+}
+
+let Command = "breakpoint access" in {
+ def breakpoint_access_allow_list : Option<"allow-list", "L">, Group<1>,
+ Arg<"Boolean">, Desc<"Determines whether the breakpoint will show up in "
+ "break list if not referred to explicitly.">;
+ def breakpoint_access_allow_disable : Option<"allow-disable", "A">, Group<2>,
+ Arg<"Boolean">, Desc<"Determines whether the breakpoint can be disabled by "
+ "name or when all breakpoints are disabled.">;
+ def breakpoint_access_allow_delete : Option<"allow-delete", "D">, Group<3>,
+ Arg<"Boolean">, Desc<"Determines whether the breakpoint can be deleted by "
+ "name or when all breakpoints are deleted.">;
+}
+
+let Command = "breakpoint read" in {
+ def breakpoint_read_file : Option<"file", "f">, Arg<"Filename">, Required,
+ Completion<"DiskFile">,
+ Desc<"The file from which to read the breakpoints.">;
+ def breakpoint_read_breakpoint_name : Option<"breakpoint-name", "N">,
+ Arg<"BreakpointName">, Desc<"Only read in breakpoints with this name.">;
+}
+
+let Command = "breakpoint write" in {
+ def breakpoint_write_file : Option<"file", "f">, Arg<"Filename">, Required,
+ Completion<"DiskFile">,
+ Desc<"The file into which to write the breakpoints.">;
+ def breakpoint_write_append : Option<"append", "a">,
+ Desc<"Append to saved breakpoints file if it exists.">;
+}
+
+let Command = "breakpoint command add" in {
+ def breakpoint_add_one_liner : Option<"one-liner", "o">, Group<1>,
+ Arg<"OneLiner">, Desc<"Specify a one-line breakpoint command inline. Be "
+ "sure to surround it with quotes.">;
+ def breakpoint_add_stop_on_error : Option<"stop-on-error", "e">,
+ Arg<"Boolean">, Desc<"Specify whether breakpoint command execution should "
+ "terminate on error.">;
+ def breakpoint_add_script_type : Option<"script-type", "s">,
+ EnumArg<"None", "ScriptOptionEnum()">,
+ Desc<"Specify the language for the commands - if none is specified, the "
+ "lldb command interpreter will be used.">;
+ def breakpoint_add_dummy_breakpoints : Option<"dummy-breakpoints", "D">,
+ Desc<"Sets Dummy breakpoints - i.e. breakpoints set before a file is "
+ "provided, which prime new targets.">;
+}
+
+let Command = "breakpoint command delete" in {
+ def breakpoint_command_delete_dummy_breakpoints :
+ Option<"dummy-breakpoints", "D">, Group<1>,
+ Desc<"Delete commands from Dummy breakpoints - i.e. breakpoints set before "
+ "a file is provided, which prime new targets.">;
+}
+
+let Command = "disassemble" in {
+ def disassemble_options_bytes : Option<"bytes", "b">,
+ Desc<"Show opcode bytes when disassembling.">;
+ def disassemble_options_kind : Option<"kind", "k">,
+ Desc<"Show instruction control flow kind. Refer to the enum "
+ "`InstructionControlFlowKind` for a list of control flow kind. "
+ "As an important note, far jumps, far calls and far returns often indicate "
+ "calls to and from kernel.">;
+ def disassemble_options_context : Option<"context", "C">, Arg<"NumLines">,
+ Desc<"Number of context lines of source to show.">;
+ def disassemble_options_mixed : Option<"mixed", "m">,
+ Desc<"Enable mixed source and assembly display.">;
+ def disassemble_options_raw : Option<"raw", "r">,
+ Desc<"Print raw disassembly with no symbol information.">;
+ def disassemble_options_plugin : Option<"plugin", "P">, Arg<"Plugin">,
+ Desc<"Name of the disassembler plugin you want to use.">;
+ def disassemble_options_flavor : Option<"flavor", "F">,
+ Arg<"DisassemblyFlavor">, Desc<"Name of the disassembly flavor you want to "
+ "use. Currently the only valid options are default, and for Intel "
+ "architectures, att and intel.">;
+ def disassemble_options_arch : Option<"arch", "A">, Arg<"Architecture">,
+ Desc<"Specify the architecture to use from cross disassembly.">;
+ def disassemble_options_start_address : Option<"start-address", "s">,
+ Groups<[1,2]>, Arg<"AddressOrExpression">, Required,
+ Desc<"Address at which to start disassembling.">;
+ def disassemble_options_end_address : Option<"end-address", "e">, Group<1>,
+ Arg<"AddressOrExpression">, Desc<"Address at which to end disassembling.">;
+ def disassemble_options_count : Option<"count", "c">, Groups<[2,3,4,5,7]>,
+ Arg<"NumLines">, Desc<"Number of instructions to display.">;
+ def disassemble_options_name : Option<"name", "n">, Group<3>,
+ Arg<"FunctionName">, Completion<"Symbol">,
+ Desc<"Disassemble entire contents of the given function name.">;
+ def disassemble_options_frame : Option<"frame", "f">, Group<4>,
+ Desc<"Disassemble from the start of the current frame's function.">;
+ def disassemble_options_pc : Option<"pc", "p">, Group<5>,
+ Desc<"Disassemble around the current pc.">;
+ def disassemble_options_line : Option<"line", "l">, Group<6>,
+ Desc<"Disassemble the current frame's current source line instructions if "
+ "there is debug line table information, else disassemble around the pc.">;
+ def disassemble_options_address : Option<"address", "a">, Group<7>,
+ Arg<"AddressOrExpression">,
+ Desc<"Disassemble function containing this address.">;
+ def disassemble_options_force : Option<"force", "\\x01">, Groups<[2,3,4,5,7]>,
+ Desc<"Force disassembly of large functions.">;
+}
+
+let Command = "expression" in {
+ def expression_options_all_threads : Option<"all-threads", "a">,
+ Groups<[1,2]>, Arg<"Boolean">, Desc<"Should we run all threads if the "
+ "execution doesn't complete on one thread.">;
+ def expression_options_ignore_breakpoints : Option<"ignore-breakpoints", "i">,
+ Groups<[1,2]>, Arg<"Boolean">,
+ Desc<"Ignore breakpoint hits while running expressions">;
+ def expression_options_timeout : Option<"timeout", "t">, Groups<[1,2]>,
+ Arg<"UnsignedInteger">,
+ Desc<"Timeout value (in microseconds) for running the expression.">;
+ def expression_options_unwind_on_error : Option<"unwind-on-error", "u">,
+ Groups<[1,2]>, Arg<"Boolean">,
+ Desc<"Clean up program state if the expression causes a crash, or raises a "
+ "signal. Note, unlike gdb hitting a breakpoint is controlled by another "
+ "option (-i).">;
+ def expression_options_debug : Option<"debug", "g">, Groups<[1,2]>,
+ Desc<"When specified, debug the JIT code by setting a breakpoint on the "
+ "first instruction and forcing breakpoints to not be ignored (-i0) and no "
+ "unwinding to happen on error (-u0).">;
+ def expression_options_language : Option<"language", "l">, Groups<[1,2,3]>,
+ Arg<"Language">, Desc<"Specifies the Language to use when parsing the "
+ "expression. If not set the target.language setting is used.">;
+ def expression_options_apply_fixits : Option<"apply-fixits", "X">,
+ Groups<[1,2]>, Arg<"Language">, Desc<"If true, simple fix-it hints will be "
+ "automatically applied to the expression.">;
+ def expression_options_description_verbosity :
+ Option<"description-verbosity", "v">, Group<1>,
+ OptionalEnumArg<"DescriptionVerbosity", "DescriptionVerbosityTypes()">,
+ Desc<"How verbose should the output of this expression be, if the object "
+ "description is asked for.">;
+ def expression_options_top_level : Option<"top-level", "p">, Groups<[1,2]>,
+ Desc<"Interpret the expression as a complete translation unit, without "
+ "injecting it into the local context. Allows declaration of persistent, "
+ "top-level entities without a $ prefix.">;
+ def expression_options_allow_jit : Option<"allow-jit", "j">, Groups<[1,2]>,
+ Arg<"Boolean">,
+ Desc<"Controls whether the expression can fall back to being JITted if it's "
+ "not supported by the interpreter (defaults to true).">;
+}
+
+let Command = "frame diag" in {
+ def frame_diag_register : Option<"register", "r">, Group<1>,
+ Arg<"RegisterName">, Desc<"A register to diagnose.">;
+ def frame_diag_address : Option<"address", "a">, Group<1>, Arg<"Address">,
+ Desc<"An address to diagnose.">;
+ def frame_diag_offset : Option<"offset", "o">, Group<1>, Arg<"Offset">,
+ Desc<"An optional offset. Requires --register.">;
+}
+
+let Command = "frame select" in {
+ def frame_select_relative : Option<"relative", "r">, Group<1>, Arg<"Offset">,
+ Desc<"A relative frame index offset from the current frame index.">;
+}
+
+let Command = "frame recognizer add" in {
+ def frame_recognizer_shlib : Option<"shlib", "s">, Arg<"ShlibName">,
+ Completion<"Module">,
+ Desc<"Name of the module or shared library that this recognizer applies "
+ "to.">;
+ def frame_recognizer_function : Option<"function", "n">, Arg<"Name">,
+ Completion<"Symbol">,
+ Desc<"Name of the function that this recognizer applies to. "
+ "Can be specified more than once except if -x|--regex is provided.">;
+ def frame_recognizer_python_class : Option<"python-class", "l">, Group<2>,
+ Arg<"PythonClass">,
+ Desc<"Give the name of a Python class to use for this frame recognizer.">;
+ def frame_recognizer_regex : Option<"regex", "x">,
+ Desc<"Function name and module name are actually regular expressions.">;
+ def frame_recognizer_first_instruction_only : Option<"first-instruction-only", "f">, Arg<"Boolean">,
+ Desc<"If true, only apply this recognizer to frames whose PC currently points to the "
+ "first instruction of the specified function. If false, the recognizer "
+ "will always be applied, regardless of the current position within the specified function. The "
+ "implementer should keep in mind that some features, e.g. accessing function argument "
+ "values via $arg<N>, are not guaranteed to work reliably in this case, so extra care must "
+ "be taken to make the recognizer operate correctly. Defaults to true.">;
+}
+
+let Command = "history" in {
+ def history_count : Option<"count", "c">, Group<1>, Arg<"UnsignedInteger">,
+ Desc<"How many history commands to print.">;
+ def history_start_index : Option<"start-index", "s">, Group<1>,
+ Arg<"UnsignedInteger">, Desc<"Index at which to start printing history "
+ "commands (or end to mean tail mode).">;
+ def history_end_index : Option<"end-index", "e">, Group<1>,
+ Arg<"UnsignedInteger">,
+ Desc<"Index at which to stop printing history commands.">;
+ def history_clear : Option<"clear", "C">, Group<2>,
+ Desc<"Clears the current command history.">;
+}
+
+let Command = "log enable" in {
+ def log_file : Option<"file", "f">, Group<1>, Arg<"Filename">,
+ Desc<"Set the destination file to log to.">;
+ def log_handler : Option<"log-handler", "h">, Group<1>,
+ EnumArg<"LogHandler", "LogHandlerType()">, Desc<"Specify a log handler which determines where log messages are written.">;
+ def log_buffer_size : Option<"buffer", "b">, Group<1>, Arg<"UnsignedInteger">,
+ Desc<"Set the log to be buffered, using the specified buffer size, if supported by the log handler.">;
+ def log_verbose : Option<"verbose", "v">, Group<1>,
+ Desc<"Enable verbose logging.">;
+ def log_sequence : Option<"sequence", "s">, Group<1>,
+ Desc<"Prepend all log lines with an increasing integer sequence id.">;
+ def log_timestamp : Option<"timestamp", "T">, Group<1>,
+ Desc<"Prepend all log lines with a timestamp.">;
+ def log_pid_tid : Option<"pid-tid", "p">, Group<1>,
+ Desc<"Prepend all log lines with the process and thread ID that generates "
+ "the log line.">;
+ def log_thread_name : Option<"thread-name", "n">, Group<1>,
+ Desc<"Prepend all log lines with the thread name for the thread that "
+ "generates the log line.">;
+
+ def log_stack : Option<"stack", "S">, Group<1>,
+ Desc<"Append a stack backtrace to each log line.">;
+ def log_append : Option<"append", "a">, Group<1>,
+ Desc<"Append to the log file instead of overwriting.">;
+ def log_file_function : Option<"file-function", "F">, Group<1>,
+ Desc<"Prepend the names of files and function that generate the logs.">;
+}
+
+let Command = "log dump" in {
+ def log_dump_file : Option<"file", "f">, Group<1>, Arg<"Filename">,
+ Desc<"Set the destination file to dump to.">;
+}
+
+let Command = "reproducer dump" in {
+ def reproducer_provider : Option<"provider", "p">, Group<1>,
+ EnumArg<"None", "ReproducerProviderType()">,
+ Required, Desc<"The reproducer provider to dump.">;
+ def reproducer_file : Option<"file", "f">, Group<1>, Arg<"Filename">,
+ Desc<"The reproducer path. If a reproducer is replayed and no path is "
+ "provided, that reproducer is dumped.">;
+}
+
+let Command = "reproducer verify" in {
+ def reproducer_verify_file : Option<"file", "f">, Group<1>, Arg<"Filename">,
+ Desc<"The reproducer path. If a reproducer is replayed and no path is "
+ "provided, that reproducer is dumped.">;
+}
+
+let Command = "reproducer xcrash" in {
+ def reproducer_signal : Option<"signal", "s">, Group<1>,
+ EnumArg<"None", "ReproducerSignalType()">,
+ Required, Desc<"The signal to crash the debugger.">;
+}
+
+let Command = "memory read" in {
+ def memory_read_num_per_line : Option<"num-per-line", "l">, Group<1>,
+ Arg<"NumberPerLine">, Desc<"The number of items per line to display.">;
+ def memory_read_binary : Option<"binary", "b">, Group<2>,
+ Desc<"If true, memory will be saved as binary. If false, the memory is "
+ "saved save as an ASCII dump that uses the format, size, count and number "
+ "per line settings.">;
+ def memory_read_type : Option<"type", "t">, Groups<[3,4]>, Arg<"Name">,
+ Required, Desc<"The name of a type to view memory as.">;
+ def memory_read_language : Option<"language", "x">, Group<4>, Arg<"Language">,
+ Desc<"The language of the type to view memory as.">;
+ def memory_read_offset : Option<"offset", "E">, Group<3>, Arg<"Count">,
+ Desc<"How many elements of the specified type to skip before starting to "
+ "display data.">;
+ def memory_read_force : Option<"force", "r">, Groups<[1,2,3]>,
+ Desc<"Necessary if reading over target.max-memory-read-size bytes.">;
+}
+
+let Command = "memory find" in {
+ def memory_find_expression : Option<"expression", "e">, Group<1>,
+ Arg<"Expression">, Required,
+ Desc<"Evaluate an expression to obtain a byte pattern.">;
+ def memory_find_string : Option<"string", "s">, Group<2>, Arg<"Name">,
+ Required, Desc<"Use text to find a byte pattern.">;
+ def memory_find_count : Option<"count", "c">, Arg<"Count">,
+ Desc<"How many times to perform the search.">;
+ def memory_find_dump_offset : Option<"dump-offset", "o">, Arg<"Offset">,
+ Desc<"When dumping memory for a match, an offset from the match location to"
+ " start dumping from.">;
+}
+
+let Command = "memory write" in {
+ def memory_write_infile : Option<"infile", "i">, Group<1>, Arg<"Filename">,
+ Required, Desc<"Write memory using the contents of a file.">;
+ def memory_write_offset : Option<"offset", "o">, Group<1>, Arg<"Offset">,
+ Desc<"Start writing bytes from an offset within the input file.">;
+}
+
+let Command = "memory region" in {
+ def memory_region_all : Option<"all", "a">, Group<2>, Required,
+ Desc<"Show all memory regions. This is equivalent to starting from address "
+ "0 and repeating the command. Unmapped areas are included.">;
+}
+
+let Command = "memory tag write" in {
+ def memory_write_end_addr : Option<"end-addr", "e">, Group<1>,
+ Arg<"AddressOrExpression">, Desc<
+ "Set tags for start address to end-addr, repeating tags as needed"
+ " to cover the range. (instead of calculating the range from the"
+ " number of tags given)">;
+}
+
+let Command = "register read" in {
+ def register_read_alternate : Option<"alternate", "A">,
+ Desc<"Display register names using the alternate register name if there "
+ "is one.">;
+ def register_read_set : Option<"set", "s">, Group<1>, Arg<"Index">,
+ Desc<"Specify which register sets to dump by index.">;
+ def register_read_all : Option<"all", "a">, Group<2>,
+ Desc<"Show all register sets.">;
+}
+
+let Command = "source" in {
+ def source_stop_on_error : Option<"stop-on-error", "e">, Arg<"Boolean">,
+ Desc<"If true, stop executing commands on error.">;
+ def source_stop_on_continue : Option<"stop-on-continue", "c">, Arg<"Boolean">,
+ Desc<"If true, stop executing commands on continue.">;
+ def source_silent_run : Option<"silent-run", "s">, Arg<"Boolean">,
+ Desc<"If true don't echo commands while executing.">;
+ def cmd_relative_to_command_file : Option<"relative-to-command-file", "C">,
+ Desc<"Resolve non-absolute paths relative to the location of the "
+ "current command file. This argument can only be used when the command is "
+ "being sourced from a file.">;
+}
+
+let Command = "alias" in {
+ def alias_help : Option<"help", "h">, Arg<"HelpText">,
+ Desc<"Help text for this command">;
+ def alias_long_help : Option<"long-help", "H">, Arg<"HelpText">,
+ Desc<"Long help text for this command">;
+}
+
+let Command = "regex" in {
+ def regex_help : Option<"help", "h">, Group<1>, Arg<"None">,
+ Desc<"The help text to display for this command.">;
+ def regex_syntax : Option<"syntax", "s">, Group<1>, Arg<"None">,
+ Desc<"A syntax string showing the typical usage syntax.">;
+}
+
+let Command = "permissions" in {
+ def permissions_permissions_value : Option<"permissions-value", "v">,
+ Arg<"PermissionsNumber">,
+ Desc<"Give out the numeric value for permissions (e.g. 757)">;
+ def permissions_permissions_string : Option<"permissions-string", "s">,
+ Arg<"PermissionsString">,
+ Desc<"Give out the string value for permissions (e.g. rwxr-xr--).">;
+ def permissions_user_read : Option<"user-read", "r">,
+ Desc<"Allow user to read.">;
+ def permissions_user_write : Option<"user-write", "w">,
+ Desc<"Allow user to write.">;
+ def permissions_user_exec : Option<"user-exec", "x">,
+ Desc<"Allow user to execute.">;
+ def permissions_group_read : Option<"group-read", "R">,
+ Desc<"Allow group to read.">;
+ def permissions_group_write : Option<"group-write", "W">,
+ Desc<"Allow group to write.">;
+ def permissions_group_exec : Option<"group-exec", "X">,
+ Desc<"Allow group to execute.">;
+ def permissions_world_read : Option<"world-read", "d">,
+ Desc<"Allow world to read.">;
+ def permissions_world_write : Option<"world-write", "t">,
+ Desc<"Allow world to write.">;
+ def permissions_world_exec : Option<"world-exec", "e">,
+ Desc<"Allow world to execute.">;
+}
+
+let Command = "platform fread" in {
+ def platform_fread_offset : Option<"offset", "o">, Group<1>, Arg<"Index">,
+ Desc<"Offset into the file at which to start reading.">;
+ def platform_fread_count : Option<"count", "c">, Group<1>, Arg<"Count">,
+ Desc<"Number of bytes to read from the file.">;
+}
+
+let Command = "platform fwrite" in {
+ def platform_fwrite_offset : Option<"offset", "o">, Group<1>, Arg<"Index">,
+ Desc<"Offset into the file at which to start reading.">;
+ def platform_fwrite_data : Option<"data", "d">, Group<1>, Arg<"Value">,
+ Desc<"Text to write to the file.">;
+}
+
+let Command = "platform process list" in {
+ def platform_process_list_pid : Option<"pid", "p">, Group<1>, Arg<"Pid">,
+ Desc<"List the process info for a specific process ID.">;
+ def platform_process_list_name : Option<"name", "n">, Group<2>,
+ Arg<"ProcessName">, Required,
+ Desc<"Find processes with executable basenames that match a string.">;
+ def platform_process_list_ends_with : Option<"ends-with", "e">, Group<3>,
+ Arg<"ProcessName">, Required,
+ Desc<"Find processes with executable basenames that end with a string.">;
+ def platform_process_list_starts_with : Option<"starts-with", "s">, Group<4>,
+ Arg<"ProcessName">, Required,
+ Desc<"Find processes with executable basenames that start with a string.">;
+ def platform_process_list_contains : Option<"contains", "c">, Group<5>,
+ Arg<"ProcessName">, Required,
+ Desc<"Find processes with executable basenames that contain a string.">;
+ def platform_process_list_regex : Option<"regex", "r">, Group<6>,
+ Arg<"RegularExpression">, Required,
+ Desc<"Find processes with executable basenames that match a regular "
+ "expression.">;
+ def platform_process_list_parent : Option<"parent", "P">, GroupRange<2, 6>,
+ Arg<"Pid">, Desc<"Find processes that have a matching parent process ID.">;
+ def platform_process_list_uid : Option<"uid", "u">, GroupRange<2, 6>,
+ Arg<"UnsignedInteger">, Validator<"&posix_validator">,
+ Desc<"Find processes that have a matching user ID.">;
+ def platform_process_list_euid : Option<"euid", "U">, GroupRange<2, 6>,
+ Arg<"UnsignedInteger">, Validator<"&posix_validator">,
+ Desc<"Find processes that have a matching effective user ID.">;
+ def platform_process_list_gid : Option<"gid", "g">, GroupRange<2, 6>,
+ Arg<"UnsignedInteger">, Validator<"&posix_validator">,
+ Desc<"Find processes that have a matching group ID.">;
+ def platform_process_list_egid : Option<"egid", "G">, GroupRange<2, 6>,
+ Arg<"UnsignedInteger">, Validator<"&posix_validator">,
+ Desc<"Find processes that have a matching effective group ID.">;
+ def platform_process_list_arch : Option<"arch", "a">, GroupRange<2, 6>,
+ Arg<"Architecture">,
+ Desc<"Find processes that have a matching architecture.">;
+ def platform_process_list_show_args : Option<"show-args", "A">,
+ GroupRange<1, 6>,
+ Desc<"Show process arguments instead of the process executable basename.">;
+ def platform_process_list_all_users: Option<"all-users", "x">,
+ GroupRange<1,6>,
+ Desc<"Show processes matching all user IDs.">;
+ def platform_process_list_verbose : Option<"verbose", "v">, GroupRange<1, 6>,
+ Desc<"Enable verbose output.">;
+}
+
+let Command = "platform process attach" in {
+ def platform_process_attach_plugin : Option<"plugin", "P">, Arg<"Plugin">,
+ Desc<"Name of the process plugin you want to use.">;
+ def platform_process_attach_pid : Option<"pid", "p">, Group<1>, Arg<"Pid">,
+ Desc<"The process ID of an existing process to attach to.">;
+ def platform_process_attach_name : Option<"name", "n">, Group<2>,
+ Arg<"ProcessName">, Desc<"The name of the process to attach to.">;
+ def platform_process_attach_waitfor : Option<"waitfor", "w">, Group<2>,
+ Desc<"Wait for the process with <process-name> to launch.">;
+}
+
+let Command = "platform shell" in {
+ def platform_shell_host : Option<"host", "h">,
+ Desc<"Run the commands on the host shell when enabled.">;
+ def platform_shell_timeout : Option<"timeout", "t">, Arg<"Value">,
+ Desc<"Seconds to wait for the remote host to finish running the command.">;
+ def platform_shell_interpreter : Option<"shell", "s">, Arg<"Path">,
+ Desc<"Shell interpreter path. This is the binary used to run the command.">;
+}
+
+let Command = "process launch" in {
+ def process_launch_stop_at_entry : Option<"stop-at-entry", "s">,
+ Desc<"Stop at the entry point of the program when launching a process.">;
+ def process_launch_disable_aslr : Option<"disable-aslr", "A">, Arg<"Boolean">,
+ Desc<"Set whether to disable address space layout randomization when launching a process.">;
+ def process_launch_plugin : Option<"plugin", "P">, Arg<"Plugin">,
+ Desc<"Name of the process plugin you want to use.">;
+ def process_launch_working_dir : Option<"working-dir", "w">, Arg<"DirectoryName">,
+ Desc<"Set the current working directory to <path> when running the inferior.">;
+ def process_launch_arch : Option<"arch", "a">, Arg<"Architecture">,
+ Desc<"Set the architecture for the process to launch when ambiguous.">;
+ def process_launch_environment : Option<"environment", "E">,
+ Arg<"None">, Desc<"Specify an environment variable name/value string "
+ "(--environment NAME=VALUE). Can be specified multiple times for subsequent "
+ "environment entries.">;
+ def process_launch_shell : Option<"shell", "c">, GroupRange<1,3>,
+ OptionalArg<"Filename">, Desc<"Run the process in a shell (not supported on all platforms).">;
+ def process_launch_stdin : Option<"stdin", "i">, Group<1>,
+ Arg<"Filename">, Desc<"Redirect stdin for the process to <filename>.">;
+ def process_launch_stdout : Option<"stdout", "o">, Group<1>,
+ Arg<"Filename">, Desc<"Redirect stdout for the process to <filename>.">;
+ def process_launch_stderr : Option<"stderr", "e">, Group<1>,
+ Arg<"Filename">, Desc<"Redirect stderr for the process to <filename>.">;
+ def process_launch_tty : Option<"tty", "t">, Group<2>,
+ Desc<"Start the process in a terminal (not supported on all platforms).">;
+ def process_launch_no_stdio : Option<"no-stdio", "n">, Group<3>,
+ Desc<"Do not set up for terminal I/O to go to running process.">;
+ def process_launch_shell_expand_args : Option<"shell-expand-args", "X">, Group<4>,
+ Arg<"Boolean">, Desc<"Set whether to shell expand arguments to the process when launching.">;
+}
+
+let Command = "process attach" in {
+ def process_attach_continue : Option<"continue", "c">,
+ Desc<"Immediately continue the process once attached.">;
+ def process_attach_plugin : Option<"plugin", "P">, Arg<"Plugin">,
+ Desc<"Name of the process plugin you want to use.">;
+ def process_attach_pid : Option<"pid", "p">, Group<1>, Arg<"Pid">,
+ Desc<"The process ID of an existing process to attach to.">;
+ def process_attach_name : Option<"name", "n">, Group<2>, Arg<"ProcessName">,
+ Desc<"The name of the process to attach to.">;
+ def process_attach_include_existing : Option<"include-existing", "i">,
+ Group<2>, Desc<"Include existing processes when doing attach -w.">;
+ def process_attach_waitfor : Option<"waitfor", "w">, Group<2>,
+ Desc<"Wait for the process with <process-name> to launch.">;
+}
+
+let Command = "process continue" in {
+ def process_continue_ignore_count : Option<"ignore-count", "i">, Group<1>,
+ Arg<"UnsignedInteger">, Desc<"Ignore <N> crossings of the breakpoint (if it"
+ " exists) for the currently selected thread.">;
+ def process_continue_run_to_bkpt : Option<"continue-to-bkpt", "b">, Group<2>,
+ Arg<"BreakpointIDRange">, Desc<"Specify a breakpoint to continue to, temporarily "
+ "ignoring other breakpoints. Can be specified more than once. "
+ "The continue action will be done synchronously if this option is specified.">;
+}
+
+let Command = "process detach" in {
+ def process_detach_keep_stopped : Option<"keep-stopped", "s">, Group<1>,
+ Arg<"Boolean">, Desc<"Whether or not the process should be kept stopped on"
+ " detach (if possible).">;
+}
+
+let Command = "process connect" in {
+ def process_connect_plugin : Option<"plugin", "p">, Arg<"Plugin">,
+ Desc<"Name of the process plugin you want to use.">;
+}
+
+let Command = "process load" in {
+ def process_load_install : Option<"install", "i">, OptionalArg<"Path">,
+ Desc<"Install the shared library to the target. If specified without an "
+ "argument then the library will installed in the current working "
+ "directory.">;
+}
+
+let Command = "process handle" in {
+ def process_handle_clear : Option<"clear", "c">, Group<2>,
+ Desc<"Removes the signals listed from the Target signal handlers">;
+ def process_handle_stop : Option<"stop", "s">, Group<1>, Arg<"Boolean">,
+ Desc<"Whether or not the process should be stopped if the signal is "
+ "received.">;
+ def process_handle_notify : Option<"notify", "n">, Group<1>, Arg<"Boolean">,
+ Desc<"Whether or not the debugger should notify the user if the signal is "
+ "received.">;
+ def process_handle_pass : Option<"pass", "p">, Group<1>, Arg<"Boolean">,
+ Desc<"Whether or not the signal should be passed to the process.">;
+ def process_handle_only_target : Option<"target", "t">, Group<1>,
+ Desc<"Show only the signals with behaviors modified in this target">;
+ def process_handle_dummy : Option<"dummy", "d">, Group<2>,
+ Desc<"Also clear the values in the dummy target so they won't be inherited by new targets.">;
+}
+
+let Command = "process status" in {
+ def process_status_verbose : Option<"verbose", "v">, Group<1>,
+ Desc<"Show verbose process status including extended crash information.">;
+}
+
+let Command = "process save_core" in {
+ def process_save_core_style : Option<"style", "s">, Group<1>,
+ EnumArg<"SaveCoreStyle", "SaveCoreStyles()">, Desc<"Request a specific style "
+ "of corefile to be saved.">;
+ def process_save_core_plugin_name : Option<"plugin-name", "p">,
+ OptionalArg<"Plugin">, Desc<"Specify a plugin name to create the core file."
+ "This allows core files to be saved in different formats.">;
+}
+
+let Command = "script import" in {
+ def script_import_allow_reload : Option<"allow-reload", "r">, Group<1>,
+ Desc<"Allow the script to be loaded even if it was already loaded before. "
+ "This argument exists for backwards compatibility, but reloading is always "
+ "allowed, whether you specify it or not.">;
+ def relative_to_command_file : Option<"relative-to-command-file", "c">,
+ Group<1>, Desc<"Resolve non-absolute paths relative to the location of the "
+ "current command file. This argument can only be used when the command is "
+ "being sourced from a file.">;
+ def silent : Option<"silent", "s">, Group<1>,
+ Desc<"If true don't print any script output while importing.">;
+}
+
+let Command = "script add" in {
+ def script_add_function : Option<"function", "f">, Group<1>,
+ Arg<"PythonFunction">,
+ Desc<"Name of the Python function to bind to this command name.">;
+ def script_add_class : Option<"class", "c">, Group<2>, Arg<"PythonClass">,
+ Desc<"Name of the Python class to bind to this command name.">;
+ def script_add_help : Option<"help", "h">, Group<1>, Arg<"HelpText">,
+ Desc<"The help text to display for this command.">;
+ def script_add_overwrite : Option<"overwrite", "o">, Groups<[1,2]>,
+ Desc<"Overwrite an existing command at this node.">;
+ def script_add_synchronicity : Option<"synchronicity", "s">,
+ EnumArg<"ScriptedCommandSynchronicity", "ScriptSynchroType()">,
+ Desc<"Set the synchronicity of this command's executions with regard to "
+ "LLDB event system.">;
+}
+
+let Command = "container add" in {
+ def container_add_help : Option<"help", "h">, Arg<"HelpText">,
+ Desc<"Help text for this command">;
+ def container_add_long_help : Option<"long-help", "H">, Arg<"HelpText">,
+ Desc<"Long help text for this command">;
+ def container_add_overwrite : Option<"overwrite", "o">, Group<1>,
+ Desc<"Overwrite an existing command at this node.">;
+}
+
+let Command = "script" in {
+ def script_language : Option<"language", "l">,
+ EnumArg<"ScriptLang", "ScriptOptionEnum()">, Desc<"Specify the scripting "
+ " language. If none is specific the default scripting language is used.">;
+}
+
+let Command = "source info" in {
+ def source_info_count : Option<"count", "c">, Arg<"Count">,
+ Desc<"The number of line entries to display.">;
+ def source_info_shlib : Option<"shlib", "s">, Groups<[1,2]>, Arg<"ShlibName">,
+ Completion<"Module">, Desc<"Look up the source in the given module or "
+ "shared library (can be specified more than once).">;
+ def source_info_file : Option<"file", "f">, Group<1>, Arg<"Filename">,
+ Completion<"SourceFile">, Desc<"The file from which to display source.">;
+ def source_info_line : Option<"line", "l">, Group<1>, Arg<"LineNum">,
+ Desc<"The line number at which to start the displaying lines.">;
+ def source_info_end_line : Option<"end-line", "e">, Group<1>, Arg<"LineNum">,
+ Desc<"The line number at which to stop displaying lines.">;
+ def source_info_name : Option<"name", "n">, Group<2>, Arg<"Symbol">,
+ Completion<"Symbol">,
+ Desc<"The name of a function whose source to display.">;
+ def source_info_address : Option<"address", "a">, Group<3>,
+ Arg<"AddressOrExpression">, Desc<"Lookup the address and display the source"
+ " information for the corresponding file and line.">;
+}
+
+let Command = "source list" in {
+ def source_list_count : Option<"count", "c">, Arg<"Count">,
+ Desc<"The number of source lines to display.">;
+ def source_list_shlib : Option<"shlib", "s">, Groups<[1,2,5]>, Arg<"ShlibName">,
+ Completion<"Module">,
+ Desc<"Look up the source file in the given shared library.">;
+ def source_list_show_breakpoints : Option<"show-breakpoints", "b">,
+ Desc<"Show the line table locations from the debug information that "
+ "indicate valid places to set source level breakpoints.">;
+ def source_list_file : Option<"file", "f">, Group<1>, Arg<"Filename">,
+ Completion<"SourceFile">, Desc<"The file from which to display source.">;
+ def source_list_line : Option<"line", "l">, Group<1>, Arg<"LineNum">,
+ Desc<"The line number at which to start the display source.">;
+ def source_list_name : Option<"name", "n">, Group<2>, Arg<"Symbol">,
+ Completion<"Symbol">,
+ Desc<"The name of a function whose source to display.">;
+ def source_list_address : Option<"address", "a">, Group<3>,
+ Arg<"AddressOrExpression">, Desc<"Lookup the address and display the source"
+ " information for the corresponding file and line.">;
+ def source_list_reverse : Option<"reverse", "r">, Group<4>, Desc<"Reverse the"
+ " listing to look backwards from the last displayed block of source.">;
+ def source_list_file_colon_line : Option<"joint-specifier", "y">, Group<5>,
+ Arg<"FileLineColumn">, Completion<"SourceFile">,
+ Desc<"A specifier in the form filename:line[:column] from which to display"
+ " source.">;
+}
+
+let Command = "target dependents" in {
+ def dependents_no_dependents : Option<"no-dependents", "d">, Group<1>,
+ OptionalEnumArg<"Value", "OptionEnumValues(g_dependents_enumeration)">,
+ Desc<"Whether or not to load dependents when creating a target. If the "
+ "option is not specified, the value is implicitly 'default'. If the "
+ "option is specified but without a value, the value is implicitly "
+ "'true'.">;
+}
+
+let Command = "target modules dump" in {
+ def target_modules_dump_verbose : Option<"verbose", "v">,
+ Desc<"Enable verbose dump.">;
+}
+
+let Command = "target modules list" in {
+ def target_modules_list_address : Option<"address", "a">, Group<1>,
+ Arg<"AddressOrExpression">, Desc<"Display the image at this address.">;
+ def target_modules_list_arch : Option<"arch", "A">, Group<1>,
+ OptionalArg<"Width">, Desc<"Display the architecture when listing images.">;
+ def target_modules_list_triple : Option<"triple", "t">, Group<1>,
+ OptionalArg<"Width">, Desc<"Display the triple when listing images.">;
+ def target_modules_list_header : Option<"header", "h">, Group<1>,
+ Desc<"Display the image base address as a load address if debugging, a file"
+ " address otherwise.">;
+ def target_modules_list_offset : Option<"offset", "o">, Group<1>,
+ Desc<"Display the image load address offset from the base file address "
+ "(the slide amount).">;
+ def target_modules_list_uuid : Option<"uuid", "u">, Group<1>,
+ Desc<"Display the UUID when listing images.">;
+ def target_modules_list_fullpath : Option<"fullpath", "f">, Group<1>,
+ OptionalArg<"Width">,
+ Desc<"Display the fullpath to the image object file.">;
+ def target_modules_list_directory : Option<"directory", "d">, Group<1>,
+ OptionalArg<"Width">, Desc<"Display the directory with optional width for "
+ "the image object file.">;
+ def target_modules_list_basename : Option<"basename", "b">, Group<1>,
+ OptionalArg<"Width">, Desc<"Display the basename with optional width for "
+ "the image object file.">;
+ def target_modules_list_symfile : Option<"symfile", "s">, Group<1>,
+ OptionalArg<"Width">, Desc<"Display the fullpath to the image symbol file "
+ "with optional width.">;
+ def target_modules_list_symfile_unique : Option<"symfile-unique", "S">,
+ Group<1>, OptionalArg<"Width">, Desc<"Display the symbol file with optional"
+ " width only if it is different from the executable object file.">;
+ def target_modules_list_mod_time : Option<"mod-time", "m">, Group<1>,
+ OptionalArg<"Width">, Desc<"Display the modification time with optional "
+ "width of the module.">;
+ def target_modules_list_ref_count : Option<"ref-count", "r">, Group<1>,
+ OptionalArg<"Width">, Desc<"Display the reference count if the module is "
+ "still in the shared module cache.">;
+ def target_modules_list_pointer : Option<"pointer", "p">, Group<1>,
+ OptionalArg<"None">, Desc<"Display the module pointer.">;
+ def target_modules_list_global : Option<"global", "g">, Group<1>,
+ Desc<"Display the modules from the global module list, not just the "
+ "current target.">;
+}
+
+let Command = "target modules show unwind" in {
+ def target_modules_show_unwind_name : Option<"name", "n">, Group<1>,
+ Arg<"FunctionName">,
+ Desc<"Show unwind instructions for a function or symbol name.">;
+ def target_modules_show_unwind_address : Option<"address", "a">, Group<2>,
+ Arg<"AddressOrExpression">, Desc<"Show unwind instructions for a function "
+ "or symbol containing an address">;
+}
+
+let Command = "target modules lookup" in {
+ def target_modules_lookup_address : Option<"address", "a">, Group<1>,
+ Arg<"AddressOrExpression">, Required, Desc<"Lookup an address in one or "
+ "more target modules.">;
+ def target_modules_lookup_offset : Option<"offset", "o">, Group<1>,
+ Arg<"Offset">, Desc<"When looking up an address subtract <offset> from any "
+ "addresses before doing the lookup.">;
+ // FIXME: re-enable regex for types when the LookupTypeInModule actually uses
+ // the regex option by adding to group 6.
+ def target_modules_lookup_regex : Option<"regex", "r">, Groups<[2,4,5]>,
+ Desc<"The <name> argument for name lookups are regular expressions.">;
+ def target_modules_lookup_symbol : Option<"symbol", "s">, Group<2>,
+ Arg<"Symbol">, Required, Desc<"Lookup a symbol by name in the symbol tables"
+ " in one or more target modules.">;
+ def target_modules_lookup_file : Option<"file", "f">, Group<3>,
+ Arg<"Filename">, Required, Desc<"Lookup a file by fullpath or basename in "
+ "one or more target modules.">;
+ def target_modules_lookup_line : Option<"line", "l">, Group<3>,
+ Arg<"LineNum">, Desc<"Lookup a line number in a file (must be used in "
+ "conjunction with --file).">;
+ def target_modules_lookup_no_inlines : Option<"no-inlines", "i">,
+ GroupRange<3,5>,
+ Desc<"Ignore inline entries (must be used in conjunction with --file or "
+ "--function).">;
+ def target_modules_lookup_function : Option<"function", "F">, Group<4>,
+ Arg<"FunctionName">, Required, Desc<"Lookup a function by name in the debug"
+ " symbols in one or more target modules.">;
+ def target_modules_lookup_name : Option<"name", "n">, Group<5>,
+ Arg<"FunctionOrSymbol">, Required, Desc<"Lookup a function or symbol by "
+ "name in one or more target modules.">;
+ def target_modules_lookup_type : Option<"type", "t">, Group<6>, Arg<"Name">,
+ Required, Desc<"Lookup a type by name in the debug symbols in one or more "
+ "target modules.">;
+ def target_modules_lookup_variables_ranges : Option<"show-variable-ranges",
+ "\\x01">, GroupRange<1, 6>, Desc<"Dump valid ranges of variables (must be "
+ "used in conjunction with --verbose">;
+ def target_modules_lookup_verbose : Option<"verbose", "v">,
+ Desc<"Enable verbose lookup information.">;
+ def target_modules_lookup_all : Option<"all", "A">, Desc<"Print all matches, "
+ "not just the best match, if a best match is available.">;
+}
+
+let Command = "target stop hook add" in {
+ def target_stop_hook_add_one_liner : Option<"one-liner", "o">, GroupRange<1,3>,
+ Arg<"OneLiner">, Desc<"Add a command for the stop hook. Can be specified "
+ "more than once, and commands will be run in the order they appear.">;
+ def target_stop_hook_add_shlib : Option<"shlib", "s">, Arg<"ShlibName">,
+ Completion<"Module">,
+ Desc<"Set the module within which the stop-hook is to be run.">;
+ def target_stop_hook_add_thread_index : Option<"thread-index", "x">,
+ Arg<"ThreadIndex">, Desc<"The stop hook is run only for the thread whose "
+ "index matches this argument.">;
+ def target_stop_hook_add_thread_id : Option<"thread-id", "t">,
+ Arg<"ThreadID">, Desc<"The stop hook is run only for the thread whose TID "
+ "matches this argument.">;
+ def target_stop_hook_add_thread_name : Option<"thread-name", "T">,
+ Arg<"ThreadName">, Desc<"The stop hook is run only for the thread whose "
+ "thread name matches this argument.">;
+ def target_stop_hook_add_queue_name : Option<"queue-name", "q">,
+ Arg<"QueueName">, Desc<"The stop hook is run only for threads in the queue "
+ "whose name is given by this argument.">;
+ def target_stop_hook_add_file : Option<"file", "f">, Groups<[1,4]>,
+ Arg<"Filename">, Desc<"Specify the source file within which the stop-hook "
+ "is to be run.">, Completion<"SourceFile">;
+ def target_stop_hook_add_start_line : Option<"start-line", "l">, Groups<[1,4]>,
+ Arg<"LineNum">, Desc<"Set the start of the line range for which the "
+ "stop-hook is to be run.">;
+ def target_stop_hook_add_end_line : Option<"end-line", "e">, Groups<[1,4]>,
+ Arg<"LineNum">, Desc<"Set the end of the line range for which the stop-hook"
+ " is to be run.">;
+ def target_stop_hook_add_classname : Option<"classname", "c">, Groups<[2,5]>,
+ Arg<"ClassName">,
+ Desc<"Specify the class within which the stop-hook is to be run.">;
+ def target_stop_hook_add_name : Option<"name", "n">, Groups<[3,6]>,
+ Arg<"FunctionName">, Desc<"Set the function name within which the stop hook"
+ " will be run.">, Completion<"Symbol">;
+ def target_stop_hook_add_auto_continue : Option<"auto-continue", "G">,
+ Arg<"Boolean">, Desc<"The breakpoint will auto-continue after running its"
+ " commands.">;
+}
+
+let Command = "thread backtrace" in {
+ def thread_backtrace_count : Option<"count", "c">, Group<1>, Arg<"Count">,
+ Desc<"How many frames to display (-1 for all)">;
+ def thread_backtrace_start : Option<"start", "s">, Group<1>,
+ Arg<"FrameIndex">, Desc<"Frame in which to start the backtrace">;
+ def thread_backtrace_extended : Option<"extended", "e">, Group<1>,
+ Arg<"Boolean">, Desc<"Show the extended backtrace, if available">;
+}
+
+let Command = "thread step scope" in {
+ def thread_step_scope_step_in_avoids_no_debug :
+ Option<"step-in-avoids-no-debug", "a">, Group<1>, Arg<"Boolean">,
+ Desc<"A boolean value that sets whether stepping into functions will step "
+ "over functions with no debug information.">;
+ def thread_step_scope_step_out_avoids_no_debug :
+ Option<"step-out-avoids-no-debug", "A">, Group<1>, Arg<"Boolean">,
+ Desc<"A boolean value, if true stepping out of functions will continue to"
+ " step out till it hits a function with debug information.">;
+ def thread_step_scope_count : Option<"count", "c">, Group<1>, Arg<"Count">,
+ Desc<"How many times to perform the stepping operation - currently only "
+ "supported for step-inst and next-inst.">;
+ def thread_step_scope_end_linenumber : Option<"end-linenumber", "e">,
+ Group<1>, Arg<"LineNum">, Desc<"The line at which to stop stepping - "
+ "defaults to the next line and only supported for step-in and step-over."
+ " You can also pass the string 'block' to step to the end of the current"
+ " block. This is particularly use in conjunction with --step-target to"
+ " step through a complex calling sequence.">;
+ def thread_step_scope_run_mode : Option<"run-mode", "m">, Group<1>,
+ EnumArg<"RunMode", "TriRunningModes()">, Desc<"Determine how to run other "
+ "threads while stepping the current thread.">;
+ def thread_step_scope_step_over_regexp : Option<"step-over-regexp", "r">,
+ Group<1>, Arg<"RegularExpression">, Desc<"A regular expression that defines "
+ "function names to not to stop at when stepping in.">;
+ def thread_step_scope_step_in_target : Option<"step-in-target", "t">,
+ Group<1>, Arg<"FunctionName">, Desc<"The name of the directly called "
+ "function step in should stop at when stepping into.">;
+}
+
+let Command = "thread until" in {
+ def thread_until_frame : Option<"frame", "f">, Group<1>, Arg<"FrameIndex">,
+ Desc<"Frame index for until operation - defaults to 0">;
+ def thread_until_thread : Option<"thread", "t">, Group<1>, Arg<"ThreadIndex">,
+ Desc<"Thread index for the thread for until operation">;
+ def thread_until_run_mode : Option<"run-mode", "m">, Group<1>,
+ EnumArg<"RunMode", "DuoRunningModes()">, Desc<"Determine how to run other "
+ "threads while stepping this one">;
+ def thread_until_address : Option<"address", "a">, Group<1>,
+ Arg<"AddressOrExpression">, Desc<"Run until we reach the specified address, "
+ "or leave the function - can be specified multiple times.">;
+}
+
+let Command = "thread info" in {
+ def thread_info_json : Option<"json", "j">, Desc<"Display the thread info in"
+ " JSON format.">;
+ def thread_info_stop_info : Option<"stop-info", "s">, Desc<"Display the "
+ "extended stop info in JSON format.">;
+}
+
+let Command = "thread return" in {
+ def thread_return_from_expression : Option<"from-expression", "x">,
+ Desc<"Return from the innermost expression evaluation.">;
+}
+
+let Command = "thread jump" in {
+ def thread_jump_file : Option<"file", "f">, Group<1>, Arg<"Filename">,
+ Completion<"SourceFile">, Desc<"Specifies the source file to jump to.">;
+ def thread_jump_line : Option<"line", "l">, Group<1>, Arg<"LineNum">,
+ Required, Desc<"Specifies the line number to jump to.">;
+ def thread_jump_by : Option<"by", "b">, Group<2>, Arg<"Offset">, Required,
+ Desc<"Jumps by a relative line offset from the current line.">;
+ def thread_jump_address : Option<"address", "a">, Group<3>,
+ Arg<"AddressOrExpression">, Required, Desc<"Jumps to a specific address.">;
+ def thread_jump_force : Option<"force", "r">, Groups<[1,2,3]>,
+ Desc<"Allows the PC to leave the current function.">;
+}
+
+let Command = "thread plan list" in {
+ def thread_plan_list_verbose : Option<"verbose", "v">, Group<1>,
+ Desc<"Display more information about the thread plans">;
+ def thread_plan_list_internal : Option<"internal", "i">, Group<1>,
+ Desc<"Display internal as well as user thread plans">;
+ def thread_plan_list_thread_id : Option<"thread-id", "t">, Group<1>,
+ Arg<"ThreadID">, Desc<"List the thread plans for this TID, can be "
+ "specified more than once.">;
+ def thread_plan_list_unreported : Option<"unreported", "u">, Group<1>,
+ Desc<"Display thread plans for unreported threads">;
+}
+
+let Command = "thread trace dump instructions" in {
+ def thread_trace_dump_instructions_forwards: Option<"forwards", "f">,
+ Group<1>,
+ Desc<"If specified, the trace is traversed forwards chronologically "
+ "starting at the oldest instruction. Otherwise, it starts at the most "
+ "recent one and the traversal is backwards.">;
+ def thread_trace_dump_instructions_count : Option<"count", "c">, Group<1>,
+ Arg<"Count">,
+ Desc<"The number of instructions to display starting at the most recent "
+ "instruction, or the oldest if --forwards is provided.">;
+ def thread_trace_dump_instructions_all : Option<"all", "a">, Group<1>,
+ Desc<"From the starting point of the trace, dump all instructions "
+ "available.">;
+ def thread_trace_dump_instructions_id: Option<"id", "i">, Group<1>,
+ Arg<"Index">,
+ Desc<"Custom starting instruction id from where to start traversing. This "
+ "id can be provided in decimal or hexadecimal representation.">;
+ def thread_trace_dump_instructions_skip: Option<"skip", "s">, Group<1>,
+ Arg<"Index">,
+ Desc<"How many trace items (instructions, errors and events) to skip from "
+ "the starting position of the trace before starting the traversal.">;
+ def thread_trace_dump_instructions_raw : Option<"raw", "r">, Group<1>,
+ Desc<"Dump only instruction address without disassembly nor symbol "
+ "information.">;
+ def thread_trace_dump_instructions_file : Option<"file", "F">, Group<1>,
+ Arg<"Filename">,
+ Desc<"Dump the instruction to a file instead of the standard output.">;
+ def thread_trace_dump_instructions_json: Option<"json", "j">,
+ Group<1>,
+ Desc<"Dump in simple JSON format.">;
+ def thread_trace_dump_instructions_pretty_print: Option<"pretty-json", "J">,
+ Group<1>,
+ Desc<"Dump in JSON format but pretty printing the output for easier readability.">;
+ def thread_trace_dump_instructions_show_kind : Option<"kind", "k">, Group<1>,
+ Desc<"Show instruction control flow kind. Refer to the enum "
+ "`InstructionControlFlowKind` for a list of control flow kind. "
+ "As an important note, far jumps, far calls and far returns often indicate "
+ "calls to and from kernel.">;
+ def thread_trace_dump_instructions_show_tsc : Option<"tsc", "t">, Group<1>,
+ Desc<"For each instruction, print the corresponding timestamp counter if "
+ "available.">;
+ def thread_trace_dump_instructions_show_events : Option<"events", "e">,
+ Group<1>,
+ Desc<"Dump the events that happened during the execution of the target.">;
+ def thread_trace_dump_instructions_continue: Option<"continue", "C">,
+ Group<1>,
+ Desc<"Continue dumping instructions right where the previous invocation of "
+ "this command was left, or from the beginning if this is the first "
+ "invocation. The --skip argument is discarded and the other arguments are "
+ "preserved from the previous invocation when possible.">;
+}
+
+let Command = "thread trace dump info" in {
+ def thread_trace_dump_info_verbose : Option<"verbose", "v">, Group<1>,
+ Desc<"show verbose thread trace dump info">;
+ def thread_trace_dump_info_json: Option<"json", "j">, Group<1>,
+ Desc<"Dump in JSON format.">;
+}
+
+let Command = "type summary add" in {
+ def type_summary_add_category : Option<"category", "w">, Arg<"Name">,
+ Desc<"Add this to the given category instead of the default one.">;
+ def type_summary_add_cascade : Option<"cascade", "C">, Arg<"Boolean">,
+ Desc<"If true, cascade through typedef chains.">;
+ def type_summary_add_no_value : Option<"no-value", "v">,
+ Desc<"Don't show the value, just show the summary, for this type.">;
+ def type_summary_add_skip_pointers : Option<"skip-pointers", "p">,
+ Desc<"Don't use this format for pointers-to-type objects.">;
+ def type_summary_add_skip_references : Option<"skip-references", "r">,
+ Desc<"Don't use this format for references-to-type objects.">;
+ def type_summary_add_regex : Option<"regex", "x">,
+ Desc<"Type names are actually regular expressions.">;
+ def type_summary_add_inline_children : Option<"inline-children", "c">,
+ Group<1>, Required,
+ Desc<"If true, inline all child values into summary string.">;
+ def type_summary_add_omit_names : Option<"omit-names", "O">, Group<1>,
+ Desc<"If true, omit value names in the summary display.">;
+ def type_summary_add_summary_string : Option<"summary-string", "s">, Group<2>,
+ Arg<"SummaryString">, Required,
+ Desc<"Summary string used to display text and object contents.">;
+ def type_summary_add_python_script : Option<"python-script", "o">, Group<3>,
+ Arg<"PythonScript">,
+ Desc<"Give a one-liner Python script as part of the command.">;
+ def type_summary_add_python_function : Option<"python-function", "F">,
+ Group<3>, Arg<"PythonFunction">,
+ Desc<"Give the name of a Python function to use for this type.">;
+ def type_summary_add_input_python : Option<"input-python", "P">, Group<3>,
+ Desc<"Input Python code to use for this type manually.">;
+ def type_summary_add_expand : Option<"expand", "e">, Groups<[2,3]>,
+ Desc<"Expand aggregate data types to show children on separate lines.">;
+ def type_summary_add_hide_empty : Option<"hide-empty", "h">, Groups<[2,3]>,
+ Desc<"Do not expand aggregate data types with no children.">;
+ def type_summary_add_name : Option<"name", "n">, Groups<[2,3]>, Arg<"Name">,
+ Desc<"A name for this summary string.">;
+}
+
+let Command = "type synth add" in {
+ def type_synth_add_cascade : Option<"cascade", "C">, Arg<"Boolean">,
+ Desc<"If true, cascade through typedef chains.">;
+ def type_synth_add_skip_pointers : Option<"skip-pointers", "p">,
+ Desc<"Don't use this format for pointers-to-type objects.">;
+ def type_synth_add_skip_references : Option<"skip-references", "r">,
+ Desc<"Don't use this format for references-to-type objects.">;
+ def type_synth_add_category : Option<"category", "w">, Arg<"Name">,
+ Desc<"Add this to the given category instead of the default one.">;
+ def type_synth_add_python_class : Option<"python-class", "l">, Group<2>,
+ Arg<"PythonClass">,
+ Desc<"Use this Python class to produce synthetic children.">;
+ def type_synth_add_input_python : Option<"input-python", "P">, Group<3>,
+ Desc<"Type Python code to generate a class that provides synthetic "
+ "children.">;
+ def type_synth_add_regex : Option<"regex", "x">,
+ Desc<"Type names are actually regular expressions.">;
+}
+
+let Command = "type format add" in {
+ def type_format_add_category : Option<"category", "w">, Arg<"Name">,
+ Desc<"Add this to the given category instead of the default one.">;
+ def type_format_add_cascade : Option<"cascade", "C">, Arg<"Boolean">,
+ Desc<"If true, cascade through typedef chains.">;
+ def type_format_add_skip_pointers : Option<"skip-pointers", "p">,
+ Desc<"Don't use this format for pointers-to-type objects.">;
+ def type_format_add_skip_references : Option<"skip-references", "r">,
+ Desc<"Don't use this format for references-to-type objects.">;
+ def type_format_add_regex : Option<"regex", "x">,
+ Desc<"Type names are actually regular expressions.">;
+ def type_format_add_type : Option<"type", "t">, Group<2>, Arg<"Name">,
+ Desc<"Format variables as if they were of this type.">;
+}
+
+let Command = "type formatter delete" in {
+ def type_formatter_delete_all : Option<"all", "a">, Group<1>,
+ Desc<"Delete from every category.">;
+ def type_formatter_delete_category : Option<"category", "w">, Group<2>,
+ Arg<"Name">, Desc<"Delete from given category.">;
+ def type_formatter_delete_language : Option<"language", "l">, Group<3>,
+ Arg<"Language">, Desc<"Delete from given language's category.">;
+}
+
+let Command = "type formatter clear" in {
+ def type_formatter_clear_all : Option<"all", "a">,
+ Desc<"Clear every category.">;
+}
+
+let Command = "type formatter list" in {
+ def type_formatter_list_category_regex : Option<"category-regex", "w">,
+ Group<1>, Arg<"Name">, Desc<"Only show categories matching this filter.">;
+ def type_formatter_list_language : Option<"language", "l">, Group<2>,
+ Arg<"Language">, Desc<"Only show the category for a specific language.">;
+}
+
+let Command = "type category define" in {
+ def type_category_define_enabled : Option<"enabled", "e">,
+ Desc<"If specified, this category will be created enabled.">;
+ def type_category_define_language : Option<"language", "l">, Arg<"Language">,
+ Desc<"Specify the language that this category is supported for.">;
+}
+
+let Command = "type category enable" in {
+ def type_category_enable_language : Option<"language", "l">, Arg<"Language">,
+ Desc<"Enable the category for this language.">;
+}
+
+let Command = "type category disable" in {
+ def type_category_disable_language : Option<"language", "l">, Arg<"Language">,
+ Desc<"Enable the category for this language.">;
+}
+
+let Command = "type filter add" in {
+ def type_filter_add_cascade : Option<"cascade", "C">, Arg<"Boolean">,
+ Desc<"If true, cascade through typedef chains.">;
+ def type_filter_add_skip_pointers : Option<"skip-pointers", "p">,
+ Desc<"Don't use this format for pointers-to-type objects.">;
+ def type_filter_add_skip_references : Option<"skip-references", "r">,
+ Desc<"Don't use this format for references-to-type objects.">;
+ def type_filter_add_category : Option<"category", "w">, Arg<"Name">,
+ Desc<"Add this to the given category instead of the default one.">;
+ def type_filter_add_child : Option<"child", "c">, Arg<"ExpressionPath">,
+ Desc<"Include this expression path in the synthetic view.">;
+ def type_filter_add_regex : Option<"regex", "x">,
+ Desc<"Type names are actually regular expressions.">;
+}
+
+let Command = "type lookup" in {
+ def type_lookup_show_help : Option<"show-help", "h">,
+ Desc<"Display available help for types">;
+ def type_lookup_language : Option<"language", "l">, Arg<"Language">,
+ Desc<"Which language's types should the search scope be">;
+}
+
+let Command = "watchpoint list" in {
+ def watchpoint_list_brief : Option<"brief", "b">, Group<1>, Desc<"Give a "
+ "brief description of the watchpoint (no location info).">;
+ def watchpoint_list_full : Option<"full", "f">, Group<2>, Desc<"Give a full "
+ "description of the watchpoint and its locations.">;
+ def watchpoint_list_verbose : Option<"verbose", "v">, Group<3>, Desc<"Explain "
+ "everything we know about the watchpoint (for debugging debugger bugs).">;
+}
+
+let Command = "watchpoint ignore" in {
+ def watchpoint_ignore_ignore_count : Option<"ignore-count", "i">,
+ Arg<"Count">, Required, Desc<"Set the number of times this watchpoint is"
+ " skipped before stopping.">;
+}
+
+let Command = "watchpoint modify" in {
+ def watchpoint_modify_condition : Option<"condition", "c">, Arg<"Expression">,
+ Desc<"The watchpoint stops only if this condition expression evaluates "
+ "to true.">;
+}
+
+let Command = "watchpoint command add" in {
+ def watchpoint_command_add_one_liner : Option<"one-liner", "o">, Group<1>,
+ Arg<"OneLiner">, Desc<"Specify a one-line watchpoint command inline. Be "
+ "sure to surround it with quotes.">;
+ def watchpoint_command_add_stop_on_error : Option<"stop-on-error", "e">,
+ Arg<"Boolean">, Desc<"Specify whether watchpoint command execution should "
+ "terminate on error.">;
+ def watchpoint_command_add_script_type : Option<"script-type", "s">,
+ EnumArg<"None", "ScriptOptionEnum()">, Desc<"Specify the language for the"
+ " commands - if none is specified, the lldb command interpreter will be "
+ "used.">;
+ def watchpoint_command_add_python_function : Option<"python-function", "F">,
+ Group<2>, Arg<"PythonFunction">, Desc<"Give the name of a Python function "
+ "to run as command for this watchpoint. Be sure to give a module name if "
+ "appropriate.">;
+}
+
+let Command = "watchpoint delete" in {
+ def watchpoint_delete_force : Option<"force", "f">, Group<1>,
+ Desc<"Delete all watchpoints without querying for confirmation.">;
+}
+
+let Command = "trace load" in {
+ def trace_load_verbose : Option<"verbose", "v">, Group<1>,
+ Desc<"Show verbose trace load logging for debugging the plug-in "
+ "implementation.">;
+}
+
+let Command = "trace save" in {
+ def trace_save_compact: Option<"compact", "c">,
+ Group<1>,
+ Desc<"Try not to save to disk information irrelevant to the traced "
+ "processes. Each trace plug-in implements this in a different "
+ "fashion.">;
+}
+
+let Command = "trace dump" in {
+ def trace_dump_verbose : Option<"verbose", "v">, Group<1>,
+ Desc<"Show verbose trace information.">;
+}
+
+let Command = "trace schema" in {
+ def trace_schema_verbose : Option<"verbose", "v">, Group<1>,
+ Desc<"Show verbose trace schema logging for debugging the plug-in.">;
+}
+
+let Command = "statistics dump" in {
+ def statistics_dump_all: Option<"all-targets", "a">, Group<1>,
+ Desc<"Include statistics for all targets.">;
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/OptionsBase.td b/contrib/llvm-project/lldb/source/Commands/OptionsBase.td
new file mode 100644
index 000000000000..f6967f067bf4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/OptionsBase.td
@@ -0,0 +1,178 @@
+
+// The fields below describe how the fields of `OptionDefinition` struct are
+// initialized by different definitions in the Options.td and this file.
+////////////////////////////////////////////////////////////////////////////////
+// Field: usage_mask
+// Default value: LLDB_OPT_SET_ALL (Option allowed in all groups)
+// Set by:
+// - `Group`: Sets a single group to this option.
+// Example: def foo : Option<"foo", "f">, Group<1>;
+// - `Groups`: Sets a given list of group numbers.
+// Example: def foo : Option<"foo", "f">, Groups<[1,4,6]>;
+// - `GroupRange`: Sets an interval of groups. Start and end are inclusive.
+// Example: def foo : Option<"foo", "f">, GroupRange<1, 4>;
+// Sets group 1, 2, 3, 4 for the option.
+////////////////////////////////////////////////////////////////////////////////
+// Field: required
+// Default value: false (Not required)
+// Set by:
+// - `Required`: Marks the option as required.
+// Example: def foo : Option<"foo", "f">, Required;
+////////////////////////////////////////////////////////////////////////////////
+// Field: long_option
+// Default value: not available (has to be defined in Option)
+// Set by:
+// - `Option` constructor: Already set by constructor.
+// Example: def foo : Option<"long-option", "l">
+// ^
+// long option value
+////////////////////////////////////////////////////////////////////////////////
+// Field: short_option
+// Default value: not available (has to be defined in Option)
+// Set by:
+// - `Option` constructor: Already set by constructor.
+// Example: def foo : Option<"long-option", "l">
+// ^
+// short option
+////////////////////////////////////////////////////////////////////////////////
+// Field: option_has_arg
+// Default value: OptionParser::eNoArgument (No argument allowed)
+// Set by:
+// - `OptionalArg`: Sets the argument type and marks it as optional.
+// - `Arg`: Sets the argument type and marks it as required.
+// - `EnumArg`: Sets the argument type to an enum and marks it as required.
+// - `OptionalEnumArg`: Same as EnumArg but marks it as optional.
+// See argument_type field for more info.
+////////////////////////////////////////////////////////////////////////////////
+// Field: validator
+// Default value: 0 (No validator for option)
+// Set by:
+// - `Validator`: Sets the value to a given validator (which has to exist in
+// the surrounding code.
+////////////////////////////////////////////////////////////////////////////////
+// Field: enum_values
+// Default value: {} (No enum associated with this option)
+// Set by:
+// - `OptionalEnumArg`:
+// - `EnumArg`: Sets the argument type and assigns it a enum holding the valid
+// values. The enum needs to be a variable in the including code.
+// Marks the option as required (see option_has_arg).
+// Example: def foo : Option<"foo", "f">,
+// EnumArg<"SortOrder",
+// "OptionEnumValues(g_sort_option_enumeration)">;
+////////////////////////////////////////////////////////////////////////////////
+// Field: completion_type
+// Default value: CommandCompletions::eNoCompletion (no tab completion)
+// Set by:
+// - `Completion`: Gives the option a single completion kind.
+// Example: def foo : Option<"foo", "f">,
+// Completion<"DiskFile">;
+// Sets the completion to eDiskFileCompletion
+//
+// - `Completions`: Sets a given kinds of completions.
+// Example: def foo : Option<"foo", "f">,
+// Completions<["DiskFile", "DiskDirectory"]>;
+// Sets the completion to
+// `eDiskFileCompletion | eDiskDirectoryCompletion`.
+////////////////////////////////////////////////////////////////////////////////
+// Field: argument_type
+// Default value: eArgTypeNone
+// Set by:
+// - `OptionalArg`: Sets the argument type and marks it as optional.
+// Example: def foo : Option<"foo", "f">, OptionalArg<"Pid">;
+// Sets the argument type to eArgTypePid and marks option as
+// optional (see option_has_arg).
+// - `Arg`: Sets the argument type and marks it as required.
+// Example: def foo : Option<"foo", "f">, Arg<"Pid">;
+// Sets the argument type to eArgTypePid and marks option as
+// required (see option_has_arg).
+// - `OptionalEnumArg`:
+// - `EnumArg`: Sets the argument type and assigns it a enum holding the valid
+// values. The enum needs to be a variable in the including code.
+// Marks the option as required (see option_has_arg).
+// Example: def foo : Option<"foo", "f">,
+// EnumArg<"SortOrder",
+// "OptionEnumValues(g_sort_option_enumeration)">;
+// Use `OptionalEnumArg` for having an option enum argument.
+////////////////////////////////////////////////////////////////////////////////
+// Field: usage_text
+// Default value: ""
+// Set by:
+// - `Desc`: Sets the description for the given option.
+// Example: def foo : Option<"foo", "f">, Desc<"does nothing.">;
+// Sets the description to "does nothing.".
+
+// Base class for all options.
+class Option<string fullname, string shortname> {
+ string FullName = fullname;
+ string ShortName = shortname;
+ // The full associated command/subcommand such as "settings set".
+ string Command;
+}
+
+// Moves the option into a list of option groups.
+class Groups<list<int> groups> {
+ list<int> Groups = groups;
+}
+
+// Moves the option in all option groups in a range.
+// Start and end values are inclusive.
+class GroupRange<int start, int end> {
+ int GroupStart = start;
+ int GroupEnd = end;
+}
+// Moves the option in a single option group.
+class Group<int group> {
+ int GroupStart = group;
+ int GroupEnd = group;
+}
+
+// Sets the description for the option that should be
+// displayed to the user.
+class Desc<string description> {
+ string Description = description;
+}
+
+// Marks the option as required when calling the
+// associated command.
+class Required {
+ bit Required = 1;
+}
+
+// Gives the option an optional argument.
+class OptionalArg<string type> {
+ string ArgType = type;
+ bit OptionalArg = 1;
+}
+
+// Gives the option an required argument.
+class Arg<string type> {
+ string ArgType = type;
+}
+
+// Gives the option an required argument.
+class EnumArg<string type, string enum> {
+ string ArgType = type;
+ string ArgEnum = enum;
+}
+
+// Gives the option an required argument.
+class OptionalEnumArg<string type, string enum> {
+ string ArgType = type;
+ string ArgEnum = enum;
+ bit OptionalArg = 1;
+}
+
+// Sets the available completions for the given option.
+class Completions<list<string> completions> {
+ list<string> Completions = completions;
+}
+// Sets a single completion for the given option.
+class Completion<string completion> {
+ list<string> Completions = [completion];
+}
+
+// Sets the validator for a given option.
+class Validator<string validator> {
+ string Validator = validator;
+}