diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Commands')
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 ¤t_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 ®ex_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 ®ex, + 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 ¤t_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 ¤t_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 ¤t_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 ¤t_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 ¤t_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 ¤t_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 ®_ctx, const RegisterInfo ®_info, + bool print_flags) { + RegisterValue reg_value; + if (!reg_ctx.ReadRegister(®_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 ¤t_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 ¤t_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 ¤t_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( + [®ex, &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; +} |