mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-04-15 21:01:29 +00:00

Make breakpoint setting by file and line much more efficient by only looking for inlined breakpoint locations if we are setting a breakpoint in anything but a source implementation file. Implementing this complex for a many reasons. Turns out that parsing compile units lazily had some issues with respect to how we need to do things with DWARF in .o files. So the fixes in the checkin for this makes these changes: - Add a new setting called "target.inline-breakpoint-strategy" which can be set to "never", "always", or "headers". "never" will never try and set any inlined breakpoints (fastest). "always" always looks for inlined breakpoint locations (slowest, but most accurate). "headers", which is the default setting, will only look for inlined breakpoint locations if the breakpoint is set in what are consudered to be header files, which is realy defined as "not in an implementation source file". - modify the breakpoint setting by file and line to check the current "target.inline-breakpoint-strategy" setting and act accordingly - Modify compile units to be able to get their language and other info lazily. This allows us to create compile units from the debug map and not have to fill all of the details in, and then lazily discover this information as we go on debuggging. This is needed to avoid parsing all .o files when setting breakpoints in implementation only files (no inlines). Otherwise we would need to parse the .o file, the object file (mach-o in our case) and the symbol file (DWARF in the object file) just to see what the compile unit was. - modify the "SymbolFileDWARFDebugMap" to subclass lldb_private::Module so that the virtual "GetObjectFile()" and "GetSymbolVendor()" functions can be intercepted when the .o file contenst are later lazilly needed. Prior to this fix, when we first instantiated the "SymbolFileDWARFDebugMap" class, we would also make modules, object files and symbol files for every .o file in the debug map because we needed to fix up the sections in the .o files with information that is in the executable debug map. Now we lazily do this in the DebugMapModule::GetObjectFile() Cleaned up header includes a bit as well. llvm-svn: 162860
4854 lines
190 KiB
C++
4854 lines
190 KiB
C++
//===-- CommandObjectTarget.cpp ---------------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "CommandObjectTarget.h"
|
|
|
|
// C Includes
|
|
#include <errno.h>
|
|
|
|
// C++ Includes
|
|
// Other libraries and framework includes
|
|
// Project includes
|
|
#include "lldb/Interpreter/Args.h"
|
|
#include "lldb/Core/Debugger.h"
|
|
#include "lldb/Core/InputReader.h"
|
|
#include "lldb/Core/Module.h"
|
|
#include "lldb/Core/ModuleSpec.h"
|
|
#include "lldb/Core/Section.h"
|
|
#include "lldb/Core/State.h"
|
|
#include "lldb/Core/Timer.h"
|
|
#include "lldb/Core/ValueObjectVariable.h"
|
|
#include "lldb/Interpreter/CommandInterpreter.h"
|
|
#include "lldb/Interpreter/CommandReturnObject.h"
|
|
#include "lldb/Interpreter/Options.h"
|
|
#include "lldb/Interpreter/OptionGroupArchitecture.h"
|
|
#include "lldb/Interpreter/OptionGroupBoolean.h"
|
|
#include "lldb/Interpreter/OptionGroupFile.h"
|
|
#include "lldb/Interpreter/OptionGroupFormat.h"
|
|
#include "lldb/Interpreter/OptionGroupVariable.h"
|
|
#include "lldb/Interpreter/OptionGroupPlatform.h"
|
|
#include "lldb/Interpreter/OptionGroupUInt64.h"
|
|
#include "lldb/Interpreter/OptionGroupUUID.h"
|
|
#include "lldb/Interpreter/OptionGroupValueObjectDisplay.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/SymbolVendor.h"
|
|
#include "lldb/Symbol/UnwindPlan.h"
|
|
#include "lldb/Symbol/VariableList.h"
|
|
#include "lldb/Target/Process.h"
|
|
#include "lldb/Target/StackFrame.h"
|
|
#include "lldb/Target/Thread.h"
|
|
#include "lldb/Target/ThreadSpec.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
|
|
|
|
static void
|
|
DumpTargetInfo (uint32_t target_idx, Target *target, const char *prefix_cstr, bool show_stopped_process_status, Stream &strm)
|
|
{
|
|
const ArchSpec &target_arch = target->GetArchitecture();
|
|
|
|
Module *exe_module = target->GetExecutableModulePointer();
|
|
char exe_path[PATH_MAX];
|
|
bool exe_valid = false;
|
|
if (exe_module)
|
|
exe_valid = exe_module->GetFileSpec().GetPath (exe_path, sizeof(exe_path));
|
|
|
|
if (!exe_valid)
|
|
::strcpy (exe_path, "<none>");
|
|
|
|
strm.Printf ("%starget #%u: %s", prefix_cstr ? prefix_cstr : "", target_idx, exe_path);
|
|
|
|
uint32_t properties = 0;
|
|
if (target_arch.IsValid())
|
|
{
|
|
strm.Printf ("%sarch=%s", properties++ > 0 ? ", " : " ( ", target_arch.GetTriple().str().c_str());
|
|
properties++;
|
|
}
|
|
PlatformSP platform_sp (target->GetPlatform());
|
|
if (platform_sp)
|
|
strm.Printf ("%splatform=%s", 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=%llu", 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;
|
|
process_sp->GetStatus (strm);
|
|
process_sp->GetThreadStatus (strm,
|
|
only_threads_with_stop_reason,
|
|
start_frame,
|
|
num_frames,
|
|
num_frames_with_source);
|
|
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
#pragma mark CommandObjectTargetCreate
|
|
|
|
//-------------------------------------------------------------------------
|
|
// "target create"
|
|
//-------------------------------------------------------------------------
|
|
|
|
class CommandObjectTargetCreate : public CommandObjectParsed
|
|
{
|
|
public:
|
|
CommandObjectTargetCreate(CommandInterpreter &interpreter) :
|
|
CommandObjectParsed (interpreter,
|
|
"target create",
|
|
"Create a target using the argument as the main executable.",
|
|
NULL),
|
|
m_option_group (interpreter),
|
|
m_arch_option (),
|
|
m_platform_options(true), // Do include the "--platform" option in the platform settings by passing true
|
|
m_core_file (LLDB_OPT_SET_1, false, "core", 'c', 0, eArgTypePath, "Fullpath to a core file to use for this target.")
|
|
{
|
|
CommandArgumentEntry arg;
|
|
CommandArgumentData file_arg;
|
|
|
|
// Define the first (and only) variant of this arg.
|
|
file_arg.arg_type = eArgTypeFilename;
|
|
file_arg.arg_repetition = eArgRepeatPlain;
|
|
|
|
// There is only one variant this argument could be; put it into the argument entry.
|
|
arg.push_back (file_arg);
|
|
|
|
// Push the data for the first argument into the m_arguments vector.
|
|
m_arguments.push_back (arg);
|
|
|
|
m_option_group.Append (&m_arch_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
|
|
m_option_group.Append (&m_platform_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
|
|
m_option_group.Append (&m_core_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
|
|
m_option_group.Finalize();
|
|
}
|
|
|
|
~CommandObjectTargetCreate ()
|
|
{
|
|
}
|
|
|
|
Options *
|
|
GetOptions ()
|
|
{
|
|
return &m_option_group;
|
|
}
|
|
|
|
int
|
|
HandleArgumentCompletion (Args &input,
|
|
int &cursor_index,
|
|
int &cursor_char_position,
|
|
OptionElementVector &opt_element_vector,
|
|
int match_start_point,
|
|
int max_return_elements,
|
|
bool &word_complete,
|
|
StringList &matches)
|
|
{
|
|
std::string completion_str (input.GetArgumentAtIndex(cursor_index));
|
|
completion_str.erase (cursor_char_position);
|
|
|
|
CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter,
|
|
CommandCompletions::eDiskFileCompletion,
|
|
completion_str.c_str(),
|
|
match_start_point,
|
|
max_return_elements,
|
|
NULL,
|
|
word_complete,
|
|
matches);
|
|
return matches.GetSize();
|
|
}
|
|
|
|
protected:
|
|
bool
|
|
DoExecute (Args& command, CommandReturnObject &result)
|
|
{
|
|
const int argc = command.GetArgumentCount();
|
|
FileSpec core_file (m_core_file.GetOptionValue().GetCurrentValue());
|
|
|
|
if (argc == 1 || core_file)
|
|
{
|
|
const char *file_path = command.GetArgumentAtIndex(0);
|
|
Timer scoped_timer(__PRETTY_FUNCTION__, "(lldb) target create '%s'", file_path);
|
|
FileSpec file_spec;
|
|
|
|
if (file_path)
|
|
file_spec.SetFile (file_path, true);
|
|
|
|
TargetSP target_sp;
|
|
Debugger &debugger = m_interpreter.GetDebugger();
|
|
const char *arch_cstr = m_arch_option.GetArchitectureName();
|
|
const bool get_dependent_files = true;
|
|
Error error (debugger.GetTargetList().CreateTarget (debugger,
|
|
file_spec,
|
|
arch_cstr,
|
|
get_dependent_files,
|
|
&m_platform_options,
|
|
target_sp));
|
|
|
|
if (target_sp)
|
|
{
|
|
debugger.GetTargetList().SetSelectedTarget(target_sp.get());
|
|
if (core_file)
|
|
{
|
|
char core_path[PATH_MAX];
|
|
core_file.GetPath(core_path, sizeof(core_path));
|
|
if (core_file.Exists())
|
|
{
|
|
FileSpec core_file_dir;
|
|
core_file_dir.GetDirectory() = core_file.GetDirectory();
|
|
target_sp->GetExecutableSearchPaths ().Append (core_file_dir);
|
|
|
|
ProcessSP process_sp (target_sp->CreateProcess (m_interpreter.GetDebugger().GetListener(), NULL, &core_file));
|
|
|
|
if (process_sp)
|
|
{
|
|
// Seems wierd that we Launch a core file, but that is
|
|
// what we do!
|
|
error = process_sp->LoadCore();
|
|
|
|
if (error.Fail())
|
|
{
|
|
result.AppendError(error.AsCString("can't find plug-in for core file"));
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
result.AppendMessageWithFormat ("Core file '%s' (%s) was loaded.\n", core_path, target_sp->GetArchitecture().GetArchitectureName());
|
|
result.SetStatus (eReturnStatusSuccessFinishNoResult);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.AppendErrorWithFormat ("Unable to find process plug-in for core file '%s'\n", core_path);
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.AppendErrorWithFormat ("Core file '%s' does not exist\n", core_path);
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.AppendMessageWithFormat ("Current executable set to '%s' (%s).\n", file_path, target_sp->GetArchitecture().GetArchitectureName());
|
|
result.SetStatus (eReturnStatusSuccessFinishNoResult);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.AppendError(error.AsCString());
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.AppendErrorWithFormat("'%s' takes exactly one executable path argument, or use the --core-file option.\n", m_cmd_name.c_str());
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
return result.Succeeded();
|
|
|
|
}
|
|
|
|
private:
|
|
OptionGroupOptions m_option_group;
|
|
OptionGroupArchitecture m_arch_option;
|
|
OptionGroupPlatform m_platform_options;
|
|
OptionGroupFile m_core_file;
|
|
|
|
};
|
|
|
|
#pragma mark CommandObjectTargetList
|
|
|
|
//----------------------------------------------------------------------
|
|
// "target list"
|
|
//----------------------------------------------------------------------
|
|
|
|
class CommandObjectTargetList : public CommandObjectParsed
|
|
{
|
|
public:
|
|
CommandObjectTargetList (CommandInterpreter &interpreter) :
|
|
CommandObjectParsed (interpreter,
|
|
"target list",
|
|
"List all current targets in the current debug session.",
|
|
NULL,
|
|
0)
|
|
{
|
|
}
|
|
|
|
virtual
|
|
~CommandObjectTargetList ()
|
|
{
|
|
}
|
|
|
|
protected:
|
|
virtual bool
|
|
DoExecute (Args& args, CommandReturnObject &result)
|
|
{
|
|
if (args.GetArgumentCount() == 0)
|
|
{
|
|
Stream &strm = result.GetOutputStream();
|
|
|
|
bool show_stopped_process_status = false;
|
|
if (DumpTargetList (m_interpreter.GetDebugger().GetTargetList(), show_stopped_process_status, strm) == 0)
|
|
{
|
|
strm.PutCString ("No targets.\n");
|
|
}
|
|
result.SetStatus (eReturnStatusSuccessFinishResult);
|
|
}
|
|
else
|
|
{
|
|
result.AppendError ("the 'target list' command takes no arguments\n");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
return result.Succeeded();
|
|
}
|
|
};
|
|
|
|
|
|
#pragma mark CommandObjectTargetSelect
|
|
|
|
//----------------------------------------------------------------------
|
|
// "target select"
|
|
//----------------------------------------------------------------------
|
|
|
|
class CommandObjectTargetSelect : public CommandObjectParsed
|
|
{
|
|
public:
|
|
CommandObjectTargetSelect (CommandInterpreter &interpreter) :
|
|
CommandObjectParsed (interpreter,
|
|
"target select",
|
|
"Select a target as the current target by target index.",
|
|
NULL,
|
|
0)
|
|
{
|
|
}
|
|
|
|
virtual
|
|
~CommandObjectTargetSelect ()
|
|
{
|
|
}
|
|
|
|
protected:
|
|
virtual bool
|
|
DoExecute (Args& args, CommandReturnObject &result)
|
|
{
|
|
if (args.GetArgumentCount() == 1)
|
|
{
|
|
bool success = false;
|
|
const char *target_idx_arg = args.GetArgumentAtIndex(0);
|
|
uint32_t target_idx = Args::StringToUInt32 (target_idx_arg, UINT32_MAX, 0, &success);
|
|
if (success)
|
|
{
|
|
TargetList &target_list = m_interpreter.GetDebugger().GetTargetList();
|
|
const uint32_t num_targets = target_list.GetNumTargets();
|
|
if (target_idx < num_targets)
|
|
{
|
|
TargetSP target_sp (target_list.GetTargetAtIndex (target_idx));
|
|
if (target_sp)
|
|
{
|
|
Stream &strm = result.GetOutputStream();
|
|
target_list.SetSelectedTarget (target_sp.get());
|
|
bool show_stopped_process_status = false;
|
|
DumpTargetList (target_list, show_stopped_process_status, strm);
|
|
result.SetStatus (eReturnStatusSuccessFinishResult);
|
|
}
|
|
else
|
|
{
|
|
result.AppendErrorWithFormat ("target #%u is NULL in target list\n", target_idx);
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.AppendErrorWithFormat ("index %u is out of range, valid target indexes are 0 - %u\n",
|
|
target_idx,
|
|
num_targets - 1);
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.AppendErrorWithFormat("invalid index string value '%s'\n", target_idx_arg);
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.AppendError ("'target select' takes a single argument: a target index\n");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
return result.Succeeded();
|
|
}
|
|
};
|
|
|
|
#pragma mark CommandObjectTargetSelect
|
|
|
|
//----------------------------------------------------------------------
|
|
// "target delete"
|
|
//----------------------------------------------------------------------
|
|
|
|
class CommandObjectTargetDelete : public CommandObjectParsed
|
|
{
|
|
public:
|
|
CommandObjectTargetDelete (CommandInterpreter &interpreter) :
|
|
CommandObjectParsed (interpreter,
|
|
"target delete",
|
|
"Delete one or more targets by target index.",
|
|
NULL,
|
|
0),
|
|
m_option_group (interpreter),
|
|
m_cleanup_option (LLDB_OPT_SET_1, false, "clean", 'c', 0, eArgTypeNone, "Perform extra cleanup to minimize memory consumption after deleting the target.", false)
|
|
{
|
|
m_option_group.Append (&m_cleanup_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
|
|
m_option_group.Finalize();
|
|
}
|
|
|
|
virtual
|
|
~CommandObjectTargetDelete ()
|
|
{
|
|
}
|
|
|
|
Options *
|
|
GetOptions ()
|
|
{
|
|
return &m_option_group;
|
|
}
|
|
|
|
protected:
|
|
virtual bool
|
|
DoExecute (Args& args, CommandReturnObject &result)
|
|
{
|
|
const size_t argc = args.GetArgumentCount();
|
|
std::vector<TargetSP> delete_target_list;
|
|
TargetList &target_list = m_interpreter.GetDebugger().GetTargetList();
|
|
bool success = true;
|
|
TargetSP target_sp;
|
|
if (argc > 0)
|
|
{
|
|
const uint32_t num_targets = target_list.GetNumTargets();
|
|
// Bail out if don't have any targets.
|
|
if (num_targets == 0) {
|
|
result.AppendError("no targets to delete");
|
|
result.SetStatus(eReturnStatusFailed);
|
|
success = false;
|
|
}
|
|
|
|
for (uint32_t arg_idx = 0; success && arg_idx < argc; ++arg_idx)
|
|
{
|
|
const char *target_idx_arg = args.GetArgumentAtIndex(arg_idx);
|
|
uint32_t target_idx = Args::StringToUInt32 (target_idx_arg, UINT32_MAX, 0, &success);
|
|
if (success)
|
|
{
|
|
if (target_idx < num_targets)
|
|
{
|
|
target_sp = target_list.GetTargetAtIndex (target_idx);
|
|
if (target_sp)
|
|
{
|
|
delete_target_list.push_back (target_sp);
|
|
continue;
|
|
}
|
|
}
|
|
if (num_targets > 1)
|
|
result.AppendErrorWithFormat ("target index %u is out of range, valid target indexes are 0 - %u\n",
|
|
target_idx,
|
|
num_targets - 1);
|
|
else
|
|
result.AppendErrorWithFormat("target index %u is out of range, the only valid index is 0\n",
|
|
target_idx);
|
|
|
|
result.SetStatus (eReturnStatusFailed);
|
|
success = false;
|
|
}
|
|
else
|
|
{
|
|
result.AppendErrorWithFormat("invalid target index '%s'\n", target_idx_arg);
|
|
result.SetStatus (eReturnStatusFailed);
|
|
success = false;
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
target_sp = target_list.GetSelectedTarget();
|
|
if (target_sp)
|
|
{
|
|
delete_target_list.push_back (target_sp);
|
|
}
|
|
else
|
|
{
|
|
result.AppendErrorWithFormat("no target is currently selected\n");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
success = false;
|
|
}
|
|
}
|
|
if (success)
|
|
{
|
|
const size_t num_targets_to_delete = delete_target_list.size();
|
|
for (size_t idx = 0; idx < num_targets_to_delete; ++idx)
|
|
{
|
|
target_sp = delete_target_list[idx];
|
|
target_list.DeleteTarget(target_sp);
|
|
target_sp->Destroy();
|
|
}
|
|
// If "--clean" was specified, prune any orphaned shared modules from
|
|
// the global shared module list
|
|
if (m_cleanup_option.GetOptionValue ())
|
|
{
|
|
const bool mandatory = true;
|
|
ModuleList::RemoveOrphanSharedModules(mandatory);
|
|
}
|
|
result.GetOutputStream().Printf("%u targets deleted.\n", (uint32_t)num_targets_to_delete);
|
|
result.SetStatus(eReturnStatusSuccessFinishResult);
|
|
}
|
|
|
|
return result.Succeeded();
|
|
}
|
|
|
|
OptionGroupOptions m_option_group;
|
|
OptionGroupBoolean m_cleanup_option;
|
|
};
|
|
|
|
|
|
#pragma mark CommandObjectTargetVariable
|
|
|
|
//----------------------------------------------------------------------
|
|
// "target variable"
|
|
//----------------------------------------------------------------------
|
|
|
|
class CommandObjectTargetVariable : public CommandObjectParsed
|
|
{
|
|
public:
|
|
CommandObjectTargetVariable (CommandInterpreter &interpreter) :
|
|
CommandObjectParsed (interpreter,
|
|
"target variable",
|
|
"Read global variable(s) prior to running your binary.",
|
|
NULL,
|
|
0),
|
|
m_option_group (interpreter),
|
|
m_option_variable (false), // Don't include frame options
|
|
m_option_format (eFormatDefault),
|
|
m_option_compile_units (LLDB_OPT_SET_1, false, "file", 'f', 0, eArgTypePath, "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",'s', 0, eArgTypePath, "A basename or fullpath to a shared library to use in the search for global variables. This option can be specified multiple times."),
|
|
m_varobj_options()
|
|
{
|
|
CommandArgumentEntry arg;
|
|
CommandArgumentData var_name_arg;
|
|
|
|
// Define the first (and only) variant of this arg.
|
|
var_name_arg.arg_type = eArgTypeVarName;
|
|
var_name_arg.arg_repetition = eArgRepeatPlus;
|
|
|
|
// There is only one variant this argument could be; put it into the argument entry.
|
|
arg.push_back (var_name_arg);
|
|
|
|
// Push the data for the first argument into the m_arguments vector.
|
|
m_arguments.push_back (arg);
|
|
|
|
m_option_group.Append (&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
|
|
m_option_group.Append (&m_option_variable, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
|
|
m_option_group.Append (&m_option_format, OptionGroupFormat::OPTION_GROUP_FORMAT | OptionGroupFormat::OPTION_GROUP_GDB_FMT, LLDB_OPT_SET_1);
|
|
m_option_group.Append (&m_option_compile_units, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
|
|
m_option_group.Append (&m_option_shared_libraries, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
|
|
m_option_group.Finalize();
|
|
}
|
|
|
|
virtual
|
|
~CommandObjectTargetVariable ()
|
|
{
|
|
}
|
|
|
|
void
|
|
DumpValueObject (Stream &s, VariableSP &var_sp, ValueObjectSP &valobj_sp, const char *root_name)
|
|
{
|
|
ValueObject::DumpValueObjectOptions options;
|
|
|
|
options.SetMaximumPointerDepth(m_varobj_options.ptr_depth)
|
|
.SetMaximumDepth(m_varobj_options.max_depth)
|
|
.SetShowTypes(m_varobj_options.show_types)
|
|
.SetShowLocation(m_varobj_options.show_location)
|
|
.SetUseObjectiveC(m_varobj_options.use_objc)
|
|
.SetUseDynamicType(m_varobj_options.use_dynamic)
|
|
.SetUseSyntheticValue(m_varobj_options.use_synth)
|
|
.SetFlatOutput(m_varobj_options.flat_output)
|
|
.SetOmitSummaryDepth(m_varobj_options.no_summary_depth)
|
|
.SetIgnoreCap(m_varobj_options.ignore_cap);
|
|
|
|
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;
|
|
|
|
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);
|
|
|
|
ValueObject::DumpValueObject (s,
|
|
valobj_sp.get(),
|
|
options);
|
|
|
|
}
|
|
|
|
|
|
static uint32_t GetVariableCallback (void *baton,
|
|
const char *name,
|
|
VariableList &variable_list)
|
|
{
|
|
Target *target = static_cast<Target *>(baton);
|
|
if (target)
|
|
{
|
|
return target->GetImages().FindGlobalVariables (ConstString(name),
|
|
true,
|
|
UINT32_MAX,
|
|
variable_list);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
Options *
|
|
GetOptions ()
|
|
{
|
|
return &m_option_group;
|
|
}
|
|
|
|
protected:
|
|
virtual bool
|
|
DoExecute (Args& args, CommandReturnObject &result)
|
|
{
|
|
ExecutionContext exe_ctx (m_interpreter.GetExecutionContext());
|
|
Target *target = exe_ctx.GetTargetPtr();
|
|
if (target)
|
|
{
|
|
const size_t argc = args.GetArgumentCount();
|
|
Stream &s = result.GetOutputStream();
|
|
if (argc > 0)
|
|
{
|
|
|
|
for (size_t idx = 0; idx < argc; ++idx)
|
|
{
|
|
VariableList variable_list;
|
|
ValueObjectList valobj_list;
|
|
|
|
const char *arg = args.GetArgumentAtIndex(idx);
|
|
uint32_t matches = 0;
|
|
bool use_var_name = false;
|
|
if (m_option_variable.use_regex)
|
|
{
|
|
RegularExpression regex(arg);
|
|
if (!regex.IsValid ())
|
|
{
|
|
result.GetErrorStream().Printf ("error: invalid regular expression: '%s'\n", arg);
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
use_var_name = true;
|
|
matches = target->GetImages().FindGlobalVariables (regex,
|
|
true,
|
|
UINT32_MAX,
|
|
variable_list);
|
|
}
|
|
else
|
|
{
|
|
Error error (Variable::GetValuesForVariableExpressionPath (arg,
|
|
exe_ctx.GetBestExecutionContextScope(),
|
|
GetVariableCallback,
|
|
target,
|
|
variable_list,
|
|
valobj_list));
|
|
matches = variable_list.GetSize();
|
|
}
|
|
|
|
if (matches == 0)
|
|
{
|
|
result.GetErrorStream().Printf ("error: can't find global variable '%s'\n", arg);
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
for (uint32_t global_idx=0; global_idx<matches; ++global_idx)
|
|
{
|
|
VariableSP var_sp (variable_list.GetVariableAtIndex(global_idx));
|
|
if (var_sp)
|
|
{
|
|
ValueObjectSP valobj_sp (valobj_list.GetValueObjectAtIndex(global_idx));
|
|
if (!valobj_sp)
|
|
valobj_sp = ValueObjectVariable::Create (exe_ctx.GetBestExecutionContextScope(), var_sp);
|
|
|
|
if (valobj_sp)
|
|
DumpValueObject (s, var_sp, valobj_sp, use_var_name ? var_sp->GetName().GetCString() : arg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bool success = false;
|
|
StackFrame *frame = exe_ctx.GetFramePtr();
|
|
CompileUnit *comp_unit = NULL;
|
|
if (frame)
|
|
{
|
|
comp_unit = frame->GetSymbolContext (eSymbolContextCompUnit).comp_unit;
|
|
if (comp_unit)
|
|
{
|
|
const bool can_create = true;
|
|
VariableListSP comp_unit_varlist_sp (comp_unit->GetVariableList(can_create));
|
|
if (comp_unit_varlist_sp)
|
|
{
|
|
size_t count = comp_unit_varlist_sp->GetSize();
|
|
if (count > 0)
|
|
{
|
|
s.Printf ("Global variables for %s/%s:\n",
|
|
comp_unit->GetDirectory().GetCString(),
|
|
comp_unit->GetFilename().GetCString());
|
|
|
|
success = true;
|
|
for (uint32_t i=0; i<count; ++i)
|
|
{
|
|
VariableSP var_sp (comp_unit_varlist_sp->GetVariableAtIndex(i));
|
|
if (var_sp)
|
|
{
|
|
ValueObjectSP valobj_sp (ValueObjectVariable::Create (exe_ctx.GetBestExecutionContextScope(), var_sp));
|
|
|
|
if (valobj_sp)
|
|
DumpValueObject (s, var_sp, valobj_sp, var_sp->GetName().GetCString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!success)
|
|
{
|
|
if (frame)
|
|
{
|
|
if (comp_unit)
|
|
result.AppendErrorWithFormat ("no global variables in current compile unit: %s/%s\n",
|
|
comp_unit->GetDirectory().GetCString(),
|
|
comp_unit->GetFilename().GetCString());
|
|
else
|
|
result.AppendError ("no debug information for frame %u\n", frame->GetFrameIndex());
|
|
}
|
|
else
|
|
result.AppendError ("'target variable' takes one or more global variable names as arguments\n");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.AppendError ("invalid target, create a debug target using the 'target create' command");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
|
|
if (m_interpreter.TruncationWarningNecessary())
|
|
{
|
|
result.GetOutputStream().Printf(m_interpreter.TruncationWarningText(),
|
|
m_cmd_name.c_str());
|
|
m_interpreter.TruncationWarningGiven();
|
|
}
|
|
|
|
return result.Succeeded();
|
|
}
|
|
|
|
OptionGroupOptions m_option_group;
|
|
OptionGroupVariable m_option_variable;
|
|
OptionGroupFormat m_option_format;
|
|
OptionGroupFileList m_option_compile_units;
|
|
OptionGroupFileList m_option_shared_libraries;
|
|
OptionGroupValueObjectDisplay m_varobj_options;
|
|
|
|
};
|
|
|
|
|
|
#pragma mark CommandObjectTargetModulesSearchPathsAdd
|
|
|
|
class CommandObjectTargetModulesSearchPathsAdd : public CommandObjectParsed
|
|
{
|
|
public:
|
|
|
|
CommandObjectTargetModulesSearchPathsAdd (CommandInterpreter &interpreter) :
|
|
CommandObjectParsed (interpreter,
|
|
"target modules search-paths add",
|
|
"Add new image search paths substitution pairs to the current target.",
|
|
NULL)
|
|
{
|
|
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 ()
|
|
{
|
|
}
|
|
|
|
protected:
|
|
bool
|
|
DoExecute (Args& command,
|
|
CommandReturnObject &result)
|
|
{
|
|
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
|
|
if (target)
|
|
{
|
|
uint32_t argc = command.GetArgumentCount();
|
|
if (argc & 1)
|
|
{
|
|
result.AppendError ("add requires an even number of arguments\n");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
else
|
|
{
|
|
for (uint32_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])
|
|
{
|
|
bool last_pair = ((argc - i) == 2);
|
|
target->GetImageSearchPathList().Append (ConstString(from),
|
|
ConstString(to),
|
|
last_pair); // Notify if this is the last pair
|
|
result.SetStatus (eReturnStatusSuccessFinishNoResult);
|
|
}
|
|
else
|
|
{
|
|
if (from[0])
|
|
result.AppendError ("<path-prefix> can't be empty\n");
|
|
else
|
|
result.AppendError ("<new-path-prefix> can't be empty\n");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.AppendError ("invalid target\n");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
return result.Succeeded();
|
|
}
|
|
};
|
|
|
|
#pragma mark CommandObjectTargetModulesSearchPathsClear
|
|
|
|
class CommandObjectTargetModulesSearchPathsClear : public CommandObjectParsed
|
|
{
|
|
public:
|
|
|
|
CommandObjectTargetModulesSearchPathsClear (CommandInterpreter &interpreter) :
|
|
CommandObjectParsed (interpreter,
|
|
"target modules search-paths clear",
|
|
"Clear all current image search path substitution pairs from the current target.",
|
|
"target modules search-paths clear")
|
|
{
|
|
}
|
|
|
|
~CommandObjectTargetModulesSearchPathsClear ()
|
|
{
|
|
}
|
|
|
|
protected:
|
|
bool
|
|
DoExecute (Args& command,
|
|
CommandReturnObject &result)
|
|
{
|
|
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
|
|
if (target)
|
|
{
|
|
bool notify = true;
|
|
target->GetImageSearchPathList().Clear(notify);
|
|
result.SetStatus (eReturnStatusSuccessFinishNoResult);
|
|
}
|
|
else
|
|
{
|
|
result.AppendError ("invalid target\n");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
return result.Succeeded();
|
|
}
|
|
};
|
|
|
|
#pragma mark CommandObjectTargetModulesSearchPathsInsert
|
|
|
|
class CommandObjectTargetModulesSearchPathsInsert : public CommandObjectParsed
|
|
{
|
|
public:
|
|
|
|
CommandObjectTargetModulesSearchPathsInsert (CommandInterpreter &interpreter) :
|
|
CommandObjectParsed (interpreter,
|
|
"target modules search-paths insert",
|
|
"Insert a new image search path substitution pair into the current target at the specified index.",
|
|
NULL)
|
|
{
|
|
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 ()
|
|
{
|
|
}
|
|
|
|
protected:
|
|
bool
|
|
DoExecute (Args& command,
|
|
CommandReturnObject &result)
|
|
{
|
|
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
|
|
if (target)
|
|
{
|
|
uint32_t argc = command.GetArgumentCount();
|
|
// check for at least 3 arguments and an odd nubmer of parameters
|
|
if (argc >= 3 && argc & 1)
|
|
{
|
|
bool success = false;
|
|
|
|
uint32_t insert_idx = Args::StringToUInt32(command.GetArgumentAtIndex(0), UINT32_MAX, 0, &success);
|
|
|
|
if (!success)
|
|
{
|
|
result.AppendErrorWithFormat("<index> parameter is not an integer: '%s'.\n", command.GetArgumentAtIndex(0));
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return result.Succeeded();
|
|
}
|
|
|
|
// shift off the index
|
|
command.Shift();
|
|
argc = command.GetArgumentCount();
|
|
|
|
for (uint32_t i=0; i<argc; i+=2, ++insert_idx)
|
|
{
|
|
const char *from = command.GetArgumentAtIndex(i);
|
|
const char *to = command.GetArgumentAtIndex(i+1);
|
|
|
|
if (from[0] && to[0])
|
|
{
|
|
bool last_pair = ((argc - i) == 2);
|
|
target->GetImageSearchPathList().Insert (ConstString(from),
|
|
ConstString(to),
|
|
insert_idx,
|
|
last_pair);
|
|
result.SetStatus (eReturnStatusSuccessFinishNoResult);
|
|
}
|
|
else
|
|
{
|
|
if (from[0])
|
|
result.AppendError ("<path-prefix> can't be empty\n");
|
|
else
|
|
result.AppendError ("<new-path-prefix> can't be empty\n");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.AppendError ("insert requires at least three arguments\n");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return result.Succeeded();
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
result.AppendError ("invalid target\n");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
return result.Succeeded();
|
|
}
|
|
};
|
|
|
|
|
|
#pragma mark CommandObjectTargetModulesSearchPathsList
|
|
|
|
|
|
class CommandObjectTargetModulesSearchPathsList : public CommandObjectParsed
|
|
{
|
|
public:
|
|
|
|
CommandObjectTargetModulesSearchPathsList (CommandInterpreter &interpreter) :
|
|
CommandObjectParsed (interpreter,
|
|
"target modules search-paths list",
|
|
"List all current image search path substitution pairs in the current target.",
|
|
"target modules search-paths list")
|
|
{
|
|
}
|
|
|
|
~CommandObjectTargetModulesSearchPathsList ()
|
|
{
|
|
}
|
|
|
|
protected:
|
|
bool
|
|
DoExecute (Args& command,
|
|
CommandReturnObject &result)
|
|
{
|
|
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
|
|
if (target)
|
|
{
|
|
if (command.GetArgumentCount() != 0)
|
|
{
|
|
result.AppendError ("list takes no arguments\n");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return result.Succeeded();
|
|
}
|
|
|
|
target->GetImageSearchPathList().Dump(&result.GetOutputStream());
|
|
result.SetStatus (eReturnStatusSuccessFinishResult);
|
|
}
|
|
else
|
|
{
|
|
result.AppendError ("invalid target\n");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
return result.Succeeded();
|
|
}
|
|
};
|
|
|
|
#pragma mark CommandObjectTargetModulesSearchPathsQuery
|
|
|
|
class CommandObjectTargetModulesSearchPathsQuery : public CommandObjectParsed
|
|
{
|
|
public:
|
|
|
|
CommandObjectTargetModulesSearchPathsQuery (CommandInterpreter &interpreter) :
|
|
CommandObjectParsed (interpreter,
|
|
"target modules search-paths query",
|
|
"Transform a path using the first applicable image search path.",
|
|
NULL)
|
|
{
|
|
CommandArgumentEntry arg;
|
|
CommandArgumentData path_arg;
|
|
|
|
// Define the first (and only) variant of this arg.
|
|
path_arg.arg_type = eArgTypePath;
|
|
path_arg.arg_repetition = eArgRepeatPlain;
|
|
|
|
// There is only one variant this argument could be; put it into the argument entry.
|
|
arg.push_back (path_arg);
|
|
|
|
// Push the data for the first argument into the m_arguments vector.
|
|
m_arguments.push_back (arg);
|
|
}
|
|
|
|
~CommandObjectTargetModulesSearchPathsQuery ()
|
|
{
|
|
}
|
|
|
|
protected:
|
|
bool
|
|
DoExecute (Args& command,
|
|
CommandReturnObject &result)
|
|
{
|
|
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
|
|
if (target)
|
|
{
|
|
if (command.GetArgumentCount() != 1)
|
|
{
|
|
result.AppendError ("query requires one argument\n");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return result.Succeeded();
|
|
}
|
|
|
|
ConstString orig(command.GetArgumentAtIndex(0));
|
|
ConstString transformed;
|
|
if (target->GetImageSearchPathList().RemapPath(orig, transformed))
|
|
result.GetOutputStream().Printf("%s\n", transformed.GetCString());
|
|
else
|
|
result.GetOutputStream().Printf("%s\n", orig.GetCString());
|
|
|
|
result.SetStatus (eReturnStatusSuccessFinishResult);
|
|
}
|
|
else
|
|
{
|
|
result.AppendError ("invalid target\n");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
return result.Succeeded();
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Static Helper functions
|
|
//----------------------------------------------------------------------
|
|
static void
|
|
DumpModuleArchitecture (Stream &strm, Module *module, bool full_triple, uint32_t width)
|
|
{
|
|
if (module)
|
|
{
|
|
const char *arch_cstr;
|
|
if (full_triple)
|
|
arch_cstr = module->GetArchitecture().GetTriple().str().c_str();
|
|
else
|
|
arch_cstr = module->GetArchitecture().GetArchitectureName();
|
|
if (width)
|
|
strm.Printf("%-*s", width, arch_cstr);
|
|
else
|
|
strm.PutCString(arch_cstr);
|
|
}
|
|
}
|
|
|
|
static void
|
|
DumpModuleUUID (Stream &strm, Module *module)
|
|
{
|
|
if (module->GetUUID().IsValid())
|
|
module->GetUUID().Dump (&strm);
|
|
else
|
|
strm.PutCString(" ");
|
|
}
|
|
|
|
static uint32_t
|
|
DumpCompileUnitLineTable
|
|
(
|
|
CommandInterpreter &interpreter,
|
|
Stream &strm,
|
|
Module *module,
|
|
const FileSpec &file_spec,
|
|
bool load_addresses
|
|
)
|
|
{
|
|
uint32_t num_matches = 0;
|
|
if (module)
|
|
{
|
|
SymbolContextList sc_list;
|
|
num_matches = module->ResolveSymbolContextsForFileSpec (file_spec,
|
|
0,
|
|
false,
|
|
eSymbolContextCompUnit,
|
|
sc_list);
|
|
|
|
for (uint32_t i=0; i<num_matches; ++i)
|
|
{
|
|
SymbolContext sc;
|
|
if (sc_list.GetContextAtIndex(i, sc))
|
|
{
|
|
if (i > 0)
|
|
strm << "\n\n";
|
|
|
|
strm << "Line table for " << *static_cast<FileSpec*> (sc.comp_unit) << " in `"
|
|
<< module->GetFileSpec().GetFilename() << "\n";
|
|
LineTable *line_table = sc.comp_unit->GetLineTable();
|
|
if (line_table)
|
|
line_table->GetDescription (&strm,
|
|
interpreter.GetExecutionContext().GetTargetPtr(),
|
|
lldb::eDescriptionLevelBrief);
|
|
else
|
|
strm << "No line table";
|
|
}
|
|
}
|
|
}
|
|
return num_matches;
|
|
}
|
|
|
|
static void
|
|
DumpFullpath (Stream &strm, const FileSpec *file_spec_ptr, uint32_t width)
|
|
{
|
|
if (file_spec_ptr)
|
|
{
|
|
if (width > 0)
|
|
{
|
|
char fullpath[PATH_MAX];
|
|
if (file_spec_ptr->GetPath(fullpath, sizeof(fullpath)))
|
|
{
|
|
strm.Printf("%-*s", width, fullpath);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
file_spec_ptr->Dump(&strm);
|
|
return;
|
|
}
|
|
}
|
|
// Keep the width spacing correct if things go wrong...
|
|
if (width > 0)
|
|
strm.Printf("%-*s", width, "");
|
|
}
|
|
|
|
static void
|
|
DumpDirectory (Stream &strm, const FileSpec *file_spec_ptr, uint32_t width)
|
|
{
|
|
if (file_spec_ptr)
|
|
{
|
|
if (width > 0)
|
|
strm.Printf("%-*s", width, file_spec_ptr->GetDirectory().AsCString(""));
|
|
else
|
|
file_spec_ptr->GetDirectory().Dump(&strm);
|
|
return;
|
|
}
|
|
// Keep the width spacing correct if things go wrong...
|
|
if (width > 0)
|
|
strm.Printf("%-*s", width, "");
|
|
}
|
|
|
|
static void
|
|
DumpBasename (Stream &strm, const FileSpec *file_spec_ptr, uint32_t width)
|
|
{
|
|
if (file_spec_ptr)
|
|
{
|
|
if (width > 0)
|
|
strm.Printf("%-*s", width, file_spec_ptr->GetFilename().AsCString(""));
|
|
else
|
|
file_spec_ptr->GetFilename().Dump(&strm);
|
|
return;
|
|
}
|
|
// Keep the width spacing correct if things go wrong...
|
|
if (width > 0)
|
|
strm.Printf("%-*s", width, "");
|
|
}
|
|
|
|
|
|
static void
|
|
DumpModuleSymtab (CommandInterpreter &interpreter, Stream &strm, Module *module, SortOrder sort_order)
|
|
{
|
|
if (module)
|
|
{
|
|
ObjectFile *objfile = module->GetObjectFile ();
|
|
if (objfile)
|
|
{
|
|
Symtab *symtab = objfile->GetSymtab();
|
|
if (symtab)
|
|
symtab->Dump(&strm, interpreter.GetExecutionContext().GetTargetPtr(), sort_order);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
DumpModuleSections (CommandInterpreter &interpreter, Stream &strm, Module *module)
|
|
{
|
|
if (module)
|
|
{
|
|
ObjectFile *objfile = module->GetObjectFile ();
|
|
if (objfile)
|
|
{
|
|
SectionList *section_list = objfile->GetSectionList();
|
|
if (section_list)
|
|
{
|
|
strm.PutCString ("Sections for '");
|
|
strm << module->GetFileSpec();
|
|
if (module->GetObjectName())
|
|
strm << '(' << module->GetObjectName() << ')';
|
|
strm.Printf ("' (%s):\n", module->GetArchitecture().GetArchitectureName());
|
|
strm.IndentMore();
|
|
section_list->Dump(&strm, interpreter.GetExecutionContext().GetTargetPtr(), true, UINT32_MAX);
|
|
strm.IndentLess();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool
|
|
DumpModuleSymbolVendor (Stream &strm, Module *module)
|
|
{
|
|
if (module)
|
|
{
|
|
SymbolVendor *symbol_vendor = module->GetSymbolVendor(true);
|
|
if (symbol_vendor)
|
|
{
|
|
symbol_vendor->Dump(&strm);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
DumpAddress (ExecutionContextScope *exe_scope, const Address &so_addr, bool verbose, Stream &strm)
|
|
{
|
|
strm.IndentMore();
|
|
strm.Indent (" Address: ");
|
|
so_addr.Dump (&strm, exe_scope, Address::DumpStyleModuleWithFileAddress);
|
|
strm.PutCString (" (");
|
|
so_addr.Dump (&strm, exe_scope, Address::DumpStyleSectionNameOffset);
|
|
strm.PutCString (")\n");
|
|
strm.Indent (" Summary: ");
|
|
const uint32_t save_indent = strm.GetIndentLevel ();
|
|
strm.SetIndentLevel (save_indent + 13);
|
|
so_addr.Dump (&strm, exe_scope, Address::DumpStyleResolvedDescription);
|
|
strm.SetIndentLevel (save_indent);
|
|
// Print out detailed address information when verbose is enabled
|
|
if (verbose)
|
|
{
|
|
strm.EOL();
|
|
so_addr.Dump (&strm, exe_scope, Address::DumpStyleDetailedSymbolContext);
|
|
}
|
|
strm.IndentLess();
|
|
}
|
|
|
|
static bool
|
|
LookupAddressInModule (CommandInterpreter &interpreter,
|
|
Stream &strm,
|
|
Module *module,
|
|
uint32_t resolve_mask,
|
|
lldb::addr_t raw_addr,
|
|
lldb::addr_t offset,
|
|
bool verbose)
|
|
{
|
|
if (module)
|
|
{
|
|
lldb::addr_t addr = raw_addr - offset;
|
|
Address so_addr;
|
|
SymbolContext sc;
|
|
Target *target = interpreter.GetExecutionContext().GetTargetPtr();
|
|
if (target && !target->GetSectionLoadList().IsEmpty())
|
|
{
|
|
if (!target->GetSectionLoadList().ResolveLoadAddress (addr, so_addr))
|
|
return false;
|
|
else if (so_addr.GetModule().get() != module)
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (!module->ResolveFileAddress (addr, so_addr))
|
|
return false;
|
|
}
|
|
|
|
ExecutionContextScope *exe_scope = interpreter.GetExecutionContext().GetBestExecutionContextScope();
|
|
DumpAddress (exe_scope, so_addr, verbose, strm);
|
|
// strm.IndentMore();
|
|
// strm.Indent (" Address: ");
|
|
// so_addr.Dump (&strm, exe_scope, Address::DumpStyleModuleWithFileAddress);
|
|
// strm.PutCString (" (");
|
|
// so_addr.Dump (&strm, exe_scope, Address::DumpStyleSectionNameOffset);
|
|
// strm.PutCString (")\n");
|
|
// strm.Indent (" Summary: ");
|
|
// const uint32_t save_indent = strm.GetIndentLevel ();
|
|
// strm.SetIndentLevel (save_indent + 13);
|
|
// so_addr.Dump (&strm, exe_scope, Address::DumpStyleResolvedDescription);
|
|
// strm.SetIndentLevel (save_indent);
|
|
// // Print out detailed address information when verbose is enabled
|
|
// if (verbose)
|
|
// {
|
|
// strm.EOL();
|
|
// so_addr.Dump (&strm, exe_scope, Address::DumpStyleDetailedSymbolContext);
|
|
// }
|
|
// strm.IndentLess();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static uint32_t
|
|
LookupSymbolInModule (CommandInterpreter &interpreter, Stream &strm, Module *module, const char *name, bool name_is_regex, bool verbose)
|
|
{
|
|
if (module)
|
|
{
|
|
SymbolContext sc;
|
|
|
|
ObjectFile *objfile = module->GetObjectFile ();
|
|
if (objfile)
|
|
{
|
|
Symtab *symtab = objfile->GetSymtab();
|
|
if (symtab)
|
|
{
|
|
uint32_t i;
|
|
std::vector<uint32_t> match_indexes;
|
|
ConstString symbol_name (name);
|
|
uint32_t num_matches = 0;
|
|
if (name_is_regex)
|
|
{
|
|
RegularExpression name_regexp(name);
|
|
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 ();
|
|
//Symtab::DumpSymbolHeader (&strm);
|
|
for (i=0; i < num_matches; ++i)
|
|
{
|
|
Symbol *symbol = symtab->SymbolAtIndex(match_indexes[i]);
|
|
DumpAddress (interpreter.GetExecutionContext().GetBestExecutionContextScope(),
|
|
symbol->GetAddress(),
|
|
verbose,
|
|
strm);
|
|
|
|
// strm.Indent ();
|
|
// symbol->Dump (&strm, interpreter.GetExecutionContext().GetTargetPtr(), i);
|
|
}
|
|
strm.IndentLess ();
|
|
return num_matches;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void
|
|
DumpSymbolContextList (ExecutionContextScope *exe_scope, Stream &strm, SymbolContextList &sc_list, bool verbose)
|
|
{
|
|
strm.IndentMore ();
|
|
uint32_t i;
|
|
const uint32_t num_matches = sc_list.GetSize();
|
|
|
|
for (i=0; i<num_matches; ++i)
|
|
{
|
|
SymbolContext sc;
|
|
if (sc_list.GetContextAtIndex(i, sc))
|
|
{
|
|
AddressRange range;
|
|
|
|
sc.GetAddressRange(eSymbolContextEverything,
|
|
0,
|
|
true,
|
|
range);
|
|
|
|
DumpAddress (exe_scope, range.GetBaseAddress(), verbose, strm);
|
|
}
|
|
}
|
|
strm.IndentLess ();
|
|
}
|
|
|
|
static uint32_t
|
|
LookupFunctionInModule (CommandInterpreter &interpreter,
|
|
Stream &strm,
|
|
Module *module,
|
|
const char *name,
|
|
bool name_is_regex,
|
|
bool include_inlines,
|
|
bool include_symbols,
|
|
bool verbose)
|
|
{
|
|
if (module && name && name[0])
|
|
{
|
|
SymbolContextList sc_list;
|
|
const bool append = true;
|
|
uint32_t num_matches = 0;
|
|
if (name_is_regex)
|
|
{
|
|
RegularExpression function_name_regex (name);
|
|
num_matches = module->FindFunctions (function_name_regex,
|
|
include_symbols,
|
|
include_inlines,
|
|
append,
|
|
sc_list);
|
|
}
|
|
else
|
|
{
|
|
ConstString function_name (name);
|
|
num_matches = module->FindFunctions (function_name,
|
|
NULL,
|
|
eFunctionNameTypeBase | eFunctionNameTypeFull | eFunctionNameTypeMethod | eFunctionNameTypeSelector,
|
|
include_symbols,
|
|
include_inlines,
|
|
append,
|
|
sc_list);
|
|
}
|
|
|
|
if (num_matches)
|
|
{
|
|
strm.Indent ();
|
|
strm.Printf("%u match%s found in ", num_matches, num_matches > 1 ? "es" : "");
|
|
DumpFullpath (strm, &module->GetFileSpec(), 0);
|
|
strm.PutCString(":\n");
|
|
DumpSymbolContextList (interpreter.GetExecutionContext().GetBestExecutionContextScope(), strm, sc_list, verbose);
|
|
}
|
|
return num_matches;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t
|
|
LookupTypeInModule (CommandInterpreter &interpreter,
|
|
Stream &strm,
|
|
Module *module,
|
|
const char *name_cstr,
|
|
bool name_is_regex)
|
|
{
|
|
if (module && name_cstr && name_cstr[0])
|
|
{
|
|
TypeList type_list;
|
|
const uint32_t max_num_matches = UINT32_MAX;
|
|
uint32_t num_matches = 0;
|
|
bool name_is_fully_qualified = false;
|
|
SymbolContext sc;
|
|
|
|
ConstString name(name_cstr);
|
|
num_matches = module->FindTypes(sc, name, name_is_fully_qualified, max_num_matches, type_list);
|
|
|
|
if (num_matches)
|
|
{
|
|
strm.Indent ();
|
|
strm.Printf("%u match%s found in ", num_matches, num_matches > 1 ? "es" : "");
|
|
DumpFullpath (strm, &module->GetFileSpec(), 0);
|
|
strm.PutCString(":\n");
|
|
const uint32_t num_types = type_list.GetSize();
|
|
for (uint32_t i=0; i<num_types; ++i)
|
|
{
|
|
TypeSP type_sp (type_list.GetTypeAtIndex(i));
|
|
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->GetClangFullType ();
|
|
type_sp->GetDescription (&strm, eDescriptionLevelFull, true);
|
|
// Print all typedef chains
|
|
TypeSP typedef_type_sp (type_sp);
|
|
TypeSP typedefed_type_sp (typedef_type_sp->GetTypedefType());
|
|
while (typedefed_type_sp)
|
|
{
|
|
strm.EOL();
|
|
strm.Printf(" typedef '%s': ", typedef_type_sp->GetName().GetCString());
|
|
typedefed_type_sp->GetClangFullType ();
|
|
typedefed_type_sp->GetDescription (&strm, eDescriptionLevelFull, true);
|
|
typedef_type_sp = typedefed_type_sp;
|
|
typedefed_type_sp = typedef_type_sp->GetTypedefType();
|
|
}
|
|
}
|
|
strm.EOL();
|
|
}
|
|
}
|
|
return num_matches;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t
|
|
LookupTypeHere (CommandInterpreter &interpreter,
|
|
Stream &strm,
|
|
const SymbolContext &sym_ctx,
|
|
const char *name_cstr,
|
|
bool name_is_regex)
|
|
{
|
|
if (!sym_ctx.module_sp)
|
|
return 0;
|
|
|
|
TypeList type_list;
|
|
const uint32_t max_num_matches = UINT32_MAX;
|
|
uint32_t num_matches = 1;
|
|
bool name_is_fully_qualified = false;
|
|
|
|
ConstString name(name_cstr);
|
|
num_matches = sym_ctx.module_sp->FindTypes(sym_ctx, name, name_is_fully_qualified, max_num_matches, type_list);
|
|
|
|
if (num_matches)
|
|
{
|
|
strm.Indent ();
|
|
strm.PutCString("Best match found in ");
|
|
DumpFullpath (strm, &sym_ctx.module_sp->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->GetClangFullType ();
|
|
type_sp->GetDescription (&strm, eDescriptionLevelFull, true);
|
|
// Print all typedef chains
|
|
TypeSP typedef_type_sp (type_sp);
|
|
TypeSP typedefed_type_sp (typedef_type_sp->GetTypedefType());
|
|
while (typedefed_type_sp)
|
|
{
|
|
strm.EOL();
|
|
strm.Printf(" typedef '%s': ", typedef_type_sp->GetName().GetCString());
|
|
typedefed_type_sp->GetClangFullType ();
|
|
typedefed_type_sp->GetDescription (&strm, eDescriptionLevelFull, true);
|
|
typedef_type_sp = typedefed_type_sp;
|
|
typedefed_type_sp = typedef_type_sp->GetTypedefType();
|
|
}
|
|
}
|
|
strm.EOL();
|
|
}
|
|
return num_matches;
|
|
}
|
|
|
|
static uint32_t
|
|
LookupFileAndLineInModule (CommandInterpreter &interpreter,
|
|
Stream &strm,
|
|
Module *module,
|
|
const FileSpec &file_spec,
|
|
uint32_t line,
|
|
bool check_inlines,
|
|
bool verbose)
|
|
{
|
|
if (module && file_spec)
|
|
{
|
|
SymbolContextList sc_list;
|
|
const uint32_t num_matches = module->ResolveSymbolContextsForFileSpec(file_spec, line, check_inlines,
|
|
eSymbolContextEverything, sc_list);
|
|
if (num_matches > 0)
|
|
{
|
|
strm.Indent ();
|
|
strm.Printf("%u match%s found in ", num_matches, num_matches > 1 ? "es" : "");
|
|
strm << file_spec;
|
|
if (line > 0)
|
|
strm.Printf (":%u", line);
|
|
strm << " in ";
|
|
DumpFullpath (strm, &module->GetFileSpec(), 0);
|
|
strm.PutCString(":\n");
|
|
DumpSymbolContextList (interpreter.GetExecutionContext().GetBestExecutionContextScope(), strm, sc_list, verbose);
|
|
return num_matches;
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
static size_t
|
|
FindModulesByName (Target *target,
|
|
const char *module_name,
|
|
ModuleList &module_list,
|
|
bool check_global_list)
|
|
{
|
|
// Dump specified images (by basename or fullpath)
|
|
FileSpec module_file_spec(module_name, false);
|
|
ModuleSpec module_spec (module_file_spec);
|
|
|
|
const size_t initial_size = module_list.GetSize ();
|
|
|
|
if (check_global_list)
|
|
{
|
|
// Check the global list
|
|
Mutex::Locker locker(Module::GetAllocationModuleCollectionMutex());
|
|
const uint32_t num_modules = Module::GetNumberAllocatedModules();
|
|
ModuleSP module_sp;
|
|
for (uint32_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)
|
|
{
|
|
const size_t num_matches = target->GetImages().FindModules (module_spec, module_list);
|
|
|
|
// 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) :
|
|
CommandObjectParsed (interpreter, name, help, syntax)
|
|
{
|
|
CommandArgumentEntry arg;
|
|
CommandArgumentData file_arg;
|
|
|
|
// Define the first (and only) variant of this arg.
|
|
file_arg.arg_type = eArgTypeFilename;
|
|
file_arg.arg_repetition = eArgRepeatStar;
|
|
|
|
// There is only one variant this argument could be; put it into the argument entry.
|
|
arg.push_back (file_arg);
|
|
|
|
// Push the data for the first argument into the m_arguments vector.
|
|
m_arguments.push_back (arg);
|
|
}
|
|
|
|
virtual
|
|
~CommandObjectTargetModulesModuleAutoComplete ()
|
|
{
|
|
}
|
|
|
|
virtual int
|
|
HandleArgumentCompletion (Args &input,
|
|
int &cursor_index,
|
|
int &cursor_char_position,
|
|
OptionElementVector &opt_element_vector,
|
|
int match_start_point,
|
|
int max_return_elements,
|
|
bool &word_complete,
|
|
StringList &matches)
|
|
{
|
|
// Arguments are the standard module completer.
|
|
std::string completion_str (input.GetArgumentAtIndex(cursor_index));
|
|
completion_str.erase (cursor_char_position);
|
|
|
|
CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter,
|
|
CommandCompletions::eModuleCompletion,
|
|
completion_str.c_str(),
|
|
match_start_point,
|
|
max_return_elements,
|
|
NULL,
|
|
word_complete,
|
|
matches);
|
|
return matches.GetSize();
|
|
}
|
|
};
|
|
|
|
#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) :
|
|
CommandObjectParsed (interpreter, name, help, syntax)
|
|
{
|
|
CommandArgumentEntry arg;
|
|
CommandArgumentData source_file_arg;
|
|
|
|
// Define the first (and only) variant of this arg.
|
|
source_file_arg.arg_type = eArgTypeSourceFile;
|
|
source_file_arg.arg_repetition = eArgRepeatPlus;
|
|
|
|
// There is only one variant this argument could be; put it into the argument entry.
|
|
arg.push_back (source_file_arg);
|
|
|
|
// Push the data for the first argument into the m_arguments vector.
|
|
m_arguments.push_back (arg);
|
|
}
|
|
|
|
virtual
|
|
~CommandObjectTargetModulesSourceFileAutoComplete ()
|
|
{
|
|
}
|
|
|
|
virtual int
|
|
HandleArgumentCompletion (Args &input,
|
|
int &cursor_index,
|
|
int &cursor_char_position,
|
|
OptionElementVector &opt_element_vector,
|
|
int match_start_point,
|
|
int max_return_elements,
|
|
bool &word_complete,
|
|
StringList &matches)
|
|
{
|
|
// Arguments are the standard source file completer.
|
|
std::string completion_str (input.GetArgumentAtIndex(cursor_index));
|
|
completion_str.erase (cursor_char_position);
|
|
|
|
CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter,
|
|
CommandCompletions::eSourceFileCompletion,
|
|
completion_str.c_str(),
|
|
match_start_point,
|
|
max_return_elements,
|
|
NULL,
|
|
word_complete,
|
|
matches);
|
|
return matches.GetSize();
|
|
}
|
|
};
|
|
|
|
|
|
#pragma mark CommandObjectTargetModulesDumpSymtab
|
|
|
|
|
|
class CommandObjectTargetModulesDumpSymtab : public CommandObjectTargetModulesModuleAutoComplete
|
|
{
|
|
public:
|
|
CommandObjectTargetModulesDumpSymtab (CommandInterpreter &interpreter) :
|
|
CommandObjectTargetModulesModuleAutoComplete (interpreter,
|
|
"target modules dump symtab",
|
|
"Dump the symbol table from one or more target modules.",
|
|
NULL),
|
|
m_options (interpreter)
|
|
{
|
|
}
|
|
|
|
virtual
|
|
~CommandObjectTargetModulesDumpSymtab ()
|
|
{
|
|
}
|
|
|
|
virtual Options *
|
|
GetOptions ()
|
|
{
|
|
return &m_options;
|
|
}
|
|
|
|
class CommandOptions : public Options
|
|
{
|
|
public:
|
|
|
|
CommandOptions (CommandInterpreter &interpreter) :
|
|
Options(interpreter),
|
|
m_sort_order (eSortOrderNone)
|
|
{
|
|
}
|
|
|
|
virtual
|
|
~CommandOptions ()
|
|
{
|
|
}
|
|
|
|
virtual Error
|
|
SetOptionValue (uint32_t option_idx, const char *option_arg)
|
|
{
|
|
Error error;
|
|
char short_option = (char) m_getopt_table[option_idx].val;
|
|
|
|
switch (short_option)
|
|
{
|
|
case 's':
|
|
m_sort_order = (SortOrder) Args::StringToOptionEnum (option_arg,
|
|
g_option_table[option_idx].enum_values,
|
|
eSortOrderNone,
|
|
error);
|
|
break;
|
|
|
|
default:
|
|
error.SetErrorStringWithFormat("invalid short option character '%c'", short_option);
|
|
break;
|
|
|
|
}
|
|
return error;
|
|
}
|
|
|
|
void
|
|
OptionParsingStarting ()
|
|
{
|
|
m_sort_order = eSortOrderNone;
|
|
}
|
|
|
|
const OptionDefinition*
|
|
GetDefinitions ()
|
|
{
|
|
return g_option_table;
|
|
}
|
|
|
|
// Options table: Required for subclasses of Options.
|
|
static OptionDefinition g_option_table[];
|
|
|
|
SortOrder m_sort_order;
|
|
};
|
|
|
|
protected:
|
|
virtual bool
|
|
DoExecute (Args& command,
|
|
CommandReturnObject &result)
|
|
{
|
|
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
|
|
if (target == NULL)
|
|
{
|
|
result.AppendError ("invalid target, create a debug target using the 'target create' command");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
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
|
|
Mutex::Locker modules_locker(target->GetImages().GetMutex());
|
|
const uint32_t num_modules = target->GetImages().GetSize();
|
|
if (num_modules > 0)
|
|
{
|
|
result.GetOutputStream().Printf("Dumping symbol table for %u modules.\n", num_modules);
|
|
for (uint32_t image_idx = 0; image_idx<num_modules; ++image_idx)
|
|
{
|
|
if (num_dumped > 0)
|
|
{
|
|
result.GetOutputStream().EOL();
|
|
result.GetOutputStream().EOL();
|
|
}
|
|
num_dumped++;
|
|
DumpModuleSymtab (m_interpreter,
|
|
result.GetOutputStream(),
|
|
target->GetImages().GetModulePointerAtIndexUnlocked(image_idx),
|
|
m_options.m_sort_order);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.AppendError ("the target has no associated executable images");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Dump specified images (by basename or fullpath)
|
|
const char *arg_cstr;
|
|
for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != NULL; ++arg_idx)
|
|
{
|
|
ModuleList module_list;
|
|
const size_t num_matches = FindModulesByName (target, arg_cstr, module_list, true);
|
|
if (num_matches > 0)
|
|
{
|
|
for (size_t i=0; i<num_matches; ++i)
|
|
{
|
|
Module *module = module_list.GetModulePointerAtIndex(i);
|
|
if (module)
|
|
{
|
|
if (num_dumped > 0)
|
|
{
|
|
result.GetOutputStream().EOL();
|
|
result.GetOutputStream().EOL();
|
|
}
|
|
num_dumped++;
|
|
DumpModuleSymtab (m_interpreter, result.GetOutputStream(), module, m_options.m_sort_order);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
result.AppendWarningWithFormat("Unable to find an image that matches '%s'.\n", arg_cstr);
|
|
}
|
|
}
|
|
|
|
if (num_dumped > 0)
|
|
result.SetStatus (eReturnStatusSuccessFinishResult);
|
|
else
|
|
{
|
|
result.AppendError ("no matching executable images found");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
}
|
|
return result.Succeeded();
|
|
}
|
|
|
|
|
|
CommandOptions m_options;
|
|
};
|
|
|
|
static OptionEnumValueElement
|
|
g_sort_option_enumeration[4] =
|
|
{
|
|
{ eSortOrderNone, "none", "No sorting, use the original symbol table order."},
|
|
{ eSortOrderByAddress, "address", "Sort output by symbol address."},
|
|
{ eSortOrderByName, "name", "Sort output by symbol name."},
|
|
{ 0, NULL, NULL }
|
|
};
|
|
|
|
|
|
OptionDefinition
|
|
CommandObjectTargetModulesDumpSymtab::CommandOptions::g_option_table[] =
|
|
{
|
|
{ LLDB_OPT_SET_1, false, "sort", 's', required_argument, g_sort_option_enumeration, 0, eArgTypeSortOrder, "Supply a sort order when dumping the symbol table."},
|
|
{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
|
|
};
|
|
|
|
#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> ...]")
|
|
NULL)
|
|
{
|
|
}
|
|
|
|
virtual
|
|
~CommandObjectTargetModulesDumpSections ()
|
|
{
|
|
}
|
|
|
|
protected:
|
|
virtual bool
|
|
DoExecute (Args& command,
|
|
CommandReturnObject &result)
|
|
{
|
|
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
|
|
if (target == NULL)
|
|
{
|
|
result.AppendError ("invalid target, create a debug target using the 'target create' command");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
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 uint32_t num_modules = target->GetImages().GetSize();
|
|
if (num_modules > 0)
|
|
{
|
|
result.GetOutputStream().Printf("Dumping sections for %u modules.\n", num_modules);
|
|
for (uint32_t image_idx = 0; image_idx<num_modules; ++image_idx)
|
|
{
|
|
num_dumped++;
|
|
DumpModuleSections (m_interpreter, result.GetOutputStream(), target->GetImages().GetModulePointerAtIndex(image_idx));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.AppendError ("the target has no associated executable images");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Dump specified images (by basename or fullpath)
|
|
const char *arg_cstr;
|
|
for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != NULL; ++arg_idx)
|
|
{
|
|
ModuleList module_list;
|
|
const size_t num_matches = FindModulesByName (target, arg_cstr, module_list, true);
|
|
if (num_matches > 0)
|
|
{
|
|
for (size_t i=0; i<num_matches; ++i)
|
|
{
|
|
Module *module = module_list.GetModulePointerAtIndex(i);
|
|
if (module)
|
|
{
|
|
num_dumped++;
|
|
DumpModuleSections (m_interpreter, result.GetOutputStream(), module);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Check the global list
|
|
Mutex::Locker locker(Module::GetAllocationModuleCollectionMutex());
|
|
|
|
result.AppendWarningWithFormat("Unable to find an image that matches '%s'.\n", arg_cstr);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (num_dumped > 0)
|
|
result.SetStatus (eReturnStatusSuccessFinishResult);
|
|
else
|
|
{
|
|
result.AppendError ("no matching executable images found");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
}
|
|
return result.Succeeded();
|
|
}
|
|
};
|
|
|
|
|
|
#pragma mark 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> ...]")
|
|
NULL)
|
|
{
|
|
}
|
|
|
|
virtual
|
|
~CommandObjectTargetModulesDumpSymfile ()
|
|
{
|
|
}
|
|
|
|
protected:
|
|
virtual bool
|
|
DoExecute (Args& command,
|
|
CommandReturnObject &result)
|
|
{
|
|
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
|
|
if (target == NULL)
|
|
{
|
|
result.AppendError ("invalid target, create a debug target using the 'target create' command");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
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
|
|
ModuleList &target_modules = target->GetImages();
|
|
Mutex::Locker modules_locker (target_modules.GetMutex());
|
|
const uint32_t num_modules = target_modules.GetSize();
|
|
if (num_modules > 0)
|
|
{
|
|
result.GetOutputStream().Printf("Dumping debug symbols for %u modules.\n", num_modules);
|
|
for (uint32_t image_idx = 0; image_idx<num_modules; ++image_idx)
|
|
{
|
|
if (DumpModuleSymbolVendor (result.GetOutputStream(), target_modules.GetModulePointerAtIndexUnlocked(image_idx)))
|
|
num_dumped++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.AppendError ("the target has no associated executable images");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Dump specified images (by basename or fullpath)
|
|
const char *arg_cstr;
|
|
for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != NULL; ++arg_idx)
|
|
{
|
|
ModuleList module_list;
|
|
const size_t num_matches = FindModulesByName (target, arg_cstr, module_list, true);
|
|
if (num_matches > 0)
|
|
{
|
|
for (size_t i=0; i<num_matches; ++i)
|
|
{
|
|
Module *module = module_list.GetModulePointerAtIndex(i);
|
|
if (module)
|
|
{
|
|
if (DumpModuleSymbolVendor (result.GetOutputStream(), module))
|
|
num_dumped++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
result.AppendWarningWithFormat("Unable to find an image that matches '%s'.\n", arg_cstr);
|
|
}
|
|
}
|
|
|
|
if (num_dumped > 0)
|
|
result.SetStatus (eReturnStatusSuccessFinishResult);
|
|
else
|
|
{
|
|
result.AppendError ("no matching executable images found");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
}
|
|
return result.Succeeded();
|
|
}
|
|
};
|
|
|
|
|
|
#pragma mark CommandObjectTargetModulesDumpLineTable
|
|
|
|
//----------------------------------------------------------------------
|
|
// Image debug line table dumping command
|
|
//----------------------------------------------------------------------
|
|
|
|
class CommandObjectTargetModulesDumpLineTable : public CommandObjectTargetModulesSourceFileAutoComplete
|
|
{
|
|
public:
|
|
CommandObjectTargetModulesDumpLineTable (CommandInterpreter &interpreter) :
|
|
CommandObjectTargetModulesSourceFileAutoComplete (interpreter,
|
|
"target modules dump line-table",
|
|
"Dump the debug symbol file for one or more target modules.",
|
|
NULL)
|
|
{
|
|
}
|
|
|
|
virtual
|
|
~CommandObjectTargetModulesDumpLineTable ()
|
|
{
|
|
}
|
|
|
|
protected:
|
|
virtual bool
|
|
DoExecute (Args& command,
|
|
CommandReturnObject &result)
|
|
{
|
|
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
|
|
if (target == NULL)
|
|
{
|
|
result.AppendError ("invalid target, create a debug target using the 'target create' command");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
ExecutionContext exe_ctx(m_interpreter.GetExecutionContext());
|
|
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.AppendErrorWithFormat ("\nSyntax: %s\n", m_cmd_syntax.c_str());
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
else
|
|
{
|
|
// Dump specified images (by basename or fullpath)
|
|
const char *arg_cstr;
|
|
for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != NULL; ++arg_idx)
|
|
{
|
|
FileSpec file_spec(arg_cstr, false);
|
|
|
|
ModuleList &target_modules = target->GetImages();
|
|
Mutex::Locker modules_locker(target_modules.GetMutex());
|
|
const uint32_t num_modules = target_modules.GetSize();
|
|
if (num_modules > 0)
|
|
{
|
|
uint32_t num_dumped = 0;
|
|
for (uint32_t i = 0; i<num_modules; ++i)
|
|
{
|
|
if (DumpCompileUnitLineTable (m_interpreter,
|
|
result.GetOutputStream(),
|
|
target_modules.GetModulePointerAtIndexUnlocked(i),
|
|
file_spec,
|
|
exe_ctx.GetProcessPtr() && exe_ctx.GetProcessRef().IsAlive()))
|
|
num_dumped++;
|
|
}
|
|
if (num_dumped == 0)
|
|
result.AppendWarningWithFormat ("No source filenames matched '%s'.\n", arg_cstr);
|
|
else
|
|
total_num_dumped += num_dumped;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (total_num_dumped > 0)
|
|
result.SetStatus (eReturnStatusSuccessFinishResult);
|
|
else
|
|
{
|
|
result.AppendError ("no source filenames matched any command arguments");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
}
|
|
return result.Succeeded();
|
|
}
|
|
};
|
|
|
|
|
|
#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",
|
|
"A set of commands for dumping information about one or more target modules.",
|
|
"target modules dump [symtab|sections|symfile|line-table] [<file1> <file2> ...]")
|
|
{
|
|
LoadSubCommand ("symtab", CommandObjectSP (new CommandObjectTargetModulesDumpSymtab (interpreter)));
|
|
LoadSubCommand ("sections", CommandObjectSP (new CommandObjectTargetModulesDumpSections (interpreter)));
|
|
LoadSubCommand ("symfile", CommandObjectSP (new CommandObjectTargetModulesDumpSymfile (interpreter)));
|
|
LoadSubCommand ("line-table", CommandObjectSP (new CommandObjectTargetModulesDumpLineTable (interpreter)));
|
|
}
|
|
|
|
virtual
|
|
~CommandObjectTargetModulesDump()
|
|
{
|
|
}
|
|
};
|
|
|
|
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>]")
|
|
{
|
|
}
|
|
|
|
virtual
|
|
~CommandObjectTargetModulesAdd ()
|
|
{
|
|
}
|
|
|
|
int
|
|
HandleArgumentCompletion (Args &input,
|
|
int &cursor_index,
|
|
int &cursor_char_position,
|
|
OptionElementVector &opt_element_vector,
|
|
int match_start_point,
|
|
int max_return_elements,
|
|
bool &word_complete,
|
|
StringList &matches)
|
|
{
|
|
std::string completion_str (input.GetArgumentAtIndex(cursor_index));
|
|
completion_str.erase (cursor_char_position);
|
|
|
|
CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter,
|
|
CommandCompletions::eDiskFileCompletion,
|
|
completion_str.c_str(),
|
|
match_start_point,
|
|
max_return_elements,
|
|
NULL,
|
|
word_complete,
|
|
matches);
|
|
return matches.GetSize();
|
|
}
|
|
|
|
protected:
|
|
virtual bool
|
|
DoExecute (Args& args,
|
|
CommandReturnObject &result)
|
|
{
|
|
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
|
|
if (target == NULL)
|
|
{
|
|
result.AppendError ("invalid target, create a debug target using the 'target create' command");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
const size_t argc = args.GetArgumentCount();
|
|
if (argc == 0)
|
|
{
|
|
result.AppendError ("one or more executable image paths must be specified");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
for (size_t i=0; i<argc; ++i)
|
|
{
|
|
const char *path = args.GetArgumentAtIndex(i);
|
|
if (path)
|
|
{
|
|
FileSpec file_spec(path, true);
|
|
if (file_spec.Exists())
|
|
{
|
|
ModuleSpec module_spec (file_spec);
|
|
ModuleSP module_sp (target->GetSharedModule (module_spec));
|
|
if (!module_sp)
|
|
{
|
|
result.AppendError ("one or more executable image paths must be specified");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
result.SetStatus (eReturnStatusSuccessFinishResult);
|
|
}
|
|
else
|
|
{
|
|
char resolved_path[PATH_MAX];
|
|
result.SetStatus (eReturnStatusFailed);
|
|
if (file_spec.GetPath (resolved_path, sizeof(resolved_path)))
|
|
{
|
|
if (strcmp (resolved_path, path) != 0)
|
|
{
|
|
result.AppendErrorWithFormat ("invalid module path '%s' with resolved path '%s'\n", path, resolved_path);
|
|
break;
|
|
}
|
|
}
|
|
result.AppendErrorWithFormat ("invalid module path '%s'\n", path);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result.Succeeded();
|
|
}
|
|
|
|
};
|
|
|
|
class CommandObjectTargetModulesLoad : public CommandObjectTargetModulesModuleAutoComplete
|
|
{
|
|
public:
|
|
CommandObjectTargetModulesLoad (CommandInterpreter &interpreter) :
|
|
CommandObjectTargetModulesModuleAutoComplete (interpreter,
|
|
"target modules load",
|
|
"Set the load addresses for one or more sections in a target module.",
|
|
"target modules load [--file <module> --uuid <uuid>] <sect-name> <address> [<sect-name> <address> ....]"),
|
|
m_option_group (interpreter),
|
|
m_file_option (LLDB_OPT_SET_1, false, "file", 'f', 0, eArgTypePath, "Fullpath or basename for module to load."),
|
|
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_slide_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
|
|
m_option_group.Finalize();
|
|
}
|
|
|
|
virtual
|
|
~CommandObjectTargetModulesLoad ()
|
|
{
|
|
}
|
|
|
|
virtual Options *
|
|
GetOptions ()
|
|
{
|
|
return &m_option_group;
|
|
}
|
|
|
|
protected:
|
|
virtual bool
|
|
DoExecute (Args& args,
|
|
CommandReturnObject &result)
|
|
{
|
|
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
|
|
if (target == NULL)
|
|
{
|
|
result.AppendError ("invalid target, create a debug target using the 'target create' command");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
const size_t argc = args.GetArgumentCount();
|
|
ModuleSpec module_spec;
|
|
bool search_using_module_spec = false;
|
|
if (m_file_option.GetOptionValue().OptionWasSet())
|
|
{
|
|
search_using_module_spec = true;
|
|
module_spec.GetFileSpec() = m_file_option.GetOptionValue().GetCurrentValue();
|
|
}
|
|
|
|
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;
|
|
const size_t num_matches = target->GetImages().FindModules (module_spec, matching_modules);
|
|
|
|
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 = objfile->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();
|
|
module->SetLoadAddress (*target, slide, changed);
|
|
}
|
|
else
|
|
{
|
|
result.AppendError ("one or more section name + load address pair must be specified");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_slide_option.GetOptionValue().OptionWasSet())
|
|
{
|
|
result.AppendError ("The \"--slide <offset>\" option can't be used in conjunction with setting section load addresses.\n");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
|
|
for (size_t i=0; i<argc; i += 2)
|
|
{
|
|
const char *sect_name = args.GetArgumentAtIndex(i);
|
|
const char *load_addr_cstr = args.GetArgumentAtIndex(i+1);
|
|
if (sect_name && load_addr_cstr)
|
|
{
|
|
ConstString const_sect_name(sect_name);
|
|
bool success = false;
|
|
addr_t load_addr = Args::StringToUInt64(load_addr_cstr, LLDB_INVALID_ADDRESS, 0, &success);
|
|
if (success)
|
|
{
|
|
SectionSP section_sp (section_list->FindSectionByName(const_sect_name));
|
|
if (section_sp)
|
|
{
|
|
if (section_sp->IsThreadSpecific())
|
|
{
|
|
result.AppendErrorWithFormat ("thread specific sections are not yet supported (section '%s')\n", sect_name);
|
|
result.SetStatus (eReturnStatusFailed);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (target->GetSectionLoadList().SetSectionLoadAddress (section_sp, load_addr))
|
|
changed = true;
|
|
result.AppendMessageWithFormat("section '%s' loaded at 0x%llx\n", sect_name, load_addr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.AppendErrorWithFormat ("no section found that matches the section name '%s'\n", sect_name);
|
|
result.SetStatus (eReturnStatusFailed);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.AppendErrorWithFormat ("invalid load address string '%s'\n", load_addr_cstr);
|
|
result.SetStatus (eReturnStatusFailed);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (sect_name)
|
|
result.AppendError ("section names must be followed by a load address.\n");
|
|
else
|
|
result.AppendError ("one or more section name + load address pair must be specified.\n");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (changed)
|
|
target->ModulesDidLoad (matching_modules);
|
|
}
|
|
else
|
|
{
|
|
module->GetFileSpec().GetPath (path, sizeof(path));
|
|
result.AppendErrorWithFormat ("no sections in object file '%s'\n", path);
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
module->GetFileSpec().GetPath (path, sizeof(path));
|
|
result.AppendErrorWithFormat ("no object file for module '%s'\n", path);
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
module->GetFileSpec().GetPath (path, sizeof(path));
|
|
result.AppendErrorWithFormat ("invalid module '%s'.\n", path);
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char uuid_cstr[64];
|
|
|
|
if (module_spec.GetFileSpec())
|
|
module_spec.GetFileSpec().GetPath (path, sizeof(path));
|
|
else
|
|
path[0] = '\0';
|
|
|
|
if (module_spec.GetUUIDPtr())
|
|
module_spec.GetUUID().GetAsCString(uuid_cstr, sizeof(uuid_cstr));
|
|
else
|
|
uuid_cstr[0] = '\0';
|
|
if (num_matches > 1)
|
|
{
|
|
result.AppendErrorWithFormat ("multiple modules match%s%s%s%s:\n",
|
|
path[0] ? " file=" : "",
|
|
path,
|
|
uuid_cstr[0] ? " uuid=" : "",
|
|
uuid_cstr);
|
|
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_cstr[0] ? " uuid=" : "",
|
|
uuid_cstr);
|
|
}
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.AppendError ("either the \"--file <module>\" or the \"--uuid <uuid>\" option must be specified.\n");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
}
|
|
return result.Succeeded();
|
|
}
|
|
|
|
OptionGroupOptions m_option_group;
|
|
OptionGroupUUID m_uuid_option_group;
|
|
OptionGroupFile m_file_option;
|
|
OptionGroupUInt64 m_slide_option;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// List images with associated information
|
|
//----------------------------------------------------------------------
|
|
class CommandObjectTargetModulesList : public CommandObjectParsed
|
|
{
|
|
public:
|
|
|
|
class CommandOptions : public Options
|
|
{
|
|
public:
|
|
|
|
CommandOptions (CommandInterpreter &interpreter) :
|
|
Options(interpreter),
|
|
m_format_array(),
|
|
m_use_global_module_list (false),
|
|
m_module_addr (LLDB_INVALID_ADDRESS)
|
|
{
|
|
}
|
|
|
|
virtual
|
|
~CommandOptions ()
|
|
{
|
|
}
|
|
|
|
virtual Error
|
|
SetOptionValue (uint32_t option_idx, const char *option_arg)
|
|
{
|
|
char short_option = (char) m_getopt_table[option_idx].val;
|
|
if (short_option == 'g')
|
|
{
|
|
m_use_global_module_list = true;
|
|
}
|
|
else if (short_option == 'a')
|
|
{
|
|
bool success;
|
|
m_module_addr = Args::StringToAddress(option_arg, LLDB_INVALID_ADDRESS, &success);
|
|
if (!success)
|
|
{
|
|
Error error;
|
|
error.SetErrorStringWithFormat("invalid address: \"%s\"", option_arg);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uint32_t width = 0;
|
|
if (option_arg)
|
|
width = strtoul (option_arg, NULL, 0);
|
|
m_format_array.push_back(std::make_pair(short_option, width));
|
|
}
|
|
Error error;
|
|
return error;
|
|
}
|
|
|
|
void
|
|
OptionParsingStarting ()
|
|
{
|
|
m_format_array.clear();
|
|
m_use_global_module_list = false;
|
|
m_module_addr = LLDB_INVALID_ADDRESS;
|
|
}
|
|
|
|
const OptionDefinition*
|
|
GetDefinitions ()
|
|
{
|
|
return g_option_table;
|
|
}
|
|
|
|
// Options table: Required for subclasses of Options.
|
|
|
|
static OptionDefinition g_option_table[];
|
|
|
|
// Instance variables to hold the values for command options.
|
|
typedef std::vector< std::pair<char, uint32_t> > FormatWidthCollection;
|
|
FormatWidthCollection m_format_array;
|
|
bool m_use_global_module_list;
|
|
lldb::addr_t m_module_addr;
|
|
};
|
|
|
|
CommandObjectTargetModulesList (CommandInterpreter &interpreter) :
|
|
CommandObjectParsed (interpreter,
|
|
"target modules list",
|
|
"List current executable and dependent shared library images.",
|
|
"target modules list [<cmd-options>]"),
|
|
m_options (interpreter)
|
|
{
|
|
}
|
|
|
|
virtual
|
|
~CommandObjectTargetModulesList ()
|
|
{
|
|
}
|
|
|
|
virtual
|
|
Options *
|
|
GetOptions ()
|
|
{
|
|
return &m_options;
|
|
}
|
|
|
|
protected:
|
|
virtual bool
|
|
DoExecute (Args& command,
|
|
CommandReturnObject &result)
|
|
{
|
|
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
|
|
const bool use_global_module_list = m_options.m_use_global_module_list;
|
|
// Define a local module list here to ensure it lives longer than any "locker"
|
|
// object which might lock its contents below (through the "module_list_ptr"
|
|
// variable).
|
|
ModuleList module_list;
|
|
if (target == NULL && use_global_module_list == false)
|
|
{
|
|
result.AppendError ("invalid target, create a debug target using the 'target create' command");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (target)
|
|
{
|
|
uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
|
|
result.GetOutputStream().SetAddressByteSize(addr_byte_size);
|
|
result.GetErrorStream().SetAddressByteSize(addr_byte_size);
|
|
}
|
|
// Dump all sections for all modules images
|
|
Stream &strm = result.GetOutputStream();
|
|
|
|
if (m_options.m_module_addr != LLDB_INVALID_ADDRESS)
|
|
{
|
|
if (target)
|
|
{
|
|
Address module_address;
|
|
if (module_address.SetLoadAddress(m_options.m_module_addr, target))
|
|
{
|
|
ModuleSP module_sp (module_address.GetModule());
|
|
if (module_sp)
|
|
{
|
|
PrintModule (target, module_sp.get(), UINT32_MAX, 0, strm);
|
|
result.SetStatus (eReturnStatusSuccessFinishResult);
|
|
}
|
|
else
|
|
{
|
|
result.AppendError ("Couldn't find module matching address: 0x%llx.", m_options.m_module_addr);
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.AppendError ("Couldn't find module containing address: 0x%llx.", m_options.m_module_addr);
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.AppendError ("Can only look up modules by address with a valid target.");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
return result.Succeeded();
|
|
}
|
|
|
|
uint32_t num_modules = 0;
|
|
Mutex::Locker locker; // This locker will be locked on the mutex in module_list_ptr if it is non-NULL.
|
|
// Otherwise it will lock the AllocationModuleCollectionMutex when accessing
|
|
// the global module list directly.
|
|
ModuleList *module_list_ptr = NULL;
|
|
const size_t argc = command.GetArgumentCount();
|
|
if (argc == 0)
|
|
{
|
|
if (use_global_module_list)
|
|
{
|
|
locker.Lock (Module::GetAllocationModuleCollectionMutex());
|
|
num_modules = Module::GetNumberAllocatedModules();
|
|
}
|
|
else
|
|
{
|
|
module_list_ptr = &target->GetImages();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (size_t i=0; i<argc; ++i)
|
|
{
|
|
// Dump specified images (by basename or fullpath)
|
|
const char *arg_cstr = command.GetArgumentAtIndex(i);
|
|
const size_t num_matches = FindModulesByName (target, arg_cstr, module_list, use_global_module_list);
|
|
if (num_matches == 0)
|
|
{
|
|
if (argc == 1)
|
|
{
|
|
result.AppendErrorWithFormat ("no modules found that match '%s'", arg_cstr);
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
module_list_ptr = &module_list;
|
|
}
|
|
|
|
if (module_list_ptr != NULL)
|
|
{
|
|
locker.Lock(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();
|
|
}
|
|
|
|
int indent = strm.Printf("[%3u] ", image_idx);
|
|
PrintModule (target, module, image_idx, indent, strm);
|
|
|
|
}
|
|
result.SetStatus (eReturnStatusSuccessFinishResult);
|
|
}
|
|
else
|
|
{
|
|
if (argc)
|
|
{
|
|
if (use_global_module_list)
|
|
result.AppendError ("the global module list has no matching modules");
|
|
else
|
|
result.AppendError ("the target has no matching modules");
|
|
}
|
|
else
|
|
{
|
|
if (use_global_module_list)
|
|
result.AppendError ("the global module list is empty");
|
|
else
|
|
result.AppendError ("the target has no associated executable images");
|
|
}
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
}
|
|
return result.Succeeded();
|
|
}
|
|
|
|
void
|
|
PrintModule (Target *target, Module *module, uint32_t idx, int indent, Stream &strm)
|
|
{
|
|
|
|
bool dump_object_name = false;
|
|
if (m_options.m_format_array.empty())
|
|
{
|
|
m_options.m_format_array.push_back(std::make_pair('u', 0));
|
|
m_options.m_format_array.push_back(std::make_pair('h', 0));
|
|
m_options.m_format_array.push_back(std::make_pair('f', 0));
|
|
m_options.m_format_array.push_back(std::make_pair('S', 0));
|
|
}
|
|
const size_t num_entries = m_options.m_format_array.size();
|
|
bool print_space = false;
|
|
for (size_t i=0; i<num_entries; ++i)
|
|
{
|
|
if (print_space)
|
|
strm.PutChar(' ');
|
|
print_space = true;
|
|
const char format_char = m_options.m_format_array[i].first;
|
|
uint32_t width = m_options.m_format_array[i].second;
|
|
switch (format_char)
|
|
{
|
|
case 'A':
|
|
DumpModuleArchitecture (strm, module, false, width);
|
|
break;
|
|
|
|
case 't':
|
|
DumpModuleArchitecture (strm, module, true, width);
|
|
break;
|
|
|
|
case 'f':
|
|
DumpFullpath (strm, &module->GetFileSpec(), width);
|
|
dump_object_name = true;
|
|
break;
|
|
|
|
case 'd':
|
|
DumpDirectory (strm, &module->GetFileSpec(), width);
|
|
break;
|
|
|
|
case 'b':
|
|
DumpBasename (strm, &module->GetFileSpec(), width);
|
|
dump_object_name = true;
|
|
break;
|
|
|
|
case 'h':
|
|
case 'o':
|
|
// Image header address
|
|
{
|
|
uint32_t addr_nibble_width = target ? (target->GetArchitecture().GetAddressByteSize() * 2) : 16;
|
|
|
|
ObjectFile *objfile = module->GetObjectFile ();
|
|
if (objfile)
|
|
{
|
|
Address header_addr(objfile->GetHeaderAddress());
|
|
if (header_addr.IsValid())
|
|
{
|
|
if (target && !target->GetSectionLoadList().IsEmpty())
|
|
{
|
|
lldb::addr_t header_load_addr = header_addr.GetLoadAddress (target);
|
|
if (header_load_addr == LLDB_INVALID_ADDRESS)
|
|
{
|
|
header_addr.Dump (&strm, target, Address::DumpStyleModuleWithFileAddress, Address::DumpStyleFileAddress);
|
|
}
|
|
else
|
|
{
|
|
if (format_char == 'o')
|
|
{
|
|
// Show the offset of slide for the image
|
|
strm.Printf ("0x%*.*llx", addr_nibble_width, addr_nibble_width, header_load_addr - header_addr.GetFileAddress());
|
|
}
|
|
else
|
|
{
|
|
// Show the load address of the image
|
|
strm.Printf ("0x%*.*llx", addr_nibble_width, addr_nibble_width, header_load_addr);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
// The address was valid, but the image isn't loaded, output the address in an appropriate format
|
|
header_addr.Dump (&strm, target, Address::DumpStyleFileAddress);
|
|
break;
|
|
}
|
|
}
|
|
strm.Printf ("%*s", addr_nibble_width + 2, "");
|
|
}
|
|
break;
|
|
case 'r':
|
|
{
|
|
uint32_t ref_count = 0;
|
|
ModuleSP module_sp (module->shared_from_this());
|
|
if (module_sp)
|
|
{
|
|
// Take one away to make sure we don't count our local "module_sp"
|
|
ref_count = module_sp.use_count() - 1;
|
|
}
|
|
if (width)
|
|
strm.Printf("{%*u}", width, ref_count);
|
|
else
|
|
strm.Printf("{%u}", ref_count);
|
|
}
|
|
break;
|
|
|
|
case 's':
|
|
case 'S':
|
|
{
|
|
SymbolVendor *symbol_vendor = module->GetSymbolVendor();
|
|
if (symbol_vendor)
|
|
{
|
|
SymbolFile *symbol_file = symbol_vendor->GetSymbolFile();
|
|
if (symbol_file)
|
|
{
|
|
if (format_char == 'S')
|
|
{
|
|
FileSpec &symfile_spec = symbol_file->GetObjectFile()->GetFileSpec();
|
|
// 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, &symbol_file->GetObjectFile()->GetFileSpec(), width);
|
|
dump_object_name = true;
|
|
break;
|
|
}
|
|
}
|
|
strm.Printf("%.*s", width, "<NONE>");
|
|
}
|
|
break;
|
|
|
|
case 'm':
|
|
module->GetModificationTime().Dump(&strm, width);
|
|
break;
|
|
|
|
case 'p':
|
|
strm.Printf("%p", 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;
|
|
};
|
|
|
|
OptionDefinition
|
|
CommandObjectTargetModulesList::CommandOptions::g_option_table[] =
|
|
{
|
|
{ LLDB_OPT_SET_1, false, "address", 'a', required_argument, NULL, 0, eArgTypeAddress, "Display the image at this address."},
|
|
{ LLDB_OPT_SET_1, false, "arch", 'A', optional_argument, NULL, 0, eArgTypeWidth, "Display the architecture when listing images."},
|
|
{ LLDB_OPT_SET_1, false, "triple", 't', optional_argument, NULL, 0, eArgTypeWidth, "Display the triple when listing images."},
|
|
{ LLDB_OPT_SET_1, false, "header", 'h', no_argument, NULL, 0, eArgTypeNone, "Display the image header address as a load address if debugging, a file address otherwise."},
|
|
{ LLDB_OPT_SET_1, false, "offset", 'o', no_argument, NULL, 0, eArgTypeNone, "Display the image header address offset from the header file address (the slide amount)."},
|
|
{ LLDB_OPT_SET_1, false, "uuid", 'u', no_argument, NULL, 0, eArgTypeNone, "Display the UUID when listing images."},
|
|
{ LLDB_OPT_SET_1, false, "fullpath", 'f', optional_argument, NULL, 0, eArgTypeWidth, "Display the fullpath to the image object file."},
|
|
{ LLDB_OPT_SET_1, false, "directory", 'd', optional_argument, NULL, 0, eArgTypeWidth, "Display the directory with optional width for the image object file."},
|
|
{ LLDB_OPT_SET_1, false, "basename", 'b', optional_argument, NULL, 0, eArgTypeWidth, "Display the basename with optional width for the image object file."},
|
|
{ LLDB_OPT_SET_1, false, "symfile", 's', optional_argument, NULL, 0, eArgTypeWidth, "Display the fullpath to the image symbol file with optional width."},
|
|
{ LLDB_OPT_SET_1, false, "symfile-unique", 'S', optional_argument, NULL, 0, eArgTypeWidth, "Display the symbol file with optional width only if it is different from the executable object file."},
|
|
{ LLDB_OPT_SET_1, false, "mod-time", 'm', optional_argument, NULL, 0, eArgTypeWidth, "Display the modification time with optional width of the module."},
|
|
{ LLDB_OPT_SET_1, false, "ref-count", 'r', optional_argument, NULL, 0, eArgTypeWidth, "Display the reference count if the module is still in the shared module cache."},
|
|
{ LLDB_OPT_SET_1, false, "pointer", 'p', optional_argument, NULL, 0, eArgTypeNone, "Display the module pointer."},
|
|
{ LLDB_OPT_SET_1, false, "global", 'g', no_argument, NULL, 0, eArgTypeNone, "Display the modules from the global module list, not just the current target."},
|
|
{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
|
|
};
|
|
|
|
#pragma mark CommandObjectTargetModulesShowUnwind
|
|
|
|
//----------------------------------------------------------------------
|
|
// Lookup unwind information in images
|
|
//----------------------------------------------------------------------
|
|
|
|
class CommandObjectTargetModulesShowUnwind : public CommandObjectParsed
|
|
{
|
|
public:
|
|
|
|
enum
|
|
{
|
|
eLookupTypeInvalid = -1,
|
|
eLookupTypeAddress = 0,
|
|
eLookupTypeSymbol,
|
|
eLookupTypeFunction,
|
|
eLookupTypeFunctionOrSymbol,
|
|
kNumLookupTypes
|
|
};
|
|
|
|
class CommandOptions : public Options
|
|
{
|
|
public:
|
|
|
|
CommandOptions (CommandInterpreter &interpreter) :
|
|
Options(interpreter),
|
|
m_type(eLookupTypeInvalid),
|
|
m_str(),
|
|
m_addr(LLDB_INVALID_ADDRESS)
|
|
{
|
|
}
|
|
|
|
virtual
|
|
~CommandOptions ()
|
|
{
|
|
}
|
|
|
|
virtual Error
|
|
SetOptionValue (uint32_t option_idx, const char *option_arg)
|
|
{
|
|
Error error;
|
|
|
|
char short_option = (char) m_getopt_table[option_idx].val;
|
|
|
|
switch (short_option)
|
|
{
|
|
case 'a':
|
|
m_type = eLookupTypeAddress;
|
|
m_addr = Args::StringToUInt64(option_arg, LLDB_INVALID_ADDRESS);
|
|
if (m_addr == LLDB_INVALID_ADDRESS)
|
|
error.SetErrorStringWithFormat ("invalid address string '%s'", option_arg);
|
|
break;
|
|
|
|
case 'n':
|
|
m_str = option_arg;
|
|
m_type = eLookupTypeFunctionOrSymbol;
|
|
break;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
void
|
|
OptionParsingStarting ()
|
|
{
|
|
m_type = eLookupTypeInvalid;
|
|
m_str.clear();
|
|
m_addr = LLDB_INVALID_ADDRESS;
|
|
}
|
|
|
|
const OptionDefinition*
|
|
GetDefinitions ()
|
|
{
|
|
return g_option_table;
|
|
}
|
|
|
|
// Options table: Required for subclasses of Options.
|
|
|
|
static OptionDefinition g_option_table[];
|
|
|
|
// Instance variables to hold the values for command options.
|
|
|
|
int m_type; // Should be a eLookupTypeXXX enum after parsing options
|
|
std::string m_str; // Holds name lookup
|
|
lldb::addr_t m_addr; // Holds the address to lookup
|
|
};
|
|
|
|
CommandObjectTargetModulesShowUnwind (CommandInterpreter &interpreter) :
|
|
CommandObjectParsed (interpreter,
|
|
"target modules show-unwind",
|
|
"Show synthesized unwind instructions for a function.",
|
|
NULL),
|
|
m_options (interpreter)
|
|
{
|
|
}
|
|
|
|
virtual
|
|
~CommandObjectTargetModulesShowUnwind ()
|
|
{
|
|
}
|
|
|
|
virtual
|
|
Options *
|
|
GetOptions ()
|
|
{
|
|
return &m_options;
|
|
}
|
|
|
|
protected:
|
|
bool
|
|
DoExecute (Args& command,
|
|
CommandReturnObject &result)
|
|
{
|
|
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
|
|
if (!target)
|
|
{
|
|
result.AppendError ("invalid target, create a debug target using the 'target create' command");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
|
|
ExecutionContext exe_ctx = m_interpreter.GetDebugger().GetSelectedExecutionContext();
|
|
Process *process = exe_ctx.GetProcessPtr();
|
|
ABI *abi = NULL;
|
|
if (process)
|
|
abi = process->GetABI().get();
|
|
|
|
if (process == NULL)
|
|
{
|
|
result.AppendError ("You must have a process running to use this command.");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
|
|
ThreadList threads(process->GetThreadList());
|
|
if (threads.GetSize() == 0)
|
|
{
|
|
result.AppendError ("The process must be paused to use this command.");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
|
|
ThreadSP thread(threads.GetThreadAtIndex(0));
|
|
if (thread.get() == NULL)
|
|
{
|
|
result.AppendError ("The process must be paused to use this command.");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
|
|
if (m_options.m_type == eLookupTypeFunctionOrSymbol)
|
|
{
|
|
SymbolContextList sc_list;
|
|
uint32_t num_matches;
|
|
ConstString function_name (m_options.m_str.c_str());
|
|
num_matches = target->GetImages().FindFunctions (function_name, eFunctionNameTypeAuto, true, false, true, sc_list);
|
|
for (uint32_t idx = 0; idx < num_matches; idx++)
|
|
{
|
|
SymbolContext sc;
|
|
sc_list.GetContextAtIndex(idx, sc);
|
|
if (sc.symbol == NULL && sc.function == NULL)
|
|
continue;
|
|
if (sc.module_sp.get() == NULL || sc.module_sp->GetObjectFile() == NULL)
|
|
continue;
|
|
AddressRange range;
|
|
if (!sc.GetAddressRange (eSymbolContextFunction | eSymbolContextSymbol, 0, false, range))
|
|
continue;
|
|
if (!range.GetBaseAddress().IsValid())
|
|
continue;
|
|
ConstString funcname(sc.GetFunctionName());
|
|
if (funcname.IsEmpty())
|
|
continue;
|
|
addr_t start_addr = range.GetBaseAddress().GetLoadAddress(target);
|
|
if (abi)
|
|
start_addr = abi->FixCodeAddress(start_addr);
|
|
|
|
FuncUnwindersSP func_unwinders_sp (sc.module_sp->GetObjectFile()->GetUnwindTable().GetUncachedFuncUnwindersContainingAddress(start_addr, sc));
|
|
if (func_unwinders_sp.get() == NULL)
|
|
continue;
|
|
|
|
Address first_non_prologue_insn (func_unwinders_sp->GetFirstNonPrologueInsn(*target));
|
|
if (first_non_prologue_insn.IsValid())
|
|
{
|
|
result.GetOutputStream().Printf("First non-prologue instruction is at address 0x%llx or offset %lld into the function.\n", first_non_prologue_insn.GetLoadAddress(target), first_non_prologue_insn.GetLoadAddress(target) - start_addr);
|
|
result.GetOutputStream().Printf ("\n");
|
|
}
|
|
|
|
UnwindPlanSP non_callsite_unwind_plan = func_unwinders_sp->GetUnwindPlanAtNonCallSite(*thread.get());
|
|
if (non_callsite_unwind_plan.get())
|
|
{
|
|
result.GetOutputStream().Printf("Asynchronous (not restricted to call-sites) UnwindPlan for %s`%s (start addr 0x%llx):\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr);
|
|
non_callsite_unwind_plan->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS);
|
|
result.GetOutputStream().Printf ("\n");
|
|
}
|
|
|
|
UnwindPlanSP callsite_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite(-1);
|
|
if (callsite_unwind_plan.get())
|
|
{
|
|
result.GetOutputStream().Printf("Synchronous (restricted to call-sites) UnwindPlan for %s`%s (start addr 0x%llx):\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr);
|
|
callsite_unwind_plan->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS);
|
|
result.GetOutputStream().Printf ("\n");
|
|
}
|
|
|
|
UnwindPlanSP arch_default_unwind_plan = func_unwinders_sp->GetUnwindPlanArchitectureDefault(*thread.get());
|
|
if (arch_default_unwind_plan.get())
|
|
{
|
|
result.GetOutputStream().Printf("Architecture default UnwindPlan for %s`%s (start addr 0x%llx):\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr);
|
|
arch_default_unwind_plan->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS);
|
|
result.GetOutputStream().Printf ("\n");
|
|
}
|
|
|
|
UnwindPlanSP fast_unwind_plan = func_unwinders_sp->GetUnwindPlanFastUnwind(*thread.get());
|
|
if (fast_unwind_plan.get())
|
|
{
|
|
result.GetOutputStream().Printf("Fast UnwindPlan for %s`%s (start addr 0x%llx):\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr);
|
|
fast_unwind_plan->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS);
|
|
result.GetOutputStream().Printf ("\n");
|
|
}
|
|
|
|
|
|
result.GetOutputStream().Printf ("\n");
|
|
}
|
|
}
|
|
return result.Succeeded();
|
|
}
|
|
|
|
CommandOptions m_options;
|
|
};
|
|
|
|
OptionDefinition
|
|
CommandObjectTargetModulesShowUnwind::CommandOptions::g_option_table[] =
|
|
{
|
|
{ LLDB_OPT_SET_1, true, "name", 'n', required_argument, NULL, 0, eArgTypeFunctionName, "Lookup a function or symbol by name in one or more target modules."},
|
|
{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Lookup information in images
|
|
//----------------------------------------------------------------------
|
|
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 (CommandInterpreter &interpreter) :
|
|
Options(interpreter)
|
|
{
|
|
OptionParsingStarting();
|
|
}
|
|
|
|
virtual
|
|
~CommandOptions ()
|
|
{
|
|
}
|
|
|
|
virtual Error
|
|
SetOptionValue (uint32_t option_idx, const char *option_arg)
|
|
{
|
|
Error error;
|
|
|
|
char short_option = (char) m_getopt_table[option_idx].val;
|
|
|
|
switch (short_option)
|
|
{
|
|
case 'a':
|
|
m_type = eLookupTypeAddress;
|
|
m_addr = Args::StringToUInt64(option_arg, LLDB_INVALID_ADDRESS);
|
|
if (m_addr == LLDB_INVALID_ADDRESS)
|
|
error.SetErrorStringWithFormat ("invalid address string '%s'", option_arg);
|
|
break;
|
|
|
|
case 'o':
|
|
m_offset = Args::StringToUInt64(option_arg, LLDB_INVALID_ADDRESS);
|
|
if (m_offset == LLDB_INVALID_ADDRESS)
|
|
error.SetErrorStringWithFormat ("invalid offset string '%s'", option_arg);
|
|
break;
|
|
|
|
case 's':
|
|
m_str = option_arg;
|
|
m_type = eLookupTypeSymbol;
|
|
break;
|
|
|
|
case 'f':
|
|
m_file.SetFile (option_arg, false);
|
|
m_type = eLookupTypeFileLine;
|
|
break;
|
|
|
|
case 'i':
|
|
m_include_inlines = false;
|
|
break;
|
|
|
|
case 'l':
|
|
m_line_number = Args::StringToUInt32(option_arg, UINT32_MAX);
|
|
if (m_line_number == UINT32_MAX)
|
|
error.SetErrorStringWithFormat ("invalid line number string '%s'", option_arg);
|
|
else if (m_line_number == 0)
|
|
error.SetErrorString ("zero is an invalid line number");
|
|
m_type = eLookupTypeFileLine;
|
|
break;
|
|
|
|
case 'F':
|
|
m_str = option_arg;
|
|
m_type = eLookupTypeFunction;
|
|
break;
|
|
|
|
case 'n':
|
|
m_str = option_arg;
|
|
m_type = eLookupTypeFunctionOrSymbol;
|
|
break;
|
|
|
|
case 't':
|
|
m_str = option_arg;
|
|
m_type = eLookupTypeType;
|
|
break;
|
|
|
|
case 'v':
|
|
m_verbose = 1;
|
|
break;
|
|
|
|
case 'A':
|
|
m_print_all = true;
|
|
break;
|
|
|
|
case 'r':
|
|
m_use_regex = true;
|
|
break;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
void
|
|
OptionParsingStarting ()
|
|
{
|
|
m_type = eLookupTypeInvalid;
|
|
m_str.clear();
|
|
m_file.Clear();
|
|
m_addr = LLDB_INVALID_ADDRESS;
|
|
m_offset = 0;
|
|
m_line_number = 0;
|
|
m_use_regex = false;
|
|
m_include_inlines = true;
|
|
m_verbose = false;
|
|
m_print_all = false;
|
|
}
|
|
|
|
const OptionDefinition*
|
|
GetDefinitions ()
|
|
{
|
|
return g_option_table;
|
|
}
|
|
|
|
// Options table: Required for subclasses of Options.
|
|
|
|
static OptionDefinition g_option_table[];
|
|
int m_type; // Should be a eLookupTypeXXX enum after parsing options
|
|
std::string m_str; // Holds name lookup
|
|
FileSpec m_file; // Files for file lookups
|
|
lldb::addr_t m_addr; // Holds the address to lookup
|
|
lldb::addr_t m_offset; // Subtract this offset from m_addr before doing lookups.
|
|
uint32_t m_line_number; // Line number for file+line lookups
|
|
bool m_use_regex; // Name lookups in m_str are regular expressions.
|
|
bool m_include_inlines;// Check for inline entries when looking up by file/line.
|
|
bool m_verbose; // Enable verbose lookup info
|
|
bool m_print_all; // Print all matches, even in cases where there's a best match.
|
|
|
|
};
|
|
|
|
CommandObjectTargetModulesLookup (CommandInterpreter &interpreter) :
|
|
CommandObjectParsed (interpreter,
|
|
"target modules lookup",
|
|
"Look up information within executable and dependent shared library images.",
|
|
NULL),
|
|
m_options (interpreter)
|
|
{
|
|
CommandArgumentEntry arg;
|
|
CommandArgumentData file_arg;
|
|
|
|
// Define the first (and only) variant of this arg.
|
|
file_arg.arg_type = eArgTypeFilename;
|
|
file_arg.arg_repetition = eArgRepeatStar;
|
|
|
|
// There is only one variant this argument could be; put it into the argument entry.
|
|
arg.push_back (file_arg);
|
|
|
|
// Push the data for the first argument into the m_arguments vector.
|
|
m_arguments.push_back (arg);
|
|
}
|
|
|
|
virtual
|
|
~CommandObjectTargetModulesLookup ()
|
|
{
|
|
}
|
|
|
|
virtual Options *
|
|
GetOptions ()
|
|
{
|
|
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;
|
|
}
|
|
|
|
ExecutionContext exe_ctx = interpreter.GetDebugger().GetSelectedExecutionContext();
|
|
|
|
StackFrameSP frame = exe_ctx.GetFrameSP();
|
|
|
|
if (!frame)
|
|
return false;
|
|
|
|
const SymbolContext &sym_ctx(frame->GetSymbolContext(eSymbolContextModule));
|
|
|
|
if (!sym_ctx.module_sp)
|
|
return false;
|
|
|
|
switch (m_options.m_type)
|
|
{
|
|
default:
|
|
return false;
|
|
case eLookupTypeType:
|
|
if (!m_options.m_str.empty())
|
|
{
|
|
if (LookupTypeHere (m_interpreter,
|
|
result.GetOutputStream(),
|
|
sym_ctx,
|
|
m_options.m_str.c_str(),
|
|
m_options.m_use_regex))
|
|
{
|
|
result.SetStatus(eReturnStatusSuccessFinishResult);
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
LookupInModule (CommandInterpreter &interpreter, Module *module, CommandReturnObject &result, bool &syntax_error)
|
|
{
|
|
switch (m_options.m_type)
|
|
{
|
|
case eLookupTypeAddress:
|
|
if (m_options.m_addr != LLDB_INVALID_ADDRESS)
|
|
{
|
|
if (LookupAddressInModule (m_interpreter,
|
|
result.GetOutputStream(),
|
|
module,
|
|
eSymbolContextEverything,
|
|
m_options.m_addr,
|
|
m_options.m_offset,
|
|
m_options.m_verbose))
|
|
{
|
|
result.SetStatus(eReturnStatusSuccessFinishResult);
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case eLookupTypeSymbol:
|
|
if (!m_options.m_str.empty())
|
|
{
|
|
if (LookupSymbolInModule (m_interpreter,
|
|
result.GetOutputStream(),
|
|
module,
|
|
m_options.m_str.c_str(),
|
|
m_options.m_use_regex,
|
|
m_options.m_verbose))
|
|
{
|
|
result.SetStatus(eReturnStatusSuccessFinishResult);
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case eLookupTypeFileLine:
|
|
if (m_options.m_file)
|
|
{
|
|
|
|
if (LookupFileAndLineInModule (m_interpreter,
|
|
result.GetOutputStream(),
|
|
module,
|
|
m_options.m_file,
|
|
m_options.m_line_number,
|
|
m_options.m_include_inlines,
|
|
m_options.m_verbose))
|
|
{
|
|
result.SetStatus(eReturnStatusSuccessFinishResult);
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case eLookupTypeFunctionOrSymbol:
|
|
case eLookupTypeFunction:
|
|
if (!m_options.m_str.empty())
|
|
{
|
|
if (LookupFunctionInModule (m_interpreter,
|
|
result.GetOutputStream(),
|
|
module,
|
|
m_options.m_str.c_str(),
|
|
m_options.m_use_regex,
|
|
m_options.m_include_inlines,
|
|
m_options.m_type == eLookupTypeFunctionOrSymbol, // include symbols
|
|
m_options.m_verbose))
|
|
{
|
|
result.SetStatus(eReturnStatusSuccessFinishResult);
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
case eLookupTypeType:
|
|
if (!m_options.m_str.empty())
|
|
{
|
|
if (LookupTypeInModule (m_interpreter,
|
|
result.GetOutputStream(),
|
|
module,
|
|
m_options.m_str.c_str(),
|
|
m_options.m_use_regex))
|
|
{
|
|
result.SetStatus(eReturnStatusSuccessFinishResult);
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
m_options.GenerateOptionUsage (result.GetErrorStream(), this);
|
|
syntax_error = true;
|
|
break;
|
|
}
|
|
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
|
|
protected:
|
|
virtual bool
|
|
DoExecute (Args& command,
|
|
CommandReturnObject &result)
|
|
{
|
|
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
|
|
if (target == NULL)
|
|
{
|
|
result.AppendError ("invalid target, create a debug target using the 'target create' command");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
bool syntax_error = false;
|
|
uint32_t i;
|
|
uint32_t num_successful_lookups = 0;
|
|
uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
|
|
result.GetOutputStream().SetAddressByteSize(addr_byte_size);
|
|
result.GetErrorStream().SetAddressByteSize(addr_byte_size);
|
|
// Dump all sections for all modules images
|
|
|
|
if (command.GetArgumentCount() == 0)
|
|
{
|
|
ModuleSP current_module;
|
|
|
|
// Where it is possible to look in the current symbol context
|
|
// first, try that. If this search was successful and --all
|
|
// was not passed, don't print anything else.
|
|
if (LookupHere (m_interpreter, result, syntax_error))
|
|
{
|
|
result.GetOutputStream().EOL();
|
|
num_successful_lookups++;
|
|
if (!m_options.m_print_all)
|
|
{
|
|
result.SetStatus (eReturnStatusSuccessFinishResult);
|
|
return result.Succeeded();
|
|
}
|
|
}
|
|
|
|
// Dump all sections for all other modules
|
|
|
|
ModuleList &target_modules = target->GetImages();
|
|
Mutex::Locker modules_locker(target_modules.GetMutex());
|
|
const uint32_t num_modules = target_modules.GetSize();
|
|
if (num_modules > 0)
|
|
{
|
|
for (i = 0; i<num_modules && syntax_error == false; ++i)
|
|
{
|
|
Module *module_pointer = target_modules.GetModulePointerAtIndexUnlocked(i);
|
|
|
|
if (module_pointer != current_module.get() &&
|
|
LookupInModule (m_interpreter, target_modules.GetModulePointerAtIndexUnlocked(i), result, syntax_error))
|
|
{
|
|
result.GetOutputStream().EOL();
|
|
num_successful_lookups++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.AppendError ("the target has no associated executable images");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Dump specified images (by basename or fullpath)
|
|
const char *arg_cstr;
|
|
for (i = 0; (arg_cstr = command.GetArgumentAtIndex(i)) != NULL && syntax_error == false; ++i)
|
|
{
|
|
ModuleList module_list;
|
|
const size_t num_matches = FindModulesByName (target, arg_cstr, module_list, false);
|
|
if (num_matches > 0)
|
|
{
|
|
for (size_t i=0; i<num_matches; ++i)
|
|
{
|
|
Module *module = module_list.GetModulePointerAtIndex(i);
|
|
if (module)
|
|
{
|
|
if (LookupInModule (m_interpreter, module, result, syntax_error))
|
|
{
|
|
result.GetOutputStream().EOL();
|
|
num_successful_lookups++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
result.AppendWarningWithFormat("Unable to find an image that matches '%s'.\n", arg_cstr);
|
|
}
|
|
}
|
|
|
|
if (num_successful_lookups > 0)
|
|
result.SetStatus (eReturnStatusSuccessFinishResult);
|
|
else
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
return result.Succeeded();
|
|
}
|
|
|
|
CommandOptions m_options;
|
|
};
|
|
|
|
OptionDefinition
|
|
CommandObjectTargetModulesLookup::CommandOptions::g_option_table[] =
|
|
{
|
|
{ LLDB_OPT_SET_1, true, "address", 'a', required_argument, NULL, 0, eArgTypeAddress, "Lookup an address in one or more target modules."},
|
|
{ LLDB_OPT_SET_1, false, "offset", 'o', required_argument, NULL, 0, eArgTypeOffset, "When looking up an address subtract <offset> from any addresses before doing the lookup."},
|
|
{ LLDB_OPT_SET_2| LLDB_OPT_SET_4 | LLDB_OPT_SET_5
|
|
/* FIXME: re-enable this for types when the LookupTypeInModule actually uses the regex option: | LLDB_OPT_SET_6 */ ,
|
|
false, "regex", 'r', no_argument, NULL, 0, eArgTypeNone, "The <name> argument for name lookups are regular expressions."},
|
|
{ LLDB_OPT_SET_2, true, "symbol", 's', required_argument, NULL, 0, eArgTypeSymbol, "Lookup a symbol by name in the symbol tables in one or more target modules."},
|
|
{ LLDB_OPT_SET_3, true, "file", 'f', required_argument, NULL, 0, eArgTypeFilename, "Lookup a file by fullpath or basename in one or more target modules."},
|
|
{ LLDB_OPT_SET_3, false, "line", 'l', required_argument, NULL, 0, eArgTypeLineNum, "Lookup a line number in a file (must be used in conjunction with --file)."},
|
|
{ LLDB_OPT_SET_FROM_TO(3,5),
|
|
false, "no-inlines", 'i', no_argument, NULL, 0, eArgTypeNone, "Ignore inline entries (must be used in conjunction with --file or --function)."},
|
|
{ LLDB_OPT_SET_4, true, "function", 'F', required_argument, NULL, 0, eArgTypeFunctionName, "Lookup a function by name in the debug symbols in one or more target modules."},
|
|
{ LLDB_OPT_SET_5, true, "name", 'n', required_argument, NULL, 0, eArgTypeFunctionName, "Lookup a function or symbol by name in one or more target modules."},
|
|
{ LLDB_OPT_SET_6, true, "type", 't', required_argument, NULL, 0, eArgTypeName, "Lookup a type by name in the debug symbols in one or more target modules."},
|
|
{ LLDB_OPT_SET_ALL, false, "verbose", 'v', no_argument, NULL, 0, eArgTypeNone, "Enable verbose lookup information."},
|
|
{ LLDB_OPT_SET_ALL, false, "all", 'A', no_argument, NULL, 0, eArgTypeNone, "Print all matches, not just the best match, if a best match is available."},
|
|
{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
|
|
};
|
|
|
|
|
|
#pragma mark CommandObjectMultiwordImageSearchPaths
|
|
|
|
//-------------------------------------------------------------------------
|
|
// CommandObjectMultiwordImageSearchPaths
|
|
//-------------------------------------------------------------------------
|
|
|
|
class CommandObjectTargetModulesImageSearchPaths : public CommandObjectMultiword
|
|
{
|
|
public:
|
|
|
|
CommandObjectTargetModulesImageSearchPaths (CommandInterpreter &interpreter) :
|
|
CommandObjectMultiword (interpreter,
|
|
"target modules search-paths",
|
|
"A set of commands for operating on debugger target image search paths.",
|
|
"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()
|
|
{
|
|
}
|
|
};
|
|
|
|
|
|
|
|
#pragma mark CommandObjectTargetModules
|
|
|
|
//-------------------------------------------------------------------------
|
|
// CommandObjectTargetModules
|
|
//-------------------------------------------------------------------------
|
|
|
|
class CommandObjectTargetModules : public CommandObjectMultiword
|
|
{
|
|
public:
|
|
//------------------------------------------------------------------
|
|
// Constructors and Destructors
|
|
//------------------------------------------------------------------
|
|
CommandObjectTargetModules(CommandInterpreter &interpreter) :
|
|
CommandObjectMultiword (interpreter,
|
|
"target modules",
|
|
"A set of 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 ("unload", CommandObjectSP (new CommandObjectTargetModulesUnload (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)));
|
|
|
|
}
|
|
virtual
|
|
~CommandObjectTargetModules()
|
|
{
|
|
}
|
|
|
|
private:
|
|
//------------------------------------------------------------------
|
|
// For CommandObjectTargetModules only
|
|
//------------------------------------------------------------------
|
|
DISALLOW_COPY_AND_ASSIGN (CommandObjectTargetModules);
|
|
};
|
|
|
|
|
|
|
|
class CommandObjectTargetSymbolsAdd : public CommandObjectParsed
|
|
{
|
|
public:
|
|
CommandObjectTargetSymbolsAdd (CommandInterpreter &interpreter) :
|
|
CommandObjectParsed (interpreter,
|
|
"target symbols add",
|
|
"Add a debug symbol file to one of the target's current modules.",
|
|
"target symbols add [<symfile>]")
|
|
{
|
|
}
|
|
|
|
virtual
|
|
~CommandObjectTargetSymbolsAdd ()
|
|
{
|
|
}
|
|
|
|
int
|
|
HandleArgumentCompletion (Args &input,
|
|
int &cursor_index,
|
|
int &cursor_char_position,
|
|
OptionElementVector &opt_element_vector,
|
|
int match_start_point,
|
|
int max_return_elements,
|
|
bool &word_complete,
|
|
StringList &matches)
|
|
{
|
|
std::string completion_str (input.GetArgumentAtIndex(cursor_index));
|
|
completion_str.erase (cursor_char_position);
|
|
|
|
CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter,
|
|
CommandCompletions::eDiskFileCompletion,
|
|
completion_str.c_str(),
|
|
match_start_point,
|
|
max_return_elements,
|
|
NULL,
|
|
word_complete,
|
|
matches);
|
|
return matches.GetSize();
|
|
}
|
|
|
|
protected:
|
|
virtual bool
|
|
DoExecute (Args& args,
|
|
CommandReturnObject &result)
|
|
{
|
|
ExecutionContext exe_ctx (m_interpreter.GetExecutionContext());
|
|
Target *target = exe_ctx.GetTargetPtr();
|
|
if (target == NULL)
|
|
{
|
|
result.AppendError ("invalid target, create a debug target using the 'target create' command");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
else
|
|
{
|
|
bool flush = false;
|
|
const size_t argc = args.GetArgumentCount();
|
|
if (argc == 0)
|
|
{
|
|
result.AppendError ("one or more symbol file paths must be specified");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
else
|
|
{
|
|
for (size_t i=0; i<argc; ++i)
|
|
{
|
|
const char *symfile_path = args.GetArgumentAtIndex(i);
|
|
if (symfile_path)
|
|
{
|
|
FileSpec symfile_spec(symfile_path, true);
|
|
ArchSpec arch;
|
|
if (symfile_spec.Exists())
|
|
{
|
|
ModuleSP symfile_module_sp (new Module (symfile_spec, target->GetArchitecture()));
|
|
const UUID &symfile_uuid = symfile_module_sp->GetUUID();
|
|
StreamString ss_symfile_uuid;
|
|
symfile_uuid.Dump(&ss_symfile_uuid);
|
|
|
|
if (symfile_module_sp)
|
|
{
|
|
// We now have a module that represents a symbol file
|
|
// that can be used for a module that might exist in the
|
|
// current target, so we need to find that module in the
|
|
// target
|
|
|
|
ModuleSP old_module_sp (target->GetImages().FindModule (symfile_uuid));
|
|
if (old_module_sp)
|
|
{
|
|
// 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!
|
|
old_module_sp->SetSymbolFileFileSpec (symfile_module_sp->GetFileSpec());
|
|
|
|
// Provide feedback that the symfile has been successfully added.
|
|
const FileSpec &module_fs = old_module_sp->GetFileSpec();
|
|
result.AppendMessageWithFormat("symbol file '%s' with UUID %s has been successfully added to the '%s/%s' module\n",
|
|
symfile_path, ss_symfile_uuid.GetData(),
|
|
module_fs.GetDirectory().AsCString(), module_fs.GetFilename().AsCString());
|
|
|
|
// Let clients know something changed in the module
|
|
// if it is currently loaded
|
|
ModuleList module_list;
|
|
module_list.Append (old_module_sp);
|
|
target->ModulesDidLoad (module_list);
|
|
flush = true;
|
|
}
|
|
else
|
|
{
|
|
result.AppendErrorWithFormat ("symbol file '%s' with UUID %s does not match any existing module%s\n",
|
|
symfile_path, ss_symfile_uuid.GetData(),
|
|
(symfile_spec.GetFileType() != FileSpec::eFileTypeRegular)
|
|
? "\n please specify the full path to the symbol file"
|
|
: "");
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.AppendError ("one or more executable image paths must be specified");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
break;
|
|
}
|
|
result.SetStatus (eReturnStatusSuccessFinishResult);
|
|
}
|
|
else
|
|
{
|
|
char resolved_symfile_path[PATH_MAX];
|
|
result.SetStatus (eReturnStatusFailed);
|
|
if (symfile_spec.GetPath (resolved_symfile_path, sizeof(resolved_symfile_path)))
|
|
{
|
|
if (strcmp (resolved_symfile_path, symfile_path) != 0)
|
|
{
|
|
result.AppendErrorWithFormat ("invalid module path '%s' with resolved path '%s'\n", symfile_path, resolved_symfile_path);
|
|
break;
|
|
}
|
|
}
|
|
result.AppendErrorWithFormat ("invalid module path '%s'\n", symfile_path);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (flush)
|
|
{
|
|
Process *process = exe_ctx.GetProcessPtr();
|
|
if (process)
|
|
process->Flush();
|
|
}
|
|
}
|
|
return result.Succeeded();
|
|
}
|
|
|
|
};
|
|
|
|
|
|
#pragma mark CommandObjectTargetSymbols
|
|
|
|
//-------------------------------------------------------------------------
|
|
// CommandObjectTargetSymbols
|
|
//-------------------------------------------------------------------------
|
|
|
|
class CommandObjectTargetSymbols : public CommandObjectMultiword
|
|
{
|
|
public:
|
|
//------------------------------------------------------------------
|
|
// Constructors and Destructors
|
|
//------------------------------------------------------------------
|
|
CommandObjectTargetSymbols(CommandInterpreter &interpreter) :
|
|
CommandObjectMultiword (interpreter,
|
|
"target symbols",
|
|
"A set of commands for adding and managing debug symbol files.",
|
|
"target symbols <sub-command> ...")
|
|
{
|
|
LoadSubCommand ("add", CommandObjectSP (new CommandObjectTargetSymbolsAdd (interpreter)));
|
|
|
|
}
|
|
virtual
|
|
~CommandObjectTargetSymbols()
|
|
{
|
|
}
|
|
|
|
private:
|
|
//------------------------------------------------------------------
|
|
// For CommandObjectTargetModules only
|
|
//------------------------------------------------------------------
|
|
DISALLOW_COPY_AND_ASSIGN (CommandObjectTargetSymbols);
|
|
};
|
|
|
|
|
|
#pragma mark CommandObjectTargetStopHookAdd
|
|
|
|
//-------------------------------------------------------------------------
|
|
// CommandObjectTargetStopHookAdd
|
|
//-------------------------------------------------------------------------
|
|
|
|
class CommandObjectTargetStopHookAdd : public CommandObjectParsed
|
|
{
|
|
public:
|
|
|
|
class CommandOptions : public Options
|
|
{
|
|
public:
|
|
CommandOptions (CommandInterpreter &interpreter) :
|
|
Options(interpreter),
|
|
m_line_start(0),
|
|
m_line_end (UINT_MAX),
|
|
m_func_name_type_mask (eFunctionNameTypeAuto),
|
|
m_sym_ctx_specified (false),
|
|
m_thread_specified (false),
|
|
m_use_one_liner (false),
|
|
m_one_liner()
|
|
{
|
|
}
|
|
|
|
~CommandOptions () {}
|
|
|
|
const OptionDefinition*
|
|
GetDefinitions ()
|
|
{
|
|
return g_option_table;
|
|
}
|
|
|
|
virtual Error
|
|
SetOptionValue (uint32_t option_idx, const char *option_arg)
|
|
{
|
|
Error error;
|
|
char short_option = (char) m_getopt_table[option_idx].val;
|
|
bool success;
|
|
|
|
switch (short_option)
|
|
{
|
|
case 'c':
|
|
m_class_name = option_arg;
|
|
m_sym_ctx_specified = true;
|
|
break;
|
|
|
|
case 'e':
|
|
m_line_end = Args::StringToUInt32 (option_arg, UINT_MAX, 0, &success);
|
|
if (!success)
|
|
{
|
|
error.SetErrorStringWithFormat ("invalid end line number: \"%s\"", option_arg);
|
|
break;
|
|
}
|
|
m_sym_ctx_specified = true;
|
|
break;
|
|
|
|
case 'l':
|
|
m_line_start = Args::StringToUInt32 (option_arg, 0, 0, &success);
|
|
if (!success)
|
|
{
|
|
error.SetErrorStringWithFormat ("invalid start line number: \"%s\"", option_arg);
|
|
break;
|
|
}
|
|
m_sym_ctx_specified = true;
|
|
break;
|
|
|
|
case 'i':
|
|
m_no_inlines = true;
|
|
break;
|
|
|
|
case 'n':
|
|
m_function_name = option_arg;
|
|
m_func_name_type_mask |= eFunctionNameTypeAuto;
|
|
m_sym_ctx_specified = true;
|
|
break;
|
|
|
|
case 'f':
|
|
m_file_name = option_arg;
|
|
m_sym_ctx_specified = true;
|
|
break;
|
|
case 's':
|
|
m_module_name = option_arg;
|
|
m_sym_ctx_specified = true;
|
|
break;
|
|
case 't' :
|
|
{
|
|
m_thread_id = Args::StringToUInt64(option_arg, LLDB_INVALID_THREAD_ID, 0);
|
|
if (m_thread_id == LLDB_INVALID_THREAD_ID)
|
|
error.SetErrorStringWithFormat ("invalid thread id string '%s'", option_arg);
|
|
m_thread_specified = true;
|
|
}
|
|
break;
|
|
case 'T':
|
|
m_thread_name = option_arg;
|
|
m_thread_specified = true;
|
|
break;
|
|
case 'q':
|
|
m_queue_name = option_arg;
|
|
m_thread_specified = true;
|
|
break;
|
|
case 'x':
|
|
{
|
|
m_thread_index = Args::StringToUInt32(option_arg, UINT32_MAX, 0);
|
|
if (m_thread_id == UINT32_MAX)
|
|
error.SetErrorStringWithFormat ("invalid thread index string '%s'", option_arg);
|
|
m_thread_specified = true;
|
|
}
|
|
break;
|
|
case 'o':
|
|
m_use_one_liner = true;
|
|
m_one_liner = option_arg;
|
|
break;
|
|
default:
|
|
error.SetErrorStringWithFormat ("unrecognized option %c.", short_option);
|
|
break;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
void
|
|
OptionParsingStarting ()
|
|
{
|
|
m_class_name.clear();
|
|
m_function_name.clear();
|
|
m_line_start = 0;
|
|
m_line_end = UINT_MAX;
|
|
m_file_name.clear();
|
|
m_module_name.clear();
|
|
m_func_name_type_mask = eFunctionNameTypeAuto;
|
|
m_thread_id = LLDB_INVALID_THREAD_ID;
|
|
m_thread_index = UINT32_MAX;
|
|
m_thread_name.clear();
|
|
m_queue_name.clear();
|
|
|
|
m_no_inlines = false;
|
|
m_sym_ctx_specified = false;
|
|
m_thread_specified = false;
|
|
|
|
m_use_one_liner = false;
|
|
m_one_liner.clear();
|
|
}
|
|
|
|
|
|
static OptionDefinition g_option_table[];
|
|
|
|
std::string m_class_name;
|
|
std::string m_function_name;
|
|
uint32_t m_line_start;
|
|
uint32_t m_line_end;
|
|
std::string m_file_name;
|
|
std::string m_module_name;
|
|
uint32_t m_func_name_type_mask; // A pick from lldb::FunctionNameType.
|
|
lldb::tid_t m_thread_id;
|
|
uint32_t m_thread_index;
|
|
std::string m_thread_name;
|
|
std::string m_queue_name;
|
|
bool m_sym_ctx_specified;
|
|
bool m_no_inlines;
|
|
bool m_thread_specified;
|
|
// Instance variables to hold the values for one_liner options.
|
|
bool m_use_one_liner;
|
|
std::string m_one_liner;
|
|
};
|
|
|
|
Options *
|
|
GetOptions ()
|
|
{
|
|
return &m_options;
|
|
}
|
|
|
|
CommandObjectTargetStopHookAdd (CommandInterpreter &interpreter) :
|
|
CommandObjectParsed (interpreter,
|
|
"target stop-hook add ",
|
|
"Add a hook to be executed when the target stops.",
|
|
"target stop-hook add"),
|
|
m_options (interpreter)
|
|
{
|
|
}
|
|
|
|
~CommandObjectTargetStopHookAdd ()
|
|
{
|
|
}
|
|
|
|
static size_t
|
|
ReadCommandsCallbackFunction (void *baton,
|
|
InputReader &reader,
|
|
lldb::InputReaderAction notification,
|
|
const char *bytes,
|
|
size_t bytes_len)
|
|
{
|
|
StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream();
|
|
Target::StopHook *new_stop_hook = ((Target::StopHook *) baton);
|
|
static bool got_interrupted;
|
|
bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode();
|
|
|
|
switch (notification)
|
|
{
|
|
case eInputReaderActivate:
|
|
if (!batch_mode)
|
|
{
|
|
out_stream->Printf ("%s\n", "Enter your stop hook command(s). Type 'DONE' to end.");
|
|
if (reader.GetPrompt())
|
|
out_stream->Printf ("%s", reader.GetPrompt());
|
|
out_stream->Flush();
|
|
}
|
|
got_interrupted = false;
|
|
break;
|
|
|
|
case eInputReaderDeactivate:
|
|
break;
|
|
|
|
case eInputReaderReactivate:
|
|
if (reader.GetPrompt() && !batch_mode)
|
|
{
|
|
out_stream->Printf ("%s", reader.GetPrompt());
|
|
out_stream->Flush();
|
|
}
|
|
got_interrupted = false;
|
|
break;
|
|
|
|
case eInputReaderAsynchronousOutputWritten:
|
|
break;
|
|
|
|
case eInputReaderGotToken:
|
|
if (bytes && bytes_len && baton)
|
|
{
|
|
StringList *commands = new_stop_hook->GetCommandPointer();
|
|
if (commands)
|
|
{
|
|
commands->AppendString (bytes, bytes_len);
|
|
}
|
|
}
|
|
if (!reader.IsDone() && reader.GetPrompt() && !batch_mode)
|
|
{
|
|
out_stream->Printf ("%s", reader.GetPrompt());
|
|
out_stream->Flush();
|
|
}
|
|
break;
|
|
|
|
case eInputReaderInterrupt:
|
|
{
|
|
// Finish, and cancel the stop hook.
|
|
new_stop_hook->GetTarget()->RemoveStopHookByID(new_stop_hook->GetID());
|
|
if (!batch_mode)
|
|
{
|
|
out_stream->Printf ("Stop hook cancelled.\n");
|
|
out_stream->Flush();
|
|
}
|
|
|
|
reader.SetIsDone (true);
|
|
}
|
|
got_interrupted = true;
|
|
break;
|
|
|
|
case eInputReaderEndOfFile:
|
|
reader.SetIsDone (true);
|
|
break;
|
|
|
|
case eInputReaderDone:
|
|
if (!got_interrupted && !batch_mode)
|
|
{
|
|
out_stream->Printf ("Stop hook #%llu added.\n", new_stop_hook->GetID());
|
|
out_stream->Flush();
|
|
}
|
|
break;
|
|
}
|
|
|
|
return bytes_len;
|
|
}
|
|
|
|
protected:
|
|
bool
|
|
DoExecute (Args& command, CommandReturnObject &result)
|
|
{
|
|
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
|
|
if (target)
|
|
{
|
|
Target::StopHookSP new_hook_sp;
|
|
target->AddStopHook (new_hook_sp);
|
|
|
|
// First step, make the specifier.
|
|
std::auto_ptr<SymbolContextSpecifier> specifier_ap;
|
|
if (m_options.m_sym_ctx_specified)
|
|
{
|
|
specifier_ap.reset(new SymbolContextSpecifier(m_interpreter.GetDebugger().GetSelectedTarget()));
|
|
|
|
if (!m_options.m_module_name.empty())
|
|
{
|
|
specifier_ap->AddSpecification (m_options.m_module_name.c_str(), SymbolContextSpecifier::eModuleSpecified);
|
|
}
|
|
|
|
if (!m_options.m_class_name.empty())
|
|
{
|
|
specifier_ap->AddSpecification (m_options.m_class_name.c_str(), SymbolContextSpecifier::eClassOrNamespaceSpecified);
|
|
}
|
|
|
|
if (!m_options.m_file_name.empty())
|
|
{
|
|
specifier_ap->AddSpecification (m_options.m_file_name.c_str(), SymbolContextSpecifier::eFileSpecified);
|
|
}
|
|
|
|
if (m_options.m_line_start != 0)
|
|
{
|
|
specifier_ap->AddLineSpecification (m_options.m_line_start, SymbolContextSpecifier::eLineStartSpecified);
|
|
}
|
|
|
|
if (m_options.m_line_end != UINT_MAX)
|
|
{
|
|
specifier_ap->AddLineSpecification (m_options.m_line_end, SymbolContextSpecifier::eLineEndSpecified);
|
|
}
|
|
|
|
if (!m_options.m_function_name.empty())
|
|
{
|
|
specifier_ap->AddSpecification (m_options.m_function_name.c_str(), SymbolContextSpecifier::eFunctionSpecified);
|
|
}
|
|
}
|
|
|
|
if (specifier_ap.get())
|
|
new_hook_sp->SetSpecifier (specifier_ap.release());
|
|
|
|
// Next see if any of the thread options have been entered:
|
|
|
|
if (m_options.m_thread_specified)
|
|
{
|
|
ThreadSpec *thread_spec = new ThreadSpec();
|
|
|
|
if (m_options.m_thread_id != LLDB_INVALID_THREAD_ID)
|
|
{
|
|
thread_spec->SetTID (m_options.m_thread_id);
|
|
}
|
|
|
|
if (m_options.m_thread_index != UINT32_MAX)
|
|
thread_spec->SetIndex (m_options.m_thread_index);
|
|
|
|
if (!m_options.m_thread_name.empty())
|
|
thread_spec->SetName (m_options.m_thread_name.c_str());
|
|
|
|
if (!m_options.m_queue_name.empty())
|
|
thread_spec->SetQueueName (m_options.m_queue_name.c_str());
|
|
|
|
new_hook_sp->SetThreadSpecifier (thread_spec);
|
|
|
|
}
|
|
if (m_options.m_use_one_liner)
|
|
{
|
|
// Use one-liner.
|
|
new_hook_sp->GetCommandPointer()->AppendString (m_options.m_one_liner.c_str());
|
|
result.AppendMessageWithFormat("Stop hook #%llu added.\n", new_hook_sp->GetID());
|
|
}
|
|
else
|
|
{
|
|
// Otherwise gather up the command list, we'll push an input reader and suck the data from that directly into
|
|
// the new stop hook's command string.
|
|
InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger()));
|
|
if (!reader_sp)
|
|
{
|
|
result.AppendError("out of memory\n");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
target->RemoveStopHookByID (new_hook_sp->GetID());
|
|
return false;
|
|
}
|
|
|
|
Error err (reader_sp->Initialize (CommandObjectTargetStopHookAdd::ReadCommandsCallbackFunction,
|
|
new_hook_sp.get(), // baton
|
|
eInputReaderGranularityLine, // token size, to pass to callback function
|
|
"DONE", // end token
|
|
"> ", // prompt
|
|
true)); // echo input
|
|
if (!err.Success())
|
|
{
|
|
result.AppendError (err.AsCString());
|
|
result.SetStatus (eReturnStatusFailed);
|
|
target->RemoveStopHookByID (new_hook_sp->GetID());
|
|
return false;
|
|
}
|
|
m_interpreter.GetDebugger().PushInputReader (reader_sp);
|
|
}
|
|
result.SetStatus (eReturnStatusSuccessFinishNoResult);
|
|
}
|
|
else
|
|
{
|
|
result.AppendError ("invalid target\n");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
|
|
return result.Succeeded();
|
|
}
|
|
private:
|
|
CommandOptions m_options;
|
|
};
|
|
|
|
OptionDefinition
|
|
CommandObjectTargetStopHookAdd::CommandOptions::g_option_table[] =
|
|
{
|
|
{ LLDB_OPT_SET_ALL, false, "one-liner", 'o', required_argument, NULL, NULL, eArgTypeOneLiner,
|
|
"Specify a one-line breakpoint command inline. Be sure to surround it with quotes." },
|
|
{ LLDB_OPT_SET_ALL, false, "shlib", 's', required_argument, NULL, CommandCompletions::eModuleCompletion, eArgTypeShlibName,
|
|
"Set the module within which the stop-hook is to be run."},
|
|
{ LLDB_OPT_SET_ALL, false, "thread-index", 'x', required_argument, NULL, NULL, eArgTypeThreadIndex,
|
|
"The stop hook is run only for the thread whose index matches this argument."},
|
|
{ LLDB_OPT_SET_ALL, false, "thread-id", 't', required_argument, NULL, NULL, eArgTypeThreadID,
|
|
"The stop hook is run only for the thread whose TID matches this argument."},
|
|
{ LLDB_OPT_SET_ALL, false, "thread-name", 'T', required_argument, NULL, NULL, eArgTypeThreadName,
|
|
"The stop hook is run only for the thread whose thread name matches this argument."},
|
|
{ LLDB_OPT_SET_ALL, false, "queue-name", 'q', required_argument, NULL, NULL, eArgTypeQueueName,
|
|
"The stop hook is run only for threads in the queue whose name is given by this argument."},
|
|
{ LLDB_OPT_SET_1, false, "file", 'f', required_argument, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename,
|
|
"Specify the source file within which the stop-hook is to be run." },
|
|
{ LLDB_OPT_SET_1, false, "start-line", 'l', required_argument, NULL, 0, eArgTypeLineNum,
|
|
"Set the start of the line range for which the stop-hook is to be run."},
|
|
{ LLDB_OPT_SET_1, false, "end-line", 'e', required_argument, NULL, 0, eArgTypeLineNum,
|
|
"Set the end of the line range for which the stop-hook is to be run."},
|
|
{ LLDB_OPT_SET_2, false, "classname", 'c', required_argument, NULL, NULL, eArgTypeClassName,
|
|
"Specify the class within which the stop-hook is to be run." },
|
|
{ LLDB_OPT_SET_3, false, "name", 'n', required_argument, NULL, CommandCompletions::eSymbolCompletion, eArgTypeFunctionName,
|
|
"Set the function name within which the stop hook will be run." },
|
|
{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
|
|
};
|
|
|
|
#pragma mark CommandObjectTargetStopHookDelete
|
|
|
|
//-------------------------------------------------------------------------
|
|
// CommandObjectTargetStopHookDelete
|
|
//-------------------------------------------------------------------------
|
|
|
|
class CommandObjectTargetStopHookDelete : public CommandObjectParsed
|
|
{
|
|
public:
|
|
|
|
CommandObjectTargetStopHookDelete (CommandInterpreter &interpreter) :
|
|
CommandObjectParsed (interpreter,
|
|
"target stop-hook delete",
|
|
"Delete a stop-hook.",
|
|
"target stop-hook delete [<idx>]")
|
|
{
|
|
}
|
|
|
|
~CommandObjectTargetStopHookDelete ()
|
|
{
|
|
}
|
|
|
|
protected:
|
|
bool
|
|
DoExecute (Args& command, CommandReturnObject &result)
|
|
{
|
|
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
|
|
if (target)
|
|
{
|
|
// FIXME: see if we can use the breakpoint id style parser?
|
|
size_t num_args = command.GetArgumentCount();
|
|
if (num_args == 0)
|
|
{
|
|
if (!m_interpreter.Confirm ("Delete all stop hooks?", true))
|
|
{
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
target->RemoveAllStopHooks();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bool success;
|
|
for (size_t i = 0; i < num_args; i++)
|
|
{
|
|
lldb::user_id_t user_id = Args::StringToUInt32 (command.GetArgumentAtIndex(i), 0, 0, &success);
|
|
if (!success)
|
|
{
|
|
result.AppendErrorWithFormat ("invalid stop hook id: \"%s\".\n", command.GetArgumentAtIndex(i));
|
|
result.SetStatus(eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
success = target->RemoveStopHookByID (user_id);
|
|
if (!success)
|
|
{
|
|
result.AppendErrorWithFormat ("unknown stop hook id: \"%s\".\n", command.GetArgumentAtIndex(i));
|
|
result.SetStatus(eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
result.SetStatus (eReturnStatusSuccessFinishNoResult);
|
|
}
|
|
else
|
|
{
|
|
result.AppendError ("invalid target\n");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
|
|
return result.Succeeded();
|
|
}
|
|
};
|
|
#pragma mark CommandObjectTargetStopHookEnableDisable
|
|
|
|
//-------------------------------------------------------------------------
|
|
// CommandObjectTargetStopHookEnableDisable
|
|
//-------------------------------------------------------------------------
|
|
|
|
class CommandObjectTargetStopHookEnableDisable : public CommandObjectParsed
|
|
{
|
|
public:
|
|
|
|
CommandObjectTargetStopHookEnableDisable (CommandInterpreter &interpreter, bool enable, const char *name, const char *help, const char *syntax) :
|
|
CommandObjectParsed (interpreter,
|
|
name,
|
|
help,
|
|
syntax),
|
|
m_enable (enable)
|
|
{
|
|
}
|
|
|
|
~CommandObjectTargetStopHookEnableDisable ()
|
|
{
|
|
}
|
|
|
|
protected:
|
|
bool
|
|
DoExecute (Args& command, CommandReturnObject &result)
|
|
{
|
|
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
|
|
if (target)
|
|
{
|
|
// 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 = Args::StringToUInt32 (command.GetArgumentAtIndex(i), 0, 0, &success);
|
|
if (!success)
|
|
{
|
|
result.AppendErrorWithFormat ("invalid stop hook id: \"%s\".\n", command.GetArgumentAtIndex(i));
|
|
result.SetStatus(eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
success = target->SetStopHookActiveStateByID (user_id, m_enable);
|
|
if (!success)
|
|
{
|
|
result.AppendErrorWithFormat ("unknown stop hook id: \"%s\".\n", command.GetArgumentAtIndex(i));
|
|
result.SetStatus(eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
result.SetStatus (eReturnStatusSuccessFinishNoResult);
|
|
}
|
|
else
|
|
{
|
|
result.AppendError ("invalid target\n");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
return result.Succeeded();
|
|
}
|
|
private:
|
|
bool m_enable;
|
|
};
|
|
|
|
#pragma mark CommandObjectTargetStopHookList
|
|
|
|
//-------------------------------------------------------------------------
|
|
// CommandObjectTargetStopHookList
|
|
//-------------------------------------------------------------------------
|
|
|
|
class CommandObjectTargetStopHookList : public CommandObjectParsed
|
|
{
|
|
public:
|
|
|
|
CommandObjectTargetStopHookList (CommandInterpreter &interpreter) :
|
|
CommandObjectParsed (interpreter,
|
|
"target stop-hook list",
|
|
"List all stop-hooks.",
|
|
"target stop-hook list [<type>]")
|
|
{
|
|
}
|
|
|
|
~CommandObjectTargetStopHookList ()
|
|
{
|
|
}
|
|
|
|
protected:
|
|
bool
|
|
DoExecute (Args& command, CommandReturnObject &result)
|
|
{
|
|
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
|
|
if (!target)
|
|
{
|
|
result.AppendError ("invalid target\n");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
return result.Succeeded();
|
|
}
|
|
|
|
size_t num_hooks = target->GetNumStopHooks ();
|
|
if (num_hooks == 0)
|
|
{
|
|
result.GetOutputStream().PutCString ("No stop hooks.\n");
|
|
}
|
|
else
|
|
{
|
|
for (size_t i = 0; i < num_hooks; i++)
|
|
{
|
|
Target::StopHookSP this_hook = target->GetStopHookAtIndex (i);
|
|
if (i > 0)
|
|
result.GetOutputStream().PutCString ("\n");
|
|
this_hook->GetDescription (&(result.GetOutputStream()), eDescriptionLevelFull);
|
|
}
|
|
}
|
|
result.SetStatus (eReturnStatusSuccessFinishResult);
|
|
return result.Succeeded();
|
|
}
|
|
};
|
|
|
|
#pragma mark CommandObjectMultiwordTargetStopHooks
|
|
//-------------------------------------------------------------------------
|
|
// CommandObjectMultiwordTargetStopHooks
|
|
//-------------------------------------------------------------------------
|
|
|
|
class CommandObjectMultiwordTargetStopHooks : public CommandObjectMultiword
|
|
{
|
|
public:
|
|
|
|
CommandObjectMultiwordTargetStopHooks (CommandInterpreter &interpreter) :
|
|
CommandObjectMultiword (interpreter,
|
|
"target stop-hook",
|
|
"A set of 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()
|
|
{
|
|
}
|
|
};
|
|
|
|
|
|
|
|
#pragma mark CommandObjectMultiwordTarget
|
|
|
|
//-------------------------------------------------------------------------
|
|
// CommandObjectMultiwordTarget
|
|
//-------------------------------------------------------------------------
|
|
|
|
CommandObjectMultiwordTarget::CommandObjectMultiwordTarget (CommandInterpreter &interpreter) :
|
|
CommandObjectMultiword (interpreter,
|
|
"target",
|
|
"A set of commands for operating on debugger targets.",
|
|
"target <subcommand> [<subcommand-options>]")
|
|
{
|
|
|
|
LoadSubCommand ("create", CommandObjectSP (new CommandObjectTargetCreate (interpreter)));
|
|
LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTargetDelete (interpreter)));
|
|
LoadSubCommand ("list", CommandObjectSP (new CommandObjectTargetList (interpreter)));
|
|
LoadSubCommand ("select", CommandObjectSP (new CommandObjectTargetSelect (interpreter)));
|
|
LoadSubCommand ("stop-hook", CommandObjectSP (new CommandObjectMultiwordTargetStopHooks (interpreter)));
|
|
LoadSubCommand ("modules", CommandObjectSP (new CommandObjectTargetModules (interpreter)));
|
|
LoadSubCommand ("symbols", CommandObjectSP (new CommandObjectTargetSymbols (interpreter)));
|
|
LoadSubCommand ("variable", CommandObjectSP (new CommandObjectTargetVariable (interpreter)));
|
|
}
|
|
|
|
CommandObjectMultiwordTarget::~CommandObjectMultiwordTarget ()
|
|
{
|
|
}
|
|
|
|
|