<rdar://problem/12649160>

Added the ability to debug through your process exec'ing itself to the same architecture.

llvm-svn: 169340
This commit is contained in:
Greg Clayton 2012-12-05 00:16:59 +00:00
parent d31802c1f6
commit 90ba81150e
24 changed files with 257 additions and 13 deletions

View File

@ -67,6 +67,7 @@ public:
/// eStopReasonWatchpoint 1 watchpoint id
/// eStopReasonSignal 1 unix signal number
/// eStopReasonException N exception data
/// eStopReasonExec 0
/// eStopReasonPlanComplete 0
//--------------------------------------------------------------------------
uint64_t

View File

@ -93,7 +93,20 @@ public:
DidLaunch () = 0;
//------------------------------------------------------------------
/// Helper function that can be used to detect when a process has
/// called exec and is now a new and different process. This can
/// be called when necessary to try and detect the exec. The process
/// might be able to answer this question, but sometimes it might
/// not be able and the dynamic loader often knows what the program
/// entry point is. So the process and the dynamic loader can work
/// together to detect this.
//------------------------------------------------------------------
virtual bool
ProcessDidExec ()
{
return false;
}
//------------------------------------------------------------------
/// Get whether the process should stop when images change.
///

View File

@ -2054,6 +2054,27 @@ public:
DidAttach () {}
//------------------------------------------------------------------
/// Called after a process re-execs itself.
///
/// Allow Process plug-ins to execute some code after a process has
/// exec'ed itself. Subclasses typically should override DoDidExec()
/// as the lldb_private::Process class needs to remove its dynamic
/// loader, runtime, ABI and other plug-ins, as well as unload all
/// shared libraries.
//------------------------------------------------------------------
virtual void
DidExec ();
//------------------------------------------------------------------
/// Subclasses of Process should implement this function if they
/// need to do anything after a process exec's itself.
//------------------------------------------------------------------
virtual void
DoDidExec ()
{
}
//------------------------------------------------------------------
/// Called before launching to a process.
///

View File

@ -129,6 +129,9 @@ public:
static lldb::StopInfoSP
CreateStopReasonWithException (Thread &thread, const char *description);
static lldb::StopInfoSP
CreateStopReasonWithExec (Thread &thread);
static lldb::ValueObjectSP
GetReturnValueObject (lldb::StopInfoSP &stop_info_sp);

View File

@ -390,6 +390,8 @@ public:
void
DeleteCurrentProcess ();
void
CleanupProcess ();
//------------------------------------------------------------------
/// Dump a description of this object to a Stream.
///

View File

@ -176,6 +176,7 @@ namespace lldb {
eStopReasonWatchpoint,
eStopReasonSignal,
eStopReasonException,
eStopReasonExec, // Program was re-exec'ed
eStopReasonPlanComplete
} StopReason;

View File

@ -88,6 +88,7 @@ public:
/// eStopReasonWatchpoint 1 watchpoint id
/// eStopReasonSignal 1 unix signal number
/// eStopReasonException N exception data
/// eStopReasonExec 0
/// eStopReasonPlanComplete 0
//--------------------------------------------------------------------------
") GetStopReasonDataAtIndex;

View File

@ -147,6 +147,7 @@ SBThread::GetStopReasonDataCount ()
case eStopReasonInvalid:
case eStopReasonNone:
case eStopReasonTrace:
case eStopReasonExec:
case eStopReasonPlanComplete:
// There is no data for these stop reasons.
return 0;
@ -204,6 +205,7 @@ SBThread::GetStopReasonDataAtIndex (uint32_t idx)
case eStopReasonInvalid:
case eStopReasonNone:
case eStopReasonTrace:
case eStopReasonExec:
case eStopReasonPlanComplete:
// There is no data for these stop reasons.
return 0;
@ -336,6 +338,14 @@ SBThread::GetStopDescription (char *dst, size_t dst_len)
}
break;
case eStopReasonExec:
{
char exc_desc[] = "exec";
stop_desc = exc_desc;
stop_desc_len = sizeof(exc_desc); // Include the NULL byte for size
}
break;
default:
break;
}

View File

@ -139,7 +139,8 @@ DynamicLoaderMacOSXDYLD::DynamicLoaderMacOSXDYLD (Process* process) :
m_break_id(LLDB_INVALID_BREAK_ID),
m_dyld_image_infos(),
m_dyld_image_infos_stop_id (UINT32_MAX),
m_mutex(Mutex::eMutexTypeRecursive)
m_mutex(Mutex::eMutexTypeRecursive),
m_process_image_addr_is_all_images_infos (false)
{
}
@ -179,6 +180,54 @@ DynamicLoaderMacOSXDYLD::DidLaunch ()
SetNotificationBreakpoint ();
}
bool
DynamicLoaderMacOSXDYLD::ProcessDidExec ()
{
if (m_process)
{
// If we are stopped after an exec, we will have only one thread...
if (m_process->GetThreadList().GetSize() == 1)
{
// We know if a process has exec'ed if our "m_dyld_all_image_infos_addr"
// value differs from the Process' image info address. When a process
// execs itself it might cause a change if ASLR is enabled.
const addr_t shlib_addr = m_process->GetImageInfoAddress ();
if (m_process_image_addr_is_all_images_infos == true && shlib_addr != m_dyld_all_image_infos_addr)
{
// The image info address from the process is the 'dyld_all_image_infos'
// address and it has changed.
return true;
}
if (m_process_image_addr_is_all_images_infos == false && shlib_addr == m_dyld.address)
{
// The image info address from the process is the mach_header
// address for dyld and it has changed.
return true;
}
// ASLR might be disabled and dyld could have ended up in the same
// location. We should try and detect if we are stopped at '_dyld_start'
ThreadSP thread_sp (m_process->GetThreadList().GetThreadAtIndex(0));
if (thread_sp)
{
lldb::StackFrameSP frame_sp (thread_sp->GetStackFrameAtIndex(0));
if (frame_sp)
{
const Symbol *symbol = frame_sp->GetSymbolContext(eSymbolContextSymbol).symbol;
if (symbol)
{
if (symbol->GetName() == ConstString("_dyld_start"))
return true;
}
}
}
}
}
return false;
}
//----------------------------------------------------------------------
// Clear out the state of this class.
@ -224,7 +273,6 @@ DynamicLoaderMacOSXDYLD::LocateDYLD()
// mach header for dyld, or it might point to the
// dyld_all_image_infos struct
const addr_t shlib_addr = m_process->GetImageInfoAddress ();
ByteOrder byte_order = m_process->GetTarget().GetArchitecture().GetByteOrder();
uint8_t buf[4];
DataExtractor data (buf, sizeof(buf), byte_order, 4);
@ -239,6 +287,7 @@ DynamicLoaderMacOSXDYLD::LocateDYLD()
case llvm::MachO::HeaderMagic64:
case llvm::MachO::HeaderMagic32Swapped:
case llvm::MachO::HeaderMagic64Swapped:
m_process_image_addr_is_all_images_infos = false;
return ReadDYLDInfoFromMemoryAndSetNotificationCallback(shlib_addr);
default:
@ -247,6 +296,7 @@ DynamicLoaderMacOSXDYLD::LocateDYLD()
}
// Maybe it points to the all image infos?
m_dyld_all_image_infos_addr = shlib_addr;
m_process_image_addr_is_all_images_infos = true;
}
if (m_dyld_all_image_infos_addr != LLDB_INVALID_ADDRESS)

View File

@ -62,6 +62,9 @@ public:
virtual void
DidLaunch ();
virtual bool
ProcessDidExec ();
virtual lldb::ThreadPlanSP
GetStepThroughTrampolinePlan (lldb_private::Thread &thread,
bool stop_others);
@ -372,6 +375,7 @@ protected:
uint32_t m_dyld_image_infos_stop_id; // The process stop ID that "m_dyld_image_infos" is valid for
mutable lldb_private::Mutex m_mutex;
lldb_private::Process::Notifications m_notification_callbacks;
bool m_process_image_addr_is_all_images_infos;
private:
DISALLOW_COPY_AND_ASSIGN (DynamicLoaderMacOSXDYLD);

View File

@ -16,6 +16,8 @@
#include "lldb/Breakpoint/Watchpoint.h"
#include "lldb/Core/ArchSpec.h"
#include "lldb/Core/StreamString.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Target/DynamicLoader.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
@ -300,7 +302,39 @@ StopInfoMachException::CreateStopReasonWithMachException
case 5: // EXC_SOFTWARE
if (exc_code == 0x10003) // EXC_SOFT_SIGNAL
{
if (exc_sub_code == 5)
{
// On MacOSX, a SIGTRAP can signify that a process has called
// exec, so we should check with our dynamic loader to verify.
ProcessSP process_sp (thread.GetProcess());
if (process_sp)
{
DynamicLoader *dynamic_loader = process_sp->GetDynamicLoader();
if (dynamic_loader && dynamic_loader->ProcessDidExec())
{
// The program was re-exec'ed
return StopInfo::CreateStopReasonWithExec (thread);
}
// if (!process_did_exec)
// {
// // We have a SIGTRAP, make sure we didn't exec by checking
// // for the PC being at "_dyld_start"...
// lldb::StackFrameSP frame_sp (thread.GetStackFrameAtIndex(0));
// if (frame_sp)
// {
// const Symbol *symbol = frame_sp->GetSymbolContext(eSymbolContextSymbol).symbol;
// if (symbol)
// {
// if (symbol->GetName() == ConstString("_dyld_start"))
// process_did_exec = true;
// }
// }
// }
}
}
return StopInfo::CreateStopReasonWithSignal (thread, exc_sub_code);
}
break;
case 6: // EXC_BREAKPOINT

View File

@ -1058,6 +1058,17 @@ ProcessGDBRemote::DidAttach ()
DidLaunchOrAttach ();
}
void
ProcessGDBRemote::DoDidExec ()
{
// The process exec'ed itself, figure out the dynamic loader, etc...
BuildDynamicRegisterInfo (true);
m_gdb_comm.ResetDiscoverableSettings();
DidLaunchOrAttach ();
}
Error
ProcessGDBRemote::WillResume ()
{

View File

@ -113,6 +113,9 @@ public:
virtual void
DidAttach ();
virtual void
DoDidExec ();
//------------------------------------------------------------------
// PluginInterface protocol
//------------------------------------------------------------------

View File

@ -5111,3 +5111,22 @@ Process::Flush ()
{
m_thread_list.Flush();
}
void
Process::DidExec ()
{
Target &target = GetTarget();
target.CleanupProcess ();
ModuleList unloaded_modules (target.GetImages());
target.ModulesDidUnload (unloaded_modules);
target.GetSectionLoadList().Clear();
m_dynamic_checkers_ap.reset();
m_abi_sp.reset();
m_os_ap.reset();
m_dyld_ap.reset();
m_image_tokens.clear();
m_allocated_memory_cache.Clear();
m_language_runtimes.clear();
DoDidExec();
CompleteAttach ();
}

View File

@ -147,6 +147,7 @@ StackFrameList::ResetCurrentInlinedDepth ()
{
case eStopReasonWatchpoint:
case eStopReasonException:
case eStopReasonExec:
case eStopReasonSignal:
// In all these cases we want to stop in the deepest most frame.
m_current_inlined_pc = curr_pc;

View File

@ -862,6 +862,49 @@ private:
ThreadPlanSP m_plan_sp;
ValueObjectSP m_return_valobj_sp;
};
class StopInfoExec : public StopInfo
{
public:
StopInfoExec (Thread &thread) :
StopInfo (thread, LLDB_INVALID_UID),
m_performed_action (false)
{
}
virtual
~StopInfoExec ()
{
}
virtual StopReason
GetStopReason () const
{
return eStopReasonExec;
}
virtual const char *
GetDescription ()
{
return "exec";
}
protected:
protected:
virtual void
PerformAction (Event *event_ptr)
{
// Only perform the action once
if (m_performed_action)
return;
m_performed_action = true;
m_thread.GetProcess()->DidExec();
}
bool m_performed_action;
};
} // namespace lldb_private
StopInfoSP
@ -906,6 +949,12 @@ StopInfo::CreateStopReasonWithException (Thread &thread, const char *description
return StopInfoSP (new StopInfoException (thread, description));
}
StopInfoSP
StopInfo::CreateStopReasonWithExec (Thread &thread)
{
return StopInfoSP (new StopInfoExec (thread));
}
ValueObjectSP
StopInfo::GetReturnValueObject(StopInfoSP &stop_info_sp)
{

View File

@ -129,6 +129,21 @@ Target::Dump (Stream *s, lldb::DescriptionLevel description_level)
}
}
void
Target::CleanupProcess ()
{
// Do any cleanup of the target we need to do between process instances.
// NB It is better to do this before destroying the process in case the
// clean up needs some help from the process.
m_breakpoint_list.ClearAllBreakpointSites();
m_internal_breakpoint_list.ClearAllBreakpointSites();
// Disable watchpoints just on the debugger side.
Mutex::Locker locker;
this->GetWatchpointList().GetListMutex(locker);
DisableAllWatchpoints(false);
ClearAllWatchpointHitCounts();
}
void
Target::DeleteCurrentProcess ()
{
@ -140,16 +155,8 @@ Target::DeleteCurrentProcess ()
m_process_sp->Finalize();
// Do any cleanup of the target we need to do between process instances.
// NB It is better to do this before destroying the process in case the
// clean up needs some help from the process.
m_breakpoint_list.ClearAllBreakpointSites();
m_internal_breakpoint_list.ClearAllBreakpointSites();
// Disable watchpoints just on the debugger side.
Mutex::Locker locker;
this->GetWatchpointList().GetListMutex(locker);
DisableAllWatchpoints(false);
ClearAllWatchpointHitCounts();
CleanupProcess ();
m_process_sp.reset();
}
}

View File

@ -1574,6 +1574,7 @@ Thread::StopReasonAsCString (lldb::StopReason reason)
case eStopReasonWatchpoint: return "watchpoint";
case eStopReasonSignal: return "signal";
case eStopReasonException: return "exception";
case eStopReasonExec: return "exec";
case eStopReasonPlanComplete: return "plan complete";
}

View File

@ -139,6 +139,14 @@ ThreadPlanBase::ShouldStop (Event *event_ptr)
m_thread.DiscardThreadPlans(false);
return true;
case eStopReasonExec:
// If we crashed, discard thread plans and stop. Don't force the discard, however,
// since on rerun the target may clean up this exception and continue normally from there.
if (log)
log->Printf("Base plan discarding thread plans for thread tid = 0x%4.4" PRIx64 " (exec.)", m_thread.GetID());
m_thread.DiscardThreadPlans(false);
return true;
case eStopReasonSignal:
if (stop_info_sp->ShouldStop(event_ptr))
{

View File

@ -335,6 +335,7 @@ ThreadPlanStepInRange::PlanExplainsStop ()
case eStopReasonWatchpoint:
case eStopReasonSignal:
case eStopReasonException:
case eStopReasonExec:
{
LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
if (log)

View File

@ -251,6 +251,7 @@ ThreadPlanStepOut::PlanExplainsStop ()
case eStopReasonWatchpoint:
case eStopReasonSignal:
case eStopReasonException:
case eStopReasonExec:
return false;
default:

View File

@ -319,6 +319,7 @@ ThreadPlanStepOverRange::PlanExplainsStop ()
case eStopReasonWatchpoint:
case eStopReasonSignal:
case eStopReasonException:
case eStopReasonExec:
default:
if (log)
log->PutCString ("ThreadPlanStepInRange got asked if it explains the stop for some reason other than step.");

View File

@ -291,6 +291,7 @@ ThreadPlanStepUntil::AnalyzeStop()
case eStopReasonWatchpoint:
case eStopReasonSignal:
case eStopReasonException:
case eStopReasonExec:
m_explains_stop = false;
break;
default:

View File

@ -806,6 +806,7 @@ Driver::UpdateSelectedThread ()
case eStopReasonWatchpoint:
case eStopReasonSignal:
case eStopReasonException:
case eStopReasonExec:
if (!other_thread.IsValid())
other_thread = thread;
break;