Index: include/lldb/Target/FileAction.h =================================================================== --- include/lldb/Target/FileAction.h +++ include/lldb/Target/FileAction.h @@ -56,6 +56,9 @@ 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 Index: source/Plugins/Platform/Linux/PlatformLinux.h =================================================================== --- source/Plugins/Platform/Linux/PlatformLinux.h +++ source/Plugins/Platform/Linux/PlatformLinux.h @@ -89,16 +89,19 @@ GetSoftwareBreakpointTrapOpcode (Target &target, BreakpointSite *bp_site) override; - lldb_private::Error - LaunchProcess (lldb_private::ProcessLaunchInfo &launch_info) override; + int32_t + GetResumeCountForLaunchInfo (ProcessLaunchInfo &launch_info) override; - lldb::ProcessSP - Attach(ProcessAttachInfo &attach_info, Debugger &debugger, - Target *target, Listener &listener, Error &error) 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 @@ lldb_private::NativeProcessProtocol::NativeDelegate &native_delegate, NativeProcessProtocolSP &process_sp) override; + static bool + UseLlgsForLocalDebugging (); + private: DISALLOW_COPY_AND_ASSIGN (PlatformLinux); }; Index: source/Plugins/Platform/Linux/PlatformLinux.cpp =================================================================== --- source/Plugins/Platform/Linux/PlatformLinux.cpp +++ source/Plugins/Platform/Linux/PlatformLinux.cpp @@ -21,16 +21,19 @@ // C++ Includes // Other libraries and framework includes // Project includes +#include "lldb/Core/Debugger.h" #include "lldb/Core/Error.h" -#include "lldb/Core/Debugger.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 @@ /// 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 @@ 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 @@ 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 @@ 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()) + int32_t resume_count = 0; + + // Always resume past the initial stop when we use eLaunchFlagDebug + if (launch_info.GetFlags ().Test (eLaunchFlagDebug)) { - if (log) - log->Printf ("PlatformLinux::%s() launching process as host", __FUNCTION__); + // Resume past the stop for the final exec into the true inferior. + ++resume_count; + } - 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); - } + // 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) { - 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"); - } + // These shells seem to re-exec themselves. Add another resume. + ++resume_count; } - return error; + + return resume_count; } -// Linux processes can not be launched by spawning and attaching. bool +PlatformLinux::UseLlgsForLocalDebugging () +{ + PlatformLinuxPropertiesSP properties_sp = GetGlobalProperties (); + assert (properties_sp && "global properties shared pointer is null"); + return properties_sp ? properties_sp->GetUseLlgsForLocal () : false; +} + +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(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) + 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; + } + + // 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 ()) { - TargetSP new_target_sp; - ArchSpec emptyArchSpec; + if (log) + log->Printf ("PlatformLinux::%s failed to create new target: %s", __FUNCTION__, error.AsCString ()); + return process_sp; + } - error = debugger.GetTargetList().CreateTarget (debugger, - NULL, - emptyArchSpec, - false, - m_remote_platform_sp, - new_target_sp); - target = new_target_sp.get(); + target = new_target_sp.get(); + if (!target) + { + error.SetErrorString ("CreateTarget() returned nullptr"); + if (log) + log->Printf ("PlatformLinux::%s failed: %s", __FUNCTION__, error.AsCString ()); + return process_sp; } - else - error.Clear(); + } + else + { + if (log) + log->Printf ("PlatformLinux::%s using provided target", __FUNCTION__); + } - if (target && error.Success()) + // 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) { - debugger.GetTargetList().SetSelectedTarget(target); + file_action->Dump (stream); + log->PutCString (stream.GetString().c_str ()); + stream.Clear(); + } + } - process_sp = target->CreateProcess (listener, - attach_info.GetProcessPluginName(), - NULL); + // 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 (process_sp) - error = process_sp->Attach (attach_info); + 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 (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 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 ( Index: source/Plugins/Platform/MacOSX/PlatformDarwin.h =================================================================== --- source/Plugins/Platform/MacOSX/PlatformDarwin.h +++ source/Plugins/Platform/MacOSX/PlatformDarwin.h @@ -65,16 +65,6 @@ 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); Index: source/Plugins/Platform/MacOSX/PlatformDarwin.cpp =================================================================== --- source/Plugins/Platform/MacOSX/PlatformDarwin.cpp +++ source/Plugins/Platform/MacOSX/PlatformDarwin.cpp @@ -672,76 +672,6 @@ 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) { Index: source/Plugins/Platform/POSIX/PlatformPOSIX.h =================================================================== --- source/Plugins/Platform/POSIX/PlatformPOSIX.h +++ source/Plugins/Platform/POSIX/PlatformPOSIX.h @@ -130,7 +130,17 @@ 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, lldb_private::Target *target, // Can be NULL, if NULL create a new target, else use existing one Index: source/Plugins/Platform/POSIX/PlatformPOSIX.cpp =================================================================== --- source/Plugins/Platform/POSIX/PlatformPOSIX.cpp +++ source/Plugins/Platform/POSIX/PlatformPOSIX.cpp @@ -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,7 +771,95 @@ 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 () : "" ); + } + + + 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, Target *target, // Can be NULL, if NULL create a new target, else use existing one Index: source/Plugins/Process/Linux/ProcessLinux.cpp =================================================================== --- source/Plugins/Process/Linux/ProcessLinux.cpp +++ source/Plugins/Process/Linux/ProcessLinux.cpp @@ -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 @@ 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); } Index: source/Target/FileAction.cpp =================================================================== --- source/Target/FileAction.cpp +++ source/Target/FileAction.cpp @@ -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 @@ } 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; + } +}