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

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

441 lines
17 KiB
C++
Raw Normal View History

//===-- RemoteAwarePlatform.cpp -------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "lldb/Target/RemoteAwarePlatform.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleList.h"
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Host/FileCache.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/Host/Host.h"
#include "lldb/Host/HostInfo.h"
#include "lldb/Utility/StreamString.h"
using namespace lldb_private;
using namespace lldb;
bool RemoteAwarePlatform::GetModuleSpec(const FileSpec &module_file_spec,
const ArchSpec &arch,
ModuleSpec &module_spec) {
if (m_remote_platform_sp)
return m_remote_platform_sp->GetModuleSpec(module_file_spec, arch,
module_spec);
return false;
}
Status RemoteAwarePlatform::ResolveExecutable(
const ModuleSpec &module_spec, ModuleSP &exe_module_sp,
const FileSpecList *module_search_paths_ptr) {
Status error;
// Nothing special to do here, just use the actual file and architecture
char exe_path[PATH_MAX];
ModuleSpec resolved_module_spec(module_spec);
if (IsHost()) {
// If we have "ls" as the exe_file, resolve the executable location based
// on the current path variables
if (!FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec())) {
resolved_module_spec.GetFileSpec().GetPath(exe_path, sizeof(exe_path));
resolved_module_spec.GetFileSpec().SetFile(exe_path,
FileSpec::Style::native);
FileSystem::Instance().Resolve(resolved_module_spec.GetFileSpec());
}
if (!FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec()))
FileSystem::Instance().ResolveExecutableLocation(
resolved_module_spec.GetFileSpec());
// Resolve any executable within a bundle on MacOSX
Host::ResolveExecutableInBundle(resolved_module_spec.GetFileSpec());
if (FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec()))
error.Clear();
else {
const uint32_t permissions = FileSystem::Instance().GetPermissions(
resolved_module_spec.GetFileSpec());
if (permissions && (permissions & eFilePermissionsEveryoneR) == 0)
error.SetErrorStringWithFormat(
"executable '%s' is not readable",
resolved_module_spec.GetFileSpec().GetPath().c_str());
else
error.SetErrorStringWithFormat(
"unable to find executable for '%s'",
resolved_module_spec.GetFileSpec().GetPath().c_str());
}
} else {
if (m_remote_platform_sp) {
return GetCachedExecutable(resolved_module_spec, exe_module_sp,
[lldb] Refactor Platform::ResolveExecutable Module resolution is probably the most complex piece of lldb [citation needed], with numerous levels of abstraction, each one implementing various retry and fallback strategies. It is also a very repetitive, with only small differences between "host", "remote-and-connected" and "remote-but-not-(yet)-connected" scenarios. The goal of this patch (first in series) is to reduce the number of abstractions, and deduplicate the code. One of the reasons for this complexity is the tension between the desire to offload the process of module resolution to the remote platform instance (that's how most other platform methods work), and the desire to keep it local to the outer platform class (its easier to subclass the outer class, and it generally makes more sense). This patch resolves that conflict in favour of doing everything in the outer class. The gdb-remote (our only remote platform) implementation of ResolveExecutable was not doing anything gdb-specific, and was rather similar to the other implementations of that method (any divergence is most likely the result of fixes not being applied everywhere rather than intentional). It does this by excising the remote platform out of the resolution codepath. The gdb-remote implementation of ResolveExecutable is moved to Platform::ResolveRemoteExecutable, and the (only) call site is redirected to that. On its own, this does not achieve (much), but it creates new opportunities for layer peeling and code sharing, since all of the code now lives closer together. Differential Revision: https://reviews.llvm.org/D113487
2021-11-09 23:32:57 +08:00
module_search_paths_ptr);
}
// We may connect to a process and use the provided executable (Don't use
// local $PATH).
// Resolve any executable within a bundle on MacOSX
Host::ResolveExecutableInBundle(resolved_module_spec.GetFileSpec());
if (FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec()))
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,
module_search_paths_ptr, nullptr, nullptr);
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, module_search_paths_ptr, nullptr, nullptr);
}
}
// TODO find out why exe_module_sp might be NULL
if (error.Fail() || !exe_module_sp || !exe_module_sp->GetObjectFile()) {
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;
llvm::ListSeparator LS;
for (const ArchSpec &arch : GetSupportedArchitectures()) {
resolved_module_spec.GetArchitecture() = arch;
error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp,
module_search_paths_ptr, nullptr, nullptr);
// Did we find an executable using one of the
if (error.Success()) {
if (exe_module_sp && exe_module_sp->GetObjectFile())
break;
else
error.SetErrorToGenericError();
}
arch_names << LS << arch.GetArchitectureName();
}
if (error.Fail() || !exe_module_sp) {
if (FileSystem::Instance().Readable(
resolved_module_spec.GetFileSpec())) {
error.SetErrorStringWithFormatv(
"'{0}' doesn't contain any '{1}' platform architectures: {2}",
resolved_module_spec.GetFileSpec(), GetPluginName(),
arch_names.GetData());
} else {
error.SetErrorStringWithFormat(
"'%s' is not readable",
resolved_module_spec.GetFileSpec().GetPath().c_str());
}
}
}
}
return error;
}
Status RemoteAwarePlatform::RunShellCommand(
llvm::StringRef command, const FileSpec &working_dir, int *status_ptr,
int *signo_ptr, std::string *command_output,
const Timeout<std::micro> &timeout) {
return RunShellCommand(llvm::StringRef(), command, working_dir, status_ptr,
signo_ptr, command_output, timeout);
}
Status RemoteAwarePlatform::RunShellCommand(
llvm::StringRef shell, llvm::StringRef command, const FileSpec &working_dir,
int *status_ptr, int *signo_ptr, std::string *command_output,
const Timeout<std::micro> &timeout) {
if (IsHost())
return Host::RunShellCommand(shell, command, working_dir, status_ptr,
signo_ptr, command_output, timeout);
if (m_remote_platform_sp)
return m_remote_platform_sp->RunShellCommand(shell, command, working_dir,
status_ptr, signo_ptr,
command_output, timeout);
return Status("unable to run a remote command without a platform");
}
Status RemoteAwarePlatform::MakeDirectory(const FileSpec &file_spec,
uint32_t file_permissions) {
if (m_remote_platform_sp)
return m_remote_platform_sp->MakeDirectory(file_spec, file_permissions);
return Platform::MakeDirectory(file_spec, file_permissions);
}
Status RemoteAwarePlatform::GetFilePermissions(const FileSpec &file_spec,
uint32_t &file_permissions) {
if (m_remote_platform_sp)
return m_remote_platform_sp->GetFilePermissions(file_spec,
file_permissions);
return Platform::GetFilePermissions(file_spec, file_permissions);
}
Status RemoteAwarePlatform::SetFilePermissions(const FileSpec &file_spec,
uint32_t file_permissions) {
if (m_remote_platform_sp)
return m_remote_platform_sp->SetFilePermissions(file_spec,
file_permissions);
return Platform::SetFilePermissions(file_spec, file_permissions);
}
lldb::user_id_t RemoteAwarePlatform::OpenFile(const FileSpec &file_spec,
File::OpenOptions flags,
uint32_t mode, Status &error) {
if (IsHost())
return FileCache::GetInstance().OpenFile(file_spec, flags, mode, error);
if (m_remote_platform_sp)
return m_remote_platform_sp->OpenFile(file_spec, flags, mode, error);
return Platform::OpenFile(file_spec, flags, mode, error);
}
bool RemoteAwarePlatform::CloseFile(lldb::user_id_t fd, Status &error) {
if (IsHost())
return FileCache::GetInstance().CloseFile(fd, error);
if (m_remote_platform_sp)
return m_remote_platform_sp->CloseFile(fd, error);
return Platform::CloseFile(fd, error);
}
uint64_t RemoteAwarePlatform::ReadFile(lldb::user_id_t fd, uint64_t offset,
void *dst, uint64_t dst_len,
Status &error) {
if (IsHost())
return FileCache::GetInstance().ReadFile(fd, offset, dst, dst_len, error);
if (m_remote_platform_sp)
return m_remote_platform_sp->ReadFile(fd, offset, dst, dst_len, error);
return Platform::ReadFile(fd, offset, dst, dst_len, error);
}
uint64_t RemoteAwarePlatform::WriteFile(lldb::user_id_t fd, uint64_t offset,
const void *src, uint64_t src_len,
Status &error) {
if (IsHost())
return FileCache::GetInstance().WriteFile(fd, offset, src, src_len, error);
if (m_remote_platform_sp)
return m_remote_platform_sp->WriteFile(fd, offset, src, src_len, error);
return Platform::WriteFile(fd, offset, src, src_len, error);
}
lldb::user_id_t RemoteAwarePlatform::GetFileSize(const FileSpec &file_spec) {
if (IsHost()) {
uint64_t Size;
if (llvm::sys::fs::file_size(file_spec.GetPath(), Size))
return 0;
return Size;
}
if (m_remote_platform_sp)
return m_remote_platform_sp->GetFileSize(file_spec);
return Platform::GetFileSize(file_spec);
}
Status RemoteAwarePlatform::CreateSymlink(const FileSpec &src,
const FileSpec &dst) {
if (IsHost())
return FileSystem::Instance().Symlink(src, dst);
if (m_remote_platform_sp)
return m_remote_platform_sp->CreateSymlink(src, dst);
return Platform::CreateSymlink(src, dst);
}
bool RemoteAwarePlatform::GetFileExists(const FileSpec &file_spec) {
if (IsHost())
return FileSystem::Instance().Exists(file_spec);
if (m_remote_platform_sp)
return m_remote_platform_sp->GetFileExists(file_spec);
return Platform::GetFileExists(file_spec);
}
Status RemoteAwarePlatform::Unlink(const FileSpec &file_spec) {
if (IsHost())
return llvm::sys::fs::remove(file_spec.GetPath());
if (m_remote_platform_sp)
return m_remote_platform_sp->Unlink(file_spec);
return Platform::Unlink(file_spec);
}
bool RemoteAwarePlatform::CalculateMD5(const FileSpec &file_spec, uint64_t &low,
uint64_t &high) {
if (IsHost())
return Platform::CalculateMD5(file_spec, low, high);
if (m_remote_platform_sp)
return m_remote_platform_sp->CalculateMD5(file_spec, low, high);
return false;
}
FileSpec RemoteAwarePlatform::GetRemoteWorkingDirectory() {
if (IsRemote() && m_remote_platform_sp)
return m_remote_platform_sp->GetRemoteWorkingDirectory();
return Platform::GetRemoteWorkingDirectory();
}
bool RemoteAwarePlatform::SetRemoteWorkingDirectory(
const FileSpec &working_dir) {
if (IsRemote() && m_remote_platform_sp)
return m_remote_platform_sp->SetRemoteWorkingDirectory(working_dir);
return Platform::SetRemoteWorkingDirectory(working_dir);
}
Status RemoteAwarePlatform::GetFileWithUUID(const FileSpec &platform_file,
const UUID *uuid_ptr,
FileSpec &local_file) {
if (IsRemote() && 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 Status();
}
bool RemoteAwarePlatform::GetRemoteOSVersion() {
if (m_remote_platform_sp) {
m_os_version = m_remote_platform_sp->GetOSVersion();
return !m_os_version.empty();
}
return false;
}
llvm::Optional<std::string> RemoteAwarePlatform::GetRemoteOSBuildString() {
if (m_remote_platform_sp)
return m_remote_platform_sp->GetRemoteOSBuildString();
return llvm::None;
}
llvm::Optional<std::string> RemoteAwarePlatform::GetRemoteOSKernelDescription() {
if (m_remote_platform_sp)
return m_remote_platform_sp->GetRemoteOSKernelDescription();
return llvm::None;
}
ArchSpec RemoteAwarePlatform::GetRemoteSystemArchitecture() {
if (m_remote_platform_sp)
return m_remote_platform_sp->GetRemoteSystemArchitecture();
return ArchSpec();
}
const char *RemoteAwarePlatform::GetHostname() {
if (IsHost())
return Platform::GetHostname();
if (m_remote_platform_sp)
return m_remote_platform_sp->GetHostname();
return nullptr;
}
UserIDResolver &RemoteAwarePlatform::GetUserIDResolver() {
if (IsHost())
return HostInfo::GetUserIDResolver();
if (m_remote_platform_sp)
return m_remote_platform_sp->GetUserIDResolver();
return UserIDResolver::GetNoopResolver();
}
Environment RemoteAwarePlatform::GetEnvironment() {
if (IsRemote()) {
if (m_remote_platform_sp)
return m_remote_platform_sp->GetEnvironment();
return Environment();
}
return Host::GetEnvironment();
}
bool RemoteAwarePlatform::IsConnected() const {
if (IsHost())
return true;
else if (m_remote_platform_sp)
return m_remote_platform_sp->IsConnected();
return false;
}
bool RemoteAwarePlatform::GetProcessInfo(lldb::pid_t pid,
ProcessInstanceInfo &process_info) {
if (IsHost())
return Platform::GetProcessInfo(pid, process_info);
if (m_remote_platform_sp)
return m_remote_platform_sp->GetProcessInfo(pid, process_info);
return false;
}
uint32_t
RemoteAwarePlatform::FindProcesses(const ProcessInstanceInfoMatch &match_info,
ProcessInstanceInfoList &process_infos) {
if (IsHost())
return Platform::FindProcesses(match_info, process_infos);
if (m_remote_platform_sp)
return m_remote_platform_sp->FindProcesses(match_info, process_infos);
return 0;
}
lldb::ProcessSP RemoteAwarePlatform::ConnectProcess(llvm::StringRef connect_url,
llvm::StringRef plugin_name,
Debugger &debugger,
Target *target,
Status &error) {
if (m_remote_platform_sp)
return m_remote_platform_sp->ConnectProcess(connect_url, plugin_name,
debugger, target, error);
return Platform::ConnectProcess(connect_url, plugin_name, debugger, target,
error);
}
Status RemoteAwarePlatform::LaunchProcess(ProcessLaunchInfo &launch_info) {
Status error;
if (IsHost()) {
error = Platform::LaunchProcess(launch_info);
} else {
if (m_remote_platform_sp)
error = m_remote_platform_sp->LaunchProcess(launch_info);
else
error.SetErrorString("the platform is not currently connected");
}
return error;
}
Status RemoteAwarePlatform::KillProcess(const lldb::pid_t pid) {
if (IsHost())
return Platform::KillProcess(pid);
if (m_remote_platform_sp)
return m_remote_platform_sp->KillProcess(pid);
return Status("the platform is not currently connected");
}
size_t RemoteAwarePlatform::ConnectToWaitingProcesses(Debugger &debugger,
Status &error) {
if (m_remote_platform_sp)
return m_remote_platform_sp->ConnectToWaitingProcesses(debugger, error);
return Platform::ConnectToWaitingProcesses(debugger, error);
}