llvm-project/lldb/source/Commands/CommandObjectTarget.cpp
qxy11 7bd06c41a3
Defer loading all DWOs by default when dumping separate_debug-info (#146166)
### Summary
Currently `target modules dump separate separate-debug-info`
automatically loads up all DWO files, even if deferred loading is
enabled through debug_names. Then, as expected all DWO files (assuming
there is no error loading it), get marked as "loaded".

This change adds the option `--force-load-all-debug-info` or `-f` for
short to force loading all debug_info up, if it hasn't been loaded yet.
Otherwise, it will change default behavior to not load all debug info so
that the correct DWO files will show up for each modules as "loaded" or
not "loaded", which could be helpful in cases where we want to know
which particular DWO files were loaded.

### Testing
#### Unit Tests
Added additional unit tests
`test_dwos_load_json_with_debug_names_default` and
`test_dwos_load_json_with_debug_names_force_load_all` to test both
default behavior and loading with the new flag
`--force-load-all-debug-info`, and changed expected behavior in
`test_dwos_loaded_symbols_on_demand`.
```
bin/lldb-dotest -p TestDumpDwo ~/llvm-project/lldb/test/API/commands/target/dump-separate-debug-info/dwo
```

#### Manual Testing
Compiled a simple binary w/ `--gsplit-dwarf --gpubnames` and loaded it
up:
```
(lldb) target create "./a.out"
Current executable set to '/home/qxy11/hello-world/a.out' (x86_64).
(lldb) help target modules dump separate-debug-info
List the separate debug info symbol files for one or more target modules.

Syntax: target modules dump separate-debug-info <cmd-options> [<filename> [<filename> [...]]]

Command Options Usage:
  target modules dump separate-debug-info [-efj] [<filename> [<filename> [...]]]

       -e ( --errors-only )
            Filter to show only debug info files with errors.

       -f ( --force-load-all-debug-info )
            Load all debug info files.

       -j ( --json )
            Output the details in JSON format.

     This command takes options and free-form arguments.  If your arguments resemble option specifiers (i.e., they start with a - or --), you must use ' -- ' between the end of the
     command options and the beginning of the arguments.
(lldb) target modules dump separate-debug-info --j
[
  {
    "separate-debug-info-files": [
      {  ...
        "dwo_name": "main.dwo",
        "loaded": false
      },
      {  ...
        "dwo_name": "foo.dwo",
        "loaded": false
      },
      { ...
        "dwo_name": "bar.dwo",
        "loaded": false
      }
    ],
  }
]
(lldb) b main
Breakpoint 1: where = a.out`main + 15 at main.cc:3:12, address = 0x00000000000011ff
(lldb) target modules dump separate-debug-info --j
[
  {
    "separate-debug-info-files": [
      { ...
        "dwo_name": "main.dwo",
        "loaded": true,
        "resolved_dwo_path": "/home/qxy11/hello-world/main.dwo"
      },
      { ...
        "dwo_name": "foo.dwo",
        "loaded": false
      },
      { ...
        "dwo_name": "bar.dwo",
        "loaded": false
      }
    ],
  }
]
(lldb) b foo
Breakpoint 2: where = a.out`foo(int) + 11 at foo.cc:12:11, address = 0x000000000000121b
(lldb) target modules dump separate-debug-info --j
[
  {
    "separate-debug-info-files": [
      { ...
        "dwo_name": "main.dwo",
        "loaded": true,
        "resolved_dwo_path": "/home/qxy11/hello-world/main.dwo"
      },
      { ...
        "dwo_name": "foo.dwo",
        "loaded": true,
        "resolved_dwo_path": "/home/qxy11/hello-world/foo.dwo"
      },
      { ...
        "dwo_name": "bar.dwo",
        "loaded": false
      }
    ],
  }
]
(lldb) b bar
Breakpoint 3: where = a.out`bar(int) + 11 at bar.cc:10:9, address = 0x000000000000126b
(lldb) target modules dump separate-debug-info --j
[
  {
    "separate-debug-info-files": [
      { ...
        "dwo_name": "main.dwo",
        "loaded": true,
        "resolved_dwo_path": "/home/qxy11/hello-world/main.dwo"
      },
      { ...
        "dwo_name": "foo.dwo",
        "loaded": true,
        "resolved_dwo_path": "/home/qxy11/hello-world/foo.dwo"
      },
      { ...
        "dwo_name": "bar.dwo",
        "loaded": true,
        "resolved_dwo_path": "/home/qxy11/hello-world/bar.dwo"
      }
    ],
  }
]
```
2025-07-07 12:01:22 -07:00

5380 lines
196 KiB
C++

//===-- CommandObjectTarget.cpp -------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "CommandObjectTarget.h"
#include "lldb/Core/Address.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/IOHandler.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/Section.h"
#include "lldb/DataFormatters/ValueObjectPrinter.h"
#include "lldb/Host/OptionParser.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/CommandOptionArgumentTable.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Interpreter/OptionArgParser.h"
#include "lldb/Interpreter/OptionGroupArchitecture.h"
#include "lldb/Interpreter/OptionGroupBoolean.h"
#include "lldb/Interpreter/OptionGroupFile.h"
#include "lldb/Interpreter/OptionGroupFormat.h"
#include "lldb/Interpreter/OptionGroupPlatform.h"
#include "lldb/Interpreter/OptionGroupPythonClassWithDict.h"
#include "lldb/Interpreter/OptionGroupString.h"
#include "lldb/Interpreter/OptionGroupUInt64.h"
#include "lldb/Interpreter/OptionGroupUUID.h"
#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
#include "lldb/Interpreter/OptionGroupVariable.h"
#include "lldb/Interpreter/Options.h"
#include "lldb/Symbol/CompileUnit.h"
#include "lldb/Symbol/FuncUnwinders.h"
#include "lldb/Symbol/LineTable.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Symbol/SymbolFile.h"
#include "lldb/Symbol/UnwindPlan.h"
#include "lldb/Symbol/VariableList.h"
#include "lldb/Target/ABI.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/SectionLoadList.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/Thread.h"
#include "lldb/Target/ThreadSpec.h"
#include "lldb/Utility/Args.h"
#include "lldb/Utility/ConstString.h"
#include "lldb/Utility/FileSpec.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/State.h"
#include "lldb/Utility/Stream.h"
#include "lldb/Utility/StructuredData.h"
#include "lldb/Utility/Timer.h"
#include "lldb/ValueObject/ValueObjectVariable.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-forward.h"
#include "lldb/lldb-private-enumerations.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Serialization/ObjectFilePCHContainerReader.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormatAdapters.h"
using namespace lldb;
using namespace lldb_private;
static void DumpTargetInfo(uint32_t target_idx, Target *target,
const char *prefix_cstr,
bool show_stopped_process_status, Stream &strm) {
const ArchSpec &target_arch = target->GetArchitecture();
Module *exe_module = target->GetExecutableModulePointer();
char exe_path[PATH_MAX];
bool exe_valid = false;
if (exe_module)
exe_valid = exe_module->GetFileSpec().GetPath(exe_path, sizeof(exe_path));
if (!exe_valid)
::strcpy(exe_path, "<none>");
std::string formatted_label = "";
const std::string &label = target->GetLabel();
if (!label.empty()) {
formatted_label = " (" + label + ")";
}
strm.Printf("%starget #%u%s: %s", prefix_cstr ? prefix_cstr : "", target_idx,
formatted_label.data(), exe_path);
uint32_t properties = 0;
if (target_arch.IsValid()) {
strm.Printf(" ( arch=");
target_arch.DumpTriple(strm.AsRawOstream());
properties++;
}
PlatformSP platform_sp(target->GetPlatform());
if (platform_sp)
strm.Format("{0}platform={1}", properties++ > 0 ? ", " : " ( ",
platform_sp->GetName());
ProcessSP process_sp(target->GetProcessSP());
bool show_process_status = false;
if (process_sp) {
lldb::pid_t pid = process_sp->GetID();
StateType state = process_sp->GetState();
if (show_stopped_process_status)
show_process_status = StateIsStoppedState(state, true);
const char *state_cstr = StateAsCString(state);
if (pid != LLDB_INVALID_PROCESS_ID)
strm.Printf("%spid=%" PRIu64, properties++ > 0 ? ", " : " ( ", pid);
strm.Printf("%sstate=%s", properties++ > 0 ? ", " : " ( ", state_cstr);
}
if (properties > 0)
strm.PutCString(" )\n");
else
strm.EOL();
if (show_process_status) {
const bool only_threads_with_stop_reason = true;
const uint32_t start_frame = 0;
const uint32_t num_frames = 1;
const uint32_t num_frames_with_source = 1;
const bool stop_format = false;
process_sp->GetStatus(strm);
process_sp->GetThreadStatus(strm, only_threads_with_stop_reason,
start_frame, num_frames, num_frames_with_source,
stop_format);
}
}
static uint32_t DumpTargetList(TargetList &target_list,
bool show_stopped_process_status, Stream &strm) {
const uint32_t num_targets = target_list.GetNumTargets();
if (num_targets) {
TargetSP selected_target_sp(target_list.GetSelectedTarget());
strm.PutCString("Current targets:\n");
for (uint32_t i = 0; i < num_targets; ++i) {
TargetSP target_sp(target_list.GetTargetAtIndex(i));
if (target_sp) {
bool is_selected = target_sp.get() == selected_target_sp.get();
DumpTargetInfo(i, target_sp.get(), is_selected ? "* " : " ",
show_stopped_process_status, strm);
}
}
}
return num_targets;
}
#define LLDB_OPTIONS_target_dependents
#include "CommandOptions.inc"
class OptionGroupDependents : public OptionGroup {
public:
OptionGroupDependents() = default;
~OptionGroupDependents() override = default;
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
return llvm::ArrayRef(g_target_dependents_options);
}
Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
ExecutionContext *execution_context) override {
Status error;
// For compatibility no value means don't load dependents.
if (option_value.empty()) {
m_load_dependent_files = eLoadDependentsNo;
return error;
}
const char short_option =
g_target_dependents_options[option_idx].short_option;
if (short_option == 'd') {
LoadDependentFiles tmp_load_dependents;
tmp_load_dependents = (LoadDependentFiles)OptionArgParser::ToOptionEnum(
option_value, g_target_dependents_options[option_idx].enum_values, 0,
error);
if (error.Success())
m_load_dependent_files = tmp_load_dependents;
} else {
error = Status::FromErrorStringWithFormat(
"unrecognized short option '%c'", short_option);
}
return error;
}
Status SetOptionValue(uint32_t, const char *, ExecutionContext *) = delete;
void OptionParsingStarting(ExecutionContext *execution_context) override {
m_load_dependent_files = eLoadDependentsDefault;
}
LoadDependentFiles m_load_dependent_files;
private:
OptionGroupDependents(const OptionGroupDependents &) = delete;
const OptionGroupDependents &
operator=(const OptionGroupDependents &) = delete;
};
#pragma mark CommandObjectTargetCreate
class CommandObjectTargetCreate : public CommandObjectParsed {
public:
CommandObjectTargetCreate(CommandInterpreter &interpreter)
: CommandObjectParsed(
interpreter, "target create",
"Create a target using the argument as the main executable.",
nullptr),
m_platform_options(true), // Include the --platform option.
m_core_file(LLDB_OPT_SET_1, false, "core", 'c', 0, eArgTypeFilename,
"Fullpath to a core file to use for this target."),
m_label(LLDB_OPT_SET_1, false, "label", 'l', 0, eArgTypeName,
"Optional name for this target.", nullptr),
m_symbol_file(LLDB_OPT_SET_1, false, "symfile", 's', 0,
eArgTypeFilename,
"Fullpath to a stand alone debug "
"symbols file for when debug symbols "
"are not in the executable."),
m_remote_file(
LLDB_OPT_SET_1, false, "remote-file", 'r', 0, eArgTypeFilename,
"Fullpath to the file on the remote host if debugging remotely.") {
AddSimpleArgumentList(eArgTypeFilename);
m_option_group.Append(&m_arch_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
m_option_group.Append(&m_platform_options, LLDB_OPT_SET_ALL, 1);
m_option_group.Append(&m_core_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
m_option_group.Append(&m_label, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
m_option_group.Append(&m_symbol_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
m_option_group.Append(&m_remote_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
m_option_group.Append(&m_add_dependents, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
m_option_group.Finalize();
}
~CommandObjectTargetCreate() override = default;
Options *GetOptions() override { return &m_option_group; }
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
const size_t argc = command.GetArgumentCount();
FileSpec core_file(m_core_file.GetOptionValue().GetCurrentValue());
FileSpec remote_file(m_remote_file.GetOptionValue().GetCurrentValue());
if (core_file) {
auto file = FileSystem::Instance().Open(
core_file, lldb_private::File::eOpenOptionReadOnly);
if (!file) {
result.AppendErrorWithFormatv("Cannot open '{0}': {1}.",
core_file.GetPath(),
llvm::toString(file.takeError()));
return;
}
}
if (argc == 1 || core_file || remote_file) {
FileSpec symfile(m_symbol_file.GetOptionValue().GetCurrentValue());
if (symfile) {
auto file = FileSystem::Instance().Open(
symfile, lldb_private::File::eOpenOptionReadOnly);
if (!file) {
result.AppendErrorWithFormatv("Cannot open '{0}': {1}.",
symfile.GetPath(),
llvm::toString(file.takeError()));
return;
}
}
const char *file_path = command.GetArgumentAtIndex(0);
LLDB_SCOPED_TIMERF("(lldb) target create '%s'", file_path);
bool must_set_platform_path = false;
Debugger &debugger = GetDebugger();
TargetSP target_sp;
llvm::StringRef arch_cstr = m_arch_option.GetArchitectureName();
Status error(debugger.GetTargetList().CreateTarget(
debugger, file_path, arch_cstr,
m_add_dependents.m_load_dependent_files, &m_platform_options,
target_sp));
if (!target_sp) {
result.AppendError(error.AsCString());
return;
}
const llvm::StringRef label =
m_label.GetOptionValue().GetCurrentValueAsRef();
if (!label.empty()) {
if (auto E = target_sp->SetLabel(label))
result.SetError(std::move(E));
return;
}
auto on_error = llvm::make_scope_exit(
[&target_list = debugger.GetTargetList(), &target_sp]() {
target_list.DeleteTarget(target_sp);
});
// Only get the platform after we create the target because we might
// have switched platforms depending on what the arguments were to
// CreateTarget() we can't rely on the selected platform.
PlatformSP platform_sp = target_sp->GetPlatform();
FileSpec file_spec;
if (file_path) {
file_spec.SetFile(file_path, FileSpec::Style::native);
FileSystem::Instance().Resolve(file_spec);
// Try to resolve the exe based on PATH and/or platform-specific
// suffixes, but only if using the host platform.
if (platform_sp && platform_sp->IsHost() &&
!FileSystem::Instance().Exists(file_spec))
FileSystem::Instance().ResolveExecutableLocation(file_spec);
}
if (remote_file) {
if (platform_sp) {
// I have a remote file.. two possible cases
if (file_spec && FileSystem::Instance().Exists(file_spec)) {
// if the remote file does not exist, push it there
if (!platform_sp->GetFileExists(remote_file)) {
Status err = platform_sp->PutFile(file_spec, remote_file);
if (err.Fail()) {
result.AppendError(err.AsCString());
return;
}
}
} else {
// there is no local file and we need one
// in order to make the remote ---> local transfer we need a
// platform
// TODO: if the user has passed in a --platform argument, use it
// to fetch the right platform
if (file_path) {
// copy the remote file to the local file
Status err = platform_sp->GetFile(remote_file, file_spec);
if (err.Fail()) {
result.AppendError(err.AsCString());
return;
}
} else {
// If the remote file exists, we can debug reading that out of
// memory. If the platform is already connected to an lldb-server
// then we can at least check the file exists remotely. Otherwise
// we'll just have to trust that it will be there when we do
// process connect.
// I don't do this for the host platform because it seems odd to
// support supplying a remote file but no local file for a local
// debug session.
if (platform_sp->IsHost()) {
result.AppendError("Supply a local file, not a remote file, "
"when debugging on the host.");
return;
}
if (platform_sp->IsConnected() && !platform_sp->GetFileExists(remote_file)) {
result.AppendError("remote --> local transfer without local "
"path is not implemented yet");
return;
}
// Since there's only a remote file, we need to set the executable
// file spec to the remote one.
ProcessLaunchInfo launch_info = target_sp->GetProcessLaunchInfo();
launch_info.SetExecutableFile(FileSpec(remote_file), true);
target_sp->SetProcessLaunchInfo(launch_info);
}
}
} else {
result.AppendError("no platform found for target");
return;
}
}
if (symfile || remote_file) {
ModuleSP module_sp(target_sp->GetExecutableModule());
if (module_sp) {
if (symfile)
module_sp->SetSymbolFileFileSpec(symfile);
if (remote_file) {
std::string remote_path = remote_file.GetPath();
target_sp->SetArg0(remote_path.c_str());
module_sp->SetPlatformFileSpec(remote_file);
}
}
}
if (must_set_platform_path) {
ModuleSpec main_module_spec(file_spec);
ModuleSP module_sp =
target_sp->GetOrCreateModule(main_module_spec, true /* notify */);
if (module_sp)
module_sp->SetPlatformFileSpec(remote_file);
}
if (core_file) {
FileSpec core_file_dir;
core_file_dir.SetDirectory(core_file.GetDirectory());
target_sp->AppendExecutableSearchPaths(core_file_dir);
ProcessSP process_sp(target_sp->CreateProcess(
GetDebugger().GetListener(), llvm::StringRef(), &core_file, false));
if (process_sp) {
// Seems weird that we Launch a core file, but that is what we
// do!
error = process_sp->LoadCore();
if (error.Fail()) {
result.AppendError(error.AsCString("unknown core file format"));
return;
} else {
result.AppendMessageWithFormatv(
"Core file '{0}' ({1}) was loaded.\n", core_file.GetPath(),
target_sp->GetArchitecture().GetArchitectureName());
result.SetStatus(eReturnStatusSuccessFinishNoResult);
on_error.release();
}
} else {
result.AppendErrorWithFormatv("Unknown core file format '{0}'\n",
core_file.GetPath());
}
} else {
result.AppendMessageWithFormat(
"Current executable set to '%s' (%s).\n",
file_spec.GetPath().c_str(),
target_sp->GetArchitecture().GetArchitectureName());
result.SetStatus(eReturnStatusSuccessFinishNoResult);
on_error.release();
}
} else {
result.AppendErrorWithFormat("'%s' takes exactly one executable path "
"argument, or use the --core option.\n",
m_cmd_name.c_str());
}
}
private:
OptionGroupOptions m_option_group;
OptionGroupArchitecture m_arch_option;
OptionGroupPlatform m_platform_options;
OptionGroupFile m_core_file;
OptionGroupString m_label;
OptionGroupFile m_symbol_file;
OptionGroupFile m_remote_file;
OptionGroupDependents m_add_dependents;
};
#pragma mark CommandObjectTargetList
class CommandObjectTargetList : public CommandObjectParsed {
public:
CommandObjectTargetList(CommandInterpreter &interpreter)
: CommandObjectParsed(
interpreter, "target list",
"List all current targets in the current debug session.", nullptr) {
}
~CommandObjectTargetList() override = default;
protected:
void DoExecute(Args &args, CommandReturnObject &result) override {
Stream &strm = result.GetOutputStream();
bool show_stopped_process_status = false;
if (DumpTargetList(GetDebugger().GetTargetList(),
show_stopped_process_status, strm) == 0) {
strm.PutCString("No targets.\n");
}
result.SetStatus(eReturnStatusSuccessFinishResult);
}
};
#pragma mark CommandObjectTargetSelect
class CommandObjectTargetSelect : public CommandObjectParsed {
public:
CommandObjectTargetSelect(CommandInterpreter &interpreter)
: CommandObjectParsed(
interpreter, "target select",
"Select a target as the current target by target index.", nullptr) {
AddSimpleArgumentList(eArgTypeTargetID);
}
~CommandObjectTargetSelect() override = default;
protected:
void DoExecute(Args &args, CommandReturnObject &result) override {
if (args.GetArgumentCount() == 1) {
const char *target_identifier = args.GetArgumentAtIndex(0);
uint32_t target_idx = LLDB_INVALID_INDEX32;
TargetList &target_list = GetDebugger().GetTargetList();
const uint32_t num_targets = target_list.GetNumTargets();
if (llvm::to_integer(target_identifier, target_idx)) {
if (target_idx < num_targets) {
target_list.SetSelectedTarget(target_idx);
Stream &strm = result.GetOutputStream();
bool show_stopped_process_status = false;
DumpTargetList(target_list, show_stopped_process_status, strm);
result.SetStatus(eReturnStatusSuccessFinishResult);
} else {
if (num_targets > 0) {
result.AppendErrorWithFormat(
"index %u is out of range, valid target indexes are 0 - %u\n",
target_idx, num_targets - 1);
} else {
result.AppendErrorWithFormat(
"index %u is out of range since there are no active targets\n",
target_idx);
}
}
} else {
for (size_t i = 0; i < num_targets; i++) {
if (TargetSP target_sp = target_list.GetTargetAtIndex(i)) {
const std::string &label = target_sp->GetLabel();
if (!label.empty() && label == target_identifier) {
target_idx = i;
break;
}
}
}
if (target_idx != LLDB_INVALID_INDEX32) {
target_list.SetSelectedTarget(target_idx);
Stream &strm = result.GetOutputStream();
bool show_stopped_process_status = false;
DumpTargetList(target_list, show_stopped_process_status, strm);
result.SetStatus(eReturnStatusSuccessFinishResult);
} else {
result.AppendErrorWithFormat("invalid index string value '%s'\n",
target_identifier);
}
}
} else {
result.AppendError(
"'target select' takes a single argument: a target index\n");
}
}
};
#pragma mark CommandObjectTargetDelete
class CommandObjectTargetDelete : public CommandObjectParsed {
public:
CommandObjectTargetDelete(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "target delete",
"Delete one or more targets by target index.",
nullptr),
m_all_option(LLDB_OPT_SET_1, false, "all", 'a', "Delete all targets.",
false, true),
m_cleanup_option(
LLDB_OPT_SET_1, false, "clean", 'c',
"Perform extra cleanup to minimize memory consumption after "
"deleting the target. "
"By default, LLDB will keep in memory any modules previously "
"loaded by the target as well "
"as all of its debug info. Specifying --clean will unload all of "
"these shared modules and "
"cause them to be reparsed again the next time the target is run",
false, true) {
m_option_group.Append(&m_all_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
m_option_group.Append(&m_cleanup_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
m_option_group.Finalize();
AddSimpleArgumentList(eArgTypeTargetID, eArgRepeatStar);
}
~CommandObjectTargetDelete() override = default;
Options *GetOptions() override { return &m_option_group; }
protected:
void DoExecute(Args &args, CommandReturnObject &result) override {
const size_t argc = args.GetArgumentCount();
std::vector<TargetSP> delete_target_list;
TargetList &target_list = GetDebugger().GetTargetList();
TargetSP target_sp;
if (m_all_option.GetOptionValue()) {
for (size_t i = 0; i < target_list.GetNumTargets(); ++i)
delete_target_list.push_back(target_list.GetTargetAtIndex(i));
} else if (argc > 0) {
const uint32_t num_targets = target_list.GetNumTargets();
// Bail out if don't have any targets.
if (num_targets == 0) {
result.AppendError("no targets to delete");
return;
}
for (auto &entry : args.entries()) {
uint32_t target_idx;
if (entry.ref().getAsInteger(0, target_idx)) {
result.AppendErrorWithFormat("invalid target index '%s'\n",
entry.c_str());
return;
}
if (target_idx < num_targets) {
target_sp = target_list.GetTargetAtIndex(target_idx);
if (target_sp) {
delete_target_list.push_back(target_sp);
continue;
}
}
if (num_targets > 1)
result.AppendErrorWithFormat("target index %u is out of range, valid "
"target indexes are 0 - %u\n",
target_idx, num_targets - 1);
else
result.AppendErrorWithFormat(
"target index %u is out of range, the only valid index is 0\n",
target_idx);
return;
}
} else {
target_sp = target_list.GetSelectedTarget();
if (!target_sp) {
result.AppendErrorWithFormat("no target is currently selected\n");
return;
}
delete_target_list.push_back(target_sp);
}
const size_t num_targets_to_delete = delete_target_list.size();
for (size_t idx = 0; idx < num_targets_to_delete; ++idx) {
target_sp = delete_target_list[idx];
target_list.DeleteTarget(target_sp);
target_sp->Destroy();
}
// If "--clean" was specified, prune any orphaned shared modules from the
// global shared module list
if (m_cleanup_option.GetOptionValue()) {
const bool mandatory = true;
ModuleList::RemoveOrphanSharedModules(mandatory);
}
result.GetOutputStream().Printf("%u targets deleted.\n",
(uint32_t)num_targets_to_delete);
result.SetStatus(eReturnStatusSuccessFinishResult);
}
OptionGroupOptions m_option_group;
OptionGroupBoolean m_all_option;
OptionGroupBoolean m_cleanup_option;
};
class CommandObjectTargetShowLaunchEnvironment : public CommandObjectParsed {
public:
CommandObjectTargetShowLaunchEnvironment(CommandInterpreter &interpreter)
: CommandObjectParsed(
interpreter, "target show-launch-environment",
"Shows the environment being passed to the process when launched, "
"taking info account 3 settings: target.env-vars, "
"target.inherit-env and target.unset-env-vars.",
nullptr, eCommandRequiresTarget) {}
~CommandObjectTargetShowLaunchEnvironment() override = default;
protected:
void DoExecute(Args &args, CommandReturnObject &result) override {
Target *target = m_exe_ctx.GetTargetPtr();
Environment env = target->GetEnvironment();
std::vector<Environment::value_type *> env_vector;
env_vector.reserve(env.size());
for (auto &KV : env)
env_vector.push_back(&KV);
std::sort(env_vector.begin(), env_vector.end(),
[](Environment::value_type *a, Environment::value_type *b) {
return a->first() < b->first();
});
auto &strm = result.GetOutputStream();
for (auto &KV : env_vector)
strm.Format("{0}={1}\n", KV->first(), KV->second);
result.SetStatus(eReturnStatusSuccessFinishResult);
}
};
#pragma mark CommandObjectTargetVariable
class CommandObjectTargetVariable : public CommandObjectParsed {
static const uint32_t SHORT_OPTION_FILE = 0x66696c65; // 'file'
static const uint32_t SHORT_OPTION_SHLB = 0x73686c62; // 'shlb'
public:
CommandObjectTargetVariable(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "target variable",
"Read global variables for the current target, "
"before or while running a process.",
nullptr, eCommandRequiresTarget),
m_option_variable(false), // Don't include frame options
m_option_format(eFormatDefault),
m_option_compile_units(LLDB_OPT_SET_1, false, "file", SHORT_OPTION_FILE,
0, eArgTypeFilename,
"A basename or fullpath to a file that contains "
"global variables. This option can be "
"specified multiple times."),
m_option_shared_libraries(
LLDB_OPT_SET_1, false, "shlib", SHORT_OPTION_SHLB, 0,
eArgTypeFilename,
"A basename or fullpath to a shared library to use in the search "
"for global "
"variables. This option can be specified multiple times.") {
AddSimpleArgumentList(eArgTypeVarName, eArgRepeatPlus);
m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
m_option_group.Append(&m_option_variable, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
m_option_group.Append(&m_option_format,
OptionGroupFormat::OPTION_GROUP_FORMAT |
OptionGroupFormat::OPTION_GROUP_GDB_FMT,
LLDB_OPT_SET_1);
m_option_group.Append(&m_option_compile_units, LLDB_OPT_SET_ALL,
LLDB_OPT_SET_1);
m_option_group.Append(&m_option_shared_libraries, LLDB_OPT_SET_ALL,
LLDB_OPT_SET_1);
m_option_group.Finalize();
}
~CommandObjectTargetVariable() override = default;
void DumpValueObject(Stream &s, VariableSP &var_sp, ValueObjectSP &valobj_sp,
const char *root_name) {
DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions());
if (!valobj_sp->GetTargetSP()->GetDisplayRuntimeSupportValues() &&
valobj_sp->IsRuntimeSupportValue())
return;
switch (var_sp->GetScope()) {
case eValueTypeVariableGlobal:
if (m_option_variable.show_scope)
s.PutCString("GLOBAL: ");
break;
case eValueTypeVariableStatic:
if (m_option_variable.show_scope)
s.PutCString("STATIC: ");
break;
case eValueTypeVariableArgument:
if (m_option_variable.show_scope)
s.PutCString(" ARG: ");
break;
case eValueTypeVariableLocal:
if (m_option_variable.show_scope)
s.PutCString(" LOCAL: ");
break;
case eValueTypeVariableThreadLocal:
if (m_option_variable.show_scope)
s.PutCString("THREAD: ");
break;
default:
break;
}
if (m_option_variable.show_decl) {
bool show_fullpaths = false;
bool show_module = true;
if (var_sp->DumpDeclaration(&s, show_fullpaths, show_module))
s.PutCString(": ");
}
const Format format = m_option_format.GetFormat();
if (format != eFormatDefault)
options.SetFormat(format);
options.SetRootValueObjectName(root_name);
if (llvm::Error error = valobj_sp->Dump(s, options))
s << "error: " << toString(std::move(error));
}
static size_t GetVariableCallback(void *baton, const char *name,
VariableList &variable_list) {
size_t old_size = variable_list.GetSize();
Target *target = static_cast<Target *>(baton);
if (target)
target->GetImages().FindGlobalVariables(ConstString(name), UINT32_MAX,
variable_list);
return variable_list.GetSize() - old_size;
}
Options *GetOptions() override { return &m_option_group; }
protected:
void DumpGlobalVariableList(const ExecutionContext &exe_ctx,
const SymbolContext &sc,
const VariableList &variable_list,
CommandReturnObject &result) {
Stream &s = result.GetOutputStream();
if (variable_list.Empty())
return;
if (sc.module_sp) {
if (sc.comp_unit) {
s.Format("Global variables for {0} in {1}:\n",
sc.comp_unit->GetPrimaryFile(), sc.module_sp->GetFileSpec());
} else {
s.Printf("Global variables for %s\n",
sc.module_sp->GetFileSpec().GetPath().c_str());
}
} else if (sc.comp_unit) {
s.Format("Global variables for {0}\n", sc.comp_unit->GetPrimaryFile());
}
for (VariableSP var_sp : variable_list) {
if (!var_sp)
continue;
ValueObjectSP valobj_sp(ValueObjectVariable::Create(
exe_ctx.GetBestExecutionContextScope(), var_sp));
if (valobj_sp) {
result.GetValueObjectList().Append(valobj_sp);
DumpValueObject(s, var_sp, valobj_sp, var_sp->GetName().GetCString());
}
}
}
void DoExecute(Args &args, CommandReturnObject &result) override {
Target *target = m_exe_ctx.GetTargetPtr();
const size_t argc = args.GetArgumentCount();
if (argc > 0) {
for (const Args::ArgEntry &arg : args) {
VariableList variable_list;
ValueObjectList valobj_list;
size_t matches = 0;
bool use_var_name = false;
if (m_option_variable.use_regex) {
RegularExpression regex(arg.ref());
if (!regex.IsValid()) {
result.GetErrorStream().Printf(
"error: invalid regular expression: '%s'\n", arg.c_str());
return;
}
use_var_name = true;
target->GetImages().FindGlobalVariables(regex, UINT32_MAX,
variable_list);
matches = variable_list.GetSize();
} else {
Status error(Variable::GetValuesForVariableExpressionPath(
arg.c_str(), m_exe_ctx.GetBestExecutionContextScope(),
GetVariableCallback, target, variable_list, valobj_list));
matches = variable_list.GetSize();
}
if (matches == 0) {
result.AppendErrorWithFormat("can't find global variable '%s'",
arg.c_str());
return;
} else {
for (uint32_t global_idx = 0; global_idx < matches; ++global_idx) {
VariableSP var_sp(variable_list.GetVariableAtIndex(global_idx));
if (var_sp) {
ValueObjectSP valobj_sp(
valobj_list.GetValueObjectAtIndex(global_idx));
if (!valobj_sp)
valobj_sp = ValueObjectVariable::Create(
m_exe_ctx.GetBestExecutionContextScope(), var_sp);
if (valobj_sp)
DumpValueObject(result.GetOutputStream(), var_sp, valobj_sp,
use_var_name ? var_sp->GetName().GetCString()
: arg.c_str());
}
}
}
}
} else {
const FileSpecList &compile_units =
m_option_compile_units.GetOptionValue().GetCurrentValue();
const FileSpecList &shlibs =
m_option_shared_libraries.GetOptionValue().GetCurrentValue();
SymbolContextList sc_list;
const size_t num_compile_units = compile_units.GetSize();
const size_t num_shlibs = shlibs.GetSize();
if (num_compile_units == 0 && num_shlibs == 0) {
bool success = false;
StackFrame *frame = m_exe_ctx.GetFramePtr();
CompileUnit *comp_unit = nullptr;
if (frame) {
SymbolContext sc = frame->GetSymbolContext(eSymbolContextCompUnit);
comp_unit = sc.comp_unit;
if (sc.comp_unit) {
const bool can_create = true;
VariableListSP comp_unit_varlist_sp(
sc.comp_unit->GetVariableList(can_create));
if (comp_unit_varlist_sp) {
size_t count = comp_unit_varlist_sp->GetSize();
if (count > 0) {
DumpGlobalVariableList(m_exe_ctx, sc, *comp_unit_varlist_sp,
result);
success = true;
}
}
}
}
if (!success) {
if (frame) {
if (comp_unit)
result.AppendErrorWithFormatv(
"no global variables in current compile unit: {0}\n",
comp_unit->GetPrimaryFile());
else
result.AppendErrorWithFormat(
"no debug information for frame %u\n",
frame->GetFrameIndex());
} else
result.AppendError("'target variable' takes one or more global "
"variable names as arguments\n");
}
} else {
SymbolContextList sc_list;
// We have one or more compile unit or shlib
if (num_shlibs > 0) {
for (size_t shlib_idx = 0; shlib_idx < num_shlibs; ++shlib_idx) {
const FileSpec module_file(shlibs.GetFileSpecAtIndex(shlib_idx));
ModuleSpec module_spec(module_file);
ModuleSP module_sp(
target->GetImages().FindFirstModule(module_spec));
if (module_sp) {
if (num_compile_units > 0) {
for (size_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx)
module_sp->FindCompileUnits(
compile_units.GetFileSpecAtIndex(cu_idx), sc_list);
} else {
SymbolContext sc;
sc.module_sp = module_sp;
sc_list.Append(sc);
}
} else {
// Didn't find matching shlib/module in target...
result.AppendErrorWithFormat(
"target doesn't contain the specified shared library: %s\n",
module_file.GetPath().c_str());
}
}
} else {
// No shared libraries, we just want to find globals for the compile
// units files that were specified
for (size_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx)
target->GetImages().FindCompileUnits(
compile_units.GetFileSpecAtIndex(cu_idx), sc_list);
}
for (const SymbolContext &sc : sc_list) {
if (sc.comp_unit) {
const bool can_create = true;
VariableListSP comp_unit_varlist_sp(
sc.comp_unit->GetVariableList(can_create));
if (comp_unit_varlist_sp)
DumpGlobalVariableList(m_exe_ctx, sc, *comp_unit_varlist_sp,
result);
} 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, result);
}
}
}
}
m_interpreter.PrintWarningsIfNecessary(result.GetOutputStream(),
m_cmd_name);
}
OptionGroupOptions m_option_group;
OptionGroupVariable m_option_variable;
OptionGroupFormat m_option_format;
OptionGroupFileList m_option_compile_units;
OptionGroupFileList m_option_shared_libraries;
OptionGroupValueObjectDisplay m_varobj_options;
};
#pragma mark CommandObjectTargetModulesSearchPathsAdd
class CommandObjectTargetModulesSearchPathsAdd : public CommandObjectParsed {
public:
CommandObjectTargetModulesSearchPathsAdd(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "target modules search-paths add",
"Add new image search paths substitution pairs to "
"the current target.",
nullptr, eCommandRequiresTarget) {
CommandArgumentEntry arg;
CommandArgumentData old_prefix_arg;
CommandArgumentData new_prefix_arg;
// Define the first variant of this arg pair.
old_prefix_arg.arg_type = eArgTypeOldPathPrefix;
old_prefix_arg.arg_repetition = eArgRepeatPairPlus;
// Define the first variant of this arg pair.
new_prefix_arg.arg_type = eArgTypeNewPathPrefix;
new_prefix_arg.arg_repetition = eArgRepeatPairPlus;
// There are two required arguments that must always occur together, i.e.
// an argument "pair". Because they must always occur together, they are
// treated as two variants of one argument rather than two independent
// arguments. Push them both into the first argument position for
// m_arguments...
arg.push_back(old_prefix_arg);
arg.push_back(new_prefix_arg);
m_arguments.push_back(arg);
}
~CommandObjectTargetModulesSearchPathsAdd() override = default;
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
Target &target = GetTarget();
const size_t argc = command.GetArgumentCount();
if (argc & 1) {
result.AppendError("add requires an even number of arguments\n");
} else {
for (size_t i = 0; i < argc; i += 2) {
const char *from = command.GetArgumentAtIndex(i);
const char *to = command.GetArgumentAtIndex(i + 1);
if (from[0] && to[0]) {
Log *log = GetLog(LLDBLog::Host);
if (log) {
LLDB_LOGF(log,
"target modules search path adding ImageSearchPath "
"pair: '%s' -> '%s'",
from, to);
}
bool last_pair = ((argc - i) == 2);
target.GetImageSearchPathList().Append(
from, to, last_pair); // Notify if this is the last pair
result.SetStatus(eReturnStatusSuccessFinishNoResult);
} else {
if (from[0])
result.AppendError("<path-prefix> can't be empty\n");
else
result.AppendError("<new-path-prefix> can't be empty\n");
}
}
}
}
};
#pragma mark CommandObjectTargetModulesSearchPathsClear
class CommandObjectTargetModulesSearchPathsClear : public CommandObjectParsed {
public:
CommandObjectTargetModulesSearchPathsClear(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "target modules search-paths clear",
"Clear all current image search path substitution "
"pairs from the current target.",
"target modules search-paths clear",
eCommandRequiresTarget) {}
~CommandObjectTargetModulesSearchPathsClear() override = default;
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
Target &target = GetTarget();
bool notify = true;
target.GetImageSearchPathList().Clear(notify);
result.SetStatus(eReturnStatusSuccessFinishNoResult);
}
};
#pragma mark CommandObjectTargetModulesSearchPathsInsert
class CommandObjectTargetModulesSearchPathsInsert : public CommandObjectParsed {
public:
CommandObjectTargetModulesSearchPathsInsert(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "target modules search-paths insert",
"Insert a new image search path substitution pair "
"into the current target at the specified index.",
nullptr, eCommandRequiresTarget) {
CommandArgumentEntry arg1;
CommandArgumentEntry arg2;
CommandArgumentData index_arg;
CommandArgumentData old_prefix_arg;
CommandArgumentData new_prefix_arg;
// Define the first and only variant of this arg.
index_arg.arg_type = eArgTypeIndex;
index_arg.arg_repetition = eArgRepeatPlain;
// Put the one and only variant into the first arg for m_arguments:
arg1.push_back(index_arg);
// Define the first variant of this arg pair.
old_prefix_arg.arg_type = eArgTypeOldPathPrefix;
old_prefix_arg.arg_repetition = eArgRepeatPairPlus;
// Define the first variant of this arg pair.
new_prefix_arg.arg_type = eArgTypeNewPathPrefix;
new_prefix_arg.arg_repetition = eArgRepeatPairPlus;
// There are two required arguments that must always occur together, i.e.
// an argument "pair". Because they must always occur together, they are
// treated as two variants of one argument rather than two independent
// arguments. Push them both into the same argument position for
// m_arguments...
arg2.push_back(old_prefix_arg);
arg2.push_back(new_prefix_arg);
// Add arguments to m_arguments.
m_arguments.push_back(arg1);
m_arguments.push_back(arg2);
}
~CommandObjectTargetModulesSearchPathsInsert() override = default;
void
HandleArgumentCompletion(CompletionRequest &request,
OptionElementVector &opt_element_vector) override {
if (!m_exe_ctx.HasTargetScope() || request.GetCursorIndex() != 0)
return;
Target *target = m_exe_ctx.GetTargetPtr();
const PathMappingList &list = target->GetImageSearchPathList();
const size_t num = list.GetSize();
ConstString old_path, new_path;
for (size_t i = 0; i < num; ++i) {
if (!list.GetPathsAtIndex(i, old_path, new_path))
break;
StreamString strm;
strm << old_path << " -> " << new_path;
request.TryCompleteCurrentArg(std::to_string(i), strm.GetString());
}
}
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
Target &target = GetTarget();
size_t argc = command.GetArgumentCount();
// check for at least 3 arguments and an odd number of parameters
if (argc >= 3 && argc & 1) {
uint32_t insert_idx;
if (!llvm::to_integer(command.GetArgumentAtIndex(0), insert_idx)) {
result.AppendErrorWithFormat(
"<index> parameter is not an integer: '%s'.\n",
command.GetArgumentAtIndex(0));
return;
}
// shift off the index
command.Shift();
argc = command.GetArgumentCount();
for (uint32_t i = 0; i < argc; i += 2, ++insert_idx) {
const char *from = command.GetArgumentAtIndex(i);
const char *to = command.GetArgumentAtIndex(i + 1);
if (from[0] && to[0]) {
bool last_pair = ((argc - i) == 2);
target.GetImageSearchPathList().Insert(from, to, insert_idx,
last_pair);
result.SetStatus(eReturnStatusSuccessFinishNoResult);
} else {
if (from[0])
result.AppendError("<path-prefix> can't be empty\n");
else
result.AppendError("<new-path-prefix> can't be empty\n");
return;
}
}
} else {
result.AppendError("insert requires at least three arguments\n");
}
}
};
#pragma mark CommandObjectTargetModulesSearchPathsList
class CommandObjectTargetModulesSearchPathsList : public CommandObjectParsed {
public:
CommandObjectTargetModulesSearchPathsList(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "target modules search-paths list",
"List all current image search path substitution "
"pairs in the current target.",
"target modules search-paths list",
eCommandRequiresTarget) {}
~CommandObjectTargetModulesSearchPathsList() override = default;
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
Target &target = GetTarget();
target.GetImageSearchPathList().Dump(&result.GetOutputStream());
result.SetStatus(eReturnStatusSuccessFinishResult);
}
};
#pragma mark CommandObjectTargetModulesSearchPathsQuery
class CommandObjectTargetModulesSearchPathsQuery : public CommandObjectParsed {
public:
CommandObjectTargetModulesSearchPathsQuery(CommandInterpreter &interpreter)
: CommandObjectParsed(
interpreter, "target modules search-paths query",
"Transform a path using the first applicable image search path.",
nullptr, eCommandRequiresTarget) {
AddSimpleArgumentList(eArgTypeDirectoryName);
}
~CommandObjectTargetModulesSearchPathsQuery() override = default;
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
Target &target = GetTarget();
if (command.GetArgumentCount() != 1) {
result.AppendError("query requires one argument\n");
return;
}
ConstString orig(command.GetArgumentAtIndex(0));
ConstString transformed;
if (target.GetImageSearchPathList().RemapPath(orig, transformed))
result.GetOutputStream().Printf("%s\n", transformed.GetCString());
else
result.GetOutputStream().Printf("%s\n", orig.GetCString());
result.SetStatus(eReturnStatusSuccessFinishResult);
}
};
// Static Helper functions
static void DumpModuleArchitecture(Stream &strm, Module *module,
bool full_triple, uint32_t width) {
if (module) {
StreamString arch_strm;
if (full_triple)
module->GetArchitecture().DumpTriple(arch_strm.AsRawOstream());
else
arch_strm.PutCString(module->GetArchitecture().GetArchitectureName());
std::string arch_str = std::string(arch_strm.GetString());
if (width)
strm.Printf("%-*s", width, arch_str.c_str());
else
strm.PutCString(arch_str);
}
}
static void DumpModuleUUID(Stream &strm, Module *module) {
if (module && module->GetUUID().IsValid())
module->GetUUID().Dump(strm);
else
strm.PutCString(" ");
}
static uint32_t DumpCompileUnitLineTable(CommandInterpreter &interpreter,
Stream &strm, Module *module,
const FileSpec &file_spec,
lldb::DescriptionLevel desc_level) {
uint32_t num_matches = 0;
if (module) {
SymbolContextList sc_list;
num_matches = module->ResolveSymbolContextsForFileSpec(
file_spec, 0, false, eSymbolContextCompUnit, sc_list);
bool first_module = true;
for (const SymbolContext &sc : sc_list) {
if (!first_module)
strm << "\n\n";
strm << "Line table for " << sc.comp_unit->GetPrimaryFile() << " in `"
<< module->GetFileSpec().GetFilename() << "\n";
LineTable *line_table = sc.comp_unit->GetLineTable();
if (line_table)
line_table->GetDescription(
&strm, interpreter.GetExecutionContext().GetTargetPtr(),
desc_level);
else
strm << "No line table";
first_module = false;
}
}
return num_matches;
}
static void DumpFullpath(Stream &strm, const FileSpec *file_spec_ptr,
uint32_t width) {
if (file_spec_ptr) {
if (width > 0) {
std::string fullpath = file_spec_ptr->GetPath();
strm.Printf("%-*s", width, fullpath.c_str());
return;
} else {
file_spec_ptr->Dump(strm.AsRawOstream());
return;
}
}
// Keep the width spacing correct if things go wrong...
if (width > 0)
strm.Printf("%-*s", width, "");
}
static void DumpDirectory(Stream &strm, const FileSpec *file_spec_ptr,
uint32_t width) {
if (file_spec_ptr) {
if (width > 0)
strm.Printf("%-*s", width, file_spec_ptr->GetDirectory().AsCString(""));
else
file_spec_ptr->GetDirectory().Dump(&strm);
return;
}
// Keep the width spacing correct if things go wrong...
if (width > 0)
strm.Printf("%-*s", width, "");
}
static void DumpBasename(Stream &strm, const FileSpec *file_spec_ptr,
uint32_t width) {
if (file_spec_ptr) {
if (width > 0)
strm.Printf("%-*s", width, file_spec_ptr->GetFilename().AsCString(""));
else
file_spec_ptr->GetFilename().Dump(&strm);
return;
}
// Keep the width spacing correct if things go wrong...
if (width > 0)
strm.Printf("%-*s", width, "");
}
static size_t DumpModuleObjfileHeaders(Stream &strm, ModuleList &module_list) {
std::lock_guard<std::recursive_mutex> guard(module_list.GetMutex());
const size_t num_modules = module_list.GetSize();
if (num_modules == 0)
return 0;
size_t num_dumped = 0;
strm.Format("Dumping headers for {0} module(s).\n", num_modules);
strm.IndentMore();
for (ModuleSP module_sp : module_list.ModulesNoLocking()) {
if (module_sp) {
if (num_dumped++ > 0) {
strm.EOL();
strm.EOL();
}
ObjectFile *objfile = module_sp->GetObjectFile();
if (objfile)
objfile->Dump(&strm);
else {
strm.Format("No object file for module: {0:F}\n",
module_sp->GetFileSpec());
}
}
}
strm.IndentLess();
return num_dumped;
}
static void DumpModuleSymtab(CommandInterpreter &interpreter, Stream &strm,
Module *module, SortOrder sort_order,
Mangled::NamePreference name_preference) {
if (!module)
return;
if (Symtab *symtab = module->GetSymtab())
symtab->Dump(&strm, interpreter.GetExecutionContext().GetTargetPtr(),
sort_order, name_preference);
}
static void DumpModuleSections(CommandInterpreter &interpreter, Stream &strm,
Module *module) {
if (module) {
SectionList *section_list = module->GetSectionList();
if (section_list) {
strm.Printf("Sections for '%s' (%s):\n",
module->GetSpecificationDescription().c_str(),
module->GetArchitecture().GetArchitectureName());
section_list->Dump(strm.AsRawOstream(), strm.GetIndentLevel() + 2,
interpreter.GetExecutionContext().GetTargetPtr(), true,
UINT32_MAX);
}
}
}
static bool DumpModuleSymbolFile(Stream &strm, Module *module) {
if (module) {
if (SymbolFile *symbol_file = module->GetSymbolFile(true)) {
symbol_file->Dump(strm);
return true;
}
}
return false;
}
static bool GetSeparateDebugInfoList(StructuredData::Array &list,
Module *module, bool errors_only,
bool load_all_debug_info) {
if (module) {
if (SymbolFile *symbol_file = module->GetSymbolFile(/*can_create=*/true)) {
StructuredData::Dictionary d;
if (symbol_file->GetSeparateDebugInfo(d, errors_only,
load_all_debug_info)) {
list.AddItem(
std::make_shared<StructuredData::Dictionary>(std::move(d)));
return true;
}
}
}
return false;
}
static void DumpDwoFilesTable(Stream &strm,
StructuredData::Array &dwo_listings) {
strm.PutCString("Dwo ID Err Dwo Path");
strm.EOL();
strm.PutCString(
"------------------ --- -----------------------------------------");
strm.EOL();
dwo_listings.ForEach([&strm](StructuredData::Object *dwo) {
StructuredData::Dictionary *dict = dwo->GetAsDictionary();
if (!dict)
return false;
uint64_t dwo_id;
if (dict->GetValueForKeyAsInteger("dwo_id", dwo_id))
strm.Printf("0x%16.16" PRIx64 " ", dwo_id);
else
strm.Printf("0x???????????????? ");
llvm::StringRef error;
if (dict->GetValueForKeyAsString("error", error))
strm << "E " << error;
else {
llvm::StringRef resolved_dwo_path;
if (dict->GetValueForKeyAsString("resolved_dwo_path",
resolved_dwo_path)) {
strm << " " << resolved_dwo_path;
if (resolved_dwo_path.ends_with(".dwp")) {
llvm::StringRef dwo_name;
if (dict->GetValueForKeyAsString("dwo_name", dwo_name))
strm << "(" << dwo_name << ")";
}
}
}
strm.EOL();
return true;
});
}
static void DumpOsoFilesTable(Stream &strm,
StructuredData::Array &oso_listings) {
strm.PutCString("Mod Time Err Oso Path");
strm.EOL();
strm.PutCString("------------------ --- ---------------------");
strm.EOL();
oso_listings.ForEach([&strm](StructuredData::Object *oso) {
StructuredData::Dictionary *dict = oso->GetAsDictionary();
if (!dict)
return false;
uint32_t oso_mod_time;
if (dict->GetValueForKeyAsInteger("oso_mod_time", oso_mod_time))
strm.Printf("0x%16.16" PRIx32 " ", oso_mod_time);
llvm::StringRef error;
if (dict->GetValueForKeyAsString("error", error))
strm << "E " << error;
else {
llvm::StringRef oso_path;
if (dict->GetValueForKeyAsString("oso_path", oso_path))
strm << " " << oso_path;
}
strm.EOL();
return true;
});
}
static void
DumpAddress(ExecutionContextScope *exe_scope, const Address &so_addr,
bool verbose, bool all_ranges, Stream &strm,
std::optional<Stream::HighlightSettings> settings = std::nullopt) {
strm.IndentMore();
strm.Indent(" Address: ");
so_addr.Dump(&strm, exe_scope, Address::DumpStyleModuleWithFileAddress);
strm.PutCString(" (");
so_addr.Dump(&strm, exe_scope, Address::DumpStyleSectionNameOffset);
strm.PutCString(")\n");
strm.Indent(" Summary: ");
const uint32_t save_indent = strm.GetIndentLevel();
strm.SetIndentLevel(save_indent + 13);
so_addr.Dump(&strm, exe_scope, Address::DumpStyleResolvedDescription,
Address::DumpStyleInvalid, UINT32_MAX, false, settings);
strm.SetIndentLevel(save_indent);
// Print out detailed address information when verbose is enabled
if (verbose) {
strm.EOL();
so_addr.Dump(&strm, exe_scope, Address::DumpStyleDetailedSymbolContext,
Address::DumpStyleInvalid, UINT32_MAX, all_ranges, settings);
}
strm.IndentLess();
}
static bool LookupAddressInModule(CommandInterpreter &interpreter, Stream &strm,
Module *module, uint32_t resolve_mask,
lldb::addr_t raw_addr, lldb::addr_t offset,
bool verbose, bool all_ranges) {
if (module) {
lldb::addr_t addr = raw_addr - offset;
Address so_addr;
SymbolContext sc;
Target *target = interpreter.GetExecutionContext().GetTargetPtr();
if (target && target->HasLoadedSections()) {
if (!target->ResolveLoadAddress(addr, so_addr))
return false;
else if (so_addr.GetModule().get() != module)
return false;
} else {
if (!module->ResolveFileAddress(addr, so_addr))
return false;
}
ExecutionContextScope *exe_scope =
interpreter.GetExecutionContext().GetBestExecutionContextScope();
DumpAddress(exe_scope, so_addr, verbose, all_ranges, strm);
return true;
}
return false;
}
static uint32_t LookupSymbolInModule(CommandInterpreter &interpreter,
Stream &strm, Module *module,
const char *name, bool name_is_regex,
bool verbose, bool all_ranges) {
if (!module)
return 0;
Symtab *symtab = module->GetSymtab();
if (!symtab)
return 0;
SymbolContext sc;
const bool use_color = interpreter.GetDebugger().GetUseColor();
std::vector<uint32_t> match_indexes;
ConstString symbol_name(name);
uint32_t num_matches = 0;
if (name_is_regex) {
RegularExpression name_regexp(symbol_name.GetStringRef());
num_matches = symtab->AppendSymbolIndexesMatchingRegExAndType(
name_regexp, eSymbolTypeAny, match_indexes);
} else {
num_matches =
symtab->AppendSymbolIndexesWithName(symbol_name, match_indexes);
}
if (num_matches > 0) {
strm.Indent();
strm.Printf("%u symbols match %s'%s' in ", num_matches,
name_is_regex ? "the regular expression " : "", name);
DumpFullpath(strm, &module->GetFileSpec(), 0);
strm.PutCString(":\n");
strm.IndentMore();
Stream::HighlightSettings settings(
name, interpreter.GetDebugger().GetRegexMatchAnsiPrefix(),
interpreter.GetDebugger().GetRegexMatchAnsiSuffix());
for (uint32_t i = 0; i < num_matches; ++i) {
Symbol *symbol = symtab->SymbolAtIndex(match_indexes[i]);
if (symbol) {
if (symbol->ValueIsAddress()) {
DumpAddress(
interpreter.GetExecutionContext().GetBestExecutionContextScope(),
symbol->GetAddressRef(), verbose, all_ranges, strm,
use_color && name_is_regex
? std::optional<Stream::HighlightSettings>{settings}
: std::nullopt);
strm.EOL();
} else {
strm.IndentMore();
strm.Indent(" Name: ");
strm.PutCStringColorHighlighted(
symbol->GetDisplayName().GetStringRef(),
use_color && name_is_regex
? std::optional<Stream::HighlightSettings>{settings}
: std::nullopt);
strm.EOL();
strm.Indent(" Value: ");
strm.Printf("0x%16.16" PRIx64 "\n", symbol->GetRawValue());
if (symbol->GetByteSizeIsValid()) {
strm.Indent(" Size: ");
strm.Printf("0x%16.16" PRIx64 "\n", symbol->GetByteSize());
}
strm.IndentLess();
}
}
}
strm.IndentLess();
}
return num_matches;
}
static void DumpSymbolContextList(
ExecutionContextScope *exe_scope, Stream &strm,
const SymbolContextList &sc_list, bool verbose, bool all_ranges,
std::optional<Stream::HighlightSettings> settings = std::nullopt) {
strm.IndentMore();
bool first_module = true;
for (const SymbolContext &sc : sc_list) {
if (!first_module)
strm.EOL();
Address addr;
if (sc.line_entry.IsValid())
addr = sc.line_entry.range.GetBaseAddress();
else if (sc.block && sc.block->GetContainingInlinedBlock())
sc.block->GetContainingInlinedBlock()->GetStartAddress(addr);
else
addr = sc.GetFunctionOrSymbolAddress();
DumpAddress(exe_scope, addr, verbose, all_ranges, strm, settings);
first_module = false;
}
strm.IndentLess();
}
static size_t LookupFunctionInModule(CommandInterpreter &interpreter,
Stream &strm, Module *module,
const char *name, bool name_is_regex,
const ModuleFunctionSearchOptions &options,
bool verbose, bool all_ranges) {
if (module && name && name[0]) {
SymbolContextList sc_list;
size_t num_matches = 0;
if (name_is_regex) {
RegularExpression function_name_regex((llvm::StringRef(name)));
module->FindFunctions(function_name_regex, options, sc_list);
} else {
ConstString function_name(name);
module->FindFunctions(function_name, CompilerDeclContext(),
eFunctionNameTypeAuto, options, sc_list);
}
num_matches = sc_list.GetSize();
if (num_matches) {
strm.Indent();
strm.Printf("%" PRIu64 " match%s found in ", (uint64_t)num_matches,
num_matches > 1 ? "es" : "");
DumpFullpath(strm, &module->GetFileSpec(), 0);
strm.PutCString(":\n");
DumpSymbolContextList(
interpreter.GetExecutionContext().GetBestExecutionContextScope(),
strm, sc_list, verbose, all_ranges);
}
return num_matches;
}
return 0;
}
static size_t LookupTypeInModule(Target *target,
CommandInterpreter &interpreter, Stream &strm,
Module *module, const char *name_cstr,
bool name_is_regex) {
if (module && name_cstr && name_cstr[0]) {
TypeQuery query(name_cstr);
TypeResults results;
module->FindTypes(query, results);
TypeList type_list;
SymbolContext sc;
if (module)
sc.module_sp = module->shared_from_this();
// Sort the type results and put the results that matched in \a module
// first if \a module was specified.
sc.SortTypeList(results.GetTypeMap(), type_list);
if (type_list.Empty())
return 0;
const uint64_t num_matches = type_list.GetSize();
strm.Indent();
strm.Printf("%" PRIu64 " match%s found in ", num_matches,
num_matches > 1 ? "es" : "");
DumpFullpath(strm, &module->GetFileSpec(), 0);
strm.PutCString(":\n");
for (TypeSP type_sp : type_list.Types()) {
if (!type_sp)
continue;
// Resolve the clang type so that any forward references to types
// that haven't yet been parsed will get parsed.
type_sp->GetFullCompilerType();
type_sp->GetDescription(&strm, eDescriptionLevelFull, true, target);
// Print all typedef chains
TypeSP typedef_type_sp(type_sp);
TypeSP typedefed_type_sp(typedef_type_sp->GetTypedefType());
while (typedefed_type_sp) {
strm.EOL();
strm.Printf(" typedef '%s': ",
typedef_type_sp->GetName().GetCString());
typedefed_type_sp->GetFullCompilerType();
typedefed_type_sp->GetDescription(&strm, eDescriptionLevelFull, true,
target);
typedef_type_sp = typedefed_type_sp;
typedefed_type_sp = typedef_type_sp->GetTypedefType();
}
strm.EOL();
}
return type_list.GetSize();
}
return 0;
}
static size_t LookupTypeHere(Target *target, CommandInterpreter &interpreter,
Stream &strm, Module &module,
const char *name_cstr, bool name_is_regex) {
TypeQuery query(name_cstr);
TypeResults results;
module.FindTypes(query, results);
TypeList type_list;
SymbolContext sc;
sc.module_sp = module.shared_from_this();
sc.SortTypeList(results.GetTypeMap(), type_list);
if (type_list.Empty())
return 0;
strm.Indent();
strm.PutCString("Best match found in ");
DumpFullpath(strm, &module.GetFileSpec(), 0);
strm.PutCString(":\n");
TypeSP type_sp(type_list.GetTypeAtIndex(0));
if (type_sp) {
// Resolve the clang type so that any forward references to types that
// haven't yet been parsed will get parsed.
type_sp->GetFullCompilerType();
type_sp->GetDescription(&strm, eDescriptionLevelFull, true, target);
// Print all typedef chains.
TypeSP typedef_type_sp(type_sp);
TypeSP typedefed_type_sp(typedef_type_sp->GetTypedefType());
while (typedefed_type_sp) {
strm.EOL();
strm.Printf(" typedef '%s': ",
typedef_type_sp->GetName().GetCString());
typedefed_type_sp->GetFullCompilerType();
typedefed_type_sp->GetDescription(&strm, eDescriptionLevelFull, true,
target);
typedef_type_sp = typedefed_type_sp;
typedefed_type_sp = typedef_type_sp->GetTypedefType();
}
}
strm.EOL();
return type_list.GetSize();
}
static uint32_t LookupFileAndLineInModule(CommandInterpreter &interpreter,
Stream &strm, Module *module,
const FileSpec &file_spec,
uint32_t line, bool check_inlines,
bool verbose, bool all_ranges) {
if (module && file_spec) {
SymbolContextList sc_list;
const uint32_t num_matches = module->ResolveSymbolContextsForFileSpec(
file_spec, line, check_inlines, eSymbolContextEverything, sc_list);
if (num_matches > 0) {
strm.Indent();
strm.Printf("%u match%s found in ", num_matches,
num_matches > 1 ? "es" : "");
strm << file_spec;
if (line > 0)
strm.Printf(":%u", line);
strm << " in ";
DumpFullpath(strm, &module->GetFileSpec(), 0);
strm.PutCString(":\n");
DumpSymbolContextList(
interpreter.GetExecutionContext().GetBestExecutionContextScope(),
strm, sc_list, verbose, all_ranges);
return num_matches;
}
}
return 0;
}
static size_t FindModulesByName(Target *target, const char *module_name,
ModuleList &module_list,
bool check_global_list) {
FileSpec module_file_spec(module_name);
ModuleSpec module_spec(module_file_spec);
const size_t initial_size = module_list.GetSize();
if (check_global_list) {
// Check the global list
std::lock_guard<std::recursive_mutex> guard(
Module::GetAllocationModuleCollectionMutex());
const size_t num_modules = Module::GetNumberAllocatedModules();
ModuleSP module_sp;
for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
Module *module = Module::GetAllocatedModuleAtIndex(image_idx);
if (module) {
if (module->MatchesModuleSpec(module_spec)) {
module_sp = module->shared_from_this();
module_list.AppendIfNeeded(module_sp);
}
}
}
} else {
if (target) {
target->GetImages().FindModules(module_spec, module_list);
const size_t num_matches = module_list.GetSize();
// Not found in our module list for our target, check the main shared
// module list in case it is a extra file used somewhere else
if (num_matches == 0) {
module_spec.GetArchitecture() = target->GetArchitecture();
ModuleList::FindSharedModules(module_spec, module_list);
}
} else {
ModuleList::FindSharedModules(module_spec, module_list);
}
}
return module_list.GetSize() - initial_size;
}
#pragma mark CommandObjectTargetModulesModuleAutoComplete
// A base command object class that can auto complete with module file
// paths
class CommandObjectTargetModulesModuleAutoComplete
: public CommandObjectParsed {
public:
CommandObjectTargetModulesModuleAutoComplete(CommandInterpreter &interpreter,
const char *name,
const char *help,
const char *syntax,
uint32_t flags = 0)
: CommandObjectParsed(interpreter, name, help, syntax, flags) {
AddSimpleArgumentList(eArgTypeFilename, eArgRepeatStar);
}
~CommandObjectTargetModulesModuleAutoComplete() override = default;
void
HandleArgumentCompletion(CompletionRequest &request,
OptionElementVector &opt_element_vector) override {
lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
GetCommandInterpreter(), lldb::eModuleCompletion, request, nullptr);
}
};
#pragma mark CommandObjectTargetModulesSourceFileAutoComplete
// A base command object class that can auto complete with module source
// file paths
class CommandObjectTargetModulesSourceFileAutoComplete
: public CommandObjectParsed {
public:
CommandObjectTargetModulesSourceFileAutoComplete(
CommandInterpreter &interpreter, const char *name, const char *help,
const char *syntax, uint32_t flags)
: CommandObjectParsed(interpreter, name, help, syntax, flags) {
AddSimpleArgumentList(eArgTypeSourceFile, eArgRepeatPlus);
}
~CommandObjectTargetModulesSourceFileAutoComplete() override = default;
void
HandleArgumentCompletion(CompletionRequest &request,
OptionElementVector &opt_element_vector) override {
lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
GetCommandInterpreter(), lldb::eSourceFileCompletion, request, nullptr);
}
};
#pragma mark CommandObjectTargetModulesDumpObjfile
class CommandObjectTargetModulesDumpObjfile
: public CommandObjectTargetModulesModuleAutoComplete {
public:
CommandObjectTargetModulesDumpObjfile(CommandInterpreter &interpreter)
: CommandObjectTargetModulesModuleAutoComplete(
interpreter, "target modules dump objfile",
"Dump the object file headers from one or more target modules.",
nullptr, eCommandRequiresTarget) {}
~CommandObjectTargetModulesDumpObjfile() override = default;
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
Target &target = GetTarget();
uint32_t addr_byte_size = target.GetArchitecture().GetAddressByteSize();
result.GetOutputStream().SetAddressByteSize(addr_byte_size);
result.GetErrorStream().SetAddressByteSize(addr_byte_size);
size_t num_dumped = 0;
if (command.GetArgumentCount() == 0) {
// Dump all headers for all modules images
num_dumped = DumpModuleObjfileHeaders(result.GetOutputStream(),
target.GetImages());
if (num_dumped == 0) {
result.AppendError("the target has no associated executable images");
}
} else {
// Find the modules that match the basename or full path.
ModuleList module_list;
const char *arg_cstr;
for (int arg_idx = 0;
(arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr;
++arg_idx) {
size_t num_matched =
FindModulesByName(&target, arg_cstr, module_list, true);
if (num_matched == 0) {
result.AppendWarningWithFormat(
"Unable to find an image that matches '%s'.\n", arg_cstr);
}
}
// Dump all the modules we found.
num_dumped =
DumpModuleObjfileHeaders(result.GetOutputStream(), module_list);
}
if (num_dumped > 0) {
result.SetStatus(eReturnStatusSuccessFinishResult);
} else {
result.AppendError("no matching executable images found");
}
}
};
#define LLDB_OPTIONS_target_modules_dump_symtab
#include "CommandOptions.inc"
class CommandObjectTargetModulesDumpSymtab
: public CommandObjectTargetModulesModuleAutoComplete {
public:
CommandObjectTargetModulesDumpSymtab(CommandInterpreter &interpreter)
: CommandObjectTargetModulesModuleAutoComplete(
interpreter, "target modules dump symtab",
"Dump the symbol table from one or more target modules.", nullptr,
eCommandRequiresTarget) {}
~CommandObjectTargetModulesDumpSymtab() override = default;
Options *GetOptions() override { return &m_options; }
class CommandOptions : public Options {
public:
CommandOptions() = default;
~CommandOptions() override = default;
Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
ExecutionContext *execution_context) override {
Status error;
const int short_option = m_getopt_table[option_idx].val;
switch (short_option) {
case 'm':
m_prefer_mangled.SetCurrentValue(true);
m_prefer_mangled.SetOptionWasSet();
break;
case 's':
m_sort_order = (SortOrder)OptionArgParser::ToOptionEnum(
option_arg, GetDefinitions()[option_idx].enum_values,
eSortOrderNone, error);
break;
default:
llvm_unreachable("Unimplemented option");
}
return error;
}
void OptionParsingStarting(ExecutionContext *execution_context) override {
m_sort_order = eSortOrderNone;
m_prefer_mangled.Clear();
}
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
return llvm::ArrayRef(g_target_modules_dump_symtab_options);
}
SortOrder m_sort_order = eSortOrderNone;
OptionValueBoolean m_prefer_mangled = {false, false};
};
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
Target &target = GetTarget();
uint32_t num_dumped = 0;
Mangled::NamePreference name_preference =
(m_options.m_prefer_mangled ? Mangled::ePreferMangled
: Mangled::ePreferDemangled);
uint32_t addr_byte_size = target.GetArchitecture().GetAddressByteSize();
result.GetOutputStream().SetAddressByteSize(addr_byte_size);
result.GetErrorStream().SetAddressByteSize(addr_byte_size);
if (command.GetArgumentCount() == 0) {
// Dump all sections for all modules images
const ModuleList &module_list = target.GetImages();
std::lock_guard<std::recursive_mutex> guard(module_list.GetMutex());
const size_t num_modules = module_list.GetSize();
if (num_modules > 0) {
result.GetOutputStream().Format(
"Dumping symbol table for {0} modules.\n", num_modules);
for (ModuleSP module_sp : module_list.ModulesNoLocking()) {
if (num_dumped > 0) {
result.GetOutputStream().EOL();
result.GetOutputStream().EOL();
}
if (INTERRUPT_REQUESTED(GetDebugger(),
"Interrupted in dump all symtabs with {0} "
"of {1} dumped.", num_dumped, num_modules))
break;
num_dumped++;
DumpModuleSymtab(m_interpreter, result.GetOutputStream(),
module_sp.get(), m_options.m_sort_order,
name_preference);
}
} else {
result.AppendError("the target has no associated executable images");
return;
}
} else {
// Dump specified images (by basename or fullpath)
const char *arg_cstr;
for (int arg_idx = 0;
(arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr;
++arg_idx) {
ModuleList module_list;
const size_t num_matches =
FindModulesByName(&target, arg_cstr, module_list, true);
if (num_matches > 0) {
for (ModuleSP module_sp : module_list.Modules()) {
if (module_sp) {
if (num_dumped > 0) {
result.GetOutputStream().EOL();
result.GetOutputStream().EOL();
}
if (INTERRUPT_REQUESTED(GetDebugger(),
"Interrupted in dump symtab list with {0} of {1} dumped.",
num_dumped, num_matches))
break;
num_dumped++;
DumpModuleSymtab(m_interpreter, result.GetOutputStream(),
module_sp.get(), m_options.m_sort_order,
name_preference);
}
}
} else
result.AppendWarningWithFormat(
"Unable to find an image that matches '%s'.\n", arg_cstr);
}
}
if (num_dumped > 0)
result.SetStatus(eReturnStatusSuccessFinishResult);
else {
result.AppendError("no matching executable images found");
}
}
CommandOptions m_options;
};
#pragma mark CommandObjectTargetModulesDumpSections
// Image section dumping command
class CommandObjectTargetModulesDumpSections
: public CommandObjectTargetModulesModuleAutoComplete {
public:
CommandObjectTargetModulesDumpSections(CommandInterpreter &interpreter)
: CommandObjectTargetModulesModuleAutoComplete(
interpreter, "target modules dump sections",
"Dump the sections from one or more target modules.",
//"target modules dump sections [<file1> ...]")
nullptr, eCommandRequiresTarget) {}
~CommandObjectTargetModulesDumpSections() override = default;
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
Target &target = GetTarget();
uint32_t num_dumped = 0;
uint32_t addr_byte_size = target.GetArchitecture().GetAddressByteSize();
result.GetOutputStream().SetAddressByteSize(addr_byte_size);
result.GetErrorStream().SetAddressByteSize(addr_byte_size);
if (command.GetArgumentCount() == 0) {
// Dump all sections for all modules images
const size_t num_modules = target.GetImages().GetSize();
if (num_modules == 0) {
result.AppendError("the target has no associated executable images");
return;
}
result.GetOutputStream().Format("Dumping sections for {0} modules.\n",
num_modules);
for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
if (INTERRUPT_REQUESTED(GetDebugger(),
"Interrupted in dump all sections with {0} of {1} dumped",
image_idx, num_modules))
break;
num_dumped++;
DumpModuleSections(
m_interpreter, result.GetOutputStream(),
target.GetImages().GetModulePointerAtIndex(image_idx));
}
} else {
// Dump specified images (by basename or fullpath)
const char *arg_cstr;
for (int arg_idx = 0;
(arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr;
++arg_idx) {
ModuleList module_list;
const size_t num_matches =
FindModulesByName(&target, arg_cstr, module_list, true);
if (num_matches > 0) {
for (size_t i = 0; i < num_matches; ++i) {
if (INTERRUPT_REQUESTED(GetDebugger(),
"Interrupted in dump section list with {0} of {1} dumped.",
i, num_matches))
break;
Module *module = module_list.GetModulePointerAtIndex(i);
if (module) {
num_dumped++;
DumpModuleSections(m_interpreter, result.GetOutputStream(),
module);
}
}
} else {
// Check the global list
std::lock_guard<std::recursive_mutex> guard(
Module::GetAllocationModuleCollectionMutex());
result.AppendWarningWithFormat(
"Unable to find an image that matches '%s'.\n", arg_cstr);
}
}
}
if (num_dumped > 0)
result.SetStatus(eReturnStatusSuccessFinishResult);
else {
result.AppendError("no matching executable images found");
}
}
};
class CommandObjectTargetModulesDumpClangPCMInfo : public CommandObjectParsed {
public:
CommandObjectTargetModulesDumpClangPCMInfo(CommandInterpreter &interpreter)
: CommandObjectParsed(
interpreter, "target modules dump pcm-info",
"Dump information about the given clang module (pcm).") {
// Take a single file argument.
AddSimpleArgumentList(eArgTypeFilename);
}
~CommandObjectTargetModulesDumpClangPCMInfo() override = default;
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
if (command.GetArgumentCount() != 1) {
result.AppendErrorWithFormat("'%s' takes exactly one pcm path argument.",
m_cmd_name.c_str());
return;
}
const char *pcm_path = command.GetArgumentAtIndex(0);
const FileSpec pcm_file{pcm_path};
if (pcm_file.GetFileNameExtension() != ".pcm") {
result.AppendError("file must have a .pcm extension");
return;
}
if (!FileSystem::Instance().Exists(pcm_file)) {
result.AppendError("pcm file does not exist");
return;
}
const char *clang_args[] = {"clang", pcm_path};
clang::CompilerInstance compiler(clang::createInvocation(clang_args));
compiler.createDiagnostics(*FileSystem::Instance().GetVirtualFileSystem());
// Pass empty deleter to not attempt to free memory that was allocated
// outside of the current scope, possibly statically.
std::shared_ptr<llvm::raw_ostream> Out(
&result.GetOutputStream().AsRawOstream(), [](llvm::raw_ostream *) {});
clang::DumpModuleInfoAction dump_module_info(Out);
// DumpModuleInfoAction requires ObjectFilePCHContainerReader.
compiler.getPCHContainerOperations()->registerReader(
std::make_unique<clang::ObjectFilePCHContainerReader>());
if (compiler.ExecuteAction(dump_module_info))
result.SetStatus(eReturnStatusSuccessFinishResult);
}
};
#pragma mark CommandObjectTargetModulesDumpClangAST
// Clang AST dumping command
class CommandObjectTargetModulesDumpClangAST
: public CommandObjectTargetModulesModuleAutoComplete {
public:
CommandObjectTargetModulesDumpClangAST(CommandInterpreter &interpreter)
: CommandObjectTargetModulesModuleAutoComplete(
interpreter, "target modules dump ast",
"Dump the clang ast for a given module's symbol file.",
"target modules dump ast [--filter <name>] [<file1> ...]",
eCommandRequiresTarget),
m_filter(LLDB_OPT_SET_1, false, "filter", 'f', 0, eArgTypeName,
"Dump only the decls whose names contain the specified filter "
"string.",
/*default_value=*/"") {
m_option_group.Append(&m_filter, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
m_option_group.Finalize();
}
Options *GetOptions() override { return &m_option_group; }
~CommandObjectTargetModulesDumpClangAST() override = default;
OptionGroupOptions m_option_group;
OptionGroupString m_filter;
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
Target &target = GetTarget();
const ModuleList &module_list = target.GetImages();
const size_t num_modules = module_list.GetSize();
if (num_modules == 0) {
result.AppendError("the target has no associated executable images");
return;
}
llvm::StringRef filter = m_filter.GetOptionValue().GetCurrentValueAsRef();
if (command.GetArgumentCount() == 0) {
// Dump all ASTs for all modules images
result.GetOutputStream().Format("Dumping clang ast for {0} modules.\n",
num_modules);
for (ModuleSP module_sp : module_list.ModulesNoLocking()) {
if (INTERRUPT_REQUESTED(GetDebugger(), "Interrupted dumping clang ast"))
break;
if (SymbolFile *sf = module_sp->GetSymbolFile())
sf->DumpClangAST(result.GetOutputStream(), filter);
}
result.SetStatus(eReturnStatusSuccessFinishResult);
return;
}
// Dump specified ASTs (by basename or fullpath)
for (const Args::ArgEntry &arg : command.entries()) {
ModuleList module_list;
const size_t num_matches =
FindModulesByName(&target, arg.c_str(), module_list, true);
if (num_matches == 0) {
// Check the global list
std::lock_guard<std::recursive_mutex> guard(
Module::GetAllocationModuleCollectionMutex());
result.AppendWarningWithFormat(
"Unable to find an image that matches '%s'.\n", arg.c_str());
continue;
}
for (size_t i = 0; i < num_matches; ++i) {
if (INTERRUPT_REQUESTED(GetDebugger(),
"Interrupted in dump clang ast list with {0} of {1} dumped.",
i, num_matches))
break;
Module *m = module_list.GetModulePointerAtIndex(i);
if (SymbolFile *sf = m->GetSymbolFile())
sf->DumpClangAST(result.GetOutputStream(), filter);
}
}
result.SetStatus(eReturnStatusSuccessFinishResult);
}
};
#pragma mark CommandObjectTargetModulesDumpSymfile
// Image debug symbol dumping command
class CommandObjectTargetModulesDumpSymfile
: public CommandObjectTargetModulesModuleAutoComplete {
public:
CommandObjectTargetModulesDumpSymfile(CommandInterpreter &interpreter)
: CommandObjectTargetModulesModuleAutoComplete(
interpreter, "target modules dump symfile",
"Dump the debug symbol file for one or more target modules.",
//"target modules dump symfile [<file1> ...]")
nullptr, eCommandRequiresTarget) {}
~CommandObjectTargetModulesDumpSymfile() override = default;
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
Target &target = GetTarget();
uint32_t num_dumped = 0;
uint32_t addr_byte_size = target.GetArchitecture().GetAddressByteSize();
result.GetOutputStream().SetAddressByteSize(addr_byte_size);
result.GetErrorStream().SetAddressByteSize(addr_byte_size);
if (command.GetArgumentCount() == 0) {
// Dump all sections for all modules images
const ModuleList &target_modules = target.GetImages();
std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
const size_t num_modules = target_modules.GetSize();
if (num_modules == 0) {
result.AppendError("the target has no associated executable images");
return;
}
result.GetOutputStream().Format(
"Dumping debug symbols for {0} modules.\n", num_modules);
for (ModuleSP module_sp : target_modules.ModulesNoLocking()) {
if (INTERRUPT_REQUESTED(GetDebugger(), "Interrupted in dumping all "
"debug symbols with {0} of {1} modules dumped",
num_dumped, num_modules))
break;
if (DumpModuleSymbolFile(result.GetOutputStream(), module_sp.get()))
num_dumped++;
}
} else {
// Dump specified images (by basename or fullpath)
const char *arg_cstr;
for (int arg_idx = 0;
(arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr;
++arg_idx) {
ModuleList module_list;
const size_t num_matches =
FindModulesByName(&target, arg_cstr, module_list, true);
if (num_matches > 0) {
for (size_t i = 0; i < num_matches; ++i) {
if (INTERRUPT_REQUESTED(GetDebugger(), "Interrupted dumping {0} "
"of {1} requested modules",
i, num_matches))
break;
Module *module = module_list.GetModulePointerAtIndex(i);
if (module) {
if (DumpModuleSymbolFile(result.GetOutputStream(), module))
num_dumped++;
}
}
} else
result.AppendWarningWithFormat(
"Unable to find an image that matches '%s'.\n", arg_cstr);
}
}
if (num_dumped > 0)
result.SetStatus(eReturnStatusSuccessFinishResult);
else {
result.AppendError("no matching executable images found");
}
}
};
#pragma mark CommandObjectTargetModulesDumpLineTable
#define LLDB_OPTIONS_target_modules_dump
#include "CommandOptions.inc"
// Image debug line table dumping command
class CommandObjectTargetModulesDumpLineTable
: public CommandObjectTargetModulesSourceFileAutoComplete {
public:
CommandObjectTargetModulesDumpLineTable(CommandInterpreter &interpreter)
: CommandObjectTargetModulesSourceFileAutoComplete(
interpreter, "target modules dump line-table",
"Dump the line table for one or more compilation units.", nullptr,
eCommandRequiresTarget) {}
~CommandObjectTargetModulesDumpLineTable() override = default;
Options *GetOptions() override { return &m_options; }
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
Target *target = m_exe_ctx.GetTargetPtr();
uint32_t total_num_dumped = 0;
uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
result.GetOutputStream().SetAddressByteSize(addr_byte_size);
result.GetErrorStream().SetAddressByteSize(addr_byte_size);
if (command.GetArgumentCount() == 0) {
result.AppendError("file option must be specified.");
return;
} else {
// Dump specified images (by basename or fullpath)
const char *arg_cstr;
for (int arg_idx = 0;
(arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr;
++arg_idx) {
FileSpec file_spec(arg_cstr);
const ModuleList &target_modules = target->GetImages();
std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
size_t num_modules = target_modules.GetSize();
if (num_modules > 0) {
uint32_t num_dumped = 0;
for (ModuleSP module_sp : target_modules.ModulesNoLocking()) {
if (INTERRUPT_REQUESTED(GetDebugger(),
"Interrupted in dump all line tables with "
"{0} of {1} dumped", num_dumped,
num_modules))
break;
if (DumpCompileUnitLineTable(
m_interpreter, result.GetOutputStream(), module_sp.get(),
file_spec,
m_options.m_verbose ? eDescriptionLevelFull
: eDescriptionLevelBrief))
num_dumped++;
}
if (num_dumped == 0)
result.AppendWarningWithFormat(
"No source filenames matched '%s'.\n", arg_cstr);
else
total_num_dumped += num_dumped;
}
}
}
if (total_num_dumped > 0)
result.SetStatus(eReturnStatusSuccessFinishResult);
else {
result.AppendError("no source filenames matched any command arguments");
}
}
class CommandOptions : public Options {
public:
CommandOptions() { OptionParsingStarting(nullptr); }
Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
ExecutionContext *execution_context) override {
assert(option_idx == 0 && "We only have one option.");
m_verbose = true;
return Status();
}
void OptionParsingStarting(ExecutionContext *execution_context) override {
m_verbose = false;
}
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
return llvm::ArrayRef(g_target_modules_dump_options);
}
bool m_verbose;
};
CommandOptions m_options;
};
#pragma mark CommandObjectTargetModulesDumpSeparateDebugInfoFiles
#define LLDB_OPTIONS_target_modules_dump_separate_debug_info
#include "CommandOptions.inc"
// Image debug separate debug info dumping command
class CommandObjectTargetModulesDumpSeparateDebugInfoFiles
: public CommandObjectTargetModulesModuleAutoComplete {
public:
CommandObjectTargetModulesDumpSeparateDebugInfoFiles(
CommandInterpreter &interpreter)
: CommandObjectTargetModulesModuleAutoComplete(
interpreter, "target modules dump separate-debug-info",
"List the separate debug info symbol files for one or more target "
"modules.",
nullptr, eCommandRequiresTarget) {}
~CommandObjectTargetModulesDumpSeparateDebugInfoFiles() override = default;
Options *GetOptions() override { return &m_options; }
class CommandOptions : public Options {
public:
CommandOptions() = default;
~CommandOptions() override = default;
Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
ExecutionContext *execution_context) override {
Status error;
const int short_option = m_getopt_table[option_idx].val;
switch (short_option) {
case 'f':
m_load_all_debug_info.SetCurrentValue(true);
m_load_all_debug_info.SetOptionWasSet();
break;
case 'j':
m_json.SetCurrentValue(true);
m_json.SetOptionWasSet();
break;
case 'e':
m_errors_only.SetCurrentValue(true);
m_errors_only.SetOptionWasSet();
break;
default:
llvm_unreachable("Unimplemented option");
}
return error;
}
void OptionParsingStarting(ExecutionContext *execution_context) override {
m_json.Clear();
m_errors_only.Clear();
m_load_all_debug_info.Clear();
}
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
return llvm::ArrayRef(g_target_modules_dump_separate_debug_info_options);
}
OptionValueBoolean m_json = false;
OptionValueBoolean m_errors_only = false;
OptionValueBoolean m_load_all_debug_info = false;
};
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
Target &target = GetTarget();
uint32_t num_dumped = 0;
uint32_t addr_byte_size = target.GetArchitecture().GetAddressByteSize();
result.GetOutputStream().SetAddressByteSize(addr_byte_size);
result.GetErrorStream().SetAddressByteSize(addr_byte_size);
StructuredData::Array separate_debug_info_lists_by_module;
if (command.GetArgumentCount() == 0) {
// Dump all sections for all modules images
const ModuleList &target_modules = target.GetImages();
std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
const size_t num_modules = target_modules.GetSize();
if (num_modules == 0) {
result.AppendError("the target has no associated executable images");
return;
}
for (ModuleSP module_sp : target_modules.ModulesNoLocking()) {
if (INTERRUPT_REQUESTED(
GetDebugger(),
"Interrupted in dumping all "
"separate debug info with {0} of {1} modules dumped",
num_dumped, num_modules))
break;
if (GetSeparateDebugInfoList(separate_debug_info_lists_by_module,
module_sp.get(),
bool(m_options.m_errors_only),
bool(m_options.m_load_all_debug_info)))
num_dumped++;
}
} else {
// Dump specified images (by basename or fullpath)
const char *arg_cstr;
for (int arg_idx = 0;
(arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr;
++arg_idx) {
ModuleList module_list;
const size_t num_matches =
FindModulesByName(&target, arg_cstr, module_list, true);
if (num_matches > 0) {
for (size_t i = 0; i < num_matches; ++i) {
if (INTERRUPT_REQUESTED(GetDebugger(),
"Interrupted dumping {0} "
"of {1} requested modules",
i, num_matches))
break;
Module *module = module_list.GetModulePointerAtIndex(i);
if (GetSeparateDebugInfoList(separate_debug_info_lists_by_module,
module, bool(m_options.m_errors_only),
bool(m_options.m_load_all_debug_info)))
num_dumped++;
}
} else
result.AppendWarningWithFormat(
"Unable to find an image that matches '%s'.\n", arg_cstr);
}
}
if (num_dumped > 0) {
Stream &strm = result.GetOutputStream();
// Display the debug info files in some format.
if (m_options.m_json) {
// JSON format
separate_debug_info_lists_by_module.Dump(strm,
/*pretty_print=*/true);
} else {
// Human-readable table format
separate_debug_info_lists_by_module.ForEach(
[&result, &strm](StructuredData::Object *obj) {
if (!obj) {
return false;
}
// Each item in `separate_debug_info_lists_by_module` should be a
// valid structured data dictionary.
StructuredData::Dictionary *separate_debug_info_list =
obj->GetAsDictionary();
if (!separate_debug_info_list) {
return false;
}
llvm::StringRef type;
llvm::StringRef symfile;
StructuredData::Array *files;
if (!(separate_debug_info_list->GetValueForKeyAsString("type",
type) &&
separate_debug_info_list->GetValueForKeyAsString("symfile",
symfile) &&
separate_debug_info_list->GetValueForKeyAsArray(
"separate-debug-info-files", files))) {
assert(false);
}
strm << "Symbol file: " << symfile;
strm.EOL();
strm << "Type: \"" << type << "\"";
strm.EOL();
if (type == "dwo") {
DumpDwoFilesTable(strm, *files);
} else if (type == "oso") {
DumpOsoFilesTable(strm, *files);
} else {
result.AppendWarningWithFormat(
"Found unsupported debug info type '%s'.\n",
type.str().c_str());
}
return true;
});
}
result.SetStatus(eReturnStatusSuccessFinishResult);
} else {
result.AppendError("no matching executable images found");
}
}
CommandOptions m_options;
};
#pragma mark CommandObjectTargetModulesDump
// Dump multi-word command for target modules
class CommandObjectTargetModulesDump : public CommandObjectMultiword {
public:
// Constructors and Destructors
CommandObjectTargetModulesDump(CommandInterpreter &interpreter)
: CommandObjectMultiword(
interpreter, "target modules dump",
"Commands for dumping information about one or more target "
"modules.",
"target modules dump "
"[objfile|symtab|sections|ast|symfile|line-table|pcm-info|separate-"
"debug-info] "
"[<file1> <file2> ...]") {
LoadSubCommand("objfile",
CommandObjectSP(
new CommandObjectTargetModulesDumpObjfile(interpreter)));
LoadSubCommand(
"symtab",
CommandObjectSP(new CommandObjectTargetModulesDumpSymtab(interpreter)));
LoadSubCommand("sections",
CommandObjectSP(new CommandObjectTargetModulesDumpSections(
interpreter)));
LoadSubCommand("symfile",
CommandObjectSP(
new CommandObjectTargetModulesDumpSymfile(interpreter)));
LoadSubCommand(
"ast", CommandObjectSP(
new CommandObjectTargetModulesDumpClangAST(interpreter)));
LoadSubCommand("line-table",
CommandObjectSP(new CommandObjectTargetModulesDumpLineTable(
interpreter)));
LoadSubCommand(
"pcm-info",
CommandObjectSP(
new CommandObjectTargetModulesDumpClangPCMInfo(interpreter)));
LoadSubCommand("separate-debug-info",
CommandObjectSP(
new CommandObjectTargetModulesDumpSeparateDebugInfoFiles(
interpreter)));
}
~CommandObjectTargetModulesDump() override = default;
};
class CommandObjectTargetModulesAdd : public CommandObjectParsed {
public:
CommandObjectTargetModulesAdd(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "target modules add",
"Add a new module to the current target's modules.",
"target modules add [<module>]",
eCommandRequiresTarget),
m_symbol_file(LLDB_OPT_SET_1, false, "symfile", 's', 0,
eArgTypeFilename,
"Fullpath to a stand alone debug "
"symbols file for when debug symbols "
"are not in the executable.") {
m_option_group.Append(&m_uuid_option_group, LLDB_OPT_SET_ALL,
LLDB_OPT_SET_1);
m_option_group.Append(&m_symbol_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
m_option_group.Finalize();
AddSimpleArgumentList(eArgTypePath, eArgRepeatStar);
}
~CommandObjectTargetModulesAdd() override = default;
Options *GetOptions() override { return &m_option_group; }
protected:
OptionGroupOptions m_option_group;
OptionGroupUUID m_uuid_option_group;
OptionGroupFile m_symbol_file;
void DoExecute(Args &args, CommandReturnObject &result) override {
Target &target = GetTarget();
bool flush = false;
const size_t argc = args.GetArgumentCount();
if (argc == 0) {
if (m_uuid_option_group.GetOptionValue().OptionWasSet()) {
// We are given a UUID only, go locate the file
ModuleSpec module_spec;
module_spec.GetUUID() =
m_uuid_option_group.GetOptionValue().GetCurrentValue();
if (m_symbol_file.GetOptionValue().OptionWasSet())
module_spec.GetSymbolFileSpec() =
m_symbol_file.GetOptionValue().GetCurrentValue();
Status error;
if (PluginManager::DownloadObjectAndSymbolFile(module_spec, error)) {
ModuleSP module_sp(
target.GetOrCreateModule(module_spec, true /* notify */));
if (module_sp) {
result.SetStatus(eReturnStatusSuccessFinishResult);
return;
} else {
StreamString strm;
module_spec.GetUUID().Dump(strm);
if (module_spec.GetFileSpec()) {
if (module_spec.GetSymbolFileSpec()) {
result.AppendErrorWithFormat(
"Unable to create the executable or symbol file with "
"UUID %s with path %s and symbol file %s",
strm.GetData(), module_spec.GetFileSpec().GetPath().c_str(),
module_spec.GetSymbolFileSpec().GetPath().c_str());
} else {
result.AppendErrorWithFormat(
"Unable to create the executable or symbol file with "
"UUID %s with path %s",
strm.GetData(),
module_spec.GetFileSpec().GetPath().c_str());
}
} else {
result.AppendErrorWithFormat("Unable to create the executable "
"or symbol file with UUID %s",
strm.GetData());
}
return;
}
} else {
StreamString strm;
module_spec.GetUUID().Dump(strm);
result.AppendErrorWithFormat(
"Unable to locate the executable or symbol file with UUID %s",
strm.GetData());
result.SetError(std::move(error));
return;
}
} else {
result.AppendError(
"one or more executable image paths must be specified");
return;
}
} else {
for (auto &entry : args.entries()) {
if (entry.ref().empty())
continue;
FileSpec file_spec(entry.ref());
if (FileSystem::Instance().Exists(file_spec)) {
ModuleSpec module_spec(file_spec);
if (m_uuid_option_group.GetOptionValue().OptionWasSet())
module_spec.GetUUID() =
m_uuid_option_group.GetOptionValue().GetCurrentValue();
if (m_symbol_file.GetOptionValue().OptionWasSet())
module_spec.GetSymbolFileSpec() =
m_symbol_file.GetOptionValue().GetCurrentValue();
if (!module_spec.GetArchitecture().IsValid())
module_spec.GetArchitecture() = target.GetArchitecture();
Status error;
ModuleSP module_sp(
target.GetOrCreateModule(module_spec, true /* notify */, &error));
if (!module_sp) {
const char *error_cstr = error.AsCString();
if (error_cstr)
result.AppendError(error_cstr);
else
result.AppendErrorWithFormat("unsupported module: %s",
entry.c_str());
return;
} else {
flush = true;
}
result.SetStatus(eReturnStatusSuccessFinishResult);
} else {
std::string resolved_path = file_spec.GetPath();
if (resolved_path != entry.ref()) {
result.AppendErrorWithFormat(
"invalid module path '%s' with resolved path '%s'\n",
entry.ref().str().c_str(), resolved_path.c_str());
break;
}
result.AppendErrorWithFormat("invalid module path '%s'\n",
entry.c_str());
break;
}
}
}
if (flush) {
ProcessSP process = target.GetProcessSP();
if (process)
process->Flush();
}
}
};
class CommandObjectTargetModulesLoad
: public CommandObjectTargetModulesModuleAutoComplete {
public:
CommandObjectTargetModulesLoad(CommandInterpreter &interpreter)
: CommandObjectTargetModulesModuleAutoComplete(
interpreter, "target modules load",
"Set the load addresses for one or more sections in a target "
"module.",
"target modules load [--file <module> --uuid <uuid>] <sect-name> "
"<address> [<sect-name> <address> ....]",
eCommandRequiresTarget),
m_file_option(LLDB_OPT_SET_1, false, "file", 'f', 0, eArgTypeName,
"Fullpath or basename for module to load.", ""),
m_load_option(LLDB_OPT_SET_1, false, "load", 'l',
"Write file contents to the memory.", false, true),
m_pc_option(LLDB_OPT_SET_1, false, "set-pc-to-entry", 'p',
"Set PC to the entry point."
" Only applicable with '--load' option.",
false, true),
m_slide_option(LLDB_OPT_SET_1, false, "slide", 's', 0, eArgTypeOffset,
"Set the load address for all sections to be the "
"virtual address in the file plus the offset.",
0) {
m_option_group.Append(&m_uuid_option_group, LLDB_OPT_SET_ALL,
LLDB_OPT_SET_1);
m_option_group.Append(&m_file_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
m_option_group.Append(&m_load_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
m_option_group.Append(&m_pc_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
m_option_group.Append(&m_slide_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
m_option_group.Finalize();
}
~CommandObjectTargetModulesLoad() override = default;
Options *GetOptions() override { return &m_option_group; }
protected:
void DoExecute(Args &args, CommandReturnObject &result) override {
Target &target = GetTarget();
const bool load = m_load_option.GetOptionValue().GetCurrentValue();
const bool set_pc = m_pc_option.GetOptionValue().GetCurrentValue();
const size_t argc = args.GetArgumentCount();
ModuleSpec module_spec;
bool search_using_module_spec = false;
// Allow "load" option to work without --file or --uuid option.
if (load) {
if (!m_file_option.GetOptionValue().OptionWasSet() &&
!m_uuid_option_group.GetOptionValue().OptionWasSet()) {
ModuleList &module_list = target.GetImages();
if (module_list.GetSize() == 1) {
search_using_module_spec = true;
module_spec.GetFileSpec() =
module_list.GetModuleAtIndex(0)->GetFileSpec();
}
}
}
if (m_file_option.GetOptionValue().OptionWasSet()) {
search_using_module_spec = true;
const char *arg_cstr = m_file_option.GetOptionValue().GetCurrentValue();
const bool use_global_module_list = true;
ModuleList module_list;
const size_t num_matches = FindModulesByName(
&target, arg_cstr, module_list, use_global_module_list);
if (num_matches == 1) {
module_spec.GetFileSpec() =
module_list.GetModuleAtIndex(0)->GetFileSpec();
} else if (num_matches > 1) {
search_using_module_spec = false;
result.AppendErrorWithFormat(
"more than 1 module matched by name '%s'\n", arg_cstr);
} else {
search_using_module_spec = false;
result.AppendErrorWithFormat("no object file for module '%s'\n",
arg_cstr);
}
}
if (m_uuid_option_group.GetOptionValue().OptionWasSet()) {
search_using_module_spec = true;
module_spec.GetUUID() =
m_uuid_option_group.GetOptionValue().GetCurrentValue();
}
if (search_using_module_spec) {
ModuleList matching_modules;
target.GetImages().FindModules(module_spec, matching_modules);
const size_t num_matches = matching_modules.GetSize();
char path[PATH_MAX];
if (num_matches == 1) {
Module *module = matching_modules.GetModulePointerAtIndex(0);
if (module) {
ObjectFile *objfile = module->GetObjectFile();
if (objfile) {
SectionList *section_list = module->GetSectionList();
if (section_list) {
bool changed = false;
if (argc == 0) {
if (m_slide_option.GetOptionValue().OptionWasSet()) {
const addr_t slide =
m_slide_option.GetOptionValue().GetCurrentValue();
const bool slide_is_offset = true;
module->SetLoadAddress(target, slide, slide_is_offset,
changed);
} else {
result.AppendError("one or more section name + load "
"address pair must be specified");
return;
}
} else {
if (m_slide_option.GetOptionValue().OptionWasSet()) {
result.AppendError("The \"--slide <offset>\" option can't "
"be used in conjunction with setting "
"section load addresses.\n");
return;
}
for (size_t i = 0; i < argc; i += 2) {
const char *sect_name = args.GetArgumentAtIndex(i);
const char *load_addr_cstr = args.GetArgumentAtIndex(i + 1);
if (sect_name && load_addr_cstr) {
ConstString const_sect_name(sect_name);
addr_t load_addr;
if (llvm::to_integer(load_addr_cstr, load_addr)) {
SectionSP section_sp(
section_list->FindSectionByName(const_sect_name));
if (section_sp) {
if (section_sp->IsThreadSpecific()) {
result.AppendErrorWithFormat(
"thread specific sections are not yet "
"supported (section '%s')\n",
sect_name);
break;
} else {
if (target.SetSectionLoadAddress(section_sp,
load_addr))
changed = true;
result.AppendMessageWithFormat(
"section '%s' loaded at 0x%" PRIx64 "\n",
sect_name, load_addr);
}
} else {
result.AppendErrorWithFormat("no section found that "
"matches the section "
"name '%s'\n",
sect_name);
break;
}
} else {
result.AppendErrorWithFormat(
"invalid load address string '%s'\n", load_addr_cstr);
break;
}
} else {
if (sect_name)
result.AppendError("section names must be followed by "
"a load address.\n");
else
result.AppendError("one or more section name + load "
"address pair must be specified.\n");
break;
}
}
}
if (changed) {
target.ModulesDidLoad(matching_modules);
Process *process = m_exe_ctx.GetProcessPtr();
if (process)
process->Flush();
}
if (load) {
ProcessSP process = target.CalculateProcess();
Address file_entry = objfile->GetEntryPointAddress();
if (!process) {
result.AppendError("No process");
return;
}
if (set_pc && !file_entry.IsValid()) {
result.AppendError("No entry address in object file");
return;
}
std::vector<ObjectFile::LoadableData> loadables(
objfile->GetLoadableData(target));
if (loadables.size() == 0) {
result.AppendError("No loadable sections");
return;
}
Status error = process->WriteObjectFile(std::move(loadables));
if (error.Fail()) {
result.AppendError(error.AsCString());
return;
}
if (set_pc) {
ThreadList &thread_list = process->GetThreadList();
RegisterContextSP reg_context(
thread_list.GetSelectedThread()->GetRegisterContext());
addr_t file_entry_addr = file_entry.GetLoadAddress(&target);
if (!reg_context->SetPC(file_entry_addr)) {
result.AppendErrorWithFormat("failed to set PC value to "
"0x%" PRIx64 "\n",
file_entry_addr);
}
}
}
} else {
module->GetFileSpec().GetPath(path, sizeof(path));
result.AppendErrorWithFormat("no sections in object file '%s'\n",
path);
}
} else {
module->GetFileSpec().GetPath(path, sizeof(path));
result.AppendErrorWithFormat("no object file for module '%s'\n",
path);
}
} else {
FileSpec *module_spec_file = module_spec.GetFileSpecPtr();
if (module_spec_file) {
module_spec_file->GetPath(path, sizeof(path));
result.AppendErrorWithFormat("invalid module '%s'.\n", path);
} else
result.AppendError("no module spec");
}
} else {
std::string uuid_str;
if (module_spec.GetFileSpec())
module_spec.GetFileSpec().GetPath(path, sizeof(path));
else
path[0] = '\0';
if (module_spec.GetUUIDPtr())
uuid_str = module_spec.GetUUID().GetAsString();
if (num_matches > 1) {
result.AppendErrorWithFormat(
"multiple modules match%s%s%s%s:\n", path[0] ? " file=" : "",
path, !uuid_str.empty() ? " uuid=" : "", uuid_str.c_str());
for (size_t i = 0; i < num_matches; ++i) {
if (matching_modules.GetModulePointerAtIndex(i)
->GetFileSpec()
.GetPath(path, sizeof(path)))
result.AppendMessageWithFormat("%s\n", path);
}
} else {
result.AppendErrorWithFormat(
"no modules were found that match%s%s%s%s.\n",
path[0] ? " file=" : "", path, !uuid_str.empty() ? " uuid=" : "",
uuid_str.c_str());
}
}
} else {
result.AppendError("either the \"--file <module>\" or the \"--uuid "
"<uuid>\" option must be specified.\n");
}
}
OptionGroupOptions m_option_group;
OptionGroupUUID m_uuid_option_group;
OptionGroupString m_file_option;
OptionGroupBoolean m_load_option;
OptionGroupBoolean m_pc_option;
OptionGroupUInt64 m_slide_option;
};
#pragma mark CommandObjectTargetModulesList
// List images with associated information
#define LLDB_OPTIONS_target_modules_list
#include "CommandOptions.inc"
class CommandObjectTargetModulesList : public CommandObjectParsed {
public:
class CommandOptions : public Options {
public:
CommandOptions() = default;
~CommandOptions() override = default;
Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
ExecutionContext *execution_context) override {
Status error;
const int short_option = m_getopt_table[option_idx].val;
if (short_option == 'g') {
m_use_global_module_list = true;
} else if (short_option == 'a') {
m_module_addr = OptionArgParser::ToAddress(
execution_context, option_arg, LLDB_INVALID_ADDRESS, &error);
} else {
unsigned long width = 0;
option_arg.getAsInteger(0, width);
m_format_array.push_back(std::make_pair(short_option, width));
}
return error;
}
void OptionParsingStarting(ExecutionContext *execution_context) override {
m_format_array.clear();
m_use_global_module_list = false;
m_module_addr = LLDB_INVALID_ADDRESS;
}
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
return llvm::ArrayRef(g_target_modules_list_options);
}
// Instance variables to hold the values for command options.
typedef std::vector<std::pair<char, uint32_t>> FormatWidthCollection;
FormatWidthCollection m_format_array;
bool m_use_global_module_list = false;
lldb::addr_t m_module_addr = LLDB_INVALID_ADDRESS;
};
CommandObjectTargetModulesList(CommandInterpreter &interpreter)
: CommandObjectParsed(
interpreter, "target modules list",
"List current executable and dependent shared library images.") {
AddSimpleArgumentList(eArgTypeModule, eArgRepeatStar);
}
~CommandObjectTargetModulesList() override = default;
Options *GetOptions() override { return &m_options; }
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
Target &target = GetTarget();
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;
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) {
Address module_address;
if (module_address.SetLoadAddress(m_options.m_module_addr, &target)) {
ModuleSP module_sp(module_address.GetModule());
if (module_sp) {
PrintModule(target, module_sp.get(), 0, strm);
result.SetStatus(eReturnStatusSuccessFinishResult);
} else {
result.AppendErrorWithFormat(
"Couldn't find module matching address: 0x%" PRIx64 ".",
m_options.m_module_addr);
}
} else {
result.AppendErrorWithFormat(
"Couldn't find module containing address: 0x%" PRIx64 ".",
m_options.m_module_addr);
}
return;
}
size_t num_modules = 0;
// This locker will be locked on the mutex in module_list_ptr if it is
// non-nullptr. Otherwise it will lock the
// AllocationModuleCollectionMutex when accessing the global module list
// directly.
std::unique_lock<std::recursive_mutex> guard(
Module::GetAllocationModuleCollectionMutex(), std::defer_lock);
const ModuleList *module_list_ptr = nullptr;
const size_t argc = command.GetArgumentCount();
if (argc == 0) {
if (use_global_module_list) {
guard.lock();
num_modules = Module::GetNumberAllocatedModules();
} else {
module_list_ptr = &target.GetImages();
}
} else {
for (const Args::ArgEntry &arg : command) {
// Dump specified images (by basename or fullpath)
const size_t num_matches = FindModulesByName(
&target, arg.c_str(), module_list, use_global_module_list);
if (num_matches == 0) {
if (argc == 1) {
result.AppendErrorWithFormat("no modules found that match '%s'",
arg.c_str());
return;
}
}
}
module_list_ptr = &module_list;
}
std::unique_lock<std::recursive_mutex> lock;
if (module_list_ptr != nullptr) {
lock =
std::unique_lock<std::recursive_mutex>(module_list_ptr->GetMutex());
num_modules = module_list_ptr->GetSize();
}
if (num_modules > 0) {
for (uint32_t image_idx = 0; image_idx < num_modules; ++image_idx) {
ModuleSP module_sp;
Module *module;
if (module_list_ptr) {
module_sp = module_list_ptr->GetModuleAtIndexUnlocked(image_idx);
module = module_sp.get();
} else {
module = Module::GetAllocatedModuleAtIndex(image_idx);
module_sp = module->shared_from_this();
}
const size_t indent = strm.Printf("[%3u] ", image_idx);
PrintModule(target, module, indent, strm);
}
result.SetStatus(eReturnStatusSuccessFinishResult);
} else {
if (argc) {
if (use_global_module_list)
result.AppendError(
"the global module list has no matching modules");
else
result.AppendError("the target has no matching modules");
} else {
if (use_global_module_list)
result.AppendError("the global module list is empty");
else
result.AppendError(
"the target has no associated executable images");
}
return;
}
}
void PrintModule(Target &target, Module *module, int indent, Stream &strm) {
if (module == nullptr) {
strm.PutCString("Null module");
return;
}
bool dump_object_name = false;
if (m_options.m_format_array.empty()) {
m_options.m_format_array.push_back(std::make_pair('u', 0));
m_options.m_format_array.push_back(std::make_pair('h', 0));
m_options.m_format_array.push_back(std::make_pair('f', 0));
m_options.m_format_array.push_back(std::make_pair('S', 0));
}
const size_t num_entries = m_options.m_format_array.size();
bool print_space = false;
for (size_t i = 0; i < num_entries; ++i) {
if (print_space)
strm.PutChar(' ');
print_space = true;
const char format_char = m_options.m_format_array[i].first;
uint32_t width = m_options.m_format_array[i].second;
switch (format_char) {
case 'A':
DumpModuleArchitecture(strm, module, false, width);
break;
case 't':
DumpModuleArchitecture(strm, module, true, width);
break;
case 'f':
DumpFullpath(strm, &module->GetFileSpec(), width);
dump_object_name = true;
break;
case 'd':
DumpDirectory(strm, &module->GetFileSpec(), width);
break;
case 'b':
DumpBasename(strm, &module->GetFileSpec(), width);
dump_object_name = true;
break;
case 'h':
case 'o':
// Image header address
{
uint32_t addr_nibble_width =
target.GetArchitecture().GetAddressByteSize() * 2;
ObjectFile *objfile = module->GetObjectFile();
if (objfile) {
Address base_addr(objfile->GetBaseAddress());
if (base_addr.IsValid()) {
if (target.HasLoadedSections()) {
lldb::addr_t load_addr = base_addr.GetLoadAddress(&target);
if (load_addr == LLDB_INVALID_ADDRESS) {
base_addr.Dump(&strm, &target,
Address::DumpStyleModuleWithFileAddress,
Address::DumpStyleFileAddress);
} else {
if (format_char == 'o') {
// Show the offset of slide for the image
strm.Printf("0x%*.*" PRIx64, addr_nibble_width,
addr_nibble_width,
load_addr - base_addr.GetFileAddress());
} else {
// Show the load address of the image
strm.Printf("0x%*.*" PRIx64, addr_nibble_width,
addr_nibble_width, load_addr);
}
}
break;
}
// The address was valid, but the image isn't loaded, output the
// address in an appropriate format
base_addr.Dump(&strm, &target, Address::DumpStyleFileAddress);
break;
}
}
strm.Printf("%*s", addr_nibble_width + 2, "");
}
break;
case 'r': {
size_t ref_count = 0;
char in_shared_cache = 'Y';
ModuleSP module_sp(module->shared_from_this());
if (!ModuleList::ModuleIsInCache(module))
in_shared_cache = 'N';
if (module_sp) {
// Take one away to make sure we don't count our local "module_sp"
ref_count = module_sp.use_count() - 1;
}
if (width)
strm.Printf("{%c %*" PRIu64 "}", in_shared_cache, width, (uint64_t)ref_count);
else
strm.Printf("{%c %" PRIu64 "}", in_shared_cache, (uint64_t)ref_count);
} break;
case 's':
case 'S': {
if (const SymbolFile *symbol_file = module->GetSymbolFile()) {
const FileSpec symfile_spec =
symbol_file->GetObjectFile()->GetFileSpec();
if (format_char == 'S') {
// Dump symbol file only if different from module file
if (!symfile_spec || symfile_spec == module->GetFileSpec()) {
print_space = false;
break;
}
// Add a newline and indent past the index
strm.Printf("\n%*s", indent, "");
}
DumpFullpath(strm, &symfile_spec, width);
dump_object_name = true;
break;
}
strm.Printf("%.*s", width, "<NONE>");
} break;
case 'm':
strm.Format("{0:%c}", llvm::fmt_align(module->GetModificationTime(),
llvm::AlignStyle::Left, width));
break;
case 'p':
strm.Printf("%p", static_cast<void *>(module));
break;
case 'u':
DumpModuleUUID(strm, module);
break;
default:
break;
}
}
if (dump_object_name) {
const char *object_name = module->GetObjectName().GetCString();
if (object_name)
strm.Printf("(%s)", object_name);
}
strm.EOL();
}
CommandOptions m_options;
};
#pragma mark CommandObjectTargetModulesShowUnwind
// Lookup unwind information in images
#define LLDB_OPTIONS_target_modules_show_unwind
#include "CommandOptions.inc"
class CommandObjectTargetModulesShowUnwind : public CommandObjectParsed {
public:
enum {
eLookupTypeInvalid = -1,
eLookupTypeAddress = 0,
eLookupTypeSymbol,
eLookupTypeFunction,
eLookupTypeFunctionOrSymbol,
kNumLookupTypes
};
class CommandOptions : public Options {
public:
CommandOptions() = default;
~CommandOptions() override = default;
Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
ExecutionContext *execution_context) override {
Status error;
const int short_option = m_getopt_table[option_idx].val;
switch (short_option) {
case 'a': {
m_str = std::string(option_arg);
m_type = eLookupTypeAddress;
m_addr = OptionArgParser::ToAddress(execution_context, option_arg,
LLDB_INVALID_ADDRESS, &error);
if (m_addr == LLDB_INVALID_ADDRESS)
error = Status::FromErrorStringWithFormat(
"invalid address string '%s'", option_arg.str().c_str());
break;
}
case 'n':
m_str = std::string(option_arg);
m_type = eLookupTypeFunctionOrSymbol;
break;
case 'c':
bool value, success;
value = OptionArgParser::ToBoolean(option_arg, false, &success);
if (success) {
m_cached = value;
} else {
return Status::FromErrorStringWithFormatv(
"invalid boolean value '%s' passed for -c option", option_arg);
}
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;
m_cached = false;
}
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
return llvm::ArrayRef(g_target_modules_show_unwind_options);
}
// Instance variables to hold the values for command options.
int m_type = eLookupTypeInvalid; // Should be a eLookupTypeXXX enum after
// parsing options
std::string m_str; // Holds name lookup
lldb::addr_t m_addr = LLDB_INVALID_ADDRESS; // Holds the address to lookup
bool m_cached = true;
};
CommandObjectTargetModulesShowUnwind(CommandInterpreter &interpreter)
: CommandObjectParsed(
interpreter, "target modules show-unwind",
"Show synthesized unwind instructions for a function.", nullptr,
eCommandRequiresTarget | eCommandRequiresProcess |
eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {}
~CommandObjectTargetModulesShowUnwind() override = default;
Options *GetOptions() override { return &m_options; }
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
Target *target = m_exe_ctx.GetTargetPtr();
Process *process = m_exe_ctx.GetProcessPtr();
ABI *abi = nullptr;
if (process)
abi = process->GetABI().get();
if (process == nullptr) {
result.AppendError(
"You must have a process running to use this command.");
return;
}
ThreadList threads(process->GetThreadList());
if (threads.GetSize() == 0) {
result.AppendError("The process must be paused to use this command.");
return;
}
ThreadSP thread(threads.GetThreadAtIndex(0));
if (!thread) {
result.AppendError("The process must be paused to use this command.");
return;
}
SymbolContextList sc_list;
if (m_options.m_type == eLookupTypeFunctionOrSymbol) {
ConstString function_name(m_options.m_str.c_str());
ModuleFunctionSearchOptions function_options;
function_options.include_symbols = true;
function_options.include_inlines = false;
target->GetImages().FindFunctions(function_name, eFunctionNameTypeAuto,
function_options, sc_list);
} else if (m_options.m_type == eLookupTypeAddress && target) {
Address addr;
if (target->ResolveLoadAddress(m_options.m_addr, addr)) {
SymbolContext sc;
ModuleSP module_sp(addr.GetModule());
module_sp->ResolveSymbolContextForAddress(addr,
eSymbolContextEverything, sc);
if (sc.function || sc.symbol) {
sc_list.Append(sc);
}
}
} else {
result.AppendError(
"address-expression or function name option must be specified.");
return;
}
if (sc_list.GetSize() == 0) {
result.AppendErrorWithFormat("no unwind data found that matches '%s'.",
m_options.m_str.c_str());
return;
}
for (const SymbolContext &sc : sc_list) {
if (sc.symbol == nullptr && sc.function == nullptr)
continue;
if (!sc.module_sp || sc.module_sp->GetObjectFile() == nullptr)
continue;
Address addr = sc.GetFunctionOrSymbolAddress();
if (!addr.IsValid())
continue;
ConstString funcname(sc.GetFunctionName());
if (funcname.IsEmpty())
continue;
addr_t start_addr = addr.GetLoadAddress(target);
if (abi)
start_addr = abi->FixCodeAddress(start_addr);
UnwindTable &uw_table = sc.module_sp->GetUnwindTable();
FuncUnwindersSP func_unwinders_sp =
m_options.m_cached
? uw_table.GetFuncUnwindersContainingAddress(start_addr, sc)
: uw_table.GetUncachedFuncUnwindersContainingAddress(start_addr,
sc);
if (!func_unwinders_sp)
continue;
result.GetOutputStream().Printf(
"UNWIND PLANS for %s`%s (start addr 0x%" PRIx64 ")\n",
sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(),
funcname.AsCString(), start_addr);
Args args;
target->GetUserSpecifiedTrapHandlerNames(args);
size_t count = args.GetArgumentCount();
for (size_t i = 0; i < count; i++) {
const char *trap_func_name = args.GetArgumentAtIndex(i);
if (strcmp(funcname.GetCString(), trap_func_name) == 0)
result.GetOutputStream().Printf(
"This function is "
"treated as a trap handler function via user setting.\n");
}
PlatformSP platform_sp(target->GetPlatform());
if (platform_sp) {
const std::vector<ConstString> trap_handler_names(
platform_sp->GetTrapHandlerSymbolNames());
for (ConstString trap_name : trap_handler_names) {
if (trap_name == funcname) {
result.GetOutputStream().Printf(
"This function's "
"name is listed by the platform as a trap handler.\n");
}
}
}
result.GetOutputStream().Printf("\n");
if (std::shared_ptr<const UnwindPlan> plan_sp =
func_unwinders_sp->GetUnwindPlanAtNonCallSite(*target, *thread)) {
result.GetOutputStream().Printf(
"Asynchronous (not restricted to call-sites) UnwindPlan is '%s'\n",
plan_sp->GetSourceName().AsCString());
}
if (std::shared_ptr<const UnwindPlan> plan_sp =
func_unwinders_sp->GetUnwindPlanAtCallSite(*target, *thread)) {
result.GetOutputStream().Printf(
"Synchronous (restricted to call-sites) UnwindPlan is '%s'\n",
plan_sp->GetSourceName().AsCString());
}
if (std::shared_ptr<const UnwindPlan> plan_sp =
func_unwinders_sp->GetUnwindPlanFastUnwind(*target, *thread)) {
result.GetOutputStream().Printf("Fast UnwindPlan is '%s'\n",
plan_sp->GetSourceName().AsCString());
}
result.GetOutputStream().Printf("\n");
if (std::shared_ptr<const UnwindPlan> plan_sp =
func_unwinders_sp->GetAssemblyUnwindPlan(*target, *thread)) {
result.GetOutputStream().Printf(
"Assembly language inspection UnwindPlan:\n");
plan_sp->Dump(result.GetOutputStream(), thread.get(),
LLDB_INVALID_ADDRESS);
result.GetOutputStream().Printf("\n");
}
if (std::shared_ptr<const UnwindPlan> plan_sp =
func_unwinders_sp->GetObjectFileUnwindPlan(*target)) {
result.GetOutputStream().Printf("object file UnwindPlan:\n");
plan_sp->Dump(result.GetOutputStream(), thread.get(),
LLDB_INVALID_ADDRESS);
result.GetOutputStream().Printf("\n");
}
if (std::shared_ptr<const UnwindPlan> plan_sp =
func_unwinders_sp->GetObjectFileAugmentedUnwindPlan(*target,
*thread)) {
result.GetOutputStream().Printf("object file augmented UnwindPlan:\n");
plan_sp->Dump(result.GetOutputStream(), thread.get(),
LLDB_INVALID_ADDRESS);
result.GetOutputStream().Printf("\n");
}
if (std::shared_ptr<const UnwindPlan> plan_sp =
func_unwinders_sp->GetEHFrameUnwindPlan(*target)) {
result.GetOutputStream().Printf("eh_frame UnwindPlan:\n");
plan_sp->Dump(result.GetOutputStream(), thread.get(),
LLDB_INVALID_ADDRESS);
result.GetOutputStream().Printf("\n");
}
if (std::shared_ptr<const UnwindPlan> plan_sp =
func_unwinders_sp->GetEHFrameAugmentedUnwindPlan(*target,
*thread)) {
result.GetOutputStream().Printf("eh_frame augmented UnwindPlan:\n");
plan_sp->Dump(result.GetOutputStream(), thread.get(),
LLDB_INVALID_ADDRESS);
result.GetOutputStream().Printf("\n");
}
if (std::shared_ptr<const UnwindPlan> 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 (std::shared_ptr<const UnwindPlan> 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");
}
if (std::shared_ptr<const UnwindPlan> plan_sp =
func_unwinders_sp->GetArmUnwindUnwindPlan(*target)) {
result.GetOutputStream().Printf("ARM.exidx unwind UnwindPlan:\n");
plan_sp->Dump(result.GetOutputStream(), thread.get(),
LLDB_INVALID_ADDRESS);
result.GetOutputStream().Printf("\n");
}
if (std::shared_ptr<const UnwindPlan> plan_sp =
func_unwinders_sp->GetSymbolFileUnwindPlan(*thread)) {
result.GetOutputStream().Printf("Symbol file UnwindPlan:\n");
plan_sp->Dump(result.GetOutputStream(), thread.get(),
LLDB_INVALID_ADDRESS);
result.GetOutputStream().Printf("\n");
}
if (std::shared_ptr<const UnwindPlan> plan_sp =
func_unwinders_sp->GetCompactUnwindUnwindPlan(*target)) {
result.GetOutputStream().Printf("Compact unwind UnwindPlan:\n");
plan_sp->Dump(result.GetOutputStream(), thread.get(),
LLDB_INVALID_ADDRESS);
result.GetOutputStream().Printf("\n");
}
if (std::shared_ptr<const UnwindPlan> plan_sp =
func_unwinders_sp->GetUnwindPlanFastUnwind(*target, *thread)) {
result.GetOutputStream().Printf("Fast UnwindPlan:\n");
plan_sp->Dump(result.GetOutputStream(), thread.get(),
LLDB_INVALID_ADDRESS);
result.GetOutputStream().Printf("\n");
}
ABISP abi_sp = process->GetABI();
if (abi_sp) {
if (UnwindPlanSP plan_sp = abi_sp->CreateDefaultUnwindPlan()) {
result.GetOutputStream().Printf("Arch default UnwindPlan:\n");
plan_sp->Dump(result.GetOutputStream(), thread.get(),
LLDB_INVALID_ADDRESS);
result.GetOutputStream().Printf("\n");
}
if (UnwindPlanSP plan_sp = abi_sp->CreateFunctionEntryUnwindPlan()) {
result.GetOutputStream().Printf(
"Arch default at entry point UnwindPlan:\n");
plan_sp->Dump(result.GetOutputStream(), thread.get(),
LLDB_INVALID_ADDRESS);
result.GetOutputStream().Printf("\n");
}
}
result.GetOutputStream().Printf("\n");
}
}
CommandOptions m_options;
};
// Lookup information in images
#define LLDB_OPTIONS_target_modules_lookup
#include "CommandOptions.inc"
class CommandObjectTargetModulesLookup : public CommandObjectParsed {
public:
enum {
eLookupTypeInvalid = -1,
eLookupTypeAddress = 0,
eLookupTypeSymbol,
eLookupTypeFileLine, // Line is optional
eLookupTypeFunction,
eLookupTypeFunctionOrSymbol,
eLookupTypeType,
kNumLookupTypes
};
class CommandOptions : public Options {
public:
CommandOptions() { OptionParsingStarting(nullptr); }
~CommandOptions() override = default;
Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
ExecutionContext *execution_context) override {
Status error;
const int short_option = m_getopt_table[option_idx].val;
switch (short_option) {
case 'a': {
m_type = eLookupTypeAddress;
m_addr = OptionArgParser::ToAddress(execution_context, option_arg,
LLDB_INVALID_ADDRESS, &error);
} break;
case 'o':
if (option_arg.getAsInteger(0, m_offset))
error = Status::FromErrorStringWithFormat(
"invalid offset string '%s'", option_arg.str().c_str());
break;
case 's':
m_str = std::string(option_arg);
m_type = eLookupTypeSymbol;
break;
case 'f':
m_file.SetFile(option_arg, FileSpec::Style::native);
m_type = eLookupTypeFileLine;
break;
case 'i':
m_include_inlines = false;
break;
case 'l':
if (option_arg.getAsInteger(0, m_line_number))
error = Status::FromErrorStringWithFormat(
"invalid line number string '%s'", option_arg.str().c_str());
else if (m_line_number == 0)
error = Status::FromErrorString("zero is an invalid line number");
m_type = eLookupTypeFileLine;
break;
case 'F':
m_str = std::string(option_arg);
m_type = eLookupTypeFunction;
break;
case 'n':
m_str = std::string(option_arg);
m_type = eLookupTypeFunctionOrSymbol;
break;
case 't':
m_str = std::string(option_arg);
m_type = eLookupTypeType;
break;
case 'v':
m_verbose = true;
break;
case 'A':
m_print_all = true;
break;
case 'r':
m_use_regex = true;
break;
case '\x01':
m_all_ranges = true;
break;
default:
llvm_unreachable("Unimplemented option");
}
return error;
}
void OptionParsingStarting(ExecutionContext *execution_context) override {
m_type = eLookupTypeInvalid;
m_str.clear();
m_file.Clear();
m_addr = LLDB_INVALID_ADDRESS;
m_offset = 0;
m_line_number = 0;
m_use_regex = false;
m_include_inlines = true;
m_all_ranges = false;
m_verbose = false;
m_print_all = false;
}
Status OptionParsingFinished(ExecutionContext *execution_context) override {
Status status;
if (m_all_ranges && !m_verbose) {
status =
Status::FromErrorString("--show-variable-ranges must be used in "
"conjunction with --verbose.");
}
return status;
}
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
return llvm::ArrayRef(g_target_modules_lookup_options);
}
int m_type; // Should be a eLookupTypeXXX enum after parsing options
std::string m_str; // Holds name lookup
FileSpec m_file; // Files for file lookups
lldb::addr_t m_addr; // Holds the address to lookup
lldb::addr_t
m_offset; // Subtract this offset from m_addr before doing lookups.
uint32_t m_line_number; // Line number for file+line lookups
bool m_use_regex; // Name lookups in m_str are regular expressions.
bool m_include_inlines; // Check for inline entries when looking up by
// file/line.
bool m_all_ranges; // Print all ranges or single range.
bool m_verbose; // Enable verbose lookup info
bool m_print_all; // Print all matches, even in cases where there's a best
// match.
};
CommandObjectTargetModulesLookup(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "target modules lookup",
"Look up information within executable and "
"dependent shared library images.",
nullptr, eCommandRequiresTarget) {
AddSimpleArgumentList(eArgTypeFilename, eArgRepeatStar);
}
~CommandObjectTargetModulesLookup() override = default;
Options *GetOptions() override { return &m_options; }
bool LookupHere(CommandInterpreter &interpreter, CommandReturnObject &result,
bool &syntax_error) {
switch (m_options.m_type) {
case eLookupTypeAddress:
case eLookupTypeFileLine:
case eLookupTypeFunction:
case eLookupTypeFunctionOrSymbol:
case eLookupTypeSymbol:
default:
return false;
case eLookupTypeType:
break;
}
StackFrameSP frame = m_exe_ctx.GetFrameSP();
if (!frame)
return false;
const SymbolContext &sym_ctx(frame->GetSymbolContext(eSymbolContextModule));
if (!sym_ctx.module_sp)
return false;
switch (m_options.m_type) {
default:
return false;
case eLookupTypeType:
if (!m_options.m_str.empty()) {
if (LookupTypeHere(&GetTarget(), m_interpreter,
result.GetOutputStream(), *sym_ctx.module_sp,
m_options.m_str.c_str(), m_options.m_use_regex)) {
result.SetStatus(eReturnStatusSuccessFinishResult);
return true;
}
}
break;
}
return false;
}
bool LookupInModule(CommandInterpreter &interpreter, Module *module,
CommandReturnObject &result, bool &syntax_error) {
switch (m_options.m_type) {
case eLookupTypeAddress:
if (m_options.m_addr != LLDB_INVALID_ADDRESS) {
if (LookupAddressInModule(
m_interpreter, result.GetOutputStream(), module,
eSymbolContextEverything |
(m_options.m_verbose
? static_cast<int>(eSymbolContextVariable)
: 0),
m_options.m_addr, m_options.m_offset, m_options.m_verbose,
m_options.m_all_ranges)) {
result.SetStatus(eReturnStatusSuccessFinishResult);
return true;
}
}
break;
case eLookupTypeSymbol:
if (!m_options.m_str.empty()) {
if (LookupSymbolInModule(m_interpreter, result.GetOutputStream(),
module, m_options.m_str.c_str(),
m_options.m_use_regex, m_options.m_verbose,
m_options.m_all_ranges)) {
result.SetStatus(eReturnStatusSuccessFinishResult);
return true;
}
}
break;
case eLookupTypeFileLine:
if (m_options.m_file) {
if (LookupFileAndLineInModule(
m_interpreter, result.GetOutputStream(), module,
m_options.m_file, m_options.m_line_number,
m_options.m_include_inlines, m_options.m_verbose,
m_options.m_all_ranges)) {
result.SetStatus(eReturnStatusSuccessFinishResult);
return true;
}
}
break;
case eLookupTypeFunctionOrSymbol:
case eLookupTypeFunction:
if (!m_options.m_str.empty()) {
ModuleFunctionSearchOptions function_options;
function_options.include_symbols =
m_options.m_type == eLookupTypeFunctionOrSymbol;
function_options.include_inlines = m_options.m_include_inlines;
if (LookupFunctionInModule(m_interpreter, result.GetOutputStream(),
module, m_options.m_str.c_str(),
m_options.m_use_regex, function_options,
m_options.m_verbose,
m_options.m_all_ranges)) {
result.SetStatus(eReturnStatusSuccessFinishResult);
return true;
}
}
break;
case eLookupTypeType:
if (!m_options.m_str.empty()) {
if (LookupTypeInModule(
&GetTarget(), m_interpreter, result.GetOutputStream(), module,
m_options.m_str.c_str(), m_options.m_use_regex)) {
result.SetStatus(eReturnStatusSuccessFinishResult);
return true;
}
}
break;
default:
m_options.GenerateOptionUsage(
result.GetErrorStream(), *this,
GetCommandInterpreter().GetDebugger().GetTerminalWidth());
syntax_error = true;
break;
}
result.SetStatus(eReturnStatusFailed);
return false;
}
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
Target &target = GetTarget();
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) {
// Where it is possible to look in the current symbol context first,
// try that. If this search was successful and --all was not passed,
// don't print anything else.
if (LookupHere(m_interpreter, result, syntax_error)) {
result.GetOutputStream().EOL();
num_successful_lookups++;
if (!m_options.m_print_all) {
result.SetStatus(eReturnStatusSuccessFinishResult);
return;
}
}
// Dump all sections for all other modules
const ModuleList &target_modules = target.GetImages();
std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
if (target_modules.GetSize() == 0) {
result.AppendError("the target has no associated executable images");
return;
}
for (ModuleSP module_sp : target_modules.ModulesNoLocking()) {
if (LookupInModule(m_interpreter, module_sp.get(), result,
syntax_error)) {
result.GetOutputStream().EOL();
num_successful_lookups++;
}
}
} else {
// Dump specified images (by basename or fullpath)
const char *arg_cstr;
for (i = 0; (arg_cstr = command.GetArgumentAtIndex(i)) != nullptr &&
!syntax_error;
++i) {
ModuleList module_list;
const size_t num_matches =
FindModulesByName(&target, arg_cstr, module_list, false);
if (num_matches > 0) {
for (size_t j = 0; j < num_matches; ++j) {
Module *module = module_list.GetModulePointerAtIndex(j);
if (module) {
if (LookupInModule(m_interpreter, module, result, syntax_error)) {
result.GetOutputStream().EOL();
num_successful_lookups++;
}
}
}
} else
result.AppendWarningWithFormat(
"Unable to find an image that matches '%s'.\n", arg_cstr);
}
}
if (num_successful_lookups > 0)
result.SetStatus(eReturnStatusSuccessFinishResult);
else
result.SetStatus(eReturnStatusFailed);
}
CommandOptions m_options;
};
#pragma mark CommandObjectMultiwordImageSearchPaths
// CommandObjectMultiwordImageSearchPaths
class CommandObjectTargetModulesImageSearchPaths
: public CommandObjectMultiword {
public:
CommandObjectTargetModulesImageSearchPaths(CommandInterpreter &interpreter)
: CommandObjectMultiword(
interpreter, "target modules search-paths",
"Commands for managing module search paths for a target.",
"target modules search-paths <subcommand> [<subcommand-options>]") {
LoadSubCommand(
"add", CommandObjectSP(
new CommandObjectTargetModulesSearchPathsAdd(interpreter)));
LoadSubCommand(
"clear", CommandObjectSP(new CommandObjectTargetModulesSearchPathsClear(
interpreter)));
LoadSubCommand(
"insert",
CommandObjectSP(
new CommandObjectTargetModulesSearchPathsInsert(interpreter)));
LoadSubCommand(
"list", CommandObjectSP(new CommandObjectTargetModulesSearchPathsList(
interpreter)));
LoadSubCommand(
"query", CommandObjectSP(new CommandObjectTargetModulesSearchPathsQuery(
interpreter)));
}
~CommandObjectTargetModulesImageSearchPaths() override = default;
};
#pragma mark CommandObjectTargetModules
// CommandObjectTargetModules
class CommandObjectTargetModules : public CommandObjectMultiword {
public:
// Constructors and Destructors
CommandObjectTargetModules(CommandInterpreter &interpreter)
: CommandObjectMultiword(interpreter, "target modules",
"Commands for accessing information for one or "
"more target modules.",
"target modules <sub-command> ...") {
LoadSubCommand(
"add", CommandObjectSP(new CommandObjectTargetModulesAdd(interpreter)));
LoadSubCommand("load", CommandObjectSP(new CommandObjectTargetModulesLoad(
interpreter)));
LoadSubCommand("dump", CommandObjectSP(new CommandObjectTargetModulesDump(
interpreter)));
LoadSubCommand("list", CommandObjectSP(new CommandObjectTargetModulesList(
interpreter)));
LoadSubCommand(
"lookup",
CommandObjectSP(new CommandObjectTargetModulesLookup(interpreter)));
LoadSubCommand(
"search-paths",
CommandObjectSP(
new CommandObjectTargetModulesImageSearchPaths(interpreter)));
LoadSubCommand(
"show-unwind",
CommandObjectSP(new CommandObjectTargetModulesShowUnwind(interpreter)));
}
~CommandObjectTargetModules() override = default;
private:
// For CommandObjectTargetModules only
CommandObjectTargetModules(const CommandObjectTargetModules &) = delete;
const CommandObjectTargetModules &
operator=(const CommandObjectTargetModules &) = delete;
};
class CommandObjectTargetSymbolsAdd : public CommandObjectParsed {
public:
CommandObjectTargetSymbolsAdd(CommandInterpreter &interpreter)
: CommandObjectParsed(
interpreter, "target symbols add",
"Add a debug symbol file to one of the target's current modules by "
"specifying a path to a debug symbols file or by using the options "
"to specify a module.",
"target symbols add <cmd-options> [<symfile>]",
eCommandRequiresTarget),
m_file_option(
LLDB_OPT_SET_1, false, "shlib", 's', lldb::eModuleCompletion,
eArgTypeShlibName,
"Locate the debug symbols for the shared library specified by "
"name."),
m_current_frame_option(
LLDB_OPT_SET_2, false, "frame", 'F',
"Locate the debug symbols for the currently selected frame.", false,
true),
m_current_stack_option(LLDB_OPT_SET_2, false, "stack", 'S',
"Locate the debug symbols for every frame in "
"the current call stack.",
false, true)
{
m_option_group.Append(&m_uuid_option_group, LLDB_OPT_SET_ALL,
LLDB_OPT_SET_1);
m_option_group.Append(&m_file_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
m_option_group.Append(&m_current_frame_option, LLDB_OPT_SET_2,
LLDB_OPT_SET_2);
m_option_group.Append(&m_current_stack_option, LLDB_OPT_SET_2,
LLDB_OPT_SET_2);
m_option_group.Finalize();
AddSimpleArgumentList(eArgTypeFilename);
}
~CommandObjectTargetSymbolsAdd() override = default;
Options *GetOptions() override { return &m_option_group; }
protected:
bool AddModuleSymbols(Target *target, ModuleSpec &module_spec, bool &flush,
CommandReturnObject &result) {
const FileSpec &symbol_fspec = module_spec.GetSymbolFileSpec();
if (!symbol_fspec) {
result.AppendError(
"one or more executable image paths must be specified");
return false;
}
char symfile_path[PATH_MAX];
symbol_fspec.GetPath(symfile_path, sizeof(symfile_path));
if (!module_spec.GetUUID().IsValid()) {
if (!module_spec.GetFileSpec() && !module_spec.GetPlatformFileSpec())
module_spec.GetFileSpec().SetFilename(symbol_fspec.GetFilename());
}
// Now module_spec represents a symbol file for a module that might exist
// in the current target. Let's find possible matches.
ModuleList matching_modules;
// First extract all module specs from the symbol file
lldb_private::ModuleSpecList symfile_module_specs;
if (ObjectFile::GetModuleSpecifications(module_spec.GetSymbolFileSpec(),
0, 0, symfile_module_specs)) {
// Now extract the module spec that matches the target architecture
ModuleSpec target_arch_module_spec;
ModuleSpec symfile_module_spec;
target_arch_module_spec.GetArchitecture() = target->GetArchitecture();
if (symfile_module_specs.FindMatchingModuleSpec(target_arch_module_spec,
symfile_module_spec)) {
if (symfile_module_spec.GetUUID().IsValid()) {
// It has a UUID, look for this UUID in the target modules
ModuleSpec symfile_uuid_module_spec;
symfile_uuid_module_spec.GetUUID() = symfile_module_spec.GetUUID();
target->GetImages().FindModules(symfile_uuid_module_spec,
matching_modules);
}
}
if (matching_modules.IsEmpty()) {
// No matches yet. Iterate through the module specs to find a UUID
// value that we can match up to an image in our target.
const size_t num_symfile_module_specs = symfile_module_specs.GetSize();
for (size_t i = 0;
i < num_symfile_module_specs && matching_modules.IsEmpty(); ++i) {
if (symfile_module_specs.GetModuleSpecAtIndex(
i, symfile_module_spec)) {
if (symfile_module_spec.GetUUID().IsValid()) {
// It has a UUID. Look for this UUID in the target modules.
ModuleSpec symfile_uuid_module_spec;
symfile_uuid_module_spec.GetUUID() =
symfile_module_spec.GetUUID();
target->GetImages().FindModules(symfile_uuid_module_spec,
matching_modules);
}
}
}
}
}
// Just try to match up the file by basename if we have no matches at
// this point. For example, module foo might have symbols in foo.debug.
if (matching_modules.IsEmpty())
target->GetImages().FindModules(module_spec, matching_modules);
while (matching_modules.IsEmpty()) {
ConstString filename_no_extension(
module_spec.GetFileSpec().GetFileNameStrippingExtension());
// Empty string returned, let's bail
if (!filename_no_extension)
break;
// Check if there was no extension to strip and the basename is the same
if (filename_no_extension == module_spec.GetFileSpec().GetFilename())
break;
// Replace basename with one fewer extension
module_spec.GetFileSpec().SetFilename(filename_no_extension);
target->GetImages().FindModules(module_spec, matching_modules);
}
if (matching_modules.GetSize() > 1) {
result.AppendErrorWithFormat("multiple modules match symbol file '%s', "
"use the --uuid option to resolve the "
"ambiguity.\n",
symfile_path);
return false;
}
if (matching_modules.GetSize() == 1) {
ModuleSP module_sp(matching_modules.GetModuleAtIndex(0));
// The module has not yet created its symbol vendor, we can just give
// the existing target module the symfile path to use for when it
// decides to create it!
module_sp->SetSymbolFileFileSpec(symbol_fspec);
SymbolFile *symbol_file =
module_sp->GetSymbolFile(true, &result.GetErrorStream());
if (symbol_file) {
ObjectFile *object_file = symbol_file->GetObjectFile();
if (object_file && object_file->GetFileSpec() == symbol_fspec) {
// Provide feedback that the symfile has been successfully added.
const FileSpec &module_fs = module_sp->GetFileSpec();
result.AppendMessageWithFormat(
"symbol file '%s' has been added to '%s'\n", symfile_path,
module_fs.GetPath().c_str());
// Let clients know something changed in the module if it is
// currently loaded
ModuleList module_list;
module_list.Append(module_sp);
target->SymbolsDidLoad(module_list);
// Make sure we load any scripting resources that may be embedded
// in the debug info files in case the platform supports that.
Status error;
StreamString feedback_stream;
module_sp->LoadScriptingResourceInTarget(target, error,
feedback_stream);
if (error.Fail() && error.AsCString())
result.AppendWarningWithFormat(
"unable to load scripting data for module %s - error "
"reported was %s",
module_sp->GetFileSpec()
.GetFileNameStrippingExtension()
.GetCString(),
error.AsCString());
else if (feedback_stream.GetSize())
result.AppendWarning(feedback_stream.GetData());
flush = true;
result.SetStatus(eReturnStatusSuccessFinishResult);
return true;
}
}
// Clear the symbol file spec if anything went wrong
module_sp->SetSymbolFileFileSpec(FileSpec());
}
StreamString ss_symfile_uuid;
if (module_spec.GetUUID().IsValid()) {
ss_symfile_uuid << " (";
module_spec.GetUUID().Dump(ss_symfile_uuid);
ss_symfile_uuid << ')';
}
result.AppendErrorWithFormat(
"symbol file '%s'%s does not match any existing module%s\n",
symfile_path, ss_symfile_uuid.GetData(),
!llvm::sys::fs::is_regular_file(symbol_fspec.GetPath())
? "\n please specify the full path to the symbol file"
: "");
return false;
}
bool DownloadObjectAndSymbolFile(ModuleSpec &module_spec,
CommandReturnObject &result, bool &flush) {
Status error;
if (PluginManager::DownloadObjectAndSymbolFile(module_spec, error)) {
if (module_spec.GetSymbolFileSpec())
return AddModuleSymbols(m_exe_ctx.GetTargetPtr(), module_spec, flush,
result);
} else {
result.SetError(std::move(error));
}
return false;
}
bool AddSymbolsForUUID(CommandReturnObject &result, bool &flush) {
assert(m_uuid_option_group.GetOptionValue().OptionWasSet());
ModuleSpec module_spec;
module_spec.GetUUID() =
m_uuid_option_group.GetOptionValue().GetCurrentValue();
if (!DownloadObjectAndSymbolFile(module_spec, result, flush)) {
StreamString error_strm;
error_strm.PutCString("unable to find debug symbols for UUID ");
module_spec.GetUUID().Dump(error_strm);
result.AppendError(error_strm.GetString());
return false;
}
return true;
}
bool AddSymbolsForFile(CommandReturnObject &result, bool &flush) {
assert(m_file_option.GetOptionValue().OptionWasSet());
ModuleSpec module_spec;
module_spec.GetFileSpec() =
m_file_option.GetOptionValue().GetCurrentValue();
Target *target = m_exe_ctx.GetTargetPtr();
ModuleSP module_sp(target->GetImages().FindFirstModule(module_spec));
if (module_sp) {
module_spec.GetFileSpec() = module_sp->GetFileSpec();
module_spec.GetPlatformFileSpec() = module_sp->GetPlatformFileSpec();
module_spec.GetUUID() = module_sp->GetUUID();
module_spec.GetArchitecture() = module_sp->GetArchitecture();
} else {
module_spec.GetArchitecture() = target->GetArchitecture();
}
if (!DownloadObjectAndSymbolFile(module_spec, result, flush)) {
StreamString error_strm;
error_strm.PutCString(
"unable to find debug symbols for the executable file ");
error_strm << module_spec.GetFileSpec();
result.AppendError(error_strm.GetString());
return false;
}
return true;
}
bool AddSymbolsForFrame(CommandReturnObject &result, bool &flush) {
assert(m_current_frame_option.GetOptionValue().OptionWasSet());
Process *process = m_exe_ctx.GetProcessPtr();
if (!process) {
result.AppendError(
"a process must exist in order to use the --frame option");
return false;
}
const StateType process_state = process->GetState();
if (!StateIsStoppedState(process_state, true)) {
result.AppendErrorWithFormat("process is not stopped: %s",
StateAsCString(process_state));
return false;
}
StackFrame *frame = m_exe_ctx.GetFramePtr();
if (!frame) {
result.AppendError("invalid current frame");
return false;
}
ModuleSP frame_module_sp(
frame->GetSymbolContext(eSymbolContextModule).module_sp);
if (!frame_module_sp) {
result.AppendError("frame has no module");
return false;
}
ModuleSpec module_spec;
module_spec.GetUUID() = frame_module_sp->GetUUID();
module_spec.GetArchitecture() = frame_module_sp->GetArchitecture();
module_spec.GetFileSpec() = frame_module_sp->GetPlatformFileSpec();
if (!DownloadObjectAndSymbolFile(module_spec, result, flush)) {
result.AppendError("unable to find debug symbols for the current frame");
return false;
}
return true;
}
bool AddSymbolsForStack(CommandReturnObject &result, bool &flush) {
assert(m_current_stack_option.GetOptionValue().OptionWasSet());
Process *process = m_exe_ctx.GetProcessPtr();
if (!process) {
result.AppendError(
"a process must exist in order to use the --stack option");
return false;
}
const StateType process_state = process->GetState();
if (!StateIsStoppedState(process_state, true)) {
result.AppendErrorWithFormat("process is not stopped: %s",
StateAsCString(process_state));
return false;
}
Thread *thread = m_exe_ctx.GetThreadPtr();
if (!thread) {
result.AppendError("invalid current thread");
return false;
}
bool symbols_found = false;
uint32_t frame_count = thread->GetStackFrameCount();
for (uint32_t i = 0; i < frame_count; ++i) {
lldb::StackFrameSP frame_sp = thread->GetStackFrameAtIndex(i);
ModuleSP frame_module_sp(
frame_sp->GetSymbolContext(eSymbolContextModule).module_sp);
if (!frame_module_sp)
continue;
ModuleSpec module_spec;
module_spec.GetUUID() = frame_module_sp->GetUUID();
module_spec.GetFileSpec() = frame_module_sp->GetPlatformFileSpec();
module_spec.GetArchitecture() = frame_module_sp->GetArchitecture();
bool current_frame_flush = false;
if (DownloadObjectAndSymbolFile(module_spec, result, current_frame_flush))
symbols_found = true;
flush |= current_frame_flush;
}
if (!symbols_found) {
result.AppendError(
"unable to find debug symbols in the current call stack");
return false;
}
return true;
}
void DoExecute(Args &args, CommandReturnObject &result) override {
Target *target = m_exe_ctx.GetTargetPtr();
result.SetStatus(eReturnStatusFailed);
bool flush = false;
ModuleSpec module_spec;
const bool uuid_option_set =
m_uuid_option_group.GetOptionValue().OptionWasSet();
const bool file_option_set = m_file_option.GetOptionValue().OptionWasSet();
const bool frame_option_set =
m_current_frame_option.GetOptionValue().OptionWasSet();
const bool stack_option_set =
m_current_stack_option.GetOptionValue().OptionWasSet();
const size_t argc = args.GetArgumentCount();
if (argc == 0) {
if (uuid_option_set)
AddSymbolsForUUID(result, flush);
else if (file_option_set)
AddSymbolsForFile(result, flush);
else if (frame_option_set)
AddSymbolsForFrame(result, flush);
else if (stack_option_set)
AddSymbolsForStack(result, flush);
else
result.AppendError("one or more symbol file paths must be specified, "
"or options must be specified");
} else {
if (uuid_option_set) {
result.AppendError("specify either one or more paths to symbol files "
"or use the --uuid option without arguments");
} else if (frame_option_set) {
result.AppendError("specify either one or more paths to symbol files "
"or use the --frame option without arguments");
} else if (file_option_set && argc > 1) {
result.AppendError("specify at most one symbol file path when "
"--shlib option is set");
} else {
PlatformSP platform_sp(target->GetPlatform());
for (auto &entry : args.entries()) {
if (!entry.ref().empty()) {
auto &symbol_file_spec = module_spec.GetSymbolFileSpec();
symbol_file_spec.SetFile(entry.ref(), FileSpec::Style::native);
FileSystem::Instance().Resolve(symbol_file_spec);
if (file_option_set) {
module_spec.GetFileSpec() =
m_file_option.GetOptionValue().GetCurrentValue();
}
if (platform_sp) {
FileSpec symfile_spec;
if (platform_sp
->ResolveSymbolFile(*target, module_spec, symfile_spec)
.Success())
module_spec.GetSymbolFileSpec() = symfile_spec;
}
bool symfile_exists =
FileSystem::Instance().Exists(module_spec.GetSymbolFileSpec());
if (symfile_exists) {
if (!AddModuleSymbols(target, module_spec, flush, result))
break;
} else {
std::string resolved_symfile_path =
module_spec.GetSymbolFileSpec().GetPath();
if (resolved_symfile_path != entry.ref()) {
result.AppendErrorWithFormat(
"invalid module path '%s' with resolved path '%s'\n",
entry.c_str(), resolved_symfile_path.c_str());
break;
}
result.AppendErrorWithFormat("invalid module path '%s'\n",
entry.c_str());
break;
}
}
}
}
}
if (flush) {
Process *process = m_exe_ctx.GetProcessPtr();
if (process)
process->Flush();
}
}
OptionGroupOptions m_option_group;
OptionGroupUUID m_uuid_option_group;
OptionGroupFile m_file_option;
OptionGroupBoolean m_current_frame_option;
OptionGroupBoolean m_current_stack_option;
};
#pragma mark CommandObjectTargetSymbols
// CommandObjectTargetSymbols
class CommandObjectTargetSymbols : public CommandObjectMultiword {
public:
// Constructors and Destructors
CommandObjectTargetSymbols(CommandInterpreter &interpreter)
: CommandObjectMultiword(
interpreter, "target symbols",
"Commands for adding and managing debug symbol files.",
"target symbols <sub-command> ...") {
LoadSubCommand(
"add", CommandObjectSP(new CommandObjectTargetSymbolsAdd(interpreter)));
}
~CommandObjectTargetSymbols() override = default;
private:
// For CommandObjectTargetModules only
CommandObjectTargetSymbols(const CommandObjectTargetSymbols &) = delete;
const CommandObjectTargetSymbols &
operator=(const CommandObjectTargetSymbols &) = delete;
};
#pragma mark CommandObjectTargetStopHookAdd
// CommandObjectTargetStopHookAdd
#define LLDB_OPTIONS_target_stop_hook_add
#include "CommandOptions.inc"
class CommandObjectTargetStopHookAdd : public CommandObjectParsed,
public IOHandlerDelegateMultiline {
public:
class CommandOptions : public OptionGroup {
public:
CommandOptions() : m_line_end(UINT_MAX) {}
~CommandOptions() override = default;
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
return llvm::ArrayRef(g_target_stop_hook_add_options);
}
Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
ExecutionContext *execution_context) override {
Status error;
const int short_option =
g_target_stop_hook_add_options[option_idx].short_option;
switch (short_option) {
case 'c':
m_class_name = std::string(option_arg);
m_sym_ctx_specified = true;
break;
case 'e':
if (option_arg.getAsInteger(0, m_line_end)) {
error = Status::FromErrorStringWithFormat(
"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 = Status::FromErrorStringWithFormat(
"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 = Status::FromErrorStringWithFormat(
"invalid start line number: \"%s\"", option_arg.str().c_str());
break;
}
m_sym_ctx_specified = true;
break;
case 'i':
m_no_inlines = true;
break;
case 'n':
m_function_name = std::string(option_arg);
m_func_name_type_mask |= eFunctionNameTypeAuto;
m_sym_ctx_specified = true;
break;
case 'f':
m_file_name = std::string(option_arg);
m_sym_ctx_specified = true;
break;
case 's':
m_module_name = std::string(option_arg);
m_sym_ctx_specified = true;
break;
case 't':
if (option_arg.getAsInteger(0, m_thread_id))
error = Status::FromErrorStringWithFormat(
"invalid thread id string '%s'", option_arg.str().c_str());
m_thread_specified = true;
break;
case 'T':
m_thread_name = std::string(option_arg);
m_thread_specified = true;
break;
case 'q':
m_queue_name = std::string(option_arg);
m_thread_specified = true;
break;
case 'x':
if (option_arg.getAsInteger(0, m_thread_index))
error = Status::FromErrorStringWithFormat(
"invalid thread index string '%s'", option_arg.str().c_str());
m_thread_specified = true;
break;
case 'o':
m_use_one_liner = true;
m_one_liner.push_back(std::string(option_arg));
break;
case 'I': {
bool value, success;
value = OptionArgParser::ToBoolean(option_arg, false, &success);
if (success)
m_at_initial_stop = value;
else
error = Status::FromErrorStringWithFormat(
"invalid boolean value '%s' passed for -F option",
option_arg.str().c_str());
} break;
default:
llvm_unreachable("Unimplemented option");
}
return error;
}
void OptionParsingStarting(ExecutionContext *execution_context) override {
m_class_name.clear();
m_function_name.clear();
m_line_start = 0;
m_line_end = LLDB_INVALID_LINE_NUMBER;
m_file_name.clear();
m_module_name.clear();
m_func_name_type_mask = eFunctionNameTypeAuto;
m_thread_id = LLDB_INVALID_THREAD_ID;
m_thread_index = UINT32_MAX;
m_thread_name.clear();
m_queue_name.clear();
m_no_inlines = false;
m_sym_ctx_specified = false;
m_thread_specified = false;
m_use_one_liner = false;
m_one_liner.clear();
m_auto_continue = false;
m_at_initial_stop = true;
}
std::string m_class_name;
std::string m_function_name;
uint32_t m_line_start = 0;
uint32_t m_line_end = LLDB_INVALID_LINE_NUMBER;
std::string m_file_name;
std::string m_module_name;
uint32_t m_func_name_type_mask =
eFunctionNameTypeAuto; // A pick from lldb::FunctionNameType.
lldb::tid_t m_thread_id = LLDB_INVALID_THREAD_ID;
uint32_t m_thread_index = UINT32_MAX;
std::string m_thread_name;
std::string m_queue_name;
bool m_sym_ctx_specified = false;
bool m_no_inlines = false;
bool m_thread_specified = false;
// Instance variables to hold the values for one_liner options.
bool m_use_one_liner = false;
std::vector<std::string> m_one_liner;
bool m_at_initial_stop;
bool m_auto_continue = false;
};
CommandObjectTargetStopHookAdd(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "target stop-hook add",
"Add a hook to be executed when the target stops."
"The hook can either be a list of commands or an "
"appropriately defined Python class. You can also "
"add filters so the hook only runs a certain stop "
"points.",
"target stop-hook add"),
IOHandlerDelegateMultiline("DONE",
IOHandlerDelegate::Completion::LLDBCommand),
m_python_class_options("scripted stop-hook", true, 'P') {
SetHelpLong(
R"(
Command Based stop-hooks:
-------------------------
Stop hooks can run a list of lldb commands by providing one or more
--one-liner options. The commands will get run in the order they are added.
Or you can provide no commands, in which case you will enter a command editor
where you can enter the commands to be run.
Python Based Stop Hooks:
------------------------
Stop hooks can be implemented with a suitably defined Python class, whose name
is passed in the --python-class option.
When the stop hook is added, the class is initialized by calling:
def __init__(self, target, extra_args, internal_dict):
target: The target that the stop hook is being added to.
extra_args: An SBStructuredData Dictionary filled with the -key -value
option pairs passed to the command.
dict: An implementation detail provided by lldb.
Then when the stop-hook triggers, lldb will run the 'handle_stop' method.
The method has the signature:
def handle_stop(self, exe_ctx, stream):
exe_ctx: An SBExecutionContext for the thread that has stopped.
stream: An SBStream, anything written to this stream will be printed in the
the stop message when the process stops.
Return Value: The method returns "should_stop". If should_stop is false
from all the stop hook executions on threads that stopped
with a reason, then the process will continue. Note that this
will happen only after all the stop hooks are run.
Filter Options:
---------------
Stop hooks can be set to always run, or to only run when the stopped thread
matches the filter options passed on the command line. The available filter
options include a shared library or a thread or queue specification,
a line range in a source file, a function name or a class name.
)");
m_all_options.Append(&m_python_class_options,
LLDB_OPT_SET_1 | LLDB_OPT_SET_2,
LLDB_OPT_SET_FROM_TO(4, 6));
m_all_options.Append(&m_options);
m_all_options.Finalize();
}
~CommandObjectTargetStopHookAdd() override = default;
Options *GetOptions() override { return &m_all_options; }
protected:
void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
if (interactive) {
if (lldb::LockableStreamFileSP output_sp =
io_handler.GetOutputStreamFileSP()) {
LockedStreamFile locked_stream = output_sp->Lock();
locked_stream.PutCString(
"Enter your stop hook command(s). Type 'DONE' to end.\n");
}
}
}
void IOHandlerInputComplete(IOHandler &io_handler,
std::string &line) override {
if (m_stop_hook_sp) {
if (line.empty()) {
if (lldb::LockableStreamFileSP error_sp =
io_handler.GetErrorStreamFileSP()) {
LockedStreamFile locked_stream = error_sp->Lock();
locked_stream.Printf("error: stop hook #%" PRIu64
" aborted, no commands.\n",
m_stop_hook_sp->GetID());
}
GetTarget().UndoCreateStopHook(m_stop_hook_sp->GetID());
} else {
// The IOHandler editor is only for command lines stop hooks:
Target::StopHookCommandLine *hook_ptr =
static_cast<Target::StopHookCommandLine *>(m_stop_hook_sp.get());
hook_ptr->SetActionFromString(line);
if (lldb::LockableStreamFileSP output_sp =
io_handler.GetOutputStreamFileSP()) {
LockedStreamFile locked_stream = output_sp->Lock();
locked_stream.Printf("Stop hook #%" PRIu64 " added.\n",
m_stop_hook_sp->GetID());
}
}
m_stop_hook_sp.reset();
}
io_handler.SetIsDone(true);
}
void DoExecute(Args &command, CommandReturnObject &result) override {
m_stop_hook_sp.reset();
Target &target = GetTarget();
Target::StopHookSP new_hook_sp =
target.CreateStopHook(m_python_class_options.GetName().empty() ?
Target::StopHook::StopHookKind::CommandBased
: Target::StopHook::StopHookKind::ScriptBased);
// First step, make the specifier.
std::unique_ptr<SymbolContextSpecifier> specifier_up;
if (m_options.m_sym_ctx_specified) {
specifier_up = std::make_unique<SymbolContextSpecifier>(
GetDebugger().GetSelectedTarget());
if (!m_options.m_module_name.empty()) {
specifier_up->AddSpecification(
m_options.m_module_name.c_str(),
SymbolContextSpecifier::eModuleSpecified);
}
if (!m_options.m_class_name.empty()) {
specifier_up->AddSpecification(
m_options.m_class_name.c_str(),
SymbolContextSpecifier::eClassOrNamespaceSpecified);
}
if (!m_options.m_file_name.empty()) {
specifier_up->AddSpecification(m_options.m_file_name.c_str(),
SymbolContextSpecifier::eFileSpecified);
}
if (m_options.m_line_start != 0) {
specifier_up->AddLineSpecification(
m_options.m_line_start,
SymbolContextSpecifier::eLineStartSpecified);
}
if (m_options.m_line_end != UINT_MAX) {
specifier_up->AddLineSpecification(
m_options.m_line_end, SymbolContextSpecifier::eLineEndSpecified);
}
if (!m_options.m_function_name.empty()) {
specifier_up->AddSpecification(
m_options.m_function_name.c_str(),
SymbolContextSpecifier::eFunctionSpecified);
}
}
if (specifier_up)
new_hook_sp->SetSpecifier(specifier_up.release());
// Should we run at the initial stop:
new_hook_sp->SetRunAtInitialStop(m_options.m_at_initial_stop);
// Next see if any of the thread options have been entered:
if (m_options.m_thread_specified) {
ThreadSpec *thread_spec = new ThreadSpec();
if (m_options.m_thread_id != LLDB_INVALID_THREAD_ID) {
thread_spec->SetTID(m_options.m_thread_id);
}
if (m_options.m_thread_index != UINT32_MAX)
thread_spec->SetIndex(m_options.m_thread_index);
if (!m_options.m_thread_name.empty())
thread_spec->SetName(m_options.m_thread_name.c_str());
if (!m_options.m_queue_name.empty())
thread_spec->SetQueueName(m_options.m_queue_name.c_str());
new_hook_sp->SetThreadSpecifier(thread_spec);
}
new_hook_sp->SetAutoContinue(m_options.m_auto_continue);
if (m_options.m_use_one_liner) {
// This is a command line stop hook:
Target::StopHookCommandLine *hook_ptr =
static_cast<Target::StopHookCommandLine *>(new_hook_sp.get());
hook_ptr->SetActionFromStrings(m_options.m_one_liner);
result.AppendMessageWithFormat("Stop hook #%" PRIu64 " added.\n",
new_hook_sp->GetID());
} else if (!m_python_class_options.GetName().empty()) {
// This is a scripted stop hook:
Target::StopHookScripted *hook_ptr =
static_cast<Target::StopHookScripted *>(new_hook_sp.get());
Status error = hook_ptr->SetScriptCallback(
m_python_class_options.GetName(),
m_python_class_options.GetStructuredData());
if (error.Success())
result.AppendMessageWithFormat("Stop hook #%" PRIu64 " added.\n",
new_hook_sp->GetID());
else {
// FIXME: Set the stop hook ID counter back.
result.AppendErrorWithFormat("Couldn't add stop hook: %s",
error.AsCString());
target.UndoCreateStopHook(new_hook_sp->GetID());
return;
}
} else {
m_stop_hook_sp = new_hook_sp;
m_interpreter.GetLLDBCommandsFromIOHandler("> ", // Prompt
*this); // IOHandlerDelegate
}
result.SetStatus(eReturnStatusSuccessFinishNoResult);
}
private:
CommandOptions m_options;
OptionGroupPythonClassWithDict m_python_class_options;
OptionGroupOptions m_all_options;
Target::StopHookSP m_stop_hook_sp;
};
#pragma mark CommandObjectTargetStopHookDelete
// CommandObjectTargetStopHookDelete
class CommandObjectTargetStopHookDelete : public CommandObjectParsed {
public:
CommandObjectTargetStopHookDelete(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "target stop-hook delete",
"Delete a stop-hook.",
"target stop-hook delete [<idx>]") {
AddSimpleArgumentList(eArgTypeStopHookID, eArgRepeatStar);
}
~CommandObjectTargetStopHookDelete() override = default;
void
HandleArgumentCompletion(CompletionRequest &request,
OptionElementVector &opt_element_vector) override {
if (request.GetCursorIndex())
return;
CommandObject::HandleArgumentCompletion(request, opt_element_vector);
}
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
Target &target = GetTarget();
// FIXME: see if we can use the breakpoint id style parser?
size_t num_args = command.GetArgumentCount();
if (num_args == 0) {
if (!m_interpreter.Confirm("Delete all stop hooks?", true)) {
result.SetStatus(eReturnStatusFailed);
return;
} else {
target.RemoveAllStopHooks();
}
} else {
for (size_t i = 0; i < num_args; i++) {
lldb::user_id_t user_id;
if (!llvm::to_integer(command.GetArgumentAtIndex(i), user_id)) {
result.AppendErrorWithFormat("invalid stop hook id: \"%s\".\n",
command.GetArgumentAtIndex(i));
return;
}
if (!target.RemoveStopHookByID(user_id)) {
result.AppendErrorWithFormat("unknown stop hook id: \"%s\".\n",
command.GetArgumentAtIndex(i));
return;
}
}
}
result.SetStatus(eReturnStatusSuccessFinishNoResult);
}
};
#pragma mark CommandObjectTargetStopHookEnableDisable
// CommandObjectTargetStopHookEnableDisable
class CommandObjectTargetStopHookEnableDisable : public CommandObjectParsed {
public:
CommandObjectTargetStopHookEnableDisable(CommandInterpreter &interpreter,
bool enable, const char *name,
const char *help, const char *syntax)
: CommandObjectParsed(interpreter, name, help, syntax), m_enable(enable) {
AddSimpleArgumentList(eArgTypeStopHookID, eArgRepeatStar);
}
~CommandObjectTargetStopHookEnableDisable() override = default;
void
HandleArgumentCompletion(CompletionRequest &request,
OptionElementVector &opt_element_vector) override {
if (request.GetCursorIndex())
return;
CommandObject::HandleArgumentCompletion(request, opt_element_vector);
}
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
Target &target = GetTarget();
// FIXME: see if we can use the breakpoint id style parser?
size_t num_args = command.GetArgumentCount();
bool success;
if (num_args == 0) {
target.SetAllStopHooksActiveState(m_enable);
} else {
for (size_t i = 0; i < num_args; i++) {
lldb::user_id_t user_id;
if (!llvm::to_integer(command.GetArgumentAtIndex(i), user_id)) {
result.AppendErrorWithFormat("invalid stop hook id: \"%s\".\n",
command.GetArgumentAtIndex(i));
return;
}
success = target.SetStopHookActiveStateByID(user_id, m_enable);
if (!success) {
result.AppendErrorWithFormat("unknown stop hook id: \"%s\".\n",
command.GetArgumentAtIndex(i));
return;
}
}
}
result.SetStatus(eReturnStatusSuccessFinishNoResult);
}
private:
bool m_enable;
};
#pragma mark CommandObjectTargetStopHookList
// CommandObjectTargetStopHookList
class CommandObjectTargetStopHookList : public CommandObjectParsed {
public:
CommandObjectTargetStopHookList(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "target stop-hook list",
"List all stop-hooks.", "target stop-hook list") {}
~CommandObjectTargetStopHookList() override = default;
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
Target &target = GetTarget();
size_t num_hooks = target.GetNumStopHooks();
if (num_hooks == 0) {
result.GetOutputStream().PutCString("No stop hooks.\n");
} else {
for (size_t i = 0; i < num_hooks; i++) {
Target::StopHookSP this_hook = target.GetStopHookAtIndex(i);
if (i > 0)
result.GetOutputStream().PutCString("\n");
this_hook->GetDescription(result.GetOutputStream(),
eDescriptionLevelFull);
}
}
result.SetStatus(eReturnStatusSuccessFinishResult);
}
};
#pragma mark CommandObjectMultiwordTargetStopHooks
// CommandObjectMultiwordTargetStopHooks
class CommandObjectMultiwordTargetStopHooks : public CommandObjectMultiword {
public:
CommandObjectMultiwordTargetStopHooks(CommandInterpreter &interpreter)
: CommandObjectMultiword(
interpreter, "target stop-hook",
"Commands for operating on debugger target stop-hooks.",
"target stop-hook <subcommand> [<subcommand-options>]") {
LoadSubCommand("add", CommandObjectSP(
new CommandObjectTargetStopHookAdd(interpreter)));
LoadSubCommand(
"delete",
CommandObjectSP(new CommandObjectTargetStopHookDelete(interpreter)));
LoadSubCommand("disable",
CommandObjectSP(new CommandObjectTargetStopHookEnableDisable(
interpreter, false, "target stop-hook disable [<id>]",
"Disable a stop-hook.", "target stop-hook disable")));
LoadSubCommand("enable",
CommandObjectSP(new CommandObjectTargetStopHookEnableDisable(
interpreter, true, "target stop-hook enable [<id>]",
"Enable a stop-hook.", "target stop-hook enable")));
LoadSubCommand("list", CommandObjectSP(new CommandObjectTargetStopHookList(
interpreter)));
}
~CommandObjectMultiwordTargetStopHooks() override = default;
};
#pragma mark CommandObjectTargetDumpTypesystem
/// Dumps the TypeSystem of the selected Target.
class CommandObjectTargetDumpTypesystem : public CommandObjectParsed {
public:
CommandObjectTargetDumpTypesystem(CommandInterpreter &interpreter)
: CommandObjectParsed(
interpreter, "target dump typesystem",
"Dump the state of the target's internal type system. Intended to "
"be used for debugging LLDB itself.",
nullptr, eCommandRequiresTarget) {}
~CommandObjectTargetDumpTypesystem() override = default;
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
// Go over every scratch TypeSystem and dump to the command output.
for (lldb::TypeSystemSP ts : GetTarget().GetScratchTypeSystems())
if (ts)
ts->Dump(result.GetOutputStream().AsRawOstream(), "");
result.SetStatus(eReturnStatusSuccessFinishResult);
}
};
#pragma mark CommandObjectTargetDumpSectionLoadList
/// Dumps the SectionLoadList of the selected Target.
class CommandObjectTargetDumpSectionLoadList : public CommandObjectParsed {
public:
CommandObjectTargetDumpSectionLoadList(CommandInterpreter &interpreter)
: CommandObjectParsed(
interpreter, "target dump section-load-list",
"Dump the state of the target's internal section load list. "
"Intended to be used for debugging LLDB itself.",
nullptr, eCommandRequiresTarget) {}
~CommandObjectTargetDumpSectionLoadList() override = default;
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
Target &target = GetTarget();
target.DumpSectionLoadList(result.GetOutputStream());
result.SetStatus(eReturnStatusSuccessFinishResult);
}
};
#pragma mark CommandObjectTargetDump
/// Multi-word command for 'target dump'.
class CommandObjectTargetDump : public CommandObjectMultiword {
public:
// Constructors and Destructors
CommandObjectTargetDump(CommandInterpreter &interpreter)
: CommandObjectMultiword(
interpreter, "target dump",
"Commands for dumping information about the target.",
"target dump [typesystem|section-load-list]") {
LoadSubCommand(
"typesystem",
CommandObjectSP(new CommandObjectTargetDumpTypesystem(interpreter)));
LoadSubCommand("section-load-list",
CommandObjectSP(new CommandObjectTargetDumpSectionLoadList(
interpreter)));
}
~CommandObjectTargetDump() override = default;
};
#pragma mark CommandObjectMultiwordTarget
// CommandObjectMultiwordTarget
CommandObjectMultiwordTarget::CommandObjectMultiwordTarget(
CommandInterpreter &interpreter)
: CommandObjectMultiword(interpreter, "target",
"Commands for operating on debugger targets.",
"target <subcommand> [<subcommand-options>]") {
LoadSubCommand("create",
CommandObjectSP(new CommandObjectTargetCreate(interpreter)));
LoadSubCommand("delete",
CommandObjectSP(new CommandObjectTargetDelete(interpreter)));
LoadSubCommand("dump",
CommandObjectSP(new CommandObjectTargetDump(interpreter)));
LoadSubCommand("list",
CommandObjectSP(new CommandObjectTargetList(interpreter)));
LoadSubCommand("select",
CommandObjectSP(new CommandObjectTargetSelect(interpreter)));
LoadSubCommand("show-launch-environment",
CommandObjectSP(new CommandObjectTargetShowLaunchEnvironment(
interpreter)));
LoadSubCommand(
"stop-hook",
CommandObjectSP(new CommandObjectMultiwordTargetStopHooks(interpreter)));
LoadSubCommand("modules",
CommandObjectSP(new CommandObjectTargetModules(interpreter)));
LoadSubCommand("symbols",
CommandObjectSP(new CommandObjectTargetSymbols(interpreter)));
LoadSubCommand("variable",
CommandObjectSP(new CommandObjectTargetVariable(interpreter)));
}
CommandObjectMultiwordTarget::~CommandObjectMultiwordTarget() = default;