Vendor import of lldb trunk r257626:
https://llvm.org/svn/llvm-project/lldb/trunk@257626
This commit is contained in:
parent
9e6d35490a
commit
7fed546d19
152
include/lldb/Core/LoadedModuleInfoList.h
Normal file
152
include/lldb/Core/LoadedModuleInfoList.h
Normal file
@ -0,0 +1,152 @@
|
||||
//===-- LoadedModuleInfoList.h ----------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef liblldb_LoadedModuleInfoList_h_
|
||||
#define liblldb_LoadedModuleInfoList_h_
|
||||
|
||||
// C Includes
|
||||
|
||||
// C++ Includes
|
||||
#include <vector>
|
||||
|
||||
// Other libraries and framework includes
|
||||
#include "lldb/lldb-private-forward.h"
|
||||
|
||||
namespace lldb_private {
|
||||
class LoadedModuleInfoList
|
||||
{
|
||||
public:
|
||||
|
||||
class LoadedModuleInfo
|
||||
{
|
||||
public:
|
||||
|
||||
enum e_data_point
|
||||
{
|
||||
e_has_name = 0,
|
||||
e_has_base ,
|
||||
e_has_dynamic ,
|
||||
e_has_link_map ,
|
||||
e_num
|
||||
};
|
||||
|
||||
LoadedModuleInfo ()
|
||||
{
|
||||
for (uint32_t i = 0; i < e_num; ++i)
|
||||
m_has[i] = false;
|
||||
};
|
||||
|
||||
void set_name (const std::string & name)
|
||||
{
|
||||
m_name = name;
|
||||
m_has[e_has_name] = true;
|
||||
}
|
||||
bool get_name (std::string & out) const
|
||||
{
|
||||
out = m_name;
|
||||
return m_has[e_has_name];
|
||||
}
|
||||
|
||||
void set_base (const lldb::addr_t base)
|
||||
{
|
||||
m_base = base;
|
||||
m_has[e_has_base] = true;
|
||||
}
|
||||
bool get_base (lldb::addr_t & out) const
|
||||
{
|
||||
out = m_base;
|
||||
return m_has[e_has_base];
|
||||
}
|
||||
|
||||
void set_base_is_offset (bool is_offset)
|
||||
{
|
||||
m_base_is_offset = is_offset;
|
||||
}
|
||||
bool get_base_is_offset(bool & out) const
|
||||
{
|
||||
out = m_base_is_offset;
|
||||
return m_has[e_has_base];
|
||||
}
|
||||
|
||||
void set_link_map (const lldb::addr_t addr)
|
||||
{
|
||||
m_link_map = addr;
|
||||
m_has[e_has_link_map] = true;
|
||||
}
|
||||
bool get_link_map (lldb::addr_t & out) const
|
||||
{
|
||||
out = m_link_map;
|
||||
return m_has[e_has_link_map];
|
||||
}
|
||||
|
||||
void set_dynamic (const lldb::addr_t addr)
|
||||
{
|
||||
m_dynamic = addr;
|
||||
m_has[e_has_dynamic] = true;
|
||||
}
|
||||
bool get_dynamic (lldb::addr_t & out) const
|
||||
{
|
||||
out = m_dynamic;
|
||||
return m_has[e_has_dynamic];
|
||||
}
|
||||
|
||||
bool has_info (e_data_point datum) const
|
||||
{
|
||||
assert (datum < e_num);
|
||||
return m_has[datum];
|
||||
}
|
||||
|
||||
bool
|
||||
operator == (LoadedModuleInfo const &rhs) const
|
||||
{
|
||||
if (e_num != rhs.e_num)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < e_num; ++i)
|
||||
{
|
||||
if (m_has[i] != rhs.m_has[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return (m_base == rhs.m_base) &&
|
||||
(m_link_map == rhs.m_link_map) &&
|
||||
(m_dynamic == rhs.m_dynamic) &&
|
||||
(m_name == rhs.m_name);
|
||||
}
|
||||
protected:
|
||||
|
||||
bool m_has[e_num];
|
||||
std::string m_name;
|
||||
lldb::addr_t m_link_map;
|
||||
lldb::addr_t m_base;
|
||||
bool m_base_is_offset;
|
||||
lldb::addr_t m_dynamic;
|
||||
};
|
||||
|
||||
LoadedModuleInfoList ()
|
||||
: m_list ()
|
||||
, m_link_map (LLDB_INVALID_ADDRESS)
|
||||
{}
|
||||
|
||||
void add (const LoadedModuleInfo & mod)
|
||||
{
|
||||
m_list.push_back (mod);
|
||||
}
|
||||
|
||||
void clear ()
|
||||
{
|
||||
m_list.clear ();
|
||||
}
|
||||
|
||||
std::vector<LoadedModuleInfo> m_list;
|
||||
lldb::addr_t m_link_map;
|
||||
};
|
||||
} // namespace lldb_private
|
||||
|
||||
#endif // liblldb_LoadedModuleInfoList_h_
|
@ -170,6 +170,18 @@ class CommandReturnObject
|
||||
void
|
||||
SetInteractive (bool b);
|
||||
|
||||
bool
|
||||
GetAbnormalStopWasExpected() const
|
||||
{
|
||||
return m_abnormal_stop_was_expected;
|
||||
}
|
||||
|
||||
void
|
||||
SetAbnormalStopWasExpected(bool signal_was_expected)
|
||||
{
|
||||
m_abnormal_stop_was_expected = signal_was_expected;
|
||||
}
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
@ -183,6 +195,12 @@ class CommandReturnObject
|
||||
lldb::ReturnStatus m_status;
|
||||
bool m_did_change_process_state;
|
||||
bool m_interactive; // If true, then the input handle from the debugger will be hooked up
|
||||
bool m_abnormal_stop_was_expected; // This is to support eHandleCommandFlagStopOnCrash vrs. attach.
|
||||
// The attach command often ends up with the process stopped due to a signal.
|
||||
// Normally that would mean stop on crash should halt batch execution, but we
|
||||
// obviously don't want that for attach. Using this flag, the attach command
|
||||
// (and anything else for which this is relevant) can say that the signal is
|
||||
// expected, and batch command execution can continue.
|
||||
};
|
||||
|
||||
} // namespace lldb_private
|
||||
|
@ -573,6 +573,9 @@ class ClangASTContext : public TypeSystem
|
||||
ConstString
|
||||
DeclContextGetName (void *opaque_decl_ctx) override;
|
||||
|
||||
ConstString
|
||||
DeclContextGetScopeQualifiedName (void *opaque_decl_ctx) override;
|
||||
|
||||
bool
|
||||
DeclContextIsClassMethod (void *opaque_decl_ctx,
|
||||
lldb::LanguageType *language_ptr,
|
||||
|
@ -128,6 +128,9 @@ class CompilerDeclContext
|
||||
ConstString
|
||||
GetName () const;
|
||||
|
||||
ConstString
|
||||
GetScopeQualifiedName() const;
|
||||
|
||||
bool
|
||||
IsStructUnionOrClass () const;
|
||||
|
||||
|
@ -112,6 +112,12 @@ class GoASTContext : public TypeSystem
|
||||
return ConstString();
|
||||
}
|
||||
|
||||
ConstString
|
||||
DeclContextGetScopeQualifiedName(void *opaque_decl_ctx) override
|
||||
{
|
||||
return ConstString();
|
||||
}
|
||||
|
||||
bool
|
||||
DeclContextIsClassMethod(void *opaque_decl_ctx, lldb::LanguageType *language_ptr, bool *is_instance_method_ptr,
|
||||
ConstString *language_object_name_ptr) override
|
||||
|
@ -144,6 +144,7 @@ class SymbolFile :
|
||||
virtual uint32_t FindTypes (const SymbolContext& sc, const ConstString &name, const CompilerDeclContext *parent_decl_ctx, bool append, uint32_t max_matches, TypeMap& types);
|
||||
virtual size_t FindTypes (const std::vector<CompilerContext> &context, bool append, TypeMap& types);
|
||||
|
||||
virtual void GetMangledNamesForFunction(const std::string &scope_qualified_name, std::vector<ConstString> &mangled_names);
|
||||
// virtual uint32_t FindTypes (const SymbolContext& sc, const RegularExpression& regex, bool append, uint32_t max_matches, TypeList& types) = 0;
|
||||
virtual TypeList * GetTypeList ();
|
||||
virtual size_t GetTypes (lldb_private::SymbolContextScope *sc_scope,
|
||||
|
@ -151,6 +151,9 @@ class TypeSystem : public PluginInterface
|
||||
virtual ConstString
|
||||
DeclContextGetName (void *opaque_decl_ctx) = 0;
|
||||
|
||||
virtual ConstString
|
||||
DeclContextGetScopeQualifiedName (void *opaque_decl_ctx) = 0;
|
||||
|
||||
virtual bool
|
||||
DeclContextIsClassMethod (void *opaque_decl_ctx,
|
||||
lldb::LanguageType *language_ptr,
|
||||
|
@ -42,9 +42,6 @@ class CPPLanguageRuntime :
|
||||
bool
|
||||
GetObjectDescription(Stream &str, Value &value, ExecutionContextScope *exe_scope) override;
|
||||
|
||||
virtual size_t
|
||||
GetAlternateManglings(const ConstString &mangled, std::vector<ConstString> &alternates) = 0;
|
||||
|
||||
protected:
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "lldb/Core/Communication.h"
|
||||
#include "lldb/Core/Error.h"
|
||||
#include "lldb/Core/Event.h"
|
||||
#include "lldb/Core/LoadedModuleInfoList.h"
|
||||
#include "lldb/Core/ThreadSafeValue.h"
|
||||
#include "lldb/Core/PluginInterface.h"
|
||||
#include "lldb/Core/StructuredData.h"
|
||||
@ -1152,6 +1153,12 @@ class Process :
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual size_t
|
||||
LoadModules (LoadedModuleInfoList &)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual JITLoaderList &
|
||||
GetJITLoaders ();
|
||||
@ -3149,6 +3156,34 @@ class Process :
|
||||
void
|
||||
ResetImageToken(size_t token);
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Find the next branch instruction to set a breakpoint on
|
||||
///
|
||||
/// When instruction stepping through a source line, instead of
|
||||
/// stepping through each instruction, we can put a breakpoint on
|
||||
/// the next branch instruction (within the range of instructions
|
||||
/// we are stepping through) and continue the process to there,
|
||||
/// yielding significant performance benefits over instruction
|
||||
/// stepping.
|
||||
///
|
||||
/// @param[in] default_stop_addr
|
||||
/// The address of the instruction where lldb would put a
|
||||
/// breakpoint normally.
|
||||
///
|
||||
/// @param[in] range_bounds
|
||||
/// The range which the breakpoint must be contained within.
|
||||
/// Typically a source line.
|
||||
///
|
||||
/// @return
|
||||
/// The address of the next branch instruction, or the end of
|
||||
/// the range provided in range_bounds. If there are any
|
||||
/// problems with the disassembly or getting the instructions,
|
||||
/// the original default_stop_addr will be returned.
|
||||
//------------------------------------------------------------------
|
||||
Address
|
||||
AdvanceAddressToNextBranchInstruction (Address default_stop_addr,
|
||||
AddressRange range_bounds);
|
||||
|
||||
protected:
|
||||
void
|
||||
SetState (lldb::EventSP &event_sp);
|
||||
|
@ -275,6 +275,23 @@ class SystemRuntime :
|
||||
return LLDB_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Retrieve the Queue kind for the queue at a thread's dispatch_qaddr.
|
||||
///
|
||||
/// Retrieve the Queue kind - either eQueueKindSerial or
|
||||
/// eQueueKindConcurrent, indicating that this queue processes work
|
||||
/// items serially or concurrently.
|
||||
///
|
||||
/// @return
|
||||
/// The Queue kind, if it could be read, else eQueueKindUnknown.
|
||||
//------------------------------------------------------------------
|
||||
virtual lldb::QueueKind
|
||||
GetQueueKind (lldb::addr_t dispatch_qaddr)
|
||||
{
|
||||
return lldb::eQueueKindUnknown;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Get the pending work items for a libdispatch Queue
|
||||
///
|
||||
|
@ -366,6 +366,35 @@ class Thread :
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Whether this thread can be associated with a libdispatch queue
|
||||
///
|
||||
/// The Thread may know if it is associated with a libdispatch queue,
|
||||
/// it may know definitively that it is NOT associated with a libdispatch
|
||||
/// queue, or it may be unknown whether it is associated with a libdispatch
|
||||
/// queue.
|
||||
///
|
||||
/// @return
|
||||
/// eLazyBoolNo if this thread is definitely not associated with a
|
||||
/// libdispatch queue (e.g. on a non-Darwin system where GCD aka
|
||||
/// libdispatch is not available).
|
||||
///
|
||||
/// eLazyBoolYes this thread is associated with a libdispatch queue.
|
||||
///
|
||||
/// eLazyBoolCalculate this thread may be associated with a libdispatch
|
||||
/// queue but the thread doesn't know one way or the other.
|
||||
//------------------------------------------------------------------
|
||||
virtual lldb_private::LazyBool
|
||||
GetAssociatedWithLibdispatchQueue ()
|
||||
{
|
||||
return eLazyBoolNo;
|
||||
}
|
||||
|
||||
virtual void
|
||||
SetAssociatedWithLibdispatchQueue (lldb_private::LazyBool associated_with_libdispatch_queue)
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Retrieve the Queue ID for the queue currently using this Thread
|
||||
///
|
||||
@ -413,6 +442,29 @@ class Thread :
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Retrieve the Queue kind for the queue currently using this Thread
|
||||
///
|
||||
/// If this Thread is doing work on behalf of a libdispatch/GCD queue,
|
||||
/// retrieve the Queue kind - either eQueueKindSerial or
|
||||
/// eQueueKindConcurrent, indicating that this queue processes work
|
||||
/// items serially or concurrently.
|
||||
///
|
||||
/// @return
|
||||
/// The Queue kind, if the Thread subclass implements this, else
|
||||
/// eQueueKindUnknown.
|
||||
//------------------------------------------------------------------
|
||||
virtual lldb::QueueKind
|
||||
GetQueueKind ()
|
||||
{
|
||||
return lldb::eQueueKindUnknown;
|
||||
}
|
||||
|
||||
virtual void
|
||||
SetQueueKind (lldb::QueueKind kind)
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Retrieve the Queue for this thread, if any.
|
||||
///
|
||||
@ -451,6 +503,30 @@ class Thread :
|
||||
return LLDB_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
virtual void
|
||||
SetQueueLibdispatchQueueAddress (lldb::addr_t dispatch_queue_t)
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Whether this Thread already has all the Queue information cached or not
|
||||
///
|
||||
/// A Thread may be associated with a libdispatch work Queue at a given
|
||||
/// public stop event. If so, the thread can satisify requests like
|
||||
/// GetQueueLibdispatchQueueAddress, GetQueueKind, GetQueueName, and GetQueueID
|
||||
/// either from information from the remote debug stub when it is initially
|
||||
/// created, or it can query the SystemRuntime for that information.
|
||||
///
|
||||
/// This method allows the SystemRuntime to discover if a thread has this
|
||||
/// information already, instead of calling the thread to get the information
|
||||
/// and having the thread call the SystemRuntime again.
|
||||
//------------------------------------------------------------------
|
||||
virtual bool
|
||||
ThreadHasQueueInformation () const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual uint32_t
|
||||
GetStackFrameCount()
|
||||
{
|
||||
@ -888,6 +964,16 @@ class Thread :
|
||||
/// @param[in] run_vote
|
||||
/// See standard meanings for the stop & run votes in ThreadPlan.h.
|
||||
///
|
||||
/// @param[in] continue_to_next_branch
|
||||
/// Normally this will enqueue a plan that will put a breakpoint on the return address and continue
|
||||
/// to there. If continue_to_next_branch is true, this is an operation not involving the user --
|
||||
/// e.g. stepping "next" in a source line and we instruction stepped into another function --
|
||||
/// so instead of putting a breakpoint on the return address, advance the breakpoint to the
|
||||
/// end of the source line that is doing the call, or until the next flow control instruction.
|
||||
/// If the return value from the function call is to be retrieved / displayed to the user, you must stop
|
||||
/// on the return address. The return value may be stored in volatile registers which are overwritten
|
||||
/// before the next branch instruction.
|
||||
///
|
||||
/// @return
|
||||
/// A shared pointer to the newly queued thread plan, or nullptr if the plan could not be queued.
|
||||
//------------------------------------------------------------------
|
||||
@ -898,7 +984,8 @@ class Thread :
|
||||
bool stop_other_threads,
|
||||
Vote stop_vote, // = eVoteYes,
|
||||
Vote run_vote, // = eVoteNoOpinion);
|
||||
uint32_t frame_idx);
|
||||
uint32_t frame_idx,
|
||||
bool continue_to_next_branch = false);
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Gets the plan used to step through the code that steps from a function
|
||||
|
@ -31,7 +31,8 @@ class ThreadPlanStepOut : public ThreadPlan,
|
||||
Vote stop_vote,
|
||||
Vote run_vote,
|
||||
uint32_t frame_idx,
|
||||
LazyBool step_out_avoids_code_without_debug_info);
|
||||
LazyBool step_out_avoids_code_without_debug_info,
|
||||
bool continue_to_next_branch = false);
|
||||
|
||||
~ThreadPlanStepOut() override;
|
||||
|
||||
|
@ -157,3 +157,10 @@ o Writing test cases:
|
||||
then use SBInterpreter::HandleCommand to run the command. You get the full result text
|
||||
from the command in the command return object, and all the part where you are driving the
|
||||
debugger to the point you want to test will be more robust.
|
||||
|
||||
o Attaching in test cases:
|
||||
|
||||
If you need to attach to inferiors in your tests, you must make sure the inferior calls
|
||||
lldb_enable_attach(), before the debugger attempts to attach. This function performs any
|
||||
platform-specific processing needed to enable attaching to this process (e.g., on Linux, we
|
||||
execute prctl(PR_SET_TRACER) syscall to disable protections present in some Linux systems).
|
||||
|
@ -51,7 +51,7 @@ def test_sb_api_listener_event_process_state(self):
|
||||
@skipIfNoSBHeaders
|
||||
@skipIfWindows # clang-cl does not support throw or catch (llvm.org/pr24538)
|
||||
@expectedFlakeyFreeBSD
|
||||
@expectedFlakeyLinux
|
||||
@expectedFailureLinux
|
||||
def test_sb_api_listener_resume(self):
|
||||
""" Test that a process can be resumed from a non-main thread. """
|
||||
self.build_and_test('driver.cpp listener_test.cpp test_listener_resume.cpp',
|
||||
|
@ -1149,11 +1149,6 @@ def getExpectedTimeouts(platform_name):
|
||||
"TestExitDuringStep.py",
|
||||
"TestHelloWorld.py",
|
||||
}
|
||||
if host.startswith("win32"):
|
||||
expected_timeout |= {
|
||||
"TestEvents.py",
|
||||
"TestThreadStates.py",
|
||||
}
|
||||
elif target.startswith("freebsd"):
|
||||
expected_timeout |= {
|
||||
"TestBreakpointConditions.py",
|
||||
|
@ -14,21 +14,12 @@ class DriverBatchModeTest (TestBase):
|
||||
|
||||
mydir = TestBase.compute_mydir(__file__)
|
||||
|
||||
@skipIfRemote # test not remote-ready llvm.org/pr24813
|
||||
@expectedFlakeyFreeBSD("llvm.org/pr25172 fails rarely on the buildbot")
|
||||
@expectedFlakeyLinux("llvm.org/pr25172")
|
||||
@expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows")
|
||||
def test_driver_batch_mode(self):
|
||||
"""Test that the lldb driver's batch mode works correctly."""
|
||||
self.build()
|
||||
self.setTearDownCleanup()
|
||||
self.batch_mode()
|
||||
|
||||
def setUp(self):
|
||||
# Call super's setUp().
|
||||
TestBase.setUp(self)
|
||||
# Our simple source filename.
|
||||
self.source = 'main.c'
|
||||
self.victim = None
|
||||
|
||||
def expect_string (self, string):
|
||||
import pexpect
|
||||
@ -40,12 +31,20 @@ def expect_string (self, string):
|
||||
except pexpect.TIMEOUT:
|
||||
self.fail ("Timed out waiting for '%s'"%(string))
|
||||
|
||||
def batch_mode (self):
|
||||
@skipIfRemote # test not remote-ready llvm.org/pr24813
|
||||
@expectedFlakeyFreeBSD("llvm.org/pr25172 fails rarely on the buildbot")
|
||||
@expectedFlakeyLinux("llvm.org/pr25172")
|
||||
@expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows")
|
||||
def test_batch_mode_run_crash (self):
|
||||
"""Test that the lldb driver's batch mode works correctly."""
|
||||
self.build()
|
||||
self.setTearDownCleanup()
|
||||
|
||||
import pexpect
|
||||
exe = os.path.join(os.getcwd(), "a.out")
|
||||
prompt = "(lldb) "
|
||||
|
||||
# First time through, pass CRASH so the process will crash and stop in batch mode.
|
||||
# Pass CRASH so the process will crash and stop in batch mode.
|
||||
run_commands = ' -b -o "break set -n main" -o "run" -o "continue" -k "frame var touch_me_not"'
|
||||
self.child = pexpect.spawn('%s %s %s %s -- CRASH' % (lldbtest_config.lldbExec, self.lldbOption, run_commands, exe))
|
||||
child = self.child
|
||||
@ -68,7 +67,21 @@ def batch_mode (self):
|
||||
|
||||
self.deletePexpectChild()
|
||||
|
||||
# Now do it again, and see make sure if we don't crash, we quit:
|
||||
|
||||
@skipIfRemote # test not remote-ready llvm.org/pr24813
|
||||
@expectedFlakeyFreeBSD("llvm.org/pr25172 fails rarely on the buildbot")
|
||||
@expectedFlakeyLinux("llvm.org/pr25172")
|
||||
@expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows")
|
||||
def test_batch_mode_run_exit (self):
|
||||
"""Test that the lldb driver's batch mode works correctly."""
|
||||
self.build()
|
||||
self.setTearDownCleanup()
|
||||
|
||||
import pexpect
|
||||
exe = os.path.join(os.getcwd(), "a.out")
|
||||
prompt = "(lldb) "
|
||||
|
||||
# Now do it again, and make sure if we don't crash, we quit:
|
||||
run_commands = ' -b -o "break set -n main" -o "run" -o "continue" '
|
||||
self.child = pexpect.spawn('%s %s %s %s -- NOCRASH' % (lldbtest_config.lldbExec, self.lldbOption, run_commands, exe))
|
||||
child = self.child
|
||||
@ -87,3 +100,69 @@ def batch_mode (self):
|
||||
index = self.child.expect([pexpect.EOF, pexpect.TIMEOUT])
|
||||
self.assertTrue(index == 0, "lldb didn't close on successful batch completion.")
|
||||
|
||||
def closeVictim(self):
|
||||
if self.victim != None:
|
||||
self.victim.close()
|
||||
self.victim = None
|
||||
|
||||
@skipIfRemote # test not remote-ready llvm.org/pr24813
|
||||
@expectedFlakeyFreeBSD("llvm.org/pr25172 fails rarely on the buildbot")
|
||||
@expectedFlakeyLinux("llvm.org/pr25172")
|
||||
@expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows")
|
||||
def test_batch_mode_attach_exit (self):
|
||||
"""Test that the lldb driver's batch mode works correctly."""
|
||||
self.build()
|
||||
self.setTearDownCleanup()
|
||||
|
||||
import pexpect
|
||||
exe = os.path.join(os.getcwd(), "a.out")
|
||||
prompt = "(lldb) "
|
||||
|
||||
# Finally, start up the process by hand, attach to it, and wait for its completion.
|
||||
# Attach is funny, since it looks like it stops with a signal on most Unixen so
|
||||
# care must be taken not to treat that as a reason to exit batch mode.
|
||||
|
||||
# Start up the process by hand and wait for it to get to the wait loop.
|
||||
|
||||
self.victim = pexpect.spawn('%s WAIT' %(exe))
|
||||
if self.victim == None:
|
||||
self.fail("Could not spawn ", exe, ".")
|
||||
|
||||
self.addTearDownHook (self.closeVictim)
|
||||
|
||||
if self.TraceOn():
|
||||
self.victim.logfile_read = sys.stdout
|
||||
|
||||
self.victim.expect("PID: ([0-9]+) END")
|
||||
if self.victim.match == None:
|
||||
self.fail("Couldn't get the target PID.")
|
||||
|
||||
victim_pid = int(self.victim.match.group(1))
|
||||
|
||||
self.victim.expect("Waiting")
|
||||
|
||||
run_commands = ' -b -o "process attach -p %d" -o "breakpoint set --file %s -p \'Stop here to unset keep_waiting\' -N keep_waiting" -o "continue" -o "break delete keep_waiting" -o "expr keep_waiting = 0" -o "continue" ' % (victim_pid, self.source)
|
||||
self.child = pexpect.spawn('%s %s %s %s' % (lldbtest_config.lldbExec, self.lldbOption, run_commands, exe))
|
||||
|
||||
child = self.child
|
||||
# Turn on logging for what the child sends back.
|
||||
if self.TraceOn():
|
||||
child.logfile_read = sys.stdout
|
||||
|
||||
# We should see the "run":
|
||||
self.expect_string ("attach")
|
||||
|
||||
self.expect_string(prompt + "continue")
|
||||
|
||||
self.expect_string(prompt + "continue")
|
||||
|
||||
# Then we should see the process exit:
|
||||
self.expect_string ("Process %d exited with status"%(victim_pid))
|
||||
|
||||
victim_index = self.victim.expect([pexpect.EOF, pexpect.TIMEOUT])
|
||||
self.assertTrue(victim_index == 0, "Victim didn't really exit.")
|
||||
|
||||
index = self.child.expect([pexpect.EOF, pexpect.TIMEOUT])
|
||||
self.assertTrue(index == 0, "lldb didn't close on successful batch completion.")
|
||||
|
||||
|
||||
|
@ -1,10 +1,36 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
if (argc >= 2 && strcmp (argv[1], "CRASH") == 0)
|
||||
lldb_enable_attach();
|
||||
|
||||
int do_crash = 0;
|
||||
int do_wait = 0;
|
||||
|
||||
int idx;
|
||||
for (idx = 1; idx < argc; idx++)
|
||||
{
|
||||
if (strcmp(argv[idx], "CRASH") == 0)
|
||||
do_crash = 1;
|
||||
if (strcmp(argv[idx], "WAIT") == 0)
|
||||
do_wait = 1;
|
||||
}
|
||||
printf("PID: %d END\n", getpid());
|
||||
|
||||
if (do_wait)
|
||||
{
|
||||
int keep_waiting = 1;
|
||||
while (keep_waiting)
|
||||
{
|
||||
printf ("Waiting\n");
|
||||
sleep(1); // Stop here to unset keep_waiting
|
||||
}
|
||||
}
|
||||
|
||||
if (do_crash)
|
||||
{
|
||||
char *touch_me_not = (char *) 0;
|
||||
printf ("About to crash.\n");
|
||||
|
@ -4,10 +4,6 @@
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
|
||||
volatile bool debugger_flag = true; // The debugger will flip this to false
|
||||
|
||||
void *start(void *data)
|
||||
@ -25,18 +21,7 @@ void *start(void *data)
|
||||
|
||||
int main(int argc, char const *argv[])
|
||||
{
|
||||
#if defined(__linux__)
|
||||
// Immediately enable any ptracer so that we can allow the stub attach
|
||||
// operation to succeed. Some Linux kernels are locked down so that
|
||||
// only an ancestor process can be a ptracer of a process. This disables that
|
||||
// restriction. Without it, attach-related stub tests will fail.
|
||||
#if defined(PR_SET_PTRACER) && defined(PR_SET_PTRACER_ANY)
|
||||
// For now we execute on best effort basis. If this fails for
|
||||
// some reason, so be it.
|
||||
const int prctl_result = prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0);
|
||||
static_cast<void> (prctl_result);
|
||||
#endif
|
||||
#endif
|
||||
lldb_enable_attach();
|
||||
|
||||
static const size_t nthreads = 16;
|
||||
std::thread threads[nthreads];
|
||||
|
@ -0,0 +1,5 @@
|
||||
LEVEL = ../../../make
|
||||
|
||||
C_SOURCES := main.c
|
||||
|
||||
include $(LEVEL)/Makefile.rules
|
@ -0,0 +1,51 @@
|
||||
"""
|
||||
Test embedded breakpoints, like `asm int 3;` in x86 or or `__debugbreak` on Windows.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import lldb
|
||||
from lldbsuite.test.lldbtest import *
|
||||
import lldbsuite.test.lldbutil as lldbutil
|
||||
|
||||
class DebugBreakTestCase(TestBase):
|
||||
|
||||
mydir = TestBase.compute_mydir(__file__)
|
||||
|
||||
@skipIf(archs=not_in(["i386", "i686"]))
|
||||
@no_debug_info_test
|
||||
def test_asm_int_3(self):
|
||||
"""Test that intrinsics like `__debugbreak();` and `asm {"int3"}` are treated like breakpoints."""
|
||||
self.build()
|
||||
exe = os.path.join(os.getcwd(), "a.out")
|
||||
|
||||
# Run the program.
|
||||
target = self.dbg.CreateTarget(exe)
|
||||
process = target.LaunchSimple(None, None, self.get_process_working_directory())
|
||||
|
||||
# We've hit the first stop, so grab the frame.
|
||||
self.assertEqual(process.GetState(), lldb.eStateStopped)
|
||||
thread = process.GetThreadAtIndex(0)
|
||||
frame = thread.GetFrameAtIndex(0)
|
||||
|
||||
# We should be in funciton 'bar'.
|
||||
self.assertTrue(frame.IsValid())
|
||||
function_name = frame.GetFunctionName()
|
||||
self.assertTrue('bar' in function_name)
|
||||
|
||||
# We should be able to evaluate the parameter foo.
|
||||
value = frame.EvaluateExpression('*foo')
|
||||
self.assertEqual(value.GetValueAsSigned(), 42)
|
||||
|
||||
# The counter should be 1 at the first stop and increase by 2 for each
|
||||
# subsequent stop.
|
||||
counter = 1
|
||||
while counter < 20:
|
||||
value = frame.EvaluateExpression('count')
|
||||
self.assertEqual(value.GetValueAsSigned(), counter)
|
||||
counter += 2
|
||||
process.Continue()
|
||||
|
||||
# The inferior should exit after the last iteration.
|
||||
self.assertEqual(process.GetState(), lldb.eStateExited)
|
@ -0,0 +1,29 @@
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#define BREAKPOINT_INTRINSIC() __debugbreak()
|
||||
#else
|
||||
#define BREAKPOINT_INTRINSIC() __asm__ __volatile__ ("int3")
|
||||
#endif
|
||||
|
||||
int
|
||||
bar(int const *foo)
|
||||
{
|
||||
int count = 0, i = 0;
|
||||
for (; i < 10; ++i)
|
||||
{
|
||||
count += 1;
|
||||
BREAKPOINT_INTRINSIC();
|
||||
count += 1;
|
||||
}
|
||||
return *foo;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int foo = 42;
|
||||
bar(&foo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,6 @@ def setUp(self):
|
||||
# Find the line number to break at.
|
||||
self.line = line_number('main.cpp', '// break here')
|
||||
|
||||
@expectedFailureWindows("llvm.org/pr24462") # Data formatters have problems on Windows
|
||||
def test_with_run_command(self):
|
||||
"""Test that LLDB correctly cleans caches when language categories change."""
|
||||
# This is the function to remove the custom formats in order to have a
|
||||
|
@ -16,6 +16,7 @@ class AssertingInferiorTestCase(TestBase):
|
||||
|
||||
@expectedFailureWindows("llvm.org/pr21793: need to implement support for detecting assertion / abort on Windows")
|
||||
@expectedFailurei386("llvm.org/pr25338")
|
||||
@expectedFailureLinux("llvm.org/pr25338", archs=['arm', 'i386'])
|
||||
def test_inferior_asserting(self):
|
||||
"""Test that lldb reliably catches the inferior asserting (command)."""
|
||||
self.build()
|
||||
@ -30,6 +31,7 @@ def test_inferior_asserting_register(self):
|
||||
|
||||
@expectedFailureWindows("llvm.org/pr21793: need to implement support for detecting assertion / abort on Windows")
|
||||
@expectedFailurei386("llvm.org/pr25338")
|
||||
@expectedFailureLinux("llvm.org/pr25338", archs=['arm', 'i386'])
|
||||
def test_inferior_asserting_disassemble(self):
|
||||
"""Test that lldb reliably disassembles frames after asserting (command)."""
|
||||
self.build()
|
||||
@ -44,6 +46,7 @@ def test_inferior_asserting_python(self):
|
||||
|
||||
@expectedFailureWindows("llvm.org/pr21793: need to implement support for detecting assertion / abort on Windows")
|
||||
@expectedFailurei386("llvm.org/pr25338")
|
||||
@expectedFailureLinux("llvm.org/pr25338", archs=['arm', 'i386'])
|
||||
def test_inferior_asserting_expr(self):
|
||||
"""Test that the lldb expression interpreter can read from the inferior after asserting (command)."""
|
||||
self.build()
|
||||
@ -51,6 +54,7 @@ def test_inferior_asserting_expr(self):
|
||||
|
||||
@expectedFailureWindows("llvm.org/pr21793: need to implement support for detecting assertion / abort on Windows")
|
||||
@expectedFailurei386("llvm.org/pr25338")
|
||||
@expectedFailureLinux("llvm.org/pr25338", archs=['arm', 'i386'])
|
||||
def test_inferior_asserting_step(self):
|
||||
"""Test that lldb functions correctly after stepping through a call to assert()."""
|
||||
self.build()
|
||||
|
@ -16,7 +16,6 @@ class TestInlineStepping(TestBase):
|
||||
@add_test_categories(['pyapi'])
|
||||
@expectedFailureFreeBSD('llvm.org/pr17214')
|
||||
@expectedFailureIcc # Not really a bug. ICC combines two inlined functions.
|
||||
@expectedFailureWindows("llvm.org/pr24778")
|
||||
# failed 1/365 dosep runs, (i386-clang), TestInlineStepping.py:237 failed to stop at first breakpoint in main
|
||||
@expectedFailureAll(oslist=["linux"], archs=["i386"])
|
||||
def test_with_python_api(self):
|
||||
|
@ -3,7 +3,7 @@
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from six import iteritems
|
||||
|
||||
|
||||
import lldb
|
||||
@ -83,8 +83,8 @@ def test_deeper_stack_in_mini_dump(self):
|
||||
thread = process.GetThreadAtIndex(0)
|
||||
|
||||
expected_stack = { 0: 'bar', 1: 'foo', 2: 'main' }
|
||||
self.assertEqual(thread.GetNumFrames(), len(expected_stack))
|
||||
for index, name in expected_stack.iteritems():
|
||||
self.assertGreaterEqual(thread.GetNumFrames(), len(expected_stack))
|
||||
for index, name in iteritems(expected_stack):
|
||||
frame = thread.GetFrameAtIndex(index)
|
||||
self.assertTrue(frame.IsValid())
|
||||
function_name = frame.GetFunctionName()
|
||||
|
@ -1,28 +1,11 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
int main(int argc, char const *argv[]) {
|
||||
int temp;
|
||||
#if defined(__linux__)
|
||||
// Immediately enable any ptracer so that we can allow the stub attach
|
||||
// operation to succeed. Some Linux kernels are locked down so that
|
||||
// only an ancestor process can be a ptracer of a process. This disables that
|
||||
// restriction. Without it, attach-related stub tests will fail.
|
||||
#if defined(PR_SET_PTRACER) && defined(PR_SET_PTRACER_ANY)
|
||||
int prctl_result;
|
||||
|
||||
// For now we execute on best effort basis. If this fails for
|
||||
// some reason, so be it.
|
||||
prctl_result = prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0);
|
||||
(void) prctl_result;
|
||||
#endif
|
||||
#endif
|
||||
lldb_enable_attach();
|
||||
|
||||
// Waiting to be attached by the debugger.
|
||||
temp = 0;
|
||||
|
@ -2,10 +2,6 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
|
||||
volatile int release_child_flag = 0;
|
||||
|
||||
int main(int argc, char const *argv[])
|
||||
@ -61,18 +57,7 @@ int main(int argc, char const *argv[])
|
||||
}
|
||||
else
|
||||
{ // child
|
||||
#if defined(__linux__)
|
||||
// Immediately enable any ptracer so that we can allow the stub attach
|
||||
// operation to succeed. Some Linux kernels are locked down so that
|
||||
// only an ancestor process can be a ptracer of a process. This disables that
|
||||
// restriction. Without it, attach-related stub tests will fail.
|
||||
#if defined(PR_SET_PTRACER) && defined(PR_SET_PTRACER_ANY)
|
||||
// For now we execute on best effort basis. If this fails for
|
||||
// some reason, so be it.
|
||||
const int prctl_result = prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0);
|
||||
(void) prctl_result;
|
||||
#endif
|
||||
#endif
|
||||
lldb_enable_attach();
|
||||
|
||||
while (! release_child_flag) // Wait for debugger to attach
|
||||
sleep(1);
|
||||
|
@ -8,10 +8,6 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
@ -19,18 +15,7 @@ long double outermost_return_long_double (long double my_long_double);
|
||||
|
||||
int main (int argc, char const *argv[])
|
||||
{
|
||||
#if defined(__linux__)
|
||||
// Immediately enable any ptracer so that we can allow the stub attach
|
||||
// operation to succeed. Some Linux kernels are locked down so that
|
||||
// only an ancestor process can be a ptracer of a process. This disables that
|
||||
// restriction. Without it, attach-related stub tests will fail.
|
||||
#if defined(PR_SET_PTRACER) && defined(PR_SET_PTRACER_ANY)
|
||||
// For now we execute on best effort basis. If this fails for
|
||||
// some reason, so be it.
|
||||
const int prctl_result = prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0);
|
||||
static_cast<void> (prctl_result);
|
||||
#endif
|
||||
#endif
|
||||
lldb_enable_attach();
|
||||
|
||||
char my_string[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 0};
|
||||
double my_double = 1234.5678;
|
||||
|
@ -4,10 +4,6 @@
|
||||
|
||||
using std::chrono::microseconds;
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
|
||||
volatile int g_thread_2_continuing = 0;
|
||||
|
||||
void *
|
||||
@ -42,20 +38,7 @@ thread_2_func (void *input)
|
||||
|
||||
int main(int argc, char const *argv[])
|
||||
{
|
||||
#if defined(__linux__)
|
||||
// Immediately enable any ptracer so that we can allow the stub attach
|
||||
// operation to succeed. Some Linux kernels are locked down so that
|
||||
// only an ancestor process can be a ptracer of a process. This disables that
|
||||
// restriction. Without it, attach-related stub tests will fail.
|
||||
#if defined(PR_SET_PTRACER) && defined(PR_SET_PTRACER_ANY)
|
||||
int prctl_result;
|
||||
|
||||
// For now we execute on best effort basis. If this fails for
|
||||
// some reason, so be it.
|
||||
prctl_result = prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0);
|
||||
(void) prctl_result;
|
||||
#endif
|
||||
#endif
|
||||
lldb_enable_attach();
|
||||
|
||||
// Create a new thread
|
||||
std::thread thread_1(thread_1_func, nullptr);
|
||||
|
@ -33,9 +33,7 @@ def test(self):
|
||||
|
||||
# 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'])
|
||||
substrs=['stopped', 'main.cpp:{}'.format(self.mark3), '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'
|
||||
|
@ -19,7 +19,6 @@ class ThreadStateTestCase(TestBase):
|
||||
@expectedFailureDarwin("rdar://15367566")
|
||||
@expectedFailureFreeBSD('llvm.org/pr15824')
|
||||
@expectedFailureLinux("llvm.org/pr15824") # thread states not properly maintained
|
||||
@expectedFailureWindows("llvm.org/pr24668") # Breakpoints not resolved correctly
|
||||
def test_state_after_breakpoint(self):
|
||||
"""Test thread state after breakpoint."""
|
||||
self.build(dictionary=self.getBuildFlags(use_cpp11=False))
|
||||
|
@ -20,7 +20,6 @@ class ThreadSpecificBreakPlusConditionTestCase(TestBase):
|
||||
@skipIfFreeBSD # test frequently times out or hangs
|
||||
@expectedFailureFreeBSD('llvm.org/pr18522') # hits break in another thread in testrun
|
||||
@add_test_categories(['pyapi'])
|
||||
@expectedFailureWindows # Thread specific breakpoints cause the inferior to crash.
|
||||
@expectedFlakeyLinux # this test fails 6/100 dosep runs
|
||||
def test_python(self):
|
||||
"""Test that we obey thread conditioned breakpoints."""
|
||||
|
@ -14,7 +14,7 @@
|
||||
class NoreturnUnwind(TestBase):
|
||||
mydir = TestBase.compute_mydir(__file__)
|
||||
|
||||
@expectedFailurei386 #xfail to get buildbot green, failing config: i386 binary running on ubuntu 14.04 x86_64
|
||||
@expectedFailurei386("llvm.org/pr25338")
|
||||
@skipIfWindows # clang-cl does not support gcc style attributes.
|
||||
def test (self):
|
||||
"""Test that we can backtrace correctly with 'noreturn' functions on the stack"""
|
||||
|
@ -17,6 +17,7 @@ def getCategories(self):
|
||||
return ['basic_process']
|
||||
|
||||
@expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported
|
||||
@expectedFailureLinux(bugnumber="llvm.org/pr26031", archs=['arm'])
|
||||
@expectedFailureWindows("llvm.org/pr24446")
|
||||
def test(self):
|
||||
"""Test stepping over watchpoints."""
|
||||
|
@ -27,6 +27,7 @@ def setUp(self):
|
||||
# Build dictionary to have unique executable names for each test method.
|
||||
|
||||
@expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported
|
||||
@expectedFailureLinux(bugnumber="llvm.org/pr26031", archs=['arm'])
|
||||
@expectedFailureWindows("llvm.org/pr24446") # WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows
|
||||
def test_watchlocation_using_watchpoint_set(self):
|
||||
"""Test watching a location with 'watchpoint set expression -w write -s size' option."""
|
||||
|
@ -0,0 +1,6 @@
|
||||
LEVEL = ../../../make
|
||||
|
||||
OBJC_SOURCES := main.m
|
||||
LDFLAGS = $(CFLAGS) -lobjc -framework Foundation
|
||||
|
||||
include $(LEVEL)/Makefile.rules
|
@ -0,0 +1,53 @@
|
||||
"""Test that a global ObjC object found before the process is started updates correctly."""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
|
||||
|
||||
import os, time
|
||||
import lldb
|
||||
import lldbsuite.test.lldbutil as lldbutil
|
||||
from lldbsuite.test.lldbtest import *
|
||||
|
||||
class TestObjCGlobalVar(TestBase):
|
||||
|
||||
mydir = TestBase.compute_mydir(__file__)
|
||||
|
||||
def setUp(self):
|
||||
# Call super's setUp().
|
||||
TestBase.setUp(self)
|
||||
self.main_source = lldb.SBFileSpec("main.m")
|
||||
|
||||
@skipUnlessDarwin
|
||||
@add_test_categories(['pyapi'])
|
||||
def test_with_python_api(self):
|
||||
"""Test that a global ObjC object found before the process is started updates correctly."""
|
||||
self.build()
|
||||
exe = os.path.join(os.getcwd(), "a.out")
|
||||
|
||||
target = self.dbg.CreateTarget(exe)
|
||||
self.assertTrue(target, VALID_TARGET)
|
||||
|
||||
bkpt = target.BreakpointCreateBySourceRegex ('NSLog', self.main_source)
|
||||
self.assertTrue(bkpt, VALID_BREAKPOINT)
|
||||
|
||||
# Before we launch, make an SBValue for our global object pointer:
|
||||
g_obj_ptr = target.FindFirstGlobalVariable("g_obj_ptr")
|
||||
self.assertTrue(g_obj_ptr.GetError().Success(), "Made the g_obj_ptr")
|
||||
self.assertTrue(g_obj_ptr.GetValueAsUnsigned(10) == 0, "g_obj_ptr is initially null")
|
||||
|
||||
# Now launch the process, and do not stop at entry point.
|
||||
process = target.LaunchSimple (None, None, self.get_process_working_directory())
|
||||
|
||||
self.assertTrue(process, PROCESS_IS_VALID)
|
||||
|
||||
# The stop reason of the thread should be breakpoint.
|
||||
threads = lldbutil.get_threads_stopped_at_breakpoint (process, bkpt)
|
||||
if len(threads) != 1:
|
||||
self.fail ("Failed to stop at breakpoint 1.")
|
||||
|
||||
thread = threads[0]
|
||||
|
||||
dyn_value = g_obj_ptr.GetDynamicValue(lldb.eDynamicCanRunTarget)
|
||||
self.assertTrue(dyn_value.GetError().Success(), "Dynamic value is valid")
|
||||
self.assertTrue(dyn_value.GetObjectDescription() == "Some NSString")
|
11
packages/Python/lldbsuite/test/lang/objc/global_ptrs/main.m
Normal file
11
packages/Python/lldbsuite/test/lang/objc/global_ptrs/main.m
Normal file
@ -0,0 +1,11 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
id g_obj_ptr = nil;
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
g_obj_ptr = @"Some NSString";
|
||||
NSLog(@"My string was %@.", g_obj_ptr);
|
||||
return 0;
|
||||
}
|
@ -3,7 +3,6 @@
|
||||
|
||||
# System modules
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Third-party modules
|
||||
|
||||
@ -23,6 +22,7 @@ def source_type(filename):
|
||||
'.mm' : 'OBJCXX_SOURCES'
|
||||
}.get(extension, None)
|
||||
|
||||
|
||||
class CommandParser:
|
||||
def __init__(self):
|
||||
self.breakpoints = []
|
||||
@ -116,11 +116,11 @@ def BuildMakefile(self):
|
||||
if ('CXX_SOURCES' in list(categories.keys())):
|
||||
makefile.write("CXXFLAGS += -std=c++11\n")
|
||||
|
||||
makefile.write("\ncleanup:\n\trm -f Makefile *.d\n\n")
|
||||
makefile.write("include $(LEVEL)/Makefile.rules\n")
|
||||
makefile.flush()
|
||||
makefile.close()
|
||||
|
||||
|
||||
@skipUnlessDarwin
|
||||
def __test_with_dsym(self):
|
||||
self.using_dsym = True
|
||||
|
@ -696,10 +696,11 @@ def expectedFailurei386(bugnumber=None):
|
||||
def expectedFailurex86_64(bugnumber=None):
|
||||
return expectedFailureArch('x86_64', bugnumber)
|
||||
|
||||
def expectedFailureOS(oslist, bugnumber=None, compilers=None, debug_info=None):
|
||||
def expectedFailureOS(oslist, bugnumber=None, compilers=None, debug_info=None, archs=None):
|
||||
def fn(self):
|
||||
return (self.getPlatform() in oslist and
|
||||
self.expectedCompiler(compilers) and
|
||||
(archs is None or self.getArchitecture() in archs) and
|
||||
(debug_info is None or self.debug_info in debug_info))
|
||||
return expectedFailure(fn, bugnumber)
|
||||
|
||||
@ -716,8 +717,8 @@ def expectedFailureDarwin(bugnumber=None, compilers=None, debug_info=None):
|
||||
def expectedFailureFreeBSD(bugnumber=None, compilers=None, debug_info=None):
|
||||
return expectedFailureOS(['freebsd'], bugnumber, compilers, debug_info=debug_info)
|
||||
|
||||
def expectedFailureLinux(bugnumber=None, compilers=None, debug_info=None):
|
||||
return expectedFailureOS(['linux'], bugnumber, compilers, debug_info=debug_info)
|
||||
def expectedFailureLinux(bugnumber=None, compilers=None, debug_info=None, archs=None):
|
||||
return expectedFailureOS(['linux'], bugnumber, compilers, debug_info=debug_info, archs=archs)
|
||||
|
||||
def expectedFailureNetBSD(bugnumber=None, compilers=None, debug_info=None):
|
||||
return expectedFailureOS(['netbsd'], bugnumber, compilers, debug_info=debug_info)
|
||||
|
@ -17,3 +17,28 @@
|
||||
// declared. This may not be necessary after MSVC 12.
|
||||
#include <eh.h>
|
||||
#endif
|
||||
|
||||
|
||||
// On some systems (e.g., some versions of linux) it is not possible to attach to a process
|
||||
// without it giving us special permissions. This defines the lldb_enable_attach macro, which
|
||||
// should perform any such actions, if needed by the platform. This is a macro instead of a
|
||||
// function to avoid the need for complex linking of the test programs.
|
||||
#if defined(__linux__)
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#if defined(PR_SET_PTRACER) && defined(PR_SET_PTRACER_ANY)
|
||||
// For now we execute on best effort basis. If this fails for some reason, so be it.
|
||||
#define lldb_enable_attach() \
|
||||
do \
|
||||
{ \
|
||||
const int prctl_result = prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0); \
|
||||
(void)prctl_result; \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
#else // not linux
|
||||
|
||||
#define lldb_enable_attach()
|
||||
|
||||
#endif
|
||||
|
@ -21,11 +21,18 @@ class DisassembleRawDataTestCase(TestBase):
|
||||
def test_disassemble_raw_data(self):
|
||||
"""Test disassembling raw bytes with the API."""
|
||||
# Create a target from the debugger.
|
||||
arch = self.getArchitecture()
|
||||
if re.match("mips*el",arch):
|
||||
target = self.dbg.CreateTargetWithFileAndTargetTriple ("", "mipsel")
|
||||
raw_bytes = bytearray([0x21,0xf0, 0xa0, 0x03])
|
||||
elif re.match("mips",arch):
|
||||
target = self.dbg.CreateTargetWithFileAndTargetTriple ("", "mips")
|
||||
raw_bytes = bytearray([0x03,0xa0, 0xf0, 0x21])
|
||||
else:
|
||||
target = self.dbg.CreateTargetWithFileAndTargetTriple ("", "x86_64")
|
||||
self.assertTrue(target, VALID_TARGET)
|
||||
|
||||
raw_bytes = bytearray([0x48, 0x89, 0xe5])
|
||||
|
||||
self.assertTrue(target, VALID_TARGET)
|
||||
insts = target.GetInstructions(lldb.SBAddress(0, target), raw_bytes)
|
||||
|
||||
inst = insts.GetInstructionAtIndex(0)
|
||||
@ -34,6 +41,9 @@ def test_disassemble_raw_data(self):
|
||||
print()
|
||||
print("Raw bytes: ", [hex(x) for x in raw_bytes])
|
||||
print("Disassembled%s" % str(inst))
|
||||
|
||||
if re.match("mips",arch):
|
||||
self.assertTrue (inst.GetMnemonic(target) == "move")
|
||||
self.assertTrue (inst.GetOperands(target) == '$' + "fp, " + '$' + "sp")
|
||||
else:
|
||||
self.assertTrue (inst.GetMnemonic(target) == "movq")
|
||||
self.assertTrue (inst.GetOperands(target) == '%' + "rsp, " + '%' + "rbp")
|
||||
|
@ -13,6 +13,7 @@
|
||||
from lldbsuite.test.lldbtest import *
|
||||
|
||||
@skipIfDarwin # llvm.org/pr25924, sometimes generating SIGSEGV
|
||||
@skipIfLinux # llvm.org/pr25924, sometimes generating SIGSEGV
|
||||
class EventAPITestCase(TestBase):
|
||||
|
||||
mydir = TestBase.compute_mydir(__file__)
|
||||
@ -25,7 +26,6 @@ def setUp(self):
|
||||
|
||||
@add_test_categories(['pyapi'])
|
||||
@expectedFailureLinux("llvm.org/pr23730") # Flaky, fails ~1/10 cases
|
||||
@skipIfLinux # skip to avoid crashes
|
||||
def test_listen_for_and_print_event(self):
|
||||
"""Exercise SBEvent API."""
|
||||
self.build()
|
||||
@ -176,7 +176,7 @@ def run(self):
|
||||
|
||||
@skipIfFreeBSD # llvm.org/pr21325
|
||||
@add_test_categories(['pyapi'])
|
||||
@expectedFlakeyLinux("llvm.org/pr23617") # Flaky, fails ~1/10 cases
|
||||
@expectedFailureLinux("llvm.org/pr23617") # Flaky, fails ~1/10 cases
|
||||
@expectedFailureWindows("llvm.org/pr24778")
|
||||
def test_add_listener_to_broadcaster(self):
|
||||
"""Exercise some SBBroadcaster APIs."""
|
||||
|
@ -1,25 +1,8 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
|
||||
int main(int argc, char const *argv[]) {
|
||||
|
||||
#if defined(__linux__)
|
||||
// Immediately enable any ptracer so that we can allow the stub attach
|
||||
// operation to succeed. Some Linux kernels are locked down so that
|
||||
// only an ancestor process can be a ptracer of a process. This disables that
|
||||
// restriction. Without it, attach-related stub tests will fail.
|
||||
#if defined(PR_SET_PTRACER) && defined(PR_SET_PTRACER_ANY)
|
||||
int prctl_result;
|
||||
|
||||
// For now we execute on best effort basis. If this fails for
|
||||
// some reason, so be it.
|
||||
prctl_result = prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0);
|
||||
(void) prctl_result;
|
||||
#endif
|
||||
#endif
|
||||
int main(int argc, char const *argv[])
|
||||
{
|
||||
lldb_enable_attach();
|
||||
|
||||
printf("Hello world.\n"); // Set break point at this line.
|
||||
if (argc == 1)
|
||||
|
@ -16,6 +16,8 @@ class ModuleAndSectionAPIsTestCase(TestBase):
|
||||
|
||||
mydir = TestBase.compute_mydir(__file__)
|
||||
|
||||
# Py3 asserts due to a bug in SWIG. A fix for this was upstreamed into SWIG 3.0.8.
|
||||
@skipIf(py_version=['>=', (3,0)], swig_version=['<', (3,0,8)])
|
||||
@add_test_categories(['pyapi'])
|
||||
def test_module_and_section(self):
|
||||
"""Test module and section APIs."""
|
||||
|
@ -28,6 +28,7 @@ def setUp(self):
|
||||
|
||||
@expectedFailureWindows("llvm.org/pr24772")
|
||||
@add_test_categories(['pyapi'])
|
||||
@expectedFlakeyLinux("llvm.org/pr25652")
|
||||
def test_change_value(self):
|
||||
"""Exercise the SBValue::SetValueFromCString API."""
|
||||
d = {'EXE': self.exe_name}
|
||||
|
@ -25,6 +25,8 @@ def setUp(self):
|
||||
# Find the line number to break at.
|
||||
self.line = line_number('main.cpp', '// Break at this line')
|
||||
|
||||
# Py3 asserts due to a bug in SWIG. A fix for this was upstreamed into SWIG 3.0.8.
|
||||
@skipIf(py_version=['>=', (3,0)], swig_version=['<', (3,0,8)])
|
||||
@add_test_categories(['pyapi'])
|
||||
def test(self):
|
||||
"""Exercise SBValue API linked_list_iter."""
|
||||
|
@ -44,7 +44,8 @@ def unique_string_match(yourentry, list):
|
||||
|
||||
def is_supported_on_platform(category, platform):
|
||||
if category == "dwo":
|
||||
return platform in ["linux", "freebsd", "windows"]
|
||||
# -gsplit-dwarf is not implemented by clang on Windows.
|
||||
return platform in ["linux", "freebsd"]
|
||||
elif category == "dsym":
|
||||
return platform in ["darwin", "macosx", "ios"]
|
||||
return True
|
||||
|
@ -20,10 +20,6 @@ int pthread_threadid_np(pthread_t,__uint64_t*);
|
||||
#include <sys/syscall.h>
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
|
||||
static const char *const RETVAL_PREFIX = "retval:";
|
||||
static const char *const SLEEP_PREFIX = "sleep:";
|
||||
static const char *const STDERR_PREFIX = "stderr:";
|
||||
@ -210,16 +206,7 @@ thread_func (void *arg)
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
#if defined(__linux__)
|
||||
// Immediately enable any ptracer so that we can allow the stub attach
|
||||
// operation to succeed. Some Linux kernels are locked down so that
|
||||
// only an ancestor can be a ptracer of a process. This disables that
|
||||
// restriction. Without it, attach-related stub tests will fail.
|
||||
#if defined(PR_SET_PTRACER) && defined(PR_SET_PTRACER_ANY)
|
||||
const int prctl_result = prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0);
|
||||
static_cast<void> (prctl_result);
|
||||
#endif
|
||||
#endif
|
||||
lldb_enable_attach();
|
||||
|
||||
std::vector<pthread_t> threads;
|
||||
std::unique_ptr<uint8_t[]> heap_array_up;
|
||||
|
@ -26,14 +26,16 @@
|
||||
}
|
||||
|
||||
%typemap(in) lldb::tid_t {
|
||||
if (PyInt_Check($input))
|
||||
$1 = PyInt_AsLong($input);
|
||||
else if (PyLong_Check($input))
|
||||
$1 = PyLong_AsLongLong($input);
|
||||
using namespace lldb_private;
|
||||
if (PythonInteger::Check($input))
|
||||
{
|
||||
PythonInteger py_int(PyRefType::Borrowed, $input);
|
||||
$1 = static_cast<lldb::tid_t>(py_int.GetInteger());
|
||||
}
|
||||
else
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "Expecting an integer");
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -550,6 +550,7 @@ class CommandObjectProcessAttach : public CommandObjectProcessLaunchOrAttach
|
||||
result.AppendMessage(stream.GetData());
|
||||
result.SetStatus (eReturnStatusSuccessFinishNoResult);
|
||||
result.SetDidChangeProcessState (true);
|
||||
result.SetAbnormalStopWasExpected(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include "lldb/Core/StreamString.h"
|
||||
#include "lldb/Host/FileSpec.h"
|
||||
#include "lldb/Core/Log.h"
|
||||
#include "lldb/Core/StreamString.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
@ -409,13 +409,13 @@ HostInfoBase::ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_6
|
||||
arch_32.SetTriple(triple);
|
||||
break;
|
||||
|
||||
case llvm::Triple::aarch64:
|
||||
case llvm::Triple::ppc64:
|
||||
case llvm::Triple::x86_64:
|
||||
arch_64.SetTriple(triple);
|
||||
arch_32.SetTriple(triple.get32BitArchVariant());
|
||||
break;
|
||||
|
||||
case llvm::Triple::aarch64:
|
||||
case llvm::Triple::mips64:
|
||||
case llvm::Triple::mips64el:
|
||||
case llvm::Triple::sparcv9:
|
||||
|
@ -3038,7 +3038,10 @@ CommandInterpreter::IOHandlerInputComplete (IOHandler &io_handler, std::string &
|
||||
for (ThreadSP thread_sp : process_sp->GetThreadList().Threads())
|
||||
{
|
||||
StopReason reason = thread_sp->GetStopReason();
|
||||
if (reason == eStopReasonSignal || reason == eStopReasonException || reason == eStopReasonInstrumentation)
|
||||
if ((reason == eStopReasonSignal
|
||||
|| reason == eStopReasonException
|
||||
|| reason == eStopReasonInstrumentation)
|
||||
&& !result.GetAbnormalStopWasExpected())
|
||||
{
|
||||
should_stop = true;
|
||||
break;
|
||||
|
@ -47,7 +47,8 @@ CommandReturnObject::CommandReturnObject () :
|
||||
m_err_stream (),
|
||||
m_status (eReturnStatusStarted),
|
||||
m_did_change_process_state (false),
|
||||
m_interactive (true)
|
||||
m_interactive (true),
|
||||
m_abnormal_stop_was_expected(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -206,6 +206,7 @@ ABISysV_mips64::PrepareTrivialCall (Thread &thread,
|
||||
const RegisterInfo *pc_reg_info = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
|
||||
const RegisterInfo *sp_reg_info = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
|
||||
const RegisterInfo *ra_reg_info = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA);
|
||||
const RegisterInfo *r25_info = reg_ctx->GetRegisterInfoByName("r25", 0);
|
||||
|
||||
if (log)
|
||||
log->Printf("Writing SP: 0x%" PRIx64, (uint64_t)sp);
|
||||
@ -228,6 +229,13 @@ ABISysV_mips64::PrepareTrivialCall (Thread &thread,
|
||||
if (!reg_ctx->WriteRegisterFromUnsigned (pc_reg_info, func_addr))
|
||||
return false;
|
||||
|
||||
if (log)
|
||||
log->Printf("Writing r25: 0x%" PRIx64, (uint64_t)func_addr);
|
||||
|
||||
// All callers of position independent functions must place the address of the called function in t9 (r25)
|
||||
if (!reg_ctx->WriteRegisterFromUnsigned (r25_info, func_addr))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -107,6 +107,7 @@ DYLDRendezvous::DYLDRendezvous(Process *process)
|
||||
m_rendezvous_addr(LLDB_INVALID_ADDRESS),
|
||||
m_current(),
|
||||
m_previous(),
|
||||
m_loaded_modules(),
|
||||
m_soentries(),
|
||||
m_added_soentries(),
|
||||
m_removed_soentries()
|
||||
@ -181,6 +182,9 @@ DYLDRendezvous::Resolve()
|
||||
m_previous = m_current;
|
||||
m_current = info;
|
||||
|
||||
if (UpdateSOEntries (true))
|
||||
return true;
|
||||
|
||||
return UpdateSOEntries();
|
||||
}
|
||||
|
||||
@ -191,18 +195,23 @@ DYLDRendezvous::IsValid()
|
||||
}
|
||||
|
||||
bool
|
||||
DYLDRendezvous::UpdateSOEntries()
|
||||
DYLDRendezvous::UpdateSOEntries(bool fromRemote)
|
||||
{
|
||||
SOEntry entry;
|
||||
LoadedModuleInfoList module_list;
|
||||
|
||||
if (m_current.map_addr == 0)
|
||||
// If we can't get the SO info from the remote, return failure.
|
||||
if (fromRemote && m_process->LoadModules (module_list) == 0)
|
||||
return false;
|
||||
|
||||
if (!fromRemote && m_current.map_addr == 0)
|
||||
return false;
|
||||
|
||||
// When the previous and current states are consistent this is the first
|
||||
// time we have been asked to update. Just take a snapshot of the currently
|
||||
// loaded modules.
|
||||
if (m_previous.state == eConsistent && m_current.state == eConsistent)
|
||||
return TakeSnapshot(m_soentries);
|
||||
return fromRemote ? SaveSOEntriesFromRemote(module_list) : TakeSnapshot(m_soentries);
|
||||
|
||||
// If we are about to add or remove a shared object clear out the current
|
||||
// state and take a snapshot of the currently loaded images.
|
||||
@ -215,6 +224,9 @@ DYLDRendezvous::UpdateSOEntries()
|
||||
return false;
|
||||
|
||||
m_soentries.clear();
|
||||
if (fromRemote)
|
||||
return SaveSOEntriesFromRemote(module_list);
|
||||
|
||||
m_added_soentries.clear();
|
||||
m_removed_soentries.clear();
|
||||
return TakeSnapshot(m_soentries);
|
||||
@ -224,15 +236,133 @@ DYLDRendezvous::UpdateSOEntries()
|
||||
// Otherwise check the previous state to determine what to expect and update
|
||||
// accordingly.
|
||||
if (m_previous.state == eAdd)
|
||||
return UpdateSOEntriesForAddition();
|
||||
return fromRemote ? AddSOEntriesFromRemote(module_list) : AddSOEntries();
|
||||
else if (m_previous.state == eDelete)
|
||||
return UpdateSOEntriesForDeletion();
|
||||
return fromRemote ? RemoveSOEntriesFromRemote(module_list) : RemoveSOEntries();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DYLDRendezvous::UpdateSOEntriesForAddition()
|
||||
DYLDRendezvous::FillSOEntryFromModuleInfo (LoadedModuleInfoList::LoadedModuleInfo const & modInfo,
|
||||
SOEntry &entry)
|
||||
{
|
||||
addr_t link_map_addr;
|
||||
addr_t base_addr;
|
||||
addr_t dyn_addr;
|
||||
std::string name;
|
||||
|
||||
if (!modInfo.get_link_map (link_map_addr) ||
|
||||
!modInfo.get_base (base_addr) ||
|
||||
!modInfo.get_dynamic (dyn_addr) ||
|
||||
!modInfo.get_name (name))
|
||||
return false;
|
||||
|
||||
entry.link_addr = link_map_addr;
|
||||
entry.base_addr = base_addr;
|
||||
entry.dyn_addr = dyn_addr;
|
||||
|
||||
entry.file_spec.SetFile(name, false);
|
||||
|
||||
UpdateBaseAddrIfNecessary(entry, name);
|
||||
|
||||
// not needed if we're using ModuleInfos
|
||||
entry.next = 0;
|
||||
entry.prev = 0;
|
||||
entry.path_addr = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DYLDRendezvous::SaveSOEntriesFromRemote(LoadedModuleInfoList &module_list)
|
||||
{
|
||||
for (auto const & modInfo : module_list.m_list)
|
||||
{
|
||||
SOEntry entry;
|
||||
if (!FillSOEntryFromModuleInfo(modInfo, entry))
|
||||
return false;
|
||||
|
||||
// Only add shared libraries and not the executable.
|
||||
if (!SOEntryIsMainExecutable(entry))
|
||||
m_soentries.push_back(entry);
|
||||
}
|
||||
|
||||
m_loaded_modules = module_list;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool
|
||||
DYLDRendezvous::AddSOEntriesFromRemote(LoadedModuleInfoList &module_list)
|
||||
{
|
||||
for (auto const & modInfo : module_list.m_list)
|
||||
{
|
||||
bool found = false;
|
||||
for (auto const & existing : m_loaded_modules.m_list)
|
||||
{
|
||||
if (modInfo == existing)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
continue;
|
||||
|
||||
SOEntry entry;
|
||||
if (!FillSOEntryFromModuleInfo(modInfo, entry))
|
||||
return false;
|
||||
|
||||
// Only add shared libraries and not the executable.
|
||||
if (!SOEntryIsMainExecutable(entry))
|
||||
m_soentries.push_back(entry);
|
||||
}
|
||||
|
||||
m_loaded_modules = module_list;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DYLDRendezvous::RemoveSOEntriesFromRemote(LoadedModuleInfoList &module_list)
|
||||
{
|
||||
for (auto const & existing : m_loaded_modules.m_list)
|
||||
{
|
||||
bool found = false;
|
||||
for (auto const & modInfo : module_list.m_list)
|
||||
{
|
||||
if (modInfo == existing)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
continue;
|
||||
|
||||
SOEntry entry;
|
||||
if (!FillSOEntryFromModuleInfo(existing, entry))
|
||||
return false;
|
||||
|
||||
// Only add shared libraries and not the executable.
|
||||
if (!SOEntryIsMainExecutable(entry))
|
||||
{
|
||||
auto pos = std::find(m_soentries.begin(), m_soentries.end(), entry);
|
||||
if (pos == m_soentries.end())
|
||||
return false;
|
||||
|
||||
m_soentries.erase(pos);
|
||||
}
|
||||
}
|
||||
|
||||
m_loaded_modules = module_list;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DYLDRendezvous::AddSOEntries()
|
||||
{
|
||||
SOEntry entry;
|
||||
iterator pos;
|
||||
@ -263,7 +393,7 @@ DYLDRendezvous::UpdateSOEntriesForAddition()
|
||||
}
|
||||
|
||||
bool
|
||||
DYLDRendezvous::UpdateSOEntriesForDeletion()
|
||||
DYLDRendezvous::RemoveSOEntries()
|
||||
{
|
||||
SOEntryList entry_list;
|
||||
iterator pos;
|
||||
@ -291,7 +421,8 @@ DYLDRendezvous::SOEntryIsMainExecutable(const SOEntry &entry)
|
||||
// FreeBSD and on Android it is the full path to the executable.
|
||||
|
||||
auto triple = m_process->GetTarget().GetArchitecture().GetTriple();
|
||||
switch (triple.getOS()) {
|
||||
switch (triple.getOS())
|
||||
{
|
||||
case llvm::Triple::FreeBSD:
|
||||
return entry.file_spec == m_exe_file_spec;
|
||||
case llvm::Triple::Linux:
|
||||
@ -386,6 +517,21 @@ isLoadBiasIncorrect(Target& target, const std::string& file_path)
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
DYLDRendezvous::UpdateBaseAddrIfNecessary(SOEntry &entry, std::string const &file_path)
|
||||
{
|
||||
// If the load bias reported by the linker is incorrect then fetch the load address of the file
|
||||
// from the proc file system.
|
||||
if (isLoadBiasIncorrect(m_process->GetTarget(), file_path))
|
||||
{
|
||||
lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
|
||||
bool is_loaded = false;
|
||||
Error error = m_process->GetFileLoadAddress(entry.file_spec, is_loaded, load_addr);
|
||||
if (error.Success() && is_loaded)
|
||||
entry.base_addr = load_addr;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
DYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry)
|
||||
{
|
||||
@ -427,16 +573,7 @@ DYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry)
|
||||
std::string file_path = ReadStringFromMemory(entry.path_addr);
|
||||
entry.file_spec.SetFile(file_path, false);
|
||||
|
||||
// If the load bias reported by the linker is incorrect then fetch the load address of the file
|
||||
// from the proc file system.
|
||||
if (isLoadBiasIncorrect(m_process->GetTarget(), file_path))
|
||||
{
|
||||
lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
|
||||
bool is_loaded = false;
|
||||
Error error = m_process->GetFileLoadAddress(entry.file_spec, is_loaded, load_addr);
|
||||
if (error.Success() && is_loaded)
|
||||
entry.base_addr = load_addr;
|
||||
}
|
||||
UpdateBaseAddrIfNecessary(entry, file_path);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -20,6 +20,10 @@
|
||||
#include "lldb/lldb-types.h"
|
||||
#include "lldb/Host/FileSpec.h"
|
||||
|
||||
#include "lldb/Core/LoadedModuleInfoList.h"
|
||||
|
||||
using lldb_private::LoadedModuleInfoList;
|
||||
|
||||
namespace lldb_private {
|
||||
class Process;
|
||||
}
|
||||
@ -201,6 +205,9 @@ class DYLDRendezvous {
|
||||
Rendezvous m_current;
|
||||
Rendezvous m_previous;
|
||||
|
||||
/// List of currently loaded SO modules
|
||||
LoadedModuleInfoList m_loaded_modules;
|
||||
|
||||
/// List of SOEntry objects corresponding to the current link map state.
|
||||
SOEntryList m_soentries;
|
||||
|
||||
@ -240,13 +247,29 @@ class DYLDRendezvous {
|
||||
/// Updates the current set of SOEntries, the set of added entries, and the
|
||||
/// set of removed entries.
|
||||
bool
|
||||
UpdateSOEntries();
|
||||
UpdateSOEntries(bool fromRemote = false);
|
||||
|
||||
bool
|
||||
UpdateSOEntriesForAddition();
|
||||
FillSOEntryFromModuleInfo (LoadedModuleInfoList::LoadedModuleInfo const & modInfo,
|
||||
SOEntry &entry);
|
||||
|
||||
bool
|
||||
UpdateSOEntriesForDeletion();
|
||||
SaveSOEntriesFromRemote(LoadedModuleInfoList &module_list);
|
||||
|
||||
bool
|
||||
AddSOEntriesFromRemote(LoadedModuleInfoList &module_list);
|
||||
|
||||
bool
|
||||
RemoveSOEntriesFromRemote(LoadedModuleInfoList &module_list);
|
||||
|
||||
bool
|
||||
AddSOEntries();
|
||||
|
||||
bool
|
||||
RemoveSOEntries();
|
||||
|
||||
void
|
||||
UpdateBaseAddrIfNecessary(SOEntry &entry, std::string const &file_path);
|
||||
|
||||
bool
|
||||
SOEntryIsMainExecutable(const SOEntry &entry);
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "lldb/Symbol/Function.h"
|
||||
#include "lldb/Symbol/ObjectFile.h"
|
||||
#include "lldb/Symbol/SymbolContext.h"
|
||||
#include "lldb/Symbol/SymbolFile.h"
|
||||
#include "lldb/Symbol/SymbolVendor.h"
|
||||
#include "lldb/Symbol/Type.h"
|
||||
#include "lldb/Symbol/TypeList.h"
|
||||
@ -570,6 +571,63 @@ FindCodeSymbolInContext
|
||||
}
|
||||
}
|
||||
|
||||
ConstString
|
||||
FindBestAlternateMangledName
|
||||
(
|
||||
const ConstString &demangled,
|
||||
const LanguageType &lang_type,
|
||||
SymbolContext &sym_ctx
|
||||
)
|
||||
{
|
||||
CPlusPlusLanguage::MethodName cpp_name(demangled);
|
||||
std::string scope_qualified_name = cpp_name.GetScopeQualifiedName();
|
||||
|
||||
if (!scope_qualified_name.size())
|
||||
return ConstString();
|
||||
|
||||
if (!sym_ctx.module_sp)
|
||||
return ConstString();
|
||||
|
||||
SymbolVendor *sym_vendor = sym_ctx.module_sp->GetSymbolVendor();
|
||||
if (!sym_vendor)
|
||||
return ConstString();
|
||||
|
||||
lldb_private::SymbolFile *sym_file = sym_vendor->GetSymbolFile();
|
||||
if (!sym_file)
|
||||
return ConstString();
|
||||
|
||||
std::vector<ConstString> alternates;
|
||||
sym_file->GetMangledNamesForFunction(scope_qualified_name, alternates);
|
||||
|
||||
std::vector<ConstString> param_and_qual_matches;
|
||||
std::vector<ConstString> param_matches;
|
||||
for (size_t i = 0; i < alternates.size(); i++)
|
||||
{
|
||||
ConstString alternate_mangled_name = alternates[i];
|
||||
Mangled mangled(alternate_mangled_name, true);
|
||||
ConstString demangled = mangled.GetDemangledName(lang_type);
|
||||
|
||||
CPlusPlusLanguage::MethodName alternate_cpp_name(demangled);
|
||||
if (!cpp_name.IsValid())
|
||||
continue;
|
||||
|
||||
if (alternate_cpp_name.GetArguments() == cpp_name.GetArguments())
|
||||
{
|
||||
if (alternate_cpp_name.GetQualifiers() == cpp_name.GetQualifiers())
|
||||
param_and_qual_matches.push_back(alternate_mangled_name);
|
||||
else
|
||||
param_matches.push_back(alternate_mangled_name);
|
||||
}
|
||||
}
|
||||
|
||||
if (param_and_qual_matches.size())
|
||||
return param_and_qual_matches[0]; // It is assumed that there will be only one!
|
||||
else if (param_matches.size())
|
||||
return param_matches[0]; // Return one of them as a best match
|
||||
else
|
||||
return ConstString();
|
||||
}
|
||||
|
||||
bool
|
||||
ClangExpressionDeclMap::GetFunctionAddress
|
||||
(
|
||||
@ -603,11 +661,20 @@ ClangExpressionDeclMap::GetFunctionAddress
|
||||
if (Language::LanguageIsCPlusPlus(lang_type) &&
|
||||
CPlusPlusLanguage::IsCPPMangledName(name.AsCString()))
|
||||
{
|
||||
// 1. Demangle the name
|
||||
Mangled mangled(name, true);
|
||||
ConstString demangled = mangled.GetDemangledName(lang_type);
|
||||
|
||||
if (demangled)
|
||||
{
|
||||
ConstString best_alternate_mangled_name = FindBestAlternateMangledName(demangled, lang_type, sc);
|
||||
if (best_alternate_mangled_name)
|
||||
{
|
||||
FindCodeSymbolInContext(
|
||||
best_alternate_mangled_name, m_parser_vars->m_sym_ctx, eFunctionNameTypeAuto, sc_list);
|
||||
sc_list_size = sc_list.GetSize();
|
||||
}
|
||||
|
||||
if (sc_list_size == 0)
|
||||
{
|
||||
FindCodeSymbolInContext(
|
||||
demangled, m_parser_vars->m_sym_ctx, eFunctionNameTypeFull, sc_list);
|
||||
@ -616,6 +683,7 @@ ClangExpressionDeclMap::GetFunctionAddress
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sc_list_size == 0)
|
||||
{
|
||||
|
@ -36,7 +36,6 @@
|
||||
#include "lldb/Host/Endian.h"
|
||||
#include "lldb/Symbol/ClangASTContext.h"
|
||||
#include "lldb/Symbol/CompilerType.h"
|
||||
#include "lldb/Target/CPPLanguageRuntime.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
@ -230,36 +229,6 @@ IRForTarget::GetFunctionAddress (llvm::Function *fun,
|
||||
{
|
||||
std::vector<lldb_private::ConstString> alternates;
|
||||
bool found_it = m_decl_map->GetFunctionAddress (name, fun_addr);
|
||||
if (!found_it)
|
||||
{
|
||||
if (log)
|
||||
log->Printf("Address of function \"%s\" not found.\n", name.GetCString());
|
||||
// Check for an alternate mangling for names from the standard library.
|
||||
// For example, "std::basic_string<...>" has an alternate mangling scheme per
|
||||
// the Itanium C++ ABI.
|
||||
lldb::ProcessSP process_sp = m_data_allocator.GetTarget()->GetProcessSP();
|
||||
if (process_sp)
|
||||
{
|
||||
lldb_private::CPPLanguageRuntime *cpp_runtime = process_sp->GetCPPLanguageRuntime();
|
||||
if (cpp_runtime && cpp_runtime->GetAlternateManglings(name, alternates))
|
||||
{
|
||||
for (size_t i = 0; i < alternates.size(); ++i)
|
||||
{
|
||||
const lldb_private::ConstString &alternate_name = alternates[i];
|
||||
if (log)
|
||||
log->Printf("Looking up address of function \"%s\" with alternate name \"%s\"",
|
||||
name.GetCString(), alternate_name.GetCString());
|
||||
if ((found_it = m_decl_map->GetFunctionAddress (alternate_name, fun_addr)))
|
||||
{
|
||||
if (log)
|
||||
log->Printf("Found address of function \"%s\" with alternate name \"%s\"",
|
||||
name.GetCString(), alternate_name.GetCString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_it)
|
||||
{
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -133,122 +133,38 @@ class EmulateInstructionMIPS64 : public lldb_private::EmulateInstruction
|
||||
Emulate_LDST_Reg (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BEQ (llvm::MCInst& insn);
|
||||
Emulate_BXX_3ops (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BNE (llvm::MCInst& insn);
|
||||
Emulate_BXX_3ops_C (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BEQL (llvm::MCInst& insn);
|
||||
Emulate_BXX_2ops (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BNEL (llvm::MCInst& insn);
|
||||
Emulate_BXX_2ops_C (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BGEZALL (llvm::MCInst& insn);
|
||||
Emulate_Bcond_Link_C (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_Bcond_Link (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_FP_branch (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_3D_branch (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BAL (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BGEZAL (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BALC (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BC (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BGEZ (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BLEZALC (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BGEZALC (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BLTZALC (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BGTZALC (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BEQZALC (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BNEZALC (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BEQC (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BNEC (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BLTC (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BGEC (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BLTUC (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BGEUC (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BLTZC (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BLEZC (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BGEZC (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BGTZC (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BEQZC (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BNEZC (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BGEZL (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BGTZ (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BGTZL (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BLEZ (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BLEZL (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BLTZ (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BLTZAL (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BLTZALL (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BLTZL (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BOVC (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BNVC (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_J (llvm::MCInst& insn);
|
||||
|
||||
@ -267,36 +183,12 @@ class EmulateInstructionMIPS64 : public lldb_private::EmulateInstruction
|
||||
bool
|
||||
Emulate_JR (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BC1F (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BC1T (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BC1FL (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BC1TL (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BC1EQZ (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BC1NEZ (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BC1ANY2F (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BC1ANY2T (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BC1ANY4F (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BC1ANY4T (llvm::MCInst& insn);
|
||||
|
||||
bool
|
||||
Emulate_BNZB (llvm::MCInst& insn);
|
||||
|
||||
|
@ -296,6 +296,22 @@ CPlusPlusLanguage::MethodName::GetQualifiers ()
|
||||
return m_qualifiers;
|
||||
}
|
||||
|
||||
std::string
|
||||
CPlusPlusLanguage::MethodName::GetScopeQualifiedName ()
|
||||
{
|
||||
if (!m_parsed)
|
||||
Parse();
|
||||
if (m_basename.empty() || m_context.empty())
|
||||
return std::string();
|
||||
|
||||
std::string res;
|
||||
res += m_context;
|
||||
res += "::";
|
||||
res += m_basename;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool
|
||||
CPlusPlusLanguage::IsCPPMangledName (const char *name)
|
||||
{
|
||||
|
@ -90,6 +90,9 @@ class CPlusPlusLanguage :
|
||||
return m_full;
|
||||
}
|
||||
|
||||
std::string
|
||||
GetScopeQualifiedName ();
|
||||
|
||||
llvm::StringRef
|
||||
GetBasename ();
|
||||
|
||||
|
@ -319,46 +319,6 @@ ItaniumABILanguageRuntime::IsVTableName (const char *name)
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::map<ConstString, std::vector<ConstString> >&
|
||||
GetAlternateManglingPrefixes()
|
||||
{
|
||||
static std::map<ConstString, std::vector<ConstString> > g_alternate_mangling_prefixes;
|
||||
return g_alternate_mangling_prefixes;
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
ItaniumABILanguageRuntime::GetAlternateManglings(const ConstString &mangled, std::vector<ConstString> &alternates)
|
||||
{
|
||||
if (!mangled)
|
||||
return static_cast<size_t>(0);
|
||||
|
||||
alternates.clear();
|
||||
const char *mangled_cstr = mangled.AsCString();
|
||||
std::map<ConstString, std::vector<ConstString> >& alternate_mangling_prefixes = GetAlternateManglingPrefixes();
|
||||
for (std::map<ConstString, std::vector<ConstString> >::iterator it = alternate_mangling_prefixes.begin();
|
||||
it != alternate_mangling_prefixes.end();
|
||||
++it)
|
||||
{
|
||||
const char *prefix_cstr = it->first.AsCString();
|
||||
if (strncmp(mangled_cstr, prefix_cstr, strlen(prefix_cstr)) == 0)
|
||||
{
|
||||
const std::vector<ConstString> &alternate_prefixes = it->second;
|
||||
for (size_t i = 0; i < alternate_prefixes.size(); ++i)
|
||||
{
|
||||
std::string alternate_mangling(alternate_prefixes[i].AsCString());
|
||||
alternate_mangling.append(mangled_cstr + strlen(prefix_cstr));
|
||||
|
||||
alternates.push_back(ConstString(alternate_mangling.c_str()));
|
||||
}
|
||||
|
||||
return alternates.size();
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<size_t>(0);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Static Functions
|
||||
//------------------------------------------------------------------
|
||||
@ -382,17 +342,6 @@ ItaniumABILanguageRuntime::Initialize()
|
||||
PluginManager::RegisterPlugin (GetPluginNameStatic(),
|
||||
"Itanium ABI for the C++ language",
|
||||
CreateInstance);
|
||||
|
||||
// Alternate manglings for std::basic_string<...>
|
||||
std::vector<ConstString> basic_string_alternates;
|
||||
basic_string_alternates.push_back(ConstString("_ZNSs"));
|
||||
basic_string_alternates.push_back(ConstString("_ZNKSs"));
|
||||
std::map<ConstString, std::vector<ConstString> >& alternate_mangling_prefixes = GetAlternateManglingPrefixes();
|
||||
|
||||
alternate_mangling_prefixes[ConstString("_ZNSbIcSt17char_traits<char>St15allocator<char>E")] =
|
||||
basic_string_alternates;
|
||||
alternate_mangling_prefixes[ConstString("_ZNKSbIcSt17char_traits<char>St15allocator<char>E")] =
|
||||
basic_string_alternates;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -80,9 +80,6 @@ namespace lldb_private {
|
||||
lldb::SearchFilterSP
|
||||
CreateExceptionSearchFilter() override;
|
||||
|
||||
size_t
|
||||
GetAlternateManglings(const ConstString &mangled, std::vector<ConstString> &alternates) override;
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// PluginInterface protocol
|
||||
//------------------------------------------------------------------
|
||||
|
@ -69,7 +69,20 @@ AppleObjCRuntime::GetObjectDescription (Stream &str, ValueObject &valobj)
|
||||
if (!valobj.ResolveValue(val.GetScalar()))
|
||||
return false;
|
||||
|
||||
ExecutionContext exe_ctx (valobj.GetExecutionContextRef());
|
||||
// Value Objects may not have a process in their ExecutionContextRef. But we need to have one
|
||||
// in the ref we pass down to eventually call description. Get it from the target if it isn't
|
||||
// present.
|
||||
ExecutionContext exe_ctx;
|
||||
if (valobj.GetProcessSP())
|
||||
{
|
||||
exe_ctx = ExecutionContext(valobj.GetExecutionContextRef());
|
||||
}
|
||||
else
|
||||
{
|
||||
exe_ctx.SetContext(valobj.GetTargetSP(), true);
|
||||
if (!exe_ctx.HasProcessScope())
|
||||
return false;
|
||||
}
|
||||
return GetObjectDescription(str, val, exe_ctx.GetBestExecutionContextScope());
|
||||
|
||||
}
|
||||
|
@ -405,10 +405,19 @@ AppleObjCRuntimeV2::GetDynamicTypeAndAddress (ValueObject &in_value,
|
||||
Address &address,
|
||||
Value::ValueType &value_type)
|
||||
{
|
||||
// The Runtime is attached to a particular process, you shouldn't pass in a value from another process.
|
||||
assert (in_value.GetProcessSP().get() == m_process);
|
||||
// We should never get here with a null process...
|
||||
assert (m_process != NULL);
|
||||
|
||||
// The Runtime is attached to a particular process, you shouldn't pass in a value from another process.
|
||||
// Note, however, the process might be NULL (e.g. if the value was made with SBTarget::EvaluateExpression...)
|
||||
// in which case it is sufficient if the target's match:
|
||||
|
||||
Process *process = in_value.GetProcessSP().get();
|
||||
if (process)
|
||||
assert (process == m_process);
|
||||
else
|
||||
assert (in_value.GetTargetSP().get() == m_process->CalculateTarget().get());
|
||||
|
||||
class_type_or_name.Clear();
|
||||
value_type = Value::ValueType::eValueTypeScalar;
|
||||
|
||||
|
@ -235,16 +235,28 @@ struct RenderScriptRuntime::AllocationDetails
|
||||
}
|
||||
};
|
||||
|
||||
// Header for reading and writing allocation contents
|
||||
// to a binary file.
|
||||
// The FileHeader struct specifies the header we use for writing allocations to a binary file.
|
||||
// Our format begins with the ASCII characters "RSAD", identifying the file as an allocation dump.
|
||||
// Member variables dims and hdr_size are then written consecutively, immediately followed by an instance of
|
||||
// the ElementHeader struct. Because Elements can contain subelements, there may be more than one instance
|
||||
// of the ElementHeader struct. With this first instance being the root element, and the other instances being
|
||||
// the root's descendants. To identify which instances are an ElementHeader's children, each struct
|
||||
// is immediately followed by a sequence of consecutive offsets to the start of its child structs.
|
||||
// These offsets are 4 bytes in size, and the 0 offset signifies no more children.
|
||||
struct FileHeader
|
||||
{
|
||||
uint8_t ident[4]; // ASCII 'RSAD' identifying the file
|
||||
uint16_t hdr_size; // Header size in bytes, for backwards compatability
|
||||
uint32_t dims[3]; // Dimensions
|
||||
uint16_t hdr_size; // Header size in bytes, including all element headers
|
||||
};
|
||||
|
||||
struct ElementHeader
|
||||
{
|
||||
uint16_t type; // DataType enum
|
||||
uint32_t kind; // DataKind enum
|
||||
uint32_t dims[3]; // Dimensions
|
||||
uint32_t element_size; // Size of a single element, including padding
|
||||
uint16_t vector_size; // Vector width
|
||||
uint32_t array_size; // Number of elements in array
|
||||
};
|
||||
|
||||
// Monotonically increasing from 1
|
||||
@ -286,7 +298,6 @@ struct RenderScriptRuntime::AllocationDetails
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const ConstString &
|
||||
RenderScriptRuntime::Element::GetFallbackStructName()
|
||||
{
|
||||
@ -2084,37 +2095,62 @@ RenderScriptRuntime::LoadAllocation(Stream &strm, const uint32_t alloc_id, const
|
||||
|
||||
// Cast start of buffer to FileHeader and use pointer to read metadata
|
||||
void* file_buffer = data_sp->GetBytes();
|
||||
const AllocationDetails::FileHeader* head = static_cast<AllocationDetails::FileHeader*>(file_buffer);
|
||||
if (file_buffer == NULL || data_sp->GetByteSize() <
|
||||
(sizeof(AllocationDetails::FileHeader) + sizeof(AllocationDetails::ElementHeader)))
|
||||
{
|
||||
strm.Printf("Error: File %s does not contain enough data for header", filename);
|
||||
strm.EOL();
|
||||
return false;
|
||||
}
|
||||
const AllocationDetails::FileHeader* file_header = static_cast<AllocationDetails::FileHeader*>(file_buffer);
|
||||
|
||||
// Advance buffer past header
|
||||
file_buffer = static_cast<uint8_t*>(file_buffer) + head->hdr_size;
|
||||
// Check file starts with ascii characters "RSAD"
|
||||
if (file_header->ident[0] != 'R' || file_header->ident[1] != 'S' || file_header->ident[2] != 'A'
|
||||
|| file_header->ident[3] != 'D')
|
||||
{
|
||||
strm.Printf("Error: File doesn't contain identifier for an RS allocation dump. Are you sure this is the correct file?");
|
||||
strm.EOL();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Look at the type of the root element in the header
|
||||
AllocationDetails::ElementHeader root_element_header;
|
||||
memcpy(&root_element_header, static_cast<uint8_t*>(file_buffer) + sizeof(AllocationDetails::FileHeader),
|
||||
sizeof(AllocationDetails::ElementHeader));
|
||||
|
||||
if (log)
|
||||
log->Printf("RenderScriptRuntime::LoadAllocation - header type %u, element size %u",
|
||||
head->type, head->element_size);
|
||||
root_element_header.type, root_element_header.element_size);
|
||||
|
||||
// Check if the target allocation and file both have the same number of bytes for an Element
|
||||
if (*alloc->element.datum_size.get() != head->element_size)
|
||||
if (*alloc->element.datum_size.get() != root_element_header.element_size)
|
||||
{
|
||||
strm.Printf("Warning: Mismatched Element sizes - file %u bytes, allocation %u bytes",
|
||||
head->element_size, *alloc->element.datum_size.get());
|
||||
root_element_header.element_size, *alloc->element.datum_size.get());
|
||||
strm.EOL();
|
||||
}
|
||||
|
||||
// Check if the target allocation and file both have the same integral type
|
||||
const unsigned int type = static_cast<unsigned int>(*alloc->element.type.get());
|
||||
if (type != head->type)
|
||||
// Check if the target allocation and file both have the same type
|
||||
const unsigned int alloc_type = static_cast<unsigned int>(*alloc->element.type.get());
|
||||
const unsigned int file_type = root_element_header.type;
|
||||
|
||||
if (file_type > Element::RS_TYPE_FONT)
|
||||
{
|
||||
strm.Printf("Warning: File has unknown allocation type");
|
||||
strm.EOL();
|
||||
}
|
||||
else if (alloc_type != file_type)
|
||||
{
|
||||
// Enum value isn't monotonous, so doesn't always index RsDataTypeToString array
|
||||
unsigned int printable_target_type_index = type;
|
||||
unsigned int printable_head_type_index = head->type;
|
||||
if (type >= Element::RS_TYPE_ELEMENT && type <= Element::RS_TYPE_FONT)
|
||||
unsigned int printable_target_type_index = alloc_type;
|
||||
unsigned int printable_head_type_index = file_type;
|
||||
if (alloc_type >= Element::RS_TYPE_ELEMENT && alloc_type <= Element::RS_TYPE_FONT)
|
||||
printable_target_type_index = static_cast<Element::DataType>(
|
||||
(type - Element::RS_TYPE_ELEMENT) + Element::RS_TYPE_MATRIX_2X2 + 1);
|
||||
(alloc_type - Element::RS_TYPE_ELEMENT) + Element::RS_TYPE_MATRIX_2X2 + 1);
|
||||
|
||||
if (head->type >= Element::RS_TYPE_ELEMENT && head->type <= Element::RS_TYPE_FONT)
|
||||
if (file_type >= Element::RS_TYPE_ELEMENT && file_type <= Element::RS_TYPE_FONT)
|
||||
printable_head_type_index = static_cast<Element::DataType>(
|
||||
(head->type - Element::RS_TYPE_ELEMENT) + Element::RS_TYPE_MATRIX_2X2 + 1);
|
||||
(file_type - Element::RS_TYPE_ELEMENT) + Element::RS_TYPE_MATRIX_2X2 + 1);
|
||||
|
||||
const char* file_type_cstr = AllocationDetails::RsDataTypeToString[printable_head_type_index][0];
|
||||
const char* target_type_cstr = AllocationDetails::RsDataTypeToString[printable_target_type_index][0];
|
||||
@ -2124,8 +2160,11 @@ RenderScriptRuntime::LoadAllocation(Stream &strm, const uint32_t alloc_id, const
|
||||
strm.EOL();
|
||||
}
|
||||
|
||||
// Advance buffer past header
|
||||
file_buffer = static_cast<uint8_t*>(file_buffer) + file_header->hdr_size;
|
||||
|
||||
// Calculate size of allocation data in file
|
||||
size_t length = data_sp->GetByteSize() - head->hdr_size;
|
||||
size_t length = data_sp->GetByteSize() - file_header->hdr_size;
|
||||
|
||||
// Check if the target allocation and file both have the same total data size.
|
||||
const unsigned int alloc_size = *alloc->size.get();
|
||||
@ -2154,6 +2193,62 @@ RenderScriptRuntime::LoadAllocation(Stream &strm, const uint32_t alloc_id, const
|
||||
return true;
|
||||
}
|
||||
|
||||
// Function takes as parameters a byte buffer, which will eventually be written to file as the element header,
|
||||
// an offset into that buffer, and an Element that will be saved into the buffer at the parametrised offset.
|
||||
// Return value is the new offset after writing the element into the buffer.
|
||||
// Elements are saved to the file as the ElementHeader struct followed by offsets to the structs of all the element's children.
|
||||
size_t
|
||||
RenderScriptRuntime::PopulateElementHeaders(const std::shared_ptr<uint8_t> header_buffer, size_t offset, const Element& elem)
|
||||
{
|
||||
// File struct for an element header with all the relevant details copied from elem.
|
||||
// We assume members are valid already.
|
||||
AllocationDetails::ElementHeader elem_header;
|
||||
elem_header.type = *elem.type.get();
|
||||
elem_header.kind = *elem.type_kind.get();
|
||||
elem_header.element_size = *elem.datum_size.get();
|
||||
elem_header.vector_size = *elem.type_vec_size.get();
|
||||
elem_header.array_size = elem.array_size.isValid() ? *elem.array_size.get() : 0;
|
||||
const size_t elem_header_size = sizeof(AllocationDetails::ElementHeader);
|
||||
|
||||
// Copy struct into buffer and advance offset
|
||||
// We assume that header_buffer has been checked for NULL before this method is called
|
||||
memcpy(header_buffer.get() + offset, &elem_header, elem_header_size);
|
||||
offset += elem_header_size;
|
||||
|
||||
// Starting offset of child ElementHeader struct
|
||||
size_t child_offset = offset + ((elem.children.size() + 1) * sizeof(uint32_t));
|
||||
for (const RenderScriptRuntime::Element& child : elem.children)
|
||||
{
|
||||
// Recursively populate the buffer with the element header structs of children.
|
||||
// Then save the offsets where they were set after the parent element header.
|
||||
memcpy(header_buffer.get() + offset, &child_offset, sizeof(uint32_t));
|
||||
offset += sizeof(uint32_t);
|
||||
|
||||
child_offset = PopulateElementHeaders(header_buffer, child_offset, child);
|
||||
}
|
||||
|
||||
// Zero indicates no more children
|
||||
memset(header_buffer.get() + offset, 0, sizeof(uint32_t));
|
||||
|
||||
return child_offset;
|
||||
}
|
||||
|
||||
// Given an Element object this function returns the total size needed in the file header to store the element's details.
|
||||
// Taking into account the size of the element header struct, plus the offsets to all the element's children.
|
||||
// Function is recursive so that the size of all ancestors is taken into account.
|
||||
size_t
|
||||
RenderScriptRuntime::CalculateElementHeaderSize(const Element& elem)
|
||||
{
|
||||
size_t size = (elem.children.size() + 1) * sizeof(uint32_t); // Offsets to children plus zero terminator
|
||||
size += sizeof(AllocationDetails::ElementHeader); // Size of header struct with type details
|
||||
|
||||
// Calculate recursively for all descendants
|
||||
for (const Element& child : elem.children)
|
||||
size += CalculateElementHeaderSize(child);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
// Function copies allocation contents into a binary file.
|
||||
// This file can then be loaded later into a different allocation.
|
||||
// There is a header, FileHeader, before the allocation data containing meta-data.
|
||||
@ -2209,17 +2304,44 @@ RenderScriptRuntime::SaveAllocation(Stream &strm, const uint32_t alloc_id, const
|
||||
// Create the file header
|
||||
AllocationDetails::FileHeader head;
|
||||
head.ident[0] = 'R'; head.ident[1] = 'S'; head.ident[2] = 'A'; head.ident[3] = 'D';
|
||||
head.hdr_size = static_cast<uint16_t>(sizeof(AllocationDetails::FileHeader));
|
||||
head.type = static_cast<uint16_t>(*alloc->element.type.get());
|
||||
head.kind = static_cast<uint32_t>(*alloc->element.type_kind.get());
|
||||
head.dims[0] = static_cast<uint32_t>(alloc->dimension.get()->dim_1);
|
||||
head.dims[1] = static_cast<uint32_t>(alloc->dimension.get()->dim_2);
|
||||
head.dims[2] = static_cast<uint32_t>(alloc->dimension.get()->dim_3);
|
||||
head.element_size = static_cast<uint32_t>(*alloc->element.datum_size.get());
|
||||
|
||||
const size_t element_header_size = CalculateElementHeaderSize(alloc->element);
|
||||
assert((sizeof(AllocationDetails::FileHeader) + element_header_size) < UINT16_MAX && "Element header too large");
|
||||
head.hdr_size = static_cast<uint16_t>(sizeof(AllocationDetails::FileHeader) + element_header_size);
|
||||
|
||||
// Write the file header
|
||||
size_t num_bytes = sizeof(AllocationDetails::FileHeader);
|
||||
Error err = file.Write(static_cast<const void*>(&head), num_bytes);
|
||||
if (log)
|
||||
log->Printf("RenderScriptRuntime::SaveAllocation - Writing File Header, 0x%zX bytes", num_bytes);
|
||||
|
||||
Error err = file.Write(&head, num_bytes);
|
||||
if (!err.Success())
|
||||
{
|
||||
strm.Printf("Error: '%s' when writing to file '%s'", err.AsCString(), filename);
|
||||
strm.EOL();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the headers describing the element type of the allocation.
|
||||
std::shared_ptr<uint8_t> element_header_buffer(new uint8_t[element_header_size]);
|
||||
if (element_header_buffer == nullptr)
|
||||
{
|
||||
strm.Printf("Internal Error: Couldn't allocate %zu bytes on the heap", element_header_size);
|
||||
strm.EOL();
|
||||
return false;
|
||||
}
|
||||
|
||||
PopulateElementHeaders(element_header_buffer, 0, alloc->element);
|
||||
|
||||
// Write headers for allocation element type to file
|
||||
num_bytes = element_header_size;
|
||||
if (log)
|
||||
log->Printf("RenderScriptRuntime::SaveAllocation - Writing Element Headers, 0x%zX bytes", num_bytes);
|
||||
|
||||
err = file.Write(element_header_buffer.get(), num_bytes);
|
||||
if (!err.Success())
|
||||
{
|
||||
strm.Printf("Error: '%s' when writing to file '%s'", err.AsCString(), filename);
|
||||
@ -2230,7 +2352,7 @@ RenderScriptRuntime::SaveAllocation(Stream &strm, const uint32_t alloc_id, const
|
||||
// Write allocation data to file
|
||||
num_bytes = static_cast<size_t>(*alloc->size.get());
|
||||
if (log)
|
||||
log->Printf("RenderScriptRuntime::SaveAllocation - Writing 0x%" PRIx64 " bytes from %p", (uint64_t) num_bytes, (void*) buffer.get());
|
||||
log->Printf("RenderScriptRuntime::SaveAllocation - Writing 0x%zX bytes", num_bytes);
|
||||
|
||||
err = file.Write(buffer.get(), num_bytes);
|
||||
if (!err.Success())
|
||||
|
@ -207,10 +207,6 @@ class RenderScriptRuntime : public lldb_private::CPPLanguageRuntime
|
||||
|
||||
void Status(Stream &strm) const;
|
||||
|
||||
size_t GetAlternateManglings(const ConstString &mangled, std::vector<ConstString> &alternates) override {
|
||||
return static_cast<size_t>(0);
|
||||
}
|
||||
|
||||
void ModulesDidLoad(const ModuleList &module_list) override;
|
||||
|
||||
bool LoadAllocation(Stream &strm, const uint32_t alloc_id, const char* filename, StackFrame* frame_ptr);
|
||||
@ -335,6 +331,9 @@ class RenderScriptRuntime : public lldb_private::CPPLanguageRuntime
|
||||
static bool GetFrameVarAsUnsigned(const lldb::StackFrameSP, const char* var_name, uint64_t& val);
|
||||
void FindStructTypeName(Element& elem, StackFrame* frame_ptr);
|
||||
|
||||
size_t PopulateElementHeaders(const std::shared_ptr<uint8_t> header_buffer, size_t offset, const Element& elem);
|
||||
size_t CalculateElementHeaderSize(const Element& elem);
|
||||
|
||||
//
|
||||
// Helper functions for jitting the runtime
|
||||
//
|
||||
|
@ -330,15 +330,15 @@ mipsVariantFromElfFlags(const elf::elf_word e_flags, uint32_t endian)
|
||||
{
|
||||
case llvm::ELF::EF_MIPS_ARCH_1:
|
||||
case llvm::ELF::EF_MIPS_ARCH_2:
|
||||
case llvm::ELF::EF_MIPS_ARCH_3:
|
||||
case llvm::ELF::EF_MIPS_ARCH_4:
|
||||
case llvm::ELF::EF_MIPS_ARCH_5:
|
||||
case llvm::ELF::EF_MIPS_ARCH_32:
|
||||
return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips32el : ArchSpec::eMIPSSubType_mips32;
|
||||
case llvm::ELF::EF_MIPS_ARCH_32R2:
|
||||
return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips32r2el : ArchSpec::eMIPSSubType_mips32r2;
|
||||
case llvm::ELF::EF_MIPS_ARCH_32R6:
|
||||
return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips32r6el : ArchSpec::eMIPSSubType_mips32r6;
|
||||
case llvm::ELF::EF_MIPS_ARCH_3:
|
||||
case llvm::ELF::EF_MIPS_ARCH_4:
|
||||
case llvm::ELF::EF_MIPS_ARCH_5:
|
||||
case llvm::ELF::EF_MIPS_ARCH_64:
|
||||
return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips64el : ArchSpec::eMIPSSubType_mips64;
|
||||
case llvm::ELF::EF_MIPS_ARCH_64R2:
|
||||
@ -954,9 +954,6 @@ ObjectFileELF::GetAddressByteSize() const
|
||||
return m_data.GetAddressByteSize();
|
||||
}
|
||||
|
||||
// Top 16 bits of the `Symbol` flags are available.
|
||||
#define ARM_ELF_SYM_IS_THUMB (1 << 16)
|
||||
|
||||
AddressClass
|
||||
ObjectFileELF::GetAddressClass (addr_t file_addr)
|
||||
{
|
||||
@ -2190,7 +2187,6 @@ ObjectFileELF::ParseSymbols (Symtab *symtab,
|
||||
// symbol.st_value to produce the final symbol_value
|
||||
// that we store in the symtab.
|
||||
symbol_value_offset = -1;
|
||||
additional_flags = ARM_ELF_SYM_IS_THUMB;
|
||||
m_address_class_map[symbol.st_value^1] = eAddressClassCodeAlternateISA;
|
||||
}
|
||||
else
|
||||
|
@ -7,7 +7,7 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#if defined(__arm__)
|
||||
#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
|
||||
|
||||
#include "NativeRegisterContextLinux_arm.h"
|
||||
|
||||
@ -16,8 +16,12 @@
|
||||
#include "lldb/Core/Log.h"
|
||||
#include "lldb/Core/RegisterValue.h"
|
||||
|
||||
#include "Plugins/Process/Linux/Procfs.h"
|
||||
#include "Plugins/Process/Utility/RegisterContextLinux_arm.h"
|
||||
|
||||
#include <elf.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#define REG_CONTEXT_SIZE (GetGPRSize() + sizeof (m_fpr))
|
||||
|
||||
#ifndef PTRACE_GETVFPREGS
|
||||
@ -169,6 +173,8 @@ g_reg_sets_arm[k_num_register_sets] =
|
||||
{ "Floating Point Registers", "fpu", k_num_fpr_registers_arm, g_fpu_regnums_arm }
|
||||
};
|
||||
|
||||
#if defined(__arm__)
|
||||
|
||||
NativeRegisterContextLinux*
|
||||
NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(const ArchSpec& target_arch,
|
||||
NativeThreadProtocol &native_thread,
|
||||
@ -177,6 +183,8 @@ NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(const ArchSpec&
|
||||
return new NativeRegisterContextLinux_arm(target_arch, native_thread, concrete_frame_idx);
|
||||
}
|
||||
|
||||
#endif // defined(__arm__)
|
||||
|
||||
NativeRegisterContextLinux_arm::NativeRegisterContextLinux_arm (const ArchSpec& target_arch,
|
||||
NativeThreadProtocol &native_thread,
|
||||
uint32_t concrete_frame_idx) :
|
||||
@ -919,14 +927,14 @@ NativeRegisterContextLinux_arm::WriteHardwareDebugRegs(int hwbType, int hwb_inde
|
||||
ctrl_buf = &m_hwp_regs[hwb_index].control;
|
||||
|
||||
error = NativeProcessLinux::PtraceWrapper(PTRACE_SETHBPREGS,
|
||||
m_thread.GetID(), (PTRACE_TYPE_ARG3) -((hwb_index << 1) + 1),
|
||||
m_thread.GetID(), (PTRACE_TYPE_ARG3)(intptr_t) -((hwb_index << 1) + 1),
|
||||
addr_buf, sizeof(unsigned int));
|
||||
|
||||
if (error.Fail())
|
||||
return error;
|
||||
|
||||
error = NativeProcessLinux::PtraceWrapper(PTRACE_SETHBPREGS,
|
||||
m_thread.GetID(), (PTRACE_TYPE_ARG3) -((hwb_index << 1) + 2),
|
||||
m_thread.GetID(), (PTRACE_TYPE_ARG3)(intptr_t) -((hwb_index << 1) + 2),
|
||||
ctrl_buf, sizeof(unsigned int));
|
||||
}
|
||||
else
|
||||
@ -935,14 +943,14 @@ NativeRegisterContextLinux_arm::WriteHardwareDebugRegs(int hwbType, int hwb_inde
|
||||
ctrl_buf = &m_hwp_regs[hwb_index].control;
|
||||
|
||||
error = NativeProcessLinux::PtraceWrapper(PTRACE_SETHBPREGS,
|
||||
m_thread.GetID(), (PTRACE_TYPE_ARG3) ((hwb_index << 1) + 1),
|
||||
m_thread.GetID(), (PTRACE_TYPE_ARG3)(intptr_t) ((hwb_index << 1) + 1),
|
||||
addr_buf, sizeof(unsigned int));
|
||||
|
||||
if (error.Fail())
|
||||
return error;
|
||||
|
||||
error = NativeProcessLinux::PtraceWrapper(PTRACE_SETHBPREGS,
|
||||
m_thread.GetID(), (PTRACE_TYPE_ARG3) ((hwb_index << 1) + 2),
|
||||
m_thread.GetID(), (PTRACE_TYPE_ARG3)(intptr_t) ((hwb_index << 1) + 2),
|
||||
ctrl_buf, sizeof(unsigned int));
|
||||
|
||||
}
|
||||
@ -956,12 +964,34 @@ NativeRegisterContextLinux_arm::CalculateFprOffset(const RegisterInfo* reg_info)
|
||||
return reg_info->byte_offset - GetRegisterInfoAtIndex(m_reg_info.first_fpr)->byte_offset;
|
||||
}
|
||||
|
||||
Error
|
||||
NativeRegisterContextLinux_arm::DoReadRegisterValue(uint32_t offset,
|
||||
const char* reg_name,
|
||||
uint32_t size,
|
||||
RegisterValue &value)
|
||||
{
|
||||
// PTRACE_PEEKUSER don't work in the aarch64 linux kernel used on android devices (always return
|
||||
// "Bad address"). To avoid using PTRACE_PEEKUSER we read out the full GPR register set instead.
|
||||
// This approach is about 4 times slower but the performance overhead is negligible in
|
||||
// comparision to processing time in lldb-server.
|
||||
assert(offset % 4 == 0 && "Try to write a register with unaligned offset");
|
||||
if (offset + sizeof(uint32_t) > sizeof(m_gpr_arm))
|
||||
return Error("Register isn't fit into the size of the GPR area");
|
||||
|
||||
Error error = DoReadGPR(m_gpr_arm, sizeof(m_gpr_arm));
|
||||
if (error.Fail())
|
||||
return error;
|
||||
|
||||
value.SetUInt32(m_gpr_arm[offset / sizeof(uint32_t)]);
|
||||
return Error();
|
||||
}
|
||||
|
||||
Error
|
||||
NativeRegisterContextLinux_arm::DoWriteRegisterValue(uint32_t offset,
|
||||
const char* reg_name,
|
||||
const RegisterValue &value)
|
||||
{
|
||||
// PTRACE_POKEUSER don't work in the aarch64 liux kernel used on android devices (always return
|
||||
// PTRACE_POKEUSER don't work in the aarch64 linux kernel used on android devices (always return
|
||||
// "Bad address"). To avoid using PTRACE_POKEUSER we read out the full GPR register set, modify
|
||||
// the requested register and write it back. This approach is about 4 times slower but the
|
||||
// performance overhead is negligible in comparision to processing time in lldb-server.
|
||||
@ -994,24 +1024,68 @@ NativeRegisterContextLinux_arm::DoWriteRegisterValue(uint32_t offset,
|
||||
return DoWriteGPR(m_gpr_arm, sizeof(m_gpr_arm));
|
||||
}
|
||||
|
||||
Error
|
||||
NativeRegisterContextLinux_arm::DoReadGPR(void *buf, size_t buf_size)
|
||||
{
|
||||
#ifdef __arm__
|
||||
return NativeRegisterContextLinux::DoReadGPR(buf, buf_size);
|
||||
#else // __aarch64__
|
||||
struct iovec ioVec;
|
||||
ioVec.iov_base = buf;
|
||||
ioVec.iov_len = buf_size;
|
||||
|
||||
return ReadRegisterSet(&ioVec, buf_size, NT_PRSTATUS);
|
||||
#endif // __arm__
|
||||
}
|
||||
|
||||
Error
|
||||
NativeRegisterContextLinux_arm::DoWriteGPR(void *buf, size_t buf_size)
|
||||
{
|
||||
#ifdef __arm__
|
||||
return NativeRegisterContextLinux::DoWriteGPR(buf, buf_size);
|
||||
#else // __aarch64__
|
||||
struct iovec ioVec;
|
||||
ioVec.iov_base = buf;
|
||||
ioVec.iov_len = buf_size;
|
||||
|
||||
return WriteRegisterSet(&ioVec, buf_size, NT_PRSTATUS);
|
||||
#endif // __arm__
|
||||
}
|
||||
|
||||
Error
|
||||
NativeRegisterContextLinux_arm::DoReadFPR(void *buf, size_t buf_size)
|
||||
{
|
||||
#ifdef __arm__
|
||||
return NativeProcessLinux::PtraceWrapper(PTRACE_GETVFPREGS,
|
||||
m_thread.GetID(),
|
||||
nullptr,
|
||||
buf,
|
||||
buf_size);
|
||||
#else // __aarch64__
|
||||
struct iovec ioVec;
|
||||
ioVec.iov_base = buf;
|
||||
ioVec.iov_len = buf_size;
|
||||
|
||||
return ReadRegisterSet(&ioVec, buf_size, NT_ARM_VFP);
|
||||
#endif // __arm__
|
||||
}
|
||||
|
||||
Error
|
||||
NativeRegisterContextLinux_arm::DoWriteFPR(void *buf, size_t buf_size)
|
||||
{
|
||||
#ifdef __arm__
|
||||
return NativeProcessLinux::PtraceWrapper(PTRACE_SETVFPREGS,
|
||||
m_thread.GetID(),
|
||||
nullptr,
|
||||
buf,
|
||||
buf_size);
|
||||
#else // __aarch64__
|
||||
struct iovec ioVec;
|
||||
ioVec.iov_base = buf;
|
||||
ioVec.iov_len = buf_size;
|
||||
|
||||
return WriteRegisterSet(&ioVec, buf_size, NT_ARM_VFP);
|
||||
#endif // __arm__
|
||||
}
|
||||
|
||||
#endif // defined(__arm__)
|
||||
#endif // defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
|
||||
|
@ -7,7 +7,7 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#if defined(__arm__) // arm register context only needed on arm devices
|
||||
#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
|
||||
|
||||
#ifndef lldb_NativeRegisterContextLinux_arm_h
|
||||
#define lldb_NativeRegisterContextLinux_arm_h
|
||||
@ -90,11 +90,23 @@ namespace process_linux {
|
||||
};
|
||||
|
||||
protected:
|
||||
Error
|
||||
DoReadRegisterValue(uint32_t offset,
|
||||
const char* reg_name,
|
||||
uint32_t size,
|
||||
RegisterValue &value) override;
|
||||
|
||||
Error
|
||||
DoWriteRegisterValue(uint32_t offset,
|
||||
const char* reg_name,
|
||||
const RegisterValue &value) override;
|
||||
|
||||
Error
|
||||
DoReadGPR(void *buf, size_t buf_size) override;
|
||||
|
||||
Error
|
||||
DoWriteGPR(void *buf, size_t buf_size) override;
|
||||
|
||||
Error
|
||||
DoReadFPR(void *buf, size_t buf_size) override;
|
||||
|
||||
@ -182,4 +194,4 @@ namespace process_linux {
|
||||
|
||||
#endif // #ifndef lldb_NativeRegisterContextLinux_arm_h
|
||||
|
||||
#endif // defined(__arm__)
|
||||
#endif // defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#if defined (__arm64__) || defined (__aarch64__)
|
||||
|
||||
#include "NativeRegisterContextLinux_arm.h"
|
||||
#include "NativeRegisterContextLinux_arm64.h"
|
||||
|
||||
// C Includes
|
||||
@ -23,6 +24,7 @@
|
||||
|
||||
#include "Plugins/Process/Linux/NativeProcessLinux.h"
|
||||
#include "Plugins/Process/Linux/Procfs.h"
|
||||
#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
|
||||
#include "Plugins/Process/Utility/RegisterContextLinux_arm64.h"
|
||||
|
||||
// System includes - They have to be included after framework includes because they define some
|
||||
@ -142,7 +144,19 @@ NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(const ArchSpec&
|
||||
NativeThreadProtocol &native_thread,
|
||||
uint32_t concrete_frame_idx)
|
||||
{
|
||||
Log *log = ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_REGISTERS);
|
||||
switch (target_arch.GetMachine())
|
||||
{
|
||||
case llvm::Triple::arm:
|
||||
return new NativeRegisterContextLinux_arm(target_arch, native_thread, concrete_frame_idx);
|
||||
case llvm::Triple::aarch64:
|
||||
return new NativeRegisterContextLinux_arm64(target_arch, native_thread, concrete_frame_idx);
|
||||
default:
|
||||
if (log)
|
||||
log->Printf("NativeRegisterContextLinux::%s() have no register context for architecture: %s\n", __FUNCTION__,
|
||||
target_arch.GetTriple().getArchName().str().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64 (const ArchSpec& target_arch,
|
||||
|
@ -1388,7 +1388,15 @@ NativeRegisterContextLinux_mips64::DoReadRegisterValue(uint32_t offset,
|
||||
{
|
||||
lldb_private::ArchSpec arch;
|
||||
if (m_thread.GetProcess()->GetArchitecture(arch))
|
||||
value.SetBytes((void *)(((unsigned char *)®s) + offset + 4 * (arch.GetMachine() == llvm::Triple::mips)), arch.GetFlags() & lldb_private::ArchSpec::eMIPSABI_O32 ? 4 : 8, arch.GetByteOrder());
|
||||
{
|
||||
void* target_address = ((uint8_t*)®s) + offset + 4 * (arch.GetMachine() == llvm::Triple::mips);
|
||||
uint32_t target_size;
|
||||
if ((::strcmp(reg_name, "sr") == 0) || (::strcmp(reg_name, "cause") == 0) || (::strcmp(reg_name, "config5") == 0))
|
||||
target_size = 4;
|
||||
else
|
||||
target_size = arch.GetFlags() & lldb_private::ArchSpec::eMIPSABI_O32 ? 4 : 8;
|
||||
value.SetBytes(target_address, target_size, arch.GetByteOrder());
|
||||
}
|
||||
else
|
||||
error.SetErrorString("failed to get architecture");
|
||||
}
|
||||
|
@ -72,6 +72,7 @@ lldb_private::InferiorCallMmap (Process *process,
|
||||
options.SetTryAllThreads(true);
|
||||
options.SetDebug (false);
|
||||
options.SetTimeoutUsec(500000);
|
||||
options.SetTrapExceptions(false);
|
||||
|
||||
addr_t prot_arg, flags_arg = 0;
|
||||
if (prot == eMmapProtNone)
|
||||
@ -172,6 +173,7 @@ lldb_private::InferiorCallMunmap (Process *process,
|
||||
options.SetTryAllThreads(true);
|
||||
options.SetDebug (false);
|
||||
options.SetTimeoutUsec(500000);
|
||||
options.SetTrapExceptions(false);
|
||||
|
||||
AddressRange munmap_range;
|
||||
if (sc.GetAddressRange(range_scope, 0, use_inline_block_range, munmap_range))
|
||||
@ -214,7 +216,8 @@ lldb_private::InferiorCallMunmap (Process *process,
|
||||
bool
|
||||
lldb_private::InferiorCall (Process *process,
|
||||
const Address *address,
|
||||
addr_t &returned_func)
|
||||
addr_t &returned_func,
|
||||
bool trap_exceptions)
|
||||
{
|
||||
Thread *thread = process->GetThreadList().GetSelectedThread().get();
|
||||
if (thread == NULL || address == NULL)
|
||||
@ -227,6 +230,7 @@ lldb_private::InferiorCall (Process *process,
|
||||
options.SetTryAllThreads(true);
|
||||
options.SetDebug (false);
|
||||
options.SetTimeoutUsec(500000);
|
||||
options.SetTrapExceptions(trap_exceptions);
|
||||
|
||||
ClangASTContext *clang_ast_context = process->GetTarget().GetScratchClangASTContext();
|
||||
CompilerType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
|
||||
|
@ -31,7 +31,8 @@ bool InferiorCallMmap(Process *proc, lldb::addr_t &allocated_addr,
|
||||
|
||||
bool InferiorCallMunmap(Process *proc, lldb::addr_t addr, lldb::addr_t length);
|
||||
|
||||
bool InferiorCall(Process *proc, const Address *address, lldb::addr_t &returned_func);
|
||||
bool InferiorCall(Process *proc, const Address *address, lldb::addr_t &returned_func,
|
||||
bool trap_exceptions = false);
|
||||
|
||||
} // namespace lldb_private
|
||||
|
||||
|
@ -265,10 +265,33 @@ RegisterContextLLDB::InitializeZerothFrame()
|
||||
|
||||
if (!ReadCFAValueForRow (row_register_kind, active_row, m_cfa))
|
||||
{
|
||||
UnwindLogMsg ("could not read CFA register for this frame.");
|
||||
// Try the fall back unwind plan since the
|
||||
// full unwind plan failed.
|
||||
FuncUnwindersSP func_unwinders_sp;
|
||||
UnwindPlanSP call_site_unwind_plan;
|
||||
bool cfa_status = false;
|
||||
|
||||
if (m_sym_ctx_valid)
|
||||
{
|
||||
func_unwinders_sp = pc_module_sp->GetObjectFile()->GetUnwindTable().GetFuncUnwindersContainingAddress (m_current_pc, m_sym_ctx);
|
||||
}
|
||||
|
||||
if(func_unwinders_sp.get() != nullptr)
|
||||
call_site_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite(process->GetTarget(), m_current_offset_backed_up_one);
|
||||
|
||||
if (call_site_unwind_plan.get() != nullptr)
|
||||
{
|
||||
m_fallback_unwind_plan_sp = call_site_unwind_plan;
|
||||
if(TryFallbackUnwindPlan())
|
||||
cfa_status = true;
|
||||
}
|
||||
if (!cfa_status)
|
||||
{
|
||||
UnwindLogMsg ("could not read CFA value for first frame.");
|
||||
m_frame_type = eNotAValidFrame;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
UnwindLogMsg ("initialized frame current pc is 0x%" PRIx64 " cfa is 0x%" PRIx64 " using %s UnwindPlan",
|
||||
(uint64_t) m_current_pc.GetLoadAddress (exe_ctx.GetTargetPtr()),
|
||||
|
@ -550,14 +550,26 @@ ProcessWindowsLive::RefreshStateAfterStop()
|
||||
if (!stop_thread)
|
||||
return;
|
||||
|
||||
switch (active_exception->GetExceptionCode())
|
||||
{
|
||||
case EXCEPTION_SINGLE_STEP:
|
||||
{
|
||||
stop_info = StopInfo::CreateStopReasonToTrace(*stop_thread);
|
||||
stop_thread->SetStopInfo(stop_info);
|
||||
WINLOG_IFANY(WINDOWS_LOG_EXCEPTION | WINDOWS_LOG_STEP, "RefreshStateAfterStop single stepping thread %u",
|
||||
stop_thread->GetID());
|
||||
stop_thread->SetStopInfo(stop_info);
|
||||
return;
|
||||
}
|
||||
|
||||
case EXCEPTION_BREAKPOINT:
|
||||
{
|
||||
RegisterContextSP register_context = stop_thread->GetRegisterContext();
|
||||
|
||||
// The current EIP is AFTER the BP opcode, which is one byte.
|
||||
uint64_t pc = register_context->GetPC() - 1;
|
||||
if (active_exception->GetExceptionCode() == EXCEPTION_BREAKPOINT)
|
||||
{
|
||||
BreakpointSiteSP site(GetBreakpointSiteList().FindByAddress(pc));
|
||||
|
||||
BreakpointSiteSP site(GetBreakpointSiteList().FindByAddress(pc));
|
||||
if (site)
|
||||
{
|
||||
WINLOG_IFANY(WINDOWS_LOG_BREAKPOINTS | WINDOWS_LOG_EXCEPTION,
|
||||
@ -581,25 +593,31 @@ ProcessWindowsLive::RefreshStateAfterStop()
|
||||
"Breakpoint site %d is not valid for this thread, creating empty stop info.",
|
||||
site->GetID());
|
||||
}
|
||||
}
|
||||
stop_thread->SetStopInfo(stop_info);
|
||||
}
|
||||
else if (active_exception->GetExceptionCode() == EXCEPTION_SINGLE_STEP)
|
||||
{
|
||||
stop_info = StopInfo::CreateStopReasonToTrace(*stop_thread);
|
||||
stop_thread->SetStopInfo(stop_info);
|
||||
WINLOG_IFANY(WINDOWS_LOG_EXCEPTION | WINDOWS_LOG_STEP, "RefreshStateAfterStop single stepping thread %u",
|
||||
stop_thread->GetID());
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The thread hit a hard-coded breakpoint like an `int 3` or `__debugbreak()`.
|
||||
WINLOG_IFALL(WINDOWS_LOG_BREAKPOINTS | WINDOWS_LOG_EXCEPTION,
|
||||
"No breakpoint site matches for this thread. __debugbreak()? "
|
||||
"Creating stop info with the exception.");
|
||||
// FALLTHROUGH: We'll treat this as a generic exception record in the default case.
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
std::string desc;
|
||||
llvm::raw_string_ostream desc_stream(desc);
|
||||
desc_stream << "Exception " << llvm::format_hex(active_exception->GetExceptionCode(), 8)
|
||||
<< " encountered at address " << llvm::format_hex(pc, 8);
|
||||
<< " encountered at address "
|
||||
<< llvm::format_hex(active_exception->GetExceptionAddress(), 8);
|
||||
stop_info = StopInfo::CreateStopReasonWithException(*stop_thread, desc_stream.str().c_str());
|
||||
stop_thread->SetStopInfo(stop_info);
|
||||
WINLOG_IFALL(WINDOWS_LOG_EXCEPTION, desc_stream.str().c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,6 +101,8 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient() :
|
||||
m_supports_QEnvironment (true),
|
||||
m_supports_QEnvironmentHexEncoded (true),
|
||||
m_supports_qSymbol (true),
|
||||
m_qSymbol_requests_done (false),
|
||||
m_supports_qModuleInfo (true),
|
||||
m_supports_jThreadsInfo (true),
|
||||
m_curr_pid (LLDB_INVALID_PROCESS_ID),
|
||||
m_curr_tid (LLDB_INVALID_THREAD_ID),
|
||||
@ -376,6 +378,8 @@ GDBRemoteCommunicationClient::ResetDiscoverableSettings (bool did_exec)
|
||||
m_supports_QEnvironment = true;
|
||||
m_supports_QEnvironmentHexEncoded = true;
|
||||
m_supports_qSymbol = true;
|
||||
m_qSymbol_requests_done = false;
|
||||
m_supports_qModuleInfo = true;
|
||||
m_host_arch.Clear();
|
||||
m_os_version_major = UINT32_MAX;
|
||||
m_os_version_minor = UINT32_MAX;
|
||||
@ -4284,6 +4288,9 @@ GDBRemoteCommunicationClient::GetModuleInfo (const FileSpec& module_file_spec,
|
||||
const lldb_private::ArchSpec& arch_spec,
|
||||
ModuleSpec &module_spec)
|
||||
{
|
||||
if (!m_supports_qModuleInfo)
|
||||
return false;
|
||||
|
||||
std::string module_path = module_file_spec.GetPath (false);
|
||||
if (module_path.empty ())
|
||||
return false;
|
||||
@ -4299,9 +4306,15 @@ GDBRemoteCommunicationClient::GetModuleInfo (const FileSpec& module_file_spec,
|
||||
if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) != PacketResult::Success)
|
||||
return false;
|
||||
|
||||
if (response.IsErrorResponse () || response.IsUnsupportedResponse ())
|
||||
if (response.IsErrorResponse ())
|
||||
return false;
|
||||
|
||||
if (response.IsUnsupportedResponse ())
|
||||
{
|
||||
m_supports_qModuleInfo = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string name;
|
||||
std::string value;
|
||||
bool success;
|
||||
@ -4432,11 +4445,42 @@ GDBRemoteCommunicationClient::ReadExtFeature (const lldb_private::ConstString ob
|
||||
// qSymbol:<sym_name> The target requests the value of symbol sym_name (hex encoded).
|
||||
// LLDB may provide the value by sending another qSymbol packet
|
||||
// in the form of"qSymbol:<sym_value>:<sym_name>".
|
||||
//
|
||||
// Three examples:
|
||||
//
|
||||
// lldb sends: qSymbol::
|
||||
// lldb receives: OK
|
||||
// Remote gdb stub does not need to know the addresses of any symbols, lldb does not
|
||||
// need to ask again in this session.
|
||||
//
|
||||
// lldb sends: qSymbol::
|
||||
// lldb receives: qSymbol:64697370617463685f71756575655f6f666673657473
|
||||
// lldb sends: qSymbol::64697370617463685f71756575655f6f666673657473
|
||||
// lldb receives: OK
|
||||
// Remote gdb stub asks for address of 'dispatch_queue_offsets'. lldb does not know
|
||||
// the address at this time. lldb needs to send qSymbol:: again when it has more
|
||||
// solibs loaded.
|
||||
//
|
||||
// lldb sends: qSymbol::
|
||||
// lldb receives: qSymbol:64697370617463685f71756575655f6f666673657473
|
||||
// lldb sends: qSymbol:2bc97554:64697370617463685f71756575655f6f666673657473
|
||||
// lldb receives: OK
|
||||
// Remote gdb stub asks for address of 'dispatch_queue_offsets'. lldb says that it
|
||||
// is at address 0x2bc97554. Remote gdb stub sends 'OK' indicating that it does not
|
||||
// need any more symbols. lldb does not need to ask again in this session.
|
||||
|
||||
void
|
||||
GDBRemoteCommunicationClient::ServeSymbolLookups(lldb_private::Process *process)
|
||||
{
|
||||
if (m_supports_qSymbol)
|
||||
// Set to true once we've resolved a symbol to an address for the remote stub.
|
||||
// If we get an 'OK' response after this, the remote stub doesn't need any more
|
||||
// symbols and we can stop asking.
|
||||
bool symbol_response_provided = false;
|
||||
|
||||
// Is this the inital qSymbol:: packet?
|
||||
bool first_qsymbol_query = true;
|
||||
|
||||
if (m_supports_qSymbol && m_qSymbol_requests_done == false)
|
||||
{
|
||||
Mutex::Locker locker;
|
||||
if (GetSequenceMutex(locker, "GDBRemoteCommunicationClient::ServeSymbolLookups() failed due to not getting the sequence mutex"))
|
||||
@ -4448,9 +4492,15 @@ GDBRemoteCommunicationClient::ServeSymbolLookups(lldb_private::Process *process)
|
||||
{
|
||||
if (response.IsOKResponse())
|
||||
{
|
||||
if (symbol_response_provided || first_qsymbol_query)
|
||||
{
|
||||
m_qSymbol_requests_done = true;
|
||||
}
|
||||
|
||||
// We are done serving symbols requests
|
||||
return;
|
||||
}
|
||||
first_qsymbol_query = false;
|
||||
|
||||
if (response.IsUnsupportedResponse())
|
||||
{
|
||||
@ -4530,7 +4580,14 @@ GDBRemoteCommunicationClient::ServeSymbolLookups(lldb_private::Process *process)
|
||||
packet.Clear();
|
||||
packet.PutCString("qSymbol:");
|
||||
if (symbol_load_addr != LLDB_INVALID_ADDRESS)
|
||||
{
|
||||
packet.Printf("%" PRIx64, symbol_load_addr);
|
||||
symbol_response_provided = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
symbol_response_provided = false;
|
||||
}
|
||||
packet.PutCString(":");
|
||||
packet.PutBytesAsRawHex8(symbol_name.data(), symbol_name.size());
|
||||
continue; // go back to the while loop and send "packet" and wait for another response
|
||||
|
@ -619,6 +619,8 @@ class GDBRemoteCommunicationClient : public GDBRemoteCommunication
|
||||
m_supports_QEnvironment:1,
|
||||
m_supports_QEnvironmentHexEncoded:1,
|
||||
m_supports_qSymbol:1,
|
||||
m_qSymbol_requests_done:1,
|
||||
m_supports_qModuleInfo:1,
|
||||
m_supports_jThreadsInfo:1;
|
||||
|
||||
lldb::pid_t m_curr_pid;
|
||||
|
@ -173,118 +173,6 @@ namespace {
|
||||
|
||||
} // anonymous namespace end
|
||||
|
||||
class ProcessGDBRemote::GDBLoadedModuleInfoList
|
||||
{
|
||||
public:
|
||||
|
||||
class LoadedModuleInfo
|
||||
{
|
||||
public:
|
||||
|
||||
enum e_data_point
|
||||
{
|
||||
e_has_name = 0,
|
||||
e_has_base ,
|
||||
e_has_dynamic ,
|
||||
e_has_link_map ,
|
||||
e_num
|
||||
};
|
||||
|
||||
LoadedModuleInfo ()
|
||||
{
|
||||
for (uint32_t i = 0; i < e_num; ++i)
|
||||
m_has[i] = false;
|
||||
}
|
||||
|
||||
void set_name (const std::string & name)
|
||||
{
|
||||
m_name = name;
|
||||
m_has[e_has_name] = true;
|
||||
}
|
||||
bool get_name (std::string & out) const
|
||||
{
|
||||
out = m_name;
|
||||
return m_has[e_has_name];
|
||||
}
|
||||
|
||||
void set_base (const lldb::addr_t base)
|
||||
{
|
||||
m_base = base;
|
||||
m_has[e_has_base] = true;
|
||||
}
|
||||
bool get_base (lldb::addr_t & out) const
|
||||
{
|
||||
out = m_base;
|
||||
return m_has[e_has_base];
|
||||
}
|
||||
|
||||
void set_base_is_offset (bool is_offset)
|
||||
{
|
||||
m_base_is_offset = is_offset;
|
||||
}
|
||||
bool get_base_is_offset(bool & out) const
|
||||
{
|
||||
out = m_base_is_offset;
|
||||
return m_has[e_has_base];
|
||||
}
|
||||
|
||||
void set_link_map (const lldb::addr_t addr)
|
||||
{
|
||||
m_link_map = addr;
|
||||
m_has[e_has_link_map] = true;
|
||||
}
|
||||
bool get_link_map (lldb::addr_t & out) const
|
||||
{
|
||||
out = m_link_map;
|
||||
return m_has[e_has_link_map];
|
||||
}
|
||||
|
||||
void set_dynamic (const lldb::addr_t addr)
|
||||
{
|
||||
m_dynamic = addr;
|
||||
m_has[e_has_dynamic] = true;
|
||||
}
|
||||
bool get_dynamic (lldb::addr_t & out) const
|
||||
{
|
||||
out = m_dynamic;
|
||||
return m_has[e_has_dynamic];
|
||||
}
|
||||
|
||||
bool has_info (e_data_point datum)
|
||||
{
|
||||
assert (datum < e_num);
|
||||
return m_has[datum];
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
bool m_has[e_num];
|
||||
std::string m_name;
|
||||
lldb::addr_t m_link_map;
|
||||
lldb::addr_t m_base;
|
||||
bool m_base_is_offset;
|
||||
lldb::addr_t m_dynamic;
|
||||
};
|
||||
|
||||
GDBLoadedModuleInfoList ()
|
||||
: m_list ()
|
||||
, m_link_map (LLDB_INVALID_ADDRESS)
|
||||
{}
|
||||
|
||||
void add (const LoadedModuleInfo & mod)
|
||||
{
|
||||
m_list.push_back (mod);
|
||||
}
|
||||
|
||||
void clear ()
|
||||
{
|
||||
m_list.clear ();
|
||||
}
|
||||
|
||||
std::vector<LoadedModuleInfo> m_list;
|
||||
lldb::addr_t m_link_map;
|
||||
};
|
||||
|
||||
// TODO Randomly assigning a port is unsafe. We should get an unused
|
||||
// ephemeral port from the kernel and make sure we reserve it before passing
|
||||
// it to debugserver.
|
||||
@ -2034,6 +1922,8 @@ ProcessGDBRemote::SetThreadStopInfo (lldb::tid_t tid,
|
||||
const std::vector<addr_t> &exc_data,
|
||||
addr_t thread_dispatch_qaddr,
|
||||
bool queue_vars_valid, // Set to true if queue_name, queue_kind and queue_serial are valid
|
||||
LazyBool associated_with_dispatch_queue,
|
||||
addr_t dispatch_queue_t,
|
||||
std::string &queue_name,
|
||||
QueueKind queue_kind,
|
||||
uint64_t queue_serial)
|
||||
@ -2074,10 +1964,15 @@ ProcessGDBRemote::SetThreadStopInfo (lldb::tid_t tid,
|
||||
gdb_thread->SetThreadDispatchQAddr (thread_dispatch_qaddr);
|
||||
// Check if the GDB server was able to provide the queue name, kind and serial number
|
||||
if (queue_vars_valid)
|
||||
gdb_thread->SetQueueInfo(std::move(queue_name), queue_kind, queue_serial);
|
||||
gdb_thread->SetQueueInfo(std::move(queue_name), queue_kind, queue_serial, dispatch_queue_t, associated_with_dispatch_queue);
|
||||
else
|
||||
gdb_thread->ClearQueueInfo();
|
||||
|
||||
gdb_thread->SetAssociatedWithLibdispatchQueue (associated_with_dispatch_queue);
|
||||
|
||||
if (dispatch_queue_t != LLDB_INVALID_ADDRESS)
|
||||
gdb_thread->SetQueueLibdispatchQueueAddress (dispatch_queue_t);
|
||||
|
||||
// Make sure we update our thread stop reason just once
|
||||
if (!thread_sp->StopInfoIsUpToDate())
|
||||
{
|
||||
@ -2248,9 +2143,11 @@ ProcessGDBRemote::SetThreadStopInfo (StructuredData::Dictionary *thread_dict)
|
||||
static ConstString g_key_metype("metype");
|
||||
static ConstString g_key_medata("medata");
|
||||
static ConstString g_key_qaddr("qaddr");
|
||||
static ConstString g_key_dispatch_queue_t("dispatch_queue_t");
|
||||
static ConstString g_key_associated_with_dispatch_queue("associated_with_dispatch_queue");
|
||||
static ConstString g_key_queue_name("qname");
|
||||
static ConstString g_key_queue_kind("qkind");
|
||||
static ConstString g_key_queue_serial("qserial");
|
||||
static ConstString g_key_queue_serial_number("qserialnum");
|
||||
static ConstString g_key_registers("registers");
|
||||
static ConstString g_key_memory("memory");
|
||||
static ConstString g_key_address("address");
|
||||
@ -2270,9 +2167,11 @@ ProcessGDBRemote::SetThreadStopInfo (StructuredData::Dictionary *thread_dict)
|
||||
addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS;
|
||||
ExpeditedRegisterMap expedited_register_map;
|
||||
bool queue_vars_valid = false;
|
||||
addr_t dispatch_queue_t = LLDB_INVALID_ADDRESS;
|
||||
LazyBool associated_with_dispatch_queue = eLazyBoolCalculate;
|
||||
std::string queue_name;
|
||||
QueueKind queue_kind = eQueueKindUnknown;
|
||||
uint64_t queue_serial = 0;
|
||||
uint64_t queue_serial_number = 0;
|
||||
// Iterate through all of the thread dictionary key/value pairs from the structured data dictionary
|
||||
|
||||
thread_dict->ForEach([this,
|
||||
@ -2286,9 +2185,11 @@ ProcessGDBRemote::SetThreadStopInfo (StructuredData::Dictionary *thread_dict)
|
||||
&exc_data,
|
||||
&thread_dispatch_qaddr,
|
||||
&queue_vars_valid,
|
||||
&associated_with_dispatch_queue,
|
||||
&dispatch_queue_t,
|
||||
&queue_name,
|
||||
&queue_kind,
|
||||
&queue_serial]
|
||||
&queue_serial_number]
|
||||
(ConstString key, StructuredData::Object* object) -> bool
|
||||
{
|
||||
if (key == g_key_tid)
|
||||
@ -2340,12 +2241,27 @@ ProcessGDBRemote::SetThreadStopInfo (StructuredData::Dictionary *thread_dict)
|
||||
queue_kind = eQueueKindConcurrent;
|
||||
}
|
||||
}
|
||||
else if (key == g_key_queue_serial)
|
||||
else if (key == g_key_queue_serial_number)
|
||||
{
|
||||
queue_serial = object->GetIntegerValue(0);
|
||||
if (queue_serial != 0)
|
||||
queue_serial_number = object->GetIntegerValue(0);
|
||||
if (queue_serial_number != 0)
|
||||
queue_vars_valid = true;
|
||||
}
|
||||
else if (key == g_key_dispatch_queue_t)
|
||||
{
|
||||
dispatch_queue_t = object->GetIntegerValue(0);
|
||||
if (dispatch_queue_t != 0 && dispatch_queue_t != LLDB_INVALID_ADDRESS)
|
||||
queue_vars_valid = true;
|
||||
}
|
||||
else if (key == g_key_associated_with_dispatch_queue)
|
||||
{
|
||||
queue_vars_valid = true;
|
||||
bool associated = object->GetBooleanValue ();
|
||||
if (associated)
|
||||
associated_with_dispatch_queue = eLazyBoolYes;
|
||||
else
|
||||
associated_with_dispatch_queue = eLazyBoolNo;
|
||||
}
|
||||
else if (key == g_key_reason)
|
||||
{
|
||||
reason = object->GetStringValue();
|
||||
@ -2416,9 +2332,11 @@ ProcessGDBRemote::SetThreadStopInfo (StructuredData::Dictionary *thread_dict)
|
||||
exc_data,
|
||||
thread_dispatch_qaddr,
|
||||
queue_vars_valid,
|
||||
associated_with_dispatch_queue,
|
||||
dispatch_queue_t,
|
||||
queue_name,
|
||||
queue_kind,
|
||||
queue_serial);
|
||||
queue_serial_number);
|
||||
}
|
||||
|
||||
StateType
|
||||
@ -2461,9 +2379,11 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet)
|
||||
std::vector<addr_t> exc_data;
|
||||
addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS;
|
||||
bool queue_vars_valid = false; // says if locals below that start with "queue_" are valid
|
||||
addr_t dispatch_queue_t = LLDB_INVALID_ADDRESS;
|
||||
LazyBool associated_with_dispatch_queue = eLazyBoolCalculate;
|
||||
std::string queue_name;
|
||||
QueueKind queue_kind = eQueueKindUnknown;
|
||||
uint64_t queue_serial = 0;
|
||||
uint64_t queue_serial_number = 0;
|
||||
ExpeditedRegisterMap expedited_register_map;
|
||||
while (stop_packet.GetNameColonValue(key, value))
|
||||
{
|
||||
@ -2554,6 +2474,11 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet)
|
||||
{
|
||||
thread_dispatch_qaddr = StringConvert::ToUInt64 (value.c_str(), 0, 16);
|
||||
}
|
||||
else if (key.compare("dispatch_queue_t") == 0)
|
||||
{
|
||||
queue_vars_valid = true;
|
||||
dispatch_queue_t = StringConvert::ToUInt64 (value.c_str(), 0, 16);
|
||||
}
|
||||
else if (key.compare("qname") == 0)
|
||||
{
|
||||
queue_vars_valid = true;
|
||||
@ -2577,10 +2502,10 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet)
|
||||
queue_kind = eQueueKindConcurrent;
|
||||
}
|
||||
}
|
||||
else if (key.compare("qserial") == 0)
|
||||
else if (key.compare("qserialnum") == 0)
|
||||
{
|
||||
queue_serial = StringConvert::ToUInt64 (value.c_str(), 0, 0);
|
||||
if (queue_serial != 0)
|
||||
queue_serial_number = StringConvert::ToUInt64 (value.c_str(), 0, 0);
|
||||
if (queue_serial_number != 0)
|
||||
queue_vars_valid = true;
|
||||
}
|
||||
else if (key.compare("reason") == 0)
|
||||
@ -2678,9 +2603,11 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet)
|
||||
exc_data,
|
||||
thread_dispatch_qaddr,
|
||||
queue_vars_valid,
|
||||
associated_with_dispatch_queue,
|
||||
dispatch_queue_t,
|
||||
queue_name,
|
||||
queue_kind,
|
||||
queue_serial);
|
||||
queue_serial_number);
|
||||
|
||||
return eStateStopped;
|
||||
}
|
||||
@ -3051,7 +2978,7 @@ ProcessGDBRemote::GetImageInfoAddress()
|
||||
// the loaded module list can also provides a link map address
|
||||
if (addr == LLDB_INVALID_ADDRESS)
|
||||
{
|
||||
GDBLoadedModuleInfoList list;
|
||||
LoadedModuleInfoList list;
|
||||
if (GetLoadedModuleList (list).Success())
|
||||
addr = list.m_link_map;
|
||||
}
|
||||
@ -4703,7 +4630,7 @@ ProcessGDBRemote::GetGDBServerRegisterInfo ()
|
||||
}
|
||||
|
||||
Error
|
||||
ProcessGDBRemote::GetLoadedModuleList (GDBLoadedModuleInfoList & list)
|
||||
ProcessGDBRemote::GetLoadedModuleList (LoadedModuleInfoList & list)
|
||||
{
|
||||
// Make sure LLDB has an XML parser it can use first
|
||||
if (!XMLDocument::XMLEnabled())
|
||||
@ -4747,7 +4674,7 @@ ProcessGDBRemote::GetLoadedModuleList (GDBLoadedModuleInfoList & list)
|
||||
|
||||
root_element.ForEachChildElementWithName("library", [log, &list](const XMLNode &library) -> bool {
|
||||
|
||||
GDBLoadedModuleInfoList::LoadedModuleInfo module;
|
||||
LoadedModuleInfoList::LoadedModuleInfo module;
|
||||
|
||||
library.ForEachAttribute([log, &module](const llvm::StringRef &name, const llvm::StringRef &value) -> bool {
|
||||
|
||||
@ -4817,7 +4744,7 @@ ProcessGDBRemote::GetLoadedModuleList (GDBLoadedModuleInfoList & list)
|
||||
return Error();
|
||||
|
||||
root_element.ForEachChildElementWithName("library", [log, &list](const XMLNode &library) -> bool {
|
||||
GDBLoadedModuleInfoList::LoadedModuleInfo module;
|
||||
LoadedModuleInfoList::LoadedModuleInfo module;
|
||||
|
||||
llvm::StringRef name = library.GetAttributeValue("name");
|
||||
module.set_name(name.str());
|
||||
@ -4879,19 +4806,18 @@ ProcessGDBRemote::LoadModuleAtAddress (const FileSpec &file, lldb::addr_t base_a
|
||||
}
|
||||
|
||||
size_t
|
||||
ProcessGDBRemote::LoadModules ()
|
||||
ProcessGDBRemote::LoadModules (LoadedModuleInfoList &module_list)
|
||||
{
|
||||
using lldb_private::process_gdb_remote::ProcessGDBRemote;
|
||||
|
||||
// request a list of loaded libraries from GDBServer
|
||||
GDBLoadedModuleInfoList module_list;
|
||||
if (GetLoadedModuleList (module_list).Fail())
|
||||
return 0;
|
||||
|
||||
// get a list of all the modules
|
||||
ModuleList new_modules;
|
||||
|
||||
for (GDBLoadedModuleInfoList::LoadedModuleInfo & modInfo : module_list.m_list)
|
||||
for (LoadedModuleInfoList::LoadedModuleInfo & modInfo : module_list.m_list)
|
||||
{
|
||||
std::string mod_name;
|
||||
lldb::addr_t mod_base;
|
||||
@ -4942,6 +4868,14 @@ ProcessGDBRemote::LoadModules ()
|
||||
}
|
||||
|
||||
return new_modules.GetSize();
|
||||
|
||||
}
|
||||
|
||||
size_t
|
||||
ProcessGDBRemote::LoadModules ()
|
||||
{
|
||||
LoadedModuleInfoList module_list;
|
||||
return LoadModules (module_list);
|
||||
}
|
||||
|
||||
Error
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "lldb/Core/StringList.h"
|
||||
#include "lldb/Core/StructuredData.h"
|
||||
#include "lldb/Core/ThreadSafeValue.h"
|
||||
#include "lldb/Core/LoadedModuleInfoList.h"
|
||||
#include "lldb/Host/HostThread.h"
|
||||
#include "lldb/lldb-private-forward.h"
|
||||
#include "lldb/Utility/StringExtractor.h"
|
||||
@ -244,6 +245,9 @@ class ProcessGDBRemote : public Process
|
||||
uint32_t &minor,
|
||||
uint32_t &update) override;
|
||||
|
||||
size_t
|
||||
LoadModules(LoadedModuleInfoList &module_list) override;
|
||||
|
||||
size_t
|
||||
LoadModules() override;
|
||||
|
||||
@ -261,8 +265,6 @@ class ProcessGDBRemote : public Process
|
||||
friend class GDBRemoteCommunicationClient;
|
||||
friend class GDBRemoteRegisterContext;
|
||||
|
||||
class GDBLoadedModuleInfoList;
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Broadcaster event bits definitions.
|
||||
//------------------------------------------------------------------
|
||||
@ -429,6 +431,8 @@ class ProcessGDBRemote : public Process
|
||||
const std::vector<lldb::addr_t> &exc_data,
|
||||
lldb::addr_t thread_dispatch_qaddr,
|
||||
bool queue_vars_valid,
|
||||
lldb_private::LazyBool associated_with_libdispatch_queue,
|
||||
lldb::addr_t dispatch_queue_t,
|
||||
std::string &queue_name,
|
||||
lldb::QueueKind queue_kind,
|
||||
uint64_t queue_serial);
|
||||
@ -461,7 +465,7 @@ class ProcessGDBRemote : public Process
|
||||
|
||||
// Query remote GDBServer for a detailed loaded library list
|
||||
Error
|
||||
GetLoadedModuleList (GDBLoadedModuleInfoList &);
|
||||
GetLoadedModuleList (LoadedModuleInfoList &);
|
||||
|
||||
lldb::ModuleSP
|
||||
LoadModuleAtAddress (const FileSpec &file, lldb::addr_t base_addr, bool value_is_offset);
|
||||
|
@ -41,8 +41,10 @@ ThreadGDBRemote::ThreadGDBRemote (Process &process, lldb::tid_t tid) :
|
||||
m_thread_name (),
|
||||
m_dispatch_queue_name (),
|
||||
m_thread_dispatch_qaddr (LLDB_INVALID_ADDRESS),
|
||||
m_queue_kind(eQueueKindUnknown),
|
||||
m_queue_serial(0)
|
||||
m_dispatch_queue_t (LLDB_INVALID_ADDRESS),
|
||||
m_queue_kind (eQueueKindUnknown),
|
||||
m_queue_serial_number (LLDB_INVALID_QUEUE_ID),
|
||||
m_associated_with_libdispatch_queue (eLazyBoolCalculate)
|
||||
{
|
||||
ProcessGDBRemoteLog::LogIf(GDBR_LOG_THREAD, "%p: ThreadGDBRemote::ThreadGDBRemote (pid = %i, tid = 0x%4.4x)",
|
||||
this,
|
||||
@ -73,15 +75,19 @@ ThreadGDBRemote::ClearQueueInfo ()
|
||||
{
|
||||
m_dispatch_queue_name.clear();
|
||||
m_queue_kind = eQueueKindUnknown;
|
||||
m_queue_serial = 0;
|
||||
m_queue_serial_number = 0;
|
||||
m_dispatch_queue_t = LLDB_INVALID_ADDRESS;
|
||||
m_associated_with_libdispatch_queue = eLazyBoolCalculate;
|
||||
}
|
||||
|
||||
void
|
||||
ThreadGDBRemote::SetQueueInfo (std::string &&queue_name, QueueKind queue_kind, uint64_t queue_serial)
|
||||
ThreadGDBRemote::SetQueueInfo (std::string &&queue_name, QueueKind queue_kind, uint64_t queue_serial, addr_t dispatch_queue_t, LazyBool associated_with_libdispatch_queue)
|
||||
{
|
||||
m_dispatch_queue_name = queue_name;
|
||||
m_queue_kind = queue_kind;
|
||||
m_queue_serial = queue_serial;
|
||||
m_queue_serial_number = queue_serial;
|
||||
m_dispatch_queue_t = dispatch_queue_t;
|
||||
m_associated_with_libdispatch_queue = associated_with_libdispatch_queue;
|
||||
}
|
||||
|
||||
|
||||
@ -100,7 +106,10 @@ ThreadGDBRemote::GetQueueName ()
|
||||
}
|
||||
// Always re-fetch the dispatch queue name since it can change
|
||||
|
||||
if (m_thread_dispatch_qaddr != 0 || m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS)
|
||||
if (m_associated_with_libdispatch_queue == eLazyBoolNo)
|
||||
return nullptr;
|
||||
|
||||
if (m_thread_dispatch_qaddr != 0 && m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS)
|
||||
{
|
||||
ProcessSP process_sp (GetProcess());
|
||||
if (process_sp)
|
||||
@ -118,6 +127,35 @@ ThreadGDBRemote::GetQueueName ()
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QueueKind
|
||||
ThreadGDBRemote::GetQueueKind ()
|
||||
{
|
||||
// If our cached queue info is valid, then someone called ThreadGDBRemote::SetQueueInfo(...)
|
||||
// with valid information that was gleaned from the stop reply packet. In this case we trust
|
||||
// that the info is valid in m_dispatch_queue_name without refetching it
|
||||
if (CachedQueueInfoIsValid())
|
||||
{
|
||||
return m_queue_kind;
|
||||
}
|
||||
|
||||
if (m_associated_with_libdispatch_queue == eLazyBoolNo)
|
||||
return eQueueKindUnknown;
|
||||
|
||||
if (m_thread_dispatch_qaddr != 0 && m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS)
|
||||
{
|
||||
ProcessSP process_sp (GetProcess());
|
||||
if (process_sp)
|
||||
{
|
||||
SystemRuntime *runtime = process_sp->GetSystemRuntime ();
|
||||
if (runtime)
|
||||
m_queue_kind = runtime->GetQueueKind (m_thread_dispatch_qaddr);
|
||||
return m_queue_kind;
|
||||
}
|
||||
}
|
||||
return eQueueKindUnknown;
|
||||
}
|
||||
|
||||
|
||||
queue_id_t
|
||||
ThreadGDBRemote::GetQueueID ()
|
||||
{
|
||||
@ -125,9 +163,12 @@ ThreadGDBRemote::GetQueueID ()
|
||||
// with valid information that was gleaned from the stop reply packet. In this case we trust
|
||||
// that the info is valid in m_dispatch_queue_name without refetching it
|
||||
if (CachedQueueInfoIsValid())
|
||||
return m_queue_serial;
|
||||
return m_queue_serial_number;
|
||||
|
||||
if (m_thread_dispatch_qaddr != 0 || m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS)
|
||||
if (m_associated_with_libdispatch_queue == eLazyBoolNo)
|
||||
return LLDB_INVALID_QUEUE_ID;
|
||||
|
||||
if (m_thread_dispatch_qaddr != 0 && m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS)
|
||||
{
|
||||
ProcessSP process_sp (GetProcess());
|
||||
if (process_sp)
|
||||
@ -161,8 +202,9 @@ ThreadGDBRemote::GetQueue ()
|
||||
addr_t
|
||||
ThreadGDBRemote::GetQueueLibdispatchQueueAddress ()
|
||||
{
|
||||
addr_t dispatch_queue_t_addr = LLDB_INVALID_ADDRESS;
|
||||
if (m_thread_dispatch_qaddr != 0 || m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS)
|
||||
if (m_dispatch_queue_t == LLDB_INVALID_ADDRESS)
|
||||
{
|
||||
if (m_thread_dispatch_qaddr != 0 && m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS)
|
||||
{
|
||||
ProcessSP process_sp (GetProcess());
|
||||
if (process_sp)
|
||||
@ -170,11 +212,44 @@ ThreadGDBRemote::GetQueueLibdispatchQueueAddress ()
|
||||
SystemRuntime *runtime = process_sp->GetSystemRuntime ();
|
||||
if (runtime)
|
||||
{
|
||||
dispatch_queue_t_addr = runtime->GetLibdispatchQueueAddressFromThreadQAddress (m_thread_dispatch_qaddr);
|
||||
m_dispatch_queue_t = runtime->GetLibdispatchQueueAddressFromThreadQAddress (m_thread_dispatch_qaddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dispatch_queue_t_addr;
|
||||
}
|
||||
return m_dispatch_queue_t;
|
||||
}
|
||||
|
||||
void
|
||||
ThreadGDBRemote::SetQueueLibdispatchQueueAddress (lldb::addr_t dispatch_queue_t)
|
||||
{
|
||||
m_dispatch_queue_t = dispatch_queue_t;
|
||||
}
|
||||
|
||||
bool
|
||||
ThreadGDBRemote::ThreadHasQueueInformation () const
|
||||
{
|
||||
if (m_thread_dispatch_qaddr != 0
|
||||
&& m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS
|
||||
&& m_dispatch_queue_t != LLDB_INVALID_ADDRESS
|
||||
&& m_queue_kind != eQueueKindUnknown
|
||||
&& m_queue_serial_number != 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
LazyBool
|
||||
ThreadGDBRemote::GetAssociatedWithLibdispatchQueue ()
|
||||
{
|
||||
return m_associated_with_libdispatch_queue;
|
||||
}
|
||||
|
||||
void
|
||||
ThreadGDBRemote::SetAssociatedWithLibdispatchQueue (LazyBool associated_with_libdispatch_queue)
|
||||
{
|
||||
m_associated_with_libdispatch_queue = associated_with_libdispatch_queue;
|
||||
}
|
||||
|
||||
StructuredData::ObjectSP
|
||||
|
@ -46,6 +46,9 @@ class ThreadGDBRemote : public Thread
|
||||
const char *
|
||||
GetQueueName () override;
|
||||
|
||||
lldb::QueueKind
|
||||
GetQueueKind () override;
|
||||
|
||||
lldb::queue_id_t
|
||||
GetQueueID () override;
|
||||
|
||||
@ -55,6 +58,12 @@ class ThreadGDBRemote : public Thread
|
||||
lldb::addr_t
|
||||
GetQueueLibdispatchQueueAddress () override;
|
||||
|
||||
void
|
||||
SetQueueLibdispatchQueueAddress (lldb::addr_t dispatch_queue_t) override;
|
||||
|
||||
bool
|
||||
ThreadHasQueueInformation () const override;
|
||||
|
||||
lldb::RegisterContextSP
|
||||
GetRegisterContext () override;
|
||||
|
||||
@ -98,7 +107,13 @@ class ThreadGDBRemote : public Thread
|
||||
ClearQueueInfo ();
|
||||
|
||||
void
|
||||
SetQueueInfo (std::string &&queue_name, lldb::QueueKind queue_kind, uint64_t queue_serial);
|
||||
SetQueueInfo (std::string &&queue_name, lldb::QueueKind queue_kind, uint64_t queue_serial, lldb::addr_t dispatch_queue_t, lldb_private::LazyBool associated_with_libdispatch_queue);
|
||||
|
||||
lldb_private::LazyBool
|
||||
GetAssociatedWithLibdispatchQueue () override;
|
||||
|
||||
void
|
||||
SetAssociatedWithLibdispatchQueue (lldb_private::LazyBool associated_with_libdispatch_queue) override;
|
||||
|
||||
StructuredData::ObjectSP
|
||||
FetchThreadExtendedInfo () override;
|
||||
@ -109,8 +124,10 @@ class ThreadGDBRemote : public Thread
|
||||
std::string m_thread_name;
|
||||
std::string m_dispatch_queue_name;
|
||||
lldb::addr_t m_thread_dispatch_qaddr;
|
||||
lldb::addr_t m_dispatch_queue_t;
|
||||
lldb::QueueKind m_queue_kind; // Queue info from stop reply/stop info for thread
|
||||
uint64_t m_queue_serial; // Queue info from stop reply/stop info for thread
|
||||
uint64_t m_queue_serial_number; // Queue info from stop reply/stop info for thread
|
||||
lldb_private::LazyBool m_associated_with_libdispatch_queue;
|
||||
|
||||
bool
|
||||
PrivateSetRegisterValue (uint32_t reg,
|
||||
|
@ -77,6 +77,10 @@ PythonObject::GetObjectType() const
|
||||
return PyObjectType::Dictionary;
|
||||
if (PythonString::Check(m_py_obj))
|
||||
return PyObjectType::String;
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
if (PythonBytes::Check(m_py_obj))
|
||||
return PyObjectType::Bytes;
|
||||
#endif
|
||||
if (PythonInteger::Check(m_py_obj))
|
||||
return PyObjectType::Integer;
|
||||
if (PythonFile::Check(m_py_obj))
|
||||
@ -210,6 +214,8 @@ PythonObject::CreateStructuredObject() const
|
||||
return PythonList(PyRefType::Borrowed, m_py_obj).CreateStructuredArray();
|
||||
case PyObjectType::String:
|
||||
return PythonString(PyRefType::Borrowed, m_py_obj).CreateStructuredString();
|
||||
case PyObjectType::Bytes:
|
||||
return PythonBytes(PyRefType::Borrowed, m_py_obj).CreateStructuredString();
|
||||
case PyObjectType::None:
|
||||
return StructuredData::ObjectSP();
|
||||
default:
|
||||
@ -217,6 +223,104 @@ PythonObject::CreateStructuredObject() const
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// PythonString
|
||||
//----------------------------------------------------------------------
|
||||
PythonBytes::PythonBytes() : PythonObject()
|
||||
{
|
||||
}
|
||||
|
||||
PythonBytes::PythonBytes(llvm::ArrayRef<uint8_t> bytes) : PythonObject()
|
||||
{
|
||||
SetBytes(bytes);
|
||||
}
|
||||
|
||||
PythonBytes::PythonBytes(const uint8_t *bytes, size_t length) : PythonObject()
|
||||
{
|
||||
SetBytes(llvm::ArrayRef<uint8_t>(bytes, length));
|
||||
}
|
||||
|
||||
PythonBytes::PythonBytes(PyRefType type, PyObject *py_obj) : PythonObject()
|
||||
{
|
||||
Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a string
|
||||
}
|
||||
|
||||
PythonBytes::PythonBytes(const PythonBytes &object) : PythonObject(object)
|
||||
{
|
||||
}
|
||||
|
||||
PythonBytes::~PythonBytes()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
PythonBytes::Check(PyObject *py_obj)
|
||||
{
|
||||
if (!py_obj)
|
||||
return false;
|
||||
if (PyBytes_Check(py_obj))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
PythonBytes::Reset(PyRefType type, PyObject *py_obj)
|
||||
{
|
||||
// Grab the desired reference type so that if we end up rejecting
|
||||
// `py_obj` it still gets decremented if necessary.
|
||||
PythonObject result(type, py_obj);
|
||||
|
||||
if (!PythonBytes::Check(py_obj))
|
||||
{
|
||||
PythonObject::Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
// Calling PythonObject::Reset(const PythonObject&) will lead to stack overflow since it calls
|
||||
// back into the virtual implementation.
|
||||
PythonObject::Reset(PyRefType::Borrowed, result.get());
|
||||
}
|
||||
|
||||
llvm::ArrayRef<uint8_t>
|
||||
PythonBytes::GetBytes() const
|
||||
{
|
||||
if (!IsValid())
|
||||
return llvm::ArrayRef<uint8_t>();
|
||||
|
||||
Py_ssize_t size;
|
||||
char *c;
|
||||
|
||||
PyBytes_AsStringAndSize(m_py_obj, &c, &size);
|
||||
return llvm::ArrayRef<uint8_t>(reinterpret_cast<uint8_t *>(c), size);
|
||||
}
|
||||
|
||||
size_t
|
||||
PythonBytes::GetSize() const
|
||||
{
|
||||
if (!IsValid())
|
||||
return 0;
|
||||
return PyBytes_Size(m_py_obj);
|
||||
}
|
||||
|
||||
void
|
||||
PythonBytes::SetBytes(llvm::ArrayRef<uint8_t> bytes)
|
||||
{
|
||||
const char *data = reinterpret_cast<const char *>(bytes.data());
|
||||
PyObject *py_bytes = PyBytes_FromStringAndSize(data, bytes.size());
|
||||
PythonObject::Reset(PyRefType::Owned, py_bytes);
|
||||
}
|
||||
|
||||
StructuredData::StringSP
|
||||
PythonBytes::CreateStructuredString() const
|
||||
{
|
||||
StructuredData::StringSP result(new StructuredData::String);
|
||||
Py_ssize_t size;
|
||||
char *c;
|
||||
PyBytes_AsStringAndSize(m_py_obj, &c, &size);
|
||||
result->SetValue(std::string(c, size));
|
||||
return result;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// PythonString
|
||||
//----------------------------------------------------------------------
|
||||
|
@ -23,8 +23,11 @@
|
||||
#include "lldb/Host/File.h"
|
||||
#include "lldb/Interpreter/OptionValue.h"
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
|
||||
namespace lldb_private {
|
||||
|
||||
class PythonBytes;
|
||||
class PythonString;
|
||||
class PythonList;
|
||||
class PythonDictionary;
|
||||
@ -71,6 +74,7 @@ enum class PyObjectType
|
||||
Dictionary,
|
||||
List,
|
||||
String,
|
||||
Bytes,
|
||||
Module,
|
||||
Callable,
|
||||
Tuple,
|
||||
@ -256,6 +260,39 @@ class PythonObject
|
||||
PyObject* m_py_obj;
|
||||
};
|
||||
|
||||
class PythonBytes : public PythonObject
|
||||
{
|
||||
public:
|
||||
PythonBytes();
|
||||
explicit PythonBytes(llvm::ArrayRef<uint8_t> bytes);
|
||||
PythonBytes(const uint8_t *bytes, size_t length);
|
||||
PythonBytes(PyRefType type, PyObject *o);
|
||||
PythonBytes(const PythonBytes &object);
|
||||
|
||||
~PythonBytes() override;
|
||||
|
||||
static bool
|
||||
Check(PyObject *py_obj);
|
||||
|
||||
// Bring in the no-argument base class version
|
||||
using PythonObject::Reset;
|
||||
|
||||
void
|
||||
Reset(PyRefType type, PyObject *py_obj) override;
|
||||
|
||||
llvm::ArrayRef<uint8_t>
|
||||
GetBytes() const;
|
||||
|
||||
size_t
|
||||
GetSize() const;
|
||||
|
||||
void
|
||||
SetBytes(llvm::ArrayRef<uint8_t> stringbytes);
|
||||
|
||||
StructuredData::StringSP
|
||||
CreateStructuredString() const;
|
||||
};
|
||||
|
||||
class PythonString : public PythonObject
|
||||
{
|
||||
public:
|
||||
|
@ -1692,10 +1692,10 @@ ScriptInterpreterPython::OSPlugin_RegisterContextData(StructuredData::ObjectSP o
|
||||
PyErr_Clear();
|
||||
}
|
||||
|
||||
assert(PythonString::Check(py_return.get()) && "get_register_data returned unknown object type!");
|
||||
assert(PythonBytes::Check(py_return.get()) && "get_register_data returned unknown object type!");
|
||||
|
||||
PythonString result_string(PyRefType::Borrowed, py_return.get());
|
||||
return result_string.CreateStructuredString();
|
||||
PythonBytes result(PyRefType::Borrowed, py_return.get());
|
||||
return result.CreateStructuredString();
|
||||
}
|
||||
|
||||
StructuredData::DictionarySP
|
||||
|
@ -33,6 +33,18 @@ struct DIERef
|
||||
lldb::user_id_t
|
||||
GetUID() const;
|
||||
|
||||
bool
|
||||
operator< (const DIERef &ref) const
|
||||
{
|
||||
return die_offset < ref.die_offset;
|
||||
}
|
||||
|
||||
bool
|
||||
operator< (const DIERef &ref)
|
||||
{
|
||||
return die_offset < ref.die_offset;
|
||||
}
|
||||
|
||||
dw_offset_t cu_offset;
|
||||
dw_offset_t die_offset;
|
||||
};
|
||||
|
@ -2928,7 +2928,7 @@ DWARFASTParserClang::ParseChildMembers (const SymbolContext& sc,
|
||||
|
||||
if (member_byte_offset >= parent_byte_size)
|
||||
{
|
||||
if (member_array_size != 1)
|
||||
if (member_array_size != 1 && (member_array_size != 0 || member_byte_offset > parent_byte_size))
|
||||
{
|
||||
module_sp->ReportError ("0x%8.8" PRIx64 ": DW_TAG_member '%s' refers to type 0x%8.8" PRIx64 " which extends beyond the bounds of 0x%8.8" PRIx64,
|
||||
die.GetID(),
|
||||
|
@ -1065,12 +1065,17 @@ SymbolFileDWARF::ParseCompileUnitSupportFiles (const SymbolContext& sc, FileSpec
|
||||
const char * cu_comp_dir = resolveCompDir(cu_die.GetAttributeValueAsString(DW_AT_comp_dir, nullptr));
|
||||
|
||||
const dw_offset_t stmt_list = cu_die.GetAttributeValueAsUnsigned(DW_AT_stmt_list, DW_INVALID_OFFSET);
|
||||
|
||||
if (stmt_list != DW_INVALID_OFFSET)
|
||||
{
|
||||
// All file indexes in DWARF are one based and a file of index zero is
|
||||
// supposed to be the compile unit itself.
|
||||
support_files.Append (*sc.comp_unit);
|
||||
|
||||
return DWARFDebugLine::ParseSupportFiles(sc.comp_unit->GetModule(), get_debug_line_data(), cu_comp_dir, stmt_list, support_files);
|
||||
return DWARFDebugLine::ParseSupportFiles(sc.comp_unit->GetModule(),
|
||||
get_debug_line_data(),
|
||||
cu_comp_dir,
|
||||
stmt_list,
|
||||
support_files);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@ -2927,6 +2932,40 @@ SymbolFileDWARF::FindFunctions(const RegularExpression& regex, bool include_inli
|
||||
return sc_list.GetSize() - original_size;
|
||||
}
|
||||
|
||||
void
|
||||
SymbolFileDWARF::GetMangledNamesForFunction (const std::string &scope_qualified_name,
|
||||
std::vector<ConstString> &mangled_names)
|
||||
{
|
||||
DWARFDebugInfo* info = DebugInfo();
|
||||
uint32_t num_comp_units = 0;
|
||||
if (info)
|
||||
num_comp_units = info->GetNumCompileUnits();
|
||||
|
||||
for (uint32_t i = 0; i < num_comp_units; i++)
|
||||
{
|
||||
DWARFCompileUnit *cu = info->GetCompileUnitAtIndex(i);
|
||||
if (cu == nullptr)
|
||||
continue;
|
||||
|
||||
SymbolFileDWARFDwo *dwo = cu->GetDwoSymbolFile();
|
||||
if (dwo)
|
||||
dwo->GetMangledNamesForFunction(scope_qualified_name, mangled_names);
|
||||
}
|
||||
|
||||
NameToOffsetMap::iterator iter = m_function_scope_qualified_name_map.find(scope_qualified_name);
|
||||
if (iter == m_function_scope_qualified_name_map.end())
|
||||
return;
|
||||
|
||||
DIERefSetSP set_sp = (*iter).second;
|
||||
std::set<DIERef>::iterator set_iter;
|
||||
for (set_iter = set_sp->begin(); set_iter != set_sp->end(); set_iter++)
|
||||
{
|
||||
DWARFDIE die = DebugInfo()->GetDIE (*set_iter);
|
||||
mangled_names.push_back(ConstString(die.GetMangledName()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32_t
|
||||
SymbolFileDWARF::FindTypes (const SymbolContext& sc,
|
||||
const ConstString &name,
|
||||
@ -3751,6 +3790,24 @@ SymbolFileDWARF::ParseType (const SymbolContext& sc, const DWARFDIE &die, bool *
|
||||
TypeList* type_list = GetTypeList();
|
||||
if (type_list)
|
||||
type_list->Insert(type_sp);
|
||||
|
||||
if (die.Tag() == DW_TAG_subprogram)
|
||||
{
|
||||
DIERef die_ref = die.GetDIERef();
|
||||
std::string scope_qualified_name(GetDeclContextForUID(die.GetID()).GetScopeQualifiedName().AsCString(""));
|
||||
if (scope_qualified_name.size())
|
||||
{
|
||||
NameToOffsetMap::iterator iter = m_function_scope_qualified_name_map.find(scope_qualified_name);
|
||||
if (iter != m_function_scope_qualified_name_map.end())
|
||||
(*iter).second->insert(die_ref);
|
||||
else
|
||||
{
|
||||
DIERefSetSP new_set(new std::set<DIERef>);
|
||||
new_set->insert(die_ref);
|
||||
m_function_scope_qualified_name_map.emplace(std::make_pair(scope_qualified_name, new_set));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -208,6 +208,10 @@ class SymbolFileDWARF : public lldb_private::SymbolFile, public lldb_private::Us
|
||||
bool append,
|
||||
lldb_private::SymbolContextList& sc_list) override;
|
||||
|
||||
void
|
||||
GetMangledNamesForFunction (const std::string &scope_qualified_name,
|
||||
std::vector<lldb_private::ConstString> &mangled_names) override;
|
||||
|
||||
uint32_t
|
||||
FindTypes (const lldb_private::SymbolContext& sc,
|
||||
const lldb_private::ConstString &name,
|
||||
@ -577,6 +581,9 @@ class SymbolFileDWARF : public lldb_private::SymbolFile, public lldb_private::Us
|
||||
m_fetched_external_modules:1;
|
||||
lldb_private::LazyBool m_supports_DW_AT_APPLE_objc_complete_type;
|
||||
|
||||
typedef std::shared_ptr<std::set<DIERef> > DIERefSetSP;
|
||||
typedef std::unordered_map<std::string, DIERefSetSP> NameToOffsetMap;
|
||||
NameToOffsetMap m_function_scope_qualified_name_map;
|
||||
std::unique_ptr<DWARFDebugRanges> m_ranges;
|
||||
UniqueDWARFASTTypeMap m_unique_ast_type_map;
|
||||
DIEToTypePtr m_die_to_type;
|
||||
|
@ -724,18 +724,30 @@ SystemRuntimeMacOSX::PopulateQueueList (lldb_private::QueueList &queue_list)
|
||||
// queue on thread 1 is always around.
|
||||
|
||||
for (ThreadSP thread_sp : m_process->Threads())
|
||||
{
|
||||
if (thread_sp->GetAssociatedWithLibdispatchQueue () != eLazyBoolNo)
|
||||
{
|
||||
if (thread_sp->GetQueueID() != LLDB_INVALID_QUEUE_ID)
|
||||
{
|
||||
if (queue_list.FindQueueByID (thread_sp->GetQueueID()).get() == NULL)
|
||||
{
|
||||
QueueSP queue_sp (new Queue(m_process->shared_from_this(), thread_sp->GetQueueID(), thread_sp->GetQueueName()));
|
||||
if (thread_sp->ThreadHasQueueInformation ())
|
||||
{
|
||||
queue_sp->SetKind (thread_sp->GetQueueKind ());
|
||||
queue_sp->SetLibdispatchQueueAddress (thread_sp->GetQueueLibdispatchQueueAddress());
|
||||
queue_list.AddQueue (queue_sp);
|
||||
}
|
||||
else
|
||||
{
|
||||
queue_sp->SetKind (GetQueueKind (thread_sp->GetQueueLibdispatchQueueAddress()));
|
||||
queue_sp->SetLibdispatchQueueAddress (thread_sp->GetQueueLibdispatchQueueAddress());
|
||||
queue_list.AddQueue (queue_sp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns either an array of introspection_dispatch_item_info_ref's for the pending items on
|
||||
|
@ -104,8 +104,8 @@ class SystemRuntimeMacOSX : public lldb_private::SystemRuntime
|
||||
void
|
||||
CompleteQueueItem(lldb_private::QueueItem *queue_item, lldb::addr_t item_ref) override;
|
||||
|
||||
virtual lldb::QueueKind
|
||||
GetQueueKind (lldb::addr_t dispatch_queue_addr);
|
||||
lldb::QueueKind
|
||||
GetQueueKind (lldb::addr_t dispatch_queue_addr) override;
|
||||
|
||||
void
|
||||
AddThreadExtendedInfoPacketHints(lldb_private::StructuredData::ObjectSP dict) override;
|
||||
|
@ -155,6 +155,7 @@ class AssemblyParse_x86
|
||||
bool mov_reg_to_local_stack_frame_p (int& regno, int& fp_offset);
|
||||
bool ret_pattern_p ();
|
||||
bool pop_rbp_pattern_p ();
|
||||
bool leave_pattern_p ();
|
||||
bool call_next_insn_pattern_p();
|
||||
uint32_t extract_4 (uint8_t *b);
|
||||
bool machine_regno_to_lldb_regno (int machine_regno, uint32_t& lldb_regno);
|
||||
@ -492,6 +493,14 @@ AssemblyParse_x86::pop_rbp_pattern_p ()
|
||||
return (*p == 0x5d);
|
||||
}
|
||||
|
||||
// leave [0xc9]
|
||||
bool
|
||||
AssemblyParse_x86::leave_pattern_p ()
|
||||
{
|
||||
uint8_t *p = m_cur_insn_bytes;
|
||||
return (*p == 0xc9);
|
||||
}
|
||||
|
||||
// call $0 [0xe8 0x0 0x0 0x0 0x0]
|
||||
bool
|
||||
AssemblyParse_x86::call_next_insn_pattern_p ()
|
||||
@ -780,8 +789,7 @@ AssemblyParse_x86::get_non_call_site_unwind_plan (UnwindPlan &unwind_plan)
|
||||
|
||||
if (machine_regno == (int)m_machine_fp_regnum)
|
||||
{
|
||||
row->GetCFAValue().SetIsRegisterPlusOffset (m_lldb_sp_regnum,
|
||||
row->GetCFAValue().GetOffset());
|
||||
row->GetCFAValue().SetIsRegisterPlusOffset (m_lldb_sp_regnum, row->GetCFAValue().GetOffset());
|
||||
}
|
||||
|
||||
in_epilogue = true;
|
||||
@ -792,12 +800,35 @@ AssemblyParse_x86::get_non_call_site_unwind_plan (UnwindPlan &unwind_plan)
|
||||
// we need to add a new row of instructions.
|
||||
if (row->GetCFAValue().GetRegisterNumber() == m_lldb_sp_regnum)
|
||||
{
|
||||
row->GetCFAValue().SetIsRegisterPlusOffset(m_lldb_sp_regnum,
|
||||
current_sp_bytes_offset_from_cfa);
|
||||
row->GetCFAValue().SetIsRegisterPlusOffset (m_lldb_sp_regnum, current_sp_bytes_offset_from_cfa);
|
||||
row_updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
// The LEAVE instruction moves the value from rbp into rsp and pops
|
||||
// a value off the stack into rbp (restoring the caller's rbp value).
|
||||
// It is the opposite of ENTER, or 'push rbp, mov rsp rbp'.
|
||||
else if (leave_pattern_p ())
|
||||
{
|
||||
// We're going to copy the value in rbp into rsp, so re-set the sp offset
|
||||
// based on the CFAValue. Also, adjust it to recognize that we're popping
|
||||
// the saved rbp value off the stack.
|
||||
current_sp_bytes_offset_from_cfa = row->GetCFAValue().GetOffset();
|
||||
current_sp_bytes_offset_from_cfa -= m_wordsize;
|
||||
row->GetCFAValue().SetOffset (current_sp_bytes_offset_from_cfa);
|
||||
|
||||
// rbp is restored to the caller's value
|
||||
saved_registers[m_machine_fp_regnum] = false;
|
||||
row->RemoveRegisterInfo (m_lldb_fp_regnum);
|
||||
|
||||
// cfa is now in terms of rsp again.
|
||||
row->GetCFAValue().SetIsRegisterPlusOffset (m_lldb_sp_regnum, row->GetCFAValue().GetOffset());
|
||||
row->GetCFAValue().SetOffset (current_sp_bytes_offset_from_cfa);
|
||||
|
||||
in_epilogue = true;
|
||||
row_updated = true;
|
||||
}
|
||||
|
||||
else if (mov_reg_to_local_stack_frame_p (machine_regno, stack_offset)
|
||||
&& nonvolatile_reg_p (machine_regno)
|
||||
&& machine_regno_to_lldb_regno (machine_regno, lldb_regno)
|
||||
@ -1137,15 +1168,15 @@ AssemblyParse_x86::augment_unwind_plan_from_call_site (AddressRange& func, Unwin
|
||||
// The only case we care about is epilogue:
|
||||
// [0x5d] pop %rbp/%ebp
|
||||
// => [0xc3] ret
|
||||
if (pop_rbp_pattern_p ())
|
||||
if (pop_rbp_pattern_p () || leave_pattern_p ())
|
||||
{
|
||||
if (target->ReadMemory (m_cur_insn, prefer_file_cache, m_cur_insn_bytes,
|
||||
1, error) != static_cast<size_t>(-1)
|
||||
&& ret_pattern_p ())
|
||||
{
|
||||
row->SetOffset (offset);
|
||||
row->GetCFAValue().SetIsRegisterPlusOffset (
|
||||
first_row->GetCFAValue().GetRegisterNumber(), m_wordsize);
|
||||
row->GetCFAValue().SetIsRegisterPlusOffset (first_row->GetCFAValue().GetRegisterNumber(),
|
||||
m_wordsize);
|
||||
|
||||
UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
|
||||
unwind_plan.InsertRow (new_row);
|
||||
|
@ -4158,6 +4158,7 @@ ClangASTContext::GetTypeClass (lldb::opaque_compiler_type_t type)
|
||||
case clang::Type::Decltype: break;
|
||||
case clang::Type::TemplateSpecialization: break;
|
||||
case clang::Type::Atomic: break;
|
||||
case clang::Type::Pipe: break;
|
||||
|
||||
// pointer type decayed from an array or function type.
|
||||
case clang::Type::Decayed: break;
|
||||
@ -4891,6 +4892,7 @@ ClangASTContext::GetEncoding (lldb::opaque_compiler_type_t type, uint64_t &count
|
||||
case clang::Type::TemplateSpecialization:
|
||||
case clang::Type::Atomic:
|
||||
case clang::Type::Adjusted:
|
||||
case clang::Type::Pipe:
|
||||
break;
|
||||
|
||||
// pointer type decayed from an array or function type.
|
||||
@ -5008,6 +5010,7 @@ ClangASTContext::GetFormat (lldb::opaque_compiler_type_t type)
|
||||
case clang::Type::TemplateSpecialization:
|
||||
case clang::Type::Atomic:
|
||||
case clang::Type::Adjusted:
|
||||
case clang::Type::Pipe:
|
||||
break;
|
||||
|
||||
// pointer type decayed from an array or function type.
|
||||
@ -9969,6 +9972,18 @@ ClangASTContext::DeclContextGetName (void *opaque_decl_ctx)
|
||||
return ConstString();
|
||||
}
|
||||
|
||||
ConstString
|
||||
ClangASTContext::DeclContextGetScopeQualifiedName (void *opaque_decl_ctx)
|
||||
{
|
||||
if (opaque_decl_ctx)
|
||||
{
|
||||
clang::NamedDecl *named_decl = llvm::dyn_cast<clang::NamedDecl>((clang::DeclContext *)opaque_decl_ctx);
|
||||
if (named_decl)
|
||||
return ConstString(llvm::StringRef(named_decl->getQualifiedNameAsString()));
|
||||
}
|
||||
return ConstString();
|
||||
}
|
||||
|
||||
bool
|
||||
ClangASTContext::DeclContextIsClassMethod (void *opaque_decl_ctx,
|
||||
lldb::LanguageType *language_ptr,
|
||||
|
@ -38,6 +38,15 @@ CompilerDeclContext::GetName () const
|
||||
return ConstString();
|
||||
}
|
||||
|
||||
ConstString
|
||||
CompilerDeclContext::GetScopeQualifiedName () const
|
||||
{
|
||||
if (IsValid())
|
||||
return m_type_system->DeclContextGetScopeQualifiedName(m_opaque_decl_ctx);
|
||||
else
|
||||
return ConstString();
|
||||
}
|
||||
|
||||
bool
|
||||
CompilerDeclContext::IsStructUnionOrClass () const
|
||||
{
|
||||
|
@ -143,6 +143,13 @@ LineTable::InsertSequence (LineSequence* sequence)
|
||||
entry_collection::iterator end_pos = m_entries.end();
|
||||
LineTable::Entry::LessThanBinaryPredicate less_than_bp(this);
|
||||
entry_collection::iterator pos = upper_bound(begin_pos, end_pos, entry, less_than_bp);
|
||||
|
||||
// We should never insert a sequence in the middle of another sequence
|
||||
if (pos != begin_pos) {
|
||||
while (pos < end_pos && !((pos - 1)->is_terminal_entry))
|
||||
pos++;
|
||||
}
|
||||
|
||||
#ifdef LLDB_CONFIGURATION_DEBUG
|
||||
// If we aren't inserting at the beginning, the previous entry should
|
||||
// terminate a sequence.
|
||||
|
@ -134,6 +134,12 @@ SymbolFile::FindFunctions (const RegularExpression& regex, bool include_inlines,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
SymbolFile::GetMangledNamesForFunction(const std::string &scope_qualified_name, std::vector<ConstString> &mangled_names)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
SymbolFile::FindTypes (const SymbolContext& sc, const ConstString &name, const CompilerDeclContext *parent_decl_ctx, bool append, uint32_t max_matches, TypeMap& types)
|
||||
{
|
||||
|
@ -6515,3 +6515,65 @@ Process::ResetImageToken(size_t token)
|
||||
if (token < m_image_tokens.size())
|
||||
m_image_tokens[token] = LLDB_INVALID_IMAGE_TOKEN;
|
||||
}
|
||||
|
||||
Address
|
||||
Process::AdvanceAddressToNextBranchInstruction (Address default_stop_addr, AddressRange range_bounds)
|
||||
{
|
||||
Target &target = GetTarget();
|
||||
DisassemblerSP disassembler_sp;
|
||||
InstructionList *insn_list = NULL;
|
||||
|
||||
Address retval = default_stop_addr;
|
||||
|
||||
if (target.GetUseFastStepping() == false)
|
||||
return retval;
|
||||
if (default_stop_addr.IsValid() == false)
|
||||
return retval;
|
||||
|
||||
ExecutionContext exe_ctx (this);
|
||||
const char *plugin_name = nullptr;
|
||||
const char *flavor = nullptr;
|
||||
const bool prefer_file_cache = true;
|
||||
disassembler_sp = Disassembler::DisassembleRange(target.GetArchitecture(),
|
||||
plugin_name,
|
||||
flavor,
|
||||
exe_ctx,
|
||||
range_bounds,
|
||||
prefer_file_cache);
|
||||
if (disassembler_sp.get())
|
||||
insn_list = &disassembler_sp->GetInstructionList();
|
||||
|
||||
if (insn_list == NULL)
|
||||
{
|
||||
return retval;
|
||||
}
|
||||
|
||||
size_t insn_offset = insn_list->GetIndexOfInstructionAtAddress (default_stop_addr);
|
||||
if (insn_offset == UINT32_MAX)
|
||||
{
|
||||
return retval;
|
||||
}
|
||||
|
||||
uint32_t branch_index = insn_list->GetIndexOfNextBranchInstruction (insn_offset, target);
|
||||
if (branch_index == UINT32_MAX)
|
||||
{
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (branch_index > insn_offset)
|
||||
{
|
||||
Address next_branch_insn_address = insn_list->GetInstructionAtIndex (branch_index)->GetAddress();
|
||||
if (next_branch_insn_address.IsValid() && range_bounds.ContainsFileAddress (next_branch_insn_address))
|
||||
{
|
||||
retval = next_branch_insn_address;
|
||||
}
|
||||
}
|
||||
|
||||
if (disassembler_sp.get())
|
||||
{
|
||||
// FIXME: The DisassemblerLLVMC has a reference cycle and won't go away if it has any active instructions.
|
||||
disassembler_sp->GetInstructionList().Clear();
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user