Enable local llgs debugging on Linux when the use-llgs-for-local setting is enabled.

See http://reviews.llvm.org/D5695 for details.

This change does the following:

Enable lldb-gdbserver (llgs) usage for local-process Linux debugging.
To turn on local llgs debugging support, which is disabled by default, enable this setting:

(lldb) settings set platform.plugin.linux.use-llgs-for-local true
Adds a stream-based Dump() function to FileAction.
Pushes some platform methods that Linux (and FreeBSD) will want to share with MacOSX from PlatformDarwin into PlatformPOSIX.

Reviewed by Greg Clayton.

llvm-svn: 219457
This commit is contained in:
Todd Fiala 2014-10-10 00:09:16 +00:00
parent 79b0fd7a48
commit 348fb385d5
9 changed files with 383 additions and 177 deletions

View File

@ -56,6 +56,9 @@ class FileAction
const char *GetPath() const;
void
Dump (Stream &stream) const;
protected:
Action m_action; // The action for this file
int m_fd; // An existing file descriptor

View File

@ -21,16 +21,19 @@
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "lldb/Core/Error.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Error.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleList.h"
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/State.h"
#include "lldb/Core/StreamString.h"
#include "lldb/Host/FileSpec.h"
#include "lldb/Host/HostInfo.h"
#include "lldb/Interpreter/OptionValueProperties.h"
#include "lldb/Interpreter/Property.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Process.h"
@ -47,18 +50,30 @@ static uint32_t g_initialize_count = 0;
/// Code to handle the PlatformLinux settings
//------------------------------------------------------------------
static PropertyDefinition
g_properties[] =
namespace
{
{ "use-llgs-for-local" , OptionValue::eTypeBoolean, true, false, NULL, NULL, "Control whether the platform uses llgs for local debug sessions." },
{ NULL , OptionValue::eTypeInvalid, false, 0 , NULL, NULL, NULL }
};
enum
{
ePropertyUseLlgsForLocal = 0,
};
enum {
ePropertyUseLlgsForLocal = 0,
};
const PropertyDefinition*
GetStaticPropertyDefinitions ()
{
static PropertyDefinition
g_properties[] =
{
{ "use-llgs-for-local" , OptionValue::eTypeBoolean, true, false, NULL, NULL, "Control whether the platform uses llgs for local debug sessions." },
{ NULL , OptionValue::eTypeInvalid, false, 0 , NULL, NULL, NULL }
};
// Allow environment variable to force using llgs-local.
if (getenv("PLATFORM_LINUX_FORCE_LLGS_LOCAL"))
g_properties[ePropertyUseLlgsForLocal].default_uint_value = true;
return g_properties;
}
}
class PlatformLinuxProperties : public Properties
{
@ -74,8 +89,8 @@ public:
PlatformLinuxProperties() :
Properties ()
{
m_collection_sp.reset (new OptionValueProperties(GetSettingName()));
m_collection_sp->Initialize(g_properties);
m_collection_sp.reset (new OptionValueProperties(GetSettingName ()));
m_collection_sp->Initialize (GetStaticPropertyDefinitions ());
}
virtual
@ -87,7 +102,7 @@ public:
GetUseLlgsForLocal() const
{
const uint32_t idx = ePropertyUseLlgsForLocal;
return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0);
return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, GetStaticPropertyDefinitions()[idx].default_uint_value != 0);
}
};
@ -526,115 +541,249 @@ PlatformLinux::GetSoftwareBreakpointTrapOpcode (Target &target,
return 0;
}
Error
PlatformLinux::LaunchProcess (ProcessLaunchInfo &launch_info)
int32_t
PlatformLinux::GetResumeCountForLaunchInfo (ProcessLaunchInfo &launch_info)
{
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM));
Error error;
if (IsHost())
{
if (log)
log->Printf ("PlatformLinux::%s() launching process as host", __FUNCTION__);
int32_t resume_count = 0;
if (launch_info.GetFlags().Test (eLaunchFlagLaunchInShell))
{
const bool is_localhost = true;
const bool will_debug = launch_info.GetFlags().Test(eLaunchFlagDebug);
const bool first_arg_is_full_shell_command = false;
uint32_t num_resumes = GetResumeCountForLaunchInfo (launch_info);
if (!launch_info.ConvertArgumentsForLaunchingInShell (error,
is_localhost,
will_debug,
first_arg_is_full_shell_command,
num_resumes))
return error;
}
error = Platform::LaunchProcess (launch_info);
}
else
// Always resume past the initial stop when we use eLaunchFlagDebug
if (launch_info.GetFlags ().Test (eLaunchFlagDebug))
{
if (m_remote_platform_sp)
{
if (log)
log->Printf ("PlatformLinux::%s() attempting to launch remote process", __FUNCTION__);
error = m_remote_platform_sp->LaunchProcess (launch_info);
}
else
{
if (log)
log->Printf ("PlatformLinux::%s() attempted to launch process but is not the host and no remote platform set", __FUNCTION__);
error.SetErrorString ("the platform is not currently connected");
}
// Resume past the stop for the final exec into the true inferior.
++resume_count;
}
return error;
// If we're not launching a shell, we're done.
const char *shell = launch_info.GetShell();
if (shell == NULL)
return resume_count;
// We're in a shell, so for sure we have to resume past the shell exec.
++resume_count;
// Figure out what shell we're planning on using.
const char *shell_name = strrchr (shell, '/');
if (shell_name == NULL)
shell_name = shell;
else
shell_name++;
if (strcmp (shell_name, "csh") == 0
|| strcmp (shell_name, "tcsh") == 0
|| strcmp (shell_name, "zsh") == 0
|| strcmp (shell_name, "sh") == 0)
{
// These shells seem to re-exec themselves. Add another resume.
++resume_count;
}
return resume_count;
}
bool
PlatformLinux::UseLlgsForLocalDebugging ()
{
PlatformLinuxPropertiesSP properties_sp = GetGlobalProperties ();
assert (properties_sp && "global properties shared pointer is null");
return properties_sp ? properties_sp->GetUseLlgsForLocal () : false;
}
// Linux processes can not be launched by spawning and attaching.
bool
PlatformLinux::CanDebugProcess ()
{
// If we're the host, launch via normal host setup.
if (IsHost ())
return false;
// If we're connected, we can debug.
return IsConnected ();
{
// The platform only does local debugging (i.e. uses llgs) when the setting indicates we do that.
// Otherwise, we'll use ProcessLinux/ProcessPOSIX to handle with ProcessMonitor.
return UseLlgsForLocalDebugging ();
}
else
{
// If we're connected, we can debug.
return IsConnected ();
}
}
// For local debugging, Linux will override the debug logic to use llgs-launch rather than
// lldb-launch, llgs-attach. This differs from current lldb-launch, debugserver-attach
// approach on MacOSX.
lldb::ProcessSP
PlatformLinux::Attach(ProcessAttachInfo &attach_info,
Debugger &debugger,
Target *target,
Listener &listener,
Error &error)
PlatformLinux::DebugProcess (ProcessLaunchInfo &launch_info,
Debugger &debugger,
Target *target, // Can be NULL, if NULL create a new target, else use existing one
Listener &listener,
Error &error)
{
lldb::ProcessSP process_sp;
if (IsHost())
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM));
if (log)
log->Printf ("PlatformLinux::%s entered (target %p)", __FUNCTION__, static_cast<void*>(target));
// If we're a remote host, use standard behavior from parent class.
if (!IsHost ())
return PlatformPOSIX::DebugProcess (launch_info, debugger, target, listener, error);
//
// For local debugging, we'll insist on having ProcessGDBRemote create the process.
//
ProcessSP process_sp;
// Ensure we're using llgs for local debugging.
if (!UseLlgsForLocalDebugging ())
{
if (target == NULL)
{
TargetSP new_target_sp;
ArchSpec emptyArchSpec;
assert (false && "we're trying to debug a local process but platform.plugin.linux.use-llgs-for-local is false, should never get here");
error.SetErrorString ("attempted to start gdb-remote-based debugging for local process but platform.plugin.linux.use-llgs-for-local is false");
return process_sp;
}
error = debugger.GetTargetList().CreateTarget (debugger,
NULL,
emptyArchSpec,
false,
m_remote_platform_sp,
new_target_sp);
target = new_target_sp.get();
// Make sure we stop at the entry point
launch_info.GetFlags ().Set (eLaunchFlagDebug);
// We always launch the process we are going to debug in a separate process
// group, since then we can handle ^C interrupts ourselves w/o having to worry
// about the target getting them as well.
launch_info.SetLaunchInSeparateProcessGroup(true);
// Ensure we have a target.
if (target == nullptr)
{
if (log)
log->Printf ("PlatformLinux::%s creating new target", __FUNCTION__);
TargetSP new_target_sp;
error = debugger.GetTargetList().CreateTarget (debugger,
nullptr,
nullptr,
false,
nullptr,
new_target_sp);
if (error.Fail ())
{
if (log)
log->Printf ("PlatformLinux::%s failed to create new target: %s", __FUNCTION__, error.AsCString ());
return process_sp;
}
else
error.Clear();
if (target && error.Success())
target = new_target_sp.get();
if (!target)
{
debugger.GetTargetList().SetSelectedTarget(target);
process_sp = target->CreateProcess (listener,
attach_info.GetProcessPluginName(),
NULL);
if (process_sp)
error = process_sp->Attach (attach_info);
error.SetErrorString ("CreateTarget() returned nullptr");
if (log)
log->Printf ("PlatformLinux::%s failed: %s", __FUNCTION__, error.AsCString ());
return process_sp;
}
}
else
{
if (m_remote_platform_sp)
process_sp = m_remote_platform_sp->Attach (attach_info, debugger, target, listener, error);
else
error.SetErrorString ("the platform is not currently connected");
if (log)
log->Printf ("PlatformLinux::%s using provided target", __FUNCTION__);
}
// Mark target as currently selected target.
debugger.GetTargetList().SetSelectedTarget(target);
// Now create the gdb-remote process.
if (log)
log->Printf ("PlatformLinux::%s having target create process with gdb-remote plugin", __FUNCTION__);
process_sp = target->CreateProcess (listener, "gdb-remote", nullptr);
if (!process_sp)
{
error.SetErrorString ("CreateProcess() failed for gdb-remote process");
if (log)
log->Printf ("PlatformLinux::%s failed: %s", __FUNCTION__, error.AsCString ());
return process_sp;
}
else
{
if (log)
log->Printf ("PlatformLinux::%s successfully created process", __FUNCTION__);
}
// Set the unix signals properly.
process_sp->SetUnixSignals (Host::GetUnixSignals ());
// Adjust launch for a hijacker.
ListenerSP listener_sp;
if (!launch_info.GetHijackListener ())
{
if (log)
log->Printf ("PlatformLinux::%s setting up hijacker", __FUNCTION__);
listener_sp.reset (new Listener("lldb.PlatformLinux.DebugProcess.hijack"));
launch_info.SetHijackListener (listener_sp);
process_sp->HijackProcessEvents (listener_sp.get ());
}
// Log file actions.
if (log)
{
log->Printf ("PlatformLinux::%s launching process with the following file actions:", __FUNCTION__);
StreamString stream;
size_t i = 0;
const FileAction *file_action;
while ((file_action = launch_info.GetFileActionAtIndex (i++)) != nullptr)
{
file_action->Dump (stream);
log->PutCString (stream.GetString().c_str ());
stream.Clear();
}
}
// Do the launch.
error = process_sp->Launch(launch_info);
if (error.Success ())
{
// Handle the hijacking of process events.
if (listener_sp)
{
const StateType state = process_sp->WaitForProcessToStop (NULL, NULL, false, listener_sp.get());
process_sp->RestoreProcessEvents();
if (state == eStateStopped)
{
if (log)
log->Printf ("PlatformLinux::%s pid %" PRIu64 " state %s\n",
__FUNCTION__, process_sp->GetID (), StateAsCString (state));
}
else
{
if (log)
log->Printf ("PlatformLinux::%s pid %" PRIu64 " state is not stopped - %s\n",
__FUNCTION__, process_sp->GetID (), StateAsCString (state));
}
}
// Hook up process PTY if we have one (which we should for local debugging with llgs).
int pty_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor();
if (pty_fd != lldb_utility::PseudoTerminal::invalid_fd)
{
process_sp->SetSTDIOFileDescriptor(pty_fd);
if (log)
log->Printf ("PlatformLinux::%s pid %" PRIu64 " hooked up STDIO pty to process", __FUNCTION__, process_sp->GetID ());
}
else
{
if (log)
log->Printf ("PlatformLinux::%s pid %" PRIu64 " not using process STDIO pty", __FUNCTION__, process_sp->GetID ());
}
}
else
{
if (log)
log->Printf ("PlatformLinux::%s process launch failed: %s", __FUNCTION__, error.AsCString ());
// FIXME figure out appropriate cleanup here. Do we delete the target? Do we delete the process? Does our caller do that?
}
return process_sp;
}
void
PlatformLinux::CalculateTrapHandlerSymbolNames ()
{
{
m_trap_handlers.push_back (ConstString ("_sigtramp"));
}
}
Error
PlatformLinux::LaunchNativeProcess (

View File

@ -89,16 +89,19 @@ namespace lldb_private {
GetSoftwareBreakpointTrapOpcode (Target &target,
BreakpointSite *bp_site) override;
lldb_private::Error
LaunchProcess (lldb_private::ProcessLaunchInfo &launch_info) override;
lldb::ProcessSP
Attach(ProcessAttachInfo &attach_info, Debugger &debugger,
Target *target, Listener &listener, Error &error) override;
int32_t
GetResumeCountForLaunchInfo (ProcessLaunchInfo &launch_info) override;
bool
CanDebugProcess () override;
lldb::ProcessSP
DebugProcess (ProcessLaunchInfo &launch_info,
Debugger &debugger,
Target *target,
Listener &listener,
Error &error) override;
void
CalculateTrapHandlerSymbolNames () override;
@ -113,6 +116,9 @@ namespace lldb_private {
lldb_private::NativeProcessProtocol::NativeDelegate &native_delegate,
NativeProcessProtocolSP &process_sp) override;
static bool
UseLlgsForLocalDebugging ();
private:
DISALLOW_COPY_AND_ASSIGN (PlatformLinux);
};

View File

@ -672,76 +672,6 @@ PlatformDarwin::FindProcesses (const ProcessInstanceInfoMatch &match_info,
return match_count;
}
Error
PlatformDarwin::LaunchProcess (ProcessLaunchInfo &launch_info)
{
Error error;
if (IsHost())
{
error = Platform::LaunchProcess (launch_info);
}
else
{
if (m_remote_platform_sp)
error = m_remote_platform_sp->LaunchProcess (launch_info);
else
error.SetErrorString ("the platform is not currently connected");
}
return error;
}
lldb::ProcessSP
PlatformDarwin::Attach (ProcessAttachInfo &attach_info,
Debugger &debugger,
Target *target,
Listener &listener,
Error &error)
{
lldb::ProcessSP process_sp;
if (IsHost())
{
if (target == NULL)
{
TargetSP new_target_sp;
error = debugger.GetTargetList().CreateTarget (debugger,
NULL,
NULL,
false,
NULL,
new_target_sp);
target = new_target_sp.get();
}
else
error.Clear();
if (target && error.Success())
{
debugger.GetTargetList().SetSelectedTarget(target);
process_sp = target->CreateProcess (listener, attach_info.GetProcessPluginName(), NULL);
if (process_sp)
{
ListenerSP listener_sp (new Listener("lldb.PlatformDarwin.attach.hijack"));
attach_info.SetHijackListener(listener_sp);
process_sp->HijackProcessEvents(listener_sp.get());
error = process_sp->Attach (attach_info);
}
}
}
else
{
if (m_remote_platform_sp)
process_sp = m_remote_platform_sp->Attach (attach_info, debugger, target, listener, error);
else
error.SetErrorString ("the platform is not currently connected");
}
return process_sp;
}
bool
PlatformDarwin::ModuleIsExcludedForNonModuleSpecificSearches (lldb_private::Target &target, const lldb::ModuleSP &module_sp)
{

View File

@ -65,16 +65,6 @@ public:
FindProcesses (const lldb_private::ProcessInstanceInfoMatch &match_info,
lldb_private::ProcessInstanceInfoList &process_infos);
virtual lldb_private::Error
LaunchProcess (lldb_private::ProcessLaunchInfo &launch_info);
virtual lldb::ProcessSP
Attach (lldb_private::ProcessAttachInfo &attach_info,
lldb_private::Debugger &debugger,
lldb_private::Target *target, // Can be NULL, if NULL create a new target, else use existing one
lldb_private::Listener &listener,
lldb_private::Error &error);
virtual bool
ModuleIsExcludedForNonModuleSpecificSearches (lldb_private::Target &target, const lldb::ModuleSP &module_sp);

View File

@ -15,7 +15,9 @@
// Project includes
#include "lldb/Core/DataBufferHeap.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/StreamString.h"
#include "lldb/Host/File.h"
#include "lldb/Host/FileCache.h"
@ -769,6 +771,94 @@ PlatformPOSIX::DisconnectRemote ()
return error;
}
Error
PlatformPOSIX::LaunchProcess (ProcessLaunchInfo &launch_info)
{
Error error;
if (IsHost())
{
error = Platform::LaunchProcess (launch_info);
}
else
{
if (m_remote_platform_sp)
error = m_remote_platform_sp->LaunchProcess (launch_info);
else
error.SetErrorString ("the platform is not currently connected");
}
return error;
}
lldb::ProcessSP
PlatformPOSIX::Attach (ProcessAttachInfo &attach_info,
Debugger &debugger,
Target *target,
Listener &listener,
Error &error)
{
lldb::ProcessSP process_sp;
Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
if (IsHost())
{
if (target == NULL)
{
TargetSP new_target_sp;
error = debugger.GetTargetList().CreateTarget (debugger,
NULL,
NULL,
false,
NULL,
new_target_sp);
target = new_target_sp.get();
if (log)
log->Printf ("PlatformPOSIX::%s created new target", __FUNCTION__);
}
else
{
error.Clear();
if (log)
log->Printf ("PlatformPOSIX::%s target already existed, setting target", __FUNCTION__);
}
if (target && error.Success())
{
debugger.GetTargetList().SetSelectedTarget(target);
if (log)
{
ModuleSP exe_module_sp = target->GetExecutableModule ();
log->Printf ("PlatformPOSIX::%s set selected target to %p %s", __FUNCTION__,
target,
exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str () : "<null>" );
}
process_sp = target->CreateProcess (listener, attach_info.GetProcessPluginName(), NULL);
if (process_sp)
{
// Set UnixSignals appropriately.
process_sp->SetUnixSignals (Host::GetUnixSignals ());
ListenerSP listener_sp (new Listener("lldb.PlatformPOSIX.attach.hijack"));
attach_info.SetHijackListener(listener_sp);
process_sp->HijackProcessEvents(listener_sp.get());
error = process_sp->Attach (attach_info);
}
}
}
else
{
if (m_remote_platform_sp)
process_sp = m_remote_platform_sp->Attach (attach_info, debugger, target, listener, error);
else
error.SetErrorString ("the platform is not currently connected");
}
return process_sp;
}
lldb::ProcessSP
PlatformPOSIX::DebugProcess (ProcessLaunchInfo &launch_info,
Debugger &debugger,

View File

@ -130,6 +130,16 @@ public:
virtual lldb_private::Error
Unlink (const char *path);
lldb_private::Error
LaunchProcess (lldb_private::ProcessLaunchInfo &launch_info) override;
lldb::ProcessSP
Attach (lldb_private::ProcessAttachInfo &attach_info,
lldb_private::Debugger &debugger,
lldb_private::Target *target, // Can be NULL, if NULL create a new target, else use existing one
lldb_private::Listener &listener,
lldb_private::Error &error) override;
lldb::ProcessSP
DebugProcess (lldb_private::ProcessLaunchInfo &launch_info,
lldb_private::Debugger &debugger,

View File

@ -20,6 +20,7 @@
#include "lldb/Target/Target.h"
#include "ProcessLinux.h"
#include "Plugins/Platform/Linux/PlatformLinux.h"
#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
#include "Plugins/Process/Utility/InferiorCallPOSIX.h"
#include "Plugins/Process/Utility/LinuxSignals.h"
@ -229,6 +230,11 @@ ProcessLinux::CanDebug(Target &target, bool plugin_specified_by_name)
if (m_core_file)
return false;
// If we're using llgs for local debugging, we must not say that this process
// is used for debugging.
if (PlatformLinux::UseLlgsForLocalDebugging ())
return false;
return ProcessPOSIX::CanDebug(target, plugin_specified_by_name);
}

View File

@ -13,6 +13,7 @@
#include "lldb/Host/Windows/win32.h" // For O_NOCTTY
#endif
#include "lldb/Core/Stream.h"
#include "lldb/Target/FileAction.h"
using namespace lldb_private;
@ -93,3 +94,24 @@ FileAction::Duplicate(int fd, int dup_fd)
}
return m_fd >= 0;
}
void
FileAction::Dump(Stream &stream) const
{
stream.PutCString("file action: ");
switch (m_action)
{
case eFileActionClose:
stream.Printf("close fd %d", m_fd);
break;
case eFileActionDuplicate:
stream.Printf("duplicate fd %d to %d", m_fd, m_arg);
break;
case eFileActionNone:
stream.PutCString("no action");
break;
case eFileActionOpen:
stream.Printf("open fd %d with '%s', OFLAGS = 0x%x", m_fd, m_path.c_str(), m_arg);
break;
}
}