forked from OSchip/llvm-project
Added a 'jump' command, similar to GDBs.
This allows the PC to be directly changed to a different line. It's similar to the example python script in examples/python/jump.py, except implemented as a builtin. Also this version will track the current function correctly even if the target line resolves to multiple addresses. (e.g. debugging a templated function) llvm-svn: 190572
This commit is contained in:
parent
50c003b577
commit
f86248d9ba
|
@ -117,6 +117,9 @@ public:
|
||||||
lldb::SBFileSpec &file_spec,
|
lldb::SBFileSpec &file_spec,
|
||||||
uint32_t line);
|
uint32_t line);
|
||||||
|
|
||||||
|
SBError
|
||||||
|
JumpToLine (lldb::SBFileSpec &file_spec, uint32_t line);
|
||||||
|
|
||||||
void
|
void
|
||||||
RunToAddress (lldb::addr_t addr);
|
RunToAddress (lldb::addr_t addr);
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ public:
|
||||||
Error (ValueType err, lldb::ErrorType type = lldb::eErrorTypeGeneric);
|
Error (ValueType err, lldb::ErrorType type = lldb::eErrorTypeGeneric);
|
||||||
|
|
||||||
explicit
|
explicit
|
||||||
Error (const char* err_str);
|
Error (const char *format, ...) __attribute__ ((format (printf, 2, 3)));
|
||||||
|
|
||||||
Error (const Error &rhs);
|
Error (const Error &rhs);
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
|
|
|
@ -346,6 +346,32 @@ public:
|
||||||
bool append,
|
bool append,
|
||||||
SymbolContextList& sc_list);
|
SymbolContextList& sc_list);
|
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
/// Find addresses by file/line
|
||||||
|
///
|
||||||
|
/// @param[in] target_sp
|
||||||
|
/// The target the addresses are desired for.
|
||||||
|
///
|
||||||
|
/// @param[in] file
|
||||||
|
/// Source file to locate.
|
||||||
|
///
|
||||||
|
/// @param[in] line
|
||||||
|
/// Source line to locate.
|
||||||
|
///
|
||||||
|
/// @param[in] function
|
||||||
|
/// Optional filter function. Addresses within this function will be
|
||||||
|
/// added to the 'local' list. All others will be added to the 'extern' list.
|
||||||
|
///
|
||||||
|
/// @param[out] output_local
|
||||||
|
/// All matching addresses within 'function'
|
||||||
|
///
|
||||||
|
/// @param[out] output_extern
|
||||||
|
/// All matching addresses not within 'function'
|
||||||
|
void FindAddressesForLine (const lldb::TargetSP target_sp,
|
||||||
|
const FileSpec &file, uint32_t line,
|
||||||
|
Function *function,
|
||||||
|
std::vector<Address> &output_local, std::vector<Address> &output_extern);
|
||||||
|
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
/// Find global and static variables by name.
|
/// Find global and static variables by name.
|
||||||
///
|
///
|
||||||
|
|
|
@ -439,7 +439,35 @@ public:
|
||||||
|
|
||||||
bool
|
bool
|
||||||
FindSourceFile (const FileSpec &orig_spec, FileSpec &new_spec) const;
|
FindSourceFile (const FileSpec &orig_spec, FileSpec &new_spec) const;
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
/// Find addresses by file/line
|
||||||
|
///
|
||||||
|
/// @param[in] target_sp
|
||||||
|
/// The target the addresses are desired for.
|
||||||
|
///
|
||||||
|
/// @param[in] file
|
||||||
|
/// Source file to locate.
|
||||||
|
///
|
||||||
|
/// @param[in] line
|
||||||
|
/// Source line to locate.
|
||||||
|
///
|
||||||
|
/// @param[in] function
|
||||||
|
/// Optional filter function. Addresses within this function will be
|
||||||
|
/// added to the 'local' list. All others will be added to the 'extern' list.
|
||||||
|
///
|
||||||
|
/// @param[out] output_local
|
||||||
|
/// All matching addresses within 'function'
|
||||||
|
///
|
||||||
|
/// @param[out] output_extern
|
||||||
|
/// All matching addresses not within 'function'
|
||||||
|
void FindAddressesForLine (const lldb::TargetSP target_sp,
|
||||||
|
const FileSpec &file, uint32_t line,
|
||||||
|
Function *function,
|
||||||
|
std::vector<Address> &output_local, std::vector<Address> &output_extern);
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Remove (const lldb::ModuleSP &module_sp);
|
Remove (const lldb::ModuleSP &module_sp);
|
||||||
|
|
||||||
|
|
|
@ -129,6 +129,8 @@ public:
|
||||||
bool
|
bool
|
||||||
SetPC (uint64_t pc);
|
SetPC (uint64_t pc);
|
||||||
|
|
||||||
|
bool SetPC (Address addr);
|
||||||
|
|
||||||
uint64_t
|
uint64_t
|
||||||
GetSP (uint64_t fail_value = LLDB_INVALID_ADDRESS);
|
GetSP (uint64_t fail_value = LLDB_INVALID_ADDRESS);
|
||||||
|
|
||||||
|
|
|
@ -377,7 +377,10 @@ public:
|
||||||
|
|
||||||
Error
|
Error
|
||||||
ReturnFromFrame (lldb::StackFrameSP frame_sp, lldb::ValueObjectSP return_value_sp, bool broadcast = false);
|
ReturnFromFrame (lldb::StackFrameSP frame_sp, lldb::ValueObjectSP return_value_sp, bool broadcast = false);
|
||||||
|
|
||||||
|
Error
|
||||||
|
JumpToLine (const FileSpec &file, uint32_t line, bool can_leave_function, std::string *warnings = NULL);
|
||||||
|
|
||||||
virtual lldb::StackFrameSP
|
virtual lldb::StackFrameSP
|
||||||
GetFrameWithStackID (const StackID &stack_id)
|
GetFrameWithStackID (const StackID &stack_id)
|
||||||
{
|
{
|
||||||
|
|
|
@ -136,10 +136,13 @@ public:
|
||||||
StepInstruction(bool step_over);
|
StepInstruction(bool step_over);
|
||||||
|
|
||||||
SBError
|
SBError
|
||||||
StepOverUntil (lldb::SBFrame &frame,
|
StepOverUntil (lldb::SBFrame &frame,
|
||||||
lldb::SBFileSpec &file_spec,
|
lldb::SBFileSpec &file_spec,
|
||||||
uint32_t line);
|
uint32_t line);
|
||||||
|
|
||||||
|
SBError
|
||||||
|
JumpToLine (lldb::SBFileSpec &file_spec, uint32_t line);
|
||||||
|
|
||||||
void
|
void
|
||||||
RunToAddress (lldb::addr_t addr);
|
RunToAddress (lldb::addr_t addr);
|
||||||
|
|
||||||
|
|
|
@ -909,6 +909,31 @@ SBThread::StepOverUntil (lldb::SBFrame &sb_frame,
|
||||||
return sb_error;
|
return sb_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SBError
|
||||||
|
SBThread::JumpToLine (lldb::SBFileSpec &file_spec, uint32_t line)
|
||||||
|
{
|
||||||
|
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
|
||||||
|
SBError sb_error;
|
||||||
|
|
||||||
|
Mutex::Locker api_locker;
|
||||||
|
ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker);
|
||||||
|
|
||||||
|
if (log)
|
||||||
|
log->Printf ("SBThread(%p)::JumpToLine (file+line = %s:%u)", exe_ctx.GetThreadPtr(), file_spec->GetPath().c_str(), line);
|
||||||
|
|
||||||
|
if (!exe_ctx.HasThreadScope())
|
||||||
|
{
|
||||||
|
sb_error.SetErrorString("this SBThread object is invalid");
|
||||||
|
return sb_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread *thread = exe_ctx.GetThreadPtr();
|
||||||
|
|
||||||
|
Error err = thread->JumpToLine (file_spec.get(), line, true);
|
||||||
|
sb_error.SetError (err);
|
||||||
|
return sb_error;
|
||||||
|
}
|
||||||
|
|
||||||
SBError
|
SBError
|
||||||
SBThread::ReturnFromFrame (SBFrame &frame, SBValue &return_value)
|
SBThread::ReturnFromFrame (SBFrame &frame, SBValue &return_value)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1462,6 +1462,208 @@ CommandObjectThreadReturn::CommandOptions::g_option_table[] =
|
||||||
{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
|
{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// CommandObjectThreadJump
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class CommandObjectThreadJump : public CommandObjectParsed
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class CommandOptions : public Options
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
CommandOptions (CommandInterpreter &interpreter) :
|
||||||
|
Options (interpreter)
|
||||||
|
{
|
||||||
|
OptionParsingStarting ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
OptionParsingStarting ()
|
||||||
|
{
|
||||||
|
m_filenames.Clear();
|
||||||
|
m_line_num = 0;
|
||||||
|
m_line_offset = 0;
|
||||||
|
m_load_addr = LLDB_INVALID_ADDRESS;
|
||||||
|
m_force = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual
|
||||||
|
~CommandOptions ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Error
|
||||||
|
SetOptionValue (uint32_t option_idx, const char *option_arg)
|
||||||
|
{
|
||||||
|
bool success;
|
||||||
|
const int short_option = m_getopt_table[option_idx].val;
|
||||||
|
Error error;
|
||||||
|
|
||||||
|
switch (short_option)
|
||||||
|
{
|
||||||
|
case 'f':
|
||||||
|
m_filenames.AppendIfUnique (FileSpec(option_arg, false));
|
||||||
|
if (m_filenames.GetSize() > 1)
|
||||||
|
return Error("only one source file expected.");
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
m_line_num = Args::StringToUInt32 (option_arg, 0, 0, &success);
|
||||||
|
if (!success || m_line_num == 0)
|
||||||
|
return Error("invalid line number: '%s'.", option_arg);
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
m_line_offset = Args::StringToSInt32 (option_arg, 0, 0, &success);
|
||||||
|
if (!success)
|
||||||
|
return Error("invalid line offset: '%s'.", option_arg);
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
{
|
||||||
|
ExecutionContext exe_ctx (m_interpreter.GetExecutionContext());
|
||||||
|
m_load_addr = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
m_force = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return Error("invalid short option character '%c'", short_option);
|
||||||
|
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const OptionDefinition*
|
||||||
|
GetDefinitions ()
|
||||||
|
{
|
||||||
|
return g_option_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSpecList m_filenames;
|
||||||
|
uint32_t m_line_num;
|
||||||
|
int32_t m_line_offset;
|
||||||
|
lldb::addr_t m_load_addr;
|
||||||
|
bool m_force;
|
||||||
|
|
||||||
|
static OptionDefinition g_option_table[];
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual
|
||||||
|
Options *
|
||||||
|
GetOptions ()
|
||||||
|
{
|
||||||
|
return &m_options;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandObjectThreadJump (CommandInterpreter &interpreter) :
|
||||||
|
CommandObjectParsed (interpreter,
|
||||||
|
"thread jump",
|
||||||
|
"Sets the program counter to a new address.",
|
||||||
|
"thread jump",
|
||||||
|
eFlagRequiresFrame |
|
||||||
|
eFlagTryTargetAPILock |
|
||||||
|
eFlagProcessMustBeLaunched |
|
||||||
|
eFlagProcessMustBePaused ),
|
||||||
|
m_options (interpreter)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~CommandObjectThreadJump()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
bool DoExecute (Args& args, CommandReturnObject &result)
|
||||||
|
{
|
||||||
|
RegisterContext *reg_ctx = m_exe_ctx.GetRegisterContext();
|
||||||
|
StackFrame *frame = m_exe_ctx.GetFramePtr();
|
||||||
|
Thread *thread = m_exe_ctx.GetThreadPtr();
|
||||||
|
Target *target = m_exe_ctx.GetTargetPtr();
|
||||||
|
const SymbolContext &sym_ctx = frame->GetSymbolContext (eSymbolContextLineEntry);
|
||||||
|
|
||||||
|
if (m_options.m_load_addr != LLDB_INVALID_ADDRESS)
|
||||||
|
{
|
||||||
|
// Use this address directly.
|
||||||
|
Address dest = Address(m_options.m_load_addr);
|
||||||
|
|
||||||
|
lldb::addr_t callAddr = dest.GetCallableLoadAddress (target);
|
||||||
|
if (callAddr == LLDB_INVALID_ADDRESS)
|
||||||
|
{
|
||||||
|
result.AppendErrorWithFormat ("Invalid destination address.");
|
||||||
|
result.SetStatus (eReturnStatusFailed);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!reg_ctx->SetPC (callAddr))
|
||||||
|
{
|
||||||
|
result.AppendErrorWithFormat ("Error changing PC value for thread %d.", thread->GetIndexID());
|
||||||
|
result.SetStatus (eReturnStatusFailed);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Pick either the absolute line, or work out a relative one.
|
||||||
|
int32_t line = (int32_t)m_options.m_line_num;
|
||||||
|
if (line == 0)
|
||||||
|
line = sym_ctx.line_entry.line + m_options.m_line_offset;
|
||||||
|
|
||||||
|
// Try the current file, but override if asked.
|
||||||
|
FileSpec file = sym_ctx.line_entry.file;
|
||||||
|
if (m_options.m_filenames.GetSize() == 1)
|
||||||
|
file = m_options.m_filenames.GetFileSpecAtIndex(0);
|
||||||
|
|
||||||
|
if (!file)
|
||||||
|
{
|
||||||
|
result.AppendErrorWithFormat ("No source file available for the current location.");
|
||||||
|
result.SetStatus (eReturnStatusFailed);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string warnings;
|
||||||
|
Error err = thread->JumpToLine (file, line, m_options.m_force, &warnings);
|
||||||
|
|
||||||
|
if (err.Fail())
|
||||||
|
{
|
||||||
|
result.SetError (err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!warnings.empty())
|
||||||
|
result.AppendWarning (warnings.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
result.SetStatus (eReturnStatusSuccessFinishResult);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandOptions m_options;
|
||||||
|
};
|
||||||
|
OptionDefinition
|
||||||
|
CommandObjectThreadJump::CommandOptions::g_option_table[] =
|
||||||
|
{
|
||||||
|
{ LLDB_OPT_SET_1, false, "file", 'f', OptionParser::eRequiredArgument, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename,
|
||||||
|
"Specifies the source file to jump to."},
|
||||||
|
|
||||||
|
{ LLDB_OPT_SET_1, true, "line", 'l', OptionParser::eRequiredArgument, NULL, 0, eArgTypeLineNum,
|
||||||
|
"Specifies the line number to jump to."},
|
||||||
|
|
||||||
|
{ LLDB_OPT_SET_2, true, "by", 'b', OptionParser::eRequiredArgument, NULL, 0, eArgTypeOffset,
|
||||||
|
"Jumps by a relative line offset from the current line."},
|
||||||
|
|
||||||
|
{ LLDB_OPT_SET_3, true, "address", 'a', OptionParser::eRequiredArgument, NULL, 0, eArgTypeAddressOrExpression,
|
||||||
|
"Jumps to a specific address."},
|
||||||
|
|
||||||
|
{ LLDB_OPT_SET_1|
|
||||||
|
LLDB_OPT_SET_2|
|
||||||
|
LLDB_OPT_SET_3, false, "force",'r', OptionParser::eNoArgument, NULL, 0, eArgTypeNone,"Allows the PC to leave the current function."},
|
||||||
|
|
||||||
|
{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
// CommandObjectMultiwordThread
|
// CommandObjectMultiwordThread
|
||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
|
@ -1476,6 +1678,7 @@ CommandObjectMultiwordThread::CommandObjectMultiwordThread (CommandInterpreter &
|
||||||
LoadSubCommand ("continue", CommandObjectSP (new CommandObjectThreadContinue (interpreter)));
|
LoadSubCommand ("continue", CommandObjectSP (new CommandObjectThreadContinue (interpreter)));
|
||||||
LoadSubCommand ("list", CommandObjectSP (new CommandObjectThreadList (interpreter)));
|
LoadSubCommand ("list", CommandObjectSP (new CommandObjectThreadList (interpreter)));
|
||||||
LoadSubCommand ("return", CommandObjectSP (new CommandObjectThreadReturn (interpreter)));
|
LoadSubCommand ("return", CommandObjectSP (new CommandObjectThreadReturn (interpreter)));
|
||||||
|
LoadSubCommand ("jump", CommandObjectSP (new CommandObjectThreadJump (interpreter)));
|
||||||
LoadSubCommand ("select", CommandObjectSP (new CommandObjectThreadSelect (interpreter)));
|
LoadSubCommand ("select", CommandObjectSP (new CommandObjectThreadSelect (interpreter)));
|
||||||
LoadSubCommand ("until", CommandObjectSP (new CommandObjectThreadUntil (interpreter)));
|
LoadSubCommand ("until", CommandObjectSP (new CommandObjectThreadUntil (interpreter)));
|
||||||
LoadSubCommand ("step-in", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope (
|
LoadSubCommand ("step-in", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope (
|
||||||
|
|
|
@ -52,12 +52,16 @@ Error::Error (const Error &rhs) :
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Error::Error (const char* err_str):
|
Error::Error (const char* format, ...):
|
||||||
m_code (0),
|
m_code (0),
|
||||||
m_type (eErrorTypeInvalid),
|
m_type (eErrorTypeInvalid),
|
||||||
m_string ()
|
m_string ()
|
||||||
{
|
{
|
||||||
SetErrorString(err_str);
|
va_list args;
|
||||||
|
va_start (args, format);
|
||||||
|
SetErrorToGenericError ();
|
||||||
|
SetErrorStringWithVarArg (format, args);
|
||||||
|
va_end (args);
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "lldb/lldb-python.h"
|
#include "lldb/lldb-python.h"
|
||||||
|
|
||||||
|
#include "lldb/Core/AddressResolverFileLine.h"
|
||||||
#include "lldb/Core/Error.h"
|
#include "lldb/Core/Error.h"
|
||||||
#include "lldb/Core/Module.h"
|
#include "lldb/Core/Module.h"
|
||||||
#include "lldb/Core/DataBuffer.h"
|
#include "lldb/Core/DataBuffer.h"
|
||||||
|
@ -752,6 +753,27 @@ Module::FindFunctions (const RegularExpression& regex,
|
||||||
return sc_list.GetSize() - start_size;
|
return sc_list.GetSize() - start_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Module::FindAddressesForLine (const lldb::TargetSP target_sp,
|
||||||
|
const FileSpec &file, uint32_t line,
|
||||||
|
Function *function,
|
||||||
|
std::vector<Address> &output_local, std::vector<Address> &output_extern)
|
||||||
|
{
|
||||||
|
SearchFilterByModule filter(target_sp, m_file);
|
||||||
|
AddressResolverFileLine resolver(file, line, true);
|
||||||
|
resolver.ResolveAddress (filter);
|
||||||
|
|
||||||
|
for (size_t n=0;n<resolver.GetNumberOfAddresses();n++)
|
||||||
|
{
|
||||||
|
Address addr = resolver.GetAddressRangeAtIndex(n).GetBaseAddress();
|
||||||
|
Function *f = addr.CalculateSymbolContextFunction();
|
||||||
|
if (f && f == function)
|
||||||
|
output_local.push_back (addr);
|
||||||
|
else
|
||||||
|
output_extern.push_back (addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
Module::FindTypes_Impl (const SymbolContext& sc,
|
Module::FindTypes_Impl (const SymbolContext& sc,
|
||||||
const ConstString &name,
|
const ConstString &name,
|
||||||
|
@ -1606,4 +1628,4 @@ Module::PrepareForFunctionNameLookup (const ConstString &name,
|
||||||
lookup_name = name;
|
lookup_name = name;
|
||||||
match_name_after_lookup = false;
|
match_name_after_lookup = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -663,7 +663,19 @@ ModuleList::FindSourceFile (const FileSpec &orig_spec, FileSpec &new_spec) const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ModuleList::FindAddressesForLine (const lldb::TargetSP target_sp,
|
||||||
|
const FileSpec &file, uint32_t line,
|
||||||
|
Function *function,
|
||||||
|
std::vector<Address> &output_local, std::vector<Address> &output_extern)
|
||||||
|
{
|
||||||
|
Mutex::Locker locker(m_modules_mutex);
|
||||||
|
collection::const_iterator pos, end = m_modules.end();
|
||||||
|
for (pos = m_modules.begin(); pos != end; ++pos)
|
||||||
|
{
|
||||||
|
(*pos)->FindAddressesForLine(target_sp, file, line, function, output_local, output_extern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ModuleSP
|
ModuleSP
|
||||||
ModuleList::FindFirstModule (const ModuleSpec &module_spec) const
|
ModuleList::FindFirstModule (const ModuleSpec &module_spec) const
|
||||||
|
|
|
@ -228,6 +228,13 @@ CommandInterpreter::Initialize ()
|
||||||
AddAlias ("t", cmd_obj_sp);
|
AddAlias ("t", cmd_obj_sp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd_obj_sp = GetCommandSPExact ("_regexp-jump",false);
|
||||||
|
if (cmd_obj_sp)
|
||||||
|
{
|
||||||
|
AddAlias ("j", cmd_obj_sp);
|
||||||
|
AddAlias ("jump", cmd_obj_sp);
|
||||||
|
}
|
||||||
|
|
||||||
cmd_obj_sp = GetCommandSPExact ("_regexp-list", false);
|
cmd_obj_sp = GetCommandSPExact ("_regexp-list", false);
|
||||||
if (cmd_obj_sp)
|
if (cmd_obj_sp)
|
||||||
{
|
{
|
||||||
|
@ -618,6 +625,26 @@ CommandInterpreter::LoadCommandDictionary ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<CommandObjectRegexCommand>
|
||||||
|
jump_regex_cmd_ap(new CommandObjectRegexCommand (*this,
|
||||||
|
"_regexp-jump",
|
||||||
|
"Sets the program counter to a new address.",
|
||||||
|
"_regexp-jump [<line>]\n"
|
||||||
|
"_regexp-jump [<+-lineoffset>]\n"
|
||||||
|
"_regexp-jump [<file>:<line>]\n"
|
||||||
|
"_regexp-jump [*<addr>]\n", 2));
|
||||||
|
if (jump_regex_cmd_ap.get())
|
||||||
|
{
|
||||||
|
if (jump_regex_cmd_ap->AddRegexCommand("^\\*(.*)$", "thread jump --addr %1") &&
|
||||||
|
jump_regex_cmd_ap->AddRegexCommand("^([0-9]+)$", "thread jump --line %1") &&
|
||||||
|
jump_regex_cmd_ap->AddRegexCommand("^([^:]+):([0-9]+)$", "thread jump --file %1 --line %2") &&
|
||||||
|
jump_regex_cmd_ap->AddRegexCommand("^([+\\-][0-9]+)$", "thread jump --by %1"))
|
||||||
|
{
|
||||||
|
CommandObjectSP jump_regex_cmd_sp(jump_regex_cmd_ap.release());
|
||||||
|
m_command_dict[jump_regex_cmd_sp->GetCommandName ()] = jump_regex_cmd_sp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
|
@ -113,6 +113,19 @@ RegisterContext::SetPC(uint64_t pc)
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
RegisterContext::SetPC(Address addr)
|
||||||
|
{
|
||||||
|
TargetSP target_sp = m_thread.CalculateTarget();
|
||||||
|
Target *target = target_sp.get();
|
||||||
|
|
||||||
|
lldb::addr_t callAddr = addr.GetCallableLoadAddress (target);
|
||||||
|
if (callAddr == LLDB_INVALID_ADDRESS)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return SetPC (callAddr);
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t
|
uint64_t
|
||||||
RegisterContext::GetSP(uint64_t fail_value)
|
RegisterContext::GetSP(uint64_t fail_value)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1745,6 +1745,79 @@ Thread::ReturnFromFrame (lldb::StackFrameSP frame_sp, lldb::ValueObjectSP return
|
||||||
return return_error;
|
return return_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void DumpAddressList (Stream &s, const std::vector<Address> &list, ExecutionContextScope *exe_scope)
|
||||||
|
{
|
||||||
|
for (size_t n=0;n<list.size();n++)
|
||||||
|
{
|
||||||
|
s << "\t";
|
||||||
|
list[n].Dump (&s, exe_scope, Address::DumpStyleResolvedDescription, Address::DumpStyleSectionNameOffset);
|
||||||
|
s << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Error
|
||||||
|
Thread::JumpToLine (const FileSpec &file, uint32_t line, bool can_leave_function, std::string *warnings)
|
||||||
|
{
|
||||||
|
ExecutionContext exe_ctx (GetStackFrameAtIndex(0));
|
||||||
|
Target *target = exe_ctx.GetTargetPtr();
|
||||||
|
TargetSP target_sp = exe_ctx.GetTargetSP();
|
||||||
|
RegisterContext *reg_ctx = exe_ctx.GetRegisterContext();
|
||||||
|
StackFrame *frame = exe_ctx.GetFramePtr();
|
||||||
|
const SymbolContext &sc = frame->GetSymbolContext(eSymbolContextFunction);
|
||||||
|
|
||||||
|
// Find candidate locations.
|
||||||
|
std::vector<Address> candidates, within_function, outside_function;
|
||||||
|
target->GetImages().FindAddressesForLine (target_sp, file, line, sc.function, within_function, outside_function);
|
||||||
|
|
||||||
|
// If possible, we try and stay within the current function.
|
||||||
|
// Within a function, we accept multiple locations (optimized code may do this,
|
||||||
|
// there's no solution here so we do the best we can).
|
||||||
|
// However if we're trying to leave the function, we don't know how to pick the
|
||||||
|
// right location, so if there's more than one then we bail.
|
||||||
|
if (!within_function.empty())
|
||||||
|
candidates = within_function;
|
||||||
|
else if (outside_function.size() == 1 && can_leave_function)
|
||||||
|
candidates = outside_function;
|
||||||
|
|
||||||
|
// Check if we got anything.
|
||||||
|
if (candidates.empty())
|
||||||
|
{
|
||||||
|
if (outside_function.empty())
|
||||||
|
{
|
||||||
|
return Error("Cannot locate an address for %s:%i.",
|
||||||
|
file.GetFilename().AsCString(), line);
|
||||||
|
}
|
||||||
|
else if (outside_function.size() == 1)
|
||||||
|
{
|
||||||
|
return Error("%s:%i is outside the current function.",
|
||||||
|
file.GetFilename().AsCString(), line);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StreamString sstr;
|
||||||
|
DumpAddressList(sstr, outside_function, target);
|
||||||
|
return Error("%s:%i has multiple candidate locations:\n%s",
|
||||||
|
file.GetFilename().AsCString(), line, sstr.GetString().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept the first location, warn about any others.
|
||||||
|
Address dest = candidates[0];
|
||||||
|
if (warnings && candidates.size() > 1)
|
||||||
|
{
|
||||||
|
StreamString sstr;
|
||||||
|
sstr.Printf("%s:%i appears multiple times in this function, selecting the first location:\n",
|
||||||
|
file.GetFilename().AsCString(), line);
|
||||||
|
DumpAddressList(sstr, candidates, target);
|
||||||
|
*warnings = sstr.GetString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!reg_ctx->SetPC (dest))
|
||||||
|
return Error("Cannot change PC to target address.");
|
||||||
|
|
||||||
|
return Error();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Thread::DumpUsingSettingsFormat (Stream &strm, uint32_t frame_idx)
|
Thread::DumpUsingSettingsFormat (Stream &strm, uint32_t frame_idx)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
LEVEL = ../../../make
|
||||||
|
|
||||||
|
CXX_SOURCES := main.cpp other.cpp
|
||||||
|
include $(LEVEL)/Makefile.rules
|
|
@ -0,0 +1,75 @@
|
||||||
|
"""
|
||||||
|
Test jumping to different places.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os, time
|
||||||
|
import unittest2
|
||||||
|
import lldb
|
||||||
|
from lldbtest import *
|
||||||
|
import lldbutil
|
||||||
|
|
||||||
|
class ThreadJumpTestCase(TestBase):
|
||||||
|
|
||||||
|
mydir = os.path.join("functionalities", "thread", "jump")
|
||||||
|
|
||||||
|
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
|
||||||
|
@dsym_test
|
||||||
|
def test_with_dsym(self):
|
||||||
|
"""Test thread jump handling."""
|
||||||
|
self.buildDsym(dictionary=self.getBuildFlags())
|
||||||
|
self.thread_jump_test()
|
||||||
|
|
||||||
|
@dwarf_test
|
||||||
|
def test_with_dwarf(self):
|
||||||
|
"""Test thread jump handling."""
|
||||||
|
self.buildDwarf(dictionary=self.getBuildFlags())
|
||||||
|
self.thread_jump_test()
|
||||||
|
|
||||||
|
def do_min_test(self, start, jump, var, value):
|
||||||
|
self.runCmd("j %i" % start) # jump to the start marker
|
||||||
|
self.runCmd("thread step-in") # step into the min fn
|
||||||
|
self.runCmd("j %i" % jump) # jump to the branch we're interested in
|
||||||
|
self.runCmd("thread step-out") # return out
|
||||||
|
self.runCmd("thread step-over") # assign to the global
|
||||||
|
self.expect("expr %s" % var, substrs = [value]) # check it
|
||||||
|
|
||||||
|
def thread_jump_test(self):
|
||||||
|
"""Test thread exit handling."""
|
||||||
|
exe = os.path.join(os.getcwd(), "a.out")
|
||||||
|
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
|
||||||
|
|
||||||
|
# Find the line numbers for our breakpoints.
|
||||||
|
self.mark1 = line_number('main.cpp', '// 1st marker')
|
||||||
|
self.mark2 = line_number('main.cpp', '// 2nd marker')
|
||||||
|
self.mark3 = line_number('main.cpp', '// 3rd marker')
|
||||||
|
self.mark4 = line_number('main.cpp', '// 4th marker')
|
||||||
|
self.mark5 = line_number('other.cpp', '// other marker')
|
||||||
|
|
||||||
|
lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.mark3, num_expected_locations=1)
|
||||||
|
self.runCmd("run", RUN_SUCCEEDED)
|
||||||
|
|
||||||
|
# The stop reason of the thread should be breakpoint 1.
|
||||||
|
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT + " 1",
|
||||||
|
substrs = ['stopped',
|
||||||
|
'* thread #1',
|
||||||
|
'stop reason = breakpoint 1'])
|
||||||
|
|
||||||
|
self.do_min_test(self.mark3, self.mark1, "i", "4"); # Try the int path, force it to return 'a'
|
||||||
|
self.do_min_test(self.mark3, self.mark2, "i", "5"); # Try the int path, force it to return 'b'
|
||||||
|
self.do_min_test(self.mark4, self.mark1, "j", "7"); # Try the double path, force it to return 'a'
|
||||||
|
self.do_min_test(self.mark4, self.mark2, "j", "8"); # Try the double path, force it to return 'b'
|
||||||
|
|
||||||
|
# Try jumping to another function in a different file.
|
||||||
|
self.runCmd("thread jump --file other.cpp --line %i --force" % self.mark5)
|
||||||
|
self.expect("process status",
|
||||||
|
substrs = ["at other.cpp:%i" % self.mark5])
|
||||||
|
|
||||||
|
# Try jumping to another function (without forcing)
|
||||||
|
self.expect("j main.cpp:%i" % self.mark1, COMMAND_FAILED_AS_EXPECTED, error = True,
|
||||||
|
substrs = ["error"])
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import atexit
|
||||||
|
lldb.SBDebugger.Initialize()
|
||||||
|
atexit.register(lambda: lldb.SBDebugger.Terminate())
|
||||||
|
unittest2.main()
|
|
@ -0,0 +1,34 @@
|
||||||
|
//===-- main.cpp ------------------------------------------------*- C++ -*-===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
// This test verifies the correct handling of program counter jumps.
|
||||||
|
|
||||||
|
int otherfn();
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T min(T a, T b)
|
||||||
|
{
|
||||||
|
if (a < b)
|
||||||
|
{
|
||||||
|
return a; // 1st marker
|
||||||
|
} else {
|
||||||
|
return b; // 2nd marker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main ()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
double j;
|
||||||
|
|
||||||
|
i = min(4, 5); // 3rd marker
|
||||||
|
j = min(7.0, 8.0); // 4th marker
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
//===-- other.cpp -----------------------------------------------*- C++ -*-===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
int otherfn()
|
||||||
|
{
|
||||||
|
return 4; // other marker
|
||||||
|
}
|
Loading…
Reference in New Issue