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.cpp899
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectApropos.cpp81
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectApropos.h31
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpoint.cpp2490
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpoint.h48
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpointCommand.cpp654
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpointCommand.h27
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectCommands.cpp2753
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectCommands.h28
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectDWIMPrint.cpp234
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectDWIMPrint.h53
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectDiagnostics.cpp114
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectDiagnostics.h29
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectDisassemble.cpp544
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectDisassemble.h96
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectExpression.cpp689
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectExpression.h107
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectFrame.cpp1142
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectFrame.h27
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectGUI.cpp45
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectGUI.h30
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectHelp.cpp210
-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.cpp601
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectLog.h33
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectMemory.cpp1814
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectMemory.h25
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectMemoryTag.cpp319
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectMemoryTag.h25
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectMultiword.cpp437
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectPlatform.cpp1845
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectPlatform.h32
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectPlugin.cpp57
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectPlugin.h25
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.cpp1864
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.h27
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectQuit.cpp105
-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.h65
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectRegister.cpp461
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectRegister.h34
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectScripting.cpp144
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectScripting.h25
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectSession.cpp195
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectSession.h23
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectSettings.cpp1093
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectSettings.h27
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectSource.cpp1295
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectSource.h25
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectStats.cpp164
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectStats.h23
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectTarget.cpp5351
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectTarget.h27
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectThread.cpp2611
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectThread.h25
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectThreadUtil.cpp207
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectThreadUtil.h104
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectTrace.cpp424
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectTrace.h51
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectType.cpp2915
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectType.h25
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectVersion.cpp28
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectVersion.h30
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpoint.cpp1185
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpoint.h31
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpointCommand.cpp590
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpointCommand.h27
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandOptionArgumentTable.cpp313
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandOptionsProcessAttach.cpp76
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandOptionsProcessAttach.h47
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandOptionsProcessLaunch.cpp151
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandOptionsProcessLaunch.h49
-rw-r--r--contrib/llvm-project/lldb/source/Commands/Options.td1451
-rw-r--r--contrib/llvm-project/lldb/source/Commands/OptionsBase.td176
77 files changed, 37289 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..54f4b3681664
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandCompletions.cpp
@@ -0,0 +1,899 @@
+//===-- 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/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
+
+#include "lldb/Breakpoint/Watchpoint.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/FileSpecList.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 {
+ uint64_t type;
+ CompletionCallback callback;
+};
+
+bool CommandCompletions::InvokeCommonCompletionCallbacks(
+ CommandInterpreter &interpreter, uint32_t completion_mask,
+ CompletionRequest &request, SearchFilter *searcher) {
+ bool handled = false;
+
+ const CommonCompletionElement common_completions[] = {
+ {lldb::eNoCompletion, nullptr},
+ {lldb::eSourceFileCompletion, CommandCompletions::SourceFiles},
+ {lldb::eDiskFileCompletion, CommandCompletions::DiskFiles},
+ {lldb::eDiskDirectoryCompletion, CommandCompletions::DiskDirectories},
+ {lldb::eSymbolCompletion, CommandCompletions::Symbols},
+ {lldb::eModuleCompletion, CommandCompletions::Modules},
+ {lldb::eModuleUUIDCompletion, CommandCompletions::ModuleUUIDs},
+ {lldb::eSettingsNameCompletion, CommandCompletions::SettingsNames},
+ {lldb::ePlatformPluginCompletion,
+ CommandCompletions::PlatformPluginNames},
+ {lldb::eArchitectureCompletion, CommandCompletions::ArchitectureNames},
+ {lldb::eVariablePathCompletion, CommandCompletions::VariablePath},
+ {lldb::eRegisterCompletion, CommandCompletions::Registers},
+ {lldb::eBreakpointCompletion, CommandCompletions::Breakpoints},
+ {lldb::eProcessPluginCompletion, CommandCompletions::ProcessPluginNames},
+ {lldb::eDisassemblyFlavorCompletion,
+ CommandCompletions::DisassemblyFlavors},
+ {lldb::eTypeLanguageCompletion, CommandCompletions::TypeLanguages},
+ {lldb::eFrameIndexCompletion, CommandCompletions::FrameIndexes},
+ {lldb::eStopHookIDCompletion, CommandCompletions::StopHookIDs},
+ {lldb::eThreadIndexCompletion, CommandCompletions::ThreadIndexes},
+ {lldb::eWatchpointIDCompletion, CommandCompletions::WatchPointIDs},
+ {lldb::eBreakpointNameCompletion, CommandCompletions::BreakpointNames},
+ {lldb::eProcessIDCompletion, CommandCompletions::ProcessIDs},
+ {lldb::eProcessNameCompletion, CommandCompletions::ProcessNames},
+ {lldb::eRemoteDiskFileCompletion, CommandCompletions::RemoteDiskFiles},
+ {lldb::eRemoteDiskDirectoryCompletion,
+ CommandCompletions::RemoteDiskDirectories},
+ {lldb::eTypeCategoryNameCompletion,
+ CommandCompletions::TypeCategoryNames},
+ {lldb::eThreadIDCompletion, CommandCompletions::ThreadIDs},
+ {lldb::eTerminatorCompletion,
+ nullptr} // This one has to be last in the list.
+ };
+
+ for (int i = 0;; i++) {
+ if (common_completions[i].type == lldb::eTerminatorCompletion)
+ 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);
+
+ // Now add the functions & symbols to the list - only add if unique:
+ for (const SymbolContext &sc : sc_list) {
+ 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) {
+ llvm::StringRef request_str = m_request.GetCursorArgumentPrefix();
+ // We can match the full path, or the file name only. The full match will be
+ // attempted always, the file name match only if the request does not
+ // contain a path separator.
+
+ // Preserve both the path as spelled by the user (used for completion) and
+ // the canonical version (used for matching).
+ m_spelled_path = request_str;
+ m_canonical_path = FileSpec(m_spelled_path).GetPath();
+ if (!m_spelled_path.empty() &&
+ llvm::sys::path::is_separator(m_spelled_path.back()) &&
+ !llvm::StringRef(m_canonical_path).ends_with(m_spelled_path.back())) {
+ m_canonical_path += m_spelled_path.back();
+ }
+
+ if (llvm::find_if(request_str, [](char c) {
+ return llvm::sys::path::is_separator(c);
+ }) == request_str.end())
+ m_file_name = request_str;
+ }
+
+ lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; }
+
+ Searcher::CallbackReturn SearchCallback(SearchFilter &filter,
+ SymbolContext &context,
+ Address *addr) override {
+ if (context.module_sp) {
+ // Attempt a full path match.
+ std::string cur_path = context.module_sp->GetFileSpec().GetPath();
+ llvm::StringRef cur_path_view = cur_path;
+ if (cur_path_view.consume_front(m_canonical_path))
+ m_request.AddCompletion((m_spelled_path + cur_path_view).str());
+
+ // And a file name match.
+ if (m_file_name) {
+ llvm::StringRef cur_file_name =
+ context.module_sp->GetFileSpec().GetFilename().GetStringRef();
+ if (cur_file_name.starts_with(*m_file_name))
+ m_request.AddCompletion(cur_file_name);
+ }
+ }
+ return Searcher::eCallbackReturnContinue;
+ }
+
+ void DoCompletion(SearchFilter *filter) override { filter->Search(*this); }
+
+private:
+ std::optional<llvm::StringRef> m_file_name;
+ llvm::StringRef m_spelled_path;
+ std::string m_canonical_path;
+
+ 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.starts_with("~")) {
+ 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 if (CompletionBuffer == path::root_directory(CompletionBuffer)) {
+ SearchDir = CompletionBuffer;
+ } 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 or the separator when passed the disk root directory.
+ // We have to filter those out, but only when the "." doesn't come from the
+ // completion request itself.
+ if ((PartialItem == "." || PartialItem == path::get_separator()) &&
+ 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.starts_with(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().starts_with("$"))
+ reg_prefix = "$";
+
+ RegisterContext *reg_ctx =
+ interpreter.GetExecutionContext().GetRegisterContext();
+ if (!reg_ctx)
+ return;
+
+ 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();
+ Debugger &dbg = interpreter.GetDebugger();
+ 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;
+ // Dumping frames can be slow, allow interruption.
+ if (INTERRUPT_REQUESTED(dbg, "Interrupted in frame completion"))
+ break;
+ 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::ThreadIDs(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->GetID()),
+ strm.GetString());
+ }
+}
+
+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..d663f2bd923f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectApropos.cpp
@@ -0,0 +1,81 @@
+//===-- 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) {
+ AddSimpleArgumentList(eArgTypeSearchWord);
+}
+
+CommandObjectApropos::~CommandObjectApropos() = default;
+
+void 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");
+ }
+}
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..f43420c1815d
--- /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:
+ void 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..773f8ed2fa8a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpoint.cpp
@@ -0,0 +1,2490 @@
+//===-- 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/CommandOptionArgumentTable.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 <optional>
+#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::ArrayRef(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;
+ const char *long_option =
+ g_breakpoint_modify_options[option_idx].long_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 = CreateOptionParsingError(option_arg, short_option, long_option,
+ g_bool_parsing_error_message);
+ } break;
+ case 'i': {
+ uint32_t ignore_count;
+ if (option_arg.getAsInteger(0, ignore_count))
+ error = CreateOptionParsingError(option_arg, short_option, long_option,
+ g_int_parsing_error_message);
+ 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 = CreateOptionParsingError(option_arg, short_option, long_option,
+ g_bool_parsing_error_message);
+ } break;
+ case 't': {
+ lldb::tid_t thread_id = LLDB_INVALID_THREAD_ID;
+ if (option_arg == "current") {
+ if (!execution_context) {
+ error = CreateOptionParsingError(
+ option_arg, short_option, long_option,
+ "No context to determine current thread");
+ } else {
+ ThreadSP ctx_thread_sp = execution_context->GetThreadSP();
+ if (!ctx_thread_sp || !ctx_thread_sp->IsValid()) {
+ error =
+ CreateOptionParsingError(option_arg, short_option, long_option,
+ "No currently selected thread");
+ } else {
+ thread_id = ctx_thread_sp->GetID();
+ }
+ }
+ } else if (option_arg.getAsInteger(0, thread_id)) {
+ error = CreateOptionParsingError(option_arg, short_option, long_option,
+ g_int_parsing_error_message);
+ }
+ if (thread_id != LLDB_INVALID_THREAD_ID)
+ 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.getAsInteger(0, thread_index)) {
+ error = CreateOptionParsingError(option_arg, short_option, long_option,
+ g_int_parsing_error_message);
+ } else {
+ 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::ArrayRef(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;
+ const char *long_option =
+ g_breakpoint_set_options[option_idx].long_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 =
+ CreateOptionParsingError(option_arg, short_option, long_option,
+ g_int_parsing_error_message);
+ break;
+
+ case 'E': {
+ LanguageType language = Language::GetLanguageTypeFromString(option_arg);
+
+ llvm::StringRef error_context;
+ 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_plus_plus:
+ error_context =
+ "Set exception breakpoints separately for c++ and objective-c";
+ break;
+ case eLanguageTypeUnknown:
+ error_context = "Unknown language type for exception breakpoint";
+ break;
+ default:
+ if (Language *languagePlugin = Language::FindPlugin(language)) {
+ if (languagePlugin->SupportsExceptionBreakpointsOnThrow() ||
+ languagePlugin->SupportsExceptionBreakpointsOnCatch()) {
+ m_exception_language = language;
+ break;
+ }
+ }
+ error_context = "Unsupported language type for exception breakpoint";
+ }
+ if (!error_context.empty())
+ error = CreateOptionParsingError(option_arg, short_option,
+ long_option, error_context);
+ } 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 =
+ CreateOptionParsingError(option_arg, short_option, long_option,
+ g_bool_parsing_error_message);
+ } 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 =
+ CreateOptionParsingError(option_arg, short_option, long_option,
+ g_bool_parsing_error_message);
+ } break;
+
+ case 'l':
+ if (option_arg.getAsInteger(0, m_line_num))
+ error =
+ CreateOptionParsingError(option_arg, short_option, long_option,
+ g_int_parsing_error_message);
+ break;
+
+ case 'L':
+ m_language = Language::GetLanguageTypeFromString(option_arg);
+ if (m_language == eLanguageTypeUnknown)
+ error =
+ CreateOptionParsingError(option_arg, short_option, long_option,
+ g_language_parsing_error_message);
+ 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 =
+ CreateOptionParsingError(option_arg, short_option, long_option,
+ g_bool_parsing_error_message);
+ 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 = CreateOptionParsingError(
+ option_arg, short_option, long_option, "Invalid breakpoint name");
+ 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 =
+ CreateOptionParsingError(option_arg, short_option, long_option,
+ g_bool_parsing_error_message);
+ } 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 = CreateOptionParsingError(option_arg, short_option,
+ long_option, 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::ArrayRef(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:
+ void 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;
+ }
+ } else if (num_files > 1) {
+ result.AppendError("Only one file at a time is allowed for file and "
+ "line breakpoints.");
+ return;
+ } 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.GetFileSpecAtIndex(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;
+ }
+ 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;
+ }
+
+ 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;
+ } 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;
+ }
+ 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;
+ }
+ } 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;
+ }
+ } 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;
+ }
+ }
+ }
+ }
+
+ 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.");
+ }
+ }
+
+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.GetFile()) {
+ file = sc.line_entry.GetFile();
+ } 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) {
+ CommandObject::AddIDsArgumentData(eBreakpointArgs);
+
+ 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 {
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::eBreakpointCompletion, request, nullptr);
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ void 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());
+ }
+ }
+ }
+ }
+ }
+
+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) {
+ CommandObject::AddIDsArgumentData(eBreakpointArgs);
+ }
+
+ ~CommandObjectBreakpointEnable() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::eBreakpointCompletion, request, nullptr);
+ }
+
+protected:
+ void 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;
+ }
+
+ 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);
+ }
+ }
+ }
+};
+
+// 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.");
+
+ CommandObject::AddIDsArgumentData(eBreakpointArgs);
+ }
+
+ ~CommandObjectBreakpointDisable() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::eBreakpointCompletion, request, nullptr);
+ }
+
+protected:
+ void 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;
+ }
+
+ 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);
+ }
+ }
+ }
+};
+
+// 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.
+ AddSimpleArgumentList(eArgTypeBreakpointID, eArgRepeatOptional);
+ }
+
+ ~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::ArrayRef(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:
+ void 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;
+ }
+
+ 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.");
+ }
+ }
+ }
+
+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::ArrayRef(g_breakpoint_clear_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ std::string m_filename;
+ uint32_t m_line_num = 0;
+ };
+
+protected:
+ void 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;
+ }
+
+ // 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.");
+ }
+ }
+
+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) {
+ CommandObject::AddIDsArgumentData(eBreakpointArgs);
+ }
+
+ ~CommandObjectBreakpointDelete() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::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::ArrayRef(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:
+ void 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;
+ }
+
+ // 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;
+ }
+
+ // 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;
+ }
+
+ for (auto breakpoint_sp : breakpoints.Breakpoints()) {
+ if (!breakpoint_sp->IsEnabled() && breakpoint_sp->AllowDelete()) {
+ BreakpointID bp_id(breakpoint_sp->GetID());
+ if (!excluded_bp_ids.Contains(bp_id))
+ valid_bp_ids.AddBreakpointID(bp_id);
+ }
+ }
+ if (valid_bp_ids.GetSize() == 0) {
+ result.AppendError("No disabled breakpoints.");
+ return;
+ }
+ } else {
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
+ command, &target, result, &valid_bp_ids,
+ BreakpointName::Permissions::PermissionKinds::deletePerm);
+ if (!result.Succeeded())
+ return;
+ }
+
+ 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);
+ }
+
+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::ArrayRef(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;
+ const char *long_option = g_breakpoint_name_options[option_idx].long_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 = CreateOptionParsingError(option_arg, short_option, long_option,
+ g_int_parsing_error_message);
+ break;
+ case 'D':
+ if (m_use_dummy.SetValueFromString(option_arg).Fail())
+ error = CreateOptionParsingError(option_arg, short_option, long_option,
+ g_bool_parsing_error_message);
+ 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::ArrayRef(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;
+ const char *long_option =
+ g_breakpoint_access_options[option_idx].long_option;
+
+ switch (short_option) {
+ case 'L': {
+ bool value, success;
+ value = OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (success) {
+ m_permissions.SetAllowList(value);
+ } else
+ error = CreateOptionParsingError(option_arg, short_option, long_option,
+ g_bool_parsing_error_message);
+ } break;
+ case 'A': {
+ bool value, success;
+ value = OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (success) {
+ m_permissions.SetAllowDisable(value);
+ } else
+ error = CreateOptionParsingError(option_arg, short_option, long_option,
+ g_bool_parsing_error_message);
+ } break;
+ case 'D': {
+ bool value, success;
+ value = OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (success) {
+ m_permissions.SetAllowDelete(value);
+ } else
+ error = CreateOptionParsingError(option_arg, short_option, long_option,
+ g_bool_parsing_error_message);
+ } 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>") {
+ AddSimpleArgumentList(eArgTypeBreakpointName, eArgRepeatOptional);
+
+ 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:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+
+ const size_t argc = command.GetArgumentCount();
+ if (argc == 0) {
+ result.AppendError("No names provided.");
+ return;
+ }
+
+ 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;
+ }
+ }
+ // 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.GetValueAs<uint64_t>().value_or(0);
+ bp_sp = target.GetBreakpointByID(bp_id);
+ if (!bp_sp) {
+ result.AppendErrorWithFormatv("Could not find specified breakpoint {0}",
+ bp_id);
+ return;
+ }
+ }
+
+ 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.GetValueAs<llvm::StringRef>()
+ .value_or("")
+ .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());
+ }
+ }
+
+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>") {
+ AddSimpleArgumentList(eArgTypeBreakpointID, eArgRepeatOptional);
+
+ 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 {
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::eBreakpointCompletion, request, nullptr);
+ }
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ if (!m_name_options.m_name.OptionWasSet()) {
+ result.AppendError("No name option provided.");
+ return;
+ }
+
+ 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;
+ }
+
+ // 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;
+ }
+ 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);
+ }
+ }
+ }
+
+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>") {
+ AddSimpleArgumentList(eArgTypeBreakpointID, eArgRepeatOptional);
+
+ 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 {
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::eBreakpointCompletion, request, nullptr);
+ }
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ if (!m_name_options.m_name.OptionWasSet()) {
+ result.AppendError("No name option provided.");
+ return;
+ }
+
+ 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;
+ }
+
+ // 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;
+ }
+ 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);
+ }
+ }
+ }
+
+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:
+ void 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);
+ }
+ }
+ }
+ }
+
+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;
+ const char *long_option =
+ m_getopt_table[option_idx].definition->long_option;
+
+ 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 = CreateOptionParsingError(option_arg, short_option,
+ long_option, 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::ArrayRef(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':
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ interpreter, lldb::eDiskFileCompletion, request, nullptr);
+ break;
+
+ case 'N':
+ std::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++) {
+ if (std::optional<llvm::StringRef> maybe_name =
+ names_array->GetItemAtIndexAsString(i))
+ request.TryCompleteCurrentArg(*maybe_name);
+ }
+ }
+ }
+ }
+
+ std::string m_filename;
+ std::vector<std::string> m_names;
+ };
+
+protected:
+ void 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;
+ }
+
+ 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);
+ }
+ }
+ }
+
+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) {
+ CommandObject::AddIDsArgumentData(eBreakpointArgs);
+ }
+
+ ~CommandObjectBreakpointWrite() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::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::ArrayRef(g_breakpoint_write_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ std::string m_filename;
+ bool m_append = false;
+ };
+
+protected:
+ void 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;
+ }
+ }
+ 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());
+ }
+ }
+
+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.
+
+ if (llvm::Error err = BreakpointIDList::FindAndReplaceIDRanges(
+ args, target, allow_locations, purpose, temp_args)) {
+ result.SetError(std::move(err));
+ return;
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+
+ // NOW, convert the list of breakpoint id strings in TEMP_ARGS into an actual
+ // BreakpointIDList:
+
+ for (llvm::StringRef temp_arg : temp_args.GetArgumentArrayRef())
+ if (auto bp_id = BreakpointID::ParseCanonicalReference(temp_arg))
+ valid_ids->AddBreakpointID(*bp_id);
+
+ // At this point, all of the breakpoint ids that the user passed in have
+ // been converted to breakpoint IDs and put into valid_ids.
+
+ // 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..6ebe6e8a3557
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpointCommand.cpp
@@ -0,0 +1,654 @@
+//===-- 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/CommandOptionArgumentTable.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;
+
+#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();
+
+ AddSimpleArgumentList(eArgTypeBreakpointID, eArgRepeatOptional);
+ }
+
+ ~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::ArrayRef(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:
+ void 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;
+ }
+
+ 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);
+ }
+ }
+ }
+
+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) {
+ AddSimpleArgumentList(eArgTypeBreakpointID);
+ }
+
+ ~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::ArrayRef(g_breakpoint_command_delete_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ bool m_use_dummy = false;
+ };
+
+protected:
+ void 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;
+ }
+
+ if (command.empty()) {
+ result.AppendError(
+ "No breakpoint specified from which to delete the commands");
+ return;
+ }
+
+ 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;
+ }
+ } else {
+ bp->ClearCallback();
+ }
+ }
+ }
+ }
+ }
+
+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) {
+ AddSimpleArgumentList(eArgTypeBreakpointID);
+ }
+
+ ~CommandObjectBreakpointCommandList() override = default;
+
+protected:
+ void 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;
+ }
+
+ if (command.empty()) {
+ result.AppendError(
+ "No breakpoint specified for which to list the commands");
+ return;
+ }
+
+ 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;
+ }
+ }
+
+ 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());
+ }
+ }
+ }
+ }
+};
+
+// 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..c63445b7c8c8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectCommands.cpp
@@ -0,0 +1,2753 @@
+//===-- 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/CommandOptionArgumentTable.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"
+#include <optional>
+
+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) {
+ AddSimpleArgumentList(eArgTypeFilename);
+ }
+
+ ~CommandObjectCommandsSource() override = default;
+
+ std::optional<std::string> GetRepeatCommand(Args &current_command_args,
+ uint32_t index) override {
+ return std::string("");
+ }
+
+ 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::ArrayRef(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;
+ };
+
+ void 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;
+ }
+
+ 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;
+ }
+ }
+
+ 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;
+ }
+ 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);
+ }
+
+ 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, exe_ctx, 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::ArrayRef(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:
+ void DoExecute(llvm::StringRef raw_command_line,
+ CommandReturnObject &result) override {
+ if (raw_command_line.empty()) {
+ result.AppendError("'command alias' requires at least two arguments");
+ return;
+ }
+
+ 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;
+
+ 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;
+ }
+
+ // Get the alias command.
+
+ auto alias_command = args[0].ref();
+ if (alias_command.starts_with("-")) {
+ 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;
+ }
+
+ // 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;
+ }
+
+ // 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;
+ }
+
+ 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;
+ }
+
+ // 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());
+ } 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.
+ HandleAliasingNormalCommand(args, result);
+ } else {
+ HandleAliasingRawCommand(alias_command, raw_command_string, *cmd_obj,
+ result);
+ }
+ }
+
+ 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;
+ // Look up the command using command's name first. This is to resolve
+ // aliases when you are making nested aliases. But if you don't find
+ // it that way, then it wasn't an alias and we can just use the object
+ // we were passed in.
+ CommandObjectSP cmd_obj_sp = m_interpreter.GetCommandSPExact(
+ cmd_obj.GetCommandName(), include_aliases);
+ if (!cmd_obj_sp)
+ cmd_obj_sp = cmd_obj.shared_from_this();
+
+ 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");
+ }
+ 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) {
+ AddSimpleArgumentList(eArgTypeAliasName);
+ }
+
+ ~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:
+ void 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+};
+
+#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) {
+ AddSimpleArgumentList(eArgTypeCommandName);
+ }
+
+ ~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:
+ void 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;
+ }
+
+ 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;
+ }
+
+ if (!m_interpreter.RemoveCommand(command_name)) {
+ result.AppendErrorWithFormat(
+ "'%s' is a permanent debugger command and cannot be removed.\n",
+ args[0].c_str());
+ return;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+};
+
+// 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/')");
+ AddSimpleArgumentList(eArgTypeSEDStylePair, eArgRepeatOptional);
+ }
+
+ ~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);
+ }
+ }
+ }
+
+ void 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;
+ }
+
+ Status error;
+ auto name = command[0].ref();
+ m_regex_cmd_up = std::make_unique<CommandObjectRegexCommand>(
+ m_interpreter, name, m_options.GetHelp(), m_options.GetSyntax(), 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));
+
+ 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());
+ }
+ }
+
+ 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::ArrayRef(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,
+ CompletionType completion_type)
+ : CommandObjectRaw(interpreter, name), m_function_name(funct),
+ m_synchro(synch), m_completion_type(completion_type) {
+ 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();
+ }
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), m_completion_type, request, nullptr);
+ }
+
+ bool WantsCompletion() override { return true; }
+
+protected:
+ void DoExecute(llvm::StringRef raw_command_line,
+ CommandReturnObject &result) override {
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+
+ m_interpreter.IncreaseCommandUsage(*this);
+
+ 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);
+ }
+ }
+ }
+
+private:
+ std::string m_function_name;
+ ScriptedCommandSynchronicity m_synchro;
+ bool m_fetched_help_long = false;
+ CompletionType m_completion_type = eNoCompletion;
+};
+
+/// This class implements a "raw" scripted command. lldb does no parsing of the
+/// command line, instead passing the line unaltered (except for backtick
+/// substitution).
+class CommandObjectScriptingObjectRaw : public CommandObjectRaw {
+public:
+ CommandObjectScriptingObjectRaw(CommandInterpreter &interpreter,
+ std::string name,
+ StructuredData::GenericSP cmd_obj_sp,
+ ScriptedCommandSynchronicity synch,
+ CompletionType completion_type)
+ : CommandObjectRaw(interpreter, name), m_cmd_obj_sp(cmd_obj_sp),
+ m_synchro(synch), m_fetched_help_short(false),
+ m_fetched_help_long(false), m_completion_type(completion_type) {
+ 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));
+ }
+
+ ~CommandObjectScriptingObjectRaw() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), m_completion_type, request, nullptr);
+ }
+
+ bool WantsCompletion() override { return true; }
+
+ bool IsRemovable() const override { return true; }
+
+ ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; }
+
+ std::optional<std::string> GetRepeatCommand(Args &args,
+ uint32_t index) override {
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+ if (!scripter)
+ return std::nullopt;
+
+ return scripter->GetRepeatCommandForScriptedCommand(m_cmd_obj_sp, args);
+ }
+
+ 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:
+ void 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);
+ }
+ }
+ }
+
+private:
+ StructuredData::GenericSP m_cmd_obj_sp;
+ ScriptedCommandSynchronicity m_synchro;
+ bool m_fetched_help_short : 1;
+ bool m_fetched_help_long : 1;
+ CompletionType m_completion_type = eNoCompletion;
+};
+
+
+/// This command implements a lldb parsed scripted command. The command
+/// provides a definition of the options and arguments, and a option value
+/// setting callback, and then the command's execution function gets passed
+/// just the parsed arguments.
+/// Note, implementing a command in Python using these base interfaces is a bit
+/// of a pain, but it is much easier to export this low level interface, and
+/// then make it nicer on the Python side, than to try to do that in a
+/// script language neutral way.
+/// So I've also added a base class in Python that provides a table-driven
+/// way of defining the options and arguments, which automatically fills the
+/// option values, making them available as properties in Python.
+///
+class CommandObjectScriptingObjectParsed : public CommandObjectParsed {
+private:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions(CommandInterpreter &interpreter,
+ StructuredData::GenericSP cmd_obj_sp) : m_interpreter(interpreter),
+ m_cmd_obj_sp(cmd_obj_sp) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ ScriptInterpreter *scripter =
+ m_interpreter.GetDebugger().GetScriptInterpreter();
+ if (!scripter) {
+ error.SetErrorString("No script interpreter for SetOptionValue.");
+ return error;
+ }
+ if (!m_cmd_obj_sp) {
+ error.SetErrorString("SetOptionValue called with empty cmd_obj.");
+ return error;
+ }
+ if (!m_options_definition_up) {
+ error.SetErrorString("SetOptionValue called before options definitions "
+ "were created.");
+ return error;
+ }
+ // Pass the long option, since you aren't actually required to have a
+ // short_option, and for those options the index or short option character
+ // aren't meaningful on the python side.
+ const char * long_option =
+ m_options_definition_up.get()[option_idx].long_option;
+ bool success = scripter->SetOptionValueForCommandObject(m_cmd_obj_sp,
+ execution_context, long_option, option_arg);
+ if (!success)
+ error.SetErrorStringWithFormatv("Error setting option: {0} to {1}",
+ long_option, option_arg);
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ ScriptInterpreter *scripter =
+ m_interpreter.GetDebugger().GetScriptInterpreter();
+ if (!scripter || !m_cmd_obj_sp)
+ return;
+
+ scripter->OptionParsingStartedForCommandObject(m_cmd_obj_sp);
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ if (!m_options_definition_up)
+ return {};
+ return llvm::ArrayRef(m_options_definition_up.get(), m_num_options);
+ }
+
+ static Status ParseUsageMaskFromArray(StructuredData::ObjectSP obj_sp,
+ size_t counter, uint32_t &usage_mask) {
+ // If the usage entry is not provided, we use LLDB_OPT_SET_ALL.
+ // If the usage mask is a UINT, the option belongs to that group.
+ // If the usage mask is a vector of UINT's, the option belongs to all the
+ // groups listed.
+ // If a subelement of the vector is a vector of two ints, then the option
+ // belongs to the inclusive range from the first to the second element.
+ Status error;
+ if (!obj_sp) {
+ usage_mask = LLDB_OPT_SET_ALL;
+ return error;
+ }
+
+ usage_mask = 0;
+
+ StructuredData::UnsignedInteger *uint_val =
+ obj_sp->GetAsUnsignedInteger();
+ if (uint_val) {
+ // If this is an integer, then this specifies a single group:
+ uint32_t value = uint_val->GetValue();
+ if (value == 0) {
+ error.SetErrorStringWithFormatv(
+ "0 is not a valid group for option {0}", counter);
+ return error;
+ }
+ usage_mask = (1 << (value - 1));
+ return error;
+ }
+ // Otherwise it has to be an array:
+ StructuredData::Array *array_val = obj_sp->GetAsArray();
+ if (!array_val) {
+ error.SetErrorStringWithFormatv(
+ "required field is not a array for option {0}", counter);
+ return error;
+ }
+ // This is the array ForEach for accumulating a group usage mask from
+ // an array of string descriptions of groups.
+ auto groups_accumulator
+ = [counter, &usage_mask, &error]
+ (StructuredData::Object *obj) -> bool {
+ StructuredData::UnsignedInteger *int_val = obj->GetAsUnsignedInteger();
+ if (int_val) {
+ uint32_t value = int_val->GetValue();
+ if (value == 0) {
+ error.SetErrorStringWithFormatv(
+ "0 is not a valid group for element {0}", counter);
+ return false;
+ }
+ usage_mask |= (1 << (value - 1));
+ return true;
+ }
+ StructuredData::Array *arr_val = obj->GetAsArray();
+ if (!arr_val) {
+ error.SetErrorStringWithFormatv(
+ "Group element not an int or array of integers for element {0}",
+ counter);
+ return false;
+ }
+ size_t num_range_elem = arr_val->GetSize();
+ if (num_range_elem != 2) {
+ error.SetErrorStringWithFormatv(
+ "Subranges of a group not a start and a stop for element {0}",
+ counter);
+ return false;
+ }
+ int_val = arr_val->GetItemAtIndex(0)->GetAsUnsignedInteger();
+ if (!int_val) {
+ error.SetErrorStringWithFormatv("Start element of a subrange of a "
+ "group not unsigned int for element {0}", counter);
+ return false;
+ }
+ uint32_t start = int_val->GetValue();
+ int_val = arr_val->GetItemAtIndex(1)->GetAsUnsignedInteger();
+ if (!int_val) {
+ error.SetErrorStringWithFormatv("End element of a subrange of a group"
+ " not unsigned int for element {0}", counter);
+ return false;
+ }
+ uint32_t end = int_val->GetValue();
+ if (start == 0 || end == 0 || start > end) {
+ error.SetErrorStringWithFormatv("Invalid subrange of a group: {0} - "
+ "{1} for element {2}", start, end, counter);
+ return false;
+ }
+ for (uint32_t i = start; i <= end; i++) {
+ usage_mask |= (1 << (i - 1));
+ }
+ return true;
+ };
+ array_val->ForEach(groups_accumulator);
+ return error;
+ }
+
+
+ Status SetOptionsFromArray(StructuredData::Dictionary &options) {
+ Status error;
+ m_num_options = options.GetSize();
+ m_options_definition_up.reset(new OptionDefinition[m_num_options]);
+ // We need to hand out pointers to contents of these vectors; we reserve
+ // as much as we'll need up front so they don't get freed on resize...
+ m_usage_container.resize(m_num_options);
+ m_enum_storage.resize(m_num_options);
+ m_enum_vector.resize(m_num_options);
+
+ size_t counter = 0;
+ size_t short_opt_counter = 0;
+ // This is the Array::ForEach function for adding option elements:
+ auto add_element = [this, &error, &counter, &short_opt_counter]
+ (llvm::StringRef long_option, StructuredData::Object *object) -> bool {
+ StructuredData::Dictionary *opt_dict = object->GetAsDictionary();
+ if (!opt_dict) {
+ error.SetErrorString("Value in options dictionary is not a dictionary");
+ return false;
+ }
+ OptionDefinition &option_def = m_options_definition_up.get()[counter];
+
+ // We aren't exposing the validator yet, set it to null
+ option_def.validator = nullptr;
+ // We don't require usage masks, so set it to one group by default:
+ option_def.usage_mask = 1;
+
+ // Now set the fields of the OptionDefinition Array from the dictionary:
+ //
+ // Note that I don't check for unknown fields in the option dictionaries
+ // so a scriptor can add extra elements that are helpful when they go to
+ // do "set_option_value"
+
+ // Usage Mask:
+ StructuredData::ObjectSP obj_sp = opt_dict->GetValueForKey("groups");
+ if (obj_sp) {
+ error = ParseUsageMaskFromArray(obj_sp, counter,
+ option_def.usage_mask);
+ if (error.Fail())
+ return false;
+ }
+
+ // Required:
+ option_def.required = false;
+ obj_sp = opt_dict->GetValueForKey("required");
+ if (obj_sp) {
+ StructuredData::Boolean *boolean_val = obj_sp->GetAsBoolean();
+ if (!boolean_val) {
+ error.SetErrorStringWithFormatv("'required' field is not a boolean "
+ "for option {0}", counter);
+ return false;
+ }
+ option_def.required = boolean_val->GetValue();
+ }
+
+ // Short Option:
+ int short_option;
+ obj_sp = opt_dict->GetValueForKey("short_option");
+ if (obj_sp) {
+ // The value is a string, so pull the
+ llvm::StringRef short_str = obj_sp->GetStringValue();
+ if (short_str.empty()) {
+ error.SetErrorStringWithFormatv("short_option field empty for "
+ "option {0}", counter);
+ return false;
+ } else if (short_str.size() != 1) {
+ error.SetErrorStringWithFormatv("short_option field has extra "
+ "characters for option {0}", counter);
+ return false;
+ }
+ short_option = (int) short_str[0];
+ } else {
+ // If the short option is not provided, then we need a unique value
+ // less than the lowest printable ASCII character.
+ short_option = short_opt_counter++;
+ }
+ option_def.short_option = short_option;
+
+ // Long Option is the key from the outer dict:
+ if (long_option.empty()) {
+ error.SetErrorStringWithFormatv("empty long_option for option {0}",
+ counter);
+ return false;
+ }
+ auto inserted = g_string_storer.insert(long_option.str());
+ option_def.long_option = ((*(inserted.first)).data());
+
+ // Value Type:
+ obj_sp = opt_dict->GetValueForKey("value_type");
+ if (obj_sp) {
+ StructuredData::UnsignedInteger *uint_val
+ = obj_sp->GetAsUnsignedInteger();
+ if (!uint_val) {
+ error.SetErrorStringWithFormatv("Value type must be an unsigned "
+ "integer");
+ return false;
+ }
+ uint64_t val_type = uint_val->GetValue();
+ if (val_type >= eArgTypeLastArg) {
+ error.SetErrorStringWithFormatv("Value type {0} beyond the "
+ "CommandArgumentType bounds", val_type);
+ return false;
+ }
+ option_def.argument_type = (CommandArgumentType) val_type;
+ option_def.option_has_arg = true;
+ } else {
+ option_def.argument_type = eArgTypeNone;
+ option_def.option_has_arg = false;
+ }
+
+ // Completion Type:
+ obj_sp = opt_dict->GetValueForKey("completion_type");
+ if (obj_sp) {
+ StructuredData::UnsignedInteger *uint_val = obj_sp->GetAsUnsignedInteger();
+ if (!uint_val) {
+ error.SetErrorStringWithFormatv("Completion type must be an "
+ "unsigned integer for option {0}", counter);
+ return false;
+ }
+ uint64_t completion_type = uint_val->GetValue();
+ if (completion_type > eCustomCompletion) {
+ error.SetErrorStringWithFormatv("Completion type for option {0} "
+ "beyond the CompletionType bounds", completion_type);
+ return false;
+ }
+ option_def.completion_type = (CommandArgumentType) completion_type;
+ } else
+ option_def.completion_type = eNoCompletion;
+
+ // Usage Text:
+ std::string usage_text;
+ obj_sp = opt_dict->GetValueForKey("help");
+ if (!obj_sp) {
+ error.SetErrorStringWithFormatv("required usage missing from option "
+ "{0}", counter);
+ return false;
+ }
+ llvm::StringRef usage_stref;
+ usage_stref = obj_sp->GetStringValue();
+ if (usage_stref.empty()) {
+ error.SetErrorStringWithFormatv("empty usage text for option {0}",
+ counter);
+ return false;
+ }
+ m_usage_container[counter] = usage_stref.str().c_str();
+ option_def.usage_text = m_usage_container[counter].data();
+
+ // Enum Values:
+
+ obj_sp = opt_dict->GetValueForKey("enum_values");
+ if (obj_sp) {
+ StructuredData::Array *array = obj_sp->GetAsArray();
+ if (!array) {
+ error.SetErrorStringWithFormatv("enum values must be an array for "
+ "option {0}", counter);
+ return false;
+ }
+ size_t num_elem = array->GetSize();
+ size_t enum_ctr = 0;
+ m_enum_storage[counter] = std::vector<EnumValueStorage>(num_elem);
+ std::vector<EnumValueStorage> &curr_elem = m_enum_storage[counter];
+
+ // This is the Array::ForEach function for adding enum elements:
+ // Since there are only two fields to specify the enum, use a simple
+ // two element array with value first, usage second.
+ // counter is only used for reporting so I pass it by value here.
+ auto add_enum = [&enum_ctr, &curr_elem, counter, &error]
+ (StructuredData::Object *object) -> bool {
+ StructuredData::Array *enum_arr = object->GetAsArray();
+ if (!enum_arr) {
+ error.SetErrorStringWithFormatv("Enum values for option {0} not "
+ "an array", counter);
+ return false;
+ }
+ size_t num_enum_elements = enum_arr->GetSize();
+ if (num_enum_elements != 2) {
+ error.SetErrorStringWithFormatv("Wrong number of elements: {0} "
+ "for enum {1} in option {2}",
+ num_enum_elements, enum_ctr, counter);
+ return false;
+ }
+ // Enum Value:
+ StructuredData::ObjectSP obj_sp = enum_arr->GetItemAtIndex(0);
+ llvm::StringRef val_stref = obj_sp->GetStringValue();
+ std::string value_cstr_str = val_stref.str().c_str();
+
+ // Enum Usage:
+ obj_sp = enum_arr->GetItemAtIndex(1);
+ if (!obj_sp) {
+ error.SetErrorStringWithFormatv("No usage for enum {0} in option "
+ "{1}", enum_ctr, counter);
+ return false;
+ }
+ llvm::StringRef usage_stref = obj_sp->GetStringValue();
+ std::string usage_cstr_str = usage_stref.str().c_str();
+ curr_elem[enum_ctr] = EnumValueStorage(value_cstr_str,
+ usage_cstr_str, enum_ctr);
+
+ enum_ctr++;
+ return true;
+ }; // end of add_enum
+
+ array->ForEach(add_enum);
+ if (!error.Success())
+ return false;
+ // We have to have a vector of elements to set in the options, make
+ // that here:
+ for (auto &elem : curr_elem)
+ m_enum_vector[counter].emplace_back(elem.element);
+
+ option_def.enum_values = llvm::ArrayRef(m_enum_vector[counter]);
+ }
+ counter++;
+ return true;
+ }; // end of add_element
+
+ options.ForEach(add_element);
+ return error;
+ }
+
+ size_t GetNumOptions() { return m_num_options; }
+
+ private:
+ struct EnumValueStorage {
+ EnumValueStorage() {
+ element.string_value = "value not set";
+ element.usage = "usage not set";
+ element.value = 0;
+ }
+
+ EnumValueStorage(std::string in_str_val, std::string in_usage,
+ size_t in_value) : value(std::move(in_str_val)), usage(std::move(in_usage)) {
+ SetElement(in_value);
+ }
+
+ EnumValueStorage(const EnumValueStorage &in) : value(in.value),
+ usage(in.usage) {
+ SetElement(in.element.value);
+ }
+
+ EnumValueStorage &operator=(const EnumValueStorage &in) {
+ value = in.value;
+ usage = in.usage;
+ SetElement(in.element.value);
+ return *this;
+ }
+
+ void SetElement(size_t in_value) {
+ element.value = in_value;
+ element.string_value = value.data();
+ element.usage = usage.data();
+ }
+
+ std::string value;
+ std::string usage;
+ OptionEnumValueElement element;
+ };
+ // We have to provide char * values for the long option, usage and enum
+ // values, that's what the option definitions hold.
+ // The long option strings are quite likely to be reused in other added
+ // commands, so those are stored in a global set: g_string_storer.
+ // But the usages are much less likely to be reused, so those are stored in
+ // a vector in the command instance. It gets resized to the correct size
+ // and then filled with null-terminated strings in the std::string, so the
+ // are valid C-strings that won't move around.
+ // The enum values and descriptions are treated similarly - these aren't
+ // all that common so it's not worth the effort to dedup them.
+ size_t m_num_options = 0;
+ std::unique_ptr<OptionDefinition> m_options_definition_up;
+ std::vector<std::vector<EnumValueStorage>> m_enum_storage;
+ std::vector<std::vector<OptionEnumValueElement>> m_enum_vector;
+ std::vector<std::string> m_usage_container;
+ CommandInterpreter &m_interpreter;
+ StructuredData::GenericSP m_cmd_obj_sp;
+ static std::unordered_set<std::string> g_string_storer;
+ };
+
+public:
+ static CommandObjectSP Create(CommandInterpreter &interpreter,
+ std::string name,
+ StructuredData::GenericSP cmd_obj_sp,
+ ScriptedCommandSynchronicity synch,
+ CommandReturnObject &result) {
+ CommandObjectSP new_cmd_sp(new CommandObjectScriptingObjectParsed(
+ interpreter, name, cmd_obj_sp, synch));
+
+ CommandObjectScriptingObjectParsed *parsed_cmd
+ = static_cast<CommandObjectScriptingObjectParsed *>(new_cmd_sp.get());
+ // Now check all the failure modes, and report if found.
+ Status opt_error = parsed_cmd->GetOptionsError();
+ Status arg_error = parsed_cmd->GetArgsError();
+
+ if (opt_error.Fail())
+ result.AppendErrorWithFormat("failed to parse option definitions: %s",
+ opt_error.AsCString());
+ if (arg_error.Fail())
+ result.AppendErrorWithFormat("%sfailed to parse argument definitions: %s",
+ opt_error.Fail() ? ", also " : "",
+ arg_error.AsCString());
+
+ if (!result.Succeeded())
+ return {};
+
+ return new_cmd_sp;
+ }
+
+ CommandObjectScriptingObjectParsed(CommandInterpreter &interpreter,
+ std::string name,
+ StructuredData::GenericSP cmd_obj_sp,
+ ScriptedCommandSynchronicity synch)
+ : CommandObjectParsed(interpreter, name.c_str()),
+ m_cmd_obj_sp(cmd_obj_sp), m_synchro(synch),
+ m_options(interpreter, cmd_obj_sp), m_fetched_help_short(false),
+ m_fetched_help_long(false) {
+ StreamString stream;
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+ if (!scripter) {
+ m_options_error.SetErrorString("No script interpreter");
+ return;
+ }
+
+ // Set the flags:
+ GetFlags().Set(scripter->GetFlagsForCommandObject(cmd_obj_sp));
+
+ // Now set up the options definitions from the options:
+ StructuredData::ObjectSP options_object_sp
+ = scripter->GetOptionsForCommandObject(cmd_obj_sp);
+ // It's okay not to have an options dict.
+ if (options_object_sp) {
+ // The options come as a dictionary of dictionaries. The key of the
+ // outer dict is the long option name (since that's required). The
+ // value holds all the other option specification bits.
+ StructuredData::Dictionary *options_dict
+ = options_object_sp->GetAsDictionary();
+ // but if it exists, it has to be an array.
+ if (options_dict) {
+ m_options_error = m_options.SetOptionsFromArray(*(options_dict));
+ // If we got an error don't bother with the arguments...
+ if (m_options_error.Fail())
+ return;
+ } else {
+ m_options_error.SetErrorString("Options array not an array");
+ return;
+ }
+ }
+ // Then fetch the args. Since the arguments can have usage masks you need
+ // an array of arrays.
+ StructuredData::ObjectSP args_object_sp
+ = scripter->GetArgumentsForCommandObject(cmd_obj_sp);
+ if (args_object_sp) {
+ StructuredData::Array *args_array = args_object_sp->GetAsArray();
+ if (!args_array) {
+ m_args_error.SetErrorString("Argument specification is not an array");
+ return;
+ }
+ size_t counter = 0;
+
+ // This is the Array::ForEach function that handles the
+ // CommandArgumentEntry arrays one by one:
+ auto arg_array_adder = [this, &counter] (StructuredData::Object *object)
+ -> bool {
+ // This is the Array::ForEach function to add argument entries:
+ CommandArgumentEntry this_entry;
+ size_t elem_counter = 0;
+ auto args_adder = [this, counter, &elem_counter, &this_entry]
+ (StructuredData::Object *object) -> bool {
+ // The arguments definition has three fields, the argument type, the
+ // repeat and the usage mask.
+ CommandArgumentType arg_type = eArgTypeNone;
+ ArgumentRepetitionType arg_repetition = eArgRepeatOptional;
+ uint32_t arg_opt_set_association;
+
+ auto report_error = [this, elem_counter, counter]
+ (const char *err_txt) -> bool {
+ m_args_error.SetErrorStringWithFormatv("Element {0} of arguments "
+ "list element {1}: %s.", elem_counter, counter, err_txt);
+ return false;
+ };
+
+ StructuredData::Dictionary *arg_dict = object->GetAsDictionary();
+ if (!arg_dict) {
+ report_error("is not a dictionary.");
+ return false;
+ }
+ // Argument Type:
+ StructuredData::ObjectSP obj_sp
+ = arg_dict->GetValueForKey("arg_type");
+ if (obj_sp) {
+ StructuredData::UnsignedInteger *uint_val
+ = obj_sp->GetAsUnsignedInteger();
+ if (!uint_val) {
+ report_error("value type must be an unsigned integer");
+ return false;
+ }
+ uint64_t arg_type_int = uint_val->GetValue();
+ if (arg_type_int >= eArgTypeLastArg) {
+ report_error("value type beyond ArgumentRepetitionType bounds");
+ return false;
+ }
+ arg_type = (CommandArgumentType) arg_type_int;
+ }
+ // Repeat Value:
+ obj_sp = arg_dict->GetValueForKey("repeat");
+ std::optional<ArgumentRepetitionType> repeat;
+ if (obj_sp) {
+ llvm::StringRef repeat_str = obj_sp->GetStringValue();
+ if (repeat_str.empty()) {
+ report_error("repeat value is empty");
+ return false;
+ }
+ repeat = ArgRepetitionFromString(repeat_str);
+ if (!repeat) {
+ report_error("invalid repeat value");
+ return false;
+ }
+ arg_repetition = *repeat;
+ }
+
+ // Usage Mask:
+ obj_sp = arg_dict->GetValueForKey("groups");
+ m_args_error = CommandOptions::ParseUsageMaskFromArray(obj_sp,
+ counter, arg_opt_set_association);
+ this_entry.emplace_back(arg_type, arg_repetition,
+ arg_opt_set_association);
+ elem_counter++;
+ return true;
+ };
+ StructuredData::Array *args_array = object->GetAsArray();
+ if (!args_array) {
+ m_args_error.SetErrorStringWithFormatv("Argument definition element "
+ "{0} is not an array", counter);
+ }
+
+ args_array->ForEach(args_adder);
+ if (m_args_error.Fail())
+ return false;
+ if (this_entry.empty()) {
+ m_args_error.SetErrorStringWithFormatv("Argument definition element "
+ "{0} is empty", counter);
+ return false;
+ }
+ m_arguments.push_back(this_entry);
+ counter++;
+ return true;
+ }; // end of arg_array_adder
+ // Here we actually parse the args definition:
+ args_array->ForEach(arg_array_adder);
+ }
+ }
+
+ ~CommandObjectScriptingObjectParsed() override = default;
+
+ Status GetOptionsError() { return m_options_error; }
+ Status GetArgsError() { return m_args_error; }
+ bool WantsCompletion() override { return true; }
+
+ bool IsRemovable() const override { return true; }
+
+ ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; }
+
+ std::optional<std::string> GetRepeatCommand(Args &args,
+ uint32_t index) override {
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+ if (!scripter)
+ return std::nullopt;
+
+ return scripter->GetRepeatCommandForScriptedCommand(m_cmd_obj_sp, args);
+ }
+
+ llvm::StringRef GetHelp() override {
+ if (m_fetched_help_short)
+ return CommandObjectParsed::GetHelp();
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+ if (!scripter)
+ return CommandObjectParsed::GetHelp();
+ std::string docstring;
+ m_fetched_help_short =
+ scripter->GetShortHelpForCommandObject(m_cmd_obj_sp, docstring);
+ if (!docstring.empty())
+ SetHelp(docstring);
+
+ return CommandObjectParsed::GetHelp();
+ }
+
+ llvm::StringRef GetHelpLong() override {
+ if (m_fetched_help_long)
+ return CommandObjectParsed::GetHelpLong();
+
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+ if (!scripter)
+ return CommandObjectParsed::GetHelpLong();
+
+ std::string docstring;
+ m_fetched_help_long =
+ scripter->GetLongHelpForCommandObject(m_cmd_obj_sp, docstring);
+ if (!docstring.empty())
+ SetHelpLong(docstring);
+ return CommandObjectParsed::GetHelpLong();
+ }
+
+ Options *GetOptions() override {
+ // CommandObjectParsed requires that a command with no options return
+ // nullptr.
+ if (m_options.GetNumOptions() == 0)
+ return nullptr;
+ return &m_options;
+ }
+
+protected:
+ void DoExecute(Args &args,
+ CommandReturnObject &result) override {
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+
+ Status error;
+
+ result.SetStatus(eReturnStatusInvalid);
+
+ if (!scripter ||
+ !scripter->RunScriptBasedParsedCommand(m_cmd_obj_sp, args,
+ 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);
+ }
+ }
+ }
+
+private:
+ StructuredData::GenericSP m_cmd_obj_sp;
+ ScriptedCommandSynchronicity m_synchro;
+ CommandOptions m_options;
+ Status m_options_error;
+ Status m_args_error;
+ bool m_fetched_help_short : 1;
+ bool m_fetched_help_long : 1;
+};
+
+std::unordered_set<std::string>
+ CommandObjectScriptingObjectParsed::CommandOptions::g_string_storer;
+
+// 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) {
+ AddSimpleArgumentList(eArgTypeFilename, eArgRepeatPlus);
+ }
+
+ ~CommandObjectCommandsScriptImport() override = default;
+
+ 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::ArrayRef(g_script_import_options);
+ }
+ bool relative_to_command_file = false;
+ bool silent = false;
+ };
+
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ if (command.empty()) {
+ result.AppendError("command script import needs one or more arguments");
+ return;
+ }
+
+ 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;
+ }
+ }
+
+ 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());
+ }
+ }
+ }
+
+ CommandOptions m_options;
+};
+
+#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") {
+ AddSimpleArgumentList(eArgTypeCommand, eArgRepeatPlus);
+ }
+
+ ~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 'p':
+ m_parsed_command = true;
+ 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;
+ case 'C': {
+ Status error;
+ OptionDefinition definition = GetDefinitions()[option_idx];
+ lldb::CompletionType completion_type =
+ static_cast<lldb::CompletionType>(OptionArgParser::ToOptionEnum(
+ option_arg, definition.enum_values, eNoCompletion, error));
+ if (!error.Success())
+ error.SetErrorStringWithFormat(
+ "unrecognized value for command completion type '%s'",
+ option_arg.str().c_str());
+ m_completion_type = completion_type;
+ } 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_completion_type = eNoCompletion;
+ m_overwrite_lazy = eLazyBoolCalculate;
+ m_synchronicity = eScriptedCommandSynchronicitySynchronous;
+ m_parsed_command = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::ArrayRef(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;
+ CompletionType m_completion_type = eNoCompletion;
+ bool m_parsed_command = false;
+ };
+
+ 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, m_completion_type));
+ 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);
+ }
+
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ if (GetDebugger().GetScriptLanguage() != lldb::eScriptLanguagePython) {
+ result.AppendError("only scripting language supported for scripted "
+ "commands is currently Python");
+ return;
+ }
+
+ if (command.GetArgumentCount() == 0) {
+ result.AppendError("'command script add' requires at least one argument");
+ return;
+ }
+ // 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;
+ }
+
+ 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;
+ m_completion_type = m_options.m_completion_type;
+
+ // 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;
+ }
+
+ 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, m_completion_type));
+ } else {
+ ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
+ if (!interpreter) {
+ result.AppendError("cannot find ScriptInterpreter");
+ return;
+ }
+
+ auto cmd_obj_sp = interpreter->CreateScriptCommandObject(
+ m_options.m_class_name.c_str());
+ if (!cmd_obj_sp) {
+ result.AppendErrorWithFormatv("cannot create helper object for: "
+ "'{0}'", m_options.m_class_name);
+ return;
+ }
+
+ if (m_options.m_parsed_command) {
+ new_cmd_sp = CommandObjectScriptingObjectParsed::Create(m_interpreter,
+ m_cmd_name, cmd_obj_sp, m_synchronicity, result);
+ if (!result.Succeeded())
+ return;
+ } else
+ new_cmd_sp.reset(new CommandObjectScriptingObjectRaw(
+ m_interpreter, m_cmd_name, cmd_obj_sp, m_synchronicity,
+ m_completion_type));
+ }
+
+ // 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());
+ }
+ }
+
+ 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;
+ CompletionType m_completion_type = eNoCompletion;
+};
+
+// CommandObjectCommandsScriptList
+
+class CommandObjectCommandsScriptList : public CommandObjectParsed {
+public:
+ CommandObjectCommandsScriptList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "command script list",
+ "List defined top-level scripted commands.",
+ nullptr) {}
+
+ ~CommandObjectCommandsScriptList() override = default;
+
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ m_interpreter.GetHelp(result, CommandInterpreter::eCommandTypesUserDef);
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+};
+
+// CommandObjectCommandsScriptClear
+
+class CommandObjectCommandsScriptClear : public CommandObjectParsed {
+public:
+ CommandObjectCommandsScriptClear(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "command script clear",
+ "Delete all scripted commands.", nullptr) {}
+
+ ~CommandObjectCommandsScriptClear() override = default;
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ m_interpreter.RemoveAllUser();
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+};
+
+// 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) {
+ AddSimpleArgumentList(eArgTypeCommand, eArgRepeatPlus);
+ }
+
+ ~CommandObjectCommandsScriptDelete() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ lldb_private::CommandCompletions::CompleteModifiableCmdPathArgs(
+ m_interpreter, request, opt_element_vector);
+ }
+
+protected:
+ void 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;
+ }
+ if (!m_interpreter.HasUserCommands() &&
+ !m_interpreter.HasUserMultiwordCommands()) {
+ result.AppendErrorWithFormat("can only delete user defined commands, "
+ "but no user defined commands found");
+ return;
+ }
+
+ CommandObjectSP cmd_sp = m_interpreter.GetCommandSPExact(root_cmd);
+ if (!cmd_sp) {
+ result.AppendErrorWithFormat("command '%s' not found.",
+ command[0].c_str());
+ return;
+ }
+ if (!cmd_sp->IsUserCommand()) {
+ result.AppendErrorWithFormat("command '%s' is not a user command.",
+ command[0].c_str());
+ return;
+ }
+ 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;
+ }
+
+ if (command.GetArgumentCount() == 1) {
+ m_interpreter.RemoveUser(root_cmd);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return;
+ }
+ // 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;
+ }
+ 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;
+ }
+ 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;
+ }
+
+ 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);
+ }
+};
+
+#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") {
+ AddSimpleArgumentList(eArgTypeCommand, eArgRepeatPlus);
+ }
+
+ ~CommandObjectCommandsContainerAdd() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ lldb_private::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::ArrayRef(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;
+ };
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ size_t num_args = command.GetArgumentCount();
+
+ if (num_args == 0) {
+ result.AppendError("no command was specified");
+ return;
+ }
+
+ 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;
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return;
+ }
+
+ // 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;
+ }
+
+ 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;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+
+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") {
+ AddSimpleArgumentList(eArgTypeCommand, eArgRepeatPlus);
+ }
+
+ ~CommandObjectCommandsContainerDelete() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ lldb_private::CommandCompletions::CompleteModifiableCmdPathArgs(
+ m_interpreter, request, opt_element_vector);
+ }
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ size_t num_args = command.GetArgumentCount();
+
+ if (num_args == 0) {
+ result.AppendError("No command was specified.");
+ return;
+ }
+
+ 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;
+ }
+ if (!cmd_sp->IsUserCommand()) {
+ result.AppendErrorWithFormat(
+ "container command %s is not a user command", cmd_name);
+ return;
+ }
+ if (!cmd_sp->GetAsMultiwordCommand()) {
+ result.AppendErrorWithFormat("command %s is not a container command",
+ cmd_name);
+ return;
+ }
+
+ bool did_remove = GetCommandInterpreter().RemoveUserMultiword(cmd_name);
+ if (!did_remove) {
+ result.AppendErrorWithFormat("error removing command %s.", cmd_name);
+ return;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return;
+ }
+
+ // 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;
+ }
+ 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;
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+};
+
+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/CommandObjectDWIMPrint.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectDWIMPrint.cpp
new file mode 100644
index 000000000000..b7cd955e0020
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectDWIMPrint.cpp
@@ -0,0 +1,234 @@
+//===-- CommandObjectDWIMPrint.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 "CommandObjectDWIMPrint.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/DumpValueObjectOptions.h"
+#include "lldb/Expression/ExpressionVariable.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionGroupFormat.h"
+#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
+#include "llvm/ADT/StringRef.h"
+
+#include <regex>
+
+using namespace llvm;
+using namespace lldb;
+using namespace lldb_private;
+
+CommandObjectDWIMPrint::CommandObjectDWIMPrint(CommandInterpreter &interpreter)
+ : CommandObjectRaw(interpreter, "dwim-print",
+ "Print a variable or expression.",
+ "dwim-print [<variable-name> | <expression>]",
+ eCommandProcessMustBePaused | eCommandTryTargetAPILock) {
+
+ AddSimpleArgumentList(eArgTypeVarName);
+
+ m_option_group.Append(&m_format_options,
+ OptionGroupFormat::OPTION_GROUP_FORMAT |
+ OptionGroupFormat::OPTION_GROUP_GDB_FMT,
+ LLDB_OPT_SET_1);
+ StringRef exclude_expr_options[] = {"debug", "top-level"};
+ m_option_group.Append(&m_expr_options, exclude_expr_options);
+ m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Finalize();
+}
+
+Options *CommandObjectDWIMPrint::GetOptions() { return &m_option_group; }
+
+void CommandObjectDWIMPrint::DoExecute(StringRef command,
+ CommandReturnObject &result) {
+ m_option_group.NotifyOptionParsingStarting(&m_exe_ctx);
+ OptionsWithRaw args{command};
+ StringRef expr = args.GetRawPart();
+
+ if (expr.empty()) {
+ result.AppendErrorWithFormatv("'{0}' takes a variable or expression",
+ m_cmd_name);
+ return;
+ }
+
+ if (args.HasArgs()) {
+ if (!ParseOptionsAndNotify(args.GetArgs(), result, m_option_group,
+ m_exe_ctx))
+ return;
+ }
+
+ // If the user has not specified, default to disabling persistent results.
+ if (m_expr_options.suppress_persistent_result == eLazyBoolCalculate)
+ m_expr_options.suppress_persistent_result = eLazyBoolYes;
+ bool suppress_result = m_expr_options.ShouldSuppressResult(m_varobj_options);
+
+ auto verbosity = GetDebugger().GetDWIMPrintVerbosity();
+
+ Target *target_ptr = m_exe_ctx.GetTargetPtr();
+ // Fallback to the dummy target, which can allow for expression evaluation.
+ Target &target = target_ptr ? *target_ptr : GetDummyTarget();
+
+ EvaluateExpressionOptions eval_options =
+ m_expr_options.GetEvaluateExpressionOptions(target, m_varobj_options);
+ // This command manually removes the result variable, make sure expression
+ // evaluation doesn't do it first.
+ eval_options.SetSuppressPersistentResult(false);
+
+ DumpValueObjectOptions dump_options = m_varobj_options.GetAsDumpOptions(
+ m_expr_options.m_verbosity, m_format_options.GetFormat());
+ dump_options.SetHideRootName(suppress_result);
+
+ bool is_po = m_varobj_options.use_objc;
+
+ StackFrame *frame = m_exe_ctx.GetFramePtr();
+
+ // Either the language was explicitly specified, or we check the frame.
+ lldb::LanguageType language = m_expr_options.language;
+ if (language == lldb::eLanguageTypeUnknown && frame)
+ language = frame->GuessLanguage().AsLanguageType();
+
+ // Add a hint if object description was requested, but no description
+ // function was implemented.
+ auto maybe_add_hint = [&](llvm::StringRef output) {
+ // Identify the default output of object description for Swift and
+ // Objective-C
+ // "<Name: 0x...>. The regex is:
+ // - Start with "<".
+ // - Followed by 1 or more non-whitespace characters.
+ // - Followed by ": 0x".
+ // - Followed by 5 or more hex digits.
+ // - Followed by ">".
+ // - End with zero or more whitespace characters.
+ const std::regex swift_class_regex("^<\\S+: 0x[[:xdigit:]]{5,}>\\s*$");
+
+ if (GetDebugger().GetShowDontUsePoHint() && target_ptr &&
+ (language == lldb::eLanguageTypeSwift ||
+ language == lldb::eLanguageTypeObjC) &&
+ std::regex_match(output.data(), swift_class_regex)) {
+
+ static bool note_shown = false;
+ if (note_shown)
+ return;
+
+ result.GetOutputStream()
+ << "note: object description requested, but type doesn't implement "
+ "a custom object description. Consider using \"p\" instead of "
+ "\"po\" (this note will only be shown once per debug session).\n";
+ note_shown = true;
+ }
+ };
+
+ // Dump `valobj` according to whether `po` was requested or not.
+ auto dump_val_object = [&](ValueObject &valobj) {
+ if (is_po) {
+ StreamString temp_result_stream;
+ if (llvm::Error error = valobj.Dump(temp_result_stream, dump_options)) {
+ result.AppendError(toString(std::move(error)));
+ return;
+ }
+ llvm::StringRef output = temp_result_stream.GetString();
+ maybe_add_hint(output);
+ result.GetOutputStream() << output;
+ } else {
+ llvm::Error error =
+ valobj.Dump(result.GetOutputStream(), dump_options);
+ if (error) {
+ result.AppendError(toString(std::move(error)));
+ return;
+ }
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ };
+
+ // First, try `expr` as the name of a frame variable.
+ if (frame) {
+ auto valobj_sp = frame->FindVariable(ConstString(expr));
+ if (valobj_sp && valobj_sp->GetError().Success()) {
+ if (!suppress_result) {
+ if (auto persisted_valobj = valobj_sp->Persist())
+ valobj_sp = persisted_valobj;
+ }
+
+ if (verbosity == eDWIMPrintVerbosityFull) {
+ StringRef flags;
+ if (args.HasArgs())
+ flags = args.GetArgString();
+ result.AppendMessageWithFormatv("note: ran `frame variable {0}{1}`",
+ flags, expr);
+ }
+
+ dump_val_object(*valobj_sp);
+ return;
+ }
+ }
+
+ // Second, try `expr` as a persistent variable.
+ if (expr.starts_with("$"))
+ if (auto *state = target.GetPersistentExpressionStateForLanguage(language))
+ if (auto var_sp = state->GetVariable(expr))
+ if (auto valobj_sp = var_sp->GetValueObject()) {
+ dump_val_object(*valobj_sp);
+ return;
+ }
+
+ // Third, and lastly, try `expr` as a source expression to evaluate.
+ {
+ auto *exe_scope = m_exe_ctx.GetBestExecutionContextScope();
+ ValueObjectSP valobj_sp;
+ std::string fixed_expression;
+
+ ExpressionResults expr_result = target.EvaluateExpression(
+ expr, exe_scope, valobj_sp, eval_options, &fixed_expression);
+
+ // Only mention Fix-Its if the expression evaluator applied them.
+ // Compiler errors refer to the final expression after applying Fix-It(s).
+ if (!fixed_expression.empty() && target.GetEnableNotifyAboutFixIts()) {
+ Stream &error_stream = result.GetErrorStream();
+ error_stream << " Evaluated this expression after applying Fix-It(s):\n";
+ error_stream << " " << fixed_expression << "\n";
+ }
+
+ // If the expression failed, return an error.
+ if (expr_result != eExpressionCompleted) {
+ if (valobj_sp)
+ result.SetError(valobj_sp->GetError());
+ else
+ result.AppendErrorWithFormatv(
+ "unknown error evaluating expression `{0}`", expr);
+ return;
+ }
+
+ if (verbosity != eDWIMPrintVerbosityNone) {
+ StringRef flags;
+ if (args.HasArgs())
+ flags = args.GetArgStringWithDelimiter();
+ result.AppendMessageWithFormatv("note: ran `expression {0}{1}`", flags,
+ expr);
+ }
+
+ if (valobj_sp->GetError().GetError() != UserExpression::kNoResult)
+ dump_val_object(*valobj_sp);
+ else
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ if (suppress_result)
+ if (auto result_var_sp =
+ target.GetPersistentVariable(valobj_sp->GetName())) {
+ auto language = valobj_sp->GetPreferredDisplayLanguage();
+ if (auto *persistent_state =
+ target.GetPersistentExpressionStateForLanguage(language))
+ persistent_state->RemovePersistentVariable(result_var_sp);
+ }
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectDWIMPrint.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectDWIMPrint.h
new file mode 100644
index 000000000000..01ba9c225e33
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectDWIMPrint.h
@@ -0,0 +1,53 @@
+//===-- CommandObjectDWIMPrint.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_COMMANDOBJECTDWIMPRINT_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTDWIMPRINT_H
+
+#include "CommandObjectExpression.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/OptionGroupFormat.h"
+#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
+#include "lldb/Interpreter/OptionValueFormat.h"
+
+namespace lldb_private {
+
+/// Implements `dwim-print`, a printing command that chooses the most direct,
+/// efficient, and resilient means of printing a given expression.
+///
+/// DWIM is an acronym for Do What I Mean. From Wikipedia, DWIM is described as:
+///
+/// > attempt to anticipate what users intend to do, correcting trivial errors
+/// > automatically rather than blindly executing users' explicit but
+/// > potentially incorrect input
+///
+/// The `dwim-print` command serves as a single print command for users who
+/// don't yet know, or perfer not to know, the various lldb commands that can be
+/// used to print, and when to use them.
+class CommandObjectDWIMPrint : public CommandObjectRaw {
+public:
+ CommandObjectDWIMPrint(CommandInterpreter &interpreter);
+
+ ~CommandObjectDWIMPrint() override = default;
+
+ Options *GetOptions() override;
+
+ bool WantsCompletion() override { return true; }
+
+private:
+ void DoExecute(llvm::StringRef command, CommandReturnObject &result) override;
+
+ OptionGroupOptions m_option_group;
+ OptionGroupFormat m_format_options = lldb::eFormatDefault;
+ OptionGroupValueObjectDisplay m_varobj_options;
+ CommandObjectExpression::CommandOptions m_expr_options;
+};
+
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectDiagnostics.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectDiagnostics.cpp
new file mode 100644
index 000000000000..ac87f869f012
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectDiagnostics.cpp
@@ -0,0 +1,114 @@
+//===-- CommandObjectDiagnostics.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 "CommandObjectDiagnostics.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandOptionArgumentTable.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/Diagnostics.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#define LLDB_OPTIONS_diagnostics_dump
+#include "CommandOptions.inc"
+
+class CommandObjectDiagnosticsDump : public CommandObjectParsed {
+public:
+ // Constructors and Destructors
+ CommandObjectDiagnosticsDump(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "diagnostics dump",
+ "Dump diagnostics to disk", nullptr) {}
+
+ ~CommandObjectDiagnosticsDump() override = default;
+
+ 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':
+ directory.SetDirectory(option_arg);
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ directory.Clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::ArrayRef(g_diagnostics_dump_options);
+ }
+
+ FileSpec directory;
+ };
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ llvm::Expected<FileSpec> GetDirectory() {
+ if (m_options.directory) {
+ auto ec =
+ llvm::sys::fs::create_directories(m_options.directory.GetPath());
+ if (ec)
+ return llvm::errorCodeToError(ec);
+ return m_options.directory;
+ }
+ return Diagnostics::CreateUniqueDirectory();
+ }
+
+ void DoExecute(Args &args, CommandReturnObject &result) override {
+ llvm::Expected<FileSpec> directory = GetDirectory();
+
+ if (!directory) {
+ result.AppendError(llvm::toString(directory.takeError()));
+ return;
+ }
+
+ llvm::Error error = Diagnostics::Instance().Create(*directory);
+ if (error) {
+ result.AppendErrorWithFormat("failed to write diagnostics to %s",
+ directory->GetPath().c_str());
+ result.AppendError(llvm::toString(std::move(error)));
+ return;
+ }
+
+ result.GetOutputStream() << "diagnostics written to " << *directory << '\n';
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return;
+ }
+
+ CommandOptions m_options;
+};
+
+CommandObjectDiagnostics::CommandObjectDiagnostics(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "diagnostics",
+ "Commands controlling LLDB diagnostics.",
+ "diagnostics <subcommand> [<command-options>]") {
+ LoadSubCommand(
+ "dump", CommandObjectSP(new CommandObjectDiagnosticsDump(interpreter)));
+}
+
+CommandObjectDiagnostics::~CommandObjectDiagnostics() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectDiagnostics.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectDiagnostics.h
new file mode 100644
index 000000000000..d28282b85023
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectDiagnostics.h
@@ -0,0 +1,29 @@
+//===-- CommandObjectDiagnostics.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_COMMANDOBJECTDIAGNOSTICS_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTDIAGNOSTICS_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+class CommandObjectDiagnostics : public CommandObjectMultiword {
+public:
+ CommandObjectDiagnostics(CommandInterpreter &interpreter);
+ ~CommandObjectDiagnostics() override;
+
+private:
+ CommandObjectDiagnostics(const CommandObjectDiagnostics &) = delete;
+ const CommandObjectDiagnostics &
+ operator=(const CommandObjectDiagnostics &) = delete;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTDIAGNOSTICS_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..d975e3980131
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectDisassemble.cpp
@@ -0,0 +1,544 @@
+//===-- 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/CommandOptionArgumentTable.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::ArrayRef(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) {}
+
+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();
+}
+
+void 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;
+ }
+
+ 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;
+ } 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;
+ }
+
+ 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;
+ }
+
+ 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";
+ }
+}
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..2e4d46dd0ec5
--- /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:
+ void 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..eb76753d98ef
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectExpression.cpp
@@ -0,0 +1,689 @@
+//===-- 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/ExpressionVariable.h"
+#include "lldb/Expression/REPL.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandOptionArgumentTable.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"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-private-enumerations.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+CommandObjectExpression::CommandOptions::CommandOptions() = default;
+
+CommandObjectExpression::CommandOptions::~CommandOptions() = default;
+
+#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) {
+ StreamString sstr;
+ sstr.Printf("unknown language type: '%s' for expression. "
+ "List of supported languages:\n",
+ option_arg.str().c_str());
+
+ Language::PrintSupportedLanguagesForExpressions(sstr, " ", "\n");
+ error.SetErrorString(sstr.GetString());
+ }
+ 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;
+ }
+
+ case '\x01': {
+ bool success;
+ bool persist_result =
+ OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (success)
+ suppress_persistent_result = !persist_result ? 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;
+ suppress_persistent_result = eLazyBoolCalculate;
+}
+
+llvm::ArrayRef<OptionDefinition>
+CommandObjectExpression::CommandOptions::GetDefinitions() {
+ return llvm::ArrayRef(g_expression_options);
+}
+
+EvaluateExpressionOptions
+CommandObjectExpression::CommandOptions::GetEvaluateExpressionOptions(
+ const Target &target, const OptionGroupValueObjectDisplay &display_opts) {
+ EvaluateExpressionOptions options;
+ options.SetCoerceToId(display_opts.use_objc);
+ options.SetUnwindOnError(unwind_on_error);
+ options.SetIgnoreBreakpoints(ignore_breakpoints);
+ options.SetKeepInMemory(true);
+ options.SetUseDynamic(display_opts.use_dynamic);
+ options.SetTryAllThreads(try_all_threads);
+ options.SetDebug(debug);
+ options.SetLanguage(language);
+ options.SetExecutionPolicy(
+ allow_jit ? EvaluateExpressionOptions::default_execution_policy
+ : lldb_private::eExecutionPolicyNever);
+
+ bool auto_apply_fixits;
+ if (this->auto_apply_fixits == eLazyBoolCalculate)
+ auto_apply_fixits = target.GetEnableAutoApplyFixIts();
+ else
+ auto_apply_fixits = this->auto_apply_fixits == eLazyBoolYes;
+
+ options.SetAutoApplyFixIts(auto_apply_fixits);
+ options.SetRetriesWithFixIts(target.GetNumberOfRetriesWithFixits());
+
+ if (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 (!ignore_breakpoints || !unwind_on_error)
+ options.SetGenerateDebugInfo(true);
+
+ if (timeout > 0)
+ options.SetTimeout(std::chrono::microseconds(timeout));
+ else
+ options.SetTimeout(std::nullopt);
+ return options;
+}
+
+bool CommandObjectExpression::CommandOptions::ShouldSuppressResult(
+ const OptionGroupValueObjectDisplay &display_opts) const {
+ // Explicitly disabling persistent results takes precedence over the
+ // m_verbosity/use_objc logic.
+ if (suppress_persistent_result != eLazyBoolCalculate)
+ return suppress_persistent_result == eLazyBoolYes;
+
+ return display_opts.use_objc &&
+ m_verbosity == eLanguageRuntimeDescriptionDisplayVerbosityCompact;
+}
+
+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_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])");
+
+ AddSimpleArgumentList(eArgTypeExpression);
+
+ // 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();
+}
+
+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;
+ }
+
+ EvaluateExpressionOptions eval_options =
+ m_command_options.GetEvaluateExpressionOptions(target, m_varobj_options);
+ // This command manually removes the result variable, make sure expression
+ // evaluation doesn't do it first.
+ eval_options.SetSuppressPersistentResult(false);
+
+ ExpressionResults success = target.EvaluateExpression(
+ expr, frame, result_valobj_sp, eval_options, &m_fixed_expression);
+
+ // Only mention Fix-Its if the expression evaluator applied them.
+ // Compiler errors refer to the final expression after applying Fix-It(s).
+ if (!m_fixed_expression.empty() && target.GetEnableNotifyAboutFixIts()) {
+ error_stream << " Evaluated this expression after applying Fix-It(s):\n";
+ error_stream << " " << m_fixed_expression << "\n";
+ }
+
+ 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;
+ }
+ }
+
+ bool suppress_result =
+ m_command_options.ShouldSuppressResult(m_varobj_options);
+
+ DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(
+ m_command_options.m_verbosity, format));
+ options.SetHideRootName(suppress_result);
+ options.SetVariableFormatDisplayLanguage(
+ result_valobj_sp->GetPreferredDisplayLanguage());
+
+ if (llvm::Error error =
+ result_valobj_sp->Dump(output_stream, options)) {
+ result.AppendError(toString(std::move(error)));
+ return false;
+ }
+
+ if (suppress_result)
+ if (auto result_var_sp =
+ target.GetPersistentVariable(result_valobj_sp->GetName())) {
+ auto language = result_valobj_sp->GetPreferredDisplayLanguage();
+ if (auto *persistent_state =
+ target.GetPersistentExpressionStateForLanguage(language))
+ persistent_state->RemovePersistentVariable(result_var_sp);
+ }
+ 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);
+ }
+ }
+ } else {
+ error_stream.Printf("error: unknown error\n");
+ }
+
+ 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));
+
+ 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(std::nullopt);
+
+ return expr_options;
+}
+
+void 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;
+ }
+
+ OptionsWithRaw args(command);
+ llvm::StringRef expr = args.GetRawPart();
+
+ if (args.HasArgs()) {
+ if (!ParseOptionsAndNotify(args.GetArgs(), result, m_option_group, exe_ctx))
+ return;
+
+ 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;
+ }
+ }
+
+ 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;
+ }
+ }
+ }
+ // No expression following options
+ else if (expr.empty()) {
+ GetMultilineExpression();
+ return;
+ }
+ }
+
+ 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;
+ }
+ result.SetStatus(eReturnStatusFailed);
+}
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..6fccf10e5dbc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectExpression.h
@@ -0,0 +1,107 @@
+//===-- 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;
+
+ /// Return the appropriate expression options used for evaluating the
+ /// expression in the given target.
+ EvaluateExpressionOptions GetEvaluateExpressionOptions(
+ const Target &target,
+ const OptionGroupValueObjectDisplay &display_opts);
+
+ bool ShouldSuppressResult(
+ const OptionGroupValueObjectDisplay &display_opts) const;
+
+ 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;
+ LazyBool suppress_persistent_result;
+ };
+
+ 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;
+
+ void DoExecute(llvm::StringRef command, CommandReturnObject &result) override;
+
+ /// 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..3f4178c1a959
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectFrame.cpp
@@ -0,0 +1,1142 @@
+//===-- 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/CommandOptionArgumentTable.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 <optional>
+#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::ArrayRef(g_frame_diag_options);
+ }
+
+ // Options.
+ std::optional<lldb::addr_t> address;
+ std::optional<ConstString> reg;
+ std::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) {
+ AddSimpleArgumentList(eArgTypeFrameIndex, eArgRepeatOptional);
+ }
+
+ ~CommandObjectFrameDiagnose() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ Thread *thread = m_exe_ctx.GetThreadPtr();
+ StackFrameSP frame_sp = thread->GetSelectedFrame(SelectMostRelevantFrame);
+
+ 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;
+ }
+ valobj_sp = frame_sp->GuessValueForAddress(*m_options.address);
+ } else if (m_options.reg) {
+ valobj_sp = frame_sp->GuessValueForRegisterAndOffset(
+ *m_options.reg, 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;
+ }
+
+ valobj_sp = StopInfo::GetCrashingDereference(stop_info_sp);
+ }
+
+ if (!valobj_sp) {
+ result.AppendError("No diagnosis available.");
+ return;
+ }
+
+ 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);
+ // We've already handled the case where the value object sp is null, so
+ // this is just to make sure future changes don't skip that:
+ assert(valobj_sp.get() && "Must have a valid ValueObject to print");
+ ValueObjectPrinter printer(*valobj_sp, &result.GetOutputStream(),
+ options);
+ if (llvm::Error error = printer.PrintValueObject())
+ result.AppendError(toString(std::move(error)));
+ }
+
+ 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:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ m_exe_ctx.GetFrameRef().DumpUsingSettingsFormat(&result.GetOutputStream());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+};
+
+#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::ArrayRef(g_frame_select_options);
+ }
+
+ std::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) {
+ AddSimpleArgumentList(eArgTypeFrameIndex, eArgRepeatOptional);
+ }
+
+ ~CommandObjectFrameSelect() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ void 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(SelectMostRelevantFrame);
+ 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;
+ } 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. OTOH, start
+ // by seeing if the requested frame exists, in which case we can avoid
+ // counting the stack here...
+ const uint32_t frame_requested = frame_idx
+ + *m_options.relative_frame_offset;
+ StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_requested);
+ if (frame_sp)
+ frame_idx = frame_requested;
+ else {
+ // The request went past the stack, so handle that case:
+ 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;
+ } 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;
+ }
+
+ if (command.GetArgumentCount() == 1) {
+ if (command[0].ref().getAsInteger(0, frame_idx)) {
+ result.AppendErrorWithFormat("invalid frame index argument '%s'.",
+ command[0].c_str());
+ return;
+ }
+ } else if (command.GetArgumentCount() == 0) {
+ frame_idx = thread->GetSelectedFrameIndex(SelectMostRelevantFrame);
+ if (frame_idx == UINT32_MAX) {
+ frame_idx = 0;
+ }
+ }
+ }
+
+ bool success = thread->SetSelectedFrameByIndexNoisily(
+ frame_idx, result.GetOutputStream());
+ if (success) {
+ m_exe_ctx.SetFrameSP(thread->GetSelectedFrame(SelectMostRelevantFrame));
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("Frame index (%u) out of range.\n",
+ frame_idx);
+ }
+ }
+
+ 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.)");
+
+ AddSimpleArgumentList(eArgTypeVarName, eArgRepeatStar);
+
+ 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; }
+
+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();
+ }
+
+ /// Returns true if `scope` matches any of the options in `m_option_variable`.
+ bool ScopeRequested(lldb::ValueType scope) {
+ switch (scope) {
+ case eValueTypeVariableGlobal:
+ case eValueTypeVariableStatic:
+ return m_option_variable.show_globals;
+ case eValueTypeVariableArgument:
+ return m_option_variable.show_args;
+ case eValueTypeVariableLocal:
+ return m_option_variable.show_locals;
+ case eValueTypeInvalid:
+ case eValueTypeRegister:
+ case eValueTypeRegisterSet:
+ case eValueTypeConstResult:
+ case eValueTypeVariableThreadLocal:
+ case eValueTypeVTable:
+ case eValueTypeVTableEntry:
+ return false;
+ }
+ llvm_unreachable("Unexpected scope value");
+ }
+
+ /// Finds all the variables in `all_variables` whose name matches `regex`,
+ /// inserting them into `matches`. Variables already contained in `matches`
+ /// are not inserted again.
+ /// Nullopt is returned in case of no matches.
+ /// A sub-range of `matches` with all newly inserted variables is returned.
+ /// This may be empty if all matches were already contained in `matches`.
+ std::optional<llvm::ArrayRef<VariableSP>>
+ findUniqueRegexMatches(RegularExpression &regex,
+ VariableList &matches,
+ const VariableList &all_variables) {
+ bool any_matches = false;
+ const size_t previous_num_vars = matches.GetSize();
+
+ for (const VariableSP &var : all_variables) {
+ if (!var->NameMatches(regex) || !ScopeRequested(var->GetScope()))
+ continue;
+ any_matches = true;
+ matches.AddVariableIfUnique(var);
+ }
+
+ if (any_matches)
+ return matches.toArrayRef().drop_front(previous_num_vars);
+ return std::nullopt;
+ }
+
+ void 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();
+
+ // Using a regex should behave like looking for an exact name match: it
+ // also finds globals.
+ m_option_variable.show_globals |= m_option_variable.use_regex;
+
+ // 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.
+
+ Status error;
+ VariableList *variable_list =
+ frame->GetVariableList(m_option_variable.show_globals, &error);
+
+ if (error.Fail() && (!variable_list || variable_list->GetSize() == 0)) {
+ result.AppendError(error.AsCString());
+
+ }
+ 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) {
+ llvm::StringRef name_str = entry.ref();
+ RegularExpression regex(name_str);
+ if (regex.IsValid()) {
+ std::optional<llvm::ArrayRef<VariableSP>> results =
+ findUniqueRegexMatches(regex, regex_var_list, *variable_list);
+ if (!results) {
+ result.AppendErrorWithFormat(
+ "no variables matched the regular expression '%s'.",
+ entry.c_str());
+ continue;
+ }
+ for (const VariableSP &var_sp : *results) {
+ 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(": ");
+ }
+ auto &strm = result.GetOutputStream();
+ if (llvm::Error error = valobj_sp->Dump(strm, options))
+ result.AppendError(toString(std::move(error)));
+ }
+ }
+ } 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);
+ if (llvm::Error error = valobj_sp->Dump(output_stream, options))
+ result.AppendError(toString(std::move(error)));
+ } 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++) {
+ VariableSP var_sp = variable_list->GetVariableAtIndex(i);
+ if (!ScopeRequested(var_sp->GetScope()))
+ continue;
+ 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);
+ if (llvm::Error error =
+ valobj_sp->Dump(result.GetOutputStream(), options))
+ result.AppendError(toString(std::move(error)));
+ }
+ }
+ }
+ }
+ }
+ 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());
+ if (llvm::Error error =
+ rec_value_sp->Dump(result.GetOutputStream(), options))
+ result.AppendError(toString(std::move(error)));
+ }
+ }
+ }
+ }
+
+ m_interpreter.PrintWarningsIfNecessary(result.GetOutputStream(),
+ m_cmd_name);
+
+ // Increment statistics.
+ TargetStats &target_stats = GetSelectedOrDummyTarget().GetStatistics();
+ if (result.Succeeded())
+ target_stats.GetFrameVariableStats().NotifySuccess();
+ else
+ target_stats.GetFrameVariableStats().NotifyFailure();
+ }
+
+ 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::ArrayRef(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:
+ void 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
+ target = frame.thread.process.target
+ value = 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;
+};
+
+void 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;
+ }
+
+ if (m_options.m_module.empty()) {
+ result.AppendErrorWithFormat("%s needs a module name (-s argument).\n",
+ m_cmd_name.c_str());
+ return;
+ }
+
+ if (m_options.m_symbols.empty()) {
+ result.AppendErrorWithFormat(
+ "%s needs at least one symbol name (-n argument).\n",
+ m_cmd_name.c_str());
+ return;
+ }
+
+ 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;
+ }
+
+ 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);
+}
+
+class CommandObjectFrameRecognizerClear : public CommandObjectParsed {
+public:
+ CommandObjectFrameRecognizerClear(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "frame recognizer clear",
+ "Delete all frame recognizers.", nullptr) {}
+
+ ~CommandObjectFrameRecognizerClear() override = default;
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ GetSelectedOrDummyTarget()
+ .GetFrameRecognizerManager()
+ .RemoveAllRecognizers();
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+};
+
+class CommandObjectFrameRecognizerDelete : public CommandObjectParsed {
+public:
+ CommandObjectFrameRecognizerDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "frame recognizer delete",
+ "Delete an existing frame recognizer by id.",
+ nullptr) {
+ AddSimpleArgumentList(eArgTypeRecognizerID);
+ }
+
+ ~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:
+ void 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;
+ }
+
+ GetSelectedOrDummyTarget()
+ .GetFrameRecognizerManager()
+ .RemoveAllRecognizers();
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return;
+ }
+
+ if (command.GetArgumentCount() != 1) {
+ result.AppendErrorWithFormat("'%s' takes zero or one arguments.\n",
+ m_cmd_name.c_str());
+ return;
+ }
+
+ 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;
+ }
+
+ if (!GetSelectedOrDummyTarget()
+ .GetFrameRecognizerManager()
+ .RemoveRecognizerWithID(recognizer_id)) {
+ result.AppendErrorWithFormat("'%s' is not a valid recognizer id.\n",
+ command.GetArgumentAtIndex(0));
+ return;
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+};
+
+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:
+ void 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);
+ }
+ }
+};
+
+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) {
+ AddSimpleArgumentList(eArgTypeFrameIndex);
+ }
+
+ ~CommandObjectFrameRecognizerInfo() override = default;
+
+protected:
+ void 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;
+ }
+
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (process == nullptr) {
+ result.AppendError("no process");
+ return;
+ }
+ Thread *thread = m_exe_ctx.GetThreadPtr();
+ if (thread == nullptr) {
+ result.AppendError("no thread");
+ return;
+ }
+ if (command.GetArgumentCount() != 1) {
+ result.AppendErrorWithFormat(
+ "'%s' takes exactly one frame index argument.\n", m_cmd_name.c_str());
+ return;
+ }
+
+ StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_index);
+ if (!frame_sp) {
+ result.AppendErrorWithFormat("no frame with index %u", frame_index);
+ return;
+ }
+
+ 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);
+ }
+};
+
+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..b56e49b073b0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectGUI.cpp
@@ -0,0 +1,45 @@
+//===-- 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;
+
+void 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.");
+ }
+#else
+ result.AppendError("lldb was not built with gui support");
+#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..fde4342724c9
--- /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:
+ void 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..f1dbd03fe97c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectHelp.cpp
@@ -0,0 +1,210 @@
+//===-- 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/CommandOptionArgumentTable.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>]") {
+ // 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.
+ AddSimpleArgumentList(eArgTypeCommand, eArgRepeatStar);
+}
+
+CommandObjectHelp::~CommandObjectHelp() = default;
+
+#define LLDB_OPTIONS_help
+#include "CommandOptions.inc"
+
+llvm::ArrayRef<OptionDefinition>
+CommandObjectHelp::CommandOptions::GetDefinitions() {
+ return llvm::ArrayRef(g_help_options);
+}
+
+void 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;
+ } 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;
+ } 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());
+ }
+ }
+ }
+}
+
+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..9b2c89e6654f
--- /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:
+ void 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..2f9f8fecc80d
--- /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:
+ void 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..48dfd9456a66
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectLog.cpp
@@ -0,0 +1,601 @@
+//===-- 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/CommandOptionArgumentTable.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;
+
+#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::ArrayRef(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:
+ void 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;
+ }
+
+ 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;
+ }
+
+ if ((m_options.handler != eLogHandlerCircular &&
+ m_options.handler != eLogHandlerStream) &&
+ m_options.buffer_size.GetCurrentValue() != 0) {
+ result.AppendError("a buffer size can only be specified for the circular "
+ "and stream buffer handler.\n");
+ return;
+ }
+
+ if (m_options.handler != eLogHandlerStream && m_options.log_file) {
+ result.AppendError(
+ "a file name can only be specified for the stream handler.\n");
+ return;
+ }
+
+ // 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);
+ }
+
+ 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:
+ void 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;
+ }
+
+ 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();
+ }
+ }
+};
+
+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) {
+ AddSimpleArgumentList(eArgTypeLogChannel, eArgRepeatStar);
+ }
+
+ ~CommandObjectLogList() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ for (llvm::StringRef channel : Log::ListChannels())
+ request.TryCompleteCurrentArg(channel);
+ }
+
+protected:
+ void 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();
+ }
+};
+class CommandObjectLogDump : public CommandObjectParsed {
+public:
+ CommandObjectLogDump(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "log dump",
+ "dump circular buffer logs", nullptr) {
+ AddSimpleArgumentList(eArgTypeLogChannel);
+ }
+
+ ~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::ArrayRef(g_log_dump_options);
+ }
+
+ FileSpec log_file;
+ };
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CompleteEnableDisable(request);
+ }
+
+protected:
+ void 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;
+ }
+
+ 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.GetPath().c_str(),
+ llvm::toString(file.takeError()).c_str());
+ return;
+ }
+ 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();
+ }
+ }
+
+ 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>") {
+ AddSimpleArgumentList(eArgTypeCount, eArgRepeatOptional);
+ }
+
+ ~CommandObjectLogTimerEnable() override = default;
+
+protected:
+ void 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());
+ }
+ }
+};
+
+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:
+ void 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());
+ }
+ }
+};
+
+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:
+ void 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());
+ }
+ }
+};
+
+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:
+ void 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());
+ }
+ }
+};
+
+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>") {
+ AddSimpleArgumentList(eArgTypeBoolean);
+ }
+
+ ~CommandObjectLogTimerIncrement() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ request.TryCompleteCurrentArg("true");
+ request.TryCompleteCurrentArg("false");
+ }
+
+protected:
+ void 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());
+ }
+ }
+};
+
+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..137b1ad98107
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectMemory.cpp
@@ -0,0 +1,1814 @@
+//===-- 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/CommandOptionArgumentTable.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>
+#include <optional>
+
+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::ArrayRef(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 = false;
+ 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; }
+
+ std::optional<std::string> GetRepeatCommand(Args &current_command_args,
+ uint32_t index) override {
+ return m_cmd_name;
+ }
+
+protected:
+ void 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;
+ }
+
+ 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
+
+ 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;
+ [[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;
+ }
+ break;
+
+ default:
+ done = true;
+ break;
+ }
+ }
+ }
+
+ 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;
+ TypeQuery query(lookup_type_name.GetStringRef(),
+ TypeQueryOptions::e_find_one);
+ TypeResults results;
+ target->GetImages().FindTypes(search_first.get(), query, results);
+ TypeSP type_sp = results.GetFirstType();
+
+ if (!type_sp && 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 (std::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;
+ }
+
+ if (user_defined_types.size() == 1) {
+ compiler_type = *user_defined_types.begin();
+ }
+ }
+
+ if (!compiler_type.IsValid()) {
+ if (type_sp) {
+ compiler_type = type_sp->GetFullCompilerType();
+ } else {
+ 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;
+ }
+ }
+
+ 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;
+ }
+ --pointer_count;
+ }
+
+ std::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;
+ }
+ 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;
+ }
+
+ 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;
+ }
+
+ 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) {
+ result.AppendError("invalid end address expression.");
+ result.AppendError(error.AsCString());
+ return;
+ } 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;
+ } 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;
+ }
+
+ 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;
+ }
+
+ 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);
+
+ std::optional<uint64_t> size = compiler_type.GetByteSize(nullptr);
+ if (!size) {
+ result.AppendError("can't get size of type");
+ return;
+ }
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+ 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;
+ }
+
+ 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;
+ } else {
+ result.AppendErrorWithFormat("Failed to write %" PRIu64
+ " bytes to '%s'.\n",
+ (uint64_t)bytes_read, path.c_str());
+ return;
+ }
+ } 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;
+ }
+ } 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));
+
+ if (llvm::Error error = valobj_sp->Dump(*output_stream_p, options)) {
+ result.AppendError(toString(std::move(error)));
+ return;
+ }
+ } else {
+ result.AppendErrorWithFormat(
+ "failed to create a value object for: (%s) %s\n",
+ view_as_type_cstr, name_strm.GetData());
+ return;
+ }
+ }
+ return;
+ }
+
+ 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;
+ }
+ }
+
+ 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();
+ }
+
+ 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::ArrayRef(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:
+ void 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;
+ }
+
+ 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;
+ }
+ 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;
+ }
+
+ if (high_addr <= low_addr) {
+ result.AppendError(
+ "starting address must be smaller than ending address");
+ return;
+ }
+
+ 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.GetValueAs<llvm::StringRef>().value_or("");
+ if (str.empty()) {
+ result.AppendError("search string must have non-zero length.");
+ return;
+ }
+ 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.GetValueAs<llvm::StringRef>().value_or(
+ ""),
+ frame, result_sp)) &&
+ result_sp) {
+ uint64_t value = result_sp->GetValueAsUnsigned(0);
+ std::optional<uint64_t> size =
+ result_sp->GetCompilerType().GetByteSize(nullptr);
+ if (!size)
+ return;
+ 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;
+ default:
+ result.AppendError(
+ "result size larger than 8 bytes. pass a string instead");
+ return;
+ }
+ } else {
+ result.AppendError(
+ "expression evaluation failed. pass a string instead");
+ return;
+ }
+ } else {
+ result.AppendError(
+ "please pass either a block of text, or an expression to evaluate.");
+ return;
+ }
+
+ size_t count = m_memory_options.m_count.GetCurrentValue();
+ found_location = low_addr;
+ bool ever_found = false;
+ while (count) {
+ found_location = process->FindInMemory(
+ 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);
+ }
+
+ 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::ArrayRef(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:
+ void 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;
+ }
+ if (argc > 1) {
+ result.AppendErrorWithFormat(
+ "%s takes only a destination address when writing file contents.\n",
+ m_cmd_name.c_str());
+ return;
+ }
+ } else if (argc < 2) {
+ result.AppendErrorWithFormat(
+ "%s takes a destination address and at least one value.\n",
+ m_cmd_name.c_str());
+ return;
+ }
+
+ 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;
+ }
+
+ 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;
+ } 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;
+
+ 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().starts_with("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;
+ } 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;
+ }
+ 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;
+ }
+ 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;
+ } 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;
+ }
+ 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;
+ }
+ break;
+ }
+ case eFormatDecimal:
+ if (entry.ref().getAsInteger(0, sval64)) {
+ result.AppendErrorWithFormat(
+ "'%s' is not a valid signed decimal value.\n", entry.c_str());
+ return;
+ } 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;
+ }
+ 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;
+ } 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;
+ }
+ 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;
+ } 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;
+ }
+ buffer.PutMaxHex64(uval64, item_byte_size);
+ break;
+ }
+ }
+
+ if (!buffer.GetString().empty()) {
+ Status error;
+ const char *buffer_data = buffer.GetString().data();
+ const size_t buffer_size = buffer.GetString().size();
+ const size_t write_size =
+ process->WriteMemory(addr, buffer_data, buffer_size, error);
+
+ if (write_size != buffer_size) {
+ result.AppendErrorWithFormat("Memory write to 0x%" PRIx64
+ " failed: %s.\n",
+ addr, error.AsCString());
+ return;
+ }
+ }
+ }
+
+ 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;
+
+ std::optional<std::string> GetRepeatCommand(Args &current_command_args,
+ uint32_t index) override {
+ return m_cmd_name;
+ }
+
+protected:
+ void 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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);
+ }
+};
+
+// CommandObjectMemoryRegion
+#pragma mark CommandObjectMemoryRegion
+
+#define LLDB_OPTIONS_memory_region
+#include "CommandOptions.inc"
+
+class CommandObjectMemoryRegion : public CommandObjectParsed {
+public:
+ class OptionGroupMemoryRegion : public OptionGroup {
+ public:
+ OptionGroupMemoryRegion() : m_all(false, false) {}
+
+ ~OptionGroupMemoryRegion() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::ArrayRef(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 std::optional<std::vector<addr_t>> &dirty_page_list =
+ range_info.GetDirtyPageList();
+ if (dirty_page_list) {
+ const size_t page_count = dirty_page_list->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");
+ }
+ }
+ }
+
+ void 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;
+ }
+
+ 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;
+ }
+
+ auto load_addr_str = command[0].ref();
+ 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;
+ }
+ } 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;
+ }
+
+ // It 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();
+ }
+ }
+ } 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;
+ }
+
+ result.AppendErrorWithFormat("%s\n", error.AsCString());
+ }
+
+ std::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..f45d6eacab3d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectMemoryTag.cpp
@@ -0,0 +1,319 @@
+//===-- 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/CommandOptionArgumentTable.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:
+ void 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;
+ }
+
+ Status error;
+ addr_t start_addr = OptionArgParser::ToRawAddress(
+ &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;
+ }
+
+ // 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::ToRawAddress(&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;
+ }
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ llvm::Expected<std::vector<lldb::addr_t>> tags = process->ReadMemoryTags(
+ tagged_range->GetRangeBase(), tagged_range->GetByteSize());
+
+ if (!tags) {
+ result.SetError(Status(tags.takeError()));
+ return;
+ }
+
+ 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);
+ }
+};
+
+#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::ArrayRef(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::ToRawAddress(
+ 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:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ if (command.GetArgumentCount() < 2) {
+ result.AppendError("wrong number of arguments; expected "
+ "<address-expression> <tag> [<tag> [...]]");
+ return;
+ }
+
+ Status error;
+ addr_t start_addr = OptionArgParser::ToRawAddress(
+ &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;
+ }
+
+ 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;
+ }
+ 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;
+ }
+
+ 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;
+ }
+
+ Status status = process->WriteMemoryTags(tagged_range->GetRangeBase(),
+ tagged_range->GetByteSize(), tags);
+
+ if (status.Fail()) {
+ result.SetError(status);
+ return;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+
+ 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..4efa5652a717
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectMultiword.cpp
@@ -0,0 +1,437 @@
+//===-- 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"
+#include <optional>
+
+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();
+}
+
+void 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;
+ }
+
+ auto sub_command = args[0].ref();
+ if (sub_command.empty()) {
+ result.AppendError("Need to specify a non-empty subcommand.");
+ return;
+ }
+
+ if (m_subcommand_dict.empty()) {
+ result.AppendErrorWithFormat("'%s' does not have any subcommands.\n",
+ GetCommandName().str().c_str());
+ return;
+ }
+
+ 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;
+ }
+
+ 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());
+}
+
+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);
+
+ // The subcommand is ambiguous. The completion isn't meaningful.
+ if (!sub_command_object)
+ 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);
+}
+
+std::optional<std::string>
+CommandObjectMultiword::GetRepeatCommand(Args &current_command_args,
+ uint32_t index) {
+ index++;
+ if (current_command_args.GetArgumentCount() <= index)
+ return std::nullopt;
+ CommandObject *sub_command_object =
+ GetSubcommandObject(current_command_args[index].ref());
+ if (sub_command_object == nullptr)
+ return std::nullopt;
+ 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);
+}
+
+std::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 std::nullopt;
+}
+
+llvm::StringRef CommandObjectProxy::GetUnsupportedError() {
+ return "command is not implemented";
+}
+
+void CommandObjectProxy::Execute(const char *args_string,
+ CommandReturnObject &result) {
+ if (CommandObject *proxy_command = GetProxyCommandObject())
+ proxy_command->Execute(args_string, result);
+ else
+ result.AppendError(GetUnsupportedError());
+}
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..5b18f2b60e92
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectPlatform.cpp
@@ -0,0 +1,1845 @@
+//===-- 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 "CommandOptionsProcessAttach.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/CommandOptionArgumentTable.h"
+#include "lldb/Interpreter/CommandOptionValidators.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionGroupFile.h"
+#include "lldb/Interpreter/OptionGroupPlatform.h"
+#include "lldb/Interpreter/OptionGroupPythonClassWithDict.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/ScriptedMetadata.h"
+#include "lldb/Utility/State.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::ArrayRef(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();
+ AddSimpleArgumentList(eArgTypePlatform);
+ }
+
+ ~CommandObjectPlatformSelect() override = default;
+
+ void HandleCompletion(CompletionRequest &request) override {
+ lldb_private::CommandCompletions::PlatformPluginNames(
+ GetCommandInterpreter(), request, nullptr);
+ }
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ void 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");
+ }
+ }
+
+ 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:
+ void 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);
+ }
+};
+
+// "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:
+ void 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");
+ }
+ }
+};
+
+// "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) {
+ AddSimpleArgumentList(eArgTypeConnectURL);
+ }
+
+ ~CommandObjectPlatformConnect() override = default;
+
+protected:
+ void 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");
+ }
+ }
+
+ 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:
+ void 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");
+ }
+ }
+};
+
+// "platform settings"
+class CommandObjectPlatformSettings : public CommandObjectParsed {
+public:
+ CommandObjectPlatformSettings(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform settings",
+ "Set settings for the current target's platform.",
+ "platform settings", 0),
+ m_option_working_dir(LLDB_OPT_SET_1, false, "working-dir", 'w',
+ lldb::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:
+ void 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");
+ }
+ }
+
+ 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) {
+ AddSimpleArgumentList(eArgTypeRemotePath);
+ }
+
+ ~CommandObjectPlatformMkDir() override = default;
+
+ void 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");
+ }
+ }
+
+ Options *GetOptions() override {
+ if (!m_options.DidFinalize()) {
+ m_options.Append(&m_option_permissions);
+ m_options.Finalize();
+ }
+ return &m_options;
+ }
+
+ OptionPermissions m_option_permissions;
+ 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) {
+ AddSimpleArgumentList(eArgTypeRemotePath);
+ }
+
+ ~CommandObjectPlatformFOpen() override = default;
+
+ void 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");
+ }
+ }
+
+ Options *GetOptions() override {
+ if (!m_options.DidFinalize()) {
+ m_options.Append(&m_option_permissions);
+ m_options.Finalize();
+ }
+ return &m_options;
+ }
+
+ OptionPermissions m_option_permissions;
+ 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) {
+ AddSimpleArgumentList(eArgTypeUnsignedInteger);
+ }
+
+ ~CommandObjectPlatformFClose() override = default;
+
+ void 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;
+ }
+ 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");
+ }
+ }
+};
+
+// "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) {
+ AddSimpleArgumentList(eArgTypeUnsignedInteger);
+ }
+
+ ~CommandObjectPlatformFRead() override = default;
+
+ void 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;
+ }
+ 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");
+ }
+ }
+
+ 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::ArrayRef(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) {
+ AddSimpleArgumentList(eArgTypeUnsignedInteger);
+ }
+
+ ~CommandObjectPlatformFWrite() override = default;
+
+ void 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;
+ }
+ 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");
+ }
+ }
+
+ 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::ArrayRef(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 = eArgTypeRemoteFilename;
+ 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)
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::eRemoteDiskFileCompletion, request,
+ nullptr);
+ else if (request.GetCursorIndex() == 1)
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::eDiskFileCompletion, request, nullptr);
+ }
+
+ void 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;
+ }
+
+ 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");
+ }
+ }
+};
+
+// "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.)");
+
+ AddSimpleArgumentList(eArgTypeRemoteFilename);
+ }
+
+ ~CommandObjectPlatformGetSize() override = default;
+
+ void 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;
+ }
+
+ 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");
+ }
+ }
+};
+
+// "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.)");
+
+ AddSimpleArgumentList(eArgTypeRemoteFilename);
+ }
+
+ ~CommandObjectPlatformGetPermissions() override = default;
+
+ void 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;
+ }
+
+ 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");
+ }
+ }
+};
+
+// "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.)");
+
+ AddSimpleArgumentList(eArgTypeRemoteFilename);
+ }
+
+ ~CommandObjectPlatformFileExists() override = default;
+
+ void 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;
+ }
+
+ 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");
+ }
+ }
+};
+
+// "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{eArgTypeRemotePath, 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)
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::eDiskFileCompletion, request, nullptr);
+ else if (request.GetCursorIndex() == 1)
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::eRemoteDiskFileCompletion, request,
+ nullptr);
+ }
+
+ void 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");
+ }
+ }
+};
+
+// "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_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();
+ AddSimpleArgumentList(eArgTypeRunArgs, eArgRepeatStar);
+ }
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ // I didn't make a type for RemoteRunArgs, but since we're going to run
+ // this on the remote system we should use the remote completer.
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::eRemoteDiskFileCompletion, request,
+ nullptr);
+ }
+
+ ~CommandObjectPlatformProcessLaunch() override = default;
+
+ Options *GetOptions() override { return &m_all_options; }
+
+protected:
+ void 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 (!m_class_options.GetName().empty()) {
+ m_options.launch_info.SetProcessPluginName("ScriptedProcess");
+ ScriptedMetadataSP metadata_sp = std::make_shared<ScriptedMetadata>(
+ m_class_options.GetName(), m_class_options.GetStructuredData());
+ m_options.launch_info.SetScriptedMetadata(metadata_sp);
+ target->SetProcessLaunchInfo(m_options.launch_info);
+ }
+
+ 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) {
+ // If no arguments were given to the command, use target.run-args.
+ Args target_run_args;
+ target->GetRunArguments(target_run_args);
+ m_options.launch_info.GetArguments().AppendArguments(target_run_args);
+ }
+
+ ProcessSP process_sp(platform_sp->DebugProcess(
+ m_options.launch_info, debugger, *target, error));
+
+ if (!process_sp && error.Success()) {
+ result.AppendError("failed to launch or debug process");
+ return;
+ } else if (!error.Success()) {
+ result.AppendError(error.AsCString());
+ return;
+ }
+
+ const bool synchronous_execution =
+ debugger.GetCommandInterpreter().GetSynchronous();
+ auto launch_info = m_options.launch_info;
+ bool rebroadcast_first_stop =
+ !synchronous_execution &&
+ launch_info.GetFlags().Test(eLaunchFlagStopAtEntry);
+
+ EventSP first_stop_event_sp;
+ StateType state = process_sp->WaitForProcessToStop(
+ std::nullopt, &first_stop_event_sp, rebroadcast_first_stop,
+ launch_info.GetHijackListener());
+ process_sp->RestoreProcessEvents();
+
+ if (rebroadcast_first_stop) {
+ assert(first_stop_event_sp);
+ process_sp->BroadcastEvent(first_stop_event_sp);
+ return;
+ }
+
+ switch (state) {
+ case eStateStopped: {
+ if (launch_info.GetFlags().Test(eLaunchFlagStopAtEntry))
+ break;
+ if (synchronous_execution) {
+ // Now we have handled the stop-from-attach, and we are just
+ // switching to a synchronous resume. So we should switch to the
+ // SyncResume hijacker.
+ process_sp->ResumeSynchronous(&result.GetOutputStream());
+ } else {
+ error = process_sp->Resume();
+ if (!error.Success()) {
+ result.AppendErrorWithFormat(
+ "process resume at entry point failed: %s",
+ error.AsCString());
+ }
+ }
+ } break;
+ default:
+ result.AppendErrorWithFormat(
+ "initial process state wasn't stopped: %s",
+ StateAsCString(state));
+ break;
+ }
+
+ if (process_sp && process_sp->IsAlive()) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return;
+ }
+ } 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;
+ }
+ } else {
+ result.AppendError("no platform is selected\n");
+ }
+ }
+
+ CommandOptionsProcessLaunch m_options;
+ OptionGroupPythonClassWithDict m_class_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:
+ void 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");
+ }
+ }
+
+ 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::ArrayRef(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) {
+ AddSimpleArgumentList(eArgTypePid, eArgRepeatStar);
+ }
+
+ ~CommandObjectPlatformProcessInfo() override = default;
+
+protected:
+ void 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");
+ }
+ }
+};
+
+#define LLDB_OPTIONS_platform_process_attach
+#include "CommandOptions.inc"
+
+class CommandObjectPlatformProcessAttach : public CommandObjectParsed {
+public:
+ CommandObjectPlatformProcessAttach(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform process attach",
+ "Attach to a process.",
+ "platform process attach <cmd-options>"),
+ 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();
+ }
+
+ ~CommandObjectPlatformProcessAttach() override = default;
+
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+
+ if (!m_class_options.GetName().empty()) {
+ m_options.attach_info.SetProcessPluginName("ScriptedProcess");
+ ScriptedMetadataSP metadata_sp = std::make_shared<ScriptedMetadata>(
+ m_class_options.GetName(), m_class_options.GetStructuredData());
+ m_options.attach_info.SetScriptedMetadata(metadata_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");
+ }
+ }
+
+ Options *GetOptions() override { return &m_all_options; }
+
+protected:
+ CommandOptionsProcessAttach m_options;
+ OptionGroupPythonClassWithDict m_class_options;
+ OptionGroupOptions m_all_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::ArrayRef(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) {
+ AddSimpleArgumentList(eArgTypeNone, eArgRepeatStar);
+ }
+
+ ~CommandObjectPlatformShell() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ void 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;
+ }
+
+ const bool is_alias = !raw_command_line.contains("platform");
+ OptionsWithRaw args(raw_command_line);
+
+ if (args.HasArgs())
+ if (!ParseOptions(args.GetArgs(), result))
+ return;
+
+ if (args.GetRawPart().empty()) {
+ result.GetOutputStream().Printf("%s <shell-command>\n",
+ is_alias ? "shell" : "platform shell");
+ return;
+ }
+
+ 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);
+ }
+ }
+
+ 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{eArgTypeRemotePath, 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;
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::eDiskFileCompletion, request, nullptr);
+ }
+
+ void DoExecute(Args &args, CommandReturnObject &result) override {
+ if (args.GetArgumentCount() != 2) {
+ result.AppendError("platform target-install takes two arguments");
+ return;
+ }
+ // 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;
+ }
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (!platform_sp) {
+ result.AppendError("no platform currently selected");
+ return;
+ }
+
+ Status error = platform_sp->Install(src, dst);
+ if (error.Success()) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendErrorWithFormat("install failed: %s", error.AsCString());
+ }
+ }
+};
+
+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..f3108b8a768d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectPlugin.cpp
@@ -0,0 +1,57 @@
+//===-- 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) {
+ AddSimpleArgumentList(eArgTypeFilename);
+ }
+
+ ~CommandObjectPluginLoad() override = default;
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ size_t argc = command.GetArgumentCount();
+
+ if (argc != 1) {
+ result.AppendError("'plugin load' requires one argument");
+ return;
+ }
+
+ 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());
+ }
+ }
+};
+
+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..50695af55693
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.cpp
@@ -0,0 +1,1864 @@
+//===-- 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 "CommandObjectBreakpoint.h"
+#include "CommandObjectTrace.h"
+#include "CommandOptionsProcessAttach.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/CommandOptionArgumentTable.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/ScriptedMetadata.h"
+#include "lldb/Utility/State.h"
+
+#include "llvm/ADT/ScopeExit.h"
+
+#include <bitset>
+#include <optional>
+
+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();
+
+ AddSimpleArgumentList(eArgTypeRunArgs, eArgRepeatOptional);
+ }
+
+ ~CommandObjectProcessLaunch() override = default;
+
+ Options *GetOptions() override { return &m_all_options; }
+
+ std::optional<std::string> GetRepeatCommand(Args &current_command_args,
+ uint32_t index) override {
+ // No repeat for "process launch"...
+ return std::string("");
+ }
+
+protected:
+ void 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;
+ }
+
+ StateType state = eStateInvalid;
+
+ if (!StopProcessIfNecessary(m_exe_ctx.GetProcessPtr(), state, result))
+ return;
+
+ // 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");
+ ScriptedMetadataSP metadata_sp = std::make_shared<ScriptedMetadata>(
+ m_class_options.GetName(), m_class_options.GetStructuredData());
+ m_options.launch_info.SetScriptedMetadata(metadata_sp);
+ 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));
+
+ // 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);
+ // This message will refer to an event that happened after the process
+ // launched.
+ llvm::StringRef data = stream.GetString();
+ if (!data.empty())
+ result.AppendMessage(data);
+ result.SetDidChangeProcessState(true);
+ } else {
+ result.AppendError(
+ "no error returned from Target::Launch, and target has no process");
+ }
+ } else {
+ result.AppendError(error.AsCString());
+ }
+ }
+
+ 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:
+ CommandObjectProcessAttach(CommandInterpreter &interpreter)
+ : CommandObjectProcessLaunchOrAttach(
+ interpreter, "process attach", "Attach to a process.",
+ "process attach <cmd-options>", 0, "attach"),
+ 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();
+ }
+
+ ~CommandObjectProcessAttach() override = default;
+
+ Options *GetOptions() override { return &m_all_options; }
+
+protected:
+ void 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;
+
+ 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;
+ }
+ }
+
+ if (!m_class_options.GetName().empty()) {
+ m_options.attach_info.SetProcessPluginName("ScriptedProcess");
+ ScriptedMetadataSP metadata_sp = std::make_shared<ScriptedMetadata>(
+ m_class_options.GetName(), m_class_options.GetStructuredData());
+ m_options.attach_info.SetScriptedMetadata(metadata_sp);
+ }
+
+ // 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;
+
+ // 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);
+ }
+ }
+
+ CommandOptionsProcessAttach m_options;
+ OptionGroupPythonClassWithDict m_class_options;
+ OptionGroupOptions m_all_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::ArrayRef(g_process_continue_options);
+ }
+
+ uint32_t m_ignore = 0;
+ Args m_run_to_bkpt_args;
+ bool m_any_bkpts_specified = false;
+ };
+
+ void 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->GetNumberOfConstituents();
+ for (size_t i = 0; i < num_owners; i++) {
+ Breakpoint &bp_ref =
+ bp_site_sp->GetConstituentAtIndex(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;
+ }
+ 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;
+ }
+
+ // 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;
+ }
+
+ // 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);
+ if (!with_locs.Contains(tmp_id) && 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));
+ }
+ }
+
+ 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::ArrayRef(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:
+ void 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());
+ }
+ }
+
+ 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::ArrayRef(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) {
+ AddSimpleArgumentList(eArgTypeConnectURL);
+ }
+
+ ~CommandObjectProcessConnect() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ void 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;
+ }
+
+ 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;
+ }
+
+ 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"));
+ }
+ }
+
+ 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;
+ ArchSpec arch =
+ execution_context->GetProcessPtr()->GetSystemArchitecture();
+ switch (short_option) {
+ case 'i':
+ do_install = true;
+ if (!option_arg.empty())
+ install_path.SetFile(option_arg, arch.GetTriple());
+ 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::ArrayRef(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) {
+ AddSimpleArgumentList(eArgTypePath, eArgRepeatPlus);
+ }
+
+ ~CommandObjectProcessLoad() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (!m_exe_ctx.HasProcessScope())
+ return;
+ CommandObject::HandleArgumentCompletion(request, opt_element_vector);
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ void 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());
+ }
+ }
+ }
+
+ 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) {
+ AddSimpleArgumentList(eArgTypeUnsignedInteger);
+ }
+
+ ~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:
+ void 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;
+ }
+ }
+ }
+ }
+};
+
+// 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) {
+ AddSimpleArgumentList(eArgTypeUnixSignal);
+ }
+
+ ~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->GetSignalAsStringRef(signo));
+ signo = signals->GetNextSignalNumber(signo);
+ }
+ }
+
+protected:
+ void 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());
+ }
+ }
+};
+
+// 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:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (process == nullptr) {
+ result.AppendError("no process to halt");
+ return;
+ }
+
+ 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());
+ }
+ }
+};
+
+// 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:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (process == nullptr) {
+ result.AppendError("no process to kill");
+ return;
+ }
+
+ Status error(process->Destroy(true));
+ if (error.Success()) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("Failed to kill process: %s\n",
+ error.AsCString());
+ }
+ }
+};
+
+#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) {
+ AddSimpleArgumentList(eArgTypePath);
+ }
+
+ ~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::ArrayRef(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':
+ error = m_core_dump_options.SetPluginName(option_arg.data());
+ break;
+ case 's':
+ m_core_dump_options.SetStyle(
+ (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_core_dump_options.Clear();
+ }
+
+ // Instance variables to hold the values for command options.
+ SaveCoreOptions m_core_dump_options;
+ };
+
+protected:
+ void 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));
+ FileSystem::Instance().Resolve(output_file);
+ auto &core_dump_options = m_options.m_core_dump_options;
+ core_dump_options.SetOutputFile(output_file);
+ Status error = PluginManager::SaveCore(process_sp, core_dump_options);
+ if (error.Success()) {
+ if (core_dump_options.GetStyle() ==
+ SaveCoreStyle::eSaveCoreDirtyOnly ||
+ core_dump_options.GetStyle() ==
+ 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");
+ }
+ }
+
+ 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::ArrayRef(g_process_status_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ bool m_verbose = false;
+ };
+
+protected:
+ void 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 != LLDB_INVALID_ADDRESS_MASK) {
+ 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;
+ }
+
+ auto expected_crash_info =
+ platform_sp->FetchExtendedCrashInformation(*process);
+
+ if (!expected_crash_info) {
+ result.AppendError(llvm::toString(expected_crash_info.takeError()));
+ return;
+ }
+
+ StructuredData::DictionarySP crash_info_sp = *expected_crash_info;
+
+ if (crash_info_sp) {
+ strm.EOL();
+ strm.PutCString("Extended Crash Information:\n");
+ crash_info_sp->GetDescription(strm);
+ }
+ }
+ }
+
+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::ArrayRef(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");
+ AddSimpleArgumentList(eArgTypeUnixSignal, eArgRepeatStar);
+ }
+
+ ~CommandObjectProcessHandle() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ void PrintSignalHeader(Stream &str) {
+ str.Printf("NAME PASS STOP NOTIFY\n");
+ str.Printf("=========== ===== ===== ======\n");
+ }
+
+ void PrintSignal(Stream &str, int32_t signo, llvm::StringRef sig_name,
+ const UnixSignalsSP &signals_sp) {
+ bool stop;
+ bool suppress;
+ bool notify;
+
+ str.Format("{0, -11} ", 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->GetSignalAsStringRef(signo),
+ signals_sp);
+ signo = signals_sp->GetNextSignalNumber(signo);
+ }
+ }
+ }
+
+protected:
+ void 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();
+
+ std::optional<bool> stop_action = {};
+ std::optional<bool> pass_action = {};
+ std::optional<bool> notify_action = {};
+
+ if (!m_options.stop.empty()) {
+ bool success = false;
+ bool value = OptionArgParser::ToBoolean(m_options.stop, false, &success);
+ if (!success) {
+ result.AppendError(
+ "Invalid argument for command option --stop; must be "
+ "true or false.\n");
+ return;
+ }
+
+ stop_action = value;
+ }
+
+ if (!m_options.pass.empty()) {
+ bool success = false;
+ bool value = OptionArgParser::ToBoolean(m_options.pass, false, &success);
+ if (!success) {
+ result.AppendError(
+ "Invalid argument for command option --pass; must be "
+ "true or false.\n");
+ return;
+ }
+ pass_action = value;
+ }
+
+ if (!m_options.notify.empty()) {
+ bool success = false;
+ bool value =
+ OptionArgParser::ToBoolean(m_options.notify, false, &success);
+ if (!success) {
+ result.AppendError("Invalid argument for command option --notify; must "
+ "be true or false.\n");
+ return;
+ }
+ notify_action = value;
+ }
+
+ if (!m_options.notify.empty() && !notify_action.has_value()) {
+ }
+
+ bool no_actions = (!stop_action.has_value() && !pass_action.has_value() &&
+ !notify_action.has_value());
+ if (m_options.only_target_values && !no_actions) {
+ result.AppendError("-t is for reporting, not setting, target values.");
+ return;
+ }
+
+ 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;
+ }
+
+ // 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;
+ }
+
+ // 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) {
+ if (stop_action.has_value())
+ signals_sp->SetShouldStop(signo, *stop_action);
+ if (pass_action.has_value()) {
+ bool suppress = !*pass_action;
+ signals_sp->SetShouldSuppress(signo, suppress);
+ }
+ if (notify_action.has_value())
+ 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;
+ }
+ num_signals_set = num_args;
+ }
+ auto set_lazy_bool = [](std::optional<bool> action) -> LazyBool {
+ if (!action.has_value())
+ return eLazyBoolCalculate;
+ return (*action) ? eLazyBoolYes : eLazyBoolNo;
+ };
+
+ // 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.has_value() || stop_action.has_value() ||
+ pass_action.has_value()) &&
+ 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.has_value())
+ signals_sp->SetShouldNotify(signo, *notify_action);
+ if (stop_action.has_value())
+ signals_sp->SetShouldStop(signo, *stop_action);
+ if (pass_action.has_value()) {
+ 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);
+ }
+
+ 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;
+
+ void 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);
+ }
+};
+
+// 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..8e7830b2afc6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectQuit.cpp
@@ -0,0 +1,105 @@
+//===-- 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]") {
+ AddSimpleArgumentList(eArgTypeUnsignedInteger);
+}
+
+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;
+}
+
+void 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;
+ }
+ }
+
+ if (command.GetArgumentCount() > 1) {
+ result.AppendError("Too many arguments for 'quit'. Only an optional exit "
+ "code is allowed");
+ return;
+ }
+
+ // 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;
+ }
+ if (!m_interpreter.SetQuitExitCode(exit_code)) {
+ result.AppendError("The current driver doesn't allow custom exit codes"
+ " for the quit command.");
+ return;
+ }
+ }
+
+ const uint32_t event_type =
+ CommandInterpreter::eBroadcastBitQuitCommandReceived;
+ m_interpreter.BroadcastEvent(event_type);
+ result.SetStatus(eReturnStatusQuit);
+}
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..c27c0d1da3b9
--- /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:
+ void 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..f638d707e17e
--- /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 completion_type_mask, bool is_removable)
+ : CommandObjectRaw(interpreter, name, help, syntax),
+ 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();
+}
+
+void 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;
+ }
+
+ // 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.
+ bool force_repeat_command = true;
+ m_interpreter.HandleCommand(new_command->c_str(), eLazyBoolNo, result,
+ force_repeat_command);
+ return;
+ }
+ }
+ 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 ";
+}
+
+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) {
+ lldb_private::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..c78b0b586c37
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectRegexCommand.h
@@ -0,0 +1,65 @@
+//===-- 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 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:
+ void 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_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..4e047ccbc10b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectRegister.cpp
@@ -0,0 +1,461 @@
+//===-- 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/DumpRegisterInfo.h"
+#include "lldb/Core/DumpRegisterValue.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandOptionArgumentTable.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, UINT64_MAX, UINT64_MAX,
+ {{CommandArgumentType::eArgTypeFormat,
+ "Specify a format to be used for display. If this "
+ "is set, register fields will not be displayed."}}) {
+ AddSimpleArgumentList(eArgTypeRegisterName, eArgRepeatStar);
+
+ // 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;
+ CommandObject::HandleArgumentCompletion(request, opt_element_vector);
+ }
+
+ Options *GetOptions() override { return &m_option_group; }
+
+ bool DumpRegister(const ExecutionContext &exe_ctx, Stream &strm,
+ RegisterContext &reg_ctx, const RegisterInfo &reg_info,
+ bool print_flags) {
+ RegisterValue reg_value;
+ if (!reg_ctx.ReadRegister(&reg_info, reg_value))
+ return false;
+
+ 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,
+ exe_ctx.GetBestExecutionContextScope(), print_flags,
+ exe_ctx.GetTargetSP());
+ 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;
+ }
+
+ 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 (reg_info && DumpRegister(exe_ctx, strm, *reg_ctx, *reg_info,
+ /*print_flags=*/false))
+ ++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:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ Stream &strm = result.GetOutputStream();
+ RegisterContext *reg_ctx = m_exe_ctx.GetRegisterContext();
+
+ 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]->GetValueAs<uint64_t>().value_or(
+ UINT32_MAX);
+ 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("$");
+
+ if (const RegisterInfo *reg_info =
+ reg_ctx->GetRegisterInfoByName(arg_str)) {
+ // If they have asked for a specific format don't obscure that by
+ // printing flags afterwards.
+ bool print_flags =
+ !m_format_options.GetFormatValue().OptionWasSet();
+ if (!DumpRegister(m_exe_ctx, strm, *reg_ctx, *reg_info,
+ print_flags))
+ strm.Printf("%-12s = error: unavailable\n", reg_info->name);
+ } else {
+ result.AppendErrorWithFormat("Invalid register name '%s'.\n",
+ arg_str.str().c_str());
+ }
+ }
+ }
+ }
+ }
+
+ 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::ArrayRef(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;
+
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::eRegisterCompletion, request, nullptr);
+ }
+
+protected:
+ void 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;
+ }
+ }
+ 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());
+ }
+ }
+ }
+};
+
+// "register info"
+class CommandObjectRegisterInfo : public CommandObjectParsed {
+public:
+ CommandObjectRegisterInfo(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "register info",
+ "View information about a register.", nullptr,
+ eCommandRequiresFrame | eCommandRequiresRegContext |
+ eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused) {
+ SetHelpLong(R"(
+Name The name lldb uses for the register, optionally with an alias.
+Size The size of the register in bytes and again in bits.
+Invalidates (*) The registers that would be changed if you wrote this
+ register. For example, writing to a narrower alias of a wider
+ register would change the value of the wider register.
+Read from (*) The registers that the value of this register is constructed
+ from. For example, a narrower alias of a wider register will be
+ read from the wider register.
+In sets (*) The register sets that contain this register. For example the
+ PC will be in the "General Purpose Register" set.
+Fields (*) A table of the names and bit positions of the values contained
+ in this register.
+
+Fields marked with (*) may not always be present. Some information may be
+different for the same register when connected to different debug servers.)");
+
+ AddSimpleArgumentList(eArgTypeRegisterName);
+ }
+
+ ~CommandObjectRegisterInfo() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (!m_exe_ctx.HasProcessScope() || request.GetCursorIndex() != 0)
+ return;
+ CommandObject::HandleArgumentCompletion(request, opt_element_vector);
+ }
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ if (command.GetArgumentCount() != 1) {
+ result.AppendError("register info takes exactly 1 argument: <reg-name>");
+ return;
+ }
+
+ llvm::StringRef reg_name = command[0].ref();
+ RegisterContext *reg_ctx = m_exe_ctx.GetRegisterContext();
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
+ if (reg_info) {
+ DumpRegisterInfo(
+ result.GetOutputStream(), *reg_ctx, *reg_info,
+ GetCommandInterpreter().GetDebugger().GetTerminalWidth());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else
+ result.AppendErrorWithFormat("No register found with name '%s'.\n",
+ reg_name.str().c_str());
+ }
+};
+
+// CommandObjectRegister constructor
+CommandObjectRegister::CommandObjectRegister(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "register",
+ "Commands to access registers for the current "
+ "thread and stack frame.",
+ "register [read|write|info] ...") {
+ LoadSubCommand("read",
+ CommandObjectSP(new CommandObjectRegisterRead(interpreter)));
+ LoadSubCommand("write",
+ CommandObjectSP(new CommandObjectRegisterWrite(interpreter)));
+ LoadSubCommand("info",
+ CommandObjectSP(new CommandObjectRegisterInfo(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/CommandObjectScripting.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectScripting.cpp
new file mode 100644
index 000000000000..fee0565a7c48
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectScripting.cpp
@@ -0,0 +1,144 @@
+//===-- CommandObjectScripting.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 "CommandObjectScripting.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/CommandOptionArgumentTable.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;
+
+#define LLDB_OPTIONS_scripting_run
+#include "CommandOptions.inc"
+
+class CommandObjectScriptingRun : public CommandObjectRaw {
+public:
+ CommandObjectScriptingRun(CommandInterpreter &interpreter)
+ : CommandObjectRaw(
+ interpreter, "scripting run",
+ "Invoke the script interpreter with provided code and display any "
+ "results. Start the interactive interpreter if no code is "
+ "supplied.",
+ "scripting run [--language <scripting-language> --] "
+ "[<script-code>]") {}
+
+ ~CommandObjectScriptingRun() 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 '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 OptionParsingStarting(ExecutionContext *execution_context) override {
+ language = lldb::eScriptLanguageNone;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::ArrayRef(g_scripting_run_options);
+ }
+
+ lldb::ScriptLanguage language = lldb::eScriptLanguageNone;
+ };
+
+protected:
+ void DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) override {
+ // 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;
+ 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;
+ }
+
+ ScriptInterpreter *script_interpreter =
+ GetDebugger().GetScriptInterpreter(true, language);
+
+ if (script_interpreter == nullptr) {
+ result.AppendError("no script interpreter");
+ return;
+ }
+
+ // 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;
+ }
+
+ // 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);
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+#pragma mark CommandObjectMultiwordScripting
+
+// CommandObjectMultiwordScripting
+
+CommandObjectMultiwordScripting::CommandObjectMultiwordScripting(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "scripting",
+ "Commands for operating on the scripting functionnalities.",
+ "scripting <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("run",
+ CommandObjectSP(new CommandObjectScriptingRun(interpreter)));
+}
+
+CommandObjectMultiwordScripting::~CommandObjectMultiwordScripting() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectScripting.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectScripting.h
new file mode 100644
index 000000000000..0ccd579deec0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectScripting.h
@@ -0,0 +1,25 @@
+//===-- CommandObjectScripting.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_COMMANDOBJECTSCRIPTING_H
+#define LLDB_SOURCE_INTERPRETER_COMMANDOBJECTSCRIPTING_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+class CommandObjectMultiwordScripting : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordScripting(CommandInterpreter &interpreter);
+
+ ~CommandObjectMultiwordScripting() override;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_INTERPRETER_COMMANDOBJECTSCRIPTING_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..c381ba4f74f1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectSession.cpp
@@ -0,0 +1,195 @@
+#include "CommandObjectSession.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandOptionArgumentTable.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]") {
+ AddSimpleArgumentList(eArgTypePath, eArgRepeatOptional);
+ }
+
+ ~CommandObjectSessionSave() override = default;
+
+protected:
+ void 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);
+ }
+};
+
+#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::ArrayRef(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;
+ };
+
+ void 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);
+ }
+ }
+ }
+
+ 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..7bbb0dd567ab
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectSettings.cpp
@@ -0,0 +1,1093 @@
+//===-- 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/CommandOptionArgumentTable.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::ArrayRef(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
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::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, error));
+ if (!value_sp)
+ return;
+ value_sp->AutoComplete(m_interpreter, request);
+ }
+
+protected:
+ void DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) override {
+ Args cmd_args(command);
+
+ // Process possible options.
+ if (!ParseOptions(cmd_args, result))
+ return;
+
+ 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;
+ }
+
+ 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;
+ }
+
+ // 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;
+ }
+
+ // 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;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+
+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) {
+ AddSimpleArgumentList(eArgTypeSettingVariableName, eArgRepeatOptional);
+ }
+
+ ~CommandObjectSettingsShow() override = default;
+
+protected:
+ void 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);
+ }
+ }
+};
+
+// 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) {
+ AddSimpleArgumentList(eArgTypeSettingVariableName, eArgRepeatOptional);
+ }
+
+ ~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::ArrayRef(g_settings_write_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ std::string m_filename;
+ bool m_append = false;
+ };
+
+protected:
+ void 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;
+ }
+
+ // Exporting should not be context sensitive.
+ ExecutionContext clean_ctx;
+
+ if (args.empty()) {
+ GetDebugger().DumpAllPropertyValues(&clean_ctx, out_file,
+ OptionValue::eDumpGroupExport);
+ return;
+ }
+
+ for (const auto &arg : args) {
+ Status error(GetDebugger().DumpPropertyValue(
+ &clean_ctx, out_file, arg.ref(), OptionValue::eDumpGroupExport));
+ if (!error.Success()) {
+ result.AppendError(error.AsCString());
+ }
+ }
+ }
+
+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::ArrayRef(g_settings_read_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ std::string m_filename;
+ };
+
+protected:
+ void 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);
+ }
+
+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 {
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::eSettingsNameCompletion, request,
+ nullptr);
+ }
+
+protected:
+ void DoExecute(Args &args, CommandReturnObject &result) override {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ 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, 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());
+ }
+ }
+};
+
+// 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)
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::eSettingsNameCompletion, request,
+ nullptr);
+ }
+
+protected:
+ void DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) override {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+
+ Args cmd_args(command);
+
+ // Process possible options.
+ if (!ParseOptions(cmd_args, result))
+ return;
+
+ 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;
+ }
+
+ 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;
+ }
+
+ // 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());
+ }
+ }
+};
+
+// 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)
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::eSettingsNameCompletion, request,
+ nullptr);
+ }
+
+protected:
+ void 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;
+ }
+
+ // 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());
+ } else {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+ }
+};
+
+// 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)
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::eSettingsNameCompletion, request,
+ nullptr);
+ }
+
+protected:
+ void 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;
+ }
+
+ 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;
+ }
+
+ // 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());
+ }
+ }
+};
+
+// 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)
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::eSettingsNameCompletion, request,
+ nullptr);
+ }
+
+protected:
+ void 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;
+ }
+
+ 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;
+ }
+
+ // 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());
+ }
+ }
+};
+
+// 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)
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::eSettingsNameCompletion, request,
+ nullptr);
+ }
+
+protected:
+ void 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;
+ }
+
+ 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;
+ }
+
+ // 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());
+ }
+ }
+};
+
+// 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) {
+ AddSimpleArgumentList(eArgTypeSettingVariableName);
+ }
+
+ ~CommandObjectSettingsClear() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ // Attempting to complete variable name
+ if (request.GetCursorIndex() < 2)
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::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::ArrayRef(g_settings_clear_options);
+ }
+
+ bool m_clear_all = false;
+ };
+
+protected:
+ void 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;
+ }
+ GetDebugger().GetValueProperties()->Clear();
+ return;
+ }
+
+ if (argc != 1) {
+ result.AppendError("'settings clear' takes exactly one argument");
+ return;
+ }
+
+ 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;
+ }
+
+ Status error(GetDebugger().SetPropertyValue(
+ &m_exe_ctx, eVarSetOperationClear, var_name, llvm::StringRef()));
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ }
+ }
+
+ 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..f54b712adfc4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectSource.cpp
@@ -0,0 +1,1295 @@
+//===-- 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/CommandOptionArgumentTable.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/Process.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Utility/FileSpec.h"
+#include <optional>
+
+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::ArrayRef(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;
+ for (const SymbolContext &sc : sc_list) {
+ 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.GetFile()))
+ 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 SupportFileList &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.GetFile());
+ 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);
+ for (const SymbolContext &sc : sc_list_symbols) {
+ 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 (const SymbolContext &sc : sc_list_funcs) {
+ 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;
+ }
+
+ void 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;
+ }
+ }
+
+ 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;
+ }
+ } else if (target->GetImages().GetSize() == 0) {
+ result.AppendError("The target has no associated executable images.");
+ return;
+ }
+
+ // 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);
+ }
+ }
+
+ 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::ArrayRef(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; }
+
+ std::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_sp->Equal(
+ *rhs.line_entry.original_file_sp,
+ SupportFile::eEqualFileSpecAndChecksumIfSet) &&
+ line_entry.line == rhs.line_entry.line;
+ }
+
+ bool operator!=(const SourceInfo &rhs) const {
+ return function != rhs.function ||
+ !line_entry.original_file_sp->Equal(
+ *rhs.line_entry.original_file_sp,
+ SupportFile::eEqualFileSpecAndChecksumIfSet) ||
+ line_entry.line != rhs.line_entry.line;
+ }
+
+ bool operator<(const SourceInfo &rhs) const {
+ if (function.GetCString() < rhs.function.GetCString())
+ return true;
+ if (line_entry.GetFile().GetDirectory().GetCString() <
+ rhs.line_entry.GetFile().GetDirectory().GetCString())
+ return true;
+ if (line_entry.GetFile().GetFilename().GetCString() <
+ rhs.line_entry.GetFile().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.GetFile();
+ 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);
+ }
+ }
+
+ void 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);
+ if (sc_list.GetSize() == 0) {
+ // 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);
+ for (const SymbolContext &sc : sc_list_symbols) {
+ 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));
+ break;
+ }
+ }
+ }
+ }
+
+ if (sc_list.GetSize() == 0) {
+ result.AppendErrorWithFormat("Could not find function named: \"%s\".\n",
+ m_options.symbol_name.c_str());
+ return;
+ }
+
+ std::set<SourceInfo> source_match_set;
+ bool displayed_something = false;
+ for (const SymbolContext &sc : sc_list) {
+ SourceInfo source_info(sc.GetFunctionName(),
+ sc.GetFunctionStartLineEntry());
+ if (source_info.IsValid() &&
+ 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);
+ return;
+ } 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;
+ }
+ } 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;
+ }
+ }
+ }
+
+ if (sc_list.GetSize() == 0) {
+ result.AppendErrorWithFormat(
+ "no modules contain load address 0x%" PRIx64 ".\n",
+ m_options.address);
+ return;
+ }
+ }
+ for (const SymbolContext &sc : sc_list) {
+ 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;
+ }
+
+ if (num_matches > 1) {
+ bool got_multiple = false;
+ CompileUnit *test_cu = nullptr;
+
+ for (const SymbolContext &sc : sc_list) {
+ 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;
+ }
+ }
+
+ 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());
+ }
+ }
+ }
+ }
+
+ 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;
+};
+
+class CommandObjectSourceCacheDump : public CommandObjectParsed {
+public:
+ CommandObjectSourceCacheDump(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "source cache dump",
+ "Dump the state of the source code cache. Intended "
+ "to be used for debugging LLDB itself.",
+ nullptr) {}
+
+ ~CommandObjectSourceCacheDump() override = default;
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ // Dump the debugger source cache.
+ result.GetOutputStream() << "Debugger Source File Cache\n";
+ SourceManager::SourceFileCache &cache = GetDebugger().GetSourceFileCache();
+ cache.Dump(result.GetOutputStream());
+
+ // Dump the process source cache.
+ if (ProcessSP process_sp = m_exe_ctx.GetProcessSP()) {
+ result.GetOutputStream() << "\nProcess Source File Cache\n";
+ SourceManager::SourceFileCache &cache = process_sp->GetSourceFileCache();
+ cache.Dump(result.GetOutputStream());
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+};
+
+class CommandObjectSourceCacheClear : public CommandObjectParsed {
+public:
+ CommandObjectSourceCacheClear(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "source cache clear",
+ "Clear the source code cache.\n", nullptr) {}
+
+ ~CommandObjectSourceCacheClear() override = default;
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ // Clear the debugger cache.
+ SourceManager::SourceFileCache &cache = GetDebugger().GetSourceFileCache();
+ cache.Clear();
+
+ // Clear the process cache.
+ if (ProcessSP process_sp = m_exe_ctx.GetProcessSP())
+ process_sp->GetSourceFileCache().Clear();
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+};
+
+class CommandObjectSourceCache : public CommandObjectMultiword {
+public:
+ CommandObjectSourceCache(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "source cache",
+ "Commands for managing the source code cache.",
+ "source cache <sub-command>") {
+ LoadSubCommand(
+ "dump", CommandObjectSP(new CommandObjectSourceCacheDump(interpreter)));
+ LoadSubCommand("clear", CommandObjectSP(new CommandObjectSourceCacheClear(
+ interpreter)));
+ }
+
+ ~CommandObjectSourceCache() override = default;
+
+private:
+ CommandObjectSourceCache(const CommandObjectSourceCache &) = delete;
+ const CommandObjectSourceCache &
+ operator=(const CommandObjectSourceCache &) = delete;
+};
+
+#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)));
+ LoadSubCommand("cache",
+ CommandObjectSP(new CommandObjectSourceCache(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..d552508f448e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectSource.h
@@ -0,0 +1,25 @@
+//===-- CommandObjectSource.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 {
+
+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..53855e7d0316
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectStats.cpp
@@ -0,0 +1,164 @@
+//===-- 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/CommandOptionArgumentTable.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.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:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ if (DebuggerStats::GetCollectingStats()) {
+ result.AppendError("statistics already enabled");
+ return;
+ }
+
+ DebuggerStats::SetCollectingStats(true);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+};
+
+class CommandObjectStatsDisable : public CommandObjectParsed {
+public:
+ CommandObjectStatsDisable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "disable",
+ "Disable statistics collection", nullptr,
+ eCommandProcessMustBePaused) {}
+
+ ~CommandObjectStatsDisable() override = default;
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ if (!DebuggerStats::GetCollectingStats()) {
+ result.AppendError("need to enable statistics before disabling them");
+ return;
+ }
+
+ DebuggerStats::SetCollectingStats(false);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+};
+
+#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;
+ case 's':
+ m_stats_options.SetSummaryOnly(true);
+ break;
+ case 'f':
+ m_stats_options.SetLoadAllDebugInfo(true);
+ break;
+ case 'r':
+ if (llvm::Expected<bool> bool_or_error =
+ OptionArgParser::ToBoolean("--targets", option_arg))
+ m_stats_options.SetIncludeTargets(*bool_or_error);
+ else
+ error = bool_or_error.takeError();
+ break;
+ case 'm':
+ if (llvm::Expected<bool> bool_or_error =
+ OptionArgParser::ToBoolean("--modules", option_arg))
+ m_stats_options.SetIncludeModules(*bool_or_error);
+ else
+ error = bool_or_error.takeError();
+ break;
+ case 't':
+ if (llvm::Expected<bool> bool_or_error =
+ OptionArgParser::ToBoolean("--transcript", option_arg))
+ m_stats_options.SetIncludeTranscript(*bool_or_error);
+ else
+ error = bool_or_error.takeError();
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_all_targets = false;
+ m_stats_options = StatisticsOptions();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::ArrayRef(g_statistics_dump_options);
+ }
+
+ const StatisticsOptions &GetStatisticsOptions() { return m_stats_options; }
+
+ bool m_all_targets = false;
+ StatisticsOptions m_stats_options = StatisticsOptions();
+ };
+
+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:
+ void 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, m_options.GetStatisticsOptions()));
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+
+ 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..d594330934ad
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectTarget.cpp
@@ -0,0 +1,5351 @@
+//===-- 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/Address.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/IOHandler.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.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/CommandOptionArgumentTable.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/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/Stream.h"
+#include "lldb/Utility/StructuredData.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/ADT/StringRef.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>");
+
+ std::string formatted_label = "";
+ const std::string &label = target->GetLabel();
+ if (!label.empty()) {
+ formatted_label = " (" + label + ")";
+ }
+
+ strm.Printf("%starget #%u%s: %s", prefix_cstr ? prefix_cstr : "", target_idx,
+ formatted_label.data(), 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;
+}
+
+#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::ArrayRef(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_label(LLDB_OPT_SET_1, false, "label", 'l', 0, eArgTypeName,
+ "Optional name for this target.", nullptr),
+ 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.") {
+
+ AddSimpleArgumentList(eArgTypeFilename);
+
+ 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_label, 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; }
+
+protected:
+ void 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;
+ }
+ }
+
+ 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;
+ }
+ }
+
+ 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;
+ }
+
+ const llvm::StringRef label =
+ m_label.GetOptionValue().GetCurrentValueAsRef();
+ if (!label.empty()) {
+ if (auto E = target_sp->SetLabel(label))
+ result.SetError(std::move(E));
+ return;
+ }
+
+ 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;
+ }
+ }
+ } 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;
+ }
+ } 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;
+ }
+ if (platform_sp->IsConnected() && !platform_sp->GetFileExists(remote_file)) {
+ result.AppendError("remote --> local transfer without local "
+ "path is not implemented yet");
+ return;
+ }
+ // 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;
+ }
+ }
+
+ 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.SetDirectory(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("unknown core file format"));
+ return;
+ } 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("Unknown core file format '{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());
+ }
+ }
+
+private:
+ OptionGroupOptions m_option_group;
+ OptionGroupArchitecture m_arch_option;
+ OptionGroupPlatform m_platform_options;
+ OptionGroupFile m_core_file;
+ OptionGroupString m_label;
+ 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:
+ void 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);
+ }
+};
+
+#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) {
+ AddSimpleArgumentList(eArgTypeTargetID);
+ }
+
+ ~CommandObjectTargetSelect() override = default;
+
+protected:
+ void DoExecute(Args &args, CommandReturnObject &result) override {
+ if (args.GetArgumentCount() == 1) {
+ const char *target_identifier = args.GetArgumentAtIndex(0);
+ uint32_t target_idx = LLDB_INVALID_INDEX32;
+ TargetList &target_list = GetDebugger().GetTargetList();
+ const uint32_t num_targets = target_list.GetNumTargets();
+ if (llvm::to_integer(target_identifier, target_idx)) {
+ 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 {
+ for (size_t i = 0; i < num_targets; i++) {
+ if (TargetSP target_sp = target_list.GetTargetAtIndex(i)) {
+ const std::string &label = target_sp->GetLabel();
+ if (!label.empty() && label == target_identifier) {
+ target_idx = i;
+ break;
+ }
+ }
+ }
+
+ if (target_idx != LLDB_INVALID_INDEX32) {
+ 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 {
+ result.AppendErrorWithFormat("invalid index string value '%s'\n",
+ target_identifier);
+ }
+ }
+ } else {
+ result.AppendError(
+ "'target select' takes a single argument: a target index\n");
+ }
+ }
+};
+
+#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();
+ AddSimpleArgumentList(eArgTypeTargetID, eArgRepeatStar);
+ }
+
+ ~CommandObjectTargetDelete() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ void 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 (size_t 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;
+ }
+
+ 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;
+ }
+ 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;
+ }
+ } else {
+ target_sp = target_list.GetSelectedTarget();
+ if (!target_sp) {
+ result.AppendErrorWithFormat("no target is currently selected\n");
+ return;
+ }
+ 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;
+ }
+
+ 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:
+ void 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);
+ }
+};
+
+#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.") {
+ AddSimpleArgumentList(eArgTypeVarName, eArgRepeatPlus);
+
+ 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);
+
+ if (llvm::Error error = valobj_sp->Dump(s, options))
+ s << "error: " << toString(std::move(error));
+ }
+
+ 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());
+ }
+ }
+
+ void 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;
+ }
+ 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;
+ } 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);
+ }
+
+ for (const SymbolContext &sc : sc_list) {
+ 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);
+ }
+
+ 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:
+ void 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");
+ }
+ }
+ }
+ }
+};
+
+#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:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+ bool notify = true;
+ target->GetImageSearchPathList().Clear(notify);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+};
+
+#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:
+ void 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;
+ }
+
+ // 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;
+ }
+ }
+ } else {
+ result.AppendError("insert requires at least three arguments\n");
+ }
+ }
+};
+
+#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:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+
+ target->GetImageSearchPathList().Dump(&result.GetOutputStream());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+};
+
+#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) {
+ AddSimpleArgumentList(eArgTypeDirectoryName);
+ }
+
+ ~CommandObjectTargetModulesSearchPathsQuery() override = default;
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+ if (command.GetArgumentCount() != 1) {
+ result.AppendError("query requires one argument\n");
+ return;
+ }
+
+ 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);
+ }
+};
+
+// 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);
+
+ bool first_module = true;
+ for (const SymbolContext &sc : sc_list) {
+ if (!first_module)
+ 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";
+
+ first_module = false;
+ }
+ }
+ 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 bool GetSeparateDebugInfoList(StructuredData::Array &list,
+ Module *module, bool errors_only) {
+ if (module) {
+ if (SymbolFile *symbol_file = module->GetSymbolFile(/*can_create=*/true)) {
+ StructuredData::Dictionary d;
+ if (symbol_file->GetSeparateDebugInfo(d, errors_only)) {
+ list.AddItem(
+ std::make_shared<StructuredData::Dictionary>(std::move(d)));
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static void DumpDwoFilesTable(Stream &strm,
+ StructuredData::Array &dwo_listings) {
+ strm.PutCString("Dwo ID Err Dwo Path");
+ strm.EOL();
+ strm.PutCString(
+ "------------------ --- -----------------------------------------");
+ strm.EOL();
+ dwo_listings.ForEach([&strm](StructuredData::Object *dwo) {
+ StructuredData::Dictionary *dict = dwo->GetAsDictionary();
+ if (!dict)
+ return false;
+
+ uint64_t dwo_id;
+ if (dict->GetValueForKeyAsInteger("dwo_id", dwo_id))
+ strm.Printf("0x%16.16" PRIx64 " ", dwo_id);
+ else
+ strm.Printf("0x???????????????? ");
+
+ llvm::StringRef error;
+ if (dict->GetValueForKeyAsString("error", error))
+ strm << "E " << error;
+ else {
+ llvm::StringRef resolved_dwo_path;
+ if (dict->GetValueForKeyAsString("resolved_dwo_path",
+ resolved_dwo_path)) {
+ strm << " " << resolved_dwo_path;
+ if (resolved_dwo_path.ends_with(".dwp")) {
+ llvm::StringRef dwo_name;
+ if (dict->GetValueForKeyAsString("dwo_name", dwo_name))
+ strm << "(" << dwo_name << ")";
+ }
+ }
+ }
+ strm.EOL();
+ return true;
+ });
+}
+
+static void DumpOsoFilesTable(Stream &strm,
+ StructuredData::Array &oso_listings) {
+ strm.PutCString("Mod Time Err Oso Path");
+ strm.EOL();
+ strm.PutCString("------------------ --- ---------------------");
+ strm.EOL();
+ oso_listings.ForEach([&strm](StructuredData::Object *oso) {
+ StructuredData::Dictionary *dict = oso->GetAsDictionary();
+ if (!dict)
+ return false;
+
+ uint32_t oso_mod_time;
+ if (dict->GetValueForKeyAsInteger("oso_mod_time", oso_mod_time))
+ strm.Printf("0x%16.16" PRIx32 " ", oso_mod_time);
+
+ llvm::StringRef error;
+ if (dict->GetValueForKeyAsString("error", error))
+ strm << "E " << error;
+ else {
+ llvm::StringRef oso_path;
+ if (dict->GetValueForKeyAsString("oso_path", oso_path))
+ strm << " " << oso_path;
+ }
+ strm.EOL();
+ return true;
+ });
+}
+
+static void
+DumpAddress(ExecutionContextScope *exe_scope, const Address &so_addr,
+ bool verbose, bool all_ranges, Stream &strm,
+ std::optional<Stream::HighlightSettings> settings = std::nullopt) {
+ 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,
+ Address::DumpStyleInvalid, UINT32_MAX, false, settings);
+ 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, settings);
+ }
+ 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);
+ 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;
+ const bool use_color = interpreter.GetDebugger().GetUseColor();
+ 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();
+ Stream::HighlightSettings settings(
+ name, interpreter.GetDebugger().GetRegexMatchAnsiPrefix(),
+ interpreter.GetDebugger().GetRegexMatchAnsiSuffix());
+ for (uint32_t i = 0; i < num_matches; ++i) {
+ Symbol *symbol = symtab->SymbolAtIndex(match_indexes[i]);
+ if (symbol) {
+ if (symbol->ValueIsAddress()) {
+ DumpAddress(
+ interpreter.GetExecutionContext().GetBestExecutionContextScope(),
+ symbol->GetAddressRef(), verbose, all_ranges, strm,
+ use_color && name_is_regex
+ ? std::optional<Stream::HighlightSettings>{settings}
+ : std::nullopt);
+ strm.EOL();
+ } else {
+ strm.IndentMore();
+ strm.Indent(" Name: ");
+ strm.PutCStringColorHighlighted(
+ symbol->GetDisplayName().GetStringRef(),
+ use_color && name_is_regex
+ ? std::optional<Stream::HighlightSettings>{settings}
+ : std::nullopt);
+ strm.EOL();
+ strm.Indent(" Value: ");
+ strm.Printf("0x%16.16" PRIx64 "\n", symbol->GetRawValue());
+ if (symbol->GetByteSizeIsValid()) {
+ strm.Indent(" Size: ");
+ strm.Printf("0x%16.16" PRIx64 "\n", symbol->GetByteSize());
+ }
+ strm.IndentLess();
+ }
+ }
+ }
+ strm.IndentLess();
+ }
+ return num_matches;
+}
+
+static void DumpSymbolContextList(
+ ExecutionContextScope *exe_scope, Stream &strm,
+ const SymbolContextList &sc_list, bool verbose, bool all_ranges,
+ std::optional<Stream::HighlightSettings> settings = std::nullopt) {
+ strm.IndentMore();
+ bool first_module = true;
+ for (const SymbolContext &sc : sc_list) {
+ if (!first_module)
+ strm.EOL();
+
+ AddressRange range;
+
+ sc.GetAddressRange(eSymbolContextEverything, 0, true, range);
+
+ DumpAddress(exe_scope, range.GetBaseAddress(), verbose, all_ranges, strm,
+ settings);
+ first_module = false;
+ }
+ 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) {
+ if (module && name_cstr && name_cstr[0]) {
+ TypeQuery query(name_cstr);
+ TypeResults results;
+ module->FindTypes(query, results);
+
+ TypeList type_list;
+ SymbolContext sc;
+ if (module)
+ sc.module_sp = module->shared_from_this();
+ // Sort the type results and put the results that matched in \a module
+ // first if \a module was specified.
+ sc.SortTypeList(results.GetTypeMap(), 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();
+ }
+ return 0;
+}
+
+static size_t LookupTypeHere(Target *target, CommandInterpreter &interpreter,
+ Stream &strm, Module &module,
+ const char *name_cstr, bool name_is_regex) {
+ TypeQuery query(name_cstr);
+ TypeResults results;
+ module.FindTypes(query, results);
+ TypeList type_list;
+ SymbolContext sc;
+ sc.module_sp = module.shared_from_this();
+ sc.SortTypeList(results.GetTypeMap(), 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) {
+ AddSimpleArgumentList(eArgTypeFilename, eArgRepeatStar);
+ }
+
+ ~CommandObjectTargetModulesModuleAutoComplete() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::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) {
+ AddSimpleArgumentList(eArgTypeSourceFile, eArgRepeatPlus);
+ }
+
+ ~CommandObjectTargetModulesSourceFileAutoComplete() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::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:
+ void 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");
+ }
+ }
+};
+
+#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::ArrayRef(g_target_modules_dump_symtab_options);
+ }
+
+ SortOrder m_sort_order = eSortOrderNone;
+ OptionValueBoolean m_prefer_mangled = {false, false};
+ };
+
+protected:
+ void 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 (INTERRUPT_REQUESTED(GetDebugger(),
+ "Interrupted in dump all symtabs with {0} "
+ "of {1} dumped.", num_dumped, num_modules))
+ 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;
+ }
+ } 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 (INTERRUPT_REQUESTED(GetDebugger(),
+ "Interrupted in dump symtab list with {0} of {1} dumped.",
+ num_dumped, num_matches))
+ 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");
+ }
+ }
+
+ 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:
+ void 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;
+ }
+
+ result.GetOutputStream().Format("Dumping sections for {0} modules.\n",
+ num_modules);
+ for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
+ if (INTERRUPT_REQUESTED(GetDebugger(),
+ "Interrupted in dump all sections with {0} of {1} dumped",
+ image_idx, num_modules))
+ 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 (INTERRUPT_REQUESTED(GetDebugger(),
+ "Interrupted in dump section list with {0} of {1} dumped.",
+ i, num_matches))
+ 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");
+ }
+ }
+};
+
+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.
+ AddSimpleArgumentList(eArgTypeFilename);
+ }
+
+ ~CommandObjectTargetModulesDumpClangPCMInfo() override = default;
+
+protected:
+ void 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;
+ }
+
+ const char *pcm_path = command.GetArgumentAtIndex(0);
+ const FileSpec pcm_file{pcm_path};
+
+ if (pcm_file.GetFileNameExtension() != ".pcm") {
+ result.AppendError("file must have a .pcm extension");
+ return;
+ }
+
+ if (!FileSystem::Instance().Exists(pcm_file)) {
+ result.AppendError("pcm file does not exist");
+ return;
+ }
+
+ clang::CompilerInstance compiler;
+ compiler.createDiagnostics();
+
+ const char *clang_args[] = {"clang", pcm_path};
+ compiler.setInvocation(clang::createInvocation(clang_args));
+
+ // Pass empty deleter to not attempt to free memory that was allocated
+ // outside of the current scope, possibly statically.
+ std::shared_ptr<llvm::raw_ostream> Out(
+ &result.GetOutputStream().AsRawOstream(), [](llvm::raw_ostream *) {});
+ clang::DumpModuleInfoAction dump_module_info(Out);
+ // DumpModuleInfoAction requires ObjectFilePCHContainerReader.
+ compiler.getPCHContainerOperations()->registerReader(
+ std::make_unique<clang::ObjectFilePCHContainerReader>());
+
+ if (compiler.ExecuteAction(dump_module_info))
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+};
+
+#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:
+ void 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;
+ }
+
+ 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 (INTERRUPT_REQUESTED(GetDebugger(), "Interrupted dumping clang ast"))
+ break;
+ if (SymbolFile *sf = module_sp->GetSymbolFile())
+ sf->DumpClangAST(result.GetOutputStream());
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return;
+ }
+
+ // 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 (INTERRUPT_REQUESTED(GetDebugger(),
+ "Interrupted in dump clang ast list with {0} of {1} dumped.",
+ i, num_matches))
+ break;
+
+ Module *m = module_list.GetModulePointerAtIndex(i);
+ if (SymbolFile *sf = m->GetSymbolFile())
+ sf->DumpClangAST(result.GetOutputStream());
+ }
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+};
+
+#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:
+ void 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;
+ }
+ result.GetOutputStream().Format(
+ "Dumping debug symbols for {0} modules.\n", num_modules);
+ for (ModuleSP module_sp : target_modules.ModulesNoLocking()) {
+ if (INTERRUPT_REQUESTED(GetDebugger(), "Interrupted in dumping all "
+ "debug symbols with {0} of {1} modules dumped",
+ num_dumped, num_modules))
+ 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 (INTERRUPT_REQUESTED(GetDebugger(), "Interrupted dumping {0} "
+ "of {1} requested modules",
+ i, num_matches))
+ 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");
+ }
+ }
+};
+
+#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:
+ void 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;
+ } 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());
+ size_t num_modules = target_modules.GetSize();
+ if (num_modules > 0) {
+ uint32_t num_dumped = 0;
+ for (ModuleSP module_sp : target_modules.ModulesNoLocking()) {
+ if (INTERRUPT_REQUESTED(GetDebugger(),
+ "Interrupted in dump all line tables with "
+ "{0} of {1} dumped", num_dumped,
+ num_modules))
+ 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");
+ }
+ }
+
+ 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::ArrayRef(g_target_modules_dump_options);
+ }
+
+ bool m_verbose;
+ };
+
+ CommandOptions m_options;
+};
+
+#pragma mark CommandObjectTargetModulesDumpSeparateDebugInfoFiles
+#define LLDB_OPTIONS_target_modules_dump_separate_debug_info
+#include "CommandOptions.inc"
+
+// Image debug separate debug info dumping command
+
+class CommandObjectTargetModulesDumpSeparateDebugInfoFiles
+ : public CommandObjectTargetModulesModuleAutoComplete {
+public:
+ CommandObjectTargetModulesDumpSeparateDebugInfoFiles(
+ CommandInterpreter &interpreter)
+ : CommandObjectTargetModulesModuleAutoComplete(
+ interpreter, "target modules dump separate-debug-info",
+ "List the separate debug info symbol files for one or more target "
+ "modules.",
+ nullptr, eCommandRequiresTarget) {}
+
+ ~CommandObjectTargetModulesDumpSeparateDebugInfoFiles() 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 'j':
+ m_json.SetCurrentValue(true);
+ m_json.SetOptionWasSet();
+ break;
+ case 'e':
+ m_errors_only.SetCurrentValue(true);
+ m_errors_only.SetOptionWasSet();
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_json.Clear();
+ m_errors_only.Clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::ArrayRef(g_target_modules_dump_separate_debug_info_options);
+ }
+
+ OptionValueBoolean m_json = false;
+ OptionValueBoolean m_errors_only = false;
+ };
+
+protected:
+ void 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);
+
+ StructuredData::Array separate_debug_info_lists_by_module;
+ 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;
+ }
+ for (ModuleSP module_sp : target_modules.ModulesNoLocking()) {
+ if (INTERRUPT_REQUESTED(
+ GetDebugger(),
+ "Interrupted in dumping all "
+ "separate debug info with {0} of {1} modules dumped",
+ num_dumped, num_modules))
+ break;
+
+ if (GetSeparateDebugInfoList(separate_debug_info_lists_by_module,
+ module_sp.get(),
+ bool(m_options.m_errors_only)))
+ 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 (INTERRUPT_REQUESTED(GetDebugger(),
+ "Interrupted dumping {0} "
+ "of {1} requested modules",
+ i, num_matches))
+ break;
+ Module *module = module_list.GetModulePointerAtIndex(i);
+ if (GetSeparateDebugInfoList(separate_debug_info_lists_by_module,
+ module, bool(m_options.m_errors_only)))
+ num_dumped++;
+ }
+ } else
+ result.AppendWarningWithFormat(
+ "Unable to find an image that matches '%s'.\n", arg_cstr);
+ }
+ }
+
+ if (num_dumped > 0) {
+ Stream &strm = result.GetOutputStream();
+ // Display the debug info files in some format.
+ if (m_options.m_json) {
+ // JSON format
+ separate_debug_info_lists_by_module.Dump(strm,
+ /*pretty_print=*/true);
+ } else {
+ // Human-readable table format
+ separate_debug_info_lists_by_module.ForEach(
+ [&result, &strm](StructuredData::Object *obj) {
+ if (!obj) {
+ return false;
+ }
+
+ // Each item in `separate_debug_info_lists_by_module` should be a
+ // valid structured data dictionary.
+ StructuredData::Dictionary *separate_debug_info_list =
+ obj->GetAsDictionary();
+ if (!separate_debug_info_list) {
+ return false;
+ }
+
+ llvm::StringRef type;
+ llvm::StringRef symfile;
+ StructuredData::Array *files;
+ if (!(separate_debug_info_list->GetValueForKeyAsString("type",
+ type) &&
+ separate_debug_info_list->GetValueForKeyAsString("symfile",
+ symfile) &&
+ separate_debug_info_list->GetValueForKeyAsArray(
+ "separate-debug-info-files", files))) {
+ assert(false);
+ }
+
+ strm << "Symbol file: " << symfile;
+ strm.EOL();
+ strm << "Type: \"" << type << "\"";
+ strm.EOL();
+ if (type == "dwo") {
+ DumpDwoFilesTable(strm, *files);
+ } else if (type == "oso") {
+ DumpOsoFilesTable(strm, *files);
+ } else {
+ result.AppendWarningWithFormat(
+ "Found unsupported debug info type '%s'.\n",
+ type.str().c_str());
+ }
+ return true;
+ });
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError("no matching executable images found");
+ }
+ }
+
+ 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|separate-"
+ "debug-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)));
+ LoadSubCommand("separate-debug-info",
+ CommandObjectSP(
+ new CommandObjectTargetModulesDumpSeparateDebugInfoFiles(
+ 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();
+ AddSimpleArgumentList(eArgTypePath, eArgRepeatStar);
+ }
+
+ ~CommandObjectTargetModulesAdd() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ OptionGroupOptions m_option_group;
+ OptionGroupUUID m_uuid_option_group;
+ OptionGroupFile m_symbol_file;
+
+ void 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 (PluginManager::DownloadObjectAndSymbolFile(module_spec, error)) {
+ ModuleSP module_sp(
+ target->GetOrCreateModule(module_spec, true /* notify */));
+ if (module_sp) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return;
+ } 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;
+ }
+ } 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;
+ }
+ } else {
+ result.AppendError(
+ "one or more executable image paths must be specified");
+ return;
+ }
+ } 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;
+ } 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();
+ }
+ }
+};
+
+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:
+ void 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;
+ }
+ } 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;
+ }
+
+ 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;
+ }
+ if (set_pc && !file_entry.IsValid()) {
+ result.AppendError("No entry address in object file");
+ return;
+ }
+ std::vector<ObjectFile::LoadableData> loadables(
+ objfile->GetLoadableData(*target));
+ if (loadables.size() == 0) {
+ result.AppendError("No loadable sections");
+ return;
+ }
+ Status error = process->WriteObjectFile(std::move(loadables));
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ return;
+ }
+ 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");
+ }
+ }
+
+ 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::ArrayRef(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.") {
+ AddSimpleArgumentList(eArgTypeModule, eArgRepeatStar);
+ }
+
+ ~CommandObjectTargetModulesList() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ void 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;
+ } 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;
+ }
+
+ 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;
+ }
+ }
+ }
+
+ 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;
+ }
+ }
+ }
+
+ 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;
+ char in_shared_cache = 'Y';
+
+ ModuleSP module_sp(module->shared_from_this());
+ if (!ModuleList::ModuleIsInCache(module))
+ in_shared_cache = 'N';
+ 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("{%c %*" PRIu64 "}", in_shared_cache, width, (uint64_t)ref_count);
+ else
+ strm.Printf("{%c %" PRIu64 "}", in_shared_cache, (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::ArrayRef(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:
+ void 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;
+ }
+
+ ThreadList threads(process->GetThreadList());
+ if (threads.GetSize() == 0) {
+ result.AppendError("The process must be paused to use this command.");
+ return;
+ }
+
+ ThreadSP thread(threads.GetThreadAtIndex(0));
+ if (!thread) {
+ result.AppendError("The process must be paused to use this command.");
+ return;
+ }
+
+ 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;
+ }
+
+ if (sc_list.GetSize() == 0) {
+ result.AppendErrorWithFormat("no unwind data found that matches '%s'.",
+ m_options.m_str.c_str());
+ return;
+ }
+
+ for (const SymbolContext &sc : sc_list) {
+ 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");
+ }
+ }
+
+ 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::ArrayRef(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) {
+ AddSimpleArgumentList(eArgTypeFilename, eArgRepeatStar);
+ }
+
+ ~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:
+ void 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;
+ }
+ }
+
+ // 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;
+ }
+
+ 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);
+ }
+
+ 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', lldb::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();
+ AddSimpleArgumentList(eArgTypeFilename);
+ }
+
+ ~CommandObjectTargetSymbolsAdd() override = default;
+
+ 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().SetFilename(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().SetFilename(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 (PluginManager::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();
+ 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();
+ module_spec.GetFileSpec() = frame_module_sp->GetPlatformFileSpec();
+ module_spec.GetArchitecture() = frame_module_sp->GetArchitecture();
+
+ 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;
+ }
+
+ void 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();
+ }
+ }
+
+ 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::ArrayRef(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 = LLDB_INVALID_LINE_NUMBER;
+ 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 = LLDB_INVALID_LINE_NUMBER;
+ 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 = LLDB_INVALID_THREAD_ID;
+ uint32_t m_thread_index = UINT32_MAX;
+ std::string m_thread_name;
+ std::string m_queue_name;
+ bool m_sym_ctx_specified = false;
+ bool m_no_inlines = false;
+ 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 = false;
+ };
+
+ 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);
+ }
+
+ void 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;
+ }
+ } else {
+ m_stop_hook_sp = new_hook_sp;
+ m_interpreter.GetLLDBCommandsFromIOHandler("> ", // Prompt
+ *this); // IOHandlerDelegate
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+
+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>]") {
+ AddSimpleArgumentList(eArgTypeStopHookID, eArgRepeatStar);
+ }
+
+ ~CommandObjectTargetStopHookDelete() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (request.GetCursorIndex())
+ return;
+ CommandObject::HandleArgumentCompletion(request, opt_element_vector);
+ }
+
+protected:
+ void 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;
+ } 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;
+ }
+ if (!target.RemoveStopHookByID(user_id)) {
+ result.AppendErrorWithFormat("unknown stop hook id: \"%s\".\n",
+ command.GetArgumentAtIndex(i));
+ return;
+ }
+ }
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+};
+
+#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) {
+ AddSimpleArgumentList(eArgTypeStopHookID, eArgRepeatStar);
+ }
+
+ ~CommandObjectTargetStopHookEnableDisable() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (request.GetCursorIndex())
+ return;
+ CommandObject::HandleArgumentCompletion(request, opt_element_vector);
+ }
+
+protected:
+ void 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;
+ }
+ success = target.SetStopHookActiveStateByID(user_id, m_enable);
+ if (!success) {
+ result.AppendErrorWithFormat("unknown stop hook id: \"%s\".\n",
+ command.GetArgumentAtIndex(i));
+ return;
+ }
+ }
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+
+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:
+ void 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);
+ }
+};
+
+#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. Intended to "
+ "be used for debugging LLDB itself.",
+ nullptr, eCommandRequiresTarget) {}
+
+ ~CommandObjectTargetDumpTypesystem() override = default;
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ // Go over every scratch TypeSystem and dump to the command output.
+ for (lldb::TypeSystemSP ts : GetSelectedTarget().GetScratchTypeSystems())
+ if (ts)
+ ts->Dump(result.GetOutputStream().AsRawOstream());
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+};
+
+#pragma mark CommandObjectTargetDumpSectionLoadList
+
+/// Dumps the SectionLoadList of the selected Target.
+class CommandObjectTargetDumpSectionLoadList : public CommandObjectParsed {
+public:
+ CommandObjectTargetDumpSectionLoadList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "target dump section-load-list",
+ "Dump the state of the target's internal section load list. "
+ "Intended to be used for debugging LLDB itself.",
+ nullptr, eCommandRequiresTarget) {}
+
+ ~CommandObjectTargetDumpSectionLoadList() override = default;
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ Target &target = GetSelectedTarget();
+ target.GetSectionLoadList().Dump(result.GetOutputStream(), &target);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+};
+
+#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|section-load-list]") {
+ LoadSubCommand(
+ "typesystem",
+ CommandObjectSP(new CommandObjectTargetDumpTypesystem(interpreter)));
+ LoadSubCommand("section-load-list",
+ CommandObjectSP(new CommandObjectTargetDumpSectionLoadList(
+ 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..4398cf3c3b89
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectThread.cpp
@@ -0,0 +1,2611 @@
+//===-- 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 <optional>
+#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/CommandOptionArgumentTable.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':
+ if (option_arg.getAsInteger(0, m_count)) {
+ m_count = UINT32_MAX;
+ error.SetErrorStringWithFormat(
+ "invalid integer value for option '%c': %s", short_option,
+ option_arg.data());
+ }
+ // A count of 0 means all frames.
+ if (m_count == 0)
+ m_count = UINT32_MAX;
+ break;
+ case 's':
+ if (option_arg.getAsInteger(0, m_start))
+ error.SetErrorStringWithFormat(
+ "invalid integer value for option '%c': %s", short_option,
+ option_arg.data());
+ break;
+ case 'e': {
+ bool success;
+ m_extended_backtrace =
+ OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "invalid boolean value for option '%c': %s", short_option,
+ option_arg.data());
+ } 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::ArrayRef(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 backtraces of 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; }
+
+ std::optional<std::string> GetRepeatCommand(Args &current_args,
+ uint32_t index) 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 == "-c" || count_opt.starts_with(arg_string)) {
+ idx++;
+ if (idx == num_entries)
+ return std::nullopt;
+ count_idx = idx;
+ if (copy_args[idx].ref().getAsInteger(0, count_val))
+ return std::nullopt;
+ } else if (arg_string == "-s" || start_opt.starts_with(arg_string)) {
+ idx++;
+ if (idx == num_entries)
+ return std::nullopt;
+ start_idx = idx;
+ if (copy_args[idx].ref().getAsInteger(0, start_val))
+ return std::nullopt;
+ }
+ }
+ if (count_idx == 0)
+ return std::nullopt;
+
+ 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 std::nullopt;
+ 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;
+ strm.PutChar('\n');
+ 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) {
+ if (!INTERRUPT_REQUESTED(GetDebugger(),
+ "Interrupt skipped extended backtrace")) {
+ DoExtendedBacktrace(thread, result);
+ }
+ }
+
+ return true;
+ }
+
+ CommandOptions m_options;
+};
+
+enum StepScope { eStepScopeSource, eStepScopeInstruction };
+
+#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::ArrayRef(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': %s", short_option,
+ option_arg.data());
+ 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': %s", short_option,
+ option_arg.data());
+ 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 integer value for option '%c': %s", short_option,
+ option_arg.data());
+ 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") {
+ AddSimpleArgumentList(eArgTypeThreadIndex, eArgRepeatOptional);
+
+ 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;
+ CommandObject::HandleArgumentCompletion(request, opt_element_vector);
+ }
+
+ Options *GetOptions() override { return &m_all_options; }
+
+protected:
+ void 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;
+ }
+ } 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;
+ }
+ 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;
+ }
+ }
+
+ if (m_step_type == eStepTypeScripted) {
+ if (m_class_options.GetName().empty()) {
+ result.AppendErrorWithFormat("empty class name for scripted step.");
+ return;
+ } 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;
+ }
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+ } 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;
+ }
+
+ 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;
+ }
+ 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(DoNoSelectMostRelevantFrame),
+ 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;
+ }
+
+ // 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;
+ }
+
+ // 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);
+ }
+ }
+
+ 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) {
+ AddSimpleArgumentList(eArgTypeThreadIndex, eArgRepeatPlus);
+ }
+
+ ~CommandObjectThreadContinue() override = default;
+
+ void 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;
+ }
+
+ 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;
+ }
+ 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;
+ }
+ }
+
+ if (resume_threads.empty()) {
+ result.AppendError("no valid thread indexes were specified");
+ return;
+ } 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;
+ }
+ // 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));
+ }
+ }
+};
+
+// CommandObjectThreadUntil
+
+#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::ArrayRef(g_thread_until_options);
+ }
+
+ uint32_t m_step_thread_idx = LLDB_INVALID_THREAD_ID;
+ bool m_stop_others = false;
+ 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) {
+ AddSimpleArgumentList(eArgTypeLineNum);
+ }
+
+ ~CommandObjectThreadUntil() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ void 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;
+ } 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ LineEntry function_start;
+ uint32_t index_ptr = 0, end_ptr = UINT32_MAX;
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+ } else {
+ result.AppendErrorWithFormat("Frame index %u of thread id %" PRIu64
+ " has no debug information.\n",
+ m_options.m_frame_idx, thread->GetID());
+ return;
+ }
+
+ if (!process->GetThreadList().SetSelectedThreadByID(thread->GetID())) {
+ result.AppendErrorWithFormat(
+ "Failed to set the selected thread to thread id %" PRIu64 ".\n",
+ thread->GetID());
+ return;
+ }
+
+ 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());
+ }
+ }
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectThreadSelect
+
+#define LLDB_OPTIONS_thread_select
+#include "CommandOptions.inc"
+
+class CommandObjectThreadSelect : public CommandObjectParsed {
+public:
+ class OptionGroupThreadSelect : public OptionGroup {
+ public:
+ OptionGroupThreadSelect() { OptionParsingStarting(nullptr); }
+
+ ~OptionGroupThreadSelect() override = default;
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_thread_id = LLDB_INVALID_THREAD_ID;
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ const int short_option = g_thread_select_options[option_idx].short_option;
+ switch (short_option) {
+ case 't': {
+ if (option_arg.getAsInteger(0, m_thread_id)) {
+ m_thread_id = LLDB_INVALID_THREAD_ID;
+ return Status("Invalid thread ID: '%s'.", option_arg.str().c_str());
+ }
+ break;
+ }
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return {};
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::ArrayRef(g_thread_select_options);
+ }
+
+ lldb::tid_t m_thread_id;
+ };
+
+ CommandObjectThreadSelect(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "thread select",
+ "Change the currently selected thread.",
+ "thread select <thread-index> (or -t <thread-id>)",
+ 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;
+ thread_idx_arg.arg_opt_set_association = LLDB_OPT_SET_1;
+
+ // 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);
+
+ m_option_group.Append(&m_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_2);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectThreadSelect() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (request.GetCursorIndex())
+ return;
+
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::eThreadIndexCompletion, request,
+ nullptr);
+ }
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (process == nullptr) {
+ result.AppendError("no process");
+ return;
+ } else if (m_options.m_thread_id == LLDB_INVALID_THREAD_ID &&
+ command.GetArgumentCount() != 1) {
+ result.AppendErrorWithFormat(
+ "'%s' takes exactly one thread index argument, or a thread ID "
+ "option:\nUsage: %s\n",
+ m_cmd_name.c_str(), m_cmd_syntax.c_str());
+ return;
+ } else if (m_options.m_thread_id != LLDB_INVALID_THREAD_ID &&
+ command.GetArgumentCount() != 0) {
+ result.AppendErrorWithFormat("'%s' cannot take both a thread ID option "
+ "and a thread index argument:\nUsage: %s\n",
+ m_cmd_name.c_str(), m_cmd_syntax.c_str());
+ return;
+ }
+
+ Thread *new_thread = nullptr;
+ if (command.GetArgumentCount() == 1) {
+ uint32_t index_id;
+ if (!llvm::to_integer(command.GetArgumentAtIndex(0), index_id)) {
+ result.AppendErrorWithFormat("Invalid thread index '%s'",
+ command.GetArgumentAtIndex(0));
+ return;
+ }
+ new_thread = process->GetThreadList().FindThreadByIndexID(index_id).get();
+ if (new_thread == nullptr) {
+ result.AppendErrorWithFormat("Invalid thread index #%s.\n",
+ command.GetArgumentAtIndex(0));
+ return;
+ }
+ } else {
+ new_thread =
+ process->GetThreadList().FindThreadByID(m_options.m_thread_id).get();
+ if (new_thread == nullptr) {
+ result.AppendErrorWithFormat("Invalid thread ID %" PRIu64 ".\n",
+ m_options.m_thread_id);
+ return;
+ }
+ }
+
+ process->GetThreadList().SetSelectedThreadByID(new_thread->GetID(), true);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+
+ OptionGroupThreadSelect m_options;
+ OptionGroupOptions m_option_group;
+};
+
+// 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:
+ void 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);
+ }
+};
+
+// 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::ArrayRef(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 {
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::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 {
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::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) {
+ if (llvm::Error error = exception_object_sp->Dump(strm)) {
+ result.AppendError(toString(std::move(error)));
+ return false;
+ }
+ }
+
+ 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 {
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::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) {
+ if (llvm::Error error = exception_object_sp->Dump(strm)) {
+ result.AppendError(toString(std::move(error)));
+ return false;
+ }
+ } 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::ArrayRef(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) {
+ AddSimpleArgumentList(eArgTypeExpression, eArgRepeatOptional);
+ }
+
+ ~CommandObjectThreadReturn() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ void 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.starts_with("-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(DoNoSelectMostRelevantFrame));
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat(
+ "Could not select 0th frame after unwinding expression.");
+ }
+ }
+ return;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+ }
+
+ 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;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+
+ 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::ArrayRef(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:
+ void 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;
+ }
+
+ if (!reg_ctx->SetPC(callAddr)) {
+ result.AppendErrorWithFormat("Error changing PC value for thread %d.",
+ thread->GetIndexID());
+ return;
+ }
+ } 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.GetFile();
+ 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;
+ }
+
+ std::string warnings;
+ Status err = thread->JumpToLine(file, line, m_options.m_force, &warnings);
+
+ if (err.Fail()) {
+ result.SetError(err);
+ return;
+ }
+
+ if (!warnings.empty())
+ result.AppendWarning(warnings.c_str());
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+
+ 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::ArrayRef(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; }
+
+ void 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;
+ } 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;
+ }
+ // 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) {
+ AddSimpleArgumentList(eArgTypeUnsignedInteger);
+ }
+
+ ~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);
+ }
+
+ void 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;
+ }
+
+ 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;
+ }
+
+ if (thread_plan_idx == 0) {
+ result.AppendErrorWithFormat(
+ "You wouldn't really want me to discard the base thread plan.");
+ return;
+ }
+
+ if (thread->DiscardUserThreadPlansUpToIndex(thread_plan_idx)) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendErrorWithFormat(
+ "Could not find User thread plan with index %s.",
+ args.GetArgumentAtIndex(0));
+ }
+ }
+};
+
+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) {
+ AddSimpleArgumentList(eArgTypeThreadID, eArgRepeatStar);
+ }
+
+ ~CommandObjectThreadPlanPrune() override = default;
+
+ void DoExecute(Args &args, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+
+ if (args.GetArgumentCount() == 0) {
+ process->PruneThreadPlans();
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return;
+ }
+
+ 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;
+ }
+ if (!process->PruneThreadPlansForTID(tid)) {
+ result.AppendErrorWithFormat("Could not find unreported tid: \"%s\"\n",
+ args.GetArgumentAtIndex(i));
+ return;
+ }
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+};
+
+// 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();
+ }
+};
+
+static ThreadSP GetSingleThreadFromArgs(ExecutionContext &exe_ctx, Args &args,
+ CommandReturnObject &result) {
+ if (args.GetArgumentCount() == 0)
+ return 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 =
+ exe_ctx.GetProcessRef().GetThreadList().FindThreadByIndexID(thread_idx);
+ if (!thread_sp)
+ result.AppendErrorWithFormat("no thread with index: \"%s\"\n", arg);
+ return thread_sp;
+}
+
+// CommandObjectTraceDumpFunctionCalls
+#define LLDB_OPTIONS_thread_trace_dump_function_calls
+#include "CommandOptions.inc"
+
+class CommandObjectTraceDumpFunctionCalls : 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 'j': {
+ m_dumper_options.json = true;
+ break;
+ }
+ case 'J': {
+ m_dumper_options.json = true;
+ m_dumper_options.pretty_print_json = true;
+ break;
+ }
+ case 'F': {
+ m_output_file.emplace(option_arg);
+ break;
+ }
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_dumper_options = {};
+ m_output_file = std::nullopt;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::ArrayRef(g_thread_trace_dump_function_calls_options);
+ }
+
+ static const size_t kDefaultCount = 20;
+
+ // Instance variables to hold the values for command options.
+ TraceDumperOptions m_dumper_options;
+ std::optional<FileSpec> m_output_file;
+ };
+
+ CommandObjectTraceDumpFunctionCalls(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "thread trace dump function-calls",
+ "Dump the traced function-calls for one thread. If no "
+ "thread is specified, the current thread is used.",
+ nullptr,
+ eCommandRequiresProcess | eCommandRequiresThread |
+ eCommandTryTargetAPILock | eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused | eCommandProcessMustBeTraced) {
+ AddSimpleArgumentList(eArgTypeThreadIndex, eArgRepeatOptional);
+ }
+
+ ~CommandObjectTraceDumpFunctionCalls() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ void DoExecute(Args &args, CommandReturnObject &result) override {
+ ThreadSP thread_sp = GetSingleThreadFromArgs(m_exe_ctx, args, result);
+ if (!thread_sp) {
+ result.AppendError("invalid thread\n");
+ return;
+ }
+
+ llvm::Expected<TraceCursorSP> cursor_or_error =
+ m_exe_ctx.GetTargetSP()->GetTrace()->CreateNewCursor(*thread_sp);
+
+ if (!cursor_or_error) {
+ result.AppendError(llvm::toString(cursor_or_error.takeError()));
+ return;
+ }
+ TraceCursorSP &cursor_sp = *cursor_or_error;
+
+ std::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 |
+ File::eOpenOptionTruncate);
+ }
+
+ m_options.m_dumper_options.forwards = true;
+
+ TraceDumper dumper(std::move(cursor_sp),
+ out_file ? *out_file : result.GetOutputStream(),
+ m_options.m_dumper_options);
+
+ dumper.DumpFunctionCalls();
+ }
+
+ CommandOptions m_options;
+};
+
+// 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_timestamps = 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 'E': {
+ m_dumper_options.only_events = true;
+ m_dumper_options.show_events = 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 = std::nullopt;
+ m_dumper_options = {};
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::ArrayRef(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;
+ std::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) {
+ AddSimpleArgumentList(eArgTypeThreadIndex, eArgRepeatOptional);
+ }
+
+ ~CommandObjectTraceDumpInstructions() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ std::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:
+ void DoExecute(Args &args, CommandReturnObject &result) override {
+ ThreadSP thread_sp = GetSingleThreadFromArgs(m_exe_ctx, args, result);
+ if (!thread_sp) {
+ result.AppendError("invalid thread\n");
+ return;
+ }
+
+ 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<TraceCursorSP> cursor_or_error =
+ m_exe_ctx.GetTargetSP()->GetTrace()->CreateNewCursor(*thread_sp);
+
+ if (!cursor_or_error) {
+ result.AppendError(llvm::toString(cursor_or_error.takeError()));
+ return;
+ }
+ TraceCursorSP &cursor_sp = *cursor_or_error;
+
+ if (m_options.m_dumper_options.id &&
+ !cursor_sp->HasId(*m_options.m_dumper_options.id)) {
+ result.AppendError("invalid instruction id\n");
+ return;
+ }
+
+ std::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 |
+ File::eOpenOptionTruncate);
+ }
+
+ 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_sp->Seek(1, lldb::eTraceCursorSeekTypeEnd);
+ }
+
+ TraceDumper dumper(std::move(cursor_sp),
+ out_file ? *out_file : result.GetOutputStream(),
+ m_options.m_dumper_options);
+
+ m_last_id = dumper.DumpInstructions(m_options.m_count);
+ }
+
+ CommandOptions m_options;
+ // Last traversed id used to continue a repeat command. std::nullopt means
+ // that all the trace has been consumed.
+ std::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::ArrayRef(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(
+ "function-calls",
+ CommandObjectSP(new CommandObjectTraceDumpFunctionCalls(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..cdc5946547f4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectThreadUtil.cpp
@@ -0,0 +1,207 @@
+//===-- 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.
+ AddSimpleArgumentList(eArgTypeThreadIndex, eArgRepeatStar);
+}
+
+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.
+ AddSimpleArgumentList(eArgTypeThreadIndex, eArgRepeatStar);
+}
+
+void 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;
+ } 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;
+ }
+
+ ThreadSP thread =
+ process->GetThreadList().FindThreadByIndexID(thread_idx);
+
+ if (!thread) {
+ result.AppendErrorWithFormat("no thread with index: \"%s\"\n",
+ command.GetArgumentAtIndex(i));
+ return;
+ }
+
+ 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;
+ }
+ }
+
+ // 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;
+ }
+ }
+ } 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;
+
+ ++idx;
+ }
+ }
+}
+
+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;
+}
+
+void 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;
+ }
+
+ ThreadSP thread = process.GetThreadList().FindThreadByIndexID(thread_idx);
+
+ if (!thread) {
+ result.AppendErrorWithFormat("no thread with index: \"%s\"\n",
+ command.GetArgumentAtIndex(i));
+ return;
+ }
+
+ tids.push_back(thread->GetID());
+ }
+ }
+
+ 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..3fc28efe8cf7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectThreadUtil.h
@@ -0,0 +1,104 @@
+//===-- 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"
+#include <stack>
+
+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;
+
+ void 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);
+
+ void 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..5bcbc236301c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectTrace.cpp
@@ -0,0 +1,424 @@
+//===-- 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/CommandOptionArgumentTable.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::ArrayRef(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) {
+ AddSimpleArgumentList(eArgTypeDirectoryName);
+ }
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::eDiskFileCompletion, request, nullptr);
+ }
+
+ ~CommandObjectTraceSave() override = default;
+
+protected:
+ void 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;
+ }
+
+ 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()));
+ }
+ }
+
+ 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 ArrayRef(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>") {
+ AddSimpleArgumentList(eArgTypeFilename);
+ }
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::eDiskFileCompletion, request, nullptr);
+ }
+
+ ~CommandObjectTraceLoad() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ void 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;
+ }
+
+ 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;
+ }
+
+ if (m_options.m_verbose) {
+ result.AppendMessageWithFormatv("loading trace with plugin {0}\n",
+ trace_or_err.get()->GetPluginName());
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+
+ 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::ArrayRef(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:
+ void 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());
+ }
+ }
+
+ 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::ArrayRef(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") {
+ AddSimpleArgumentList(eArgTypeNone);
+ }
+
+ ~CommandObjectTraceSchema() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ void 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;
+ }
+
+ 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());
+ }
+ }
+
+ 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..46537dd1b98a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectType.cpp
@@ -0,0 +1,2915 @@
+//===-- 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/DataFormatters/FormatClasses.h"
+#include "lldb/Host/Config.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandOptionArgumentTable.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>
+
+using namespace lldb;
+using namespace lldb_private;
+
+class ScriptAddOptions {
+public:
+ TypeSummaryImpl::Flags m_flags;
+ StringList m_target_types;
+ FormatterMatchType m_match_type;
+ ConstString m_name;
+ std::string m_category;
+
+ ScriptAddOptions(const TypeSummaryImpl::Flags &flags,
+ FormatterMatchType match_type, ConstString name,
+ std::string catg)
+ : m_flags(flags), m_match_type(match_type), 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;
+ FormatterMatchType m_match_type;
+ StringList m_target_types;
+ std::string m_category;
+
+ SynthAddOptions(bool sptr, bool sref, bool casc,
+ FormatterMatchType match_type, std::string catg)
+ : m_skip_pointers(sptr), m_skip_references(sref), m_cascade(casc),
+ m_match_type(match_type), 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;
+}
+
+const char *FormatCategoryToString(FormatCategoryItem item, bool long_name) {
+ switch (item) {
+ case eFormatCategoryItemSummary:
+ return "summary";
+ case eFormatCategoryItemFilter:
+ return "filter";
+ case eFormatCategoryItemSynth:
+ if (long_name)
+ return "synthetic child provider";
+ return "synthetic";
+ case eFormatCategoryItemFormat:
+ return "format";
+ }
+ llvm_unreachable("Fully covered switch above!");
+}
+
+#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::ArrayRef(g_type_summary_add_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ TypeSummaryImpl::Flags m_flags;
+ FormatterMatchType m_match_type = eFormatterMatchExact;
+ std::string m_format_string;
+ ConstString m_name;
+ std::string m_python_script;
+ std::string m_python_function;
+ bool m_is_add_script = false;
+ 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:
+ 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) {
+ AddSummary(ConstString(type_name), script_format,
+ options->m_match_type, options->m_category,
+ &error);
+ if (error.Fail()) {
+ error_sp->Printf("error: %s", error.AsCString());
+ error_sp->Flush();
+ }
+ }
+
+ if (options->m_name) {
+ CommandObjectTypeSummaryAdd::AddNamedSummary(
+ options->m_name, script_format, &error);
+ if (error.Fail()) {
+ CommandObjectTypeSummaryAdd::AddNamedSummary(
+ options->m_name, script_format, &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);
+ }
+
+ bool AddSummary(ConstString type_name, lldb::TypeSummaryImplSP entry,
+ FormatterMatchType match_type, std::string category,
+ Status *error = nullptr);
+
+ bool AddNamedSummary(ConstString summary_name, lldb::TypeSummaryImplSP entry,
+ Status *error = nullptr);
+
+protected:
+ void 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':
+ if (m_match_type == eFormatterMatchCallback)
+ error.SetErrorString(
+ "can't use --regex and --recognizer-function at the same time");
+ else
+ m_match_type = eFormatterMatchRegex;
+ break;
+ case '\x01':
+ if (m_match_type == eFormatterMatchRegex)
+ error.SetErrorString(
+ "can't use --regex and --recognizer-function at the same time");
+ else
+ m_match_type = eFormatterMatchCallback;
+ 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_match_type = eFormatterMatchExact;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::ArrayRef(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;
+ FormatterMatchType m_match_type;
+ };
+
+ CommandOptions m_options;
+
+ Options *GetOptions() override { return &m_options; }
+
+ bool Execute_HandwritePython(Args &command, CommandReturnObject &result);
+
+ bool Execute_PythonClass(Args &command, CommandReturnObject &result);
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ WarnOnPotentialUnquotedUnsignedType(command, result);
+
+ if (m_options.handwrite_python)
+ Execute_HandwritePython(command, result);
+ else if (m_options.is_class_based)
+ 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");
+ }
+ }
+
+ 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 (AddSynth(ConstString(type_name), synth_provider,
+ options->m_match_type, 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:
+ CommandObjectTypeSynthAdd(CommandInterpreter &interpreter);
+
+ ~CommandObjectTypeSynthAdd() override = default;
+
+ bool AddSynth(ConstString type_name, lldb::SyntheticChildrenSP entry,
+ FormatterMatchType match_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::ArrayRef(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) {
+ AddSimpleArgumentList(eArgTypeName, eArgRepeatPlus);
+
+ 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:
+ void 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;
+ }
+
+ 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;
+ }
+
+ 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;
+
+ WarnOnPotentialUnquotedUnsignedType(command, result);
+
+ for (auto &arg_entry : command.entries()) {
+ if (arg_entry.ref().empty()) {
+ result.AppendError("empty typenames not allowed");
+ return;
+ }
+
+ FormatterMatchType match_type = eFormatterMatchExact;
+ if (m_command_options.m_regex) {
+ match_type = eFormatterMatchRegex;
+ RegularExpression typeRX(arg_entry.ref());
+ if (!typeRX.IsValid()) {
+ result.AppendError(
+ "regex format error (maybe this is not really a regex?)");
+ return;
+ }
+ }
+ category_sp->AddTypeFormat(arg_entry.ref(), match_type, entry);
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+};
+
+#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::ArrayRef(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;
+ FormatCategoryItem m_formatter_kind;
+
+ Options *GetOptions() override { return &m_options; }
+
+ static constexpr const char *g_short_help_template =
+ "Delete an existing %s for a type.";
+
+ static constexpr const char *g_long_help_template =
+ "Delete an existing %s for a type. Unless you specify a "
+ "specific category or all categories, only the "
+ "'default' category is searched. The names must be exactly as "
+ "shown in the 'type %s list' output";
+
+public:
+ CommandObjectTypeFormatterDelete(CommandInterpreter &interpreter,
+ FormatCategoryItem formatter_kind)
+ : CommandObjectParsed(interpreter,
+ FormatCategoryToString(formatter_kind, false)),
+ m_formatter_kind(formatter_kind) {
+ AddSimpleArgumentList(eArgTypeName);
+
+ const char *kind = FormatCategoryToString(formatter_kind, true);
+ const char *short_kind = FormatCategoryToString(formatter_kind, false);
+
+ StreamString s;
+ s.Printf(g_short_help_template, kind);
+ SetHelp(s.GetData());
+ s.Clear();
+ s.Printf(g_long_help_template, kind, short_kind);
+ SetHelpLong(s.GetData());
+ s.Clear();
+ s.Printf("type %s delete", short_kind);
+ SetCommandName(s.GetData());
+ }
+
+ ~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) {
+ category_sp->AutoComplete(request, m_formatter_kind);
+ return true;
+ });
+ }
+
+protected:
+ virtual bool FormatterSpecificDeletion(ConstString typeCS) { return false; }
+
+ void 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;
+ }
+
+ const char *typeA = command.GetArgumentAtIndex(0);
+ ConstString typeCS(typeA);
+
+ if (!typeCS) {
+ result.AppendError("empty typenames not allowed");
+ return;
+ }
+
+ if (m_options.m_delete_all) {
+ DataVisualization::Categories::ForEach(
+ [this, typeCS](const lldb::TypeCategoryImplSP &category_sp) -> bool {
+ category_sp->Delete(typeCS, m_formatter_kind);
+ return true;
+ });
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return;
+ }
+
+ 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);
+ 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);
+ extra_deletion = FormatterSpecificDeletion(typeCS);
+ }
+
+ if (delete_category || extra_deletion) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendErrorWithFormat("no custom formatter for %s.\n", typeA);
+ }
+ }
+};
+
+#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::ArrayRef(g_type_formatter_clear_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ bool m_delete_all;
+ };
+
+ CommandOptions m_options;
+ FormatCategoryItem m_formatter_kind;
+
+ Options *GetOptions() override { return &m_options; }
+
+public:
+ CommandObjectTypeFormatterClear(CommandInterpreter &interpreter,
+ FormatCategoryItem formatter_kind,
+ const char *name, const char *help)
+ : CommandObjectParsed(interpreter, name, help, nullptr),
+ m_formatter_kind(formatter_kind) {
+ AddSimpleArgumentList(eArgTypeName, eArgRepeatOptional);
+ }
+
+ ~CommandObjectTypeFormatterClear() override = default;
+
+protected:
+ virtual void FormatterSpecificDeletion() {}
+
+ void 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);
+ 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);
+ }
+
+ FormatterSpecificDeletion();
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+};
+
+// CommandObjectTypeFormatDelete
+
+class CommandObjectTypeFormatDelete : public CommandObjectTypeFormatterDelete {
+public:
+ CommandObjectTypeFormatDelete(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterDelete(
+ interpreter, eFormatCategoryItemFormat) {}
+
+ ~CommandObjectTypeFormatDelete() override = default;
+};
+
+// CommandObjectTypeFormatClear
+
+class CommandObjectTypeFormatClear : public CommandObjectTypeFormatterClear {
+public:
+ CommandObjectTypeFormatClear(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterClear(interpreter, eFormatCategoryItemFormat,
+ "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::ArrayRef(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() {
+ AddSimpleArgumentList(eArgTypeName, eArgRepeatOptional);
+ }
+
+ ~CommandObjectTypeFormatterList() override = default;
+
+protected:
+ virtual bool FormatterSpecificList(CommandReturnObject &result) {
+ return false;
+ }
+
+ static bool ShouldListItem(llvm::StringRef s, RegularExpression *regex) {
+ // If we have a regex, it can match two kinds of results:
+ // - An item created with that same regex string (exact string match), so
+ // the user can list it using the same string it used at creation time.
+ // - Items that match the regex.
+ // No regex means list everything.
+ return regex == nullptr || s == regex->GetText() || regex->Execute(s);
+ }
+
+ void 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;
+ }
+ }
+
+ 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;
+ }
+ }
+
+ 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::ForEachCallback<FormatterType> print_formatter =
+ [&result, &formatter_regex,
+ &any_printed](const TypeMatcher &type_matcher,
+ const FormatterSharedPointer &format_sp) -> bool {
+ if (ShouldListItem(type_matcher.GetMatchString().GetStringRef(),
+ formatter_regex.get())) {
+ any_printed = true;
+ result.GetOutputStream().Printf(
+ "%s: %s\n", type_matcher.GetMatchString().GetCString(),
+ format_sp->GetDescription().c_str());
+ }
+ return true;
+ };
+ category->ForEach(print_formatter);
+ };
+
+ 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 (ShouldListItem(category->GetName(), category_regex.get())) {
+ 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);
+ }
+ }
+};
+
+// 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':
+ if (m_match_type == eFormatterMatchCallback)
+ error.SetErrorString(
+ "can't use --regex and --recognizer-function at the same time");
+ else
+ m_match_type = eFormatterMatchRegex;
+ break;
+ case '\x01':
+ if (m_match_type == eFormatterMatchRegex)
+ error.SetErrorString(
+ "can't use --regex and --recognizer-function at the same time");
+ else
+ m_match_type = eFormatterMatchCallback;
+ 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_match_type = eFormatterMatchExact;
+ 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_match_type, 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()) {
+ AddSummary(ConstString(entry.ref()), script_format, m_options.m_match_type,
+ m_options.m_category, &error);
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ return false;
+ }
+ }
+
+ if (m_options.m_name) {
+ AddNamedSummary(m_options.m_name, script_format, &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_match_type, m_options.m_category,
+ &error);
+
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ return false;
+ }
+ }
+
+ if (m_options.m_name) {
+ AddNamedSummary(m_options.m_name, entry, &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) {
+ AddSimpleArgumentList(eArgTypeName, eArgRepeatPlus);
+
+ 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();")");
+}
+
+void CommandObjectTypeSummaryAdd::DoExecute(Args &command,
+ CommandReturnObject &result) {
+ WarnOnPotentialUnquotedUnsignedType(command, result);
+
+ if (m_options.m_is_add_script) {
+#if LLDB_ENABLE_PYTHON
+ Execute_ScriptSummary(command, result);
+#else
+ result.AppendError("python is disabled");
+#endif
+ return;
+ }
+
+ Execute_StringSummary(command, result);
+}
+
+static bool FixArrayTypeNameWithRegex(ConstString &type_name) {
+ llvm::StringRef type_name_ref(type_name.GetStringRef());
+
+ if (type_name_ref.ends_with("[]")) {
+ 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::AddNamedSummary(ConstString summary_name,
+ TypeSummaryImplSP entry,
+ Status *error) {
+ // system named summaries do not exist (yet?)
+ DataVisualization::NamedSummaryFormats::Add(summary_name, entry);
+ return true;
+}
+
+bool CommandObjectTypeSummaryAdd::AddSummary(ConstString type_name,
+ TypeSummaryImplSP entry,
+ FormatterMatchType match_type,
+ std::string category_name,
+ Status *error) {
+ lldb::TypeCategoryImplSP category;
+ DataVisualization::Categories::GetCategory(ConstString(category_name.c_str()),
+ category);
+
+ if (match_type == eFormatterMatchExact) {
+ if (FixArrayTypeNameWithRegex(type_name))
+ match_type = eFormatterMatchRegex;
+ }
+
+ if (match_type == eFormatterMatchRegex) {
+ match_type = eFormatterMatchRegex;
+ RegularExpression typeRX(type_name.GetStringRef());
+ if (!typeRX.IsValid()) {
+ if (error)
+ error->SetErrorString(
+ "regex format error (maybe this is not really a regex?)");
+ return false;
+ }
+ }
+
+ if (match_type == eFormatterMatchCallback) {
+ const char *function_name = type_name.AsCString();
+ ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
+ if (interpreter && !interpreter->CheckObjectExists(function_name)) {
+ error->SetErrorStringWithFormat(
+ "The provided recognizer function \"%s\" does not exist - "
+ "please define it before attempting to use this summary.\n",
+ function_name);
+ return false;
+ }
+ }
+ category->AddTypeSummary(type_name.GetStringRef(), match_type, entry);
+ return true;
+}
+
+// CommandObjectTypeSummaryDelete
+
+class CommandObjectTypeSummaryDelete : public CommandObjectTypeFormatterDelete {
+public:
+ CommandObjectTypeSummaryDelete(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterDelete(
+ interpreter, eFormatCategoryItemSummary) {}
+
+ ~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,
+ "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::ArrayRef(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) {
+ AddSimpleArgumentList(eArgTypeName, eArgRepeatPlus);
+ }
+
+ ~CommandObjectTypeCategoryDefine() override = default;
+
+protected:
+ void 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;
+ }
+
+ 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);
+ }
+};
+
+// 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::ArrayRef(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) {
+ AddSimpleArgumentList(eArgTypeName, eArgRepeatPlus);
+ }
+
+ ~CommandObjectTypeCategoryEnable() override = default;
+
+protected:
+ void 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;
+ }
+
+ 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;
+ }
+ 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);
+ }
+};
+
+// CommandObjectTypeCategoryDelete
+
+class CommandObjectTypeCategoryDelete : public CommandObjectParsed {
+public:
+ CommandObjectTypeCategoryDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "type category delete",
+ "Delete a category and all associated formatters.",
+ nullptr) {
+ AddSimpleArgumentList(eArgTypeName, eArgRepeatPlus);
+ }
+
+ ~CommandObjectTypeCategoryDelete() override = default;
+
+protected:
+ void 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;
+ }
+
+ 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;
+ }
+ if (!DataVisualization::Categories::Delete(typeCS))
+ success = false; // keep deleting even if we hit an error
+ }
+ if (success) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError("cannot delete one or more categories\n");
+ }
+ }
+};
+
+// 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::ArrayRef(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) {
+ AddSimpleArgumentList(eArgTypeName, eArgRepeatPlus);
+ }
+
+ ~CommandObjectTypeCategoryDisable() override = default;
+
+protected:
+ void 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;
+ }
+
+ 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;
+ }
+ DataVisualization::Categories::Disable(typeCS);
+ }
+ }
+
+ if (m_options.m_language != lldb::eLanguageTypeUnknown)
+ DataVisualization::Categories::Disable(m_options.m_language);
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+};
+
+// CommandObjectTypeCategoryList
+
+class CommandObjectTypeCategoryList : public CommandObjectParsed {
+public:
+ CommandObjectTypeCategoryList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "type category list",
+ "Provide a list of all existing categories.",
+ nullptr) {
+ AddSimpleArgumentList(eArgTypeName, eArgRepeatOptional);
+ }
+
+ ~CommandObjectTypeCategoryList() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (request.GetCursorIndex())
+ return;
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::eTypeCategoryNameCompletion, request,
+ nullptr);
+ }
+
+protected:
+ void 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;
+ }
+ } else if (argc != 0) {
+ result.AppendErrorWithFormat("%s takes 0 or one arg.\n",
+ m_cmd_name.c_str());
+ return;
+ }
+
+ 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);
+ }
+};
+
+// CommandObjectTypeFilterList
+
+class CommandObjectTypeFilterList
+ : public CommandObjectTypeFormatterList<TypeFilterImpl> {
+public:
+ CommandObjectTypeFilterList(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterList(interpreter, "type filter list",
+ "Show a list of current filters.") {}
+};
+
+// CommandObjectTypeSynthList
+
+class CommandObjectTypeSynthList
+ : public CommandObjectTypeFormatterList<SyntheticChildren> {
+public:
+ CommandObjectTypeSynthList(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterList(
+ interpreter, "type synthetic list",
+ "Show a list of current synthetic providers.") {}
+};
+
+// CommandObjectTypeFilterDelete
+
+class CommandObjectTypeFilterDelete : public CommandObjectTypeFormatterDelete {
+public:
+ CommandObjectTypeFilterDelete(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterDelete(
+ interpreter, eFormatCategoryItemFilter) {}
+
+ ~CommandObjectTypeFilterDelete() override = default;
+};
+
+// CommandObjectTypeSynthDelete
+
+class CommandObjectTypeSynthDelete : public CommandObjectTypeFormatterDelete {
+public:
+ CommandObjectTypeSynthDelete(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterDelete(
+ interpreter, eFormatCategoryItemSynth) {}
+
+ ~CommandObjectTypeSynthDelete() override = default;
+};
+
+
+// CommandObjectTypeFilterClear
+
+class CommandObjectTypeFilterClear : public CommandObjectTypeFormatterClear {
+public:
+ CommandObjectTypeFilterClear(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterClear(interpreter, eFormatCategoryItemFilter,
+ "type filter clear",
+ "Delete all existing filter.") {}
+};
+
+// CommandObjectTypeSynthClear
+
+class CommandObjectTypeSynthClear : public CommandObjectTypeFormatterClear {
+public:
+ CommandObjectTypeSynthClear(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterClear(
+ interpreter, eFormatCategoryItemSynth, "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_match_type, 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_match_type, 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() {
+ AddSimpleArgumentList(eArgTypeName, eArgRepeatPlus);
+}
+
+bool CommandObjectTypeSynthAdd::AddSynth(ConstString type_name,
+ SyntheticChildrenSP entry,
+ FormatterMatchType match_type,
+ std::string category_name,
+ Status *error) {
+ lldb::TypeCategoryImplSP category;
+ DataVisualization::Categories::GetCategory(ConstString(category_name.c_str()),
+ category);
+
+ if (match_type == eFormatterMatchExact) {
+ if (FixArrayTypeNameWithRegex(type_name))
+ match_type = eFormatterMatchRegex;
+ }
+
+ // Only check for conflicting filters in the same category if `type_name` is
+ // an actual type name. Matching a regex string against registered regexes
+ // doesn't work.
+ if (match_type == eFormatterMatchExact) {
+ // It's not generally possible to get a type object here. For example, this
+ // command can be run before loading any binaries. Do just a best-effort
+ // name-based lookup here to try to prevent conflicts.
+ FormattersMatchCandidate candidate_type(type_name, nullptr, TypeImpl(),
+ FormattersMatchCandidate::Flags());
+ if (category->AnyMatches(candidate_type, eFormatCategoryItemFilter,
+ false)) {
+ if (error)
+ error->SetErrorStringWithFormat("cannot add synthetic for type %s when "
+ "filter is defined in same category!",
+ type_name.AsCString());
+ return false;
+ }
+ }
+
+ if (match_type == eFormatterMatchRegex) {
+ RegularExpression typeRX(type_name.GetStringRef());
+ if (!typeRX.IsValid()) {
+ if (error)
+ error->SetErrorString(
+ "regex format error (maybe this is not really a regex?)");
+ return false;
+ }
+ }
+
+ if (match_type == eFormatterMatchCallback) {
+ const char *function_name = type_name.AsCString();
+ ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
+ if (interpreter && !interpreter->CheckObjectExists(function_name)) {
+ error->SetErrorStringWithFormat(
+ "The provided recognizer function \"%s\" does not exist - "
+ "please define it before attempting to use this summary.\n",
+ function_name);
+ return false;
+ }
+ }
+
+ category->AddTypeSynthetic(type_name.GetStringRef(), match_type, entry);
+ return true;
+}
+
+#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::ArrayRef(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;
+ }
+
+ // Only check for conflicting synthetic child providers in the same category
+ // if `type_name` is an actual type name. Matching a regex string against
+ // registered regexes doesn't work.
+ if (type == eRegularFilter) {
+ // It's not generally possible to get a type object here. For example,
+ // this command can be run before loading any binaries. Do just a
+ // best-effort name-based lookup here to try to prevent conflicts.
+ FormattersMatchCandidate candidate_type(
+ type_name, nullptr, TypeImpl(), FormattersMatchCandidate::Flags());
+ lldb::SyntheticChildrenSP entry;
+ if (category->AnyMatches(candidate_type, eFormatCategoryItemSynth,
+ false)) {
+ if (error)
+ error->SetErrorStringWithFormat("cannot add filter for type %s when "
+ "synthetic is defined in same "
+ "category!",
+ type_name.AsCString());
+ return false;
+ }
+ }
+
+ FormatterMatchType match_type = eFormatterMatchExact;
+ if (type == eRegexFilter) {
+ match_type = eFormatterMatchRegex;
+ 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->AddTypeFilter(type_name.GetStringRef(), match_type, entry);
+ return true;
+ }
+
+public:
+ CommandObjectTypeFilterAdd(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "type filter add",
+ "Add a new filter for a type.", nullptr) {
+ AddSimpleArgumentList(eArgTypeName, eArgRepeatPlus);
+
+ 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:
+ void 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;
+ }
+
+ if (m_options.m_expr_paths.empty()) {
+ result.AppendErrorWithFormat("%s needs one or more children.\n",
+ m_cmd_name.c_str());
+ return;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+};
+
+// "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().AsLanguageType();
+ 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::ArrayRef(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;
+ }
+
+ void 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;
+ }
+
+ 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;
+
+ 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);
+ }
+};
+
+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:
+ void DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) override {
+ TargetSP target_sp = GetDebugger().GetSelectedTarget();
+ Thread *thread = GetDefaultThread();
+ if (!thread) {
+ result.AppendError("no default thread");
+ return;
+ }
+
+ StackFrameSP frame_sp =
+ thread->GetSelectedFrame(DoNoSelectMostRelevantFrame);
+ 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);
+ }
+ } else {
+ result.AppendError("failed to evaluate expression");
+ }
+ }
+
+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;
+};
+
+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;
+};
+
+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)));
+ LoadSubCommand("synthetic",
+ CommandObjectSP(new CommandObjectTypeSynth(interpreter)));
+ 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..f13ec18e240c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectVersion.cpp
@@ -0,0 +1,28 @@
+//===-- 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;
+
+void CommandObjectVersion::DoExecute(Args &args, CommandReturnObject &result) {
+ result.AppendMessageWithFormat("%s\n", lldb_private::GetVersion());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+}
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..4ba081bf8706
--- /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:
+ void 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..f123211e7237
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpoint.cpp
@@ -0,0 +1,1185 @@
+//===-- 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 <memory>
+#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/CommandOptionArgumentTable.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Symbol/Function.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) {
+ CommandObject::AddIDsArgumentData(eWatchpointArgs);
+ }
+
+ ~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::ArrayRef(g_watchpoint_list_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ lldb::DescriptionLevel m_level = lldb::eDescriptionLevelBrief;
+ };
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+
+ if (target->GetProcessSP() && target->GetProcessSP()->IsAlive()) {
+ std::optional<uint32_t> num_supported_hardware_watchpoints =
+ target->GetProcessSP()->GetWatchpointSlotCount();
+
+ if (num_supported_hardware_watchpoints)
+ 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;
+ }
+
+ 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) {
+ WatchpointSP watch_sp = watchpoints.GetByIndex(i);
+ AddWatchpointDescription(output_stream, *watch_sp, 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;
+ }
+
+ const size_t size = wp_ids.size();
+ for (size_t i = 0; i < size; ++i) {
+ WatchpointSP watch_sp = watchpoints.FindByID(wp_ids[i]);
+ if (watch_sp)
+ AddWatchpointDescription(output_stream, *watch_sp, m_options.m_level);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+ }
+ }
+
+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) {
+ CommandObject::AddIDsArgumentData(eWatchpointArgs);
+ }
+
+ ~CommandObjectWatchpointEnable() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::eWatchpointIDCompletion, request,
+ nullptr);
+ }
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+ if (!CheckTargetForWatchpointOperations(target, result))
+ return;
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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);
+ }
+ }
+};
+
+// 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) {
+ CommandObject::AddIDsArgumentData(eWatchpointArgs);
+ }
+
+ ~CommandObjectWatchpointDisable() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::eWatchpointIDCompletion, request,
+ nullptr);
+ }
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+ if (!CheckTargetForWatchpointOperations(target, result))
+ return;
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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);
+ }
+ }
+};
+
+// 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) {
+ CommandObject::AddIDsArgumentData(eWatchpointArgs);
+ }
+
+ ~CommandObjectWatchpointDelete() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::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::ArrayRef(g_watchpoint_delete_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ bool m_force = false;
+ };
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+ if (!CheckTargetForWatchpointOperations(target, result))
+ return;
+
+ 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;
+ }
+
+ 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;
+ }
+
+ // Particular watchpoints selected; delete them.
+ std::vector<uint32_t> wp_ids;
+ if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command,
+ wp_ids)) {
+ result.AppendError("Invalid watchpoints specification.");
+ return;
+ }
+
+ 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);
+ }
+
+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) {
+ CommandObject::AddIDsArgumentData(eWatchpointArgs);
+ }
+
+ ~CommandObjectWatchpointIgnore() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::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::ArrayRef(g_watchpoint_ignore_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ uint32_t m_ignore_count = 0;
+ };
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+ if (!CheckTargetForWatchpointOperations(target, result))
+ return;
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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);
+ }
+ }
+
+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) {
+ CommandObject::AddIDsArgumentData(eWatchpointArgs);
+ }
+
+ ~CommandObjectWatchpointModify() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), lldb::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::ArrayRef(g_watchpoint_modify_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ std::string m_condition;
+ bool m_condition_passed = false;
+ };
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = &GetSelectedTarget();
+ if (!CheckTargetForWatchpointOperations(target, result))
+ return;
+
+ 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;
+ }
+
+ if (command.GetArgumentCount() == 0) {
+ WatchpointSP watch_sp = target->GetLastCreatedWatchpoint();
+ watch_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;
+ }
+
+ int count = 0;
+ const size_t size = wp_ids.size();
+ for (size_t i = 0; i < size; ++i) {
+ WatchpointSP watch_sp = watchpoints.FindByID(wp_ids[i]);
+ if (watch_sp) {
+ watch_sp->SetCondition(m_options.m_condition.c_str());
+ ++count;
+ }
+ }
+ result.AppendMessageWithFormat("%d watchpoints modified.\n", count);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+ }
+
+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 modify. "
+ "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.");
+
+ AddSimpleArgumentList(eArgTypeVarName);
+
+ // Absorb the '-w' and '-s' options into our option group.
+ m_option_group.Append(&m_option_watchpoint, LLDB_OPT_SET_1, LLDB_OPT_SET_1);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectWatchpointSetVariable() override = default;
+
+ 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;
+ }
+
+ void 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;
+ }
+
+ // If no '-w' is specified, default to '-w modify'.
+ if (!m_option_watchpoint.watch_type_specified) {
+ m_option_watchpoint.watch_type = OptionGroupWatchpoint::eWatchModify;
+ }
+
+ // 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;
+ }
+
+ // 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.GetCurrentValue() == 0
+ ? valobj_sp->GetByteSize().value_or(0)
+ : m_option_watchpoint.watch_size.GetCurrentValue();
+ }
+ 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;
+ }
+
+ // Now it's time to create the watchpoint.
+ uint32_t watch_type = 0;
+ switch (m_option_watchpoint.watch_type) {
+ case OptionGroupWatchpoint::eWatchModify:
+ watch_type |= LLDB_WATCH_TYPE_MODIFY;
+ break;
+ case OptionGroupWatchpoint::eWatchRead:
+ watch_type |= LLDB_WATCH_TYPE_READ;
+ break;
+ case OptionGroupWatchpoint::eWatchReadWrite:
+ watch_type |= LLDB_WATCH_TYPE_READ | LLDB_WATCH_TYPE_WRITE;
+ break;
+ case OptionGroupWatchpoint::eWatchWrite:
+ watch_type |= LLDB_WATCH_TYPE_WRITE;
+ break;
+ case OptionGroupWatchpoint::eWatchInvalid:
+ break;
+ };
+
+ error.Clear();
+ WatchpointSP watch_sp =
+ target->CreateWatchpoint(addr, size, &compiler_type, watch_type, error);
+ if (!watch_sp) {
+ result.AppendErrorWithFormat(
+ "Watchpoint creation failed (addr=0x%" PRIx64 ", size=%" PRIu64
+ ", variable expression='%s').\n",
+ addr, static_cast<uint64_t>(size), command.GetArgumentAtIndex(0));
+ if (const char *error_message = error.AsCString(nullptr))
+ result.AppendError(error_message);
+ return;
+ }
+
+ watch_sp->SetWatchSpec(command.GetArgumentAtIndex(0));
+ watch_sp->SetWatchVariable(true);
+ if (var_sp) {
+ if (var_sp->GetDeclaration().GetFile()) {
+ StreamString ss;
+ // True to show fullpath for declaration file.
+ var_sp->GetDeclaration().DumpStopContext(&ss, true);
+ watch_sp->SetDeclInfo(std::string(ss.GetString()));
+ }
+ if (var_sp->GetScope() == eValueTypeVariableLocal)
+ watch_sp->SetupVariableWatchpointDisabler(m_exe_ctx.GetFrameSP());
+ }
+ output_stream.Printf("Watchpoint created: ");
+ watch_sp->GetDescription(&output_stream, lldb::eDescriptionLevelFull);
+ output_stream.EOL();
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+
+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 '-l' option to specify the language of the 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 modify. "
+ "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 modify -s 1 -- foo + 32
+
+ Watches write access for the 1-byte region pointed to by the address 'foo + 32')");
+
+ AddSimpleArgumentList(eArgTypeExpression);
+
+ // 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:
+ void 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;
+
+ // 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;
+ }
+
+ // If no '-w' is specified, default to '-w write'.
+ if (!m_option_watchpoint.watch_type_specified) {
+ m_option_watchpoint.watch_type = OptionGroupWatchpoint::eWatchModify;
+ }
+
+ // 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(std::nullopt);
+ if (m_option_watchpoint.language_type != eLanguageTypeUnknown)
+ options.SetLanguage(m_option_watchpoint.language_type);
+
+ 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;
+ }
+
+ // 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;
+ }
+
+ if (m_option_watchpoint.watch_size.GetCurrentValue() != 0)
+ size = m_option_watchpoint.watch_size.GetCurrentValue();
+ else
+ size = target->GetArchitecture().GetAddressByteSize();
+
+ // Now it's time to create the watchpoint.
+ uint32_t watch_type;
+ switch (m_option_watchpoint.watch_type) {
+ case OptionGroupWatchpoint::eWatchRead:
+ watch_type = LLDB_WATCH_TYPE_READ;
+ break;
+ case OptionGroupWatchpoint::eWatchWrite:
+ watch_type = LLDB_WATCH_TYPE_WRITE;
+ break;
+ case OptionGroupWatchpoint::eWatchModify:
+ watch_type = LLDB_WATCH_TYPE_MODIFY;
+ break;
+ case OptionGroupWatchpoint::eWatchReadWrite:
+ watch_type = LLDB_WATCH_TYPE_READ | LLDB_WATCH_TYPE_WRITE;
+ break;
+ default:
+ watch_type = LLDB_WATCH_TYPE_MODIFY;
+ }
+
+ // 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());
+
+ std::optional<uint64_t> valobj_size = valobj_sp->GetByteSize();
+ // Set the type as a uint8_t array if the size being watched is
+ // larger than the ValueObject's size (which is probably the size
+ // of a pointer).
+ if (valobj_size && size > *valobj_size) {
+ auto type_system = compiler_type.GetTypeSystem();
+ if (type_system) {
+ CompilerType clang_uint8_type =
+ type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 8);
+ compiler_type = clang_uint8_type.GetArrayType(size);
+ }
+ }
+
+ Status error;
+ WatchpointSP watch_sp =
+ target->CreateWatchpoint(addr, size, &compiler_type, watch_type, error);
+ if (watch_sp) {
+ watch_sp->SetWatchSpec(std::string(expr));
+ Stream &output_stream = result.GetOutputStream();
+ output_stream.Printf("Watchpoint created: ");
+ watch_sp->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());
+ }
+ }
+
+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..aaf14540cb28
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpointCommand.cpp
@@ -0,0 +1,590 @@
+//===-- 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/CommandOptionArgumentTable.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#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.");
+
+ AddSimpleArgumentList(eArgTypeWatchpointID);
+ }
+
+ ~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::ArrayRef(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:
+ void 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;
+ }
+
+ 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;
+ }
+
+ 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(),
+ /*is_callback=*/false);
+ }
+ // 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 function_signature = m_options.m_function_name;
+ function_signature += "(frame, wp, internal_dict)";
+ script_interp->SetWatchpointCommandCallback(
+ wp_options, function_signature.c_str(), /*is_callback=*/true);
+ } 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);
+ }
+ }
+ }
+ }
+
+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) {
+ AddSimpleArgumentList(eArgTypeWatchpointID);
+ }
+
+ ~CommandObjectWatchpointCommandDelete() override = default;
+
+protected:
+ void 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;
+ }
+
+ if (command.GetArgumentCount() == 0) {
+ result.AppendError(
+ "No watchpoint specified from which to delete the commands");
+ return;
+ }
+
+ std::vector<uint32_t> valid_wp_ids;
+ if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command,
+ valid_wp_ids)) {
+ result.AppendError("Invalid watchpoints specification.");
+ return;
+ }
+
+ 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;
+ }
+ }
+ }
+};
+
+// 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) {
+ AddSimpleArgumentList(eArgTypeWatchpointID);
+ }
+
+ ~CommandObjectWatchpointCommandList() override = default;
+
+protected:
+ void 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;
+ }
+
+ if (command.GetArgumentCount() == 0) {
+ result.AppendError(
+ "No watchpoint specified for which to list the commands");
+ return;
+ }
+
+ std::vector<uint32_t> valid_wp_ids;
+ if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command,
+ valid_wp_ids)) {
+ result.AppendError("Invalid watchpoints specification.");
+ return;
+ }
+
+ 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);
+ }
+ }
+ }
+ }
+};
+
+// 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/CommandOptionArgumentTable.cpp b/contrib/llvm-project/lldb/source/Commands/CommandOptionArgumentTable.cpp
new file mode 100644
index 000000000000..e8e9307b6291
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandOptionArgumentTable.cpp
@@ -0,0 +1,313 @@
+//===-- CommandOptionArgumentTable.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/CommandOptionArgumentTable.h"
+#include "lldb/DataFormatters/FormatManager.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace lldb_private {
+llvm::StringRef RegisterNameHelpTextCallback() {
+ return "Register names can be specified using the architecture specific "
+ "names. "
+ "They can also be specified using generic names. Not all generic "
+ "entities have "
+ "registers backing them on all architectures. When they don't the "
+ "generic name "
+ "will return an error.\n"
+ "The generic names defined in lldb are:\n"
+ "\n"
+ "pc - program counter register\n"
+ "ra - return address register\n"
+ "fp - frame pointer register\n"
+ "sp - stack pointer register\n"
+ "flags - the flags register\n"
+ "arg{1-6} - integer argument passing registers.\n";
+}
+
+llvm::StringRef BreakpointIDHelpTextCallback() {
+ return "Breakpoints are identified using major and minor numbers; the major "
+ "number corresponds to the single entity that was created with a "
+ "'breakpoint "
+ "set' command; the minor numbers correspond to all the locations that "
+ "were "
+ "actually found/set based on the major breakpoint. A full breakpoint "
+ "ID might "
+ "look like 3.14, meaning the 14th location set for the 3rd "
+ "breakpoint. You "
+ "can specify all the locations of a breakpoint by just indicating the "
+ "major "
+ "breakpoint number. A valid breakpoint ID consists either of just the "
+ "major "
+ "number, or the major number followed by a dot and the location "
+ "number (e.g. "
+ "3 or 3.2 could both be valid breakpoint IDs.)";
+}
+
+llvm::StringRef BreakpointIDRangeHelpTextCallback() {
+ return "A 'breakpoint ID list' is a manner of specifying multiple "
+ "breakpoints. "
+ "This can be done through several mechanisms. The easiest way is to "
+ "just "
+ "enter a space-separated list of breakpoint IDs. To specify all the "
+ "breakpoint locations under a major breakpoint, you can use the major "
+ "breakpoint number followed by '.*', eg. '5.*' means all the "
+ "locations under "
+ "breakpoint 5. You can also indicate a range of breakpoints by using "
+ "<start-bp-id> - <end-bp-id>. The start-bp-id and end-bp-id for a "
+ "range can "
+ "be any valid breakpoint IDs. It is not legal, however, to specify a "
+ "range "
+ "using specific locations that cross major breakpoint numbers. I.e. "
+ "3.2 - 3.7"
+ " is legal; 2 - 5 is legal; but 3.2 - 4.4 is not legal.";
+}
+
+llvm::StringRef BreakpointNameHelpTextCallback() {
+ return "A name that can be added to a breakpoint when it is created, or "
+ "later "
+ "on with the \"breakpoint name add\" command. "
+ "Breakpoint names can be used to specify breakpoints in all the "
+ "places breakpoint IDs "
+ "and breakpoint ID ranges can be used. As such they provide a "
+ "convenient way to group breakpoints, "
+ "and to operate on breakpoints you create without having to track the "
+ "breakpoint number. "
+ "Note, the attributes you set when using a breakpoint name in a "
+ "breakpoint command don't "
+ "adhere to the name, but instead are set individually on all the "
+ "breakpoints currently tagged with that "
+ "name. Future breakpoints "
+ "tagged with that name will not pick up the attributes previously "
+ "given using that name. "
+ "In order to distinguish breakpoint names from breakpoint IDs and "
+ "ranges, "
+ "names must start with a letter from a-z or A-Z and cannot contain "
+ "spaces, \".\" or \"-\". "
+ "Also, breakpoint names can only be applied to breakpoints, not to "
+ "breakpoint locations.";
+}
+
+llvm::StringRef GDBFormatHelpTextCallback() {
+ return "A GDB format consists of a repeat count, a format letter and a size "
+ "letter. "
+ "The repeat count is optional and defaults to 1. The format letter is "
+ "optional "
+ "and defaults to the previous format that was used. The size letter "
+ "is optional "
+ "and defaults to the previous size that was used.\n"
+ "\n"
+ "Format letters include:\n"
+ "o - octal\n"
+ "x - hexadecimal\n"
+ "d - decimal\n"
+ "u - unsigned decimal\n"
+ "t - binary\n"
+ "f - float\n"
+ "a - address\n"
+ "i - instruction\n"
+ "c - char\n"
+ "s - string\n"
+ "T - OSType\n"
+ "A - float as hex\n"
+ "\n"
+ "Size letters include:\n"
+ "b - 1 byte (byte)\n"
+ "h - 2 bytes (halfword)\n"
+ "w - 4 bytes (word)\n"
+ "g - 8 bytes (giant)\n"
+ "\n"
+ "Example formats:\n"
+ "32xb - show 32 1 byte hexadecimal integer values\n"
+ "16xh - show 16 2 byte hexadecimal integer values\n"
+ "64 - show 64 2 byte hexadecimal integer values (format and size "
+ "from the last format)\n"
+ "dw - show 1 4 byte decimal integer value\n";
+}
+
+llvm::StringRef FormatHelpTextCallback() {
+ static std::string help_text;
+
+ if (!help_text.empty())
+ return help_text;
+
+ StreamString sstr;
+ sstr << "One of the format names (or one-character names) that can be used "
+ "to show a variable's value:\n";
+ for (Format f = eFormatDefault; f < kNumFormats; f = Format(f + 1)) {
+ if (f != eFormatDefault)
+ sstr.PutChar('\n');
+
+ char format_char = FormatManager::GetFormatAsFormatChar(f);
+ if (format_char)
+ sstr.Printf("'%c' or ", format_char);
+
+ sstr.Printf("\"%s\"", FormatManager::GetFormatAsCString(f));
+ }
+
+ sstr.Flush();
+
+ help_text = std::string(sstr.GetString());
+
+ return help_text;
+}
+
+llvm::StringRef LanguageTypeHelpTextCallback() {
+ static std::string help_text;
+
+ if (!help_text.empty())
+ return help_text;
+
+ StreamString sstr;
+ sstr << "One of the following languages:\n";
+
+ Language::PrintAllLanguages(sstr, " ", "\n");
+
+ sstr.Flush();
+
+ help_text = std::string(sstr.GetString());
+
+ return help_text;
+}
+
+llvm::StringRef SummaryStringHelpTextCallback() {
+ return "A summary string is a way to extract information from variables in "
+ "order to present them using a summary.\n"
+ "Summary strings contain static text, variables, scopes and control "
+ "sequences:\n"
+ " - Static text can be any sequence of non-special characters, i.e. "
+ "anything but '{', '}', '$', or '\\'.\n"
+ " - Variables are sequences of characters beginning with ${, ending "
+ "with } and that contain symbols in the format described below.\n"
+ " - Scopes are any sequence of text between { and }. Anything "
+ "included in a scope will only appear in the output summary if there "
+ "were no errors.\n"
+ " - Control sequences are the usual C/C++ '\\a', '\\n', ..., plus "
+ "'\\$', '\\{' and '\\}'.\n"
+ "A summary string works by copying static text verbatim, turning "
+ "control sequences into their character counterpart, expanding "
+ "variables and trying to expand scopes.\n"
+ "A variable is expanded by giving it a value other than its textual "
+ "representation, and the way this is done depends on what comes after "
+ "the ${ marker.\n"
+ "The most common sequence if ${var followed by an expression path, "
+ "which is the text one would type to access a member of an aggregate "
+ "types, given a variable of that type"
+ " (e.g. if type T has a member named x, which has a member named y, "
+ "and if t is of type T, the expression path would be .x.y and the way "
+ "to fit that into a summary string would be"
+ " ${var.x.y}). You can also use ${*var followed by an expression path "
+ "and in that case the object referred by the path will be "
+ "dereferenced before being displayed."
+ " If the object is not a pointer, doing so will cause an error. For "
+ "additional details on expression paths, you can type 'help "
+ "expr-path'. \n"
+ "By default, summary strings attempt to display the summary for any "
+ "variable they reference, and if that fails the value. If neither can "
+ "be shown, nothing is displayed."
+ "In a summary string, you can also use an array index [n], or a "
+ "slice-like range [n-m]. This can have two different meanings "
+ "depending on what kind of object the expression"
+ " path refers to:\n"
+ " - if it is a scalar type (any basic type like int, float, ...) the "
+ "expression is a bitfield, i.e. the bits indicated by the indexing "
+ "operator are extracted out of the number"
+ " and displayed as an individual variable\n"
+ " - if it is an array or pointer the array items indicated by the "
+ "indexing operator are shown as the result of the variable. if the "
+ "expression is an array, real array items are"
+ " printed; if it is a pointer, the pointer-as-array syntax is used to "
+ "obtain the values (this means, the latter case can have no range "
+ "checking)\n"
+ "If you are trying to display an array for which the size is known, "
+ "you can also use [] instead of giving an exact range. This has the "
+ "effect of showing items 0 thru size - 1.\n"
+ "Additionally, a variable can contain an (optional) format code, as "
+ "in ${var.x.y%code}, where code can be any of the valid formats "
+ "described in 'help format', or one of the"
+ " special symbols only allowed as part of a variable:\n"
+ " %V: show the value of the object by default\n"
+ " %S: show the summary of the object by default\n"
+ " %@: show the runtime-provided object description (for "
+ "Objective-C, it calls NSPrintForDebugger; for C/C++ it does "
+ "nothing)\n"
+ " %L: show the location of the object (memory address or a "
+ "register name)\n"
+ " %#: show the number of children of the object\n"
+ " %T: show the type of the object\n"
+ "Another variable that you can use in summary strings is ${svar . "
+ "This sequence works exactly like ${var, including the fact that "
+ "${*svar is an allowed sequence, but uses"
+ " the object's synthetic children provider instead of the actual "
+ "objects. For instance, if you are using STL synthetic children "
+ "providers, the following summary string would"
+ " count the number of actual elements stored in an std::list:\n"
+ "type summary add -s \"${svar%#}\" -x \"std::list<\"";
+}
+
+llvm::StringRef ExprPathHelpTextCallback() {
+ return "An expression path is the sequence of symbols that is used in C/C++ "
+ "to access a member variable of an aggregate object (class).\n"
+ "For instance, given a class:\n"
+ " class foo {\n"
+ " int a;\n"
+ " int b; .\n"
+ " foo* next;\n"
+ " };\n"
+ "the expression to read item b in the item pointed to by next for foo "
+ "aFoo would be aFoo.next->b.\n"
+ "Given that aFoo could just be any object of type foo, the string "
+ "'.next->b' is the expression path, because it can be attached to any "
+ "foo instance to achieve the effect.\n"
+ "Expression paths in LLDB include dot (.) and arrow (->) operators, "
+ "and most commands using expression paths have ways to also accept "
+ "the star (*) operator.\n"
+ "The meaning of these operators is the same as the usual one given to "
+ "them by the C/C++ standards.\n"
+ "LLDB also has support for indexing ([ ]) in expression paths, and "
+ "extends the traditional meaning of the square brackets operator to "
+ "allow bitfield extraction:\n"
+ "for objects of native types (int, float, char, ...) saying '[n-m]' "
+ "as an expression path (where n and m are any positive integers, e.g. "
+ "[3-5]) causes LLDB to extract"
+ " bits n thru m from the value of the variable. If n == m, [n] is "
+ "also allowed as a shortcut syntax. For arrays and pointers, "
+ "expression paths can only contain one index"
+ " and the meaning of the operation is the same as the one defined by "
+ "C/C++ (item extraction). Some commands extend bitfield-like syntax "
+ "for arrays and pointers with the"
+ " meaning of array slicing (taking elements n thru m inside the array "
+ "or pointed-to memory).";
+}
+
+llvm::StringRef arch_helper() {
+ static StreamString g_archs_help;
+ if (g_archs_help.Empty()) {
+ StringList archs;
+
+ ArchSpec::ListSupportedArchNames(archs);
+ g_archs_help.Printf("These are the supported architecture names:\n");
+ archs.Join("\n", g_archs_help);
+ }
+ return g_archs_help.GetString();
+}
+
+template <int I> struct TableValidator : TableValidator<I + 1> {
+ static_assert(
+ g_argument_table[I].arg_type == I,
+ "g_argument_table order doesn't match CommandArgumentType enumeration");
+};
+
+template <> struct TableValidator<eArgTypeLastArg> {};
+
+TableValidator<0> validator;
+
+} // namespace lldb_private
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandOptionsProcessAttach.cpp b/contrib/llvm-project/lldb/source/Commands/CommandOptionsProcessAttach.cpp
new file mode 100644
index 000000000000..d3d864dfe025
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandOptionsProcessAttach.cpp
@@ -0,0 +1,76 @@
+//===-- CommandOptionsProcessAttach.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 "CommandOptionsProcessAttach.h"
+
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandCompletions.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandOptionArgumentTable.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_attach
+#include "CommandOptions.inc"
+
+Status CommandOptionsProcessAttach::SetOptionValue(
+ uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+ const int short_option = g_process_attach_options[option_idx].short_option;
+ 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;
+}
+
+llvm::ArrayRef<OptionDefinition> CommandOptionsProcessAttach::GetDefinitions() {
+ return llvm::ArrayRef(g_process_attach_options);
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandOptionsProcessAttach.h b/contrib/llvm-project/lldb/source/Commands/CommandOptionsProcessAttach.h
new file mode 100644
index 000000000000..02daf1aa769a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandOptionsProcessAttach.h
@@ -0,0 +1,47 @@
+//===-- CommandOptionsProcessAttach.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_COMMANDOPTIONSPROCESSATTACH_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOPTIONSPROCESSATTACH_H
+
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Target/Process.h"
+
+namespace lldb_private {
+
+// CommandOptionsProcessAttach
+
+class CommandOptionsProcessAttach : public lldb_private::OptionGroup {
+public:
+ CommandOptionsProcessAttach() {
+ // Keep default values of all options in one place: OptionParsingStarting
+ // ()
+ OptionParsingStarting(nullptr);
+ }
+
+ ~CommandOptionsProcessAttach() 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 {
+ attach_info.Clear();
+ }
+
+ llvm::ArrayRef<lldb_private::OptionDefinition> GetDefinitions() override;
+
+ // Instance variables to hold the values for command options.
+
+ lldb_private::ProcessAttachInfo attach_info;
+}; // CommandOptionsProcessAttach
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOPTIONSPROCESSATTACH_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..b1c13d4df79e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandOptionsProcessLaunch.cpp
@@ -0,0 +1,151 @@
+//===-- 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/CommandObject.h"
+#include "lldb/Interpreter/CommandOptionArgumentTable.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;
+
+ TargetSP target_sp =
+ execution_context ? execution_context->GetTargetSP() : TargetSP();
+ switch (short_option) {
+ case 's': // Stop at program entry point
+ launch_info.GetFlags().Set(eLaunchFlagStopAtEntry);
+ break;
+ case 'm': // Stop at user entry point
+ target_sp->CreateBreakpointAtUserEntry(error);
+ 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': {
+ 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::ArrayRef(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..24e97f3bb97d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/Options.td
@@ -0,0 +1,1451 @@
+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">;
+ def tm_smn : Option<"show-mangled-names", "m">, Group<1>,
+ Desc<"Do not demangle symbol names before showing them.">;
+}
+
+let Command = "target modules dump separate debug info" in {
+ def tm_json : Option<"json", "j">, Group<1>,
+ Desc<"Output the details in JSON format.">;
+ def tm_errors_only : Option<"errors-only", "e">, Group<1>,
+ Desc<"Filter to show only debug info files with errors.">;
+}
+
+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<"ScriptLang">,
+ 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 = "diagnostics dump" in {
+ def diagnostics_dump_directory : Option<"directory", "d">, Group<1>,
+ Arg<"Path">, Desc<"Dump the diagnostics to the given directory.">;
+}
+
+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<"Boolean">, 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">,
+ 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).">;
+ def persistent_result : Option<"persistent-result", "\\x01">, Groups<[1,2]>,
+ Arg<"Boolean">,
+ Desc<"Persist expression result in a variable for subsequent use. "
+ "Expression results will be labeled with $-prefixed variables, e.g. $0, "
+ "$1, etc.">;
+}
+
+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">, 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 = "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_stop_at_user_entry : Option<"stop-at-user-entry", "m">,
+ Desc<"Stop at the user entry point when launching a process. For C based "
+ "languages this will be the 'main' function, but this might differ for "
+ "other languages.">;
+ 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">, 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">, Groups<[2,3]>,
+ 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">,
+ Desc<"Overwrite an existing command at this node.">;
+ def script_add_synchronicity : Option<"synchronicity", "s">,
+ EnumArg<"ScriptedCommandSynchronicity">,
+ Desc<"Set the synchronicity of this command's executions with regard to "
+ "LLDB event system.">;
+ def script_add_completion_type : Option<"completion-type", "C">,
+ Groups<[1,2]>, EnumArg<"CompletionType">,
+ Desc<"Specify which completion type the command should use - if none is "
+ "specified, the command won't use auto-completion.">;
+ def script_add_parsed_command : Option<"parsed", "p">, Group<3>,
+ Desc<"Make a parsed command. The command class will provide the command "
+ "definition by implementing get_options and get_arguments.">;
+
+}
+
+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 = "scripting run" in {
+ def script_language : Option<"language", "l">,
+ EnumArg<"ScriptLang">, 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">,
+ 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 whether the module is still in the "
+ "the shared module cache (Y/N), and its shared pointer use_count.">;
+ 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 (0 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">, 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">, 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 select" in {
+ def thread_select_thread_id : Option<"thread-id", "t">, Group<2>,
+ Arg<"ThreadID">, Completion<"ThreadID">,
+ Desc<"Provide a thread ID instead of a thread index.">;
+}
+
+let Command = "thread trace dump function calls" in {
+ def thread_trace_dump_function_calls_file : Option<"file", "F">, Group<1>,
+ Arg<"Filename">,
+ Desc<"Dump the function calls to a file instead of the standard output.">;
+ def thread_trace_dump_function_calls_json: Option<"json", "j">,
+ Group<1>,
+ Desc<"Dump in simple JSON format.">;
+ def thread_trace_dump_function_calls_pretty_json: Option<"pretty-json", "J">,
+ Group<1>,
+ Desc<"Dump in JSON format but pretty printing the output for easier "
+ "readability.">;
+}
+
+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_timestamps: Option<"time", "t">,
+ Group<1>,
+ Desc<"For each trace item, print the corresponding wall clock timestamp "
+ "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_instruction_only_events : Option<"only-events", "E">,
+ Group<1>,
+ Desc<"Dump only the events that happened during the execution of the "
+ "target. No instrutions are dumped.">;
+ 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_recognizer_function :
+ Option<"recognizer-function", "\\x01">,
+ Desc<"The names in the argument list are actually the names of python "
+ "functions that decide whether to use this summary for any given type. "
+ "Cannot be specified at the same time as --regex (-x).">;
+ 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.">;
+ def type_synth_add_recognizer_function :
+ Option<"recognizer-function", "\\x01">,
+ Desc<"The names in the argument list are actually the names of python "
+ "functions that decide whether to use this summary for any given type. "
+ "Cannot be specified at the same time as --regex (-x).">;
+}
+
+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<"ScriptLang">, 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.">;
+ def statistics_dump_summary: Option<"summary", "s">, Group<1>,
+ Desc<"Dump only high-level summary statistics. "
+ "Exclude targets, modules, breakpoints etc... details.">;
+ def statistics_dump_force: Option<"load-all-debug-info", "f">, Group<1>,
+ Desc<"Dump the total possible debug info statistics. "
+ "Force loading all the debug information if not yet loaded, and collect "
+ "statistics with those.">;
+ def statistics_dump_targets: Option<"targets", "r">, Group<1>,
+ Arg<"Boolean">,
+ Desc<"Dump statistics for the targets, including breakpoints, expression "
+ "evaluations, frame variables, etc. "
+ "Defaults to true in both default mode and summary mode. "
+ "In default mode, if both '--targets' and '--modules' are 'true', a list "
+ "of module identifiers will be added to the 'targets' section.">;
+ def statistics_dump_modules: Option<"modules", "m">, Group<1>,
+ Arg<"Boolean">,
+ Desc<"Dump statistics for the modules, including time and size of various "
+ "aspects of the module and debug information, type system, path, etc. "
+ "Defaults to true, unless the '--summary' mode is enabled, in which case "
+ "this is turned off unless specified. "
+ "In default mode, if both '--targets' and '--modules' are 'true', a list "
+ "of module identifiers will be added to the 'targets' section.">;
+ def statistics_dump_transcript: Option<"transcript", "t">, Group<1>,
+ Arg<"Boolean">,
+ Desc<"If the setting interpreter.save-transcript is enabled and this "
+ "option is 'true', include a JSON array with all commands the user and/or "
+ "scripts executed during a debug session. "
+ "Defaults to true, unless the '--summary' mode is enabled, in which case "
+ "this is turned off unless specified.">;
+
+}
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..9612cc130334
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/OptionsBase.td
@@ -0,0 +1,176 @@
+
+// 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 ArgType = type;
+}
+
+// Gives the option an required argument.
+class OptionalEnumArg<string type> {
+ string ArgType = type;
+ 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;
+}