This finally removes the use of the Mutex and Condition classes. This is an
intricate patch as the Mutex and Condition classes were tied together.
Furthermore, many places had slightly differing uses of time values. Convert
timeout values to relative everywhere to permit the use of
std::chrono::duration, which is required for the use of
std::condition_variable's timeout. Adjust all Condition and related Mutex
classes over to std::{,recursive_}mutex and std::condition_variable.
This change primarily comes at the cost of breaking the TracingMutex which was
based around the Mutex class. It would be possible to write a wrapper to
provide similar functionality, but that is beyond the scope of this change.
llvm-svn: 277011
779 lines
25 KiB
C++
779 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;
|
|
case 9: triple.setArchName("s390x"); 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(std::chrono::microseconds(0), 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());
|
|
}
|