llvm-project/lldb/source/Target/Platform.cpp

2090 lines
65 KiB
C++

//===-- Platform.cpp --------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lldb/Target/Platform.h"
// C Includes
// C++ Includes
#include <algorithm>
#include <fstream>
#include <vector>
// Other libraries and framework includes
// Project includes
#include "lldb/Breakpoint/BreakpointIDList.h"
#include "lldb/Core/DataBufferHeap.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Error.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Core/StructuredData.h"
#include "lldb/Host/FileSpec.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/Host/Host.h"
#include "lldb/Host/HostInfo.h"
#include "lldb/Interpreter/OptionValueProperties.h"
#include "lldb/Interpreter/Property.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/UnixSignals.h"
#include "lldb/Utility/Utils.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "Utility/ModuleCache.h"
// Define these constants from POSIX mman.h rather than include the file
// so that they will be correct even when compiled on Linux.
#define MAP_PRIVATE 2
#define MAP_ANON 0x1000
using namespace lldb;
using namespace lldb_private;
static uint32_t g_initialize_count = 0;
// Use a singleton function for g_local_platform_sp to avoid init
// constructors since LLDB is often part of a shared library
static PlatformSP&
GetHostPlatformSP ()
{
static PlatformSP g_platform_sp;
return g_platform_sp;
}
const char *
Platform::GetHostPlatformName ()
{
return "host";
}
namespace {
PropertyDefinition
g_properties[] =
{
{ "use-module-cache" , OptionValue::eTypeBoolean , true, true, nullptr, nullptr, "Use module cache." },
{ "module-cache-directory", OptionValue::eTypeFileSpec, true, 0 , nullptr, nullptr, "Root directory for cached modules." },
{ nullptr , OptionValue::eTypeInvalid , false, 0, nullptr, nullptr, nullptr }
};
enum
{
ePropertyUseModuleCache,
ePropertyModuleCacheDirectory
};
} // namespace
ConstString
PlatformProperties::GetSettingName ()
{
static ConstString g_setting_name("platform");
return g_setting_name;
}
PlatformProperties::PlatformProperties ()
{
m_collection_sp.reset (new OptionValueProperties (GetSettingName ()));
m_collection_sp->Initialize (g_properties);
auto module_cache_dir = GetModuleCacheDirectory ();
if (module_cache_dir)
return;
llvm::SmallString<64> user_home_dir;
if (!llvm::sys::path::home_directory (user_home_dir))
return;
module_cache_dir = FileSpec (user_home_dir.c_str(), false);
module_cache_dir.AppendPathComponent (".lldb");
module_cache_dir.AppendPathComponent ("module_cache");
SetModuleCacheDirectory (module_cache_dir);
}
bool
PlatformProperties::GetUseModuleCache () const
{
const auto idx = ePropertyUseModuleCache;
return m_collection_sp->GetPropertyAtIndexAsBoolean (
nullptr, idx, g_properties[idx].default_uint_value != 0);
}
bool
PlatformProperties::SetUseModuleCache (bool use_module_cache)
{
return m_collection_sp->SetPropertyAtIndexAsBoolean (nullptr, ePropertyUseModuleCache, use_module_cache);
}
FileSpec
PlatformProperties::GetModuleCacheDirectory () const
{
return m_collection_sp->GetPropertyAtIndexAsFileSpec (nullptr, ePropertyModuleCacheDirectory);
}
bool
PlatformProperties::SetModuleCacheDirectory (const FileSpec& dir_spec)
{
return m_collection_sp->SetPropertyAtIndexAsFileSpec (nullptr, ePropertyModuleCacheDirectory, dir_spec);
}
//------------------------------------------------------------------
/// Get the native host platform plug-in.
///
/// There should only be one of these for each host that LLDB runs
/// upon that should be statically compiled in and registered using
/// preprocessor macros or other similar build mechanisms.
///
/// This platform will be used as the default platform when launching
/// or attaching to processes unless another platform is specified.
//------------------------------------------------------------------
PlatformSP
Platform::GetHostPlatform ()
{
return GetHostPlatformSP ();
}
static std::vector<PlatformSP> &
GetPlatformList()
{
static std::vector<PlatformSP> g_platform_list;
return g_platform_list;
}
static Mutex &
GetPlatformListMutex ()
{
static Mutex g_mutex(Mutex::eMutexTypeRecursive);
return g_mutex;
}
void
Platform::Initialize ()
{
g_initialize_count++;
}
void
Platform::Terminate ()
{
if (g_initialize_count > 0)
{
if (--g_initialize_count == 0)
{
Mutex::Locker locker(GetPlatformListMutex ());
GetPlatformList().clear();
}
}
}
const PlatformPropertiesSP &
Platform::GetGlobalPlatformProperties ()
{
static const auto g_settings_sp (std::make_shared<PlatformProperties> ());
return g_settings_sp;
}
void
Platform::SetHostPlatform (const lldb::PlatformSP &platform_sp)
{
// The native platform should use its static void Platform::Initialize()
// function to register itself as the native platform.
GetHostPlatformSP () = platform_sp;
if (platform_sp)
{
Mutex::Locker locker(GetPlatformListMutex ());
GetPlatformList().push_back(platform_sp);
}
}
Error
Platform::GetFileWithUUID (const FileSpec &platform_file,
const UUID *uuid_ptr,
FileSpec &local_file)
{
// Default to the local case
local_file = platform_file;
return Error();
}
FileSpecList
Platform::LocateExecutableScriptingResources (Target *target, Module &module, Stream* feedback_stream)
{
return FileSpecList();
}
//PlatformSP
//Platform::FindPlugin (Process *process, const ConstString &plugin_name)
//{
// PlatformCreateInstance create_callback = NULL;
// if (plugin_name)
// {
// create_callback = PluginManager::GetPlatformCreateCallbackForPluginName (plugin_name);
// if (create_callback)
// {
// ArchSpec arch;
// if (process)
// {
// arch = process->GetTarget().GetArchitecture();
// }
// PlatformSP platform_sp(create_callback(process, &arch));
// if (platform_sp)
// return platform_sp;
// }
// }
// else
// {
// for (uint32_t idx = 0; (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex(idx)) != NULL; ++idx)
// {
// PlatformSP platform_sp(create_callback(process, nullptr));
// if (platform_sp)
// return platform_sp;
// }
// }
// return PlatformSP();
//}
Error
Platform::GetSharedModule (const ModuleSpec &module_spec,
Process* process,
ModuleSP &module_sp,
const FileSpecList *module_search_paths_ptr,
ModuleSP *old_module_sp_ptr,
bool *did_create_ptr)
{
if (IsHost ())
return ModuleList::GetSharedModule (module_spec,
module_sp,
module_search_paths_ptr,
old_module_sp_ptr,
did_create_ptr,
false);
return GetRemoteSharedModule (module_spec,
process,
module_sp,
[&](const ModuleSpec &spec)
{
Error error = ModuleList::GetSharedModule (
spec, module_sp, module_search_paths_ptr, old_module_sp_ptr, did_create_ptr, false);
if (error.Success() && module_sp)
module_sp->SetPlatformFileSpec(spec.GetFileSpec());
return error;
},
did_create_ptr);
}
bool
Platform::GetModuleSpec (const FileSpec& module_file_spec,
const ArchSpec& arch,
ModuleSpec &module_spec)
{
ModuleSpecList module_specs;
if (ObjectFile::GetModuleSpecifications (module_file_spec, 0, 0, module_specs) == 0)
return false;
ModuleSpec matched_module_spec;
return module_specs.FindMatchingModuleSpec (ModuleSpec (module_file_spec, arch),
module_spec);
}
PlatformSP
Platform::Find (const ConstString &name)
{
if (name)
{
static ConstString g_host_platform_name ("host");
if (name == g_host_platform_name)
return GetHostPlatform();
Mutex::Locker locker(GetPlatformListMutex ());
for (const auto &platform_sp : GetPlatformList())
{
if (platform_sp->GetName() == name)
return platform_sp;
}
}
return PlatformSP();
}
PlatformSP
Platform::Create (const ConstString &name, Error &error)
{
PlatformCreateInstance create_callback = NULL;
lldb::PlatformSP platform_sp;
if (name)
{
static ConstString g_host_platform_name ("host");
if (name == g_host_platform_name)
return GetHostPlatform();
create_callback = PluginManager::GetPlatformCreateCallbackForPluginName (name);
if (create_callback)
platform_sp = create_callback(true, NULL);
else
error.SetErrorStringWithFormat ("unable to find a plug-in for the platform named \"%s\"", name.GetCString());
}
else
error.SetErrorString ("invalid platform name");
if (platform_sp)
{
Mutex::Locker locker(GetPlatformListMutex ());
GetPlatformList().push_back(platform_sp);
}
return platform_sp;
}
PlatformSP
Platform::Create (const ArchSpec &arch, ArchSpec *platform_arch_ptr, Error &error)
{
lldb::PlatformSP platform_sp;
if (arch.IsValid())
{
// Scope for locker
{
// First try exact arch matches across all platforms already created
Mutex::Locker locker(GetPlatformListMutex ());
for (const auto &platform_sp : GetPlatformList())
{
if (platform_sp->IsCompatibleArchitecture(arch, true, platform_arch_ptr))
return platform_sp;
}
// Next try compatible arch matches across all platforms already created
for (const auto &platform_sp : GetPlatformList())
{
if (platform_sp->IsCompatibleArchitecture(arch, false, platform_arch_ptr))
return platform_sp;
}
}
PlatformCreateInstance create_callback;
// First try exact arch matches across all platform plug-ins
uint32_t idx;
for (idx = 0; (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex (idx)); ++idx)
{
if (create_callback)
{
platform_sp = create_callback(false, &arch);
if (platform_sp && platform_sp->IsCompatibleArchitecture(arch, true, platform_arch_ptr))
{
Mutex::Locker locker(GetPlatformListMutex ());
GetPlatformList().push_back(platform_sp);
return platform_sp;
}
}
}
// Next try compatible arch matches across all platform plug-ins
for (idx = 0; (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex (idx)); ++idx)
{
if (create_callback)
{
platform_sp = create_callback(false, &arch);
if (platform_sp && platform_sp->IsCompatibleArchitecture(arch, false, platform_arch_ptr))
{
Mutex::Locker locker(GetPlatformListMutex ());
GetPlatformList().push_back(platform_sp);
return platform_sp;
}
}
}
}
else
error.SetErrorString ("invalid platform name");
if (platform_arch_ptr)
platform_arch_ptr->Clear();
platform_sp.reset();
return platform_sp;
}
//------------------------------------------------------------------
/// Default Constructor
//------------------------------------------------------------------
Platform::Platform (bool is_host) :
m_is_host (is_host),
m_os_version_set_while_connected (false),
m_system_arch_set_while_connected (false),
m_sdk_sysroot (),
m_sdk_build (),
m_working_dir (),
m_remote_url (),
m_name (),
m_major_os_version (UINT32_MAX),
m_minor_os_version (UINT32_MAX),
m_update_os_version (UINT32_MAX),
m_system_arch(),
m_mutex (Mutex::eMutexTypeRecursive),
m_uid_map(),
m_gid_map(),
m_max_uid_name_len (0),
m_max_gid_name_len (0),
m_supports_rsync (false),
m_rsync_opts (),
m_rsync_prefix (),
m_supports_ssh (false),
m_ssh_opts (),
m_ignores_remote_hostname (false),
m_trap_handlers(),
m_calculated_trap_handlers (false),
m_module_cache (llvm::make_unique<ModuleCache> ())
{
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT));
if (log)
log->Printf ("%p Platform::Platform()", static_cast<void*>(this));
}
//------------------------------------------------------------------
/// Destructor.
///
/// The destructor is virtual since this class is designed to be
/// inherited from by the plug-in instance.
//------------------------------------------------------------------
Platform::~Platform()
{
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT));
if (log)
log->Printf ("%p Platform::~Platform()", static_cast<void*>(this));
}
void
Platform::GetStatus (Stream &strm)
{
uint32_t major = UINT32_MAX;
uint32_t minor = UINT32_MAX;
uint32_t update = UINT32_MAX;
std::string s;
strm.Printf (" Platform: %s\n", GetPluginName().GetCString());
ArchSpec arch (GetSystemArchitecture());
if (arch.IsValid())
{
if (!arch.GetTriple().str().empty())
{
strm.Printf(" Triple: ");
arch.DumpTriple(strm);
strm.EOL();
}
}
if (GetOSVersion(major, minor, update))
{
strm.Printf("OS Version: %u", major);
if (minor != UINT32_MAX)
strm.Printf(".%u", minor);
if (update != UINT32_MAX)
strm.Printf(".%u", update);
if (GetOSBuildString (s))
strm.Printf(" (%s)", s.c_str());
strm.EOL();
}
if (GetOSKernelDescription (s))
strm.Printf(" Kernel: %s\n", s.c_str());
if (IsHost())
{
strm.Printf(" Hostname: %s\n", GetHostname());
}
else
{
const bool is_connected = IsConnected();
if (is_connected)
strm.Printf(" Hostname: %s\n", GetHostname());
strm.Printf(" Connected: %s\n", is_connected ? "yes" : "no");
}
if (GetWorkingDirectory())
{
strm.Printf("WorkingDir: %s\n", GetWorkingDirectory().GetCString());
}
if (!IsConnected())
return;
std::string specific_info(GetPlatformSpecificConnectionInformation());
if (specific_info.empty() == false)
strm.Printf("Platform-specific connection: %s\n", specific_info.c_str());
}
bool
Platform::GetOSVersion (uint32_t &major,
uint32_t &minor,
uint32_t &update,
Process *process)
{
Mutex::Locker locker (m_mutex);
bool success = m_major_os_version != UINT32_MAX;
if (IsHost())
{
if (!success)
{
// We have a local host platform
success = HostInfo::GetOSVersion(m_major_os_version, m_minor_os_version, m_update_os_version);
m_os_version_set_while_connected = success;
}
}
else
{
// We have a remote platform. We can only fetch the remote
// OS version if we are connected, and we don't want to do it
// more than once.
const bool is_connected = IsConnected();
bool fetch = false;
if (success)
{
// We have valid OS version info, check to make sure it wasn't
// manually set prior to connecting. If it was manually set prior
// to connecting, then lets fetch the actual OS version info
// if we are now connected.
if (is_connected && !m_os_version_set_while_connected)
fetch = true;
}
else
{
// We don't have valid OS version info, fetch it if we are connected
fetch = is_connected;
}
if (fetch)
{
success = GetRemoteOSVersion ();
m_os_version_set_while_connected = success;
}
}
if (success)
{
major = m_major_os_version;
minor = m_minor_os_version;
update = m_update_os_version;
}
else if (process)
{
// Check with the process in case it can answer the question if
// a process was provided
return process->GetHostOSVersion(major, minor, update);
}
return success;
}
bool
Platform::GetOSBuildString (std::string &s)
{
s.clear();
if (IsHost())
#if !defined(__linux__)
return HostInfo::GetOSBuildString(s);
#else
return false;
#endif
else
return GetRemoteOSBuildString (s);
}
bool
Platform::GetOSKernelDescription (std::string &s)
{
if (IsHost())
#if !defined(__linux__)
return HostInfo::GetOSKernelDescription(s);
#else
return false;
#endif
else
return GetRemoteOSKernelDescription (s);
}
void
Platform::AddClangModuleCompilationOptions (Target *target, std::vector<std::string> &options)
{
std::vector<std::string> default_compilation_options =
{
"-x", "c++", "-Xclang", "-nostdsysteminc", "-Xclang", "-nostdsysteminc"
};
options.insert(options.end(),
default_compilation_options.begin(),
default_compilation_options.end());
}
FileSpec
Platform::GetWorkingDirectory ()
{
if (IsHost())
{
char cwd[PATH_MAX];
if (getcwd(cwd, sizeof(cwd)))
return FileSpec{cwd, true};
else
return FileSpec{};
}
else
{
if (!m_working_dir)
m_working_dir = GetRemoteWorkingDirectory();
return m_working_dir;
}
}
struct RecurseCopyBaton
{
const FileSpec& dst;
Platform *platform_ptr;
Error error;
};
static FileSpec::EnumerateDirectoryResult
RecurseCopy_Callback (void *baton,
FileSpec::FileType file_type,
const FileSpec &src)
{
RecurseCopyBaton* rc_baton = (RecurseCopyBaton*)baton;
switch (file_type)
{
case FileSpec::eFileTypePipe:
case FileSpec::eFileTypeSocket:
// we have no way to copy pipes and sockets - ignore them and continue
return FileSpec::eEnumerateDirectoryResultNext;
break;
case FileSpec::eFileTypeDirectory:
{
// make the new directory and get in there
FileSpec dst_dir = rc_baton->dst;
if (!dst_dir.GetFilename())
dst_dir.GetFilename() = src.GetLastPathComponent();
Error error = rc_baton->platform_ptr->MakeDirectory(dst_dir, lldb::eFilePermissionsDirectoryDefault);
if (error.Fail())
{
rc_baton->error.SetErrorStringWithFormat("unable to setup directory %s on remote end",
dst_dir.GetCString());
return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out
}
// now recurse
std::string src_dir_path (src.GetPath());
// Make a filespec that only fills in the directory of a FileSpec so
// when we enumerate we can quickly fill in the filename for dst copies
FileSpec recurse_dst;
recurse_dst.GetDirectory().SetCString(dst_dir.GetPath().c_str());
RecurseCopyBaton rc_baton2 = { recurse_dst, rc_baton->platform_ptr, Error() };
FileSpec::EnumerateDirectory(src_dir_path.c_str(), true, true, true, RecurseCopy_Callback, &rc_baton2);
if (rc_baton2.error.Fail())
{
rc_baton->error.SetErrorString(rc_baton2.error.AsCString());
return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out
}
return FileSpec::eEnumerateDirectoryResultNext;
}
break;
case FileSpec::eFileTypeSymbolicLink:
{
// copy the file and keep going
FileSpec dst_file = rc_baton->dst;
if (!dst_file.GetFilename())
dst_file.GetFilename() = src.GetFilename();
FileSpec src_resolved;
rc_baton->error = FileSystem::Readlink(src, src_resolved);
if (rc_baton->error.Fail())
return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out
rc_baton->error = rc_baton->platform_ptr->CreateSymlink(dst_file, src_resolved);
if (rc_baton->error.Fail())
return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out
return FileSpec::eEnumerateDirectoryResultNext;
}
break;
case FileSpec::eFileTypeRegular:
{
// copy the file and keep going
FileSpec dst_file = rc_baton->dst;
if (!dst_file.GetFilename())
dst_file.GetFilename() = src.GetFilename();
Error err = rc_baton->platform_ptr->PutFile(src, dst_file);
if (err.Fail())
{
rc_baton->error.SetErrorString(err.AsCString());
return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out
}
return FileSpec::eEnumerateDirectoryResultNext;
}
break;
case FileSpec::eFileTypeInvalid:
case FileSpec::eFileTypeOther:
case FileSpec::eFileTypeUnknown:
rc_baton->error.SetErrorStringWithFormat("invalid file detected during copy: %s", src.GetPath().c_str());
return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out
break;
}
llvm_unreachable("Unhandled FileSpec::FileType!");
}
Error
Platform::Install (const FileSpec& src, const FileSpec& dst)
{
Error error;
Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
if (log)
log->Printf ("Platform::Install (src='%s', dst='%s')", src.GetPath().c_str(), dst.GetPath().c_str());
FileSpec fixed_dst(dst);
if (!fixed_dst.GetFilename())
fixed_dst.GetFilename() = src.GetFilename();
FileSpec working_dir = GetWorkingDirectory();
if (dst)
{
if (dst.GetDirectory())
{
const char first_dst_dir_char = dst.GetDirectory().GetCString()[0];
if (first_dst_dir_char == '/' || first_dst_dir_char == '\\')
{
fixed_dst.GetDirectory() = dst.GetDirectory();
}
// If the fixed destination file doesn't have a directory yet,
// then we must have a relative path. We will resolve this relative
// path against the platform's working directory
if (!fixed_dst.GetDirectory())
{
FileSpec relative_spec;
std::string path;
if (working_dir)
{
relative_spec = working_dir;
relative_spec.AppendPathComponent(dst.GetPath());
fixed_dst.GetDirectory() = relative_spec.GetDirectory();
}
else
{
error.SetErrorStringWithFormat("platform working directory must be valid for relative path '%s'", dst.GetPath().c_str());
return error;
}
}
}
else
{
if (working_dir)
{
fixed_dst.GetDirectory().SetCString(working_dir.GetCString());
}
else
{
error.SetErrorStringWithFormat("platform working directory must be valid for relative path '%s'", dst.GetPath().c_str());
return error;
}
}
}
else
{
if (working_dir)
{
fixed_dst.GetDirectory().SetCString(working_dir.GetCString());
}
else
{
error.SetErrorStringWithFormat("platform working directory must be valid when destination directory is empty");
return error;
}
}
if (log)
log->Printf ("Platform::Install (src='%s', dst='%s') fixed_dst='%s'", src.GetPath().c_str(), dst.GetPath().c_str(), fixed_dst.GetPath().c_str());
if (GetSupportsRSync())
{
error = PutFile(src, dst);
}
else
{
switch (src.GetFileType())
{
case FileSpec::eFileTypeDirectory:
{
if (GetFileExists (fixed_dst))
Unlink(fixed_dst);
uint32_t permissions = src.GetPermissions();
if (permissions == 0)
permissions = eFilePermissionsDirectoryDefault;
error = MakeDirectory(fixed_dst, permissions);
if (error.Success())
{
// Make a filespec that only fills in the directory of a FileSpec so
// when we enumerate we can quickly fill in the filename for dst copies
FileSpec recurse_dst;
recurse_dst.GetDirectory().SetCString(fixed_dst.GetCString());
std::string src_dir_path (src.GetPath());
RecurseCopyBaton baton = { recurse_dst, this, Error() };
FileSpec::EnumerateDirectory(src_dir_path.c_str(), true, true, true, RecurseCopy_Callback, &baton);
return baton.error;
}
}
break;
case FileSpec::eFileTypeRegular:
if (GetFileExists (fixed_dst))
Unlink(fixed_dst);
error = PutFile(src, fixed_dst);
break;
case FileSpec::eFileTypeSymbolicLink:
{
if (GetFileExists (fixed_dst))
Unlink(fixed_dst);
FileSpec src_resolved;
error = FileSystem::Readlink(src, src_resolved);
if (error.Success())
error = CreateSymlink(dst, src_resolved);
}
break;
case FileSpec::eFileTypePipe:
error.SetErrorString("platform install doesn't handle pipes");
break;
case FileSpec::eFileTypeSocket:
error.SetErrorString("platform install doesn't handle sockets");
break;
case FileSpec::eFileTypeInvalid:
case FileSpec::eFileTypeUnknown:
case FileSpec::eFileTypeOther:
error.SetErrorString("platform install doesn't handle non file or directory items");
break;
}
}
return error;
}
bool
Platform::SetWorkingDirectory(const FileSpec &file_spec)
{
if (IsHost())
{
Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
if (log)
log->Printf("Platform::SetWorkingDirectory('%s')",
file_spec.GetCString());
if (file_spec)
{
if (::chdir(file_spec.GetCString()) == 0)
return true;
}
return false;
}
else
{
m_working_dir.Clear();
return SetRemoteWorkingDirectory(file_spec);
}
}
Error
Platform::MakeDirectory(const FileSpec &file_spec, uint32_t permissions)
{
if (IsHost())
return FileSystem::MakeDirectory(file_spec, permissions);
else
{
Error error;
error.SetErrorStringWithFormat("remote platform %s doesn't support %s", GetPluginName().GetCString(), __PRETTY_FUNCTION__);
return error;
}
}
Error
Platform::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions)
{
if (IsHost())
return FileSystem::GetFilePermissions(file_spec, file_permissions);
else
{
Error error;
error.SetErrorStringWithFormat("remote platform %s doesn't support %s", GetPluginName().GetCString(), __PRETTY_FUNCTION__);
return error;
}
}
Error
Platform::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions)
{
if (IsHost())
return FileSystem::SetFilePermissions(file_spec, file_permissions);
else
{
Error error;
error.SetErrorStringWithFormat("remote platform %s doesn't support %s", GetPluginName().GetCString(), __PRETTY_FUNCTION__);
return error;
}
}
ConstString
Platform::GetName ()
{
return GetPluginName();
}
const char *
Platform::GetHostname ()
{
if (IsHost())
return "127.0.0.1";
if (m_name.empty())
return NULL;
return m_name.c_str();
}
ConstString
Platform::GetFullNameForDylib (ConstString basename)
{
return basename;
}
bool
Platform::SetRemoteWorkingDirectory(const FileSpec &working_dir)
{
Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
if (log)
log->Printf("Platform::SetRemoteWorkingDirectory('%s')",
working_dir.GetCString());
m_working_dir = working_dir;
return true;
}
const char *
Platform::GetUserName (uint32_t uid)
{
#if !defined(LLDB_DISABLE_POSIX)
const char *user_name = GetCachedUserName(uid);
if (user_name)
return user_name;
if (IsHost())
{
std::string name;
if (HostInfo::LookupUserName(uid, name))
return SetCachedUserName (uid, name.c_str(), name.size());
}
#endif
return NULL;
}
const char *
Platform::GetGroupName (uint32_t gid)
{
#if !defined(LLDB_DISABLE_POSIX)
const char *group_name = GetCachedGroupName(gid);
if (group_name)
return group_name;
if (IsHost())
{
std::string name;
if (HostInfo::LookupGroupName(gid, name))
return SetCachedGroupName (gid, name.c_str(), name.size());
}
#endif
return NULL;
}
bool
Platform::SetOSVersion (uint32_t major,
uint32_t minor,
uint32_t update)
{
if (IsHost())
{
// We don't need anyone setting the OS version for the host platform,
// we should be able to figure it out by calling HostInfo::GetOSVersion(...).
return false;
}
else
{
// We have a remote platform, allow setting the target OS version if
// we aren't connected, since if we are connected, we should be able to
// request the remote OS version from the connected platform.
if (IsConnected())
return false;
else
{
// We aren't connected and we might want to set the OS version
// ahead of time before we connect so we can peruse files and
// use a local SDK or PDK cache of support files to disassemble
// or do other things.
m_major_os_version = major;
m_minor_os_version = minor;
m_update_os_version = update;
return true;
}
}
return false;
}
Error
Platform::ResolveExecutable (const ModuleSpec &module_spec,
lldb::ModuleSP &exe_module_sp,
const FileSpecList *module_search_paths_ptr)
{
Error error;
if (module_spec.GetFileSpec().Exists())
{
if (module_spec.GetArchitecture().IsValid())
{
error = ModuleList::GetSharedModule (module_spec,
exe_module_sp,
module_search_paths_ptr,
NULL,
NULL);
}
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
ModuleSpec arch_module_spec(module_spec);
for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, arch_module_spec.GetArchitecture()); ++idx)
{
error = ModuleList::GetSharedModule (arch_module_spec,
exe_module_sp,
module_search_paths_ptr,
NULL,
NULL);
// Did we find an executable using one of the
if (error.Success() && exe_module_sp)
break;
}
}
}
else
{
error.SetErrorStringWithFormat ("'%s' does not exist",
module_spec.GetFileSpec().GetPath().c_str());
}
return error;
}
Error
Platform::ResolveSymbolFile (Target &target,
const ModuleSpec &sym_spec,
FileSpec &sym_file)
{
Error error;
if (sym_spec.GetSymbolFileSpec().Exists())
sym_file = sym_spec.GetSymbolFileSpec();
else
error.SetErrorString("unable to resolve symbol file");
return error;
}
bool
Platform::ResolveRemotePath (const FileSpec &platform_path,
FileSpec &resolved_platform_path)
{
resolved_platform_path = platform_path;
return resolved_platform_path.ResolvePath();
}
const ArchSpec &
Platform::GetSystemArchitecture()
{
if (IsHost())
{
if (!m_system_arch.IsValid())
{
// We have a local host platform
m_system_arch = HostInfo::GetArchitecture();
m_system_arch_set_while_connected = m_system_arch.IsValid();
}
}
else
{
// We have a remote platform. We can only fetch the remote
// system architecture if we are connected, and we don't want to do it
// more than once.
const bool is_connected = IsConnected();
bool fetch = false;
if (m_system_arch.IsValid())
{
// We have valid OS version info, check to make sure it wasn't
// manually set prior to connecting. If it was manually set prior
// to connecting, then lets fetch the actual OS version info
// if we are now connected.
if (is_connected && !m_system_arch_set_while_connected)
fetch = true;
}
else
{
// We don't have valid OS version info, fetch it if we are connected
fetch = is_connected;
}
if (fetch)
{
m_system_arch = GetRemoteSystemArchitecture ();
m_system_arch_set_while_connected = m_system_arch.IsValid();
}
}
return m_system_arch;
}
Error
Platform::ConnectRemote (Args& args)
{
Error error;
if (IsHost())
error.SetErrorStringWithFormat ("The currently selected platform (%s) is the host platform and is always connected.", GetPluginName().GetCString());
else
error.SetErrorStringWithFormat ("Platform::ConnectRemote() is not supported by %s", GetPluginName().GetCString());
return error;
}
Error
Platform::DisconnectRemote ()
{
Error error;
if (IsHost())
error.SetErrorStringWithFormat ("The currently selected platform (%s) is the host platform and is always connected.", GetPluginName().GetCString());
else
error.SetErrorStringWithFormat ("Platform::DisconnectRemote() is not supported by %s", GetPluginName().GetCString());
return error;
}
bool
Platform::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info)
{
// Take care of the host case so that each subclass can just
// call this function to get the host functionality.
if (IsHost())
return Host::GetProcessInfo (pid, process_info);
return false;
}
uint32_t
Platform::FindProcesses (const ProcessInstanceInfoMatch &match_info,
ProcessInstanceInfoList &process_infos)
{
// Take care of the host case so that each subclass can just
// call this function to get the host functionality.
uint32_t match_count = 0;
if (IsHost())
match_count = Host::FindProcesses (match_info, process_infos);
return match_count;
}
Error
Platform::LaunchProcess (ProcessLaunchInfo &launch_info)
{
Error error;
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM));
if (log)
log->Printf ("Platform::%s()", __FUNCTION__);
// Take care of the host case so that each subclass can just
// call this function to get the host functionality.
if (IsHost())
{
if (::getenv ("LLDB_LAUNCH_FLAG_LAUNCH_IN_TTY"))
launch_info.GetFlags().Set (eLaunchFlagLaunchInTTY);
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 (log)
{
const FileSpec &shell = launch_info.GetShell();
const char *shell_str = (shell) ? shell.GetPath().c_str() : "<null>";
log->Printf ("Platform::%s GetResumeCountForLaunchInfo() returned %" PRIu32 ", shell is '%s'",
__FUNCTION__,
num_resumes,
shell_str);
}
if (!launch_info.ConvertArgumentsForLaunchingInShell (error,
is_localhost,
will_debug,
first_arg_is_full_shell_command,
num_resumes))
return error;
}
else if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments))
{
error = ShellExpandArguments(launch_info);
if (error.Fail())
return error;
}
if (log)
log->Printf ("Platform::%s final launch_info resume count: %" PRIu32, __FUNCTION__, launch_info.GetResumeCount ());
error = Host::LaunchProcess (launch_info);
}
else
error.SetErrorString ("base lldb_private::Platform class can't launch remote processes");
return error;
}
Error
Platform::ShellExpandArguments (ProcessLaunchInfo &launch_info)
{
if (IsHost())
return Host::ShellExpandArguments(launch_info);
return Error("base lldb_private::Platform class can't expand arguments");
}
Error
Platform::KillProcess (const lldb::pid_t pid)
{
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM));
if (log)
log->Printf ("Platform::%s, pid %" PRIu64, __FUNCTION__, pid);
// Try to find a process plugin to handle this Kill request. If we can't, fall back to
// the default OS implementation.
size_t num_debuggers = Debugger::GetNumDebuggers();
for (size_t didx = 0; didx < num_debuggers; ++didx)
{
DebuggerSP debugger = Debugger::GetDebuggerAtIndex(didx);
lldb_private::TargetList &targets = debugger->GetTargetList();
for (int tidx = 0; tidx < targets.GetNumTargets(); ++tidx)
{
ProcessSP process = targets.GetTargetAtIndex(tidx)->GetProcessSP();
if (process->GetID() == pid)
return process->Destroy(true);
}
}
if (!IsHost())
{
return Error("base lldb_private::Platform class can't kill remote processes unless "
"they are controlled by a process plugin");
}
Host::Kill(pid, SIGTERM);
return Error();
}
lldb::ProcessSP
Platform::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(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM));
if (log)
log->Printf ("Platform::%s entered (target %p)", __FUNCTION__, static_cast<void*>(target));
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);
error = LaunchProcess (launch_info);
if (error.Success())
{
if (log)
log->Printf ("Platform::%s LaunchProcess() call succeeded (pid=%" PRIu64 ")", __FUNCTION__, launch_info.GetProcessID ());
if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID)
{
ProcessAttachInfo attach_info (launch_info);
process_sp = Attach (attach_info, debugger, target, error);
if (process_sp)
{
if (log)
log->Printf ("Platform::%s Attach() succeeded, Process plugin: %s", __FUNCTION__, process_sp->GetPluginName ().AsCString ());
launch_info.SetHijackListener(attach_info.GetHijackListener());
// Since we attached to the process, it will think it needs to detach
// if the process object just goes away without an explicit call to
// Process::Kill() or Process::Detach(), so let it know to kill the
// process if this happens.
process_sp->SetShouldDetach (false);
// If we didn't have any file actions, the pseudo terminal might
// have been used where the slave side was given as the file to
// open for stdin/out/err after we have already opened the master
// so we can read/write stdin/out/err.
int pty_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor();
if (pty_fd != lldb_utility::PseudoTerminal::invalid_fd)
{
process_sp->SetSTDIOFileDescriptor(pty_fd);
}
}
else
{
if (log)
log->Printf ("Platform::%s Attach() failed: %s", __FUNCTION__, error.AsCString ());
}
}
else
{
if (log)
log->Printf ("Platform::%s LaunchProcess() returned launch_info with invalid process id", __FUNCTION__);
}
}
else
{
if (log)
log->Printf ("Platform::%s LaunchProcess() failed: %s", __FUNCTION__, error.AsCString ());
}
return process_sp;
}
lldb::PlatformSP
Platform::GetPlatformForArchitecture (const ArchSpec &arch, ArchSpec *platform_arch_ptr)
{
lldb::PlatformSP platform_sp;
Error error;
if (arch.IsValid())
platform_sp = Platform::Create (arch, platform_arch_ptr, error);
return platform_sp;
}
//------------------------------------------------------------------
/// Lets a platform answer if it is compatible with a given
/// architecture and the target triple contained within.
//------------------------------------------------------------------
bool
Platform::IsCompatibleArchitecture (const ArchSpec &arch, bool exact_arch_match, ArchSpec *compatible_arch_ptr)
{
// If the architecture is invalid, we must answer true...
if (arch.IsValid())
{
ArchSpec platform_arch;
// Try for an exact architecture match first.
if (exact_arch_match)
{
for (uint32_t arch_idx=0; GetSupportedArchitectureAtIndex (arch_idx, platform_arch); ++arch_idx)
{
if (arch.IsExactMatch(platform_arch))
{
if (compatible_arch_ptr)
*compatible_arch_ptr = platform_arch;
return true;
}
}
}
else
{
for (uint32_t arch_idx=0; GetSupportedArchitectureAtIndex (arch_idx, platform_arch); ++arch_idx)
{
if (arch.IsCompatibleMatch(platform_arch))
{
if (compatible_arch_ptr)
*compatible_arch_ptr = platform_arch;
return true;
}
}
}
}
if (compatible_arch_ptr)
compatible_arch_ptr->Clear();
return false;
}
Error
Platform::PutFile (const FileSpec& source,
const FileSpec& destination,
uint32_t uid,
uint32_t gid)
{
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM));
if (log)
log->Printf("[PutFile] Using block by block transfer....\n");
uint32_t source_open_options = File::eOpenOptionRead | File::eOpenOptionCloseOnExec;
if (source.GetFileType() == FileSpec::eFileTypeSymbolicLink)
source_open_options |= File::eOpenoptionDontFollowSymlinks;
File source_file(source, source_open_options, lldb::eFilePermissionsUserRW);
Error error;
uint32_t permissions = source_file.GetPermissions(error);
if (permissions == 0)
permissions = lldb::eFilePermissionsFileDefault;
if (!source_file.IsValid())
return Error("PutFile: unable to open source file");
lldb::user_id_t dest_file = OpenFile (destination,
File::eOpenOptionCanCreate |
File::eOpenOptionWrite |
File::eOpenOptionTruncate |
File::eOpenOptionCloseOnExec,
permissions,
error);
if (log)
log->Printf ("dest_file = %" PRIu64 "\n", dest_file);
if (error.Fail())
return error;
if (dest_file == UINT64_MAX)
return Error("unable to open target file");
lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0));
uint64_t offset = 0;
for (;;)
{
size_t bytes_read = buffer_sp->GetByteSize();
error = source_file.Read(buffer_sp->GetBytes(), bytes_read);
if (error.Fail() || bytes_read == 0)
break;
const uint64_t bytes_written = WriteFile(dest_file, offset,
buffer_sp->GetBytes(), bytes_read, error);
if (error.Fail())
break;
offset += bytes_written;
if (bytes_written != bytes_read)
{
// We didn't write the correct number of bytes, so adjust
// the file position in the source file we are reading from...
source_file.SeekFromStart(offset);
}
}
CloseFile(dest_file, error);
if (uid == UINT32_MAX && gid == UINT32_MAX)
return error;
// TODO: ChownFile?
return error;
}
Error
Platform::GetFile(const FileSpec &source,
const FileSpec &destination)
{
Error error("unimplemented");
return error;
}
Error
Platform::CreateSymlink(const FileSpec &src, // The name of the link is in src
const FileSpec &dst) // The symlink points to dst
{
Error error("unimplemented");
return error;
}
bool
Platform::GetFileExists(const lldb_private::FileSpec &file_spec)
{
return false;
}
Error
Platform::Unlink(const FileSpec &path)
{
Error error("unimplemented");
return error;
}
uint64_t
Platform::ConvertMmapFlagsToPlatform(const ArchSpec &arch, unsigned flags)
{
uint64_t flags_platform = 0;
if (flags & eMmapFlagsPrivate)
flags_platform |= MAP_PRIVATE;
if (flags & eMmapFlagsAnon)
flags_platform |= MAP_ANON;
return flags_platform;
}
lldb_private::Error
Platform::RunShellCommand(const char *command, // Shouldn't be NULL
const FileSpec &working_dir, // Pass empty FileSpec to use the current working directory
int *status_ptr, // Pass NULL if you don't want the process exit status
int *signo_ptr, // Pass NULL if you don't want the signal that caused the process to exit
std::string *command_output, // Pass NULL if you don't want the command output
uint32_t timeout_sec) // Timeout in seconds to wait for shell program to finish
{
if (IsHost())
return Host::RunShellCommand (command, working_dir, status_ptr, signo_ptr, command_output, timeout_sec);
else
return Error("unimplemented");
}
bool
Platform::CalculateMD5 (const FileSpec& file_spec,
uint64_t &low,
uint64_t &high)
{
if (IsHost())
return FileSystem::CalculateMD5(file_spec, low, high);
else
return false;
}
void
Platform::SetLocalCacheDirectory (const char* local)
{
m_local_cache_directory.assign(local);
}
const char*
Platform::GetLocalCacheDirectory ()
{
return m_local_cache_directory.c_str();
}
static OptionDefinition
g_rsync_option_table[] =
{
{ LLDB_OPT_SET_ALL, false, "rsync" , 'r', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone , "Enable rsync." },
{ LLDB_OPT_SET_ALL, false, "rsync-opts" , 'R', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeCommandName , "Platform-specific options required for rsync to work." },
{ LLDB_OPT_SET_ALL, false, "rsync-prefix" , 'P', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeCommandName , "Platform-specific rsync prefix put before the remote path." },
{ LLDB_OPT_SET_ALL, false, "ignore-remote-hostname" , 'i', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone , "Do not automatically fill in the remote hostname when composing the rsync command." },
};
static OptionDefinition
g_ssh_option_table[] =
{
{ LLDB_OPT_SET_ALL, false, "ssh" , 's', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone , "Enable SSH." },
{ LLDB_OPT_SET_ALL, false, "ssh-opts" , 'S', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeCommandName , "Platform-specific options required for SSH to work." },
};
static OptionDefinition
g_caching_option_table[] =
{
{ LLDB_OPT_SET_ALL, false, "local-cache-dir" , 'c', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypePath , "Path in which to store local copies of files." },
};
OptionGroupPlatformRSync::OptionGroupPlatformRSync ()
{
}
OptionGroupPlatformRSync::~OptionGroupPlatformRSync ()
{
}
const lldb_private::OptionDefinition*
OptionGroupPlatformRSync::GetDefinitions ()
{
return g_rsync_option_table;
}
void
OptionGroupPlatformRSync::OptionParsingStarting (CommandInterpreter &interpreter)
{
m_rsync = false;
m_rsync_opts.clear();
m_rsync_prefix.clear();
m_ignores_remote_hostname = false;
}
lldb_private::Error
OptionGroupPlatformRSync::SetOptionValue (CommandInterpreter &interpreter,
uint32_t option_idx,
const char *option_arg)
{
Error error;
char short_option = (char) GetDefinitions()[option_idx].short_option;
switch (short_option)
{
case 'r':
m_rsync = true;
break;
case 'R':
m_rsync_opts.assign(option_arg);
break;
case 'P':
m_rsync_prefix.assign(option_arg);
break;
case 'i':
m_ignores_remote_hostname = true;
break;
default:
error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option);
break;
}
return error;
}
uint32_t
OptionGroupPlatformRSync::GetNumDefinitions ()
{
return llvm::array_lengthof(g_rsync_option_table);
}
lldb::BreakpointSP
Platform::SetThreadCreationBreakpoint (lldb_private::Target &target)
{
return lldb::BreakpointSP();
}
OptionGroupPlatformSSH::OptionGroupPlatformSSH ()
{
}
OptionGroupPlatformSSH::~OptionGroupPlatformSSH ()
{
}
const lldb_private::OptionDefinition*
OptionGroupPlatformSSH::GetDefinitions ()
{
return g_ssh_option_table;
}
void
OptionGroupPlatformSSH::OptionParsingStarting (CommandInterpreter &interpreter)
{
m_ssh = false;
m_ssh_opts.clear();
}
lldb_private::Error
OptionGroupPlatformSSH::SetOptionValue (CommandInterpreter &interpreter,
uint32_t option_idx,
const char *option_arg)
{
Error error;
char short_option = (char) GetDefinitions()[option_idx].short_option;
switch (short_option)
{
case 's':
m_ssh = true;
break;
case 'S':
m_ssh_opts.assign(option_arg);
break;
default:
error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option);
break;
}
return error;
}
uint32_t
OptionGroupPlatformSSH::GetNumDefinitions ()
{
return llvm::array_lengthof(g_ssh_option_table);
}
OptionGroupPlatformCaching::OptionGroupPlatformCaching ()
{
}
OptionGroupPlatformCaching::~OptionGroupPlatformCaching ()
{
}
const lldb_private::OptionDefinition*
OptionGroupPlatformCaching::GetDefinitions ()
{
return g_caching_option_table;
}
void
OptionGroupPlatformCaching::OptionParsingStarting (CommandInterpreter &interpreter)
{
m_cache_dir.clear();
}
lldb_private::Error
OptionGroupPlatformCaching::SetOptionValue (CommandInterpreter &interpreter,
uint32_t option_idx,
const char *option_arg)
{
Error error;
char short_option = (char) GetDefinitions()[option_idx].short_option;
switch (short_option)
{
case 'c':
m_cache_dir.assign(option_arg);
break;
default:
error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option);
break;
}
return error;
}
uint32_t
OptionGroupPlatformCaching::GetNumDefinitions ()
{
return llvm::array_lengthof(g_caching_option_table);
}
size_t
Platform::GetEnvironment (StringList &environment)
{
environment.Clear();
return false;
}
const std::vector<ConstString> &
Platform::GetTrapHandlerSymbolNames ()
{
if (!m_calculated_trap_handlers)
{
Mutex::Locker locker (m_mutex);
if (!m_calculated_trap_handlers)
{
CalculateTrapHandlerSymbolNames();
m_calculated_trap_handlers = true;
}
}
return m_trap_handlers;
}
Error
Platform::GetCachedExecutable (ModuleSpec &module_spec,
lldb::ModuleSP &module_sp,
const FileSpecList *module_search_paths_ptr,
Platform &remote_platform)
{
const auto platform_spec = module_spec.GetFileSpec ();
const auto error = LoadCachedExecutable (module_spec,
module_sp,
module_search_paths_ptr,
remote_platform);
if (error.Success ())
{
module_spec.GetFileSpec () = module_sp->GetFileSpec ();
module_spec.GetPlatformFileSpec () = platform_spec;
}
return error;
}
Error
Platform::LoadCachedExecutable (const ModuleSpec &module_spec,
lldb::ModuleSP &module_sp,
const FileSpecList *module_search_paths_ptr,
Platform &remote_platform)
{
return GetRemoteSharedModule (module_spec,
nullptr,
module_sp,
[&](const ModuleSpec &spec)
{
return remote_platform.ResolveExecutable (
spec, module_sp, module_search_paths_ptr);
},
nullptr);
}
Error
Platform::GetRemoteSharedModule (const ModuleSpec &module_spec,
Process* process,
lldb::ModuleSP &module_sp,
const ModuleResolver &module_resolver,
bool *did_create_ptr)
{
// Get module information from a target.
ModuleSpec resolved_module_spec;
bool got_module_spec = false;
if (process)
{
// Try to get module information from the process
if (process->GetModuleSpec (module_spec.GetFileSpec (), module_spec.GetArchitecture (), resolved_module_spec))
got_module_spec = true;
}
if (!got_module_spec)
{
// Get module information from a target.
if (!GetModuleSpec (module_spec.GetFileSpec (), module_spec.GetArchitecture (), resolved_module_spec))
return module_resolver (module_spec);
}
// Trying to find a module by UUID on local file system.
const auto error = module_resolver (resolved_module_spec);
if (error.Fail ())
{
if (GetCachedSharedModule (resolved_module_spec, module_sp, did_create_ptr))
return Error ();
}
return error;
}
bool
Platform::GetCachedSharedModule (const ModuleSpec &module_spec,
lldb::ModuleSP &module_sp,
bool *did_create_ptr)
{
if (IsHost() ||
!GetGlobalPlatformProperties ()->GetUseModuleCache () ||
!GetGlobalPlatformProperties ()->GetModuleCacheDirectory ())
return false;
Log *log = GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PLATFORM);
// Check local cache for a module.
auto error = m_module_cache->GetAndPut (
GetModuleCacheRoot (),
GetCacheHostname (),
module_spec,
[this](const ModuleSpec &module_spec, const FileSpec &tmp_download_file_spec)
{
return DownloadModuleSlice (module_spec.GetFileSpec (),
module_spec.GetObjectOffset (),
module_spec.GetObjectSize (),
tmp_download_file_spec);
},
[this](const ModuleSP& module_sp, const FileSpec& tmp_download_file_spec)
{
return DownloadSymbolFile (module_sp, tmp_download_file_spec);
},
module_sp,
did_create_ptr);
if (error.Success ())
return true;
if (log)
log->Printf("Platform::%s - module %s not found in local cache: %s",
__FUNCTION__, module_spec.GetUUID ().GetAsString ().c_str (), error.AsCString ());
return false;
}
Error
Platform::DownloadModuleSlice (const FileSpec& src_file_spec,
const uint64_t src_offset,
const uint64_t src_size,
const FileSpec& dst_file_spec)
{
Error error;
std::ofstream dst (dst_file_spec.GetPath(), std::ios::out | std::ios::binary);
if (!dst.is_open())
{
error.SetErrorStringWithFormat ("unable to open destination file: %s", dst_file_spec.GetPath ().c_str ());
return error;
}
auto src_fd = OpenFile (src_file_spec,
File::eOpenOptionRead,
lldb::eFilePermissionsFileDefault,
error);
if (error.Fail ())
{
error.SetErrorStringWithFormat ("unable to open source file: %s", error.AsCString ());
return error;
}
std::vector<char> buffer (1024);
auto offset = src_offset;
uint64_t total_bytes_read = 0;
while (total_bytes_read < src_size)
{
const auto to_read = std::min (static_cast<uint64_t>(buffer.size ()), src_size - total_bytes_read);
const uint64_t n_read = ReadFile (src_fd, offset, &buffer[0], to_read, error);
if (error.Fail ())
break;
if (n_read == 0)
{
error.SetErrorString ("read 0 bytes");
break;
}
offset += n_read;
total_bytes_read += n_read;
dst.write (&buffer[0], n_read);
}
Error close_error;
CloseFile (src_fd, close_error); // Ignoring close error.
return error;
}
Error
Platform::DownloadSymbolFile (const lldb::ModuleSP& module_sp, const FileSpec& dst_file_spec)
{
return Error ("Symbol file downloading not supported by the default platform.");
}
FileSpec
Platform::GetModuleCacheRoot ()
{
auto dir_spec = GetGlobalPlatformProperties ()->GetModuleCacheDirectory ();
dir_spec.AppendPathComponent (GetName ().AsCString ());
return dir_spec;
}
const char *
Platform::GetCacheHostname ()
{
return GetHostname ();
}
const UnixSignalsSP &
Platform::GetRemoteUnixSignals()
{
static const auto s_default_unix_signals_sp = std::make_shared<UnixSignals>();
return s_default_unix_signals_sp;
}
const UnixSignalsSP &
Platform::GetUnixSignals()
{
if (IsHost())
return Host::GetUnixSignals();
return GetRemoteUnixSignals();
}
uint32_t
Platform::LoadImage(lldb_private::Process* process,
const lldb_private::FileSpec& local_file,
const lldb_private::FileSpec& remote_file,
lldb_private::Error& error)
{
if (local_file && remote_file)
{
// Both local and remote file was specified. Install the local file to the given location.
if (IsRemote() || local_file != remote_file)
{
error = Install(local_file, remote_file);
if (error.Fail())
return LLDB_INVALID_IMAGE_TOKEN;
}
return DoLoadImage(process, remote_file, error);
}
if (local_file)
{
// Only local file was specified. Install it to the current working directory.
FileSpec target_file = GetWorkingDirectory();
target_file.AppendPathComponent(local_file.GetFilename().AsCString());
if (IsRemote() || local_file != target_file)
{
error = Install(local_file, target_file);
if (error.Fail())
return LLDB_INVALID_IMAGE_TOKEN;
}
return DoLoadImage(process, target_file, error);
}
if (remote_file)
{
// Only remote file was specified so we don't have to do any copying
return DoLoadImage(process, remote_file, error);
}
error.SetErrorString("Neither local nor remote file was specified");
return LLDB_INVALID_IMAGE_TOKEN;
}
uint32_t
Platform::DoLoadImage (lldb_private::Process* process,
const lldb_private::FileSpec& remote_file,
lldb_private::Error& error)
{
error.SetErrorString("LoadImage is not supported on the current platform");
return LLDB_INVALID_IMAGE_TOKEN;
}
Error
Platform::UnloadImage(lldb_private::Process* process, uint32_t image_token)
{
return Error("UnloadImage is not supported on the current platform");
}
lldb::ProcessSP
Platform::ConnectProcess(const char* connect_url,
const char* plugin_name,
lldb_private::Debugger &debugger,
lldb_private::Target *target,
lldb_private::Error &error)
{
error.Clear();
if (!target)
{
TargetSP new_target_sp;
error = debugger.GetTargetList().CreateTarget(debugger,
nullptr,
nullptr,
false,
nullptr,
new_target_sp);
target = new_target_sp.get();
}
if (!target || error.Fail())
return nullptr;
debugger.GetTargetList().SetSelectedTarget(target);
lldb::ProcessSP process_sp = target->CreateProcess(debugger.GetListener(),
plugin_name,
nullptr);
if (!process_sp)
return nullptr;
error = process_sp->ConnectRemote(debugger.GetOutputFile().get(), connect_url);
if (error.Fail())
return nullptr;
return process_sp;
}
size_t
Platform::ConnectToWaitingProcesses(lldb_private::Debugger& debugger, lldb_private::Error& error)
{
error.Clear();
return 0;
}