Files
clang-p2996/lldb/source/Plugins/Platform/Linux/PlatformLinux.cpp
Jim Ingham 583bbb1dd4 Change over the broadcaster/listener process to hold shared or weak pointers
to each other.  This should remove some infrequent teardown crashes when the
listener is not the debugger's listener.

Processes now need to take a ListenerSP, not a Listener&.

This required changing over the Process plugin class constructors to take a ListenerSP, instead
of a Listener&.   Other than that there should be no functional change.
 
<rdar://problem/24580184> CrashTracer: [USER] Xcode at …ework: lldb_private::Listener::BroadcasterWillDestruct + 39

llvm-svn: 262863
2016-03-07 21:50:25 +00:00

777 lines
25 KiB
C++

//===-- PlatformLinux.cpp ---------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "PlatformLinux.h"
#include "lldb/Host/Config.h"
// C Includes
#include <stdio.h>
#ifndef LLDB_DISABLE_POSIX
#include <sys/utsname.h>
#endif
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "lldb/Breakpoint/BreakpointLocation.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"
// Define these constants from Linux mman.h for use when targeting
// remote linux systems even when host has different values.
#define MAP_PRIVATE 2
#define MAP_ANON 0x20
using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::platform_linux;
static uint32_t g_initialize_count = 0;
//------------------------------------------------------------------
/// Code to handle the PlatformLinux settings
//------------------------------------------------------------------
namespace
{
class PlatformLinuxProperties : public Properties
{
public:
PlatformLinuxProperties();
~PlatformLinuxProperties() override = default;
static ConstString&
GetSettingName ();
private:
static const PropertyDefinition*
GetStaticPropertyDefinitions();
};
typedef std::shared_ptr<PlatformLinuxProperties> PlatformLinuxPropertiesSP;
} // anonymous namespace
PlatformLinuxProperties::PlatformLinuxProperties() :
Properties ()
{
m_collection_sp.reset (new OptionValueProperties(GetSettingName ()));
m_collection_sp->Initialize (GetStaticPropertyDefinitions ());
}
ConstString&
PlatformLinuxProperties::GetSettingName ()
{
static ConstString g_setting_name("linux");
return g_setting_name;
}
const PropertyDefinition*
PlatformLinuxProperties::GetStaticPropertyDefinitions()
{
static PropertyDefinition
g_properties[] =
{
{ NULL , OptionValue::eTypeInvalid, false, 0 , NULL, NULL, NULL }
};
return g_properties;
}
static const PlatformLinuxPropertiesSP &
GetGlobalProperties()
{
static PlatformLinuxPropertiesSP g_settings_sp;
if (!g_settings_sp)
g_settings_sp.reset (new PlatformLinuxProperties ());
return g_settings_sp;
}
void
PlatformLinux::DebuggerInitialize (Debugger &debugger)
{
if (!PluginManager::GetSettingForPlatformPlugin (debugger, PlatformLinuxProperties::GetSettingName()))
{
const bool is_global_setting = true;
PluginManager::CreateSettingForPlatformPlugin (debugger,
GetGlobalProperties()->GetValueProperties(),
ConstString ("Properties for the PlatformLinux plug-in."),
is_global_setting);
}
}
//------------------------------------------------------------------
PlatformSP
PlatformLinux::CreateInstance (bool force, const ArchSpec *arch)
{
Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM));
if (log)
{
const char *arch_name;
if (arch && arch->GetArchitectureName ())
arch_name = arch->GetArchitectureName ();
else
arch_name = "<null>";
const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : "<null>";
log->Printf ("PlatformLinux::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr);
}
bool create = force;
if (create == false && arch && arch->IsValid())
{
const llvm::Triple &triple = arch->GetTriple();
switch (triple.getOS())
{
case llvm::Triple::Linux:
create = true;
break;
#if defined(__linux__)
// Only accept "unknown" for the OS if the host is linux and
// it "unknown" wasn't specified (it was just returned because it
// was NOT specified)
case llvm::Triple::OSType::UnknownOS:
create = !arch->TripleOSWasSpecified();
break;
#endif
default:
break;
}
}
if (create)
{
if (log)
log->Printf ("PlatformLinux::%s() creating remote-linux platform", __FUNCTION__);
return PlatformSP(new PlatformLinux(false));
}
if (log)
log->Printf ("PlatformLinux::%s() aborting creation of remote-linux platform", __FUNCTION__);
return PlatformSP();
}
ConstString
PlatformLinux::GetPluginNameStatic (bool is_host)
{
if (is_host)
{
static ConstString g_host_name(Platform::GetHostPlatformName ());
return g_host_name;
}
else
{
static ConstString g_remote_name("remote-linux");
return g_remote_name;
}
}
const char *
PlatformLinux::GetPluginDescriptionStatic (bool is_host)
{
if (is_host)
return "Local Linux user platform plug-in.";
else
return "Remote Linux user platform plug-in.";
}
ConstString
PlatformLinux::GetPluginName()
{
return GetPluginNameStatic(IsHost());
}
void
PlatformLinux::Initialize ()
{
PlatformPOSIX::Initialize ();
if (g_initialize_count++ == 0)
{
#if defined(__linux__) && !defined(__ANDROID__)
PlatformSP default_platform_sp (new PlatformLinux(true));
default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture());
Platform::SetHostPlatform (default_platform_sp);
#endif
PluginManager::RegisterPlugin(PlatformLinux::GetPluginNameStatic(false),
PlatformLinux::GetPluginDescriptionStatic(false),
PlatformLinux::CreateInstance,
PlatformLinux::DebuggerInitialize);
}
}
void
PlatformLinux::Terminate ()
{
if (g_initialize_count > 0)
{
if (--g_initialize_count == 0)
{
PluginManager::UnregisterPlugin (PlatformLinux::CreateInstance);
}
}
PlatformPOSIX::Terminate ();
}
Error
PlatformLinux::ResolveExecutable (const ModuleSpec &ms,
lldb::ModuleSP &exe_module_sp,
const FileSpecList *module_search_paths_ptr)
{
Error error;
// Nothing special to do here, just use the actual file and architecture
char exe_path[PATH_MAX];
ModuleSpec resolved_module_spec (ms);
if (IsHost())
{
// If we have "ls" as the exe_file, resolve the executable location based on
// the current path variables
if (!resolved_module_spec.GetFileSpec().Exists())
{
resolved_module_spec.GetFileSpec().GetPath(exe_path, sizeof(exe_path));
resolved_module_spec.GetFileSpec().SetFile(exe_path, true);
}
if (!resolved_module_spec.GetFileSpec().Exists())
resolved_module_spec.GetFileSpec().ResolveExecutableLocation ();
if (resolved_module_spec.GetFileSpec().Exists())
error.Clear();
else
{
error.SetErrorStringWithFormat("unable to find executable for '%s'", resolved_module_spec.GetFileSpec().GetPath().c_str());
}
}
else
{
if (m_remote_platform_sp)
{
error = GetCachedExecutable (resolved_module_spec, exe_module_sp, module_search_paths_ptr, *m_remote_platform_sp);
}
else
{
// We may connect to a process and use the provided executable (Don't use local $PATH).
if (resolved_module_spec.GetFileSpec().Exists())
error.Clear();
else
error.SetErrorStringWithFormat("the platform is not currently connected, and '%s' doesn't exist in the system root.", exe_path);
}
}
if (error.Success())
{
if (resolved_module_spec.GetArchitecture().IsValid())
{
error = ModuleList::GetSharedModule (resolved_module_spec,
exe_module_sp,
NULL,
NULL,
NULL);
if (error.Fail())
{
// If we failed, it may be because the vendor and os aren't known. If that is the
// case, try setting them to the host architecture and give it another try.
llvm::Triple &module_triple = resolved_module_spec.GetArchitecture().GetTriple();
bool is_vendor_specified = (module_triple.getVendor() != llvm::Triple::UnknownVendor);
bool is_os_specified = (module_triple.getOS() != llvm::Triple::UnknownOS);
if (!is_vendor_specified || !is_os_specified)
{
const llvm::Triple &host_triple = HostInfo::GetArchitecture(HostInfo::eArchKindDefault).GetTriple();
if (!is_vendor_specified)
module_triple.setVendorName (host_triple.getVendorName());
if (!is_os_specified)
module_triple.setOSName (host_triple.getOSName());
error = ModuleList::GetSharedModule (resolved_module_spec,
exe_module_sp,
NULL,
NULL,
NULL);
}
}
// TODO find out why exe_module_sp might be NULL
if (!exe_module_sp || exe_module_sp->GetObjectFile() == NULL)
{
exe_module_sp.reset();
error.SetErrorStringWithFormat ("'%s' doesn't contain the architecture %s",
resolved_module_spec.GetFileSpec().GetPath().c_str(),
resolved_module_spec.GetArchitecture().GetArchitectureName());
}
}
else
{
// No valid architecture was specified, ask the platform for
// the architectures that we should be using (in the correct order)
// and see if we can find a match that way
StreamString arch_names;
for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, resolved_module_spec.GetArchitecture()); ++idx)
{
error = ModuleList::GetSharedModule (resolved_module_spec,
exe_module_sp,
NULL,
NULL,
NULL);
// Did we find an executable using one of the
if (error.Success())
{
if (exe_module_sp && exe_module_sp->GetObjectFile())
break;
else
error.SetErrorToGenericError();
}
if (idx > 0)
arch_names.PutCString (", ");
arch_names.PutCString (resolved_module_spec.GetArchitecture().GetArchitectureName());
}
if (error.Fail() || !exe_module_sp)
{
if (resolved_module_spec.GetFileSpec().Readable())
{
error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s",
resolved_module_spec.GetFileSpec().GetPath().c_str(),
GetPluginName().GetCString(),
arch_names.GetString().c_str());
}
else
{
error.SetErrorStringWithFormat("'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str());
}
}
}
}
return error;
}
Error
PlatformLinux::GetFileWithUUID (const FileSpec &platform_file,
const UUID *uuid_ptr, FileSpec &local_file)
{
if (IsRemote())
{
if (m_remote_platform_sp)
return m_remote_platform_sp->GetFileWithUUID (platform_file, uuid_ptr, local_file);
}
// Default to the local case
local_file = platform_file;
return Error();
}
//------------------------------------------------------------------
/// Default Constructor
//------------------------------------------------------------------
PlatformLinux::PlatformLinux (bool is_host) :
PlatformPOSIX(is_host) // This is the local host platform
{
}
//------------------------------------------------------------------
/// Destructor.
///
/// The destructor is virtual since this class is designed to be
/// inherited from by the plug-in instance.
//------------------------------------------------------------------
PlatformLinux::~PlatformLinux() = default;
bool
PlatformLinux::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info)
{
bool success = false;
if (IsHost())
{
success = Platform::GetProcessInfo (pid, process_info);
}
else
{
if (m_remote_platform_sp)
success = m_remote_platform_sp->GetProcessInfo (pid, process_info);
}
return success;
}
uint32_t
PlatformLinux::FindProcesses (const ProcessInstanceInfoMatch &match_info,
ProcessInstanceInfoList &process_infos)
{
uint32_t match_count = 0;
if (IsHost())
{
// Let the base class figure out the host details
match_count = Platform::FindProcesses (match_info, process_infos);
}
else
{
// If we are remote, we can only return results if we are connected
if (m_remote_platform_sp)
match_count = m_remote_platform_sp->FindProcesses (match_info, process_infos);
}
return match_count;
}
bool
PlatformLinux::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch)
{
if (IsHost())
{
ArchSpec hostArch = HostInfo::GetArchitecture(HostInfo::eArchKindDefault);
if (hostArch.GetTriple().isOSLinux())
{
if (idx == 0)
{
arch = hostArch;
return arch.IsValid();
}
else if (idx == 1)
{
// If the default host architecture is 64-bit, look for a 32-bit variant
if (hostArch.IsValid() && hostArch.GetTriple().isArch64Bit())
{
arch = HostInfo::GetArchitecture(HostInfo::eArchKind32);
return arch.IsValid();
}
}
}
}
else
{
if (m_remote_platform_sp)
return m_remote_platform_sp->GetSupportedArchitectureAtIndex(idx, arch);
llvm::Triple triple;
// Set the OS to linux
triple.setOS(llvm::Triple::Linux);
// Set the architecture
switch (idx)
{
case 0: triple.setArchName("x86_64"); break;
case 1: triple.setArchName("i386"); break;
case 2: triple.setArchName("arm"); break;
case 3: triple.setArchName("aarch64"); break;
case 4: triple.setArchName("mips64"); break;
case 5: triple.setArchName("hexagon"); break;
case 6: triple.setArchName("mips"); break;
case 7: triple.setArchName("mips64el"); break;
case 8: triple.setArchName("mipsel"); break;
default: return false;
}
// Leave the vendor as "llvm::Triple:UnknownVendor" and don't specify the vendor by
// calling triple.SetVendorName("unknown") so that it is a "unspecified unknown".
// This means when someone calls triple.GetVendorName() it will return an empty string
// which indicates that the vendor can be set when two architectures are merged
// Now set the triple into "arch" and return true
arch.SetTriple(triple);
return true;
}
return false;
}
void
PlatformLinux::GetStatus (Stream &strm)
{
Platform::GetStatus(strm);
#ifndef LLDB_DISABLE_POSIX
// Display local kernel information only when we are running in host mode.
// Otherwise, we would end up printing non-Linux information (when running
// on Mac OS for example).
if (IsHost())
{
struct utsname un;
if (uname(&un))
return;
strm.Printf (" Kernel: %s\n", un.sysname);
strm.Printf (" Release: %s\n", un.release);
strm.Printf (" Version: %s\n", un.version);
}
#endif
}
int32_t
PlatformLinux::GetResumeCountForLaunchInfo (ProcessLaunchInfo &launch_info)
{
int32_t resume_count = 0;
// Always resume past the initial stop when we use eLaunchFlagDebug
if (launch_info.GetFlags ().Test (eLaunchFlagDebug))
{
// Resume past the stop for the final exec into the true inferior.
++resume_count;
}
// If we're not launching a shell, we're done.
const FileSpec &shell = launch_info.GetShell();
if (!shell)
return resume_count;
std::string shell_string = shell.GetPath();
// 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_string.c_str(), '/');
if (shell_name == NULL)
shell_name = shell_string.c_str();
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::CanDebugProcess ()
{
if (IsHost ())
{
return true;
}
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::DebugProcess (ProcessLaunchInfo &launch_info,
Debugger &debugger,
Target *target, // Can be NULL, if NULL create a new target, else use existing one
Error &error)
{
Log *log(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, error);
//
// For local debugging, we'll insist on having ProcessGDBRemote create the process.
//
ProcessSP 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 ())
{
if (log)
log->Printf ("PlatformLinux::%s failed to create new target: %s", __FUNCTION__, error.AsCString ());
return process_sp;
}
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
{
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 (launch_info.GetListenerForProcess(debugger), "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__);
}
// Adjust launch for a hijacker.
ListenerSP listener_sp;
if (!launch_info.GetHijackListener ())
{
if (log)
log->Printf ("PlatformLinux::%s setting up hijacker", __FUNCTION__);
listener_sp = Listener::MakeListener("lldb.PlatformLinux.DebugProcess.hijack");
launch_info.SetHijackListener (listener_sp);
process_sp->HijackProcessEvents (listener_sp);
}
// 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);
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"));
}
uint64_t
PlatformLinux::ConvertMmapFlagsToPlatform(const ArchSpec &arch, unsigned flags)
{
uint64_t flags_platform = 0;
uint64_t map_anon = MAP_ANON;
// To get correct flags for MIPS Architecture
if (arch.GetTriple ().getArch () == llvm::Triple::mips64
|| arch.GetTriple ().getArch () == llvm::Triple::mips64el
|| arch.GetTriple ().getArch () == llvm::Triple::mips
|| arch.GetTriple ().getArch () == llvm::Triple::mipsel)
map_anon = 0x800;
if (flags & eMmapFlagsPrivate)
flags_platform |= MAP_PRIVATE;
if (flags & eMmapFlagsAnon)
flags_platform |= map_anon;
return flags_platform;
}
ConstString
PlatformLinux::GetFullNameForDylib (ConstString basename)
{
if (basename.IsEmpty())
return basename;
StreamString stream;
stream.Printf("lib%s.so", basename.GetCString());
return ConstString(stream.GetData());
}