| //===-- CommandObjectTarget.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 "CommandObjectTarget.h" | |
| #include "lldb/Core/Debugger.h" | |
| #include "lldb/Core/IOHandler.h" | |
| #include "lldb/Core/Module.h" | |
| #include "lldb/Core/ModuleSpec.h" | |
| #include "lldb/Core/Section.h" | |
| #include "lldb/Core/ValueObjectVariable.h" | |
| #include "lldb/DataFormatters/ValueObjectPrinter.h" | |
| #include "lldb/Host/OptionParser.h" | |
| #include "lldb/Host/StringConvert.h" | |
| #include "lldb/Interpreter/CommandInterpreter.h" | |
| #include "lldb/Interpreter/CommandReturnObject.h" | |
| #include "lldb/Interpreter/OptionArgParser.h" | |
| #include "lldb/Interpreter/OptionGroupArchitecture.h" | |
| #include "lldb/Interpreter/OptionGroupBoolean.h" | |
| #include "lldb/Interpreter/OptionGroupFile.h" | |
| #include "lldb/Interpreter/OptionGroupFormat.h" | |
| #include "lldb/Interpreter/OptionGroupPlatform.h" | |
| #include "lldb/Interpreter/OptionGroupString.h" | |
| #include "lldb/Interpreter/OptionGroupUInt64.h" | |
| #include "lldb/Interpreter/OptionGroupUUID.h" | |
| #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" | |
| #include "lldb/Interpreter/OptionGroupVariable.h" | |
| #include "lldb/Interpreter/Options.h" | |
| #include "lldb/Symbol/CompileUnit.h" | |
| #include "lldb/Symbol/FuncUnwinders.h" | |
| #include "lldb/Symbol/LineTable.h" | |
| #include "lldb/Symbol/LocateSymbolFile.h" | |
| #include "lldb/Symbol/ObjectFile.h" | |
| #include "lldb/Symbol/SymbolFile.h" | |
| #include "lldb/Symbol/UnwindPlan.h" | |
| #include "lldb/Symbol/VariableList.h" | |
| #include "lldb/Target/ABI.h" | |
| #include "lldb/Target/Process.h" | |
| #include "lldb/Target/RegisterContext.h" | |
| #include "lldb/Target/SectionLoadList.h" | |
| #include "lldb/Target/StackFrame.h" | |
| #include "lldb/Target/Thread.h" | |
| #include "lldb/Target/ThreadSpec.h" | |
| #include "lldb/Utility/Args.h" | |
| #include "lldb/Utility/State.h" | |
| #include "lldb/Utility/Timer.h" | |
| #include "llvm/Support/FileSystem.h" | |
| #include "llvm/Support/FormatAdapters.h" | |
| #include <cerrno> | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| static void DumpTargetInfo(uint32_t target_idx, Target *target, | |
| const char *prefix_cstr, | |
| bool show_stopped_process_status, Stream &strm) { | |
| const ArchSpec &target_arch = target->GetArchitecture(); | |
| Module *exe_module = target->GetExecutableModulePointer(); | |
| char exe_path[PATH_MAX]; | |
| bool exe_valid = false; | |
| if (exe_module) | |
| exe_valid = exe_module->GetFileSpec().GetPath(exe_path, sizeof(exe_path)); | |
| if (!exe_valid) | |
| ::strcpy(exe_path, "<none>"); | |
| strm.Printf("%starget #%u: %s", prefix_cstr ? prefix_cstr : "", target_idx, | |
| exe_path); | |
| uint32_t properties = 0; | |
| if (target_arch.IsValid()) { | |
| strm.Printf("%sarch=", properties++ > 0 ? ", " : " ( "); | |
| target_arch.DumpTriple(strm); | |
| properties++; | |
| } | |
| PlatformSP platform_sp(target->GetPlatform()); | |
| if (platform_sp) | |
| strm.Printf("%splatform=%s", properties++ > 0 ? ", " : " ( ", | |
| platform_sp->GetName().GetCString()); | |
| ProcessSP process_sp(target->GetProcessSP()); | |
| bool show_process_status = false; | |
| if (process_sp) { | |
| lldb::pid_t pid = process_sp->GetID(); | |
| StateType state = process_sp->GetState(); | |
| if (show_stopped_process_status) | |
| show_process_status = StateIsStoppedState(state, true); | |
| const char *state_cstr = StateAsCString(state); | |
| if (pid != LLDB_INVALID_PROCESS_ID) | |
| strm.Printf("%spid=%" PRIu64, properties++ > 0 ? ", " : " ( ", pid); | |
| strm.Printf("%sstate=%s", properties++ > 0 ? ", " : " ( ", state_cstr); | |
| } | |
| if (properties > 0) | |
| strm.PutCString(" )\n"); | |
| else | |
| strm.EOL(); | |
| if (show_process_status) { | |
| const bool only_threads_with_stop_reason = true; | |
| const uint32_t start_frame = 0; | |
| const uint32_t num_frames = 1; | |
| const uint32_t num_frames_with_source = 1; | |
| const bool stop_format = false; | |
| process_sp->GetStatus(strm); | |
| process_sp->GetThreadStatus(strm, only_threads_with_stop_reason, | |
| start_frame, num_frames, | |
| num_frames_with_source, stop_format); | |
| } | |
| } | |
| static uint32_t DumpTargetList(TargetList &target_list, | |
| bool show_stopped_process_status, Stream &strm) { | |
| const uint32_t num_targets = target_list.GetNumTargets(); | |
| if (num_targets) { | |
| TargetSP selected_target_sp(target_list.GetSelectedTarget()); | |
| strm.PutCString("Current targets:\n"); | |
| for (uint32_t i = 0; i < num_targets; ++i) { | |
| TargetSP target_sp(target_list.GetTargetAtIndex(i)); | |
| if (target_sp) { | |
| bool is_selected = target_sp.get() == selected_target_sp.get(); | |
| DumpTargetInfo(i, target_sp.get(), is_selected ? "* " : " ", | |
| show_stopped_process_status, strm); | |
| } | |
| } | |
| } | |
| return num_targets; | |
| } | |
| // Note that the negation in the argument name causes a slightly confusing | |
| // mapping of the enum values. | |
| static constexpr OptionEnumValueElement g_dependents_enumaration[] = { | |
| { | |
| eLoadDependentsDefault, | |
| "default", | |
| "Only load dependents when the target is an executable.", | |
| }, | |
| { | |
| eLoadDependentsNo, | |
| "true", | |
| "Don't load dependents, even if the target is an executable.", | |
| }, | |
| { | |
| eLoadDependentsYes, | |
| "false", | |
| "Load dependents, even if the target is not an executable.", | |
| }, | |
| }; | |
| #define LLDB_OPTIONS_target_dependents | |
| #include "CommandOptions.inc" | |
| class OptionGroupDependents : public OptionGroup { | |
| public: | |
| OptionGroupDependents() {} | |
| ~OptionGroupDependents() override {} | |
| llvm::ArrayRef<OptionDefinition> GetDefinitions() override { | |
| return llvm::makeArrayRef(g_target_dependents_options); | |
| } | |
| Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, | |
| ExecutionContext *execution_context) override { | |
| Status error; | |
| // For compatibility no value means don't load dependents. | |
| if (option_value.empty()) { | |
| m_load_dependent_files = eLoadDependentsNo; | |
| return error; | |
| } | |
| const char short_option = | |
| g_target_dependents_options[option_idx].short_option; | |
| if (short_option == 'd') { | |
| LoadDependentFiles tmp_load_dependents; | |
| tmp_load_dependents = (LoadDependentFiles)OptionArgParser::ToOptionEnum( | |
| option_value, g_target_dependents_options[option_idx].enum_values, 0, | |
| error); | |
| if (error.Success()) | |
| m_load_dependent_files = tmp_load_dependents; | |
| } else { | |
| error.SetErrorStringWithFormat("unrecognized short option '%c'", | |
| short_option); | |
| } | |
| return error; | |
| } | |
| Status SetOptionValue(uint32_t, const char *, ExecutionContext *) = delete; | |
| void OptionParsingStarting(ExecutionContext *execution_context) override { | |
| m_load_dependent_files = eLoadDependentsDefault; | |
| } | |
| LoadDependentFiles m_load_dependent_files; | |
| private: | |
| DISALLOW_COPY_AND_ASSIGN(OptionGroupDependents); | |
| }; | |
| #pragma mark CommandObjectTargetCreate | |
| // "target create" | |
| class CommandObjectTargetCreate : public CommandObjectParsed { | |
| public: | |
| CommandObjectTargetCreate(CommandInterpreter &interpreter) | |
| : CommandObjectParsed( | |
| interpreter, "target create", | |
| "Create a target using the argument as the main executable.", | |
| nullptr), | |
| m_option_group(), m_arch_option(), | |
| m_core_file(LLDB_OPT_SET_1, false, "core", 'c', 0, eArgTypeFilename, | |
| "Fullpath to a core file to use for this target."), | |
| m_platform_path(LLDB_OPT_SET_1, false, "platform-path", 'P', 0, | |
| eArgTypePath, | |
| "Path to the remote file to use for this target."), | |
| m_symbol_file(LLDB_OPT_SET_1, false, "symfile", 's', 0, | |
| eArgTypeFilename, | |
| "Fullpath to a stand alone debug " | |
| "symbols file for when debug symbols " | |
| "are not in the executable."), | |
| m_remote_file( | |
| LLDB_OPT_SET_1, false, "remote-file", 'r', 0, eArgTypeFilename, | |
| "Fullpath to the file on the remote host if debugging remotely."), | |
| m_add_dependents() { | |
| CommandArgumentEntry arg; | |
| CommandArgumentData file_arg; | |
| // Define the first (and only) variant of this arg. | |
| file_arg.arg_type = eArgTypeFilename; | |
| file_arg.arg_repetition = eArgRepeatPlain; | |
| // There is only one variant this argument could be; put it into the | |
| // argument entry. | |
| arg.push_back(file_arg); | |
| // Push the data for the first argument into the m_arguments vector. | |
| m_arguments.push_back(arg); | |
| m_option_group.Append(&m_arch_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); | |
| m_option_group.Append(&m_core_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); | |
| m_option_group.Append(&m_platform_path, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); | |
| m_option_group.Append(&m_symbol_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); | |
| m_option_group.Append(&m_remote_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); | |
| m_option_group.Append(&m_add_dependents, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); | |
| m_option_group.Finalize(); | |
| } | |
| ~CommandObjectTargetCreate() override = default; | |
| Options *GetOptions() override { return &m_option_group; } | |
| void | |
| HandleArgumentCompletion(CompletionRequest &request, | |
| OptionElementVector &opt_element_vector) override { | |
| CommandCompletions::InvokeCommonCompletionCallbacks( | |
| GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion, | |
| request, nullptr); | |
| } | |
| protected: | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| const size_t argc = command.GetArgumentCount(); | |
| FileSpec core_file(m_core_file.GetOptionValue().GetCurrentValue()); | |
| FileSpec remote_file(m_remote_file.GetOptionValue().GetCurrentValue()); | |
| if (core_file) { | |
| if (!FileSystem::Instance().Exists(core_file)) { | |
| result.AppendErrorWithFormat("core file '%s' doesn't exist", | |
| core_file.GetPath().c_str()); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| if (!FileSystem::Instance().Readable(core_file)) { | |
| result.AppendErrorWithFormat("core file '%s' is not readable", | |
| core_file.GetPath().c_str()); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| } | |
| if (argc == 1 || core_file || remote_file) { | |
| FileSpec symfile(m_symbol_file.GetOptionValue().GetCurrentValue()); | |
| if (symfile) { | |
| if (FileSystem::Instance().Exists(symfile)) { | |
| if (!FileSystem::Instance().Readable(symfile)) { | |
| result.AppendErrorWithFormat("symbol file '%s' is not readable", | |
| symfile.GetPath().c_str()); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| } else { | |
| char symfile_path[PATH_MAX]; | |
| symfile.GetPath(symfile_path, sizeof(symfile_path)); | |
| result.AppendErrorWithFormat("invalid symbol file path '%s'", | |
| symfile_path); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| } | |
| const char *file_path = command.GetArgumentAtIndex(0); | |
| static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); | |
| Timer scoped_timer(func_cat, "(lldb) target create '%s'", file_path); | |
| FileSpec file_spec; | |
| if (file_path) { | |
| file_spec.SetFile(file_path, FileSpec::Style::native); | |
| FileSystem::Instance().Resolve(file_spec); | |
| } | |
| 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, nullptr, target_sp)); | |
| if (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(); | |
| 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()); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| } | |
| } else { | |
| // there is no local file and we need one | |
| // in order to make the remote ---> local transfer we need a | |
| // platform | |
| // TODO: if the user has passed in a --platform argument, use it | |
| // to fetch the right platform | |
| if (!platform_sp) { | |
| result.AppendError( | |
| "unable to perform remote debugging without a platform"); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| 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()); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| } else { | |
| // make up a local file | |
| result.AppendError("remote --> local transfer without local " | |
| "path is not implemented yet"); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| } | |
| } else { | |
| result.AppendError("no platform found for target"); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| } | |
| if (symfile || remote_file) { | |
| ModuleSP module_sp(target_sp->GetExecutableModule()); | |
| if (module_sp) { | |
| if (symfile) | |
| module_sp->SetSymbolFileFileSpec(symfile); | |
| if (remote_file) { | |
| std::string remote_path = remote_file.GetPath(); | |
| target_sp->SetArg0(remote_path.c_str()); | |
| module_sp->SetPlatformFileSpec(remote_file); | |
| } | |
| } | |
| } | |
| debugger.GetTargetList().SetSelectedTarget(target_sp.get()); | |
| 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) { | |
| char core_path[PATH_MAX]; | |
| core_file.GetPath(core_path, sizeof(core_path)); | |
| if (FileSystem::Instance().Exists(core_file)) { | |
| if (!FileSystem::Instance().Readable(core_file)) { | |
| result.AppendMessageWithFormat( | |
| "Core file '%s' is not readable.\n", core_path); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| FileSpec core_file_dir; | |
| core_file_dir.GetDirectory() = core_file.GetDirectory(); | |
| target_sp->AppendExecutableSearchPaths(core_file_dir); | |
| ProcessSP process_sp(target_sp->CreateProcess( | |
| GetDebugger().GetListener(), llvm::StringRef(), &core_file)); | |
| if (process_sp) { | |
| // Seems weird that we Launch a core file, but that is what we | |
| // do! | |
| error = process_sp->LoadCore(); | |
| if (error.Fail()) { | |
| result.AppendError( | |
| error.AsCString("can't find plug-in for core file")); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } else { | |
| result.AppendMessageWithFormat( | |
| "Core file '%s' (%s) was loaded.\n", core_path, | |
| target_sp->GetArchitecture().GetArchitectureName()); | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| } | |
| } else { | |
| result.AppendErrorWithFormat( | |
| "Unable to find process plug-in for core file '%s'\n", | |
| core_path); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| } else { | |
| result.AppendErrorWithFormat("Core file '%s' does not exist\n", | |
| core_path); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| } else { | |
| result.AppendMessageWithFormat( | |
| "Current executable set to '%s' (%s).\n", | |
| file_spec.GetPath().c_str(), | |
| target_sp->GetArchitecture().GetArchitectureName()); | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| } | |
| } else { | |
| result.AppendError(error.AsCString()); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| } else { | |
| result.AppendErrorWithFormat("'%s' takes exactly one executable path " | |
| "argument, or use the --core option.\n", | |
| m_cmd_name.c_str()); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| return result.Succeeded(); | |
| } | |
| private: | |
| OptionGroupOptions m_option_group; | |
| OptionGroupArchitecture m_arch_option; | |
| OptionGroupFile m_core_file; | |
| OptionGroupFile m_platform_path; | |
| OptionGroupFile m_symbol_file; | |
| OptionGroupFile m_remote_file; | |
| OptionGroupDependents m_add_dependents; | |
| }; | |
| #pragma mark CommandObjectTargetList | |
| // "target list" | |
| class CommandObjectTargetList : public CommandObjectParsed { | |
| public: | |
| CommandObjectTargetList(CommandInterpreter &interpreter) | |
| : CommandObjectParsed( | |
| interpreter, "target list", | |
| "List all current targets in the current debug session.", nullptr) { | |
| } | |
| ~CommandObjectTargetList() override = default; | |
| protected: | |
| bool DoExecute(Args &args, CommandReturnObject &result) override { | |
| if (args.GetArgumentCount() == 0) { | |
| 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); | |
| } else { | |
| result.AppendError("the 'target list' command takes no arguments\n"); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| return result.Succeeded(); | |
| } | |
| }; | |
| #pragma mark CommandObjectTargetSelect | |
| // "target select" | |
| class CommandObjectTargetSelect : public CommandObjectParsed { | |
| public: | |
| CommandObjectTargetSelect(CommandInterpreter &interpreter) | |
| : CommandObjectParsed( | |
| interpreter, "target select", | |
| "Select a target as the current target by target index.", nullptr) { | |
| } | |
| ~CommandObjectTargetSelect() override = default; | |
| protected: | |
| bool DoExecute(Args &args, CommandReturnObject &result) override { | |
| if (args.GetArgumentCount() == 1) { | |
| bool success = false; | |
| const char *target_idx_arg = args.GetArgumentAtIndex(0); | |
| uint32_t target_idx = | |
| StringConvert::ToUInt32(target_idx_arg, UINT32_MAX, 0, &success); | |
| if (success) { | |
| TargetList &target_list = GetDebugger().GetTargetList(); | |
| const uint32_t num_targets = target_list.GetNumTargets(); | |
| if (target_idx < num_targets) { | |
| TargetSP target_sp(target_list.GetTargetAtIndex(target_idx)); | |
| if (target_sp) { | |
| Stream &strm = result.GetOutputStream(); | |
| target_list.SetSelectedTarget(target_sp.get()); | |
| bool show_stopped_process_status = false; | |
| DumpTargetList(target_list, show_stopped_process_status, strm); | |
| result.SetStatus(eReturnStatusSuccessFinishResult); | |
| } else { | |
| result.AppendErrorWithFormat("target #%u is NULL in target list\n", | |
| target_idx); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| } 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); | |
| } | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| } else { | |
| result.AppendErrorWithFormat("invalid index string value '%s'\n", | |
| target_idx_arg); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| } else { | |
| result.AppendError( | |
| "'target select' takes a single argument: a target index\n"); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| return result.Succeeded(); | |
| } | |
| }; | |
| #pragma mark CommandObjectTargetSelect | |
| // "target delete" | |
| class CommandObjectTargetDelete : public CommandObjectParsed { | |
| public: | |
| CommandObjectTargetDelete(CommandInterpreter &interpreter) | |
| : CommandObjectParsed(interpreter, "target delete", | |
| "Delete one or more targets by target index.", | |
| nullptr), | |
| m_option_group(), 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(); | |
| } | |
| ~CommandObjectTargetDelete() override = default; | |
| Options *GetOptions() override { return &m_option_group; } | |
| protected: | |
| bool DoExecute(Args &args, CommandReturnObject &result) override { | |
| const size_t argc = args.GetArgumentCount(); | |
| std::vector<TargetSP> delete_target_list; | |
| TargetList &target_list = GetDebugger().GetTargetList(); | |
| TargetSP target_sp; | |
| if (m_all_option.GetOptionValue()) { | |
| for (int i = 0; i < target_list.GetNumTargets(); ++i) | |
| delete_target_list.push_back(target_list.GetTargetAtIndex(i)); | |
| } else if (argc > 0) { | |
| const uint32_t num_targets = target_list.GetNumTargets(); | |
| // Bail out if don't have any targets. | |
| if (num_targets == 0) { | |
| result.AppendError("no targets to delete"); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| for (auto &entry : args.entries()) { | |
| uint32_t target_idx; | |
| if (entry.ref().getAsInteger(0, target_idx)) { | |
| result.AppendErrorWithFormat("invalid target index '%s'\n", | |
| entry.c_str()); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| if (target_idx < num_targets) { | |
| target_sp = target_list.GetTargetAtIndex(target_idx); | |
| if (target_sp) { | |
| delete_target_list.push_back(target_sp); | |
| continue; | |
| } | |
| } | |
| if (num_targets > 1) | |
| result.AppendErrorWithFormat("target index %u is out of range, valid " | |
| "target indexes are 0 - %u\n", | |
| target_idx, num_targets - 1); | |
| else | |
| result.AppendErrorWithFormat( | |
| "target index %u is out of range, the only valid index is 0\n", | |
| target_idx); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| } else { | |
| target_sp = target_list.GetSelectedTarget(); | |
| if (!target_sp) { | |
| result.AppendErrorWithFormat("no target is currently selected\n"); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| delete_target_list.push_back(target_sp); | |
| } | |
| const size_t num_targets_to_delete = delete_target_list.size(); | |
| for (size_t idx = 0; idx < num_targets_to_delete; ++idx) { | |
| target_sp = delete_target_list[idx]; | |
| target_list.DeleteTarget(target_sp); | |
| target_sp->Destroy(); | |
| } | |
| // If "--clean" was specified, prune any orphaned shared modules from the | |
| // global shared module list | |
| if (m_cleanup_option.GetOptionValue()) { | |
| const bool mandatory = true; | |
| ModuleList::RemoveOrphanSharedModules(mandatory); | |
| } | |
| result.GetOutputStream().Printf("%u targets deleted.\n", | |
| (uint32_t)num_targets_to_delete); | |
| result.SetStatus(eReturnStatusSuccessFinishResult); | |
| return true; | |
| } | |
| OptionGroupOptions m_option_group; | |
| OptionGroupBoolean m_all_option; | |
| OptionGroupBoolean m_cleanup_option; | |
| }; | |
| #pragma mark CommandObjectTargetVariable | |
| // "target variable" | |
| 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_group(), | |
| 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."), | |
| m_varobj_options() { | |
| CommandArgumentEntry arg; | |
| CommandArgumentData var_name_arg; | |
| // Define the first (and only) variant of this arg. | |
| var_name_arg.arg_type = eArgTypeVarName; | |
| var_name_arg.arg_repetition = eArgRepeatPlus; | |
| // There is only one variant this argument could be; put it into the | |
| // argument entry. | |
| arg.push_back(var_name_arg); | |
| // Push the data for the first argument into the m_arguments vector. | |
| m_arguments.push_back(arg); | |
| m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); | |
| m_option_group.Append(&m_option_variable, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); | |
| m_option_group.Append(&m_option_format, | |
| OptionGroupFormat::OPTION_GROUP_FORMAT | | |
| OptionGroupFormat::OPTION_GROUP_GDB_FMT, | |
| LLDB_OPT_SET_1); | |
| m_option_group.Append(&m_option_compile_units, LLDB_OPT_SET_ALL, | |
| LLDB_OPT_SET_1); | |
| m_option_group.Append(&m_option_shared_libraries, LLDB_OPT_SET_ALL, | |
| LLDB_OPT_SET_1); | |
| m_option_group.Finalize(); | |
| } | |
| ~CommandObjectTargetVariable() override = default; | |
| void DumpValueObject(Stream &s, VariableSP &var_sp, ValueObjectSP &valobj_sp, | |
| const char *root_name) { | |
| DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions()); | |
| if (!valobj_sp->GetTargetSP()->GetDisplayRuntimeSupportValues() && | |
| valobj_sp->IsRuntimeSupportValue()) | |
| return; | |
| switch (var_sp->GetScope()) { | |
| case eValueTypeVariableGlobal: | |
| if (m_option_variable.show_scope) | |
| s.PutCString("GLOBAL: "); | |
| break; | |
| case eValueTypeVariableStatic: | |
| if (m_option_variable.show_scope) | |
| s.PutCString("STATIC: "); | |
| break; | |
| case eValueTypeVariableArgument: | |
| if (m_option_variable.show_scope) | |
| s.PutCString(" ARG: "); | |
| break; | |
| case eValueTypeVariableLocal: | |
| if (m_option_variable.show_scope) | |
| s.PutCString(" LOCAL: "); | |
| break; | |
| case eValueTypeVariableThreadLocal: | |
| if (m_option_variable.show_scope) | |
| s.PutCString("THREAD: "); | |
| break; | |
| default: | |
| break; | |
| } | |
| if (m_option_variable.show_decl) { | |
| bool show_fullpaths = false; | |
| bool show_module = true; | |
| if (var_sp->DumpDeclaration(&s, show_fullpaths, show_module)) | |
| s.PutCString(": "); | |
| } | |
| const Format format = m_option_format.GetFormat(); | |
| if (format != eFormatDefault) | |
| options.SetFormat(format); | |
| options.SetRootValueObjectName(root_name); | |
| valobj_sp->Dump(s, options); | |
| } | |
| static size_t GetVariableCallback(void *baton, const char *name, | |
| VariableList &variable_list) { | |
| size_t old_size = variable_list.GetSize(); | |
| Target *target = static_cast<Target *>(baton); | |
| if (target) | |
| target->GetImages().FindGlobalVariables(ConstString(name), UINT32_MAX, | |
| variable_list); | |
| return variable_list.GetSize() - old_size; | |
| } | |
| Options *GetOptions() override { return &m_option_group; } | |
| protected: | |
| void DumpGlobalVariableList(const ExecutionContext &exe_ctx, | |
| const SymbolContext &sc, | |
| const VariableList &variable_list, Stream &s) { | |
| size_t count = variable_list.GetSize(); | |
| if (count > 0) { | |
| if (sc.module_sp) { | |
| if (sc.comp_unit) { | |
| s.Printf("Global variables for %s in %s:\n", | |
| sc.comp_unit->GetPath().c_str(), | |
| sc.module_sp->GetFileSpec().GetPath().c_str()); | |
| } else { | |
| s.Printf("Global variables for %s\n", | |
| sc.module_sp->GetFileSpec().GetPath().c_str()); | |
| } | |
| } else if (sc.comp_unit) { | |
| s.Printf("Global variables for %s\n", sc.comp_unit->GetPath().c_str()); | |
| } | |
| for (uint32_t i = 0; i < count; ++i) { | |
| VariableSP var_sp(variable_list.GetVariableAtIndex(i)); | |
| if (var_sp) { | |
| ValueObjectSP valobj_sp(ValueObjectVariable::Create( | |
| exe_ctx.GetBestExecutionContextScope(), var_sp)); | |
| if (valobj_sp) | |
| DumpValueObject(s, var_sp, valobj_sp, | |
| var_sp->GetName().GetCString()); | |
| } | |
| } | |
| } | |
| } | |
| bool DoExecute(Args &args, CommandReturnObject &result) override { | |
| Target *target = m_exe_ctx.GetTargetPtr(); | |
| const size_t argc = args.GetArgumentCount(); | |
| Stream &s = result.GetOutputStream(); | |
| if (argc > 0) { | |
| // TODO: Convert to entry-based iteration. Requires converting | |
| // DumpValueObject. | |
| for (size_t idx = 0; idx < argc; ++idx) { | |
| VariableList variable_list; | |
| ValueObjectList valobj_list; | |
| const char *arg = args.GetArgumentAtIndex(idx); | |
| size_t matches = 0; | |
| bool use_var_name = false; | |
| if (m_option_variable.use_regex) { | |
| RegularExpression regex(llvm::StringRef::withNullAsEmpty(arg)); | |
| if (!regex.IsValid()) { | |
| result.GetErrorStream().Printf( | |
| "error: invalid regular expression: '%s'\n", arg); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| use_var_name = true; | |
| target->GetImages().FindGlobalVariables(regex, UINT32_MAX, | |
| variable_list); | |
| matches = variable_list.GetSize(); | |
| } else { | |
| Status error(Variable::GetValuesForVariableExpressionPath( | |
| arg, m_exe_ctx.GetBestExecutionContextScope(), | |
| GetVariableCallback, target, variable_list, valobj_list)); | |
| matches = variable_list.GetSize(); | |
| } | |
| if (matches == 0) { | |
| result.GetErrorStream().Printf( | |
| "error: can't find global variable '%s'\n", arg); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } else { | |
| for (uint32_t global_idx = 0; global_idx < matches; ++global_idx) { | |
| VariableSP var_sp(variable_list.GetVariableAtIndex(global_idx)); | |
| if (var_sp) { | |
| ValueObjectSP valobj_sp( | |
| valobj_list.GetValueObjectAtIndex(global_idx)); | |
| if (!valobj_sp) | |
| valobj_sp = ValueObjectVariable::Create( | |
| m_exe_ctx.GetBestExecutionContextScope(), var_sp); | |
| if (valobj_sp) | |
| DumpValueObject(s, var_sp, valobj_sp, | |
| use_var_name ? var_sp->GetName().GetCString() | |
| : arg); | |
| } | |
| } | |
| } | |
| } | |
| } 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); | |
| 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.AppendErrorWithFormat( | |
| "no global variables in current compile unit: %s\n", | |
| comp_unit->GetPath().c_str()); | |
| 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"); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| } else { | |
| SymbolContextList sc_list; | |
| // We have one or more compile unit or shlib | |
| if (num_shlibs > 0) { | |
| for (size_t shlib_idx = 0; shlib_idx < num_shlibs; ++shlib_idx) { | |
| const FileSpec module_file(shlibs.GetFileSpecAtIndex(shlib_idx)); | |
| ModuleSpec module_spec(module_file); | |
| ModuleSP module_sp( | |
| target->GetImages().FindFirstModule(module_spec)); | |
| if (module_sp) { | |
| if (num_compile_units > 0) { | |
| for (size_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) | |
| module_sp->FindCompileUnits( | |
| compile_units.GetFileSpecAtIndex(cu_idx), sc_list); | |
| } else { | |
| SymbolContext sc; | |
| sc.module_sp = module_sp; | |
| sc_list.Append(sc); | |
| } | |
| } else { | |
| // Didn't find matching shlib/module in target... | |
| result.AppendErrorWithFormat( | |
| "target doesn't contain the specified shared library: %s\n", | |
| module_file.GetPath().c_str()); | |
| } | |
| } | |
| } else { | |
| // No shared libraries, we just want to find globals for the compile | |
| // units files that were specified | |
| for (size_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) | |
| target->GetImages().FindCompileUnits( | |
| compile_units.GetFileSpecAtIndex(cu_idx), sc_list); | |
| } | |
| const uint32_t num_scs = sc_list.GetSize(); | |
| if (num_scs > 0) { | |
| SymbolContext sc; | |
| for (uint32_t sc_idx = 0; sc_idx < num_scs; ++sc_idx) { | |
| if (sc_list.GetContextAtIndex(sc_idx, sc)) { | |
| if (sc.comp_unit) { | |
| const bool can_create = true; | |
| VariableListSP comp_unit_varlist_sp( | |
| sc.comp_unit->GetVariableList(can_create)); | |
| if (comp_unit_varlist_sp) | |
| DumpGlobalVariableList(m_exe_ctx, sc, *comp_unit_varlist_sp, | |
| s); | |
| } else if (sc.module_sp) { | |
| // Get all global variables for this module | |
| lldb_private::RegularExpression all_globals_regex( | |
| llvm::StringRef( | |
| ".")); // Any global with at least one character | |
| VariableList variable_list; | |
| sc.module_sp->FindGlobalVariables(all_globals_regex, UINT32_MAX, | |
| variable_list); | |
| DumpGlobalVariableList(m_exe_ctx, sc, variable_list, s); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if (m_interpreter.TruncationWarningNecessary()) { | |
| result.GetOutputStream().Printf(m_interpreter.TruncationWarningText(), | |
| m_cmd_name.c_str()); | |
| m_interpreter.TruncationWarningGiven(); | |
| } | |
| return result.Succeeded(); | |
| } | |
| OptionGroupOptions m_option_group; | |
| OptionGroupVariable m_option_variable; | |
| OptionGroupFormat m_option_format; | |
| OptionGroupFileList m_option_compile_units; | |
| OptionGroupFileList m_option_shared_libraries; | |
| OptionGroupValueObjectDisplay m_varobj_options; | |
| }; | |
| #pragma mark CommandObjectTargetModulesSearchPathsAdd | |
| class CommandObjectTargetModulesSearchPathsAdd : public CommandObjectParsed { | |
| public: | |
| CommandObjectTargetModulesSearchPathsAdd(CommandInterpreter &interpreter) | |
| : CommandObjectParsed(interpreter, "target modules search-paths add", | |
| "Add new image search paths substitution pairs to " | |
| "the current target.", | |
| nullptr, eCommandRequiresTarget) { | |
| CommandArgumentEntry arg; | |
| CommandArgumentData old_prefix_arg; | |
| CommandArgumentData new_prefix_arg; | |
| // Define the first variant of this arg pair. | |
| old_prefix_arg.arg_type = eArgTypeOldPathPrefix; | |
| old_prefix_arg.arg_repetition = eArgRepeatPairPlus; | |
| // Define the first variant of this arg pair. | |
| new_prefix_arg.arg_type = eArgTypeNewPathPrefix; | |
| new_prefix_arg.arg_repetition = eArgRepeatPairPlus; | |
| // There are two required arguments that must always occur together, i.e. | |
| // an argument "pair". Because they must always occur together, they are | |
| // treated as two variants of one argument rather than two independent | |
| // arguments. Push them both into the first argument position for | |
| // m_arguments... | |
| arg.push_back(old_prefix_arg); | |
| arg.push_back(new_prefix_arg); | |
| m_arguments.push_back(arg); | |
| } | |
| ~CommandObjectTargetModulesSearchPathsAdd() override = default; | |
| protected: | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = &GetSelectedTarget(); | |
| const size_t argc = command.GetArgumentCount(); | |
| if (argc & 1) { | |
| result.AppendError("add requires an even number of arguments\n"); | |
| result.SetStatus(eReturnStatusFailed); | |
| } 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 = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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( | |
| ConstString(from), ConstString(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"); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| } | |
| } | |
| return result.Succeeded(); | |
| } | |
| }; | |
| #pragma mark CommandObjectTargetModulesSearchPathsClear | |
| class CommandObjectTargetModulesSearchPathsClear : public CommandObjectParsed { | |
| public: | |
| CommandObjectTargetModulesSearchPathsClear(CommandInterpreter &interpreter) | |
| : CommandObjectParsed(interpreter, "target modules search-paths clear", | |
| "Clear all current image search path substitution " | |
| "pairs from the current target.", | |
| "target modules search-paths clear", | |
| eCommandRequiresTarget) {} | |
| ~CommandObjectTargetModulesSearchPathsClear() override = default; | |
| protected: | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = &GetSelectedTarget(); | |
| bool notify = true; | |
| target->GetImageSearchPathList().Clear(notify); | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| return result.Succeeded(); | |
| } | |
| }; | |
| #pragma mark CommandObjectTargetModulesSearchPathsInsert | |
| class CommandObjectTargetModulesSearchPathsInsert : public CommandObjectParsed { | |
| public: | |
| CommandObjectTargetModulesSearchPathsInsert(CommandInterpreter &interpreter) | |
| : CommandObjectParsed(interpreter, "target modules search-paths insert", | |
| "Insert a new image search path substitution pair " | |
| "into the current target at the specified index.", | |
| nullptr, eCommandRequiresTarget) { | |
| CommandArgumentEntry arg1; | |
| CommandArgumentEntry arg2; | |
| CommandArgumentData index_arg; | |
| CommandArgumentData old_prefix_arg; | |
| CommandArgumentData new_prefix_arg; | |
| // Define the first and only variant of this arg. | |
| index_arg.arg_type = eArgTypeIndex; | |
| index_arg.arg_repetition = eArgRepeatPlain; | |
| // Put the one and only variant into the first arg for m_arguments: | |
| arg1.push_back(index_arg); | |
| // Define the first variant of this arg pair. | |
| old_prefix_arg.arg_type = eArgTypeOldPathPrefix; | |
| old_prefix_arg.arg_repetition = eArgRepeatPairPlus; | |
| // Define the first variant of this arg pair. | |
| new_prefix_arg.arg_type = eArgTypeNewPathPrefix; | |
| new_prefix_arg.arg_repetition = eArgRepeatPairPlus; | |
| // There are two required arguments that must always occur together, i.e. | |
| // an argument "pair". Because they must always occur together, they are | |
| // treated as two variants of one argument rather than two independent | |
| // arguments. Push them both into the same argument position for | |
| // m_arguments... | |
| arg2.push_back(old_prefix_arg); | |
| arg2.push_back(new_prefix_arg); | |
| // Add arguments to m_arguments. | |
| m_arguments.push_back(arg1); | |
| m_arguments.push_back(arg2); | |
| } | |
| ~CommandObjectTargetModulesSearchPathsInsert() override = default; | |
| protected: | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = &GetSelectedTarget(); | |
| size_t argc = command.GetArgumentCount(); | |
| // check for at least 3 arguments and an odd number of parameters | |
| if (argc >= 3 && argc & 1) { | |
| bool success = false; | |
| uint32_t insert_idx = StringConvert::ToUInt32( | |
| command.GetArgumentAtIndex(0), UINT32_MAX, 0, &success); | |
| if (!success) { | |
| result.AppendErrorWithFormat( | |
| "<index> parameter is not an integer: '%s'.\n", | |
| command.GetArgumentAtIndex(0)); | |
| result.SetStatus(eReturnStatusFailed); | |
| return result.Succeeded(); | |
| } | |
| // shift off the index | |
| command.Shift(); | |
| argc = command.GetArgumentCount(); | |
| for (uint32_t i = 0; i < argc; i += 2, ++insert_idx) { | |
| const char *from = command.GetArgumentAtIndex(i); | |
| const char *to = command.GetArgumentAtIndex(i + 1); | |
| if (from[0] && to[0]) { | |
| bool last_pair = ((argc - i) == 2); | |
| target->GetImageSearchPathList().Insert( | |
| ConstString(from), ConstString(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"); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| } | |
| } else { | |
| result.AppendError("insert requires at least three arguments\n"); | |
| result.SetStatus(eReturnStatusFailed); | |
| return result.Succeeded(); | |
| } | |
| return result.Succeeded(); | |
| } | |
| }; | |
| #pragma mark CommandObjectTargetModulesSearchPathsList | |
| class CommandObjectTargetModulesSearchPathsList : public CommandObjectParsed { | |
| public: | |
| CommandObjectTargetModulesSearchPathsList(CommandInterpreter &interpreter) | |
| : CommandObjectParsed(interpreter, "target modules search-paths list", | |
| "List all current image search path substitution " | |
| "pairs in the current target.", | |
| "target modules search-paths list", | |
| eCommandRequiresTarget) {} | |
| ~CommandObjectTargetModulesSearchPathsList() override = default; | |
| protected: | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = &GetSelectedTarget(); | |
| if (command.GetArgumentCount() != 0) { | |
| result.AppendError("list takes no arguments\n"); | |
| result.SetStatus(eReturnStatusFailed); | |
| return result.Succeeded(); | |
| } | |
| target->GetImageSearchPathList().Dump(&result.GetOutputStream()); | |
| result.SetStatus(eReturnStatusSuccessFinishResult); | |
| return result.Succeeded(); | |
| } | |
| }; | |
| #pragma mark CommandObjectTargetModulesSearchPathsQuery | |
| class CommandObjectTargetModulesSearchPathsQuery : public CommandObjectParsed { | |
| public: | |
| CommandObjectTargetModulesSearchPathsQuery(CommandInterpreter &interpreter) | |
| : CommandObjectParsed( | |
| interpreter, "target modules search-paths query", | |
| "Transform a path using the first applicable image search path.", | |
| nullptr, eCommandRequiresTarget) { | |
| CommandArgumentEntry arg; | |
| CommandArgumentData path_arg; | |
| // Define the first (and only) variant of this arg. | |
| path_arg.arg_type = eArgTypeDirectoryName; | |
| path_arg.arg_repetition = eArgRepeatPlain; | |
| // There is only one variant this argument could be; put it into the | |
| // argument entry. | |
| arg.push_back(path_arg); | |
| // Push the data for the first argument into the m_arguments vector. | |
| m_arguments.push_back(arg); | |
| } | |
| ~CommandObjectTargetModulesSearchPathsQuery() override = default; | |
| protected: | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = &GetSelectedTarget(); | |
| if (command.GetArgumentCount() != 1) { | |
| result.AppendError("query requires one argument\n"); | |
| result.SetStatus(eReturnStatusFailed); | |
| return result.Succeeded(); | |
| } | |
| ConstString orig(command.GetArgumentAtIndex(0)); | |
| ConstString transformed; | |
| if (target->GetImageSearchPathList().RemapPath(orig, transformed)) | |
| result.GetOutputStream().Printf("%s\n", transformed.GetCString()); | |
| else | |
| result.GetOutputStream().Printf("%s\n", orig.GetCString()); | |
| result.SetStatus(eReturnStatusSuccessFinishResult); | |
| return result.Succeeded(); | |
| } | |
| }; | |
| // Static Helper functions | |
| static void DumpModuleArchitecture(Stream &strm, Module *module, | |
| bool full_triple, uint32_t width) { | |
| if (module) { | |
| StreamString arch_strm; | |
| if (full_triple) | |
| module->GetArchitecture().DumpTriple(arch_strm); | |
| else | |
| arch_strm.PutCString(module->GetArchitecture().GetArchitectureName()); | |
| std::string arch_str = arch_strm.GetString(); | |
| if (width) | |
| strm.Printf("%-*s", width, arch_str.c_str()); | |
| else | |
| strm.PutCString(arch_str); | |
| } | |
| } | |
| static void DumpModuleUUID(Stream &strm, Module *module) { | |
| if (module && module->GetUUID().IsValid()) | |
| module->GetUUID().Dump(&strm); | |
| else | |
| strm.PutCString(" "); | |
| } | |
| static uint32_t DumpCompileUnitLineTable(CommandInterpreter &interpreter, | |
| Stream &strm, Module *module, | |
| const FileSpec &file_spec, | |
| lldb::DescriptionLevel desc_level) { | |
| uint32_t num_matches = 0; | |
| if (module) { | |
| SymbolContextList sc_list; | |
| num_matches = module->ResolveSymbolContextsForFileSpec( | |
| file_spec, 0, false, eSymbolContextCompUnit, sc_list); | |
| for (uint32_t i = 0; i < num_matches; ++i) { | |
| SymbolContext sc; | |
| if (sc_list.GetContextAtIndex(i, sc)) { | |
| if (i > 0) | |
| strm << "\n\n"; | |
| strm << "Line table for " << *static_cast<FileSpec *>(sc.comp_unit) | |
| << " in `" << module->GetFileSpec().GetFilename() << "\n"; | |
| LineTable *line_table = sc.comp_unit->GetLineTable(); | |
| if (line_table) | |
| line_table->GetDescription( | |
| &strm, interpreter.GetExecutionContext().GetTargetPtr(), | |
| desc_level); | |
| else | |
| strm << "No line table"; | |
| } | |
| } | |
| } | |
| return num_matches; | |
| } | |
| static void DumpFullpath(Stream &strm, const FileSpec *file_spec_ptr, | |
| uint32_t width) { | |
| if (file_spec_ptr) { | |
| if (width > 0) { | |
| std::string fullpath = file_spec_ptr->GetPath(); | |
| strm.Printf("%-*s", width, fullpath.c_str()); | |
| return; | |
| } else { | |
| file_spec_ptr->Dump(&strm); | |
| 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) { | |
| size_t num_dumped = 0; | |
| std::lock_guard<std::recursive_mutex> guard(module_list.GetMutex()); | |
| const size_t num_modules = module_list.GetSize(); | |
| if (num_modules > 0) { | |
| strm.Printf("Dumping headers for %" PRIu64 " module(s).\n", | |
| static_cast<uint64_t>(num_modules)); | |
| strm.IndentMore(); | |
| for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) { | |
| Module *module = module_list.GetModulePointerAtIndexUnlocked(image_idx); | |
| if (module) { | |
| if (num_dumped++ > 0) { | |
| strm.EOL(); | |
| strm.EOL(); | |
| } | |
| ObjectFile *objfile = module->GetObjectFile(); | |
| if (objfile) | |
| objfile->Dump(&strm); | |
| else { | |
| strm.Format("No object file for module: {0:F}\n", | |
| module->GetFileSpec()); | |
| } | |
| } | |
| } | |
| strm.IndentLess(); | |
| } | |
| return num_dumped; | |
| } | |
| static void DumpModuleSymtab(CommandInterpreter &interpreter, Stream &strm, | |
| Module *module, SortOrder sort_order) { | |
| if (!module) | |
| return; | |
| if (Symtab *symtab = module->GetSymtab()) | |
| symtab->Dump(&strm, interpreter.GetExecutionContext().GetTargetPtr(), | |
| sort_order); | |
| } | |
| 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()); | |
| strm.IndentMore(); | |
| section_list->Dump(&strm, | |
| interpreter.GetExecutionContext().GetTargetPtr(), true, | |
| UINT32_MAX); | |
| strm.IndentLess(); | |
| } | |
| } | |
| } | |
| static bool DumpModuleSymbolFile(Stream &strm, Module *module) { | |
| if (module) { | |
| if (SymbolFile *symbol_file = module->GetSymbolFile(true)) { | |
| symbol_file->Dump(strm); | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| static void DumpAddress(ExecutionContextScope *exe_scope, | |
| const Address &so_addr, bool verbose, Stream &strm) { | |
| strm.IndentMore(); | |
| strm.Indent(" Address: "); | |
| so_addr.Dump(&strm, exe_scope, Address::DumpStyleModuleWithFileAddress); | |
| strm.PutCString(" ("); | |
| so_addr.Dump(&strm, exe_scope, Address::DumpStyleSectionNameOffset); | |
| strm.PutCString(")\n"); | |
| strm.Indent(" Summary: "); | |
| const uint32_t save_indent = strm.GetIndentLevel(); | |
| strm.SetIndentLevel(save_indent + 13); | |
| so_addr.Dump(&strm, exe_scope, Address::DumpStyleResolvedDescription); | |
| strm.SetIndentLevel(save_indent); | |
| // Print out detailed address information when verbose is enabled | |
| if (verbose) { | |
| strm.EOL(); | |
| so_addr.Dump(&strm, exe_scope, Address::DumpStyleDetailedSymbolContext); | |
| } | |
| 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) { | |
| 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, strm); | |
| // strm.IndentMore(); | |
| // strm.Indent (" Address: "); | |
| // so_addr.Dump (&strm, exe_scope, | |
| // Address::DumpStyleModuleWithFileAddress); | |
| // strm.PutCString (" ("); | |
| // so_addr.Dump (&strm, exe_scope, | |
| // Address::DumpStyleSectionNameOffset); | |
| // strm.PutCString (")\n"); | |
| // strm.Indent (" Summary: "); | |
| // const uint32_t save_indent = strm.GetIndentLevel (); | |
| // strm.SetIndentLevel (save_indent + 13); | |
| // so_addr.Dump (&strm, exe_scope, | |
| // Address::DumpStyleResolvedDescription); | |
| // strm.SetIndentLevel (save_indent); | |
| // // Print out detailed address information when verbose is enabled | |
| // if (verbose) | |
| // { | |
| // strm.EOL(); | |
| // so_addr.Dump (&strm, exe_scope, | |
| // Address::DumpStyleDetailedSymbolContext); | |
| // } | |
| // strm.IndentLess(); | |
| return true; | |
| } | |
| return false; | |
| } | |
| static uint32_t LookupSymbolInModule(CommandInterpreter &interpreter, | |
| Stream &strm, Module *module, | |
| const char *name, bool name_is_regex, | |
| bool verbose) { | |
| if (!module) | |
| return 0; | |
| Symtab *symtab = module->GetSymtab(); | |
| if (!symtab) | |
| return 0; | |
| SymbolContext sc; | |
| std::vector<uint32_t> match_indexes; | |
| ConstString symbol_name(name); | |
| uint32_t num_matches = 0; | |
| if (name_is_regex) { | |
| RegularExpression name_regexp(symbol_name.GetStringRef()); | |
| num_matches = symtab->AppendSymbolIndexesMatchingRegExAndType( | |
| name_regexp, eSymbolTypeAny, match_indexes); | |
| } else { | |
| num_matches = | |
| symtab->AppendSymbolIndexesWithName(symbol_name, match_indexes); | |
| } | |
| if (num_matches > 0) { | |
| strm.Indent(); | |
| strm.Printf("%u symbols match %s'%s' in ", num_matches, | |
| name_is_regex ? "the regular expression " : "", name); | |
| DumpFullpath(strm, &module->GetFileSpec(), 0); | |
| strm.PutCString(":\n"); | |
| strm.IndentMore(); | |
| for (uint32_t i = 0; i < num_matches; ++i) { | |
| Symbol *symbol = symtab->SymbolAtIndex(match_indexes[i]); | |
| if (symbol && symbol->ValueIsAddress()) { | |
| DumpAddress( | |
| interpreter.GetExecutionContext().GetBestExecutionContextScope(), | |
| symbol->GetAddressRef(), verbose, strm); | |
| } | |
| } | |
| strm.IndentLess(); | |
| } | |
| return num_matches; | |
| } | |
| static void DumpSymbolContextList(ExecutionContextScope *exe_scope, | |
| Stream &strm, SymbolContextList &sc_list, | |
| bool verbose) { | |
| strm.IndentMore(); | |
| const uint32_t num_matches = sc_list.GetSize(); | |
| for (uint32_t i = 0; i < num_matches; ++i) { | |
| SymbolContext sc; | |
| if (sc_list.GetContextAtIndex(i, sc)) { | |
| AddressRange range; | |
| sc.GetAddressRange(eSymbolContextEverything, 0, true, range); | |
| DumpAddress(exe_scope, range.GetBaseAddress(), verbose, strm); | |
| } | |
| } | |
| strm.IndentLess(); | |
| } | |
| static size_t LookupFunctionInModule(CommandInterpreter &interpreter, | |
| Stream &strm, Module *module, | |
| const char *name, bool name_is_regex, | |
| bool include_inlines, bool include_symbols, | |
| bool verbose) { | |
| 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, include_symbols, | |
| include_inlines, sc_list); | |
| } else { | |
| ConstString function_name(name); | |
| module->FindFunctions(function_name, nullptr, eFunctionNameTypeAuto, | |
| include_symbols, include_inlines, 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); | |
| } | |
| return num_matches; | |
| } | |
| return 0; | |
| } | |
| static size_t LookupTypeInModule(CommandInterpreter &interpreter, Stream &strm, | |
| Module *module, const char *name_cstr, | |
| bool name_is_regex) { | |
| TypeList type_list; | |
| if (module && name_cstr && name_cstr[0]) { | |
| const uint32_t max_num_matches = UINT32_MAX; | |
| size_t num_matches = 0; | |
| bool name_is_fully_qualified = false; | |
| ConstString name(name_cstr); | |
| llvm::DenseSet<lldb_private::SymbolFile *> searched_symbol_files; | |
| module->FindTypes(name, name_is_fully_qualified, max_num_matches, | |
| searched_symbol_files, type_list); | |
| if (type_list.Empty()) | |
| return 0; | |
| 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"); | |
| 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); | |
| // 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); | |
| typedef_type_sp = typedefed_type_sp; | |
| typedefed_type_sp = typedef_type_sp->GetTypedefType(); | |
| } | |
| } | |
| strm.EOL(); | |
| } | |
| return type_list.GetSize(); | |
| } | |
| static size_t LookupTypeHere(CommandInterpreter &interpreter, Stream &strm, | |
| Module &module, const char *name_cstr, | |
| bool name_is_regex) { | |
| TypeList type_list; | |
| const uint32_t max_num_matches = UINT32_MAX; | |
| bool name_is_fully_qualified = false; | |
| ConstString name(name_cstr); | |
| llvm::DenseSet<SymbolFile *> searched_symbol_files; | |
| module.FindTypes(name, name_is_fully_qualified, max_num_matches, | |
| searched_symbol_files, type_list); | |
| if (type_list.Empty()) | |
| return 0; | |
| strm.Indent(); | |
| strm.PutCString("Best match found in "); | |
| DumpFullpath(strm, &module.GetFileSpec(), 0); | |
| strm.PutCString(":\n"); | |
| TypeSP type_sp(type_list.GetTypeAtIndex(0)); | |
| if (type_sp) { | |
| // Resolve the clang type so that any forward references to types that | |
| // haven't yet been parsed will get parsed. | |
| type_sp->GetFullCompilerType(); | |
| type_sp->GetDescription(&strm, eDescriptionLevelFull, true); | |
| // 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); | |
| 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) { | |
| 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); | |
| return num_matches; | |
| } | |
| } | |
| return 0; | |
| } | |
| static size_t FindModulesByName(Target *target, const char *module_name, | |
| ModuleList &module_list, | |
| bool check_global_list) { | |
| FileSpec module_file_spec(module_name); | |
| ModuleSpec module_spec(module_file_spec); | |
| const size_t initial_size = module_list.GetSize(); | |
| if (check_global_list) { | |
| // Check the global list | |
| std::lock_guard<std::recursive_mutex> guard( | |
| Module::GetAllocationModuleCollectionMutex()); | |
| const size_t num_modules = Module::GetNumberAllocatedModules(); | |
| ModuleSP module_sp; | |
| for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) { | |
| Module *module = Module::GetAllocatedModuleAtIndex(image_idx); | |
| if (module) { | |
| if (module->MatchesModuleSpec(module_spec)) { | |
| module_sp = module->shared_from_this(); | |
| module_list.AppendIfNeeded(module_sp); | |
| } | |
| } | |
| } | |
| } else { | |
| if (target) { | |
| target->GetImages().FindModules(module_spec, module_list); | |
| const size_t num_matches = module_list.GetSize(); | |
| // Not found in our module list for our target, check the main shared | |
| // module list in case it is a extra file used somewhere else | |
| if (num_matches == 0) { | |
| module_spec.GetArchitecture() = target->GetArchitecture(); | |
| ModuleList::FindSharedModules(module_spec, module_list); | |
| } | |
| } else { | |
| ModuleList::FindSharedModules(module_spec, module_list); | |
| } | |
| } | |
| return module_list.GetSize() - initial_size; | |
| } | |
| #pragma mark CommandObjectTargetModulesModuleAutoComplete | |
| // A base command object class that can auto complete with module file | |
| // paths | |
| class CommandObjectTargetModulesModuleAutoComplete | |
| : public CommandObjectParsed { | |
| public: | |
| CommandObjectTargetModulesModuleAutoComplete(CommandInterpreter &interpreter, | |
| const char *name, | |
| const char *help, | |
| const char *syntax, | |
| uint32_t flags = 0) | |
| : CommandObjectParsed(interpreter, name, help, syntax, flags) { | |
| CommandArgumentEntry arg; | |
| CommandArgumentData file_arg; | |
| // Define the first (and only) variant of this arg. | |
| file_arg.arg_type = eArgTypeFilename; | |
| file_arg.arg_repetition = eArgRepeatStar; | |
| // There is only one variant this argument could be; put it into the | |
| // argument entry. | |
| arg.push_back(file_arg); | |
| // Push the data for the first argument into the m_arguments vector. | |
| m_arguments.push_back(arg); | |
| } | |
| ~CommandObjectTargetModulesModuleAutoComplete() override = default; | |
| void | |
| HandleArgumentCompletion(CompletionRequest &request, | |
| OptionElementVector &opt_element_vector) override { | |
| CommandCompletions::InvokeCommonCompletionCallbacks( | |
| GetCommandInterpreter(), CommandCompletions::eModuleCompletion, request, | |
| nullptr); | |
| } | |
| }; | |
| #pragma mark CommandObjectTargetModulesSourceFileAutoComplete | |
| // A base command object class that can auto complete with module source | |
| // file paths | |
| class CommandObjectTargetModulesSourceFileAutoComplete | |
| : public CommandObjectParsed { | |
| public: | |
| CommandObjectTargetModulesSourceFileAutoComplete( | |
| CommandInterpreter &interpreter, const char *name, const char *help, | |
| const char *syntax, uint32_t flags) | |
| : CommandObjectParsed(interpreter, name, help, syntax, flags) { | |
| CommandArgumentEntry arg; | |
| CommandArgumentData source_file_arg; | |
| // Define the first (and only) variant of this arg. | |
| source_file_arg.arg_type = eArgTypeSourceFile; | |
| source_file_arg.arg_repetition = eArgRepeatPlus; | |
| // There is only one variant this argument could be; put it into the | |
| // argument entry. | |
| arg.push_back(source_file_arg); | |
| // Push the data for the first argument into the m_arguments vector. | |
| m_arguments.push_back(arg); | |
| } | |
| ~CommandObjectTargetModulesSourceFileAutoComplete() override = default; | |
| void | |
| HandleArgumentCompletion(CompletionRequest &request, | |
| OptionElementVector &opt_element_vector) override { | |
| CommandCompletions::InvokeCommonCompletionCallbacks( | |
| GetCommandInterpreter(), CommandCompletions::eSourceFileCompletion, | |
| request, nullptr); | |
| } | |
| }; | |
| #pragma mark CommandObjectTargetModulesDumpObjfile | |
| class CommandObjectTargetModulesDumpObjfile | |
| : public CommandObjectTargetModulesModuleAutoComplete { | |
| public: | |
| CommandObjectTargetModulesDumpObjfile(CommandInterpreter &interpreter) | |
| : CommandObjectTargetModulesModuleAutoComplete( | |
| interpreter, "target modules dump objfile", | |
| "Dump the object file headers from one or more target modules.", | |
| nullptr, eCommandRequiresTarget) {} | |
| ~CommandObjectTargetModulesDumpObjfile() override = default; | |
| protected: | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = &GetSelectedTarget(); | |
| uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); | |
| result.GetOutputStream().SetAddressByteSize(addr_byte_size); | |
| result.GetErrorStream().SetAddressByteSize(addr_byte_size); | |
| size_t num_dumped = 0; | |
| if (command.GetArgumentCount() == 0) { | |
| // Dump all headers for all modules images | |
| num_dumped = DumpModuleObjfileHeaders(result.GetOutputStream(), | |
| target->GetImages()); | |
| if (num_dumped == 0) { | |
| result.AppendError("the target has no associated executable images"); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| } 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"); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| return result.Succeeded(); | |
| } | |
| }; | |
| #pragma mark CommandObjectTargetModulesDumpSymtab | |
| static constexpr OptionEnumValueElement g_sort_option_enumeration[] = { | |
| { | |
| eSortOrderNone, | |
| "none", | |
| "No sorting, use the original symbol table order.", | |
| }, | |
| { | |
| eSortOrderByAddress, | |
| "address", | |
| "Sort output by symbol address.", | |
| }, | |
| { | |
| eSortOrderByName, | |
| "name", | |
| "Sort output by symbol name.", | |
| }, | |
| }; | |
| #define LLDB_OPTIONS_target_modules_dump_symtab | |
| #include "CommandOptions.inc" | |
| class CommandObjectTargetModulesDumpSymtab | |
| : public CommandObjectTargetModulesModuleAutoComplete { | |
| public: | |
| CommandObjectTargetModulesDumpSymtab(CommandInterpreter &interpreter) | |
| : CommandObjectTargetModulesModuleAutoComplete( | |
| interpreter, "target modules dump symtab", | |
| "Dump the symbol table from one or more target modules.", nullptr, | |
| eCommandRequiresTarget), | |
| m_options() {} | |
| ~CommandObjectTargetModulesDumpSymtab() override = default; | |
| Options *GetOptions() override { return &m_options; } | |
| class CommandOptions : public Options { | |
| public: | |
| CommandOptions() : Options(), m_sort_order(eSortOrderNone) {} | |
| ~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': | |
| 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; | |
| } | |
| llvm::ArrayRef<OptionDefinition> GetDefinitions() override { | |
| return llvm::makeArrayRef(g_target_modules_dump_symtab_options); | |
| } | |
| SortOrder m_sort_order; | |
| }; | |
| protected: | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = &GetSelectedTarget(); | |
| uint32_t num_dumped = 0; | |
| uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); | |
| result.GetOutputStream().SetAddressByteSize(addr_byte_size); | |
| result.GetErrorStream().SetAddressByteSize(addr_byte_size); | |
| if (command.GetArgumentCount() == 0) { | |
| // Dump all sections for all modules images | |
| std::lock_guard<std::recursive_mutex> guard( | |
| target->GetImages().GetMutex()); | |
| const size_t num_modules = target->GetImages().GetSize(); | |
| if (num_modules > 0) { | |
| result.GetOutputStream().Printf("Dumping symbol table for %" PRIu64 | |
| " modules.\n", | |
| (uint64_t)num_modules); | |
| for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) { | |
| if (num_dumped > 0) { | |
| result.GetOutputStream().EOL(); | |
| result.GetOutputStream().EOL(); | |
| } | |
| if (m_interpreter.WasInterrupted()) | |
| break; | |
| num_dumped++; | |
| DumpModuleSymtab( | |
| m_interpreter, result.GetOutputStream(), | |
| target->GetImages().GetModulePointerAtIndexUnlocked(image_idx), | |
| m_options.m_sort_order); | |
| } | |
| } else { | |
| result.AppendError("the target has no associated executable images"); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| } else { | |
| // Dump specified images (by basename or fullpath) | |
| const char *arg_cstr; | |
| for (int arg_idx = 0; | |
| (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr; | |
| ++arg_idx) { | |
| ModuleList module_list; | |
| const size_t num_matches = | |
| FindModulesByName(target, arg_cstr, module_list, true); | |
| if (num_matches > 0) { | |
| for (size_t i = 0; i < num_matches; ++i) { | |
| Module *module = module_list.GetModulePointerAtIndex(i); | |
| if (module) { | |
| if (num_dumped > 0) { | |
| result.GetOutputStream().EOL(); | |
| result.GetOutputStream().EOL(); | |
| } | |
| if (m_interpreter.WasInterrupted()) | |
| break; | |
| num_dumped++; | |
| DumpModuleSymtab(m_interpreter, result.GetOutputStream(), module, | |
| m_options.m_sort_order); | |
| } | |
| } | |
| } 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"); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| return result.Succeeded(); | |
| } | |
| CommandOptions m_options; | |
| }; | |
| #pragma mark CommandObjectTargetModulesDumpSections | |
| // Image section dumping command | |
| class CommandObjectTargetModulesDumpSections | |
| : public CommandObjectTargetModulesModuleAutoComplete { | |
| public: | |
| CommandObjectTargetModulesDumpSections(CommandInterpreter &interpreter) | |
| : CommandObjectTargetModulesModuleAutoComplete( | |
| interpreter, "target modules dump sections", | |
| "Dump the sections from one or more target modules.", | |
| //"target modules dump sections [<file1> ...]") | |
| nullptr, eCommandRequiresTarget) {} | |
| ~CommandObjectTargetModulesDumpSections() override = default; | |
| protected: | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = &GetSelectedTarget(); | |
| uint32_t num_dumped = 0; | |
| uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); | |
| result.GetOutputStream().SetAddressByteSize(addr_byte_size); | |
| result.GetErrorStream().SetAddressByteSize(addr_byte_size); | |
| if (command.GetArgumentCount() == 0) { | |
| // Dump all sections for all modules images | |
| const size_t num_modules = target->GetImages().GetSize(); | |
| if (num_modules > 0) { | |
| result.GetOutputStream().Printf("Dumping sections for %" PRIu64 | |
| " modules.\n", | |
| (uint64_t)num_modules); | |
| for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) { | |
| if (m_interpreter.WasInterrupted()) | |
| break; | |
| num_dumped++; | |
| DumpModuleSections( | |
| m_interpreter, result.GetOutputStream(), | |
| target->GetImages().GetModulePointerAtIndex(image_idx)); | |
| } | |
| } else { | |
| result.AppendError("the target has no associated executable images"); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| } else { | |
| // Dump specified images (by basename or fullpath) | |
| const char *arg_cstr; | |
| for (int arg_idx = 0; | |
| (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr; | |
| ++arg_idx) { | |
| ModuleList module_list; | |
| const size_t num_matches = | |
| FindModulesByName(target, arg_cstr, module_list, true); | |
| if (num_matches > 0) { | |
| for (size_t i = 0; i < num_matches; ++i) { | |
| if (m_interpreter.WasInterrupted()) | |
| break; | |
| Module *module = module_list.GetModulePointerAtIndex(i); | |
| if (module) { | |
| num_dumped++; | |
| DumpModuleSections(m_interpreter, result.GetOutputStream(), | |
| module); | |
| } | |
| } | |
| } else { | |
| // Check the global list | |
| std::lock_guard<std::recursive_mutex> guard( | |
| Module::GetAllocationModuleCollectionMutex()); | |
| result.AppendWarningWithFormat( | |
| "Unable to find an image that matches '%s'.\n", arg_cstr); | |
| } | |
| } | |
| } | |
| if (num_dumped > 0) | |
| result.SetStatus(eReturnStatusSuccessFinishResult); | |
| else { | |
| result.AppendError("no matching executable images found"); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| return result.Succeeded(); | |
| } | |
| }; | |
| #pragma mark CommandObjectTargetModulesDumpSections | |
| // Clang AST dumping command | |
| class CommandObjectTargetModulesDumpClangAST | |
| : public CommandObjectTargetModulesModuleAutoComplete { | |
| public: | |
| CommandObjectTargetModulesDumpClangAST(CommandInterpreter &interpreter) | |
| : CommandObjectTargetModulesModuleAutoComplete( | |
| interpreter, "target modules dump ast", | |
| "Dump the clang ast for a given module's symbol file.", | |
| //"target modules dump ast [<file1> ...]") | |
| nullptr, eCommandRequiresTarget) {} | |
| ~CommandObjectTargetModulesDumpClangAST() override = default; | |
| protected: | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = &GetSelectedTarget(); | |
| const size_t num_modules = target->GetImages().GetSize(); | |
| if (num_modules == 0) { | |
| result.AppendError("the target has no associated executable images"); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| if (command.GetArgumentCount() == 0) { | |
| // Dump all ASTs for all modules images | |
| result.GetOutputStream().Printf("Dumping clang ast for %" PRIu64 | |
| " modules.\n", | |
| (uint64_t)num_modules); | |
| for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) { | |
| if (m_interpreter.WasInterrupted()) | |
| break; | |
| Module *m = target->GetImages().GetModulePointerAtIndex(image_idx); | |
| if (SymbolFile *sf = m->GetSymbolFile()) | |
| sf->DumpClangAST(result.GetOutputStream()); | |
| } | |
| result.SetStatus(eReturnStatusSuccessFinishResult); | |
| return true; | |
| } | |
| // Dump specified ASTs (by basename or fullpath) | |
| for (const Args::ArgEntry &arg : command.entries()) { | |
| ModuleList module_list; | |
| const size_t num_matches = | |
| FindModulesByName(target, arg.c_str(), module_list, true); | |
| if (num_matches == 0) { | |
| // Check the global list | |
| std::lock_guard<std::recursive_mutex> guard( | |
| Module::GetAllocationModuleCollectionMutex()); | |
| result.AppendWarningWithFormat( | |
| "Unable to find an image that matches '%s'.\n", arg.c_str()); | |
| continue; | |
| } | |
| for (size_t i = 0; i < num_matches; ++i) { | |
| if (m_interpreter.WasInterrupted()) | |
| break; | |
| Module *m = module_list.GetModulePointerAtIndex(i); | |
| if (SymbolFile *sf = m->GetSymbolFile()) | |
| sf->DumpClangAST(result.GetOutputStream()); | |
| } | |
| } | |
| result.SetStatus(eReturnStatusSuccessFinishResult); | |
| return true; | |
| } | |
| }; | |
| #pragma mark CommandObjectTargetModulesDumpSymfile | |
| // Image debug symbol dumping command | |
| class CommandObjectTargetModulesDumpSymfile | |
| : public CommandObjectTargetModulesModuleAutoComplete { | |
| public: | |
| CommandObjectTargetModulesDumpSymfile(CommandInterpreter &interpreter) | |
| : CommandObjectTargetModulesModuleAutoComplete( | |
| interpreter, "target modules dump symfile", | |
| "Dump the debug symbol file for one or more target modules.", | |
| //"target modules dump symfile [<file1> ...]") | |
| nullptr, eCommandRequiresTarget) {} | |
| ~CommandObjectTargetModulesDumpSymfile() override = default; | |
| protected: | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = &GetSelectedTarget(); | |
| uint32_t num_dumped = 0; | |
| uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); | |
| result.GetOutputStream().SetAddressByteSize(addr_byte_size); | |
| result.GetErrorStream().SetAddressByteSize(addr_byte_size); | |
| if (command.GetArgumentCount() == 0) { | |
| // Dump all sections for all modules images | |
| const ModuleList &target_modules = target->GetImages(); | |
| std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex()); | |
| const size_t num_modules = target_modules.GetSize(); | |
| if (num_modules > 0) { | |
| result.GetOutputStream().Printf("Dumping debug symbols for %" PRIu64 | |
| " modules.\n", | |
| (uint64_t)num_modules); | |
| for (uint32_t image_idx = 0; image_idx < num_modules; ++image_idx) { | |
| if (m_interpreter.WasInterrupted()) | |
| break; | |
| if (DumpModuleSymbolFile( | |
| result.GetOutputStream(), | |
| target_modules.GetModulePointerAtIndexUnlocked(image_idx))) | |
| num_dumped++; | |
| } | |
| } else { | |
| result.AppendError("the target has no associated executable images"); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| } else { | |
| // Dump specified images (by basename or fullpath) | |
| const char *arg_cstr; | |
| for (int arg_idx = 0; | |
| (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr; | |
| ++arg_idx) { | |
| ModuleList module_list; | |
| const size_t num_matches = | |
| FindModulesByName(target, arg_cstr, module_list, true); | |
| if (num_matches > 0) { | |
| for (size_t i = 0; i < num_matches; ++i) { | |
| if (m_interpreter.WasInterrupted()) | |
| break; | |
| Module *module = module_list.GetModulePointerAtIndex(i); | |
| if (module) { | |
| if (DumpModuleSymbolFile(result.GetOutputStream(), module)) | |
| num_dumped++; | |
| } | |
| } | |
| } else | |
| result.AppendWarningWithFormat( | |
| "Unable to find an image that matches '%s'.\n", arg_cstr); | |
| } | |
| } | |
| if (num_dumped > 0) | |
| result.SetStatus(eReturnStatusSuccessFinishResult); | |
| else { | |
| result.AppendError("no matching executable images found"); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| return result.Succeeded(); | |
| } | |
| }; | |
| #pragma mark CommandObjectTargetModulesDumpLineTable | |
| #define LLDB_OPTIONS_target_modules_dump | |
| #include "CommandOptions.inc" | |
| // Image debug line table dumping command | |
| class CommandObjectTargetModulesDumpLineTable | |
| : public CommandObjectTargetModulesSourceFileAutoComplete { | |
| public: | |
| CommandObjectTargetModulesDumpLineTable(CommandInterpreter &interpreter) | |
| : CommandObjectTargetModulesSourceFileAutoComplete( | |
| interpreter, "target modules dump line-table", | |
| "Dump the line table for one or more compilation units.", nullptr, | |
| eCommandRequiresTarget) {} | |
| ~CommandObjectTargetModulesDumpLineTable() override = default; | |
| Options *GetOptions() override { return &m_options; } | |
| protected: | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = m_exe_ctx.GetTargetPtr(); | |
| uint32_t total_num_dumped = 0; | |
| uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); | |
| result.GetOutputStream().SetAddressByteSize(addr_byte_size); | |
| result.GetErrorStream().SetAddressByteSize(addr_byte_size); | |
| if (command.GetArgumentCount() == 0) { | |
| result.AppendError("file option must be specified."); | |
| result.SetStatus(eReturnStatusFailed); | |
| return result.Succeeded(); | |
| } else { | |
| // Dump specified images (by basename or fullpath) | |
| const char *arg_cstr; | |
| for (int arg_idx = 0; | |
| (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr; | |
| ++arg_idx) { | |
| FileSpec file_spec(arg_cstr); | |
| const ModuleList &target_modules = target->GetImages(); | |
| std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex()); | |
| const size_t num_modules = target_modules.GetSize(); | |
| if (num_modules > 0) { | |
| uint32_t num_dumped = 0; | |
| for (uint32_t i = 0; i < num_modules; ++i) { | |
| if (m_interpreter.WasInterrupted()) | |
| break; | |
| if (DumpCompileUnitLineTable( | |
| m_interpreter, result.GetOutputStream(), | |
| target_modules.GetModulePointerAtIndexUnlocked(i), | |
| 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"); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| return result.Succeeded(); | |
| } | |
| class CommandOptions : public Options { | |
| public: | |
| CommandOptions() : Options() { OptionParsingStarting(nullptr); } | |
| Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, | |
| ExecutionContext *execution_context) override { | |
| assert(option_idx == 0 && "We only have one option."); | |
| m_verbose = true; | |
| return Status(); | |
| } | |
| void OptionParsingStarting(ExecutionContext *execution_context) override { | |
| m_verbose = false; | |
| } | |
| llvm::ArrayRef<OptionDefinition> GetDefinitions() override { | |
| return llvm::makeArrayRef(g_target_modules_dump_options); | |
| } | |
| bool m_verbose; | |
| }; | |
| CommandOptions m_options; | |
| }; | |
| #pragma mark CommandObjectTargetModulesDump | |
| // Dump multi-word command for target modules | |
| class CommandObjectTargetModulesDump : public CommandObjectMultiword { | |
| public: | |
| // Constructors and Destructors | |
| CommandObjectTargetModulesDump(CommandInterpreter &interpreter) | |
| : CommandObjectMultiword( | |
| interpreter, "target modules dump", | |
| "Commands for dumping information about one or " | |
| "more target modules.", | |
| "target modules dump " | |
| "[headers|symtab|sections|ast|symfile|line-table] " | |
| "[<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))); | |
| } | |
| ~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_option_group(), 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(); | |
| } | |
| ~CommandObjectTargetModulesAdd() override = default; | |
| Options *GetOptions() override { return &m_option_group; } | |
| void | |
| HandleArgumentCompletion(CompletionRequest &request, | |
| OptionElementVector &opt_element_vector) override { | |
| CommandCompletions::InvokeCommonCompletionCallbacks( | |
| GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion, | |
| request, nullptr); | |
| } | |
| protected: | |
| OptionGroupOptions m_option_group; | |
| OptionGroupUUID m_uuid_option_group; | |
| OptionGroupFile m_symbol_file; | |
| bool DoExecute(Args &args, CommandReturnObject &result) override { | |
| Target *target = &GetSelectedTarget(); | |
| bool flush = false; | |
| const size_t argc = args.GetArgumentCount(); | |
| if (argc == 0) { | |
| if (m_uuid_option_group.GetOptionValue().OptionWasSet()) { | |
| // We are given a UUID only, go locate the file | |
| ModuleSpec module_spec; | |
| module_spec.GetUUID() = | |
| m_uuid_option_group.GetOptionValue().GetCurrentValue(); | |
| if (m_symbol_file.GetOptionValue().OptionWasSet()) | |
| module_spec.GetSymbolFileSpec() = | |
| m_symbol_file.GetOptionValue().GetCurrentValue(); | |
| if (Symbols::DownloadObjectAndSymbolFile(module_spec)) { | |
| ModuleSP module_sp( | |
| target->GetOrCreateModule(module_spec, true /* notify */)); | |
| if (module_sp) { | |
| result.SetStatus(eReturnStatusSuccessFinishResult); | |
| return true; | |
| } else { | |
| StreamString strm; | |
| module_spec.GetUUID().Dump(&strm); | |
| if (module_spec.GetFileSpec()) { | |
| if (module_spec.GetSymbolFileSpec()) { | |
| result.AppendErrorWithFormat( | |
| "Unable to create the executable or symbol file with " | |
| "UUID %s with path %s and symbol file %s", | |
| strm.GetData(), module_spec.GetFileSpec().GetPath().c_str(), | |
| module_spec.GetSymbolFileSpec().GetPath().c_str()); | |
| } else { | |
| result.AppendErrorWithFormat( | |
| "Unable to create the executable or symbol file with " | |
| "UUID %s with path %s", | |
| strm.GetData(), | |
| module_spec.GetFileSpec().GetPath().c_str()); | |
| } | |
| } else { | |
| result.AppendErrorWithFormat("Unable to create the executable " | |
| "or symbol file with UUID %s", | |
| strm.GetData()); | |
| } | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| } else { | |
| StreamString strm; | |
| module_spec.GetUUID().Dump(&strm); | |
| result.AppendErrorWithFormat( | |
| "Unable to locate the executable or symbol file with UUID %s", | |
| strm.GetData()); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| } else { | |
| result.AppendError( | |
| "one or more executable image paths must be specified"); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| } else { | |
| for (auto &entry : args.entries()) { | |
| if (entry.ref().empty()) | |
| continue; | |
| FileSpec file_spec(entry.ref()); | |
| if (FileSystem::Instance().Exists(file_spec)) { | |
| ModuleSpec module_spec(file_spec); | |
| if (m_uuid_option_group.GetOptionValue().OptionWasSet()) | |
| module_spec.GetUUID() = | |
| m_uuid_option_group.GetOptionValue().GetCurrentValue(); | |
| if (m_symbol_file.GetOptionValue().OptionWasSet()) | |
| module_spec.GetSymbolFileSpec() = | |
| m_symbol_file.GetOptionValue().GetCurrentValue(); | |
| if (!module_spec.GetArchitecture().IsValid()) | |
| module_spec.GetArchitecture() = target->GetArchitecture(); | |
| Status error; | |
| ModuleSP module_sp(target->GetOrCreateModule( | |
| module_spec, true /* notify */, &error)); | |
| if (!module_sp) { | |
| const char *error_cstr = error.AsCString(); | |
| if (error_cstr) | |
| result.AppendError(error_cstr); | |
| else | |
| result.AppendErrorWithFormat("unsupported module: %s", | |
| entry.c_str()); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } else { | |
| flush = true; | |
| } | |
| result.SetStatus(eReturnStatusSuccessFinishResult); | |
| } else { | |
| std::string resolved_path = file_spec.GetPath(); | |
| result.SetStatus(eReturnStatusFailed); | |
| if (resolved_path != entry.ref()) { | |
| result.AppendErrorWithFormat( | |
| "invalid module path '%s' with resolved path '%s'\n", | |
| entry.ref().str().c_str(), resolved_path.c_str()); | |
| break; | |
| } | |
| result.AppendErrorWithFormat("invalid module path '%s'\n", | |
| entry.c_str()); | |
| break; | |
| } | |
| } | |
| } | |
| if (flush) { | |
| ProcessSP process = target->GetProcessSP(); | |
| if (process) | |
| process->Flush(); | |
| } | |
| return result.Succeeded(); | |
| } | |
| }; | |
| class CommandObjectTargetModulesLoad | |
| : public CommandObjectTargetModulesModuleAutoComplete { | |
| public: | |
| CommandObjectTargetModulesLoad(CommandInterpreter &interpreter) | |
| : CommandObjectTargetModulesModuleAutoComplete( | |
| interpreter, "target modules load", | |
| "Set the load addresses for one or more sections in a target " | |
| "module.", | |
| "target modules load [--file <module> --uuid <uuid>] <sect-name> " | |
| "<address> [<sect-name> <address> ....]", | |
| eCommandRequiresTarget), | |
| m_option_group(), | |
| m_file_option(LLDB_OPT_SET_1, false, "file", 'f', 0, eArgTypeName, | |
| "Fullpath or basename for module to load.", ""), | |
| m_load_option(LLDB_OPT_SET_1, false, "load", 'l', | |
| "Write file contents to the memory.", false, true), | |
| m_pc_option(LLDB_OPT_SET_1, false, "set-pc-to-entry", 'p', | |
| "Set PC to the entry point." | |
| " Only applicable with '--load' option.", | |
| false, true), | |
| m_slide_option(LLDB_OPT_SET_1, false, "slide", 's', 0, eArgTypeOffset, | |
| "Set the load address for all sections to be the " | |
| "virtual address in the file plus the offset.", | |
| 0) { | |
| m_option_group.Append(&m_uuid_option_group, LLDB_OPT_SET_ALL, | |
| LLDB_OPT_SET_1); | |
| m_option_group.Append(&m_file_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); | |
| m_option_group.Append(&m_load_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); | |
| m_option_group.Append(&m_pc_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); | |
| m_option_group.Append(&m_slide_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); | |
| m_option_group.Finalize(); | |
| } | |
| ~CommandObjectTargetModulesLoad() override = default; | |
| Options *GetOptions() override { return &m_option_group; } | |
| protected: | |
| bool DoExecute(Args &args, CommandReturnObject &result) override { | |
| Target *target = &GetSelectedTarget(); | |
| const bool load = m_load_option.GetOptionValue().GetCurrentValue(); | |
| const bool set_pc = m_pc_option.GetOptionValue().GetCurrentValue(); | |
| const size_t argc = args.GetArgumentCount(); | |
| ModuleSpec module_spec; | |
| bool search_using_module_spec = false; | |
| // Allow "load" option to work without --file or --uuid option. | |
| if (load) { | |
| if (!m_file_option.GetOptionValue().OptionWasSet() && | |
| !m_uuid_option_group.GetOptionValue().OptionWasSet()) { | |
| ModuleList &module_list = target->GetImages(); | |
| if (module_list.GetSize() == 1) { | |
| search_using_module_spec = true; | |
| module_spec.GetFileSpec() = | |
| module_list.GetModuleAtIndex(0)->GetFileSpec(); | |
| } | |
| } | |
| } | |
| if (m_file_option.GetOptionValue().OptionWasSet()) { | |
| search_using_module_spec = true; | |
| const char *arg_cstr = m_file_option.GetOptionValue().GetCurrentValue(); | |
| const bool use_global_module_list = true; | |
| ModuleList module_list; | |
| const size_t num_matches = FindModulesByName( | |
| target, arg_cstr, module_list, use_global_module_list); | |
| if (num_matches == 1) { | |
| module_spec.GetFileSpec() = | |
| module_list.GetModuleAtIndex(0)->GetFileSpec(); | |
| } else if (num_matches > 1) { | |
| search_using_module_spec = false; | |
| result.AppendErrorWithFormat( | |
| "more than 1 module matched by name '%s'\n", arg_cstr); | |
| result.SetStatus(eReturnStatusFailed); | |
| } else { | |
| search_using_module_spec = false; | |
| result.AppendErrorWithFormat("no object file for module '%s'\n", | |
| arg_cstr); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| } | |
| 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"); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| } else { | |
| if (m_slide_option.GetOptionValue().OptionWasSet()) { | |
| result.AppendError("The \"--slide <offset>\" option can't " | |
| "be used in conjunction with setting " | |
| "section load addresses.\n"); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| for (size_t i = 0; i < argc; i += 2) { | |
| const char *sect_name = args.GetArgumentAtIndex(i); | |
| const char *load_addr_cstr = args.GetArgumentAtIndex(i + 1); | |
| if (sect_name && load_addr_cstr) { | |
| ConstString const_sect_name(sect_name); | |
| bool success = false; | |
| addr_t load_addr = StringConvert::ToUInt64( | |
| load_addr_cstr, LLDB_INVALID_ADDRESS, 0, &success); | |
| if (success) { | |
| 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); | |
| result.SetStatus(eReturnStatusFailed); | |
| 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); | |
| result.SetStatus(eReturnStatusFailed); | |
| break; | |
| } | |
| } else { | |
| result.AppendErrorWithFormat( | |
| "invalid load address string '%s'\n", load_addr_cstr); | |
| result.SetStatus(eReturnStatusFailed); | |
| 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"); | |
| result.SetStatus(eReturnStatusFailed); | |
| break; | |
| } | |
| } | |
| } | |
| if (changed) { | |
| target->ModulesDidLoad(matching_modules); | |
| Process *process = m_exe_ctx.GetProcessPtr(); | |
| if (process) | |
| process->Flush(); | |
| } | |
| if (load) { | |
| ProcessSP process = target->CalculateProcess(); | |
| Address file_entry = objfile->GetEntryPointAddress(); | |
| if (!process) { | |
| result.AppendError("No process"); | |
| return false; | |
| } | |
| if (set_pc && !file_entry.IsValid()) { | |
| result.AppendError("No entry address in object file"); | |
| return false; | |
| } | |
| std::vector<ObjectFile::LoadableData> loadables( | |
| objfile->GetLoadableData(*target)); | |
| if (loadables.size() == 0) { | |
| result.AppendError("No loadable sections"); | |
| return false; | |
| } | |
| Status error = process->WriteObjectFile(std::move(loadables)); | |
| if (error.Fail()) { | |
| result.AppendError(error.AsCString()); | |
| return false; | |
| } | |
| if (set_pc) { | |
| ThreadList &thread_list = process->GetThreadList(); | |
| RegisterContextSP reg_context( | |
| thread_list.GetSelectedThread()->GetRegisterContext()); | |
| addr_t file_entry_addr = file_entry.GetLoadAddress(target); | |
| if (!reg_context->SetPC(file_entry_addr)) { | |
| result.AppendErrorWithFormat("failed to set PC value to " | |
| "0x%" PRIx64 "\n", | |
| file_entry_addr); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| } | |
| } | |
| } else { | |
| module->GetFileSpec().GetPath(path, sizeof(path)); | |
| result.AppendErrorWithFormat("no sections in object file '%s'\n", | |
| path); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| } else { | |
| module->GetFileSpec().GetPath(path, sizeof(path)); | |
| result.AppendErrorWithFormat("no object file for module '%s'\n", | |
| path); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| } 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"); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| } 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()); | |
| } | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| } else { | |
| result.AppendError("either the \"--file <module>\" or the \"--uuid " | |
| "<uuid>\" option must be specified.\n"); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| return result.Succeeded(); | |
| } | |
| OptionGroupOptions m_option_group; | |
| OptionGroupUUID m_uuid_option_group; | |
| OptionGroupString m_file_option; | |
| OptionGroupBoolean m_load_option; | |
| OptionGroupBoolean m_pc_option; | |
| OptionGroupUInt64 m_slide_option; | |
| }; | |
| // 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() | |
| : Options(), m_format_array(), m_use_global_module_list(false), | |
| m_module_addr(LLDB_INVALID_ADDRESS) {} | |
| ~CommandOptions() override = default; | |
| Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, | |
| ExecutionContext *execution_context) override { | |
| Status error; | |
| const int short_option = m_getopt_table[option_idx].val; | |
| if (short_option == 'g') { | |
| m_use_global_module_list = true; | |
| } else if (short_option == 'a') { | |
| m_module_addr = OptionArgParser::ToAddress( | |
| execution_context, option_arg, LLDB_INVALID_ADDRESS, &error); | |
| } else { | |
| unsigned long width = 0; | |
| option_arg.getAsInteger(0, width); | |
| m_format_array.push_back(std::make_pair(short_option, width)); | |
| } | |
| return error; | |
| } | |
| void OptionParsingStarting(ExecutionContext *execution_context) override { | |
| m_format_array.clear(); | |
| m_use_global_module_list = false; | |
| m_module_addr = LLDB_INVALID_ADDRESS; | |
| } | |
| llvm::ArrayRef<OptionDefinition> GetDefinitions() override { | |
| return llvm::makeArrayRef(g_target_modules_list_options); | |
| } | |
| // Instance variables to hold the values for command options. | |
| typedef std::vector<std::pair<char, uint32_t>> FormatWidthCollection; | |
| FormatWidthCollection m_format_array; | |
| bool m_use_global_module_list; | |
| lldb::addr_t m_module_addr; | |
| }; | |
| CommandObjectTargetModulesList(CommandInterpreter &interpreter) | |
| : CommandObjectParsed( | |
| interpreter, "target modules list", | |
| "List current executable and dependent shared library images.", | |
| "target modules list [<cmd-options>]"), | |
| m_options() {} | |
| ~CommandObjectTargetModulesList() override = default; | |
| Options *GetOptions() override { return &m_options; } | |
| protected: | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = GetDebugger().GetSelectedTarget().get(); | |
| const bool use_global_module_list = m_options.m_use_global_module_list; | |
| // Define a local module list here to ensure it lives longer than any | |
| // "locker" object which might lock its contents below (through the | |
| // "module_list_ptr" variable). | |
| ModuleList module_list; | |
| if (target == nullptr && !use_global_module_list) { | |
| result.AppendError("invalid target, create a debug target using the " | |
| "'target create' command"); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } else { | |
| if (target) { | |
| uint32_t addr_byte_size = | |
| target->GetArchitecture().GetAddressByteSize(); | |
| result.GetOutputStream().SetAddressByteSize(addr_byte_size); | |
| result.GetErrorStream().SetAddressByteSize(addr_byte_size); | |
| } | |
| // Dump all sections for all modules images | |
| Stream &strm = result.GetOutputStream(); | |
| if (m_options.m_module_addr != LLDB_INVALID_ADDRESS) { | |
| if (target) { | |
| Address module_address; | |
| if (module_address.SetLoadAddress(m_options.m_module_addr, target)) { | |
| ModuleSP module_sp(module_address.GetModule()); | |
| if (module_sp) { | |
| PrintModule(target, module_sp.get(), 0, strm); | |
| result.SetStatus(eReturnStatusSuccessFinishResult); | |
| } else { | |
| result.AppendErrorWithFormat( | |
| "Couldn't find module matching address: 0x%" PRIx64 ".", | |
| m_options.m_module_addr); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| } else { | |
| result.AppendErrorWithFormat( | |
| "Couldn't find module containing address: 0x%" PRIx64 ".", | |
| m_options.m_module_addr); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| } else { | |
| result.AppendError( | |
| "Can only look up modules by address with a valid target."); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| return result.Succeeded(); | |
| } | |
| size_t num_modules = 0; | |
| // This locker will be locked on the mutex in module_list_ptr if it is | |
| // non-nullptr. Otherwise it will lock the | |
| // AllocationModuleCollectionMutex when accessing the global module list | |
| // directly. | |
| std::unique_lock<std::recursive_mutex> guard( | |
| Module::GetAllocationModuleCollectionMutex(), std::defer_lock); | |
| const ModuleList *module_list_ptr = nullptr; | |
| const size_t argc = command.GetArgumentCount(); | |
| if (argc == 0) { | |
| if (use_global_module_list) { | |
| guard.lock(); | |
| num_modules = Module::GetNumberAllocatedModules(); | |
| } else { | |
| module_list_ptr = &target->GetImages(); | |
| } | |
| } else { | |
| // TODO: Convert to entry based iteration. Requires converting | |
| // FindModulesByName. | |
| for (size_t i = 0; i < argc; ++i) { | |
| // Dump specified images (by basename or fullpath) | |
| const char *arg_cstr = command.GetArgumentAtIndex(i); | |
| const size_t num_matches = FindModulesByName( | |
| target, arg_cstr, module_list, use_global_module_list); | |
| if (num_matches == 0) { | |
| if (argc == 1) { | |
| result.AppendErrorWithFormat("no modules found that match '%s'", | |
| arg_cstr); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| } | |
| } | |
| module_list_ptr = &module_list; | |
| } | |
| std::unique_lock<std::recursive_mutex> lock; | |
| if (module_list_ptr != nullptr) { | |
| lock = | |
| std::unique_lock<std::recursive_mutex>(module_list_ptr->GetMutex()); | |
| num_modules = module_list_ptr->GetSize(); | |
| } | |
| if (num_modules > 0) { | |
| for (uint32_t image_idx = 0; image_idx < num_modules; ++image_idx) { | |
| ModuleSP module_sp; | |
| Module *module; | |
| if (module_list_ptr) { | |
| module_sp = module_list_ptr->GetModuleAtIndexUnlocked(image_idx); | |
| module = module_sp.get(); | |
| } else { | |
| module = Module::GetAllocatedModuleAtIndex(image_idx); | |
| module_sp = module->shared_from_this(); | |
| } | |
| const size_t indent = strm.Printf("[%3u] ", image_idx); | |
| PrintModule(target, module, indent, strm); | |
| } | |
| result.SetStatus(eReturnStatusSuccessFinishResult); | |
| } else { | |
| if (argc) { | |
| if (use_global_module_list) | |
| result.AppendError( | |
| "the global module list has no matching modules"); | |
| else | |
| result.AppendError("the target has no matching modules"); | |
| } else { | |
| if (use_global_module_list) | |
| result.AppendError("the global module list is empty"); | |
| else | |
| result.AppendError( | |
| "the target has no associated executable images"); | |
| } | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| } | |
| return result.Succeeded(); | |
| } | |
| void PrintModule(Target *target, Module *module, int indent, Stream &strm) { | |
| if (module == nullptr) { | |
| strm.PutCString("Null module"); | |
| return; | |
| } | |
| bool dump_object_name = false; | |
| if (m_options.m_format_array.empty()) { | |
| m_options.m_format_array.push_back(std::make_pair('u', 0)); | |
| m_options.m_format_array.push_back(std::make_pair('h', 0)); | |
| m_options.m_format_array.push_back(std::make_pair('f', 0)); | |
| m_options.m_format_array.push_back(std::make_pair('S', 0)); | |
| } | |
| const size_t num_entries = m_options.m_format_array.size(); | |
| bool print_space = false; | |
| for (size_t i = 0; i < num_entries; ++i) { | |
| if (print_space) | |
| strm.PutChar(' '); | |
| print_space = true; | |
| const char format_char = m_options.m_format_array[i].first; | |
| uint32_t width = m_options.m_format_array[i].second; | |
| switch (format_char) { | |
| case 'A': | |
| DumpModuleArchitecture(strm, module, false, width); | |
| break; | |
| case 't': | |
| DumpModuleArchitecture(strm, module, true, width); | |
| break; | |
| case 'f': | |
| DumpFullpath(strm, &module->GetFileSpec(), width); | |
| dump_object_name = true; | |
| break; | |
| case 'd': | |
| DumpDirectory(strm, &module->GetFileSpec(), width); | |
| break; | |
| case 'b': | |
| DumpBasename(strm, &module->GetFileSpec(), width); | |
| dump_object_name = true; | |
| break; | |
| case 'h': | |
| case 'o': | |
| // Image header address | |
| { | |
| uint32_t addr_nibble_width = | |
| target ? (target->GetArchitecture().GetAddressByteSize() * 2) | |
| : 16; | |
| ObjectFile *objfile = module->GetObjectFile(); | |
| if (objfile) { | |
| Address base_addr(objfile->GetBaseAddress()); | |
| if (base_addr.IsValid()) { | |
| if (target && !target->GetSectionLoadList().IsEmpty()) { | |
| lldb::addr_t load_addr = | |
| base_addr.GetLoadAddress(target); | |
| if (load_addr == LLDB_INVALID_ADDRESS) { | |
| base_addr.Dump(&strm, target, | |
| Address::DumpStyleModuleWithFileAddress, | |
| Address::DumpStyleFileAddress); | |
| } else { | |
| if (format_char == 'o') { | |
| // Show the offset of slide for the image | |
| strm.Printf( | |
| "0x%*.*" PRIx64, addr_nibble_width, addr_nibble_width, | |
| load_addr - base_addr.GetFileAddress()); | |
| } else { | |
| // Show the load address of the image | |
| strm.Printf("0x%*.*" PRIx64, addr_nibble_width, | |
| addr_nibble_width, load_addr); | |
| } | |
| } | |
| break; | |
| } | |
| // The address was valid, but the image isn't loaded, output the | |
| // address in an appropriate format | |
| base_addr.Dump(&strm, target, Address::DumpStyleFileAddress); | |
| break; | |
| } | |
| } | |
| strm.Printf("%*s", addr_nibble_width + 2, ""); | |
| } | |
| break; | |
| case 'r': { | |
| size_t ref_count = 0; | |
| ModuleSP module_sp(module->shared_from_this()); | |
| if (module_sp) { | |
| // Take one away to make sure we don't count our local "module_sp" | |
| ref_count = module_sp.use_count() - 1; | |
| } | |
| if (width) | |
| strm.Printf("{%*" PRIu64 "}", width, (uint64_t)ref_count); | |
| else | |
| strm.Printf("{%" PRIu64 "}", (uint64_t)ref_count); | |
| } break; | |
| case 's': | |
| case 'S': { | |
| if (const SymbolFile *symbol_file = module->GetSymbolFile()) { | |
| const FileSpec symfile_spec = | |
| symbol_file->GetObjectFile()->GetFileSpec(); | |
| if (format_char == 'S') { | |
| // Dump symbol file only if different from module file | |
| if (!symfile_spec || symfile_spec == module->GetFileSpec()) { | |
| print_space = false; | |
| break; | |
| } | |
| // Add a newline and indent past the index | |
| strm.Printf("\n%*s", indent, ""); | |
| } | |
| DumpFullpath(strm, &symfile_spec, width); | |
| dump_object_name = true; | |
| break; | |
| } | |
| strm.Printf("%.*s", width, "<NONE>"); | |
| } break; | |
| case 'm': | |
| strm.Format("{0:%c}", llvm::fmt_align(module->GetModificationTime(), | |
| llvm::AlignStyle::Left, width)); | |
| break; | |
| case 'p': | |
| strm.Printf("%p", static_cast<void *>(module)); | |
| break; | |
| case 'u': | |
| DumpModuleUUID(strm, module); | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| if (dump_object_name) { | |
| const char *object_name = module->GetObjectName().GetCString(); | |
| if (object_name) | |
| strm.Printf("(%s)", object_name); | |
| } | |
| strm.EOL(); | |
| } | |
| CommandOptions m_options; | |
| }; | |
| #pragma mark CommandObjectTargetModulesShowUnwind | |
| // Lookup unwind information in images | |
| #define LLDB_OPTIONS_target_modules_show_unwind | |
| #include "CommandOptions.inc" | |
| class CommandObjectTargetModulesShowUnwind : public CommandObjectParsed { | |
| public: | |
| enum { | |
| eLookupTypeInvalid = -1, | |
| eLookupTypeAddress = 0, | |
| eLookupTypeSymbol, | |
| eLookupTypeFunction, | |
| eLookupTypeFunctionOrSymbol, | |
| kNumLookupTypes | |
| }; | |
| class CommandOptions : public Options { | |
| public: | |
| CommandOptions() | |
| : Options(), m_type(eLookupTypeInvalid), m_str(), | |
| m_addr(LLDB_INVALID_ADDRESS) {} | |
| ~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 = 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 = option_arg; | |
| m_type = eLookupTypeFunctionOrSymbol; | |
| break; | |
| default: | |
| llvm_unreachable("Unimplemented option"); | |
| } | |
| return error; | |
| } | |
| void OptionParsingStarting(ExecutionContext *execution_context) override { | |
| m_type = eLookupTypeInvalid; | |
| m_str.clear(); | |
| m_addr = LLDB_INVALID_ADDRESS; | |
| } | |
| llvm::ArrayRef<OptionDefinition> GetDefinitions() override { | |
| return llvm::makeArrayRef(g_target_modules_show_unwind_options); | |
| } | |
| // Instance variables to hold the values for command options. | |
| int m_type; // Should be a eLookupTypeXXX enum after parsing options | |
| std::string m_str; // Holds name lookup | |
| lldb::addr_t m_addr; // 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), | |
| m_options() {} | |
| ~CommandObjectTargetModulesShowUnwind() override = default; | |
| Options *GetOptions() override { return &m_options; } | |
| protected: | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = m_exe_ctx.GetTargetPtr(); | |
| Process *process = m_exe_ctx.GetProcessPtr(); | |
| ABI *abi = nullptr; | |
| if (process) | |
| abi = process->GetABI().get(); | |
| if (process == nullptr) { | |
| result.AppendError( | |
| "You must have a process running to use this command."); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| ThreadList threads(process->GetThreadList()); | |
| if (threads.GetSize() == 0) { | |
| result.AppendError("The process must be paused to use this command."); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| ThreadSP thread(threads.GetThreadAtIndex(0)); | |
| if (!thread) { | |
| result.AppendError("The process must be paused to use this command."); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| SymbolContextList sc_list; | |
| if (m_options.m_type == eLookupTypeFunctionOrSymbol) { | |
| ConstString function_name(m_options.m_str.c_str()); | |
| target->GetImages().FindFunctions(function_name, eFunctionNameTypeAuto, | |
| true, false, 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."); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| size_t num_matches = sc_list.GetSize(); | |
| if (num_matches == 0) { | |
| result.AppendErrorWithFormat("no unwind data found that matches '%s'.", | |
| m_options.m_str.c_str()); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| for (uint32_t idx = 0; idx < num_matches; idx++) { | |
| SymbolContext sc; | |
| sc_list.GetContextAtIndex(idx, sc); | |
| if (sc.symbol == nullptr && sc.function == nullptr) | |
| continue; | |
| if (!sc.module_sp || sc.module_sp->GetObjectFile() == nullptr) | |
| continue; | |
| AddressRange range; | |
| if (!sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0, | |
| false, range)) | |
| continue; | |
| if (!range.GetBaseAddress().IsValid()) | |
| continue; | |
| ConstString funcname(sc.GetFunctionName()); | |
| if (funcname.IsEmpty()) | |
| continue; | |
| addr_t start_addr = range.GetBaseAddress().GetLoadAddress(target); | |
| if (abi) | |
| start_addr = abi->FixCodeAddress(start_addr); | |
| FuncUnwindersSP func_unwinders_sp( | |
| sc.module_sp->GetUnwindTable() | |
| .GetUncachedFuncUnwindersContainingAddress(start_addr, sc)); | |
| if (!func_unwinders_sp) | |
| continue; | |
| result.GetOutputStream().Printf( | |
| "UNWIND PLANS for %s`%s (start addr 0x%" PRIx64 ")\n\n", | |
| sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), | |
| funcname.AsCString(), start_addr); | |
| UnwindPlanSP non_callsite_unwind_plan = | |
| func_unwinders_sp->GetUnwindPlanAtNonCallSite(*target, *thread); | |
| if (non_callsite_unwind_plan) { | |
| result.GetOutputStream().Printf( | |
| "Asynchronous (not restricted to call-sites) UnwindPlan is '%s'\n", | |
| non_callsite_unwind_plan->GetSourceName().AsCString()); | |
| } | |
| UnwindPlanSP callsite_unwind_plan = | |
| func_unwinders_sp->GetUnwindPlanAtCallSite(*target, *thread); | |
| if (callsite_unwind_plan) { | |
| result.GetOutputStream().Printf( | |
| "Synchronous (restricted to call-sites) UnwindPlan is '%s'\n", | |
| callsite_unwind_plan->GetSourceName().AsCString()); | |
| } | |
| UnwindPlanSP fast_unwind_plan = | |
| func_unwinders_sp->GetUnwindPlanFastUnwind(*target, *thread); | |
| if (fast_unwind_plan) { | |
| result.GetOutputStream().Printf( | |
| "Fast UnwindPlan is '%s'\n", | |
| fast_unwind_plan->GetSourceName().AsCString()); | |
| } | |
| result.GetOutputStream().Printf("\n"); | |
| UnwindPlanSP assembly_sp = | |
| func_unwinders_sp->GetAssemblyUnwindPlan(*target, *thread); | |
| if (assembly_sp) { | |
| result.GetOutputStream().Printf( | |
| "Assembly language inspection UnwindPlan:\n"); | |
| assembly_sp->Dump(result.GetOutputStream(), thread.get(), | |
| LLDB_INVALID_ADDRESS); | |
| result.GetOutputStream().Printf("\n"); | |
| } | |
| UnwindPlanSP of_unwind_sp = | |
| func_unwinders_sp->GetObjectFileUnwindPlan(*target); | |
| if (of_unwind_sp) { | |
| result.GetOutputStream().Printf("object file UnwindPlan:\n"); | |
| of_unwind_sp->Dump(result.GetOutputStream(), thread.get(), | |
| LLDB_INVALID_ADDRESS); | |
| result.GetOutputStream().Printf("\n"); | |
| } | |
| UnwindPlanSP of_unwind_augmented_sp = | |
| func_unwinders_sp->GetObjectFileAugmentedUnwindPlan(*target, | |
| *thread); | |
| if (of_unwind_augmented_sp) { | |
| result.GetOutputStream().Printf("object file augmented UnwindPlan:\n"); | |
| of_unwind_augmented_sp->Dump(result.GetOutputStream(), thread.get(), | |
| LLDB_INVALID_ADDRESS); | |
| result.GetOutputStream().Printf("\n"); | |
| } | |
| UnwindPlanSP ehframe_sp = | |
| func_unwinders_sp->GetEHFrameUnwindPlan(*target); | |
| if (ehframe_sp) { | |
| result.GetOutputStream().Printf("eh_frame UnwindPlan:\n"); | |
| ehframe_sp->Dump(result.GetOutputStream(), thread.get(), | |
| LLDB_INVALID_ADDRESS); | |
| result.GetOutputStream().Printf("\n"); | |
| } | |
| UnwindPlanSP ehframe_augmented_sp = | |
| func_unwinders_sp->GetEHFrameAugmentedUnwindPlan(*target, *thread); | |
| if (ehframe_augmented_sp) { | |
| result.GetOutputStream().Printf("eh_frame augmented UnwindPlan:\n"); | |
| ehframe_augmented_sp->Dump(result.GetOutputStream(), thread.get(), | |
| LLDB_INVALID_ADDRESS); | |
| result.GetOutputStream().Printf("\n"); | |
| } | |
| if (UnwindPlanSP plan_sp = | |
| func_unwinders_sp->GetDebugFrameUnwindPlan(*target)) { | |
| result.GetOutputStream().Printf("debug_frame UnwindPlan:\n"); | |
| plan_sp->Dump(result.GetOutputStream(), thread.get(), | |
| LLDB_INVALID_ADDRESS); | |
| result.GetOutputStream().Printf("\n"); | |
| } | |
| if (UnwindPlanSP plan_sp = | |
| func_unwinders_sp->GetDebugFrameAugmentedUnwindPlan(*target, | |
| *thread)) { | |
| result.GetOutputStream().Printf("debug_frame augmented UnwindPlan:\n"); | |
| plan_sp->Dump(result.GetOutputStream(), thread.get(), | |
| LLDB_INVALID_ADDRESS); | |
| result.GetOutputStream().Printf("\n"); | |
| } | |
| UnwindPlanSP arm_unwind_sp = | |
| func_unwinders_sp->GetArmUnwindUnwindPlan(*target); | |
| if (arm_unwind_sp) { | |
| result.GetOutputStream().Printf("ARM.exidx unwind UnwindPlan:\n"); | |
| arm_unwind_sp->Dump(result.GetOutputStream(), thread.get(), | |
| LLDB_INVALID_ADDRESS); | |
| result.GetOutputStream().Printf("\n"); | |
| } | |
| if (UnwindPlanSP symfile_plan_sp = | |
| func_unwinders_sp->GetSymbolFileUnwindPlan(*thread)) { | |
| result.GetOutputStream().Printf("Symbol file UnwindPlan:\n"); | |
| symfile_plan_sp->Dump(result.GetOutputStream(), thread.get(), | |
| LLDB_INVALID_ADDRESS); | |
| result.GetOutputStream().Printf("\n"); | |
| } | |
| UnwindPlanSP compact_unwind_sp = | |
| func_unwinders_sp->GetCompactUnwindUnwindPlan(*target); | |
| if (compact_unwind_sp) { | |
| result.GetOutputStream().Printf("Compact unwind UnwindPlan:\n"); | |
| compact_unwind_sp->Dump(result.GetOutputStream(), thread.get(), | |
| LLDB_INVALID_ADDRESS); | |
| result.GetOutputStream().Printf("\n"); | |
| } | |
| if (fast_unwind_plan) { | |
| result.GetOutputStream().Printf("Fast UnwindPlan:\n"); | |
| fast_unwind_plan->Dump(result.GetOutputStream(), thread.get(), | |
| LLDB_INVALID_ADDRESS); | |
| result.GetOutputStream().Printf("\n"); | |
| } | |
| ABISP abi_sp = process->GetABI(); | |
| if (abi_sp) { | |
| UnwindPlan arch_default(lldb::eRegisterKindGeneric); | |
| if (abi_sp->CreateDefaultUnwindPlan(arch_default)) { | |
| result.GetOutputStream().Printf("Arch default UnwindPlan:\n"); | |
| arch_default.Dump(result.GetOutputStream(), thread.get(), | |
| LLDB_INVALID_ADDRESS); | |
| result.GetOutputStream().Printf("\n"); | |
| } | |
| UnwindPlan arch_entry(lldb::eRegisterKindGeneric); | |
| if (abi_sp->CreateFunctionEntryUnwindPlan(arch_entry)) { | |
| result.GetOutputStream().Printf( | |
| "Arch default at entry point UnwindPlan:\n"); | |
| arch_entry.Dump(result.GetOutputStream(), thread.get(), | |
| LLDB_INVALID_ADDRESS); | |
| result.GetOutputStream().Printf("\n"); | |
| } | |
| } | |
| result.GetOutputStream().Printf("\n"); | |
| } | |
| return result.Succeeded(); | |
| } | |
| CommandOptions m_options; | |
| }; | |
| // Lookup information in images | |
| #define LLDB_OPTIONS_target_modules_lookup | |
| #include "CommandOptions.inc" | |
| class CommandObjectTargetModulesLookup : public CommandObjectParsed { | |
| public: | |
| enum { | |
| eLookupTypeInvalid = -1, | |
| eLookupTypeAddress = 0, | |
| eLookupTypeSymbol, | |
| eLookupTypeFileLine, // Line is optional | |
| eLookupTypeFunction, | |
| eLookupTypeFunctionOrSymbol, | |
| eLookupTypeType, | |
| kNumLookupTypes | |
| }; | |
| class CommandOptions : public Options { | |
| public: | |
| CommandOptions() : Options() { 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 = 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 = option_arg; | |
| m_type = eLookupTypeFunction; | |
| break; | |
| case 'n': | |
| m_str = option_arg; | |
| m_type = eLookupTypeFunctionOrSymbol; | |
| break; | |
| case 't': | |
| m_str = 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; | |
| 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_verbose = false; | |
| m_print_all = false; | |
| } | |
| llvm::ArrayRef<OptionDefinition> GetDefinitions() override { | |
| return llvm::makeArrayRef(g_target_modules_lookup_options); | |
| } | |
| int m_type; // Should be a eLookupTypeXXX enum after parsing options | |
| std::string m_str; // Holds name lookup | |
| FileSpec m_file; // Files for file lookups | |
| lldb::addr_t m_addr; // Holds the address to lookup | |
| lldb::addr_t | |
| m_offset; // Subtract this offset from m_addr before doing lookups. | |
| uint32_t m_line_number; // Line number for file+line lookups | |
| bool m_use_regex; // Name lookups in m_str are regular expressions. | |
| bool m_include_inlines; // Check for inline entries when looking up by | |
| // file/line. | |
| bool m_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), | |
| m_options() { | |
| CommandArgumentEntry arg; | |
| CommandArgumentData file_arg; | |
| // Define the first (and only) variant of this arg. | |
| file_arg.arg_type = eArgTypeFilename; | |
| file_arg.arg_repetition = eArgRepeatStar; | |
| // There is only one variant this argument could be; put it into the | |
| // argument entry. | |
| arg.push_back(file_arg); | |
| // Push the data for the first argument into the m_arguments vector. | |
| m_arguments.push_back(arg); | |
| } | |
| ~CommandObjectTargetModulesLookup() override = default; | |
| Options *GetOptions() override { return &m_options; } | |
| bool LookupHere(CommandInterpreter &interpreter, CommandReturnObject &result, | |
| bool &syntax_error) { | |
| switch (m_options.m_type) { | |
| case eLookupTypeAddress: | |
| case eLookupTypeFileLine: | |
| case eLookupTypeFunction: | |
| case eLookupTypeFunctionOrSymbol: | |
| case eLookupTypeSymbol: | |
| default: | |
| return false; | |
| case eLookupTypeType: | |
| break; | |
| } | |
| StackFrameSP frame = m_exe_ctx.GetFrameSP(); | |
| if (!frame) | |
| return false; | |
| const SymbolContext &sym_ctx(frame->GetSymbolContext(eSymbolContextModule)); | |
| if (!sym_ctx.module_sp) | |
| return false; | |
| switch (m_options.m_type) { | |
| default: | |
| return false; | |
| case eLookupTypeType: | |
| if (!m_options.m_str.empty()) { | |
| if (LookupTypeHere(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 true; | |
| } | |
| 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)) { | |
| 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)) { | |
| 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)) { | |
| result.SetStatus(eReturnStatusSuccessFinishResult); | |
| return true; | |
| } | |
| } | |
| break; | |
| case eLookupTypeFunctionOrSymbol: | |
| case eLookupTypeFunction: | |
| if (!m_options.m_str.empty()) { | |
| if (LookupFunctionInModule( | |
| m_interpreter, result.GetOutputStream(), module, | |
| m_options.m_str.c_str(), m_options.m_use_regex, | |
| m_options.m_include_inlines, | |
| m_options.m_type == | |
| eLookupTypeFunctionOrSymbol, // include symbols | |
| m_options.m_verbose)) { | |
| result.SetStatus(eReturnStatusSuccessFinishResult); | |
| return true; | |
| } | |
| } | |
| break; | |
| case eLookupTypeType: | |
| if (!m_options.m_str.empty()) { | |
| if (LookupTypeInModule(m_interpreter, result.GetOutputStream(), module, | |
| m_options.m_str.c_str(), | |
| m_options.m_use_regex)) { | |
| result.SetStatus(eReturnStatusSuccessFinishResult); | |
| return true; | |
| } | |
| } | |
| break; | |
| default: | |
| m_options.GenerateOptionUsage( | |
| result.GetErrorStream(), this, | |
| GetCommandInterpreter().GetDebugger().GetTerminalWidth()); | |
| syntax_error = true; | |
| break; | |
| } | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| protected: | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = &GetSelectedTarget(); | |
| bool syntax_error = false; | |
| uint32_t i; | |
| uint32_t num_successful_lookups = 0; | |
| uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); | |
| result.GetOutputStream().SetAddressByteSize(addr_byte_size); | |
| result.GetErrorStream().SetAddressByteSize(addr_byte_size); | |
| // Dump all sections for all modules images | |
| if (command.GetArgumentCount() == 0) { | |
| ModuleSP current_module; | |
| // Where it is possible to look in the current symbol context first, | |
| // try that. If this search was successful and --all was not passed, | |
| // don't print anything else. | |
| if (LookupHere(m_interpreter, result, syntax_error)) { | |
| result.GetOutputStream().EOL(); | |
| num_successful_lookups++; | |
| if (!m_options.m_print_all) { | |
| result.SetStatus(eReturnStatusSuccessFinishResult); | |
| return result.Succeeded(); | |
| } | |
| } | |
| // Dump all sections for all other modules | |
| const ModuleList &target_modules = target->GetImages(); | |
| std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex()); | |
| const size_t num_modules = target_modules.GetSize(); | |
| if (num_modules > 0) { | |
| for (i = 0; i < num_modules && !syntax_error; ++i) { | |
| Module *module_pointer = | |
| target_modules.GetModulePointerAtIndexUnlocked(i); | |
| if (module_pointer != current_module.get() && | |
| LookupInModule(m_interpreter, | |
| target_modules.GetModulePointerAtIndexUnlocked(i), | |
| result, syntax_error)) { | |
| result.GetOutputStream().EOL(); | |
| num_successful_lookups++; | |
| } | |
| } | |
| } else { | |
| result.AppendError("the target has no associated executable images"); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| } else { | |
| // Dump specified images (by basename or fullpath) | |
| const char *arg_cstr; | |
| for (i = 0; (arg_cstr = command.GetArgumentAtIndex(i)) != nullptr && | |
| !syntax_error; | |
| ++i) { | |
| ModuleList module_list; | |
| const size_t num_matches = | |
| FindModulesByName(target, arg_cstr, module_list, false); | |
| if (num_matches > 0) { | |
| for (size_t j = 0; j < num_matches; ++j) { | |
| Module *module = module_list.GetModulePointerAtIndex(j); | |
| if (module) { | |
| if (LookupInModule(m_interpreter, module, result, syntax_error)) { | |
| result.GetOutputStream().EOL(); | |
| num_successful_lookups++; | |
| } | |
| } | |
| } | |
| } else | |
| result.AppendWarningWithFormat( | |
| "Unable to find an image that matches '%s'.\n", arg_cstr); | |
| } | |
| } | |
| if (num_successful_lookups > 0) | |
| result.SetStatus(eReturnStatusSuccessFinishResult); | |
| else | |
| result.SetStatus(eReturnStatusFailed); | |
| return result.Succeeded(); | |
| } | |
| CommandOptions m_options; | |
| }; | |
| #pragma mark CommandObjectMultiwordImageSearchPaths | |
| // CommandObjectMultiwordImageSearchPaths | |
| class CommandObjectTargetModulesImageSearchPaths | |
| : public CommandObjectMultiword { | |
| public: | |
| CommandObjectTargetModulesImageSearchPaths(CommandInterpreter &interpreter) | |
| : CommandObjectMultiword( | |
| interpreter, "target modules search-paths", | |
| "Commands for managing module search paths for a target.", | |
| "target modules search-paths <subcommand> [<subcommand-options>]") { | |
| LoadSubCommand( | |
| "add", CommandObjectSP( | |
| new CommandObjectTargetModulesSearchPathsAdd(interpreter))); | |
| LoadSubCommand( | |
| "clear", CommandObjectSP(new CommandObjectTargetModulesSearchPathsClear( | |
| interpreter))); | |
| LoadSubCommand( | |
| "insert", | |
| CommandObjectSP( | |
| new CommandObjectTargetModulesSearchPathsInsert(interpreter))); | |
| LoadSubCommand( | |
| "list", CommandObjectSP(new CommandObjectTargetModulesSearchPathsList( | |
| interpreter))); | |
| LoadSubCommand( | |
| "query", CommandObjectSP(new CommandObjectTargetModulesSearchPathsQuery( | |
| interpreter))); | |
| } | |
| ~CommandObjectTargetModulesImageSearchPaths() override = default; | |
| }; | |
| #pragma mark CommandObjectTargetModules | |
| // CommandObjectTargetModules | |
| class CommandObjectTargetModules : public CommandObjectMultiword { | |
| public: | |
| // Constructors and Destructors | |
| CommandObjectTargetModules(CommandInterpreter &interpreter) | |
| : CommandObjectMultiword(interpreter, "target modules", | |
| "Commands for accessing information for one or " | |
| "more target modules.", | |
| "target modules <sub-command> ...") { | |
| LoadSubCommand( | |
| "add", CommandObjectSP(new CommandObjectTargetModulesAdd(interpreter))); | |
| LoadSubCommand("load", CommandObjectSP(new CommandObjectTargetModulesLoad( | |
| interpreter))); | |
| LoadSubCommand("dump", CommandObjectSP(new CommandObjectTargetModulesDump( | |
| interpreter))); | |
| LoadSubCommand("list", CommandObjectSP(new CommandObjectTargetModulesList( | |
| interpreter))); | |
| LoadSubCommand( | |
| "lookup", | |
| CommandObjectSP(new CommandObjectTargetModulesLookup(interpreter))); | |
| LoadSubCommand( | |
| "search-paths", | |
| CommandObjectSP( | |
| new CommandObjectTargetModulesImageSearchPaths(interpreter))); | |
| LoadSubCommand( | |
| "show-unwind", | |
| CommandObjectSP(new CommandObjectTargetModulesShowUnwind(interpreter))); | |
| } | |
| ~CommandObjectTargetModules() override = default; | |
| private: | |
| // For CommandObjectTargetModules only | |
| DISALLOW_COPY_AND_ASSIGN(CommandObjectTargetModules); | |
| }; | |
| 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 using the options " | |
| "to specify a module to download symbols for.", | |
| "target symbols add <cmd-options> [<symfile>]", | |
| eCommandRequiresTarget), | |
| m_option_group(), | |
| m_file_option( | |
| LLDB_OPT_SET_1, false, "shlib", 's', | |
| CommandCompletions::eModuleCompletion, eArgTypeShlibName, | |
| "Fullpath or basename for module to find debug symbols for."), | |
| m_current_frame_option( | |
| LLDB_OPT_SET_2, false, "frame", 'F', | |
| "Locate the debug symbols the currently selected frame.", 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.Finalize(); | |
| } | |
| ~CommandObjectTargetSymbolsAdd() override = default; | |
| void | |
| HandleArgumentCompletion(CompletionRequest &request, | |
| OptionElementVector &opt_element_vector) override { | |
| CommandCompletions::InvokeCommonCompletionCallbacks( | |
| GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion, | |
| request, nullptr); | |
| } | |
| Options *GetOptions() override { return &m_option_group; } | |
| protected: | |
| bool AddModuleSymbols(Target *target, ModuleSpec &module_spec, bool &flush, | |
| CommandReturnObject &result) { | |
| const FileSpec &symbol_fspec = module_spec.GetSymbolFileSpec(); | |
| if (symbol_fspec) { | |
| char symfile_path[PATH_MAX]; | |
| symbol_fspec.GetPath(symfile_path, sizeof(symfile_path)); | |
| if (!module_spec.GetUUID().IsValid()) { | |
| if (!module_spec.GetFileSpec() && !module_spec.GetPlatformFileSpec()) | |
| module_spec.GetFileSpec().GetFilename() = symbol_fspec.GetFilename(); | |
| } | |
| // We now have a module that represents a symbol file that can be used | |
| // for a module that might exist in the current target, so we need to | |
| // find that module in the target | |
| ModuleList matching_module_list; | |
| size_t num_matches = 0; | |
| // 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)) { | |
| // See if it has a UUID? | |
| 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_module_list); | |
| num_matches = matching_module_list.GetSize(); | |
| } | |
| } | |
| if (num_matches == 0) { | |
| // 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 && num_matches == 0; | |
| ++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_module_list); | |
| num_matches = matching_module_list.GetSize(); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // Just try to match up the file by basename if we have no matches at | |
| // this point | |
| if (num_matches == 0) { | |
| target->GetImages().FindModules(module_spec, matching_module_list); | |
| num_matches = matching_module_list.GetSize(); | |
| } | |
| while (num_matches == 0) { | |
| ConstString filename_no_extension( | |
| module_spec.GetFileSpec().GetFileNameStrippingExtension()); | |
| // Empty string returned, lets 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 less extension | |
| module_spec.GetFileSpec().GetFilename() = filename_no_extension; | |
| target->GetImages().FindModules(module_spec, matching_module_list); | |
| num_matches = matching_module_list.GetSize(); | |
| } | |
| if (num_matches > 1) { | |
| result.AppendErrorWithFormat("multiple modules match symbol file '%s', " | |
| "use the --uuid option to resolve the " | |
| "ambiguity.\n", | |
| symfile_path); | |
| } else if (num_matches == 1) { | |
| ModuleSP module_sp(matching_module_list.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.AppendWarningWithFormat("%s", feedback_stream.GetData()); | |
| flush = true; | |
| result.SetStatus(eReturnStatusSuccessFinishResult); | |
| return true; | |
| } | |
| } | |
| // Clear the symbol file spec if anything went wrong | |
| module_sp->SetSymbolFileFileSpec(FileSpec()); | |
| } | |
| namespace fs = llvm::sys::fs; | |
| if (module_spec.GetUUID().IsValid()) { | |
| StreamString ss_symfile_uuid; | |
| module_spec.GetUUID().Dump(&ss_symfile_uuid); | |
| result.AppendErrorWithFormat( | |
| "symbol file '%s' (%s) does not match any existing module%s\n", | |
| symfile_path, ss_symfile_uuid.GetData(), | |
| !fs::is_regular_file(symbol_fspec.GetPath()) | |
| ? "\n please specify the full path to the symbol file" | |
| : ""); | |
| } else { | |
| result.AppendErrorWithFormat( | |
| "symbol file '%s' does not match any existing module%s\n", | |
| symfile_path, | |
| !fs::is_regular_file(symbol_fspec.GetPath()) | |
| ? "\n please specify the full path to the symbol file" | |
| : ""); | |
| } | |
| } else { | |
| result.AppendError( | |
| "one or more executable image paths must be specified"); | |
| } | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| bool DoExecute(Args &args, CommandReturnObject &result) override { | |
| Target *target = m_exe_ctx.GetTargetPtr(); | |
| result.SetStatus(eReturnStatusFailed); | |
| bool flush = false; | |
| ModuleSpec module_spec; | |
| const bool uuid_option_set = | |
| m_uuid_option_group.GetOptionValue().OptionWasSet(); | |
| const bool file_option_set = m_file_option.GetOptionValue().OptionWasSet(); | |
| const bool frame_option_set = | |
| m_current_frame_option.GetOptionValue().OptionWasSet(); | |
| const size_t argc = args.GetArgumentCount(); | |
| if (argc == 0) { | |
| if (uuid_option_set || file_option_set || frame_option_set) { | |
| bool success = false; | |
| bool error_set = false; | |
| if (frame_option_set) { | |
| Process *process = m_exe_ctx.GetProcessPtr(); | |
| if (process) { | |
| const StateType process_state = process->GetState(); | |
| if (StateIsStoppedState(process_state, true)) { | |
| StackFrame *frame = m_exe_ctx.GetFramePtr(); | |
| if (frame) { | |
| ModuleSP frame_module_sp( | |
| frame->GetSymbolContext(eSymbolContextModule).module_sp); | |
| if (frame_module_sp) { | |
| if (FileSystem::Instance().Exists( | |
| frame_module_sp->GetPlatformFileSpec())) { | |
| module_spec.GetArchitecture() = | |
| frame_module_sp->GetArchitecture(); | |
| module_spec.GetFileSpec() = | |
| frame_module_sp->GetPlatformFileSpec(); | |
| } | |
| module_spec.GetUUID() = frame_module_sp->GetUUID(); | |
| success = module_spec.GetUUID().IsValid() || | |
| module_spec.GetFileSpec(); | |
| } else { | |
| result.AppendError("frame has no module"); | |
| error_set = true; | |
| } | |
| } else { | |
| result.AppendError("invalid current frame"); | |
| error_set = true; | |
| } | |
| } else { | |
| result.AppendErrorWithFormat("process is not stopped: %s", | |
| StateAsCString(process_state)); | |
| error_set = true; | |
| } | |
| } else { | |
| result.AppendError( | |
| "a process must exist in order to use the --frame option"); | |
| error_set = true; | |
| } | |
| } else { | |
| if (uuid_option_set) { | |
| module_spec.GetUUID() = | |
| m_uuid_option_group.GetOptionValue().GetCurrentValue(); | |
| success |= module_spec.GetUUID().IsValid(); | |
| } else if (file_option_set) { | |
| module_spec.GetFileSpec() = | |
| m_file_option.GetOptionValue().GetCurrentValue(); | |
| 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(); | |
| } | |
| success |= module_spec.GetUUID().IsValid() || | |
| FileSystem::Instance().Exists(module_spec.GetFileSpec()); | |
| } | |
| } | |
| if (success) { | |
| if (Symbols::DownloadObjectAndSymbolFile(module_spec)) { | |
| if (module_spec.GetSymbolFileSpec()) | |
| success = AddModuleSymbols(target, module_spec, flush, result); | |
| } | |
| } | |
| if (!success && !error_set) { | |
| StreamString error_strm; | |
| if (uuid_option_set) { | |
| error_strm.PutCString("unable to find debug symbols for UUID "); | |
| module_spec.GetUUID().Dump(&error_strm); | |
| } else if (file_option_set) { | |
| error_strm.PutCString( | |
| "unable to find debug symbols for the executable file "); | |
| error_strm << module_spec.GetFileSpec(); | |
| } else if (frame_option_set) { | |
| error_strm.PutCString( | |
| "unable to find debug symbols for the current frame"); | |
| } | |
| result.AppendError(error_strm.GetString()); | |
| } | |
| } 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; | |
| } | |
| ArchSpec arch; | |
| bool symfile_exists = | |
| FileSystem::Instance().Exists(module_spec.GetSymbolFileSpec()); | |
| if (symfile_exists) { | |
| if (!AddModuleSymbols(target, module_spec, flush, result)) | |
| break; | |
| } else { | |
| std::string resolved_symfile_path = | |
| module_spec.GetSymbolFileSpec().GetPath(); | |
| if (resolved_symfile_path != entry.ref()) { | |
| result.AppendErrorWithFormat( | |
| "invalid module path '%s' with resolved path '%s'\n", | |
| entry.c_str(), resolved_symfile_path.c_str()); | |
| break; | |
| } | |
| result.AppendErrorWithFormat("invalid module path '%s'\n", | |
| entry.c_str()); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if (flush) { | |
| Process *process = m_exe_ctx.GetProcessPtr(); | |
| if (process) | |
| process->Flush(); | |
| } | |
| return result.Succeeded(); | |
| } | |
| OptionGroupOptions m_option_group; | |
| OptionGroupUUID m_uuid_option_group; | |
| OptionGroupFile m_file_option; | |
| OptionGroupBoolean m_current_frame_option; | |
| }; | |
| #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 | |
| DISALLOW_COPY_AND_ASSIGN(CommandObjectTargetSymbols); | |
| }; | |
| #pragma mark CommandObjectTargetStopHookAdd | |
| // CommandObjectTargetStopHookAdd | |
| #define LLDB_OPTIONS_target_stop_hook_add | |
| #include "CommandOptions.inc" | |
| class CommandObjectTargetStopHookAdd : public CommandObjectParsed, | |
| public IOHandlerDelegateMultiline { | |
| public: | |
| class CommandOptions : public Options { | |
| public: | |
| CommandOptions() | |
| : Options(), m_line_start(0), m_line_end(UINT_MAX), | |
| m_func_name_type_mask(eFunctionNameTypeAuto), | |
| m_sym_ctx_specified(false), m_thread_specified(false), | |
| m_use_one_liner(false), m_one_liner() {} | |
| ~CommandOptions() override = default; | |
| llvm::ArrayRef<OptionDefinition> GetDefinitions() override { | |
| return llvm::makeArrayRef(g_target_stop_hook_add_options); | |
| } | |
| Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, | |
| ExecutionContext *execution_context) override { | |
| Status error; | |
| const int short_option = m_getopt_table[option_idx].val; | |
| switch (short_option) { | |
| case 'c': | |
| m_class_name = 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 = option_arg; | |
| m_func_name_type_mask |= eFunctionNameTypeAuto; | |
| m_sym_ctx_specified = true; | |
| break; | |
| case 'f': | |
| m_file_name = option_arg; | |
| m_sym_ctx_specified = true; | |
| break; | |
| case 's': | |
| m_module_name = 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 = option_arg; | |
| m_thread_specified = true; | |
| break; | |
| case 'q': | |
| m_queue_name = 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(option_arg); | |
| break; | |
| default: | |
| llvm_unreachable("Unimplemented option"); | |
| } | |
| return error; | |
| } | |
| void OptionParsingStarting(ExecutionContext *execution_context) override { | |
| m_class_name.clear(); | |
| m_function_name.clear(); | |
| m_line_start = 0; | |
| m_line_end = UINT_MAX; | |
| m_file_name.clear(); | |
| m_module_name.clear(); | |
| m_func_name_type_mask = eFunctionNameTypeAuto; | |
| m_thread_id = LLDB_INVALID_THREAD_ID; | |
| m_thread_index = UINT32_MAX; | |
| m_thread_name.clear(); | |
| m_queue_name.clear(); | |
| m_no_inlines = false; | |
| m_sym_ctx_specified = false; | |
| m_thread_specified = false; | |
| m_use_one_liner = false; | |
| m_one_liner.clear(); | |
| m_auto_continue = false; | |
| } | |
| std::string m_class_name; | |
| std::string m_function_name; | |
| uint32_t m_line_start; | |
| uint32_t m_line_end; | |
| std::string m_file_name; | |
| std::string m_module_name; | |
| uint32_t m_func_name_type_mask; // A pick from lldb::FunctionNameType. | |
| lldb::tid_t m_thread_id; | |
| uint32_t m_thread_index; | |
| std::string m_thread_name; | |
| std::string m_queue_name; | |
| bool m_sym_ctx_specified; | |
| bool m_no_inlines; | |
| bool m_thread_specified; | |
| // Instance variables to hold the values for one_liner options. | |
| bool m_use_one_liner; | |
| std::vector<std::string> m_one_liner; | |
| bool m_auto_continue; | |
| }; | |
| CommandObjectTargetStopHookAdd(CommandInterpreter &interpreter) | |
| : CommandObjectParsed(interpreter, "target stop-hook add", | |
| "Add a hook to be executed when the target stops.", | |
| "target stop-hook add"), | |
| IOHandlerDelegateMultiline("DONE", | |
| IOHandlerDelegate::Completion::LLDBCommand), | |
| m_options() {} | |
| ~CommandObjectTargetStopHookAdd() override = default; | |
| Options *GetOptions() override { return &m_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->RemoveStopHookByID(m_stop_hook_sp->GetID()); | |
| } else { | |
| m_stop_hook_sp->GetCommandPointer()->SplitIntoLines(line); | |
| StreamFileSP output_sp(io_handler.GetOutputStreamFileSP()); | |
| if (output_sp) { | |
| output_sp->Printf("Stop hook #%" PRIu64 " added.\n", | |
| m_stop_hook_sp->GetID()); | |
| output_sp->Flush(); | |
| } | |
| } | |
| m_stop_hook_sp.reset(); | |
| } | |
| io_handler.SetIsDone(true); | |
| } | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| m_stop_hook_sp.reset(); | |
| Target &target = GetSelectedOrDummyTarget(); | |
| Target::StopHookSP new_hook_sp = target.CreateStopHook(); | |
| // First step, make the specifier. | |
| std::unique_ptr<SymbolContextSpecifier> specifier_up; | |
| if (m_options.m_sym_ctx_specified) { | |
| specifier_up.reset( | |
| new 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) { | |
| // Use one-liners. | |
| for (auto cmd : m_options.m_one_liner) | |
| new_hook_sp->GetCommandPointer()->AppendString( | |
| cmd.c_str()); | |
| result.AppendMessageWithFormat("Stop hook #%" PRIu64 " added.\n", | |
| new_hook_sp->GetID()); | |
| } else { | |
| m_stop_hook_sp = new_hook_sp; | |
| m_interpreter.GetLLDBCommandsFromIOHandler( | |
| "> ", // Prompt | |
| *this, // IOHandlerDelegate | |
| true, // Run IOHandler in async mode | |
| nullptr); // Baton for the "io_handler" that will be passed back | |
| // into our IOHandlerDelegate functions | |
| } | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| return result.Succeeded(); | |
| } | |
| private: | |
| CommandOptions m_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>]") {} | |
| ~CommandObjectTargetStopHookDelete() override = default; | |
| protected: | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target &target = GetSelectedOrDummyTarget(); | |
| // FIXME: see if we can use the breakpoint id style parser? | |
| size_t num_args = command.GetArgumentCount(); | |
| if (num_args == 0) { | |
| if (!m_interpreter.Confirm("Delete all stop hooks?", true)) { | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } else { | |
| target.RemoveAllStopHooks(); | |
| } | |
| } else { | |
| bool success; | |
| for (size_t i = 0; i < num_args; i++) { | |
| lldb::user_id_t user_id = StringConvert::ToUInt32( | |
| command.GetArgumentAtIndex(i), 0, 0, &success); | |
| if (!success) { | |
| result.AppendErrorWithFormat("invalid stop hook id: \"%s\".\n", | |
| command.GetArgumentAtIndex(i)); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| success = target.RemoveStopHookByID(user_id); | |
| if (!success) { | |
| result.AppendErrorWithFormat("unknown stop hook id: \"%s\".\n", | |
| command.GetArgumentAtIndex(i)); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| } | |
| } | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| return result.Succeeded(); | |
| } | |
| }; | |
| #pragma mark CommandObjectTargetStopHookEnableDisable | |
| // CommandObjectTargetStopHookEnableDisable | |
| class CommandObjectTargetStopHookEnableDisable : public CommandObjectParsed { | |
| public: | |
| CommandObjectTargetStopHookEnableDisable(CommandInterpreter &interpreter, | |
| bool enable, const char *name, | |
| const char *help, const char *syntax) | |
| : CommandObjectParsed(interpreter, name, help, syntax), m_enable(enable) { | |
| } | |
| ~CommandObjectTargetStopHookEnableDisable() override = default; | |
| protected: | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target &target = GetSelectedOrDummyTarget(); | |
| // FIXME: see if we can use the breakpoint id style parser? | |
| size_t num_args = command.GetArgumentCount(); | |
| bool success; | |
| if (num_args == 0) { | |
| target.SetAllStopHooksActiveState(m_enable); | |
| } else { | |
| for (size_t i = 0; i < num_args; i++) { | |
| lldb::user_id_t user_id = StringConvert::ToUInt32( | |
| command.GetArgumentAtIndex(i), 0, 0, &success); | |
| if (!success) { | |
| result.AppendErrorWithFormat("invalid stop hook id: \"%s\".\n", | |
| command.GetArgumentAtIndex(i)); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| success = target.SetStopHookActiveStateByID(user_id, m_enable); | |
| if (!success) { | |
| result.AppendErrorWithFormat("unknown stop hook id: \"%s\".\n", | |
| command.GetArgumentAtIndex(i)); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| } | |
| } | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| return result.Succeeded(); | |
| } | |
| private: | |
| bool m_enable; | |
| }; | |
| #pragma mark CommandObjectTargetStopHookList | |
| // CommandObjectTargetStopHookList | |
| class CommandObjectTargetStopHookList : public CommandObjectParsed { | |
| public: | |
| CommandObjectTargetStopHookList(CommandInterpreter &interpreter) | |
| : CommandObjectParsed(interpreter, "target stop-hook list", | |
| "List all stop-hooks.", | |
| "target stop-hook list [<type>]") {} | |
| ~CommandObjectTargetStopHookList() override = default; | |
| protected: | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target &target = GetSelectedOrDummyTarget(); | |
| size_t num_hooks = target.GetNumStopHooks(); | |
| if (num_hooks == 0) { | |
| result.GetOutputStream().PutCString("No stop hooks.\n"); | |
| } else { | |
| for (size_t i = 0; i < num_hooks; i++) { | |
| Target::StopHookSP this_hook = target.GetStopHookAtIndex(i); | |
| if (i > 0) | |
| result.GetOutputStream().PutCString("\n"); | |
| this_hook->GetDescription(&(result.GetOutputStream()), | |
| eDescriptionLevelFull); | |
| } | |
| } | |
| result.SetStatus(eReturnStatusSuccessFinishResult); | |
| return result.Succeeded(); | |
| } | |
| }; | |
| #pragma mark CommandObjectMultiwordTargetStopHooks | |
| // CommandObjectMultiwordTargetStopHooks | |
| class CommandObjectMultiwordTargetStopHooks : public CommandObjectMultiword { | |
| public: | |
| CommandObjectMultiwordTargetStopHooks(CommandInterpreter &interpreter) | |
| : CommandObjectMultiword( | |
| interpreter, "target stop-hook", | |
| "Commands for operating on debugger target stop-hooks.", | |
| "target stop-hook <subcommand> [<subcommand-options>]") { | |
| LoadSubCommand("add", CommandObjectSP( | |
| new CommandObjectTargetStopHookAdd(interpreter))); | |
| LoadSubCommand( | |
| "delete", | |
| CommandObjectSP(new CommandObjectTargetStopHookDelete(interpreter))); | |
| LoadSubCommand("disable", | |
| CommandObjectSP(new CommandObjectTargetStopHookEnableDisable( | |
| interpreter, false, "target stop-hook disable [<id>]", | |
| "Disable a stop-hook.", "target stop-hook disable"))); | |
| LoadSubCommand("enable", | |
| CommandObjectSP(new CommandObjectTargetStopHookEnableDisable( | |
| interpreter, true, "target stop-hook enable [<id>]", | |
| "Enable a stop-hook.", "target stop-hook enable"))); | |
| LoadSubCommand("list", CommandObjectSP(new CommandObjectTargetStopHookList( | |
| interpreter))); | |
| } | |
| ~CommandObjectMultiwordTargetStopHooks() override = default; | |
| }; | |
| #pragma mark 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("list", | |
| CommandObjectSP(new CommandObjectTargetList(interpreter))); | |
| LoadSubCommand("select", | |
| CommandObjectSP(new CommandObjectTargetSelect(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; |