diff options
author | Stephen Wilson <wilsons@start.ca> | 2011-03-30 15:55:52 +0000 |
---|---|---|
committer | Stephen Wilson <wilsons@start.ca> | 2011-03-30 15:55:52 +0000 |
commit | 84ffe7033c41fd3501eab22632cab93d7df7b1a9 (patch) | |
tree | 24cc9529cf897be63fa3db393e0441e53d6914b9 | |
parent | 8564e0de96d18eba9d8501611591c7c3ef9d8be3 (diff) | |
download | bcm5719-llvm-84ffe7033c41fd3501eab22632cab93d7df7b1a9.tar.gz bcm5719-llvm-84ffe7033c41fd3501eab22632cab93d7df7b1a9.zip |
linux: initial support for 'real' signal handling
This patch upgrades the Linux process plugin to handle a larger range of signal
events. For example, we can detect when the inferior has "crashed" and why,
interrupt a running process, deliver an arbitrary signal, and so on.
llvm-svn: 128547
-rw-r--r-- | lldb/source/Plugins/Process/Linux/LinuxSignals.cpp | 67 | ||||
-rw-r--r-- | lldb/source/Plugins/Process/Linux/LinuxSignals.h | 31 | ||||
-rw-r--r-- | lldb/source/Plugins/Process/Linux/LinuxStopInfo.cpp | 60 | ||||
-rw-r--r-- | lldb/source/Plugins/Process/Linux/LinuxStopInfo.h | 92 | ||||
-rw-r--r-- | lldb/source/Plugins/Process/Linux/LinuxThread.cpp | 112 | ||||
-rw-r--r-- | lldb/source/Plugins/Process/Linux/LinuxThread.h | 30 | ||||
-rw-r--r-- | lldb/source/Plugins/Process/Linux/ProcessLinux.cpp | 158 | ||||
-rw-r--r-- | lldb/source/Plugins/Process/Linux/ProcessLinux.h | 20 | ||||
-rw-r--r-- | lldb/source/Plugins/Process/Linux/ProcessMessage.cpp | 91 | ||||
-rw-r--r-- | lldb/source/Plugins/Process/Linux/ProcessMessage.h | 107 | ||||
-rw-r--r-- | lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp | 337 | ||||
-rw-r--r-- | lldb/source/Plugins/Process/Linux/ProcessMonitor.h | 43 |
12 files changed, 927 insertions, 221 deletions
diff --git a/lldb/source/Plugins/Process/Linux/LinuxSignals.cpp b/lldb/source/Plugins/Process/Linux/LinuxSignals.cpp new file mode 100644 index 00000000000..0a6c34cfbc6 --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/LinuxSignals.cpp @@ -0,0 +1,67 @@ +//===-- LinuxSignals.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include <signal.h> + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "LinuxSignals.h" + +LinuxSignals::LinuxSignals() + : UnixSignals() +{ + Reset(); +} + +void +LinuxSignals::Reset() +{ + m_signals.clear(); + +#define ADDSIGNAL(S, SUPPRESS, STOP, NOTIFY, DESCRIPTION) \ + AddSignal(SIG ## S, "SIG" #S, #S, SUPPRESS, STOP, NOTIFY, DESCRIPTION) + + ADDSIGNAL(HUP, false, true, true, "hangup"); + ADDSIGNAL(INT, true, true, true, "interrupt"); + ADDSIGNAL(QUIT, false, true, true, "quit"); + ADDSIGNAL(ILL, false, true, true, "illegal instruction"); + ADDSIGNAL(TRAP, true, true, true, "trace trap (not reset when caught)"); + ADDSIGNAL(ABRT, false, true, true, "abort"); + ADDSIGNAL(IOT, false, true, true, "abort"); + ADDSIGNAL(BUS, false, true, true, "bus error"); + ADDSIGNAL(FPE, false, true, true, "floating point exception"); + ADDSIGNAL(KILL, false, true, true, "kill"); + ADDSIGNAL(USR1, false, true, true, "user defined signal 1"); + ADDSIGNAL(SEGV, false, true, true, "segmentation violation"); + ADDSIGNAL(USR2, false, true, true, "user defined signal 2"); + ADDSIGNAL(PIPE, false, true, true, "write to pipe with reading end closed"); + ADDSIGNAL(ALRM, false, false, true, "alarm"); + ADDSIGNAL(TERM, false, true, true, "termination requested"); + ADDSIGNAL(STKFLT, false, true, true, "stack fault"); + ADDSIGNAL(CHLD, false, false, true, "child process exit"); + ADDSIGNAL(CONT, false, true, true, "process continue"); + ADDSIGNAL(STOP, false, true, true, "process stop"); + ADDSIGNAL(TSTP, false, true, true, "tty stop"); + ADDSIGNAL(TTIN, false, true, true, "background tty read"); + ADDSIGNAL(TTOU, false, true, true, "background tty write"); + ADDSIGNAL(URG, false, true, true, "urgent data on socket"); + ADDSIGNAL(XCPU, false, true, true, "CPU resource exceeded"); + ADDSIGNAL(XFSZ, false, true, true, "file size limit exceeded"); + ADDSIGNAL(VTALRM, false, true, true, "virtual alarm"); + ADDSIGNAL(PROF, false, true, true, "profiling alarm"); + ADDSIGNAL(WINCH, false, true, true, "window size change"); + ADDSIGNAL(POLL, false, true, true, "pollable event"); + ADDSIGNAL(IO, false, true, true, "input/output ready"); + ADDSIGNAL(PWR, false, true, true, "power failure"); + ADDSIGNAL(SYS, false, true, true, "invalid system call"); + +#undef ADDSIGNAL +} diff --git a/lldb/source/Plugins/Process/Linux/LinuxSignals.h b/lldb/source/Plugins/Process/Linux/LinuxSignals.h new file mode 100644 index 00000000000..a726ecc4040 --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/LinuxSignals.h @@ -0,0 +1,31 @@ +//===-- LinuxSignals.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_LinuxSignals_H_ +#define liblldb_LinuxSignals_H_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/UnixSignals.h" + +/// Linux specific set of Unix signals. +class LinuxSignals + : public lldb_private::UnixSignals +{ +public: + LinuxSignals(); + +private: + void + Reset(); +}; + +#endif diff --git a/lldb/source/Plugins/Process/Linux/LinuxStopInfo.cpp b/lldb/source/Plugins/Process/Linux/LinuxStopInfo.cpp new file mode 100644 index 00000000000..5bcafbd5072 --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/LinuxStopInfo.cpp @@ -0,0 +1,60 @@ +//===-- LinuxStopInfo.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LinuxStopInfo.h" + +using namespace lldb; +using namespace lldb_private; + + +//===----------------------------------------------------------------------===// +// LinuxLimboStopInfo + +LinuxLimboStopInfo::~LinuxLimboStopInfo() { } + +lldb::StopReason +LinuxLimboStopInfo::GetStopReason() const +{ + return lldb::eStopReasonTrace; +} + +const char * +LinuxLimboStopInfo::GetDescription() +{ + return "thread exiting"; +} + +bool +LinuxLimboStopInfo::ShouldStop(Event *event_ptr) +{ + return true; +} + +bool +LinuxLimboStopInfo::ShouldNotify(Event *event_ptr) +{ + return true; +} + +//===----------------------------------------------------------------------===// +// LinuxCrashStopInfo + +LinuxCrashStopInfo::~LinuxCrashStopInfo() { } + +lldb::StopReason +LinuxCrashStopInfo::GetStopReason() const +{ + return lldb::eStopReasonException; +} + +const char * +LinuxCrashStopInfo::GetDescription() +{ + return ProcessMessage::GetCrashReasonString(m_crash_reason); +} diff --git a/lldb/source/Plugins/Process/Linux/LinuxStopInfo.h b/lldb/source/Plugins/Process/Linux/LinuxStopInfo.h new file mode 100644 index 00000000000..96b402a594c --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/LinuxStopInfo.h @@ -0,0 +1,92 @@ +//===-- LinuxStopInfo.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_LinuxStopInfo_H_ +#define liblldb_LinuxStopInfo_H_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/StopInfo.h" + +#include "LinuxThread.h" +#include "ProcessMessage.h" + +//===----------------------------------------------------------------------===// +/// @class LinuxStopInfo +/// @brief Simple base class for all Linux-specific StopInfo objects. +/// +class LinuxStopInfo + : public lldb_private::StopInfo +{ +public: + LinuxStopInfo(lldb_private::Thread &thread, uint32_t status) + : StopInfo(thread, status) + { } +}; + +//===----------------------------------------------------------------------===// +/// @class LinuxLimboStopInfo +/// @brief Represents the stop state of a process ready to exit. +/// +class LinuxLimboStopInfo + : public LinuxStopInfo +{ +public: + LinuxLimboStopInfo(LinuxThread &thread) + : LinuxStopInfo(thread, 0) + { } + + ~LinuxLimboStopInfo(); + + lldb::StopReason + GetStopReason() const; + + const char * + GetDescription(); + + bool + ShouldStop(lldb_private::Event *event_ptr); + + bool + ShouldNotify(lldb_private::Event *event_ptr); +}; + + +//===----------------------------------------------------------------------===// +/// @class LinuxCrashStopInfo +/// @brief Represents the stop state of process that is ready to crash. +/// +class LinuxCrashStopInfo + : public LinuxStopInfo +{ +public: + LinuxCrashStopInfo(LinuxThread &thread, uint32_t status, + ProcessMessage::CrashReason reason) + : LinuxStopInfo(thread, status), + m_crash_reason(reason) + { } + + ~LinuxCrashStopInfo(); + + lldb::StopReason + GetStopReason() const; + + const char * + GetDescription(); + + ProcessMessage::CrashReason + GetCrashReason() const; + +private: + ProcessMessage::CrashReason m_crash_reason; +}; + +#endif diff --git a/lldb/source/Plugins/Process/Linux/LinuxThread.cpp b/lldb/source/Plugins/Process/Linux/LinuxThread.cpp index ebda0d8965c..bac9d48c3ce 100644 --- a/lldb/source/Plugins/Process/Linux/LinuxThread.cpp +++ b/lldb/source/Plugins/Process/Linux/LinuxThread.cpp @@ -12,11 +12,13 @@ // C++ Includes // Other libraries and framework includes +// Project includes #include "lldb/Host/Host.h" #include "lldb/Target/Process.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/Target.h" +#include "LinuxStopInfo.h" #include "LinuxThread.h" #include "ProcessLinux.h" #include "ProcessMonitor.h" @@ -26,11 +28,10 @@ using namespace lldb_private; + LinuxThread::LinuxThread(Process &process, lldb::tid_t tid) : Thread(process, tid), - m_frame_ap(0), - m_stop_info_id(0), - m_note(eNone) + m_frame_ap(0) { } @@ -49,7 +50,6 @@ LinuxThread::GetMonitor() void LinuxThread::RefreshStateAfterStop() { - RefreshPrivateStopReason(); } const char * @@ -117,10 +117,6 @@ LinuxThread::CreateRegisterContextForFrame(lldb_private::StackFrame *frame) lldb::StopInfoSP LinuxThread::GetPrivateStopReason() { - const uint32_t process_stop_id = GetProcess().GetStopID(); - - if (m_stop_info_id != process_stop_id || !m_stop_info || !m_stop_info->IsValid()) - RefreshPrivateStopReason(); return m_stop_info; } @@ -152,34 +148,64 @@ LinuxThread::Resume() ProcessMonitor &monitor = GetMonitor(); bool status; - switch (GetResumeState()) + switch (resume_state) { default: assert(false && "Unexpected state for resume!"); status = false; break; - case lldb::eStateSuspended: - // FIXME: Implement process suspension. - status = false; - case lldb::eStateRunning: SetState(resume_state); - status = monitor.Resume(GetID()); + status = monitor.Resume(GetID(), GetResumeSignal()); break; case lldb::eStateStepping: SetState(resume_state); - status = monitor.SingleStep(GetID()); + status = monitor.SingleStep(GetID(), GetResumeSignal()); break; } - m_note = eNone; return status; } void -LinuxThread::BreakNotify() +LinuxThread::Notify(const ProcessMessage &message) +{ + switch (message.GetKind()) + { + default: + assert(false && "Unexpected message kind!"); + break; + + case ProcessMessage::eLimboMessage: + LimboNotify(message); + break; + + case ProcessMessage::eSignalMessage: + SignalNotify(message); + break; + + case ProcessMessage::eSignalDeliveredMessage: + SignalDeliveredNotify(message); + break; + + case ProcessMessage::eTraceMessage: + TraceNotify(message); + break; + + case ProcessMessage::eBreakpointMessage: + BreakNotify(message); + break; + + case ProcessMessage::eCrashMessage: + CrashNotify(message); + break; + } +} + +void +LinuxThread::BreakNotify(const ProcessMessage &message) { bool status; @@ -190,47 +216,53 @@ LinuxThread::BreakNotify() // corresponding to our current PC. lldb::addr_t pc = GetRegisterContext()->GetPC(); lldb::BreakpointSiteSP bp_site(GetProcess().GetBreakpointSiteList().FindByAddress(pc)); + lldb::break_id_t bp_id = bp_site->GetID(); assert(bp_site && bp_site->ValidForThisThread(this)); - m_note = eBreak; + m_breakpoint = bp_site; + m_stop_info = StopInfo::CreateStopReasonWithBreakpointSiteID(*this, bp_id); } void -LinuxThread::TraceNotify() +LinuxThread::TraceNotify(const ProcessMessage &message) { - m_note = eTrace; + m_stop_info = StopInfo::CreateStopReasonToTrace(*this); } void -LinuxThread::ExitNotify() +LinuxThread::LimboNotify(const ProcessMessage &message) { - m_note = eExit; + m_stop_info = lldb::StopInfoSP(new LinuxLimboStopInfo(*this)); } void -LinuxThread::RefreshPrivateStopReason() +LinuxThread::SignalNotify(const ProcessMessage &message) { - m_stop_info_id = GetProcess().GetStopID(); + int signo = message.GetSignal(); - switch (m_note) { + m_stop_info = StopInfo::CreateStopReasonWithSignal(*this, signo); + SetResumeSignal(signo); +} - default: - case eNone: - m_stop_info.reset(); - break; +void +LinuxThread::SignalDeliveredNotify(const ProcessMessage &message) +{ + int signo = message.GetSignal(); - case eBreak: - m_stop_info = StopInfo::CreateStopReasonWithBreakpointSiteID( - *this, m_breakpoint->GetID()); - break; + // Just treat debugger generated signal events like breakpoints for now. + m_stop_info = StopInfo::CreateStopReasonToTrace(*this); + SetResumeSignal(signo); +} - case eTrace: - m_stop_info = StopInfo::CreateStopReasonToTrace(*this); - break; +void +LinuxThread::CrashNotify(const ProcessMessage &message) +{ + int signo = message.GetSignal(); - case eExit: - m_stop_info = StopInfo::CreateStopReasonWithSignal(*this, SIGCHLD); - break; - } + assert(message.GetKind() == ProcessMessage::eCrashMessage); + + m_stop_info = lldb::StopInfoSP(new LinuxCrashStopInfo( + *this, signo, message.GetCrashReason())); + SetResumeSignal(signo); } diff --git a/lldb/source/Plugins/Process/Linux/LinuxThread.h b/lldb/source/Plugins/Process/Linux/LinuxThread.h index 88973fc8844..cafc83a7c87 100644 --- a/lldb/source/Plugins/Process/Linux/LinuxThread.h +++ b/lldb/source/Plugins/Process/Linux/LinuxThread.h @@ -17,6 +17,7 @@ // Other libraries and framework includes #include "lldb/Target/Thread.h" +class ProcessMessage; class ProcessMonitor; class RegisterContextLinux; @@ -51,9 +52,7 @@ public: // bool Resume(); - void BreakNotify(); - void TraceNotify(); - void ExitNotify(); + void Notify(const ProcessMessage &message); protected: virtual bool @@ -63,7 +62,6 @@ protected: RestoreSaveFrameZero(const RegisterCheckpoint &checkpoint); private: - RegisterContextLinux * GetRegisterContextLinux () { @@ -77,26 +75,18 @@ private: lldb::BreakpointSiteSP m_breakpoint; lldb::StopInfoSP m_stop_info; - // Cached process stop id. Used to ensure we do not recalculate stop - // information/state needlessly. - uint32_t m_stop_info_id; - - enum Notification { - eNone, - eBreak, - eTrace, - eExit - }; - - Notification m_note; - - ProcessMonitor &GetMonitor(); + ProcessMonitor & + GetMonitor(); lldb::StopInfoSP GetPrivateStopReason(); - void - RefreshPrivateStopReason(); + void BreakNotify(const ProcessMessage &message); + void TraceNotify(const ProcessMessage &message); + void LimboNotify(const ProcessMessage &message); + void SignalNotify(const ProcessMessage &message); + void SignalDeliveredNotify(const ProcessMessage &message); + void CrashNotify(const ProcessMessage &message); lldb_private::Unwind * GetUnwinder(); diff --git a/lldb/source/Plugins/Process/Linux/ProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/ProcessLinux.cpp index b3405dda944..f915888ef18 100644 --- a/lldb/source/Plugins/Process/Linux/ProcessLinux.cpp +++ b/lldb/source/Plugins/Process/Linux/ProcessLinux.cpp @@ -72,7 +72,9 @@ ProcessLinux::GetPluginDescriptionStatic() ProcessLinux::ProcessLinux(Target& target, Listener &listener) : Process(target, listener), m_monitor(NULL), - m_module(NULL) + m_module(NULL), + m_in_limbo(false), + m_exit_now(false) { // FIXME: Putting this code in the ctor and saving the byte order in a // member variable is a hack to avoid const qual issues in GetByteOrder. @@ -147,11 +149,20 @@ ProcessLinux::DidLaunch() Error ProcessLinux::DoResume() { - assert(GetPrivateState() == eStateStopped && "Bad state for DoResume!"); + StateType state = GetPrivateState(); - // Set our state to running. This ensures inferior threads do not post a - // state change first. - SetPrivateState(eStateRunning); + assert(state == eStateStopped || state == eStateCrashed); + + // We are about to resume a thread that will cause the process to exit so + // set our exit status now. Do not change our state if the inferior + // crashed. + if (state == eStateStopped) + { + if (m_in_limbo) + SetExitStatus(m_exit_status, NULL); + else + SetPrivateState(eStateRunning); + } bool did_resume = false; uint32_t thread_count = m_thread_list.GetSize(false); @@ -182,7 +193,23 @@ ProcessLinux::GetImageInfoAddress() Error ProcessLinux::DoHalt(bool &caused_stop) { - return Error(1, eErrorTypeGeneric); + Error error; + + if (IsStopped()) + { + caused_stop = false; + } + else if (kill(GetID(), SIGSTOP)) + { + caused_stop = false; + error.SetErrorToErrno(); + } + else + { + caused_stop = true; + } + + return error; } Error @@ -194,7 +221,12 @@ ProcessLinux::DoDetach() Error ProcessLinux::DoSignal(int signal) { - return Error(1, eErrorTypeGeneric); + Error error; + + if (kill(GetID(), signal)) + error.SetErrorToErrno(); + + return error; } Error @@ -204,42 +236,19 @@ ProcessLinux::DoDestroy() if (!HasExited()) { - // Shut down the private state thread as we will synchronize with events - // ourselves. Discard all current thread plans. - PausePrivateStateThread(); - GetThreadList().DiscardThreadPlans(); - - // Bringing the inferior into limbo will be caught by our monitor - // thread, in turn updating the process state. - if (!m_monitor->BringProcessIntoLimbo()) + // Drive the exit event to completion (do not keep the inferior in + // limbo). + m_exit_now = true; + + if (kill(m_monitor->GetPID(), SIGKILL) && error.Success()) { - error.SetErrorToGenericError(); - error.SetErrorString("Process termination failed."); + error.SetErrorToErrno(); return error; } - // Wait for the event to arrive. This is guaranteed to be an exit event. - StateType state; - EventSP event; - do { - TimeValue timeout_time; - timeout_time = TimeValue::Now(); - timeout_time.OffsetWithSeconds(2); - state = WaitForStateChangedEventsPrivate(&timeout_time, event); - } while (state != eStateExited && state != eStateInvalid); - - // Check if we timed out waiting for the exit event to arrive. - if (state == eStateInvalid) - error.SetErrorString("ProcessLinux::DoDestroy timed out."); - - // Restart standard event handling and send the process the final kill, - // driving it out of limbo. - ResumePrivateStateThread(); + SetPrivateState(eStateExited); } - if (kill(m_monitor->GetPID(), SIGKILL) && error.Success()) - error.SetErrorToErrno(); - return error; } @@ -251,18 +260,41 @@ ProcessLinux::SendMessage(const ProcessMessage &message) switch (message.GetKind()) { default: - SetPrivateState(eStateStopped); + assert(false && "Unexpected process message!"); break; case ProcessMessage::eInvalidMessage: return; + case ProcessMessage::eLimboMessage: + m_in_limbo = true; + m_exit_status = message.GetExitStatus(); + if (m_exit_now) + { + SetPrivateState(eStateExited); + m_monitor->Detach(); + } + else + SetPrivateState(eStateStopped); + break; + case ProcessMessage::eExitMessage: - SetExitStatus(message.GetExitStatus(), NULL); + m_exit_status = message.GetExitStatus(); + SetExitStatus(m_exit_status, NULL); + break; + + case ProcessMessage::eTraceMessage: + case ProcessMessage::eBreakpointMessage: + SetPrivateState(eStateStopped); break; case ProcessMessage::eSignalMessage: - SetExitStatus(-1, NULL); + case ProcessMessage::eSignalDeliveredMessage: + SetPrivateState(eStateStopped); + break; + + case ProcessMessage::eCrashMessage: + SetPrivateState(eStateCrashed); break; } @@ -278,30 +310,12 @@ ProcessLinux::RefreshStateAfterStop() ProcessMessage &message = m_message_queue.front(); - // Resolve the thread this message corresponds to. + // Resolve the thread this message corresponds to and pass it along. lldb::tid_t tid = message.GetTID(); LinuxThread *thread = static_cast<LinuxThread*>( GetThreadList().FindThreadByID(tid, false).get()); - switch (message.GetKind()) - { - default: - assert(false && "Unexpected message kind!"); - break; - - case ProcessMessage::eExitMessage: - case ProcessMessage::eSignalMessage: - thread->ExitNotify(); - break; - - case ProcessMessage::eTraceMessage: - thread->TraceNotify(); - break; - - case ProcessMessage::eBreakpointMessage: - thread->BreakNotify(); - break; - } + thread->Notify(message); m_message_queue.pop(); } @@ -432,6 +446,11 @@ ProcessLinux::GetSTDERR(char *buf, size_t len, Error &error) return GetSTDOUT(buf, len, error); } +UnixSignals & +ProcessLinux::GetUnixSignals() +{ + return m_linux_signals; +} //------------------------------------------------------------------------------ // ProcessInterface protocol. @@ -482,8 +501,6 @@ ProcessLinux::HasExited() default: break; - case eStateUnloaded: - case eStateCrashed: case eStateDetached: case eStateExited: return true; @@ -491,3 +508,20 @@ ProcessLinux::HasExited() return false; } + +bool +ProcessLinux::IsStopped() +{ + switch (GetPrivateState()) + { + default: + break; + + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + return true; + } + + return false; +} diff --git a/lldb/source/Plugins/Process/Linux/ProcessLinux.h b/lldb/source/Plugins/Process/Linux/ProcessLinux.h index 733561195de..762f817c335 100644 --- a/lldb/source/Plugins/Process/Linux/ProcessLinux.h +++ b/lldb/source/Plugins/Process/Linux/ProcessLinux.h @@ -17,6 +17,7 @@ // Other libraries and framework includes #include "lldb/Target/Process.h" +#include "LinuxSignals.h" #include "ProcessMessage.h" class ProcessMonitor; @@ -178,6 +179,9 @@ public: ProcessMonitor &GetMonitor() { return *m_monitor; } + lldb_private::UnixSignals & + GetUnixSignals(); + private: /// Target byte order. lldb::ByteOrder m_byte_order; @@ -192,6 +196,19 @@ private: lldb_private::Mutex m_message_mutex; std::queue<ProcessMessage> m_message_queue; + /// True when the process has entered a state of "limbo". + /// + /// This flag qualifies eStateStopped. It lets us know that when we + /// continue from this state the process will exit. Also, when true, + /// Process::m_exit_status is set. + bool m_in_limbo; + + /// Drive any exit events to completion. + bool m_exit_now; + + /// Linux-specific signal set. + LinuxSignals m_linux_signals; + /// Updates the loaded sections provided by the executable. /// /// FIXME: It would probably be better to delegate this task to the @@ -200,6 +217,9 @@ private: /// Returns true if the process has exited. bool HasExited(); + + /// Returns true if the process is stopped. + bool IsStopped(); }; #endif // liblldb_MacOSXProcess_H_ diff --git a/lldb/source/Plugins/Process/Linux/ProcessMessage.cpp b/lldb/source/Plugins/Process/Linux/ProcessMessage.cpp new file mode 100644 index 00000000000..db7827d551d --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/ProcessMessage.cpp @@ -0,0 +1,91 @@ +//===-- ProcessMessage.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ProcessMessage.h" + +using namespace lldb_private; + +const char * +ProcessMessage::GetCrashReasonString(CrashReason reason) +{ + const char *str = NULL; + + switch (reason) + { + default: + assert(false && "invalid CrashReason"); + break; + + case eInvalidAddress: + str = "invalid address"; + break; + case ePrivilegedAddress: + str = "address access protected"; + break; + case eIllegalOpcode: + str = "illegal instruction"; + break; + case eIllegalOperand: + str = "illegal instruction operand"; + break; + case eIllegalAddressingMode: + str = "illegal addressing mode"; + break; + case eIllegalTrap: + str = "illegal trap"; + break; + case ePrivilegedOpcode: + str = "privileged instruction"; + break; + case ePrivilegedRegister: + str = "privileged register"; + break; + case eCoprocessorError: + str = "coprocessor error"; + break; + case eInternalStackError: + str = "internal stack error"; + break; + case eIllegalAlignment: + str = "illegal alignment"; + break; + case eIllegalAddress: + str = "illegal address"; + break; + case eHardwareError: + str = "hardware error"; + break; + case eIntegerDivideByZero: + str = "integer divide by zero"; + break; + case eIntegerOverflow: + str = "integer overflow"; + break; + case eFloatDivideByZero: + str = "floating point divide by zero"; + break; + case eFloatOverflow: + str = "floating point overflow"; + break; + case eFloatUnderflow: + str = "floating point underflow"; + break; + case eFloatInexactResult: + str = "inexact floating point result"; + break; + case eFloatInvalidOperation: + str = "invalid floating point operation"; + break; + case eFloatSubscriptRange: + str = "invalid floating point subscript range"; + break; + } + + return str; +} diff --git a/lldb/source/Plugins/Process/Linux/ProcessMessage.h b/lldb/source/Plugins/Process/Linux/ProcessMessage.h index b13321ef6f5..5162d30825c 100644 --- a/lldb/source/Plugins/Process/Linux/ProcessMessage.h +++ b/lldb/source/Plugins/Process/Linux/ProcessMessage.h @@ -26,63 +26,134 @@ public: eExitMessage, eLimboMessage, eSignalMessage, + eSignalDeliveredMessage, eTraceMessage, - eBreakpointMessage + eBreakpointMessage, + eCrashMessage + }; + + enum CrashReason + { + eInvalidCrashReason, + + // SIGSEGV crash reasons. + eInvalidAddress, + ePrivilegedAddress, + + // SIGILL crash reasons. + eIllegalOpcode, + eIllegalOperand, + eIllegalAddressingMode, + eIllegalTrap, + ePrivilegedOpcode, + ePrivilegedRegister, + eCoprocessorError, + eInternalStackError, + + // SIGBUS crash reasons, + eIllegalAlignment, + eIllegalAddress, + eHardwareError, + + // SIGFPE crash reasons, + eIntegerDivideByZero, + eIntegerOverflow, + eFloatDivideByZero, + eFloatOverflow, + eFloatUnderflow, + eFloatInexactResult, + eFloatInvalidOperation, + eFloatSubscriptRange }; ProcessMessage() - : m_kind(eInvalidMessage), - m_tid(LLDB_INVALID_PROCESS_ID), - m_data(0) { } + : m_tid(LLDB_INVALID_PROCESS_ID), + m_kind(eInvalidMessage), + m_crash_reason(eInvalidCrashReason), + m_status(0), + m_addr(0) { } Kind GetKind() const { return m_kind; } lldb::tid_t GetTID() const { return m_tid; } - static ProcessMessage Exit(lldb::tid_t tid, int status) { - return ProcessMessage(tid, eExitMessage, status); - } - + /// Indicates that the thread @p tid is about to exit with status @p status. static ProcessMessage Limbo(lldb::tid_t tid, int status) { return ProcessMessage(tid, eLimboMessage, status); } + /// Indicates that the thread @p tid had the signal @p signum delivered. static ProcessMessage Signal(lldb::tid_t tid, int signum) { return ProcessMessage(tid, eSignalMessage, signum); } + /// Indicates that a signal @p signum generated by the debugging process was + /// delivered to the thread @p tid. + static ProcessMessage SignalDelivered(lldb::tid_t tid, int signum) { + return ProcessMessage(tid, eSignalDeliveredMessage, signum); + } + + /// Indicates that the thread @p tid encountered a trace point. static ProcessMessage Trace(lldb::tid_t tid) { return ProcessMessage(tid, eTraceMessage); } + /// Indicates that the thread @p tid encountered a break point. static ProcessMessage Break(lldb::tid_t tid) { return ProcessMessage(tid, eBreakpointMessage); } + /// Indicates that the thread @p tid crashed. + static ProcessMessage Crash(lldb::pid_t pid, CrashReason reason, + int signo, lldb::addr_t fault_addr) { + ProcessMessage message(pid, eCrashMessage, signo, fault_addr); + message.m_crash_reason = reason; + return message; + } + int GetExitStatus() const { assert(GetKind() == eExitMessage || GetKind() == eLimboMessage); - return m_data; + return m_status; } int GetSignal() const { - assert(GetKind() == eSignalMessage); - return m_data; + assert(GetKind() == eSignalMessage || GetKind() == eCrashMessage || + GetKind() == eSignalDeliveredMessage); + return m_status; } int GetStopStatus() const { assert(GetKind() == eSignalMessage); - return m_data; + return m_status; + } + + CrashReason GetCrashReason() const { + assert(GetKind() == eCrashMessage); + return m_crash_reason; } + lldb::addr_t GetFaultAddress() const { + assert(GetKind() == eCrashMessage); + return m_addr; + } + + static const char * + GetCrashReasonString(CrashReason reason); + private: - ProcessMessage(lldb::tid_t tid, Kind kind, int data = 0) - : m_kind(kind), - m_tid(tid), - m_data(data) { } + ProcessMessage(lldb::tid_t tid, Kind kind, + int status = 0, lldb::addr_t addr = 0) + : m_tid(tid), + m_kind(kind), + m_crash_reason(eInvalidCrashReason), + m_status(status), + m_addr(addr) { } - Kind m_kind; lldb::tid_t m_tid; - int m_data; + Kind m_kind : 8; + CrashReason m_crash_reason : 8; + int m_status; + lldb::addr_t m_addr; }; #endif // #ifndef liblldb_ProcessMessage_H_ diff --git a/lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp b/lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp index c5909c85a6d..48a57a8b010 100644 --- a/lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp +++ b/lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp @@ -338,20 +338,26 @@ ReadFPROperation::Execute(ProcessMonitor *monitor) class ResumeOperation : public Operation { public: - ResumeOperation(lldb::tid_t tid, bool &result) : - m_tid(tid), m_result(result) { } + ResumeOperation(lldb::tid_t tid, uint32_t signo, bool &result) : + m_tid(tid), m_signo(signo), m_result(result) { } void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; + uint32_t m_signo; bool &m_result; }; void ResumeOperation::Execute(ProcessMonitor *monitor) { - if (ptrace(PTRACE_CONT, m_tid, NULL, NULL)) + int data = 0; + + if (m_signo != LLDB_INVALID_SIGNAL_NUMBER) + data = m_signo; + + if (ptrace(PTRACE_CONT, m_tid, NULL, data)) m_result = false; else m_result = true; @@ -363,20 +369,26 @@ ResumeOperation::Execute(ProcessMonitor *monitor) class SingleStepOperation : public Operation { public: - SingleStepOperation(lldb::tid_t tid, bool &result) - : m_tid(tid), m_result(result) { } + SingleStepOperation(lldb::tid_t tid, uint32_t signo, bool &result) + : m_tid(tid), m_signo(signo), m_result(result) { } void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; + uint32_t m_signo; bool &m_result; }; void SingleStepOperation::Execute(ProcessMonitor *monitor) { - if (ptrace(PTRACE_SINGLESTEP, m_tid, NULL, NULL)) + int data = 0; + + if (m_signo != LLDB_INVALID_SIGNAL_NUMBER) + data = m_signo; + + if (ptrace(PTRACE_SINGLESTEP, m_tid, NULL, data)) m_result = false; else m_result = true; @@ -457,32 +469,6 @@ KillOperation::Execute(ProcessMonitor *monitor) m_result = false; else m_result = true; - -#if 0 - // First, stop the inferior process. - if (kill(pid, SIGSTOP)) - { - m_result = false; - return; - } - - // Clear any ptrace options. When PTRACE_O_TRACEEXIT is set, a plain - // PTRACE_KILL (or any termination signal) will not truely terminate the - // inferior process. Instead, the process is left in a state of "limbo" - // allowing us to interrogate its state. However in this case we really do - // want the process gone. - if (ptrace(PTRACE_SETOPTIONS, pid, NULL, 0UL)) - { - m_result = false; - return; - } - - // Kill it. - if (ptrace(PTRACE_KILL, pid, NULL, NULL)) - m_result = false; - else - m_result = true; -#endif } ProcessMonitor::LaunchArgs::LaunchArgs(ProcessMonitor *monitor, @@ -586,12 +572,7 @@ WAIT_AGAIN: ProcessMonitor::~ProcessMonitor() { - StopMonitoringChildProcess(); - StopOperationThread(); - - close(m_terminal_fd); - close(m_client_fd); - close(m_server_fd); + StopMonitor(); } //------------------------------------------------------------------------------ @@ -768,50 +749,39 @@ ProcessMonitor::MonitorCallback(void *callback_baton, ProcessMessage message; ProcessMonitor *monitor = static_cast<ProcessMonitor*>(callback_baton); ProcessLinux *process = monitor->m_process; + bool stop_monitoring; + siginfo_t info; - switch (signal) - { - case 0: - // No signal. The child has exited normally. - message = ProcessMessage::Exit(pid, status); - break; - - case SIGTRAP: - // Specially handle SIGTRAP and form the appropriate message. - message = MonitorSIGTRAP(monitor, pid); - break; + if (!monitor->GetSignalInfo(pid, &info)) + stop_monitoring = true; // pid is gone. Bail. + else { + switch (info.si_signo) + { + case SIGTRAP: + message = MonitorSIGTRAP(monitor, &info, pid); + break; + + default: + message = MonitorSignal(monitor, &info, pid); + break; + } - default: - // For all other signals simply notify the process instance. Note that - // the process exit status is set when the signal resulted in - // termination. - // - // FIXME: We need a specialized message to inform the process instance - // about "crashes". - if (status) - message = ProcessMessage::Exit(pid, status); - else - message = ProcessMessage::Signal(pid, signal); + process->SendMessage(message); + stop_monitoring = message.GetKind() == ProcessMessage::eExitMessage; } - process->SendMessage(message); - bool stop_monitoring = message.GetKind() == ProcessMessage::eExitMessage; return stop_monitoring; } ProcessMessage -ProcessMonitor::MonitorSIGTRAP(ProcessMonitor *monitor, lldb::pid_t pid) +ProcessMonitor::MonitorSIGTRAP(ProcessMonitor *monitor, + const struct siginfo *info, lldb::pid_t pid) { - siginfo_t info; ProcessMessage message; - bool status; - - status = monitor->GetSignalInfo(pid, &info); - assert(status && "GetSignalInfo failed!"); - assert(info.si_signo == SIGTRAP && "Unexpected child signal!"); + assert(info->si_signo == SIGTRAP && "Unexpected child signal!"); - switch (info.si_code) + switch (info->si_code) { default: assert(false && "Unexpected SIGTRAP code!"); @@ -825,7 +795,7 @@ ProcessMonitor::MonitorSIGTRAP(ProcessMonitor *monitor, lldb::pid_t pid) unsigned long data = 0; if (!monitor->GetEventMessage(pid, &data)) data = -1; - message = ProcessMessage::Exit(pid, (data >> 8)); + message = ProcessMessage::Limbo(pid, (data >> 8)); break; } @@ -843,6 +813,193 @@ ProcessMonitor::MonitorSIGTRAP(ProcessMonitor *monitor, lldb::pid_t pid) return message; } +ProcessMessage +ProcessMonitor::MonitorSignal(ProcessMonitor *monitor, + const struct siginfo *info, lldb::pid_t pid) +{ + ProcessMessage message; + int signo = info->si_signo; + + // POSIX says that process behaviour is undefined after it ignores a SIGFPE, + // SIGILL, SIGSEGV, or SIGBUS *unless* that signal was generated by a + // kill(2) or raise(3). Similarly for tgkill(2) on Linux. + // + // IOW, user generated signals never generate what we consider to be a + // "crash". + // + // Similarly, ACK signals generated by this monitor. + if (info->si_code == SI_TKILL || info->si_code == SI_USER) + { + if (info->si_pid == getpid()) + return ProcessMessage::SignalDelivered(pid, signo); + else + return ProcessMessage::Signal(pid, signo); + } + + if (signo == SIGSEGV) { + lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr); + ProcessMessage::CrashReason reason = GetCrashReasonForSIGSEGV(info); + return ProcessMessage::Crash(pid, reason, signo, fault_addr); + } + + if (signo == SIGILL) { + lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr); + ProcessMessage::CrashReason reason = GetCrashReasonForSIGILL(info); + return ProcessMessage::Crash(pid, reason, signo, fault_addr); + } + + if (signo == SIGFPE) { + lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr); + ProcessMessage::CrashReason reason = GetCrashReasonForSIGFPE(info); + return ProcessMessage::Crash(pid, reason, signo, fault_addr); + } + + if (signo == SIGBUS) { + lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr); + ProcessMessage::CrashReason reason = GetCrashReasonForSIGBUS(info); + return ProcessMessage::Crash(pid, reason, signo, fault_addr); + } + + // Everything else is "normal" and does not require any special action on + // our part. + return ProcessMessage::Signal(pid, signo); +} + +ProcessMessage::CrashReason +ProcessMonitor::GetCrashReasonForSIGSEGV(const struct siginfo *info) +{ + ProcessMessage::CrashReason reason; + assert(info->si_signo == SIGSEGV); + + reason = ProcessMessage::eInvalidCrashReason; + + switch (info->si_code) + { + default: + assert(false && "unexpected si_code for SIGSEGV"); + break; + case SEGV_MAPERR: + reason = ProcessMessage::eInvalidAddress; + break; + case SEGV_ACCERR: + reason = ProcessMessage::ePrivilegedAddress; + break; + } + + return reason; +} + +ProcessMessage::CrashReason +ProcessMonitor::GetCrashReasonForSIGILL(const struct siginfo *info) +{ + ProcessMessage::CrashReason reason; + assert(info->si_signo == SIGILL); + + reason = ProcessMessage::eInvalidCrashReason; + + switch (info->si_code) + { + default: + assert(false && "unexpected si_code for SIGILL"); + break; + case ILL_ILLOPC: + reason = ProcessMessage::eIllegalOpcode; + break; + case ILL_ILLOPN: + reason = ProcessMessage::eIllegalOperand; + break; + case ILL_ILLADR: + reason = ProcessMessage::eIllegalAddressingMode; + break; + case ILL_ILLTRP: + reason = ProcessMessage::eIllegalTrap; + break; + case ILL_PRVOPC: + reason = ProcessMessage::ePrivilegedOpcode; + break; + case ILL_PRVREG: + reason = ProcessMessage::ePrivilegedRegister; + break; + case ILL_COPROC: + reason = ProcessMessage::eCoprocessorError; + break; + case ILL_BADSTK: + reason = ProcessMessage::eInternalStackError; + break; + } + + return reason; +} + +ProcessMessage::CrashReason +ProcessMonitor::GetCrashReasonForSIGFPE(const struct siginfo *info) +{ + ProcessMessage::CrashReason reason; + assert(info->si_signo == SIGFPE); + + reason = ProcessMessage::eInvalidCrashReason; + + switch (info->si_code) + { + default: + assert(false && "unexpected si_code for SIGFPE"); + break; + case FPE_INTDIV: + reason = ProcessMessage::eIntegerDivideByZero; + break; + case FPE_INTOVF: + reason = ProcessMessage::eIntegerOverflow; + break; + case FPE_FLTDIV: + reason = ProcessMessage::eFloatDivideByZero; + break; + case FPE_FLTOVF: + reason = ProcessMessage::eFloatOverflow; + break; + case FPE_FLTUND: + reason = ProcessMessage::eFloatUnderflow; + break; + case FPE_FLTRES: + reason = ProcessMessage::eFloatInexactResult; + break; + case FPE_FLTINV: + reason = ProcessMessage::eFloatInvalidOperation; + break; + case FPE_FLTSUB: + reason = ProcessMessage::eFloatSubscriptRange; + break; + } + + return reason; +} + +ProcessMessage::CrashReason +ProcessMonitor::GetCrashReasonForSIGBUS(const struct siginfo *info) +{ + ProcessMessage::CrashReason reason; + assert(info->si_signo == SIGBUS); + + reason = ProcessMessage::eInvalidCrashReason; + + switch (info->si_code) + { + default: + assert(false && "unexpected si_code for SIGBUS"); + break; + case BUS_ADRALN: + reason = ProcessMessage::eIllegalAlignment; + break; + case BUS_ADRERR: + reason = ProcessMessage::eIllegalAddress; + break; + case BUS_OBJERR: + reason = ProcessMessage::eHardwareError; + break; + } + + return reason; +} + void ProcessMonitor::ServeOperation(LaunchArgs *args) { @@ -976,19 +1133,19 @@ ProcessMonitor::ReadFPR(void *buf) } bool -ProcessMonitor::Resume(lldb::tid_t tid) +ProcessMonitor::Resume(lldb::tid_t tid, uint32_t signo) { bool result; - ResumeOperation op(tid, result); + ResumeOperation op(tid, signo, result); DoOperation(&op); return result; } bool -ProcessMonitor::SingleStep(lldb::tid_t tid) +ProcessMonitor::SingleStep(lldb::tid_t tid, uint32_t signo) { bool result; - SingleStepOperation op(tid, result); + SingleStepOperation op(tid, signo, result); DoOperation(&op); return result; } @@ -1021,6 +1178,16 @@ ProcessMonitor::GetEventMessage(lldb::tid_t tid, unsigned long *message) } bool +ProcessMonitor::Detach() +{ + bool result; + KillOperation op(result); + DoOperation(&op); + StopMonitor(); + return result; +} + +bool ProcessMonitor::DupDescriptor(const char *path, int fd, int flags) { int target_fd = open(path, flags); @@ -1043,3 +1210,23 @@ ProcessMonitor::StopMonitoringChildProcess() m_monitor_thread = LLDB_INVALID_HOST_THREAD; } } + +void +ProcessMonitor::StopMonitor() +{ + StopMonitoringChildProcess(); + StopOperationThread(); + CloseFD(m_terminal_fd); + CloseFD(m_client_fd); + CloseFD(m_server_fd); +} + +void +ProcessMonitor::CloseFD(int &fd) +{ + if (fd != -1) + { + close(fd); + fd = -1; + } +} diff --git a/lldb/source/Plugins/Process/Linux/ProcessMonitor.h b/lldb/source/Plugins/Process/Linux/ProcessMonitor.h index c0160199652..1bfdb862d88 100644 --- a/lldb/source/Plugins/Process/Linux/ProcessMonitor.h +++ b/lldb/source/Plugins/Process/Linux/ProcessMonitor.h @@ -12,6 +12,7 @@ // C Includes #include <semaphore.h> +#include <signal.h> // C++ Includes // Other libraries and framework includes @@ -127,13 +128,15 @@ public: bool GetEventMessage(lldb::tid_t tid, unsigned long *message); - /// Resumes the given thread. + /// Resumes the given thread. If @p signo is anything but + /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. bool - Resume(lldb::tid_t tid); + Resume(lldb::tid_t tid, uint32_t signo); - /// Single steps the given thread. + /// Single steps the given thread. If @p signo is anything but + /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. bool - SingleStep(lldb::tid_t tid); + SingleStep(lldb::tid_t tid, uint32_t signo); /// Sends the inferior process a PTRACE_KILL signal. The inferior will /// still exists and can be interrogated. Once resumed it will exit as @@ -141,6 +144,10 @@ public: bool BringProcessIntoLimbo(); + bool + Detach(); + + private: ProcessLinux *m_process; @@ -207,13 +214,37 @@ private: lldb::pid_t pid, int signal, int status); static ProcessMessage - MonitorSIGTRAP(ProcessMonitor *monitor, lldb::pid_t pid); + MonitorSIGTRAP(ProcessMonitor *monitor, + const struct siginfo *info, lldb::pid_t pid); + + static ProcessMessage + MonitorSignal(ProcessMonitor *monitor, + const struct siginfo *info, lldb::pid_t pid); + + static ProcessMessage::CrashReason + GetCrashReasonForSIGSEGV(const struct siginfo *info); + + static ProcessMessage::CrashReason + GetCrashReasonForSIGILL(const struct siginfo *info); + + static ProcessMessage::CrashReason + GetCrashReasonForSIGFPE(const struct siginfo *info); + + static ProcessMessage::CrashReason + GetCrashReasonForSIGBUS(const struct siginfo *info); void DoOperation(Operation *op); /// Stops the child monitor thread. - void StopMonitoringChildProcess(); + void + StopMonitoringChildProcess(); + + void + StopMonitor(); + + void + CloseFD(int &fd); }; #endif // #ifndef liblldb_ProcessMonitor_H_ |