Added a test case that verifies that LLDB can debug across a process exec'ing itself into a new program. This currently is only enabled for Darwin since we exec from 64 bit to 32 bit and vice versa for 'x86_64' targets.
This can easily be adapted for linux and other platforms, but I didn't want to break any buildbots by assuming it will work. llvm-svn: 182428
This commit is contained in:
parent
4df05caadb
commit
8cda7f0830
|
@ -1005,15 +1005,6 @@ ProcessGDBRemote::DidAttach ()
|
|||
DidLaunchOrAttach ();
|
||||
}
|
||||
|
||||
void
|
||||
ProcessGDBRemote::DoDidExec ()
|
||||
{
|
||||
// The process exec'ed itself, figure out the dynamic loader, etc...
|
||||
BuildDynamicRegisterInfo (true);
|
||||
m_gdb_comm.ResetDiscoverableSettings();
|
||||
}
|
||||
|
||||
|
||||
|
||||
Error
|
||||
ProcessGDBRemote::WillResume ()
|
||||
|
@ -1354,15 +1345,8 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet)
|
|||
// register info before we lookup and threads and populate the expedited
|
||||
// register values so we need to know this right away so we can cleanup
|
||||
// and update our registers.
|
||||
const bool did_exec = stop_packet.GetStringRef().find(";reason:exec;") != std::string::npos;
|
||||
|
||||
if (did_exec)
|
||||
{
|
||||
m_thread_list_real.Clear();
|
||||
m_thread_list.Clear();
|
||||
}
|
||||
|
||||
if (GetStopID() == 0 || did_exec)
|
||||
const uint32_t stop_id = GetStopID();
|
||||
if (stop_id == 0)
|
||||
{
|
||||
// Our first stop, make sure we have a process ID, and also make
|
||||
// sure we know about our registers
|
||||
|
@ -1519,6 +1503,7 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet)
|
|||
else
|
||||
{
|
||||
bool handled = false;
|
||||
bool did_exec = false;
|
||||
if (!reason.empty())
|
||||
{
|
||||
if (reason.compare("trace") == 0)
|
||||
|
@ -1566,6 +1551,7 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet)
|
|||
}
|
||||
else if (reason.compare("exec") == 0)
|
||||
{
|
||||
did_exec = true;
|
||||
thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithExec(*thread_sp));
|
||||
handled = true;
|
||||
}
|
||||
|
@ -1898,6 +1884,26 @@ ProcessGDBRemote::DoDestroy ()
|
|||
return error;
|
||||
}
|
||||
|
||||
void
|
||||
ProcessGDBRemote::SetLastStopPacket (const StringExtractorGDBRemote &response)
|
||||
{
|
||||
lldb_private::Mutex::Locker locker (m_last_stop_packet_mutex);
|
||||
const bool did_exec = response.GetStringRef().find(";reason:exec;") != std::string::npos;
|
||||
if (did_exec)
|
||||
{
|
||||
Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
|
||||
if (log)
|
||||
log->Printf ("ProcessGDBRemote::SetLastStopPacket () - detected exec");
|
||||
|
||||
m_thread_list_real.Clear();
|
||||
m_thread_list.Clear();
|
||||
BuildDynamicRegisterInfo (true);
|
||||
m_gdb_comm.ResetDiscoverableSettings();
|
||||
}
|
||||
m_last_stop_packet = response;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Process Queries
|
||||
//------------------------------------------------------------------
|
||||
|
|
|
@ -114,9 +114,6 @@ public:
|
|||
virtual void
|
||||
DidAttach ();
|
||||
|
||||
virtual void
|
||||
DoDidExec ();
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// PluginInterface protocol
|
||||
//------------------------------------------------------------------
|
||||
|
@ -289,11 +286,7 @@ protected:
|
|||
BuildDynamicRegisterInfo (bool force);
|
||||
|
||||
void
|
||||
SetLastStopPacket (const StringExtractorGDBRemote &response)
|
||||
{
|
||||
lldb_private::Mutex::Locker locker (m_last_stop_packet_mutex);
|
||||
m_last_stop_packet = response;
|
||||
}
|
||||
SetLastStopPacket (const StringExtractorGDBRemote &response);
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Broadcaster event bits definitions.
|
||||
|
|
|
@ -5588,8 +5588,6 @@ Process::DidExec ()
|
|||
m_allocated_memory_cache.Clear();
|
||||
m_language_runtimes.clear();
|
||||
m_thread_list.DiscardThreadPlans();
|
||||
m_thread_list.Clear();
|
||||
m_thread_list_real.Clear();
|
||||
m_memory_cache.Clear(true);
|
||||
DoDidExec();
|
||||
CompleteAttach ();
|
||||
|
|
|
@ -442,6 +442,9 @@ Thread::SetStopInfo (const lldb::StopInfoSP &stop_info_sp)
|
|||
m_stop_info_stop_id = process_sp->GetStopID();
|
||||
else
|
||||
m_stop_info_stop_id = UINT32_MAX;
|
||||
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
|
||||
if (log)
|
||||
log->Printf("%p: tid = 0x%llx: stop info = %s (stop_id = %u)\n", this, GetID(), stop_info_sp ? stop_info_sp->GetDescription() : "<NULL>", m_stop_info_stop_id);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -73,6 +73,12 @@ public:
|
|||
return m_packet;
|
||||
}
|
||||
|
||||
const std::string &
|
||||
GetStringRef () const
|
||||
{
|
||||
return m_packet;
|
||||
}
|
||||
|
||||
bool
|
||||
Empty()
|
||||
{
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
LEVEL = ../../make
|
||||
|
||||
CXX_SOURCES := main.cpp
|
||||
|
||||
include $(LEVEL)/Makefile.rules
|
|
@ -0,0 +1,121 @@
|
|||
"""
|
||||
Test some lldb command abbreviations.
|
||||
"""
|
||||
import commands
|
||||
import lldb
|
||||
import os
|
||||
import time
|
||||
import unittest2
|
||||
from lldbtest import *
|
||||
import lldbutil
|
||||
|
||||
def execute_command (command):
|
||||
print '%% %s' % (command)
|
||||
(exit_status, output) = commands.getstatusoutput (command)
|
||||
if output:
|
||||
print output
|
||||
print 'status = %u' % (exit_status)
|
||||
return exit_status
|
||||
|
||||
class ExecTestCase(TestBase):
|
||||
|
||||
mydir = os.path.join("functionalities", "exec")
|
||||
|
||||
|
||||
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
|
||||
@dsym_test
|
||||
def test_with_dsym (self):
|
||||
if self.getArchitecture() == 'x86_64':
|
||||
source = os.path.join (os.getcwd(), "main.cpp")
|
||||
o_file = os.path.join (os.getcwd(), "main.o")
|
||||
execute_command ("'%s' -g -O0 -arch i386 -arch x86_64 '%s' -c -o '%s'" % (os.environ["CC"], source, o_file))
|
||||
execute_command ("'%s' -g -O0 -arch i386 -arch x86_64 '%s'" % (os.environ["CC"], o_file))
|
||||
else:
|
||||
self.buildDsym()
|
||||
self.do_test ()
|
||||
|
||||
|
||||
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
|
||||
@dwarf_test
|
||||
def test_with_dwarf (self):
|
||||
if self.getArchitecture() == 'x86_64':
|
||||
source = os.path.join (os.getcwd(), "main.cpp")
|
||||
o_file = os.path.join (os.getcwd(), "main.o")
|
||||
dsym_path = os.path.join (os.getcwd(), "a.out.dSYM")
|
||||
execute_command ("'%s' -g -O0 -arch i386 -arch x86_64 '%s' -c -o '%s'" % (os.environ["CC"], source, o_file))
|
||||
execute_command ("'%s' -g -O0 -arch i386 -arch x86_64 '%s'" % (os.environ["CC"], o_file))
|
||||
execute_command ("rm -rf '%s'" % (dsym_path))
|
||||
else:
|
||||
self.buildDwarf()
|
||||
self.do_test ()
|
||||
|
||||
def do_test (self):
|
||||
exe = os.path.join (os.getcwd(), "a.out")
|
||||
|
||||
# Create the target
|
||||
target = self.dbg.CreateTarget(exe)
|
||||
|
||||
print target
|
||||
# Create any breakpoints we need
|
||||
breakpoint = target.BreakpointCreateBySourceRegex ('Set breakpoint 1 here', lldb.SBFileSpec ("main.cpp", False))
|
||||
print breakpoint
|
||||
self.assertTrue(breakpoint, VALID_BREAKPOINT)
|
||||
|
||||
# Launch the process
|
||||
process = target.LaunchSimple(None, None, os.getcwd())
|
||||
self.assertTrue(process, PROCESS_IS_VALID)
|
||||
|
||||
for i in range(6):
|
||||
# The stop reason of the thread should be breakpoint.
|
||||
self.assertTrue(process.GetState() == lldb.eStateStopped,
|
||||
STOPPED_DUE_TO_BREAKPOINT)
|
||||
|
||||
thread = process.GetThreadAtIndex (0)
|
||||
|
||||
self.assertTrue (thread.IsValid(),
|
||||
"Process stopped at 'main' should have a valid thread");
|
||||
|
||||
stop_reason = thread.GetStopReason()
|
||||
|
||||
print 'stop_reason = %u' % (stop_reason)
|
||||
|
||||
print thread
|
||||
for frame in thread:
|
||||
print frame
|
||||
|
||||
self.assertTrue (stop_reason == lldb.eStopReasonBreakpoint,
|
||||
"Thread in process stopped in 'main' should have a stop reason of eStopReasonBreakpoint");
|
||||
|
||||
# Run and we should stop due to exec
|
||||
process.Continue()
|
||||
|
||||
print process
|
||||
self.assertTrue(process.GetState() == lldb.eStateStopped,
|
||||
"Process should be stopped at __dyld_start")
|
||||
|
||||
thread = process.GetThreadAtIndex (0)
|
||||
|
||||
self.assertTrue (thread.IsValid(),
|
||||
"Process stopped at exec should have a valid thread");
|
||||
|
||||
print thread
|
||||
for frame in thread:
|
||||
print frame
|
||||
|
||||
stop_reason = thread.GetStopReason()
|
||||
|
||||
print 'stop_reason = %u' % (stop_reason)
|
||||
|
||||
self.assertTrue (stop_reason == lldb.eStopReasonExec,
|
||||
"Thread in process stopped on exec should have a stop reason of eStopReasonExec");
|
||||
|
||||
# Run and we should stop at breakpoint in main after exec
|
||||
process.Continue()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import atexit
|
||||
lldb.SBDebugger.Initialize()
|
||||
atexit.register(lambda: lldb.SBDebugger.Terminate())
|
||||
unittest2.main()
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
#include <errno.h>
|
||||
#include <mach/mach.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <spawn.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void
|
||||
exit_with_errno (int err, const char *prefix)
|
||||
{
|
||||
if (err)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"%s%s",
|
||||
prefix ? prefix : "",
|
||||
strerror(err));
|
||||
exit (err);
|
||||
}
|
||||
}
|
||||
|
||||
static pid_t
|
||||
spawn_process (const char **argv,
|
||||
const char **envp,
|
||||
cpu_type_t cpu_type,
|
||||
int &err)
|
||||
{
|
||||
pid_t pid = 0;
|
||||
|
||||
const posix_spawn_file_actions_t *file_actions = NULL;
|
||||
posix_spawnattr_t attr;
|
||||
err = posix_spawnattr_init (&attr);
|
||||
if (err)
|
||||
return pid;
|
||||
|
||||
short flags = POSIX_SPAWN_SETEXEC | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
|
||||
err = posix_spawnattr_setflags (&attr, flags);
|
||||
if (err == 0)
|
||||
{
|
||||
// Use the default signal masks
|
||||
sigset_t no_signals;
|
||||
sigset_t all_signals;
|
||||
sigemptyset (&no_signals);
|
||||
sigfillset (&all_signals);
|
||||
posix_spawnattr_setsigmask(&attr, &no_signals);
|
||||
posix_spawnattr_setsigdefault(&attr, &all_signals);
|
||||
|
||||
if (cpu_type != 0)
|
||||
{
|
||||
size_t ocount = 0;
|
||||
err = posix_spawnattr_setbinpref_np (&attr, 1, &cpu_type, &ocount);
|
||||
}
|
||||
|
||||
if (err == 0)
|
||||
{
|
||||
err = posix_spawn (&pid,
|
||||
argv[0],
|
||||
file_actions,
|
||||
&attr,
|
||||
(char * const *)argv,
|
||||
(char * const *)envp);
|
||||
}
|
||||
|
||||
posix_spawnattr_destroy(&attr);
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char const **argv)
|
||||
{
|
||||
printf ("pid %i: Pointer size is %zu.\n", getpid(), sizeof(void *));
|
||||
int err = 0; // Set breakpoint 1 here
|
||||
#if defined (__x86_64__)
|
||||
if (sizeof(void *) == 8)
|
||||
{
|
||||
spawn_process (argv, NULL, CPU_TYPE_I386, err);
|
||||
if (err)
|
||||
exit_with_errno (err, "posix_spawn i386 error");
|
||||
}
|
||||
else
|
||||
{
|
||||
spawn_process (argv, NULL, CPU_TYPE_X86_64, err);
|
||||
if (err)
|
||||
exit_with_errno (err, "posix_spawn x86_64 error");
|
||||
}
|
||||
#else
|
||||
spawn_process (argv, NULL, 0, err);
|
||||
if (err)
|
||||
exit_with_errno (err, "posix_spawn x86_64 error");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue