Vendor import of lldb trunk r302418:
https://llvm.org/svn/llvm-project/lldb/trunk@302418
This commit is contained in:
parent
52fd8de56a
commit
8b4000f13b
@ -103,6 +103,8 @@ class LLDB_API SBAddress {
|
||||
|
||||
const lldb_private::Address *operator->() const;
|
||||
|
||||
friend bool operator==(const SBAddress &lhs, const SBAddress &rhs);
|
||||
|
||||
lldb_private::Address *get();
|
||||
|
||||
lldb_private::Address &ref();
|
||||
@ -117,6 +119,8 @@ class LLDB_API SBAddress {
|
||||
std::unique_ptr<lldb_private::Address> m_opaque_ap;
|
||||
};
|
||||
|
||||
bool operator==(const SBAddress &lhs, const SBAddress &rhs);
|
||||
|
||||
} // namespace lldb
|
||||
|
||||
#endif // LLDB_SBAddress_h_
|
||||
|
@ -53,6 +53,8 @@ class LLDB_API SBInstruction {
|
||||
|
||||
bool HasDelaySlot();
|
||||
|
||||
bool CanSetBreakpoint();
|
||||
|
||||
void Print(FILE *out);
|
||||
|
||||
bool GetDescription(lldb::SBStream &description);
|
||||
|
@ -32,6 +32,15 @@ class LLDB_API SBInstructionList {
|
||||
|
||||
lldb::SBInstruction GetInstructionAtIndex(uint32_t idx);
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Returns the number of instructions between the start and end address.
|
||||
// If canSetBreakpoint is true then the count will be the number of
|
||||
// instructions on which a breakpoint can be set.
|
||||
// ----------------------------------------------------------------------
|
||||
size_t GetInstructionsCount(const SBAddress &start,
|
||||
const SBAddress &end,
|
||||
bool canSetBreakpoint = false);
|
||||
|
||||
void Clear();
|
||||
|
||||
void AppendInstruction(lldb::SBInstruction inst);
|
||||
|
@ -173,6 +173,8 @@ class Instruction {
|
||||
|
||||
virtual bool HasDelaySlot();
|
||||
|
||||
bool CanSetBreakpoint ();
|
||||
|
||||
virtual size_t Decode(const Disassembler &disassembler,
|
||||
const DataExtractor &data,
|
||||
lldb::offset_t data_offset) = 0;
|
||||
|
@ -99,6 +99,16 @@ class Expression {
|
||||
//------------------------------------------------------------------
|
||||
lldb::addr_t StartAddress() { return m_jit_start_addr; }
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Called to notify the expression that it is about to be executed.
|
||||
//------------------------------------------------------------------
|
||||
virtual void WillStartExecuting() {}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Called to notify the expression that its execution has finished.
|
||||
//------------------------------------------------------------------
|
||||
virtual void DidFinishExecuting() {}
|
||||
|
||||
virtual ExpressionTypeSystemHelper *GetTypeSystemHelper() { return nullptr; }
|
||||
|
||||
protected:
|
||||
|
@ -42,6 +42,7 @@ class MainLoop : public MainLoopBase {
|
||||
public:
|
||||
typedef std::unique_ptr<SignalHandle> SignalHandleUP;
|
||||
|
||||
MainLoop();
|
||||
~MainLoop() override;
|
||||
|
||||
ReadHandleUP RegisterReadObject(const lldb::IOObjectSP &object_sp,
|
||||
@ -71,6 +72,9 @@ class MainLoop : public MainLoopBase {
|
||||
void UnregisterSignal(int signo);
|
||||
|
||||
private:
|
||||
void ProcessReadObject(IOObject::WaitableHandle handle);
|
||||
void ProcessSignal(int signo);
|
||||
|
||||
class SignalHandle {
|
||||
public:
|
||||
~SignalHandle() { m_mainloop.UnregisterSignal(m_signo); }
|
||||
@ -97,6 +101,9 @@ class MainLoop : public MainLoopBase {
|
||||
|
||||
llvm::DenseMap<IOObject::WaitableHandle, Callback> m_read_fds;
|
||||
llvm::DenseMap<int, SignalInfo> m_signals;
|
||||
#if HAVE_SYS_EVENT_H
|
||||
int m_kqueue;
|
||||
#endif
|
||||
bool m_terminate_request : 1;
|
||||
};
|
||||
|
||||
|
@ -21,15 +21,13 @@ class UDPSocket : public Socket {
|
||||
Socket *&socket);
|
||||
|
||||
private:
|
||||
UDPSocket(NativeSocket socket, const UDPSocket &listen_socket);
|
||||
UDPSocket(NativeSocket socket);
|
||||
|
||||
size_t Send(const void *buf, const size_t num_bytes) override;
|
||||
Error Connect(llvm::StringRef name) override;
|
||||
Error Listen(llvm::StringRef name, int backlog) override;
|
||||
Error Accept(Socket *&socket) override;
|
||||
|
||||
Error CreateSocket();
|
||||
|
||||
SocketAddress m_sockaddr;
|
||||
};
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ class ThreadPlanCallFunction : public ThreadPlan {
|
||||
lldb::addr_t &start_load_addr,
|
||||
lldb::addr_t &function_load_addr);
|
||||
|
||||
void DoTakedown(bool success);
|
||||
virtual void DoTakedown(bool success);
|
||||
|
||||
void SetBreakpoints();
|
||||
|
||||
|
@ -35,6 +35,8 @@ class ThreadPlanCallUserExpression : public ThreadPlanCallFunction {
|
||||
|
||||
void GetDescription(Stream *s, lldb::DescriptionLevel level) override;
|
||||
|
||||
void DidPush() override;
|
||||
|
||||
void WillPop() override;
|
||||
|
||||
lldb::StopInfoSP GetRealStopInfo() override;
|
||||
@ -48,6 +50,7 @@ class ThreadPlanCallUserExpression : public ThreadPlanCallFunction {
|
||||
}
|
||||
|
||||
protected:
|
||||
void DoTakedown(bool success) override;
|
||||
private:
|
||||
lldb::UserExpressionSP
|
||||
m_user_expression_sp; // This is currently just used to ensure the
|
||||
|
@ -53,50 +53,6 @@ class TaskPool {
|
||||
static void AddTaskImpl(std::function<void()> &&task_fn);
|
||||
};
|
||||
|
||||
// Wrapper class around the global TaskPool implementation to make it possible
|
||||
// to create a set of
|
||||
// tasks and then wait for the tasks to be completed by the
|
||||
// WaitForNextCompletedTask call. This
|
||||
// class should be used when WaitForNextCompletedTask is needed because this
|
||||
// class add no other
|
||||
// extra functionality to the TaskPool class and it have a very minor
|
||||
// performance overhead.
|
||||
template <typename T> // The return type of the tasks what will be added to this
|
||||
// task runner
|
||||
class TaskRunner {
|
||||
public:
|
||||
// Add a task to the task runner what will also add the task to the global
|
||||
// TaskPool. The
|
||||
// function doesn't return the std::future for the task because it will be
|
||||
// supplied by the
|
||||
// WaitForNextCompletedTask after the task is completed.
|
||||
template <typename F, typename... Args> void AddTask(F &&f, Args &&... args);
|
||||
|
||||
// Wait for the next task in this task runner to finish and then return the
|
||||
// std::future what
|
||||
// belongs to the finished task. If there is no task in this task runner
|
||||
// (neither pending nor
|
||||
// comleted) then this function will return an invalid future. Usually this
|
||||
// function should be
|
||||
// called in a loop processing the results of the tasks until it returns an
|
||||
// invalid std::future
|
||||
// what means that all task in this task runner is completed.
|
||||
std::future<T> WaitForNextCompletedTask();
|
||||
|
||||
// Convenience method to wait for all task in this TaskRunner to finish. Do
|
||||
// NOT use this class
|
||||
// just because of this method. Use TaskPool instead and wait for each
|
||||
// std::future returned by
|
||||
// AddTask in a loop.
|
||||
void WaitForAllTasks();
|
||||
|
||||
private:
|
||||
std::list<std::future<T>> m_ready;
|
||||
std::list<std::future<T>> m_pending;
|
||||
std::mutex m_mutex;
|
||||
std::condition_variable m_cv;
|
||||
};
|
||||
|
||||
template <typename F, typename... Args>
|
||||
std::future<typename std::result_of<F(Args...)>::type>
|
||||
TaskPool::AddTask(F &&f, Args &&... args) {
|
||||
@ -126,64 +82,10 @@ template <> struct TaskPool::RunTaskImpl<> {
|
||||
static void Run() {}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
template <typename F, typename... Args>
|
||||
void TaskRunner<T>::AddTask(F &&f, Args &&... args) {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
auto it = m_pending.emplace(m_pending.end());
|
||||
*it = std::move(TaskPool::AddTask(
|
||||
[this, it](F f, Args... args) {
|
||||
T &&r = f(std::forward<Args>(args)...);
|
||||
|
||||
std::unique_lock<std::mutex> lock(this->m_mutex);
|
||||
this->m_ready.splice(this->m_ready.end(), this->m_pending, it);
|
||||
lock.unlock();
|
||||
|
||||
this->m_cv.notify_one();
|
||||
return r;
|
||||
},
|
||||
std::forward<F>(f), std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <>
|
||||
template <typename F, typename... Args>
|
||||
void TaskRunner<void>::AddTask(F &&f, Args &&... args) {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
auto it = m_pending.emplace(m_pending.end());
|
||||
*it = std::move(TaskPool::AddTask(
|
||||
[this, it](F f, Args... args) {
|
||||
f(std::forward<Args>(args)...);
|
||||
|
||||
std::unique_lock<std::mutex> lock(this->m_mutex);
|
||||
this->m_ready.emplace_back(std::move(*it));
|
||||
this->m_pending.erase(it);
|
||||
lock.unlock();
|
||||
|
||||
this->m_cv.notify_one();
|
||||
},
|
||||
std::forward<F>(f), std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename T> std::future<T> TaskRunner<T>::WaitForNextCompletedTask() {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
if (m_ready.empty() && m_pending.empty())
|
||||
return std::future<T>(); // No more tasks
|
||||
|
||||
if (m_ready.empty())
|
||||
m_cv.wait(lock, [this]() { return !this->m_ready.empty(); });
|
||||
|
||||
std::future<T> res = std::move(m_ready.front());
|
||||
m_ready.pop_front();
|
||||
|
||||
lock.unlock();
|
||||
res.wait();
|
||||
|
||||
return std::move(res);
|
||||
}
|
||||
|
||||
template <typename T> void TaskRunner<T>::WaitForAllTasks() {
|
||||
while (WaitForNextCompletedTask().valid())
|
||||
;
|
||||
}
|
||||
// Run 'func' on every value from begin .. end-1. Each worker will grab
|
||||
// 'batch_size' numbers at a time to work on, so for very fast functions, batch
|
||||
// should be large enough to avoid too much cache line contention.
|
||||
void TaskMapOverInt(size_t begin, size_t end,
|
||||
std::function<void(size_t)> const &func);
|
||||
|
||||
#endif // #ifndef utility_TaskPool_h_
|
||||
|
@ -12,6 +12,7 @@
|
||||
class MultilineExpressionsTestCase(TestBase):
|
||||
|
||||
mydir = TestBase.compute_mydir(__file__)
|
||||
NO_DEBUG_INFO_TESTCASE = True
|
||||
|
||||
def setUp(self):
|
||||
# Call super's setUp().
|
||||
@ -60,3 +61,30 @@ def test_with_run_commands(self):
|
||||
child.expect_exact(prompt)
|
||||
self.expect(child.before, exe=False,
|
||||
patterns=['= 5'])
|
||||
|
||||
@skipIfRemote
|
||||
@expectedFailureAll(
|
||||
oslist=["windows"],
|
||||
bugnumber="llvm.org/pr22274: need a pexpect replacement for windows")
|
||||
def test_empty_list(self):
|
||||
"""Test printing an empty list of expressions"""
|
||||
import pexpect
|
||||
prompt = "(lldb) "
|
||||
|
||||
# So that the child gets torn down after the test
|
||||
self.child = pexpect.spawn(
|
||||
"%s %s" %
|
||||
(lldbtest_config.lldbExec, self.lldbOption))
|
||||
child = self.child
|
||||
|
||||
# Turn on logging for what the child sends back.
|
||||
if self.TraceOn():
|
||||
child.logfile_read = sys.stdout
|
||||
|
||||
# We expect a prompt, then send "print" to start a list of expressions,
|
||||
# then an empty line. We expect a prompt back.
|
||||
child.expect_exact(prompt)
|
||||
child.sendline("print")
|
||||
child.expect_exact('1:')
|
||||
child.sendline("")
|
||||
child.expect_exact(prompt)
|
||||
|
@ -62,12 +62,11 @@ def test_step_instruction(self):
|
||||
instructions = function.GetInstructions(self.target)
|
||||
addr_1 = self.breakpoint1.GetLocationAtIndex(0).GetAddress()
|
||||
addr_4 = self.breakpoint4.GetLocationAtIndex(0).GetAddress()
|
||||
for i in range(instructions.GetSize()) :
|
||||
addr = instructions.GetInstructionAtIndex(i).GetAddress()
|
||||
if (addr == addr_1) : index_1 = i
|
||||
if (addr == addr_4) : index_4 = i
|
||||
|
||||
steps_expected = index_4 - index_1
|
||||
# if third argument is true then the count will be the number of
|
||||
# instructions on which a breakpoint can be set.
|
||||
# start = addr_1, end = addr_4, canSetBreakpoint = True
|
||||
steps_expected = instructions.GetInstructionsCount(addr_1, addr_4, True)
|
||||
step_count = 0
|
||||
# Step from breakpoint_1 to breakpoint_4
|
||||
while True:
|
||||
|
@ -171,17 +171,45 @@ def test_with_python(self):
|
||||
#self.return_and_test_struct_value ("return_one_int_one_double_packed")
|
||||
self.return_and_test_struct_value("return_one_int_one_long")
|
||||
|
||||
# icc and gcc don't support this extension.
|
||||
if self.getCompiler().endswith('clang'):
|
||||
self.return_and_test_struct_value("return_vector_size_float32_8")
|
||||
self.return_and_test_struct_value("return_vector_size_float32_16")
|
||||
self.return_and_test_struct_value("return_vector_size_float32_32")
|
||||
self.return_and_test_struct_value(
|
||||
"return_ext_vector_size_float32_2")
|
||||
self.return_and_test_struct_value(
|
||||
"return_ext_vector_size_float32_4")
|
||||
self.return_and_test_struct_value(
|
||||
"return_ext_vector_size_float32_8")
|
||||
@expectedFailureAll(oslist=["freebsd"], archs=["i386"])
|
||||
@expectedFailureAll(oslist=["macosx"], archs=["i386"], bugnumber="<rdar://problem/28719652>")
|
||||
@expectedFailureAll(
|
||||
oslist=["linux"],
|
||||
compiler="clang",
|
||||
compiler_version=[
|
||||
"<=",
|
||||
"3.6"],
|
||||
archs=["i386"])
|
||||
@expectedFailureAll(
|
||||
bugnumber="llvm.org/pr25785",
|
||||
hostoslist=["windows"],
|
||||
compiler="gcc",
|
||||
archs=["i386"],
|
||||
triple='.*-android')
|
||||
@expectedFailureAll(compiler=["gcc"], archs=["x86_64", "i386"])
|
||||
@expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24778")
|
||||
def test_vector_values(self):
|
||||
self.build()
|
||||
exe = os.path.join(os.getcwd(), "a.out")
|
||||
error = lldb.SBError()
|
||||
|
||||
self.target = self.dbg.CreateTarget(exe)
|
||||
self.assertTrue(self.target, VALID_TARGET)
|
||||
|
||||
main_bktp = self.target.BreakpointCreateByName("main", exe)
|
||||
self.assertTrue(main_bktp, VALID_BREAKPOINT)
|
||||
|
||||
self.process = self.target.LaunchSimple(
|
||||
None, None, self.get_process_working_directory())
|
||||
self.assertEqual(len(lldbutil.get_threads_stopped_at_breakpoint(
|
||||
self.process, main_bktp)), 1)
|
||||
|
||||
self.return_and_test_struct_value("return_vector_size_float32_8")
|
||||
self.return_and_test_struct_value("return_vector_size_float32_16")
|
||||
self.return_and_test_struct_value("return_vector_size_float32_32")
|
||||
self.return_and_test_struct_value("return_ext_vector_size_float32_2")
|
||||
self.return_and_test_struct_value("return_ext_vector_size_float32_4")
|
||||
self.return_and_test_struct_value("return_ext_vector_size_float32_8")
|
||||
|
||||
def return_and_test_struct_value(self, func_name):
|
||||
"""Pass in the name of the function to return from - takes in value, returns value."""
|
||||
|
@ -14,6 +14,7 @@ class TestGdbRemoteHostInfo(GdbRemoteTestCaseBase):
|
||||
mydir = TestBase.compute_mydir(__file__)
|
||||
|
||||
KNOWN_HOST_INFO_KEYS = set([
|
||||
"arch",
|
||||
"cputype",
|
||||
"cpusubtype",
|
||||
"distribution_id",
|
||||
|
@ -54,6 +54,9 @@ public:
|
||||
bool
|
||||
HasDelaySlot ();
|
||||
|
||||
bool
|
||||
CanSetBreakpoint ();
|
||||
|
||||
void
|
||||
Print (FILE *out);
|
||||
|
||||
|
@ -44,6 +44,9 @@ public:
|
||||
lldb::SBInstruction
|
||||
GetInstructionAtIndex (uint32_t idx);
|
||||
|
||||
size_t GetInstructionsCount(const SBAddress &start, const SBAddress &end,
|
||||
bool canSetBreakpoint);
|
||||
|
||||
void
|
||||
Clear ();
|
||||
|
||||
|
@ -55,6 +55,12 @@ const SBAddress &SBAddress::operator=(const SBAddress &rhs) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool lldb::operator==(const SBAddress &lhs, const SBAddress &rhs) {
|
||||
if (lhs.IsValid() && rhs.IsValid())
|
||||
return lhs.ref() == rhs.ref();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SBAddress::IsValid() const {
|
||||
return m_opaque_ap.get() != NULL && m_opaque_ap->IsValid();
|
||||
}
|
||||
|
@ -176,6 +176,13 @@ bool SBInstruction::HasDelaySlot() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SBInstruction::CanSetBreakpoint () {
|
||||
lldb::InstructionSP inst_sp(GetOpaque());
|
||||
if (inst_sp)
|
||||
return inst_sp->CanSetBreakpoint();
|
||||
return false;
|
||||
}
|
||||
|
||||
lldb::InstructionSP SBInstruction::GetOpaque() {
|
||||
if (m_opaque_sp)
|
||||
return m_opaque_sp->GetSP();
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "lldb/API/SBInstructionList.h"
|
||||
#include "lldb/API/SBInstruction.h"
|
||||
#include "lldb/API/SBAddress.h"
|
||||
#include "lldb/API/SBStream.h"
|
||||
#include "lldb/Core/Disassembler.h"
|
||||
#include "lldb/Core/Module.h"
|
||||
@ -49,6 +50,31 @@ SBInstruction SBInstructionList::GetInstructionAtIndex(uint32_t idx) {
|
||||
return inst;
|
||||
}
|
||||
|
||||
size_t SBInstructionList::GetInstructionsCount(const SBAddress &start,
|
||||
const SBAddress &end,
|
||||
bool canSetBreakpoint) {
|
||||
size_t num_instructions = GetSize();
|
||||
size_t i = 0;
|
||||
SBAddress addr;
|
||||
size_t lower_index = 0;
|
||||
size_t upper_index = 0;
|
||||
size_t instructions_to_skip = 0;
|
||||
for (i = 0; i < num_instructions; ++i) {
|
||||
addr = GetInstructionAtIndex(i).GetAddress();
|
||||
if (start == addr)
|
||||
lower_index = i;
|
||||
if (end == addr)
|
||||
upper_index = i;
|
||||
}
|
||||
if (canSetBreakpoint)
|
||||
for (i = lower_index; i <= upper_index; ++i) {
|
||||
SBInstruction insn = GetInstructionAtIndex(i);
|
||||
if (!insn.CanSetBreakpoint())
|
||||
++instructions_to_skip;
|
||||
}
|
||||
return upper_index - lower_index - instructions_to_skip;
|
||||
}
|
||||
|
||||
void SBInstructionList::Clear() { m_opaque_sp.reset(); }
|
||||
|
||||
void SBInstructionList::AppendInstruction(SBInstruction insn) {}
|
||||
|
@ -1157,22 +1157,34 @@ uint32_t SBProcess::LoadImage(lldb::SBFileSpec &sb_remote_image_spec,
|
||||
uint32_t SBProcess::LoadImage(const lldb::SBFileSpec &sb_local_image_spec,
|
||||
const lldb::SBFileSpec &sb_remote_image_spec,
|
||||
lldb::SBError &sb_error) {
|
||||
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
|
||||
ProcessSP process_sp(GetSP());
|
||||
if (process_sp) {
|
||||
Process::StopLocker stop_locker;
|
||||
if (stop_locker.TryLock(&process_sp->GetRunLock())) {
|
||||
if (log)
|
||||
log->Printf("SBProcess(%p)::LoadImage() => calling Platform::LoadImage"
|
||||
"for: %s",
|
||||
static_cast<void *>(process_sp.get()),
|
||||
sb_local_image_spec.GetFilename());
|
||||
|
||||
std::lock_guard<std::recursive_mutex> guard(
|
||||
process_sp->GetTarget().GetAPIMutex());
|
||||
process_sp->GetTarget().GetAPIMutex());
|
||||
PlatformSP platform_sp = process_sp->GetTarget().GetPlatform();
|
||||
return platform_sp->LoadImage(process_sp.get(), *sb_local_image_spec,
|
||||
*sb_remote_image_spec, sb_error.ref());
|
||||
} else {
|
||||
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
|
||||
if (log)
|
||||
log->Printf("SBProcess(%p)::LoadImage() => error: process is running",
|
||||
static_cast<void *>(process_sp.get()));
|
||||
sb_error.SetErrorString("process is running");
|
||||
}
|
||||
} else {
|
||||
if (log)
|
||||
log->Printf("SBProcess(%p)::LoadImage() => error: called with invalid"
|
||||
" process",
|
||||
static_cast<void *>(process_sp.get()));
|
||||
sb_error.SetErrorString("process is invalid");
|
||||
}
|
||||
return LLDB_INVALID_IMAGE_TOKEN;
|
||||
}
|
||||
|
@ -759,6 +759,10 @@ bool Instruction::DumpEmulation(const ArchSpec &arch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Instruction::CanSetBreakpoint () {
|
||||
return !HasDelaySlot();
|
||||
}
|
||||
|
||||
bool Instruction::HasDelaySlot() {
|
||||
// Default is false.
|
||||
return false;
|
||||
|
@ -367,7 +367,7 @@ void Editline::MoveCursor(CursorLocation from, CursorLocation to) {
|
||||
if (to == CursorLocation::EditingCursor) {
|
||||
toColumn =
|
||||
editline_cursor_position - (editline_cursor_row * m_terminal_width) + 1;
|
||||
} else if (to == CursorLocation::BlockEnd) {
|
||||
} else if (to == CursorLocation::BlockEnd && !m_input_lines.empty()) {
|
||||
toColumn =
|
||||
((m_input_lines[m_input_lines.size() - 1].length() + GetPromptWidth()) %
|
||||
80) +
|
||||
|
@ -18,6 +18,11 @@
|
||||
#include <vector>
|
||||
#include <time.h>
|
||||
|
||||
// Multiplexing is implemented using kqueue on systems that support it (BSD
|
||||
// variants including OSX). On linux we use ppoll, while android uses pselect
|
||||
// (ppoll is present but not implemented properly). On windows we use WSApoll
|
||||
// (which does not support signals).
|
||||
|
||||
#if HAVE_SYS_EVENT_H
|
||||
#include <sys/event.h>
|
||||
#elif defined(LLVM_ON_WIN32)
|
||||
@ -65,92 +70,72 @@ static void SignalHandler(int signo, siginfo_t *info, void *) {
|
||||
|
||||
class MainLoop::RunImpl {
|
||||
public:
|
||||
// TODO: Use llvm::Expected<T>
|
||||
static std::unique_ptr<RunImpl> Create(MainLoop &loop, Error &error);
|
||||
~RunImpl();
|
||||
RunImpl(MainLoop &loop);
|
||||
~RunImpl() = default;
|
||||
|
||||
Error Poll();
|
||||
|
||||
template <typename F> void ForEachReadFD(F &&f);
|
||||
template <typename F> void ForEachSignal(F &&f);
|
||||
void ProcessEvents();
|
||||
|
||||
private:
|
||||
MainLoop &loop;
|
||||
|
||||
#if HAVE_SYS_EVENT_H
|
||||
int queue_id;
|
||||
std::vector<struct kevent> in_events;
|
||||
struct kevent out_events[4];
|
||||
int num_events = -1;
|
||||
|
||||
RunImpl(MainLoop &loop, int queue_id) : loop(loop), queue_id(queue_id) {
|
||||
in_events.reserve(loop.m_read_fds.size() + loop.m_signals.size());
|
||||
}
|
||||
#else
|
||||
std::vector<int> signals;
|
||||
#ifdef FORCE_PSELECT
|
||||
fd_set read_fd_set;
|
||||
#else
|
||||
std::vector<struct pollfd> read_fds;
|
||||
#endif
|
||||
|
||||
RunImpl(MainLoop &loop) : loop(loop) {
|
||||
signals.reserve(loop.m_signals.size());
|
||||
}
|
||||
|
||||
sigset_t get_sigmask();
|
||||
#endif
|
||||
};
|
||||
|
||||
#if HAVE_SYS_EVENT_H
|
||||
MainLoop::RunImpl::~RunImpl() {
|
||||
int r = close(queue_id);
|
||||
assert(r == 0);
|
||||
(void)r;
|
||||
}
|
||||
std::unique_ptr<MainLoop::RunImpl> MainLoop::RunImpl::Create(MainLoop &loop, Error &error)
|
||||
{
|
||||
error.Clear();
|
||||
int queue_id = kqueue();
|
||||
if(queue_id < 0) {
|
||||
error = Error(errno, eErrorTypePOSIX);
|
||||
return nullptr;
|
||||
}
|
||||
return std::unique_ptr<RunImpl>(new RunImpl(loop, queue_id));
|
||||
MainLoop::RunImpl::RunImpl(MainLoop &loop) : loop(loop) {
|
||||
in_events.reserve(loop.m_read_fds.size());
|
||||
}
|
||||
|
||||
Error MainLoop::RunImpl::Poll() {
|
||||
in_events.resize(loop.m_read_fds.size() + loop.m_signals.size());
|
||||
in_events.resize(loop.m_read_fds.size());
|
||||
unsigned i = 0;
|
||||
for (auto &fd : loop.m_read_fds)
|
||||
EV_SET(&in_events[i++], fd.first, EVFILT_READ, EV_ADD, 0, 0, 0);
|
||||
|
||||
for (const auto &sig : loop.m_signals)
|
||||
EV_SET(&in_events[i++], sig.first, EVFILT_SIGNAL, EV_ADD, 0, 0, 0);
|
||||
|
||||
num_events = kevent(queue_id, in_events.data(), in_events.size(), out_events,
|
||||
llvm::array_lengthof(out_events), nullptr);
|
||||
num_events = kevent(loop.m_kqueue, in_events.data(), in_events.size(),
|
||||
out_events, llvm::array_lengthof(out_events), nullptr);
|
||||
|
||||
if (num_events < 0)
|
||||
return Error("kevent() failed with error %d\n", num_events);
|
||||
return Error();
|
||||
}
|
||||
|
||||
template <typename F> void MainLoop::RunImpl::ForEachReadFD(F &&f) {
|
||||
void MainLoop::RunImpl::ProcessEvents() {
|
||||
assert(num_events >= 0);
|
||||
for (int i = 0; i < num_events; ++i) {
|
||||
f(out_events[i].ident);
|
||||
if (loop.m_terminate_request)
|
||||
return;
|
||||
switch (out_events[i].filter) {
|
||||
case EVFILT_READ:
|
||||
loop.ProcessReadObject(out_events[i].ident);
|
||||
break;
|
||||
case EVFILT_SIGNAL:
|
||||
loop.ProcessSignal(out_events[i].ident);
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("Unknown event");
|
||||
}
|
||||
}
|
||||
}
|
||||
template <typename F> void MainLoop::RunImpl::ForEachSignal(F && f) {}
|
||||
#else
|
||||
MainLoop::RunImpl::~RunImpl() {}
|
||||
std::unique_ptr<MainLoop::RunImpl> MainLoop::RunImpl::Create(MainLoop &loop, Error &error)
|
||||
{
|
||||
error.Clear();
|
||||
return std::unique_ptr<RunImpl>(new RunImpl(loop));
|
||||
MainLoop::RunImpl::RunImpl(MainLoop &loop) : loop(loop) {
|
||||
#ifndef FORCE_PSELECT
|
||||
read_fds.reserve(loop.m_read_fds.size());
|
||||
#endif
|
||||
}
|
||||
|
||||
sigset_t MainLoop::RunImpl::get_sigmask() {
|
||||
@ -162,18 +147,14 @@ sigset_t MainLoop::RunImpl::get_sigmask() {
|
||||
assert(ret == 0);
|
||||
(void) ret;
|
||||
|
||||
for (const auto &sig : loop.m_signals) {
|
||||
signals.push_back(sig.first);
|
||||
for (const auto &sig : loop.m_signals)
|
||||
sigdelset(&sigmask, sig.first);
|
||||
}
|
||||
return sigmask;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef FORCE_PSELECT
|
||||
Error MainLoop::RunImpl::Poll() {
|
||||
signals.clear();
|
||||
|
||||
FD_ZERO(&read_fd_set);
|
||||
int nfds = 0;
|
||||
for (const auto &fd : loop.m_read_fds) {
|
||||
@ -188,20 +169,8 @@ Error MainLoop::RunImpl::Poll() {
|
||||
|
||||
return Error();
|
||||
}
|
||||
|
||||
template <typename F> void MainLoop::RunImpl::ForEachReadFD(F &&f) {
|
||||
for (const auto &fd : loop.m_read_fds) {
|
||||
if(!FD_ISSET(fd.first, &read_fd_set))
|
||||
continue;
|
||||
|
||||
f(fd.first);
|
||||
if (loop.m_terminate_request)
|
||||
return;
|
||||
}
|
||||
}
|
||||
#else
|
||||
Error MainLoop::RunImpl::Poll() {
|
||||
signals.clear();
|
||||
read_fds.clear();
|
||||
|
||||
sigset_t sigmask = get_sigmask();
|
||||
@ -220,33 +189,47 @@ Error MainLoop::RunImpl::Poll() {
|
||||
|
||||
return Error();
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename F> void MainLoop::RunImpl::ForEachReadFD(F &&f) {
|
||||
void MainLoop::RunImpl::ProcessEvents() {
|
||||
#ifdef FORCE_PSELECT
|
||||
for (const auto &fd : loop.m_read_fds) {
|
||||
if (!FD_ISSET(fd.first, &read_fd_set))
|
||||
continue;
|
||||
IOObject::WaitableHandle handle = fd.first;
|
||||
#else
|
||||
for (const auto &fd : read_fds) {
|
||||
if ((fd.revents & POLLIN) == 0)
|
||||
continue;
|
||||
|
||||
f(fd.fd);
|
||||
IOObject::WaitableHandle handle = fd.fd;
|
||||
#endif
|
||||
if (loop.m_terminate_request)
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename F> void MainLoop::RunImpl::ForEachSignal(F &&f) {
|
||||
for (int sig : signals) {
|
||||
if (g_signal_flags[sig] == 0)
|
||||
loop.ProcessReadObject(handle);
|
||||
}
|
||||
|
||||
for (const auto &entry : loop.m_signals) {
|
||||
if (loop.m_terminate_request)
|
||||
return;
|
||||
if (g_signal_flags[entry.first] == 0)
|
||||
continue; // No signal
|
||||
g_signal_flags[sig] = 0;
|
||||
f(sig);
|
||||
|
||||
if (loop.m_terminate_request)
|
||||
return;
|
||||
g_signal_flags[entry.first] = 0;
|
||||
loop.ProcessSignal(entry.first);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
MainLoop::MainLoop() {
|
||||
#if HAVE_SYS_EVENT_H
|
||||
m_kqueue = kqueue();
|
||||
assert(m_kqueue >= 0);
|
||||
#endif
|
||||
}
|
||||
MainLoop::~MainLoop() {
|
||||
#if HAVE_SYS_EVENT_H
|
||||
close(m_kqueue);
|
||||
#endif
|
||||
assert(m_read_fds.size() == 0);
|
||||
assert(m_signals.size() == 0);
|
||||
}
|
||||
@ -298,25 +281,31 @@ MainLoop::RegisterSignal(int signo, const Callback &callback,
|
||||
new_action.sa_flags = SA_SIGINFO;
|
||||
sigemptyset(&new_action.sa_mask);
|
||||
sigaddset(&new_action.sa_mask, signo);
|
||||
|
||||
sigset_t old_set;
|
||||
if (int ret = pthread_sigmask(SIG_BLOCK, &new_action.sa_mask, &old_set)) {
|
||||
error.SetErrorStringWithFormat("pthread_sigmask failed with error %d\n",
|
||||
ret);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
info.was_blocked = sigismember(&old_set, signo);
|
||||
if (sigaction(signo, &new_action, &info.old_action) == -1) {
|
||||
error.SetErrorToErrno();
|
||||
if (!info.was_blocked)
|
||||
pthread_sigmask(SIG_UNBLOCK, &new_action.sa_mask, nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
m_signals.insert({signo, info});
|
||||
g_signal_flags[signo] = 0;
|
||||
|
||||
// Even if using kqueue, the signal handler will still be invoked, so it's
|
||||
// important to replace it with our "bening" handler.
|
||||
int ret = sigaction(signo, &new_action, &info.old_action);
|
||||
assert(ret == 0 && "sigaction failed");
|
||||
|
||||
#if HAVE_SYS_EVENT_H
|
||||
struct kevent ev;
|
||||
EV_SET(&ev, signo, EVFILT_SIGNAL, EV_ADD, 0, 0, 0);
|
||||
ret = kevent(m_kqueue, &ev, 1, nullptr, 0, nullptr);
|
||||
assert(ret == 0);
|
||||
#endif
|
||||
|
||||
// If we're using kqueue, the signal needs to be unblocked in order to recieve
|
||||
// it. If using pselect/ppoll, we need to block it, and later unblock it as a
|
||||
// part of the system call.
|
||||
ret = pthread_sigmask(HAVE_SYS_EVENT_H ? SIG_UNBLOCK : SIG_BLOCK,
|
||||
&new_action.sa_mask, &old_set);
|
||||
assert(ret == 0 && "pthread_sigmask failed");
|
||||
info.was_blocked = sigismember(&old_set, signo);
|
||||
m_signals.insert({signo, info});
|
||||
|
||||
return SignalHandleUP(new SignalHandle(*this, signo));
|
||||
#endif
|
||||
}
|
||||
@ -331,7 +320,6 @@ void MainLoop::UnregisterSignal(int signo) {
|
||||
#if SIGNAL_POLLING_UNSUPPORTED
|
||||
Error("Signal polling is not supported on this platform.");
|
||||
#else
|
||||
// We undo the actions of RegisterSignal on a best-effort basis.
|
||||
auto it = m_signals.find(signo);
|
||||
assert(it != m_signals.end());
|
||||
|
||||
@ -340,8 +328,17 @@ void MainLoop::UnregisterSignal(int signo) {
|
||||
sigset_t set;
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, signo);
|
||||
pthread_sigmask(it->second.was_blocked ? SIG_BLOCK : SIG_UNBLOCK, &set,
|
||||
nullptr);
|
||||
int ret = pthread_sigmask(it->second.was_blocked ? SIG_BLOCK : SIG_UNBLOCK,
|
||||
&set, nullptr);
|
||||
assert(ret == 0);
|
||||
(void)ret;
|
||||
|
||||
#if HAVE_SYS_EVENT_H
|
||||
struct kevent ev;
|
||||
EV_SET(&ev, signo, EVFILT_SIGNAL, EV_DELETE, 0, 0, 0);
|
||||
ret = kevent(m_kqueue, &ev, 1, nullptr, 0, nullptr);
|
||||
assert(ret == 0);
|
||||
#endif
|
||||
|
||||
m_signals.erase(it);
|
||||
#endif
|
||||
@ -351,32 +348,31 @@ Error MainLoop::Run() {
|
||||
m_terminate_request = false;
|
||||
|
||||
Error error;
|
||||
auto impl = RunImpl::Create(*this, error);
|
||||
if (!impl)
|
||||
return error;
|
||||
RunImpl impl(*this);
|
||||
|
||||
// run until termination or until we run out of things to listen to
|
||||
while (!m_terminate_request && (!m_read_fds.empty() || !m_signals.empty())) {
|
||||
|
||||
error = impl->Poll();
|
||||
error = impl.Poll();
|
||||
if (error.Fail())
|
||||
return error;
|
||||
|
||||
impl->ForEachSignal([&](int sig) {
|
||||
auto it = m_signals.find(sig);
|
||||
if (it != m_signals.end())
|
||||
it->second.callback(*this); // Do the work
|
||||
});
|
||||
if (m_terminate_request)
|
||||
return Error();
|
||||
impl.ProcessEvents();
|
||||
|
||||
impl->ForEachReadFD([&](int fd) {
|
||||
auto it = m_read_fds.find(fd);
|
||||
if (it != m_read_fds.end())
|
||||
it->second(*this); // Do the work
|
||||
});
|
||||
if (m_terminate_request)
|
||||
return Error();
|
||||
}
|
||||
return Error();
|
||||
}
|
||||
|
||||
void MainLoop::ProcessSignal(int signo) {
|
||||
auto it = m_signals.find(signo);
|
||||
if (it != m_signals.end())
|
||||
it->second.callback(*this); // Do the work
|
||||
}
|
||||
|
||||
void MainLoop::ProcessReadObject(IOObject::WaitableHandle handle) {
|
||||
auto it = m_read_fds.find(handle);
|
||||
if (it != m_read_fds.end())
|
||||
it->second(*this); // Do the work
|
||||
}
|
||||
|
@ -28,31 +28,41 @@ const int kDomain = AF_INET;
|
||||
const int kType = SOCK_DGRAM;
|
||||
|
||||
static const char *g_not_supported_error = "Not supported";
|
||||
} // namespace
|
||||
}
|
||||
|
||||
UDPSocket::UDPSocket(NativeSocket socket) : Socket(ProtocolUdp, true, true) {
|
||||
m_socket = socket;
|
||||
}
|
||||
|
||||
UDPSocket::UDPSocket(bool should_close, bool child_processes_inherit)
|
||||
: Socket(ProtocolUdp, should_close, child_processes_inherit) {}
|
||||
|
||||
UDPSocket::UDPSocket(NativeSocket socket, const UDPSocket &listen_socket)
|
||||
: Socket(ProtocolUdp, listen_socket.m_should_close_fd,
|
||||
listen_socket.m_child_processes_inherit) {
|
||||
m_socket = socket;
|
||||
}
|
||||
|
||||
size_t UDPSocket::Send(const void *buf, const size_t num_bytes) {
|
||||
return ::sendto(m_socket, static_cast<const char *>(buf), num_bytes, 0,
|
||||
m_sockaddr, m_sockaddr.GetLength());
|
||||
}
|
||||
|
||||
Error UDPSocket::Connect(llvm::StringRef name) {
|
||||
return Error("%s", g_not_supported_error);
|
||||
}
|
||||
|
||||
Error UDPSocket::Listen(llvm::StringRef name, int backlog) {
|
||||
return Error("%s", g_not_supported_error);
|
||||
}
|
||||
|
||||
Error UDPSocket::Accept(Socket *&socket) {
|
||||
return Error("%s", g_not_supported_error);
|
||||
}
|
||||
|
||||
Error UDPSocket::Connect(llvm::StringRef name, bool child_processes_inherit,
|
||||
Socket *&socket) {
|
||||
std::unique_ptr<UDPSocket> final_socket;
|
||||
|
||||
Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
|
||||
if (log)
|
||||
log->Printf("UDPSocket::%s (host/port = %s)", __FUNCTION__, name.data());
|
||||
|
||||
Error error;
|
||||
if (error.Fail())
|
||||
return error;
|
||||
|
||||
std::string host_str;
|
||||
std::string port_str;
|
||||
int32_t port = INT32_MIN;
|
||||
@ -84,11 +94,12 @@ Error UDPSocket::Connect(llvm::StringRef name) {
|
||||
for (struct addrinfo *service_info_ptr = service_info_list;
|
||||
service_info_ptr != nullptr;
|
||||
service_info_ptr = service_info_ptr->ai_next) {
|
||||
m_socket = Socket::CreateSocket(
|
||||
auto send_fd = CreateSocket(
|
||||
service_info_ptr->ai_family, service_info_ptr->ai_socktype,
|
||||
service_info_ptr->ai_protocol, m_child_processes_inherit, error);
|
||||
service_info_ptr->ai_protocol, child_processes_inherit, error);
|
||||
if (error.Success()) {
|
||||
m_sockaddr = service_info_ptr;
|
||||
final_socket.reset(new UDPSocket(send_fd));
|
||||
final_socket->m_sockaddr = service_info_ptr;
|
||||
break;
|
||||
} else
|
||||
continue;
|
||||
@ -96,17 +107,16 @@ Error UDPSocket::Connect(llvm::StringRef name) {
|
||||
|
||||
::freeaddrinfo(service_info_list);
|
||||
|
||||
if (IsValid())
|
||||
if (!final_socket)
|
||||
return error;
|
||||
|
||||
SocketAddress bind_addr;
|
||||
|
||||
// Only bind to the loopback address if we are expecting a connection from
|
||||
// localhost to avoid any firewall issues.
|
||||
const bool bind_addr_success =
|
||||
(host_str == "127.0.0.1" || host_str == "localhost")
|
||||
? bind_addr.SetToLocalhost(kDomain, port)
|
||||
: bind_addr.SetToAnyAddress(kDomain, port);
|
||||
const bool bind_addr_success = (host_str == "127.0.0.1" || host_str == "localhost")
|
||||
? bind_addr.SetToLocalhost(kDomain, port)
|
||||
: bind_addr.SetToAnyAddress(kDomain, port);
|
||||
|
||||
if (!bind_addr_success) {
|
||||
error.SetErrorString("Failed to get hostspec to bind for");
|
||||
@ -115,37 +125,13 @@ Error UDPSocket::Connect(llvm::StringRef name) {
|
||||
|
||||
bind_addr.SetPort(0); // Let the source port # be determined dynamically
|
||||
|
||||
err = ::bind(m_socket, bind_addr, bind_addr.GetLength());
|
||||
err = ::bind(final_socket->GetNativeSocket(), bind_addr, bind_addr.GetLength());
|
||||
|
||||
struct sockaddr_in source_info;
|
||||
socklen_t address_len = sizeof (struct sockaddr_in);
|
||||
err = ::getsockname(final_socket->GetNativeSocket(), (struct sockaddr *) &source_info, &address_len);
|
||||
|
||||
socket = final_socket.release();
|
||||
error.Clear();
|
||||
return error;
|
||||
}
|
||||
|
||||
Error UDPSocket::Listen(llvm::StringRef name, int backlog) {
|
||||
return Error("%s", g_not_supported_error);
|
||||
}
|
||||
|
||||
Error UDPSocket::Accept(Socket *&socket) {
|
||||
return Error("%s", g_not_supported_error);
|
||||
}
|
||||
|
||||
Error UDPSocket::CreateSocket() {
|
||||
Error error;
|
||||
if (IsValid())
|
||||
error = Close();
|
||||
if (error.Fail())
|
||||
return error;
|
||||
m_socket =
|
||||
Socket::CreateSocket(kDomain, kType, 0, m_child_processes_inherit, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
Error UDPSocket::Connect(llvm::StringRef name, bool child_processes_inherit,
|
||||
Socket *&socket) {
|
||||
std::unique_ptr<UDPSocket> final_socket(
|
||||
new UDPSocket(true, child_processes_inherit));
|
||||
Error error = final_socket->Connect(name);
|
||||
if (!error.Fail())
|
||||
socket = final_socket.release();
|
||||
return error;
|
||||
}
|
||||
|
@ -2362,32 +2362,30 @@ ValueObjectSP ABISysV_arm64::GetReturnValueObjectImpl(
|
||||
if (success)
|
||||
return_valobj_sp = ValueObjectConstResult::Create(
|
||||
thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
|
||||
} else if (type_flags & eTypeIsVector) {
|
||||
} else if (type_flags & eTypeIsVector && byte_size <= 16) {
|
||||
if (byte_size > 0) {
|
||||
const RegisterInfo *v0_info = reg_ctx->GetRegisterInfoByName("v0", 0);
|
||||
|
||||
if (v0_info) {
|
||||
if (byte_size <= v0_info->byte_size) {
|
||||
std::unique_ptr<DataBufferHeap> heap_data_ap(
|
||||
new DataBufferHeap(byte_size, 0));
|
||||
const ByteOrder byte_order = exe_ctx.GetProcessRef().GetByteOrder();
|
||||
RegisterValue reg_value;
|
||||
if (reg_ctx->ReadRegister(v0_info, reg_value)) {
|
||||
Error error;
|
||||
if (reg_value.GetAsMemoryData(v0_info, heap_data_ap->GetBytes(),
|
||||
heap_data_ap->GetByteSize(),
|
||||
byte_order, error)) {
|
||||
DataExtractor data(DataBufferSP(heap_data_ap.release()),
|
||||
byte_order,
|
||||
exe_ctx.GetProcessRef().GetAddressByteSize());
|
||||
return_valobj_sp = ValueObjectConstResult::Create(
|
||||
&thread, return_compiler_type, ConstString(""), data);
|
||||
}
|
||||
std::unique_ptr<DataBufferHeap> heap_data_ap(
|
||||
new DataBufferHeap(byte_size, 0));
|
||||
const ByteOrder byte_order = exe_ctx.GetProcessRef().GetByteOrder();
|
||||
RegisterValue reg_value;
|
||||
if (reg_ctx->ReadRegister(v0_info, reg_value)) {
|
||||
Error error;
|
||||
if (reg_value.GetAsMemoryData(v0_info, heap_data_ap->GetBytes(),
|
||||
heap_data_ap->GetByteSize(), byte_order,
|
||||
error)) {
|
||||
DataExtractor data(DataBufferSP(heap_data_ap.release()), byte_order,
|
||||
exe_ctx.GetProcessRef().GetAddressByteSize());
|
||||
return_valobj_sp = ValueObjectConstResult::Create(
|
||||
&thread, return_compiler_type, ConstString(""), data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (type_flags & eTypeIsStructUnion || type_flags & eTypeIsClass) {
|
||||
} else if (type_flags & eTypeIsStructUnion || type_flags & eTypeIsClass ||
|
||||
(type_flags & eTypeIsVector && byte_size > 16)) {
|
||||
DataExtractor data;
|
||||
|
||||
uint32_t NGRN = 0; // Search ABI docs for NGRN
|
||||
|
@ -434,24 +434,25 @@ Error DynamicLoaderMacOS::CanLoadImage() {
|
||||
|
||||
// Default assumption is that it is OK to load images.
|
||||
// Only say that we cannot load images if we find the symbol in libdyld and it
|
||||
// indicates that
|
||||
// we cannot.
|
||||
// indicates that we cannot.
|
||||
|
||||
if (symbol_address != LLDB_INVALID_ADDRESS) {
|
||||
{
|
||||
int lock_held =
|
||||
m_process->ReadUnsignedIntegerFromMemory(symbol_address, 4, 0, error);
|
||||
if (lock_held != 0) {
|
||||
error.SetErrorToGenericError();
|
||||
error.SetErrorString("dyld lock held - unsafe to load images.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If we were unable to find _dyld_global_lock_held in any modules, or it is
|
||||
// not loaded into
|
||||
// memory yet, we may be at process startup (sitting at _dyld_start) - so we
|
||||
// should not allow
|
||||
// dlopen calls.
|
||||
error.SetErrorToGenericError();
|
||||
// not loaded into memory yet, we may be at process startup (sitting
|
||||
// at _dyld_start) - so we should not allow dlopen calls.
|
||||
// But if we found more than one module then we are clearly past _dyld_start
|
||||
// so in that case we'll default to "it's safe".
|
||||
if (num_modules <= 1)
|
||||
error.SetErrorString("could not find the dyld library or "
|
||||
"the dyld lock symbol");
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
@ -1946,7 +1946,9 @@ void SymbolFileDWARF::Index() {
|
||||
std::vector<NameToDIE> type_index(num_compile_units);
|
||||
std::vector<NameToDIE> namespace_index(num_compile_units);
|
||||
|
||||
std::vector<bool> clear_cu_dies(num_compile_units, false);
|
||||
// std::vector<bool> might be implemented using bit test-and-set, so use
|
||||
// uint8_t instead.
|
||||
std::vector<uint8_t> clear_cu_dies(num_compile_units, false);
|
||||
auto parser_fn = [debug_info, &function_basename_index,
|
||||
&function_fullname_index, &function_method_index,
|
||||
&function_selector_index, &objc_class_selectors_index,
|
||||
@ -1963,22 +1965,18 @@ void SymbolFileDWARF::Index() {
|
||||
return cu_idx;
|
||||
};
|
||||
|
||||
auto extract_fn = [debug_info](uint32_t cu_idx) {
|
||||
auto extract_fn = [debug_info, &clear_cu_dies](uint32_t cu_idx) {
|
||||
DWARFCompileUnit *dwarf_cu = debug_info->GetCompileUnitAtIndex(cu_idx);
|
||||
if (dwarf_cu) {
|
||||
// dwarf_cu->ExtractDIEsIfNeeded(false) will return zero if the
|
||||
// DIEs for a compile unit have already been parsed.
|
||||
return std::make_pair(cu_idx, dwarf_cu->ExtractDIEsIfNeeded(false) > 1);
|
||||
if (dwarf_cu->ExtractDIEsIfNeeded(false) > 1)
|
||||
clear_cu_dies[cu_idx] = true;
|
||||
}
|
||||
return std::make_pair(cu_idx, false);
|
||||
};
|
||||
|
||||
// Create a task runner that extracts dies for each DWARF compile unit in a
|
||||
// separate thread
|
||||
TaskRunner<std::pair<uint32_t, bool>> task_runner_extract;
|
||||
for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx)
|
||||
task_runner_extract.AddTask(extract_fn, cu_idx);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// First figure out which compile units didn't have their DIEs already
|
||||
// parsed and remember this. If no DIEs were parsed prior to this index
|
||||
@ -1988,48 +1986,37 @@ void SymbolFileDWARF::Index() {
|
||||
// a DIE in one compile unit refers to another and the indexes accesses
|
||||
// those DIEs.
|
||||
//----------------------------------------------------------------------
|
||||
while (true) {
|
||||
auto f = task_runner_extract.WaitForNextCompletedTask();
|
||||
if (!f.valid())
|
||||
break;
|
||||
unsigned cu_idx;
|
||||
bool clear;
|
||||
std::tie(cu_idx, clear) = f.get();
|
||||
clear_cu_dies[cu_idx] = clear;
|
||||
}
|
||||
TaskMapOverInt(0, num_compile_units, extract_fn);
|
||||
|
||||
// Now create a task runner that can index each DWARF compile unit in a
|
||||
// separate
|
||||
// thread so we can index quickly.
|
||||
|
||||
TaskRunner<uint32_t> task_runner;
|
||||
for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx)
|
||||
task_runner.AddTask(parser_fn, cu_idx);
|
||||
TaskMapOverInt(0, num_compile_units, parser_fn);
|
||||
|
||||
while (true) {
|
||||
std::future<uint32_t> f = task_runner.WaitForNextCompletedTask();
|
||||
if (!f.valid())
|
||||
break;
|
||||
uint32_t cu_idx = f.get();
|
||||
auto finalize_fn = [](NameToDIE &index, std::vector<NameToDIE> &srcs) {
|
||||
for (auto &src : srcs)
|
||||
index.Append(src);
|
||||
index.Finalize();
|
||||
};
|
||||
|
||||
m_function_basename_index.Append(function_basename_index[cu_idx]);
|
||||
m_function_fullname_index.Append(function_fullname_index[cu_idx]);
|
||||
m_function_method_index.Append(function_method_index[cu_idx]);
|
||||
m_function_selector_index.Append(function_selector_index[cu_idx]);
|
||||
m_objc_class_selectors_index.Append(objc_class_selectors_index[cu_idx]);
|
||||
m_global_index.Append(global_index[cu_idx]);
|
||||
m_type_index.Append(type_index[cu_idx]);
|
||||
m_namespace_index.Append(namespace_index[cu_idx]);
|
||||
}
|
||||
|
||||
TaskPool::RunTasks([&]() { m_function_basename_index.Finalize(); },
|
||||
[&]() { m_function_fullname_index.Finalize(); },
|
||||
[&]() { m_function_method_index.Finalize(); },
|
||||
[&]() { m_function_selector_index.Finalize(); },
|
||||
[&]() { m_objc_class_selectors_index.Finalize(); },
|
||||
[&]() { m_global_index.Finalize(); },
|
||||
[&]() { m_type_index.Finalize(); },
|
||||
[&]() { m_namespace_index.Finalize(); });
|
||||
TaskPool::RunTasks(
|
||||
[&]() {
|
||||
finalize_fn(m_function_basename_index, function_basename_index);
|
||||
},
|
||||
[&]() {
|
||||
finalize_fn(m_function_fullname_index, function_fullname_index);
|
||||
},
|
||||
[&]() { finalize_fn(m_function_method_index, function_method_index); },
|
||||
[&]() {
|
||||
finalize_fn(m_function_selector_index, function_selector_index);
|
||||
},
|
||||
[&]() {
|
||||
finalize_fn(m_objc_class_selectors_index, objc_class_selectors_index);
|
||||
},
|
||||
[&]() { finalize_fn(m_global_index, global_index); },
|
||||
[&]() { finalize_fn(m_type_index, type_index); },
|
||||
[&]() { finalize_fn(m_namespace_index, namespace_index); });
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Keep memory down by clearing DIEs for any compile units if indexing
|
||||
|
@ -60,6 +60,12 @@ void ThreadPlanCallUserExpression::GetDescription(
|
||||
ThreadPlanCallFunction::GetDescription(s, level);
|
||||
}
|
||||
|
||||
void ThreadPlanCallUserExpression::DidPush() {
|
||||
ThreadPlanCallFunction::DidPush();
|
||||
if (m_user_expression_sp)
|
||||
m_user_expression_sp->WillStartExecuting();
|
||||
}
|
||||
|
||||
void ThreadPlanCallUserExpression::WillPop() {
|
||||
ThreadPlanCallFunction::WillPop();
|
||||
if (m_user_expression_sp)
|
||||
@ -113,3 +119,8 @@ StopInfoSP ThreadPlanCallUserExpression::GetRealStopInfo() {
|
||||
|
||||
return stop_info_sp;
|
||||
}
|
||||
|
||||
void ThreadPlanCallUserExpression::DoTakedown(bool success) {
|
||||
ThreadPlanCallFunction::DoTakedown(success);
|
||||
m_user_expression_sp->DidFinishExecuting();
|
||||
}
|
||||
|
@ -73,3 +73,26 @@ void TaskPoolImpl::Worker(TaskPoolImpl *pool) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
void TaskMapOverInt(size_t begin, size_t end,
|
||||
std::function<void(size_t)> const &func) {
|
||||
std::atomic<size_t> idx{begin};
|
||||
size_t num_workers =
|
||||
std::min<size_t>(end, std::thread::hardware_concurrency());
|
||||
|
||||
auto wrapper = [&idx, end, &func]() {
|
||||
while (true) {
|
||||
size_t i = idx.fetch_add(1);
|
||||
if (i >= end)
|
||||
break;
|
||||
func(i);
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<std::future<void>> futures;
|
||||
futures.reserve(num_workers);
|
||||
for (size_t i = 0; i < num_workers; i++)
|
||||
futures.push_back(TaskPool::AddTask(wrapper));
|
||||
for (size_t i = 0; i < num_workers; i++)
|
||||
futures[i].wait();
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
set (FILES
|
||||
FileSpecTest.cpp
|
||||
FileSystemTest.cpp
|
||||
MainLoopTest.cpp
|
||||
SocketAddressTest.cpp
|
||||
SocketTest.cpp
|
||||
SymbolsTest.cpp
|
||||
|
120
unittests/Host/MainLoopTest.cpp
Normal file
120
unittests/Host/MainLoopTest.cpp
Normal file
@ -0,0 +1,120 @@
|
||||
//===-- MainLoopTest.cpp ----------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lldb/Host/MainLoop.h"
|
||||
#include "lldb/Host/common/TCPSocket.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include <future>
|
||||
|
||||
using namespace lldb_private;
|
||||
|
||||
namespace {
|
||||
class MainLoopTest : public testing::Test {
|
||||
public:
|
||||
static void SetUpTestCase() {
|
||||
#ifdef _MSC_VER
|
||||
WSADATA data;
|
||||
ASSERT_EQ(0, WSAStartup(MAKEWORD(2, 2), &data));
|
||||
#endif
|
||||
}
|
||||
|
||||
static void TearDownTestCase() {
|
||||
#ifdef _MSC_VER
|
||||
ASSERT_EQ(0, WSACleanup());
|
||||
#endif
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
bool child_processes_inherit = false;
|
||||
Error error;
|
||||
std::unique_ptr<TCPSocket> listen_socket_up(
|
||||
new TCPSocket(true, child_processes_inherit));
|
||||
ASSERT_TRUE(error.Success());
|
||||
error = listen_socket_up->Listen("localhost:0", 5);
|
||||
ASSERT_TRUE(error.Success());
|
||||
|
||||
Socket *accept_socket;
|
||||
std::future<Error> accept_error = std::async(std::launch::async, [&] {
|
||||
return listen_socket_up->Accept(accept_socket);
|
||||
});
|
||||
|
||||
std::unique_ptr<TCPSocket> connect_socket_up(
|
||||
new TCPSocket(true, child_processes_inherit));
|
||||
error = connect_socket_up->Connect(
|
||||
llvm::formatv("localhost:{0}", listen_socket_up->GetLocalPortNumber())
|
||||
.str());
|
||||
ASSERT_TRUE(error.Success());
|
||||
ASSERT_TRUE(accept_error.get().Success());
|
||||
|
||||
callback_count = 0;
|
||||
socketpair[0] = std::move(connect_socket_up);
|
||||
socketpair[1].reset(accept_socket);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
socketpair[0].reset();
|
||||
socketpair[1].reset();
|
||||
}
|
||||
|
||||
protected:
|
||||
MainLoop::Callback make_callback() {
|
||||
return [&](MainLoopBase &loop) {
|
||||
++callback_count;
|
||||
loop.RequestTermination();
|
||||
};
|
||||
}
|
||||
std::shared_ptr<Socket> socketpair[2];
|
||||
unsigned callback_count;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST_F(MainLoopTest, ReadObject) {
|
||||
char X = 'X';
|
||||
size_t len = sizeof(X);
|
||||
ASSERT_TRUE(socketpair[0]->Write(&X, len).Success());
|
||||
|
||||
MainLoop loop;
|
||||
|
||||
Error error;
|
||||
auto handle = loop.RegisterReadObject(socketpair[1], make_callback(), error);
|
||||
ASSERT_TRUE(error.Success());
|
||||
ASSERT_TRUE(handle);
|
||||
ASSERT_TRUE(loop.Run().Success());
|
||||
ASSERT_EQ(1u, callback_count);
|
||||
}
|
||||
|
||||
TEST_F(MainLoopTest, TerminatesImmediately) {
|
||||
char X = 'X';
|
||||
size_t len = sizeof(X);
|
||||
ASSERT_TRUE(socketpair[0]->Write(&X, len).Success());
|
||||
ASSERT_TRUE(socketpair[1]->Write(&X, len).Success());
|
||||
|
||||
MainLoop loop;
|
||||
Error error;
|
||||
auto handle0 = loop.RegisterReadObject(socketpair[0], make_callback(), error);
|
||||
ASSERT_TRUE(error.Success());
|
||||
auto handle1 = loop.RegisterReadObject(socketpair[1], make_callback(), error);
|
||||
ASSERT_TRUE(error.Success());
|
||||
|
||||
ASSERT_TRUE(loop.Run().Success());
|
||||
ASSERT_EQ(1u, callback_count);
|
||||
}
|
||||
|
||||
#ifdef LLVM_ON_UNIX
|
||||
TEST_F(MainLoopTest, Signal) {
|
||||
MainLoop loop;
|
||||
Error error;
|
||||
|
||||
auto handle = loop.RegisterSignal(SIGUSR1, make_callback(), error);
|
||||
ASSERT_TRUE(error.Success());
|
||||
kill(getpid(), SIGUSR1);
|
||||
ASSERT_TRUE(loop.Run().Success());
|
||||
ASSERT_EQ(1u, callback_count);
|
||||
}
|
||||
#endif
|
@ -30,25 +30,14 @@ TEST(TaskPoolTest, RunTasks) {
|
||||
ASSERT_EQ(17, r[3]);
|
||||
}
|
||||
|
||||
TEST(TaskPoolTest, TaskRunner) {
|
||||
auto fn = [](int x) { return std::make_pair(x, x * x); };
|
||||
TEST(TaskPoolTest, TaskMap) {
|
||||
int data[4];
|
||||
auto fn = [&data](int x) { data[x] = x * x; };
|
||||
|
||||
TaskRunner<std::pair<int, int>> tr;
|
||||
tr.AddTask(fn, 1);
|
||||
tr.AddTask(fn, 2);
|
||||
tr.AddTask(fn, 3);
|
||||
tr.AddTask(fn, 4);
|
||||
TaskMapOverInt(0, 4, fn);
|
||||
|
||||
int count = 0;
|
||||
while (true) {
|
||||
auto f = tr.WaitForNextCompletedTask();
|
||||
if (!f.valid())
|
||||
break;
|
||||
|
||||
++count;
|
||||
std::pair<int, int> v = f.get();
|
||||
ASSERT_EQ(v.first * v.first, v.second);
|
||||
}
|
||||
|
||||
ASSERT_EQ(4, count);
|
||||
ASSERT_EQ(data[0], 0);
|
||||
ASSERT_EQ(data[1], 1);
|
||||
ASSERT_EQ(data[2], 4);
|
||||
ASSERT_EQ(data[3], 9);
|
||||
}
|
||||
|
@ -772,6 +772,27 @@
|
||||
</tr>
|
||||
|
||||
|
||||
<tr><td class="header" colspan="2">List the threads in your program.</td></tr>
|
||||
<tr>
|
||||
<td class="content">
|
||||
<b>(gdb)</b> info threads<br>
|
||||
</td>
|
||||
<td class="content">
|
||||
<b>(lldb)</b> thread list<br>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr><td class="header" colspan="2">Select thread 1 as the default thread for subsequent commands.</td></tr>
|
||||
<tr>
|
||||
<td class="content">
|
||||
<b>(gdb)</b> thread 1<br>
|
||||
</td>
|
||||
<td class="content">
|
||||
<b>(lldb)</b> thread select 1<br>
|
||||
<b>(lldb)</b> t 1<br>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr><td class="header" colspan="2">Show the stack backtrace for the current thread.</td></tr>
|
||||
<tr>
|
||||
<td class="content">
|
||||
@ -1250,6 +1271,16 @@
|
||||
<td class="hed" width="50%">LLDB</td>
|
||||
</tr>
|
||||
|
||||
<tr><td class="header" colspan="2">Search command help for a keyword.</td></tr>
|
||||
<tr>
|
||||
<td class="content">
|
||||
<b>(gdb)</b> apropos keyword<br>
|
||||
</td>
|
||||
<td class="content">
|
||||
<b>(lldb)</b> apropos keyword<br>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr><td class="header" colspan="2">Echo text to the screen.</td></tr>
|
||||
<tr>
|
||||
<td class="content">
|
||||
|
Loading…
Reference in New Issue
Block a user