Files
clang-p2996/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
Jim Ingham 8d94ba0fb1 This change introduces a "ExpressionExecutionThread" to the ThreadList.
Turns out that most of the code that runs expressions (e.g. the ObjC runtime grubber) on
behalf of the expression parser was using the currently selected thread.  But sometimes,
e.g. when we are evaluating breakpoint conditions/commands, we don't select the thread
we're running on, we instead set the context for the interpreter, and explicitly pass
that to other callers.  That wasn't getting communicated to these utility expressions, so
they would run on some other thread instead, and that could cause a variety of subtle and
hard to reproduce problems.  

I also went through the commands and cleaned up the use of GetSelectedThread.  All those
uses should have been trying the thread in the m_exe_ctx belonging to the command object
first.  It would actually have been pretty hard to get misbehavior in these cases, but for
correctness sake it is good to make this usage consistent.

<rdar://problem/24978569>

llvm-svn: 263326
2016-03-12 02:45:34 +00:00

1024 lines
34 KiB
C++

//===-- PlatformPOSIX.cpp ---------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "PlatformPOSIX.h"
// C Includes
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "lldb/Core/DataBufferHeap.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/StreamString.h"
#include "lldb/Core/ValueObject.h"
#include "lldb/Expression/UserExpression.h"
#include "lldb/Host/File.h"
#include "lldb/Host/FileCache.h"
#include "lldb/Host/FileSpec.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/Host/Host.h"
#include "lldb/Target/DynamicLoader.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/ProcessLaunchInfo.h"
#include "lldb/Target/Thread.h"
using namespace lldb;
using namespace lldb_private;
//------------------------------------------------------------------
/// Default Constructor
//------------------------------------------------------------------
PlatformPOSIX::PlatformPOSIX (bool is_host) :
Platform(is_host), // This is the local host platform
m_option_group_platform_rsync(new OptionGroupPlatformRSync()),
m_option_group_platform_ssh(new OptionGroupPlatformSSH()),
m_option_group_platform_caching(new OptionGroupPlatformCaching()),
m_remote_platform_sp ()
{
}
//------------------------------------------------------------------
/// Destructor.
///
/// The destructor is virtual since this class is designed to be
/// inherited from by the plug-in instance.
//------------------------------------------------------------------
PlatformPOSIX::~PlatformPOSIX()
{
}
bool
PlatformPOSIX::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 Platform::GetModuleSpec (module_file_spec, arch, module_spec);
}
lldb_private::OptionGroupOptions*
PlatformPOSIX::GetConnectionOptions (lldb_private::CommandInterpreter& interpreter)
{
auto iter = m_options.find(&interpreter), end = m_options.end();
if (iter == end)
{
std::unique_ptr<lldb_private::OptionGroupOptions> options(new OptionGroupOptions(interpreter));
options->Append(m_option_group_platform_rsync.get());
options->Append(m_option_group_platform_ssh.get());
options->Append(m_option_group_platform_caching.get());
m_options[&interpreter] = std::move(options);
}
return m_options.at(&interpreter).get();
}
bool
PlatformPOSIX::IsConnected () const
{
if (IsHost())
return true;
else if (m_remote_platform_sp)
return m_remote_platform_sp->IsConnected();
return false;
}
lldb_private::Error
PlatformPOSIX::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
{
if (m_remote_platform_sp)
return m_remote_platform_sp->RunShellCommand(command, working_dir, status_ptr, signo_ptr, command_output, timeout_sec);
else
return Error("unable to run a remote command without a platform");
}
}
Error
PlatformPOSIX::MakeDirectory(const FileSpec &file_spec, uint32_t file_permissions)
{
if (m_remote_platform_sp)
return m_remote_platform_sp->MakeDirectory(file_spec, file_permissions);
else
return Platform::MakeDirectory(file_spec ,file_permissions);
}
Error
PlatformPOSIX::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions)
{
if (m_remote_platform_sp)
return m_remote_platform_sp->GetFilePermissions(file_spec, file_permissions);
else
return Platform::GetFilePermissions(file_spec ,file_permissions);
}
Error
PlatformPOSIX::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions)
{
if (m_remote_platform_sp)
return m_remote_platform_sp->SetFilePermissions(file_spec, file_permissions);
else
return Platform::SetFilePermissions(file_spec, file_permissions);
}
lldb::user_id_t
PlatformPOSIX::OpenFile (const FileSpec& file_spec,
uint32_t flags,
uint32_t mode,
Error &error)
{
if (IsHost())
return FileCache::GetInstance().OpenFile(file_spec, flags, mode, error);
else if (m_remote_platform_sp)
return m_remote_platform_sp->OpenFile(file_spec, flags, mode, error);
else
return Platform::OpenFile(file_spec, flags, mode, error);
}
bool
PlatformPOSIX::CloseFile (lldb::user_id_t fd, Error &error)
{
if (IsHost())
return FileCache::GetInstance().CloseFile(fd, error);
else if (m_remote_platform_sp)
return m_remote_platform_sp->CloseFile(fd, error);
else
return Platform::CloseFile(fd, error);
}
uint64_t
PlatformPOSIX::ReadFile (lldb::user_id_t fd,
uint64_t offset,
void *dst,
uint64_t dst_len,
Error &error)
{
if (IsHost())
return FileCache::GetInstance().ReadFile(fd, offset, dst, dst_len, error);
else if (m_remote_platform_sp)
return m_remote_platform_sp->ReadFile(fd, offset, dst, dst_len, error);
else
return Platform::ReadFile(fd, offset, dst, dst_len, error);
}
uint64_t
PlatformPOSIX::WriteFile (lldb::user_id_t fd,
uint64_t offset,
const void* src,
uint64_t src_len,
Error &error)
{
if (IsHost())
return FileCache::GetInstance().WriteFile(fd, offset, src, src_len, error);
else if (m_remote_platform_sp)
return m_remote_platform_sp->WriteFile(fd, offset, src, src_len, error);
else
return Platform::WriteFile(fd, offset, src, src_len, error);
}
static uint32_t
chown_file(Platform *platform,
const char* path,
uint32_t uid = UINT32_MAX,
uint32_t gid = UINT32_MAX)
{
if (!platform || !path || *path == 0)
return UINT32_MAX;
if (uid == UINT32_MAX && gid == UINT32_MAX)
return 0; // pretend I did chown correctly - actually I just didn't care
StreamString command;
command.PutCString("chown ");
if (uid != UINT32_MAX)
command.Printf("%d",uid);
if (gid != UINT32_MAX)
command.Printf(":%d",gid);
command.Printf("%s",path);
int status;
platform->RunShellCommand(command.GetData(),
NULL,
&status,
NULL,
NULL,
10);
return status;
}
lldb_private::Error
PlatformPOSIX::PutFile (const lldb_private::FileSpec& source,
const lldb_private::FileSpec& destination,
uint32_t uid,
uint32_t gid)
{
Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
if (IsHost())
{
if (FileSpec::Equal(source, destination, true))
return Error();
// cp src dst
// chown uid:gid dst
std::string src_path (source.GetPath());
if (src_path.empty())
return Error("unable to get file path for source");
std::string dst_path (destination.GetPath());
if (dst_path.empty())
return Error("unable to get file path for destination");
StreamString command;
command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str());
int status;
RunShellCommand(command.GetData(),
NULL,
&status,
NULL,
NULL,
10);
if (status != 0)
return Error("unable to perform copy");
if (uid == UINT32_MAX && gid == UINT32_MAX)
return Error();
if (chown_file(this,dst_path.c_str(),uid,gid) != 0)
return Error("unable to perform chown");
return Error();
}
else if (m_remote_platform_sp)
{
if (GetSupportsRSync())
{
std::string src_path (source.GetPath());
if (src_path.empty())
return Error("unable to get file path for source");
std::string dst_path (destination.GetPath());
if (dst_path.empty())
return Error("unable to get file path for destination");
StreamString command;
if (GetIgnoresRemoteHostname())
{
if (!GetRSyncPrefix())
command.Printf("rsync %s %s %s",
GetRSyncOpts(),
src_path.c_str(),
dst_path.c_str());
else
command.Printf("rsync %s %s %s%s",
GetRSyncOpts(),
src_path.c_str(),
GetRSyncPrefix(),
dst_path.c_str());
}
else
command.Printf("rsync %s %s %s:%s",
GetRSyncOpts(),
src_path.c_str(),
GetHostname(),
dst_path.c_str());
if (log)
log->Printf("[PutFile] Running command: %s\n", command.GetData());
int retcode;
Host::RunShellCommand(command.GetData(),
NULL,
&retcode,
NULL,
NULL,
60);
if (retcode == 0)
{
// Don't chown a local file for a remote system
// if (chown_file(this,dst_path.c_str(),uid,gid) != 0)
// return Error("unable to perform chown");
return Error();
}
// if we are still here rsync has failed - let's try the slow way before giving up
}
}
return Platform::PutFile(source,destination,uid,gid);
}
lldb::user_id_t
PlatformPOSIX::GetFileSize (const FileSpec& file_spec)
{
if (IsHost())
return FileSystem::GetFileSize(file_spec);
else if (m_remote_platform_sp)
return m_remote_platform_sp->GetFileSize(file_spec);
else
return Platform::GetFileSize(file_spec);
}
Error
PlatformPOSIX::CreateSymlink(const FileSpec &src, const FileSpec &dst)
{
if (IsHost())
return FileSystem::Symlink(src, dst);
else if (m_remote_platform_sp)
return m_remote_platform_sp->CreateSymlink(src, dst);
else
return Platform::CreateSymlink(src, dst);
}
bool
PlatformPOSIX::GetFileExists (const FileSpec& file_spec)
{
if (IsHost())
return file_spec.Exists();
else if (m_remote_platform_sp)
return m_remote_platform_sp->GetFileExists(file_spec);
else
return Platform::GetFileExists(file_spec);
}
Error
PlatformPOSIX::Unlink(const FileSpec &file_spec)
{
if (IsHost())
return FileSystem::Unlink(file_spec);
else if (m_remote_platform_sp)
return m_remote_platform_sp->Unlink(file_spec);
else
return Platform::Unlink(file_spec);
}
lldb_private::Error
PlatformPOSIX::GetFile(const lldb_private::FileSpec &source, // remote file path
const lldb_private::FileSpec &destination) // local file path
{
Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
// Check the args, first.
std::string src_path (source.GetPath());
if (src_path.empty())
return Error("unable to get file path for source");
std::string dst_path (destination.GetPath());
if (dst_path.empty())
return Error("unable to get file path for destination");
if (IsHost())
{
if (FileSpec::Equal(source, destination, true))
return Error("local scenario->source and destination are the same file path: no operation performed");
// cp src dst
StreamString cp_command;
cp_command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str());
int status;
RunShellCommand(cp_command.GetData(),
NULL,
&status,
NULL,
NULL,
10);
if (status != 0)
return Error("unable to perform copy");
return Error();
}
else if (m_remote_platform_sp)
{
if (GetSupportsRSync())
{
StreamString command;
if (GetIgnoresRemoteHostname())
{
if (!GetRSyncPrefix())
command.Printf("rsync %s %s %s",
GetRSyncOpts(),
src_path.c_str(),
dst_path.c_str());
else
command.Printf("rsync %s %s%s %s",
GetRSyncOpts(),
GetRSyncPrefix(),
src_path.c_str(),
dst_path.c_str());
}
else
command.Printf("rsync %s %s:%s %s",
GetRSyncOpts(),
m_remote_platform_sp->GetHostname(),
src_path.c_str(),
dst_path.c_str());
if (log)
log->Printf("[GetFile] Running command: %s\n", command.GetData());
int retcode;
Host::RunShellCommand(command.GetData(),
NULL,
&retcode,
NULL,
NULL,
60);
if (retcode == 0)
return Error();
// If we are here, rsync has failed - let's try the slow way before giving up
}
// open src and dst
// read/write, read/write, read/write, ...
// close src
// close dst
if (log)
log->Printf("[GetFile] Using block by block transfer....\n");
Error error;
user_id_t fd_src = OpenFile (source,
File::eOpenOptionRead,
lldb::eFilePermissionsFileDefault,
error);
if (fd_src == UINT64_MAX)
return Error("unable to open source file");
uint32_t permissions = 0;
error = GetFilePermissions(source, permissions);
if (permissions == 0)
permissions = lldb::eFilePermissionsFileDefault;
user_id_t fd_dst = FileCache::GetInstance().OpenFile(
destination, File::eOpenOptionCanCreate | File::eOpenOptionWrite | File::eOpenOptionTruncate, permissions,
error);
if (fd_dst == UINT64_MAX)
{
if (error.Success())
error.SetErrorString("unable to open destination file");
}
if (error.Success())
{
lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0));
uint64_t offset = 0;
error.Clear();
while (error.Success())
{
const uint64_t n_read = ReadFile (fd_src,
offset,
buffer_sp->GetBytes(),
buffer_sp->GetByteSize(),
error);
if (error.Fail())
break;
if (n_read == 0)
break;
if (FileCache::GetInstance().WriteFile(fd_dst, offset, buffer_sp->GetBytes(), n_read, error) != n_read)
{
if (!error.Fail())
error.SetErrorString("unable to write to destination file");
break;
}
offset += n_read;
}
}
// Ignore the close error of src.
if (fd_src != UINT64_MAX)
CloseFile(fd_src, error);
// And close the dst file descriptot.
if (fd_dst != UINT64_MAX && !FileCache::GetInstance().CloseFile(fd_dst, error))
{
if (!error.Fail())
error.SetErrorString("unable to close destination file");
}
return error;
}
return Platform::GetFile(source,destination);
}
std::string
PlatformPOSIX::GetPlatformSpecificConnectionInformation()
{
StreamString stream;
if (GetSupportsRSync())
{
stream.PutCString("rsync");
if ( (GetRSyncOpts() && *GetRSyncOpts()) ||
(GetRSyncPrefix() && *GetRSyncPrefix()) ||
GetIgnoresRemoteHostname())
{
stream.Printf(", options: ");
if (GetRSyncOpts() && *GetRSyncOpts())
stream.Printf("'%s' ",GetRSyncOpts());
stream.Printf(", prefix: ");
if (GetRSyncPrefix() && *GetRSyncPrefix())
stream.Printf("'%s' ",GetRSyncPrefix());
if (GetIgnoresRemoteHostname())
stream.Printf("ignore remote-hostname ");
}
}
if (GetSupportsSSH())
{
stream.PutCString("ssh");
if (GetSSHOpts() && *GetSSHOpts())
stream.Printf(", options: '%s' ",GetSSHOpts());
}
if (GetLocalCacheDirectory() && *GetLocalCacheDirectory())
stream.Printf("cache dir: %s",GetLocalCacheDirectory());
if (stream.GetSize())
return stream.GetData();
else
return "";
}
bool
PlatformPOSIX::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;
}
const lldb::UnixSignalsSP &
PlatformPOSIX::GetRemoteUnixSignals() {
if (IsRemote() && m_remote_platform_sp)
return m_remote_platform_sp->GetRemoteUnixSignals();
return Platform::GetRemoteUnixSignals();
}
FileSpec
PlatformPOSIX::GetRemoteWorkingDirectory()
{
if (IsRemote() && m_remote_platform_sp)
return m_remote_platform_sp->GetRemoteWorkingDirectory();
else
return Platform::GetRemoteWorkingDirectory();
}
bool
PlatformPOSIX::SetRemoteWorkingDirectory(const FileSpec &working_dir)
{
if (IsRemote() && m_remote_platform_sp)
return m_remote_platform_sp->SetRemoteWorkingDirectory(working_dir);
else
return Platform::SetRemoteWorkingDirectory(working_dir);
}
bool
PlatformPOSIX::GetRemoteOSVersion ()
{
if (m_remote_platform_sp)
return m_remote_platform_sp->GetOSVersion (m_major_os_version,
m_minor_os_version,
m_update_os_version);
return false;
}
bool
PlatformPOSIX::GetRemoteOSBuildString (std::string &s)
{
if (m_remote_platform_sp)
return m_remote_platform_sp->GetRemoteOSBuildString (s);
s.clear();
return false;
}
size_t
PlatformPOSIX::GetEnvironment (StringList &env)
{
if (IsRemote())
{
if (m_remote_platform_sp)
return m_remote_platform_sp->GetEnvironment(env);
return 0;
}
return Host::GetEnvironment(env);
}
bool
PlatformPOSIX::GetRemoteOSKernelDescription (std::string &s)
{
if (m_remote_platform_sp)
return m_remote_platform_sp->GetRemoteOSKernelDescription (s);
s.clear();
return false;
}
// Remote Platform subclasses need to override this function
ArchSpec
PlatformPOSIX::GetRemoteSystemArchitecture ()
{
if (m_remote_platform_sp)
return m_remote_platform_sp->GetRemoteSystemArchitecture ();
return ArchSpec();
}
const char *
PlatformPOSIX::GetHostname ()
{
if (IsHost())
return Platform::GetHostname();
if (m_remote_platform_sp)
return m_remote_platform_sp->GetHostname ();
return NULL;
}
const char *
PlatformPOSIX::GetUserName (uint32_t uid)
{
// Check the cache in Platform in case we have already looked this uid up
const char *user_name = Platform::GetUserName(uid);
if (user_name)
return user_name;
if (IsRemote() && m_remote_platform_sp)
return m_remote_platform_sp->GetUserName(uid);
return NULL;
}
const char *
PlatformPOSIX::GetGroupName (uint32_t gid)
{
const char *group_name = Platform::GetGroupName(gid);
if (group_name)
return group_name;
if (IsRemote() && m_remote_platform_sp)
return m_remote_platform_sp->GetGroupName(gid);
return NULL;
}
Error
PlatformPOSIX::ConnectRemote (Args& args)
{
Error error;
if (IsHost())
{
error.SetErrorStringWithFormat ("can't connect to the host platform '%s', always connected", GetPluginName().GetCString());
}
else
{
if (!m_remote_platform_sp)
m_remote_platform_sp = Platform::Create (ConstString("remote-gdb-server"), error);
if (m_remote_platform_sp && error.Success())
error = m_remote_platform_sp->ConnectRemote (args);
else
error.SetErrorString ("failed to create a 'remote-gdb-server' platform");
if (error.Fail())
m_remote_platform_sp.reset();
}
if (error.Success() && m_remote_platform_sp)
{
if (m_option_group_platform_rsync.get() && m_option_group_platform_ssh.get() && m_option_group_platform_caching.get())
{
if (m_option_group_platform_rsync->m_rsync)
{
SetSupportsRSync(true);
SetRSyncOpts(m_option_group_platform_rsync->m_rsync_opts.c_str());
SetRSyncPrefix(m_option_group_platform_rsync->m_rsync_prefix.c_str());
SetIgnoresRemoteHostname(m_option_group_platform_rsync->m_ignores_remote_hostname);
}
if (m_option_group_platform_ssh->m_ssh)
{
SetSupportsSSH(true);
SetSSHOpts(m_option_group_platform_ssh->m_ssh_opts.c_str());
}
SetLocalCacheDirectory(m_option_group_platform_caching->m_cache_dir.c_str());
}
}
return error;
}
Error
PlatformPOSIX::DisconnectRemote ()
{
Error error;
if (IsHost())
{
error.SetErrorStringWithFormat ("can't disconnect from the host platform '%s', always connected", GetPluginName().GetCString());
}
else
{
if (m_remote_platform_sp)
error = m_remote_platform_sp->DisconnectRemote ();
else
error.SetErrorString ("the platform is not currently connected");
}
return error;
}
Error
PlatformPOSIX::LaunchProcess (ProcessLaunchInfo &launch_info)
{
Error error;
if (IsHost())
{
error = Platform::LaunchProcess (launch_info);
}
else
{
if (m_remote_platform_sp)
error = m_remote_platform_sp->LaunchProcess (launch_info);
else
error.SetErrorString ("the platform is not currently connected");
}
return error;
}
lldb_private::Error
PlatformPOSIX::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 Error ("the platform is not currently connected");
}
lldb::ProcessSP
PlatformPOSIX::Attach (ProcessAttachInfo &attach_info,
Debugger &debugger,
Target *target,
Error &error)
{
lldb::ProcessSP process_sp;
Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
if (IsHost())
{
if (target == NULL)
{
TargetSP new_target_sp;
error = debugger.GetTargetList().CreateTarget (debugger,
NULL,
NULL,
false,
NULL,
new_target_sp);
target = new_target_sp.get();
if (log)
log->Printf ("PlatformPOSIX::%s created new target", __FUNCTION__);
}
else
{
error.Clear();
if (log)
log->Printf ("PlatformPOSIX::%s target already existed, setting target", __FUNCTION__);
}
if (target && error.Success())
{
debugger.GetTargetList().SetSelectedTarget(target);
if (log)
{
ModuleSP exe_module_sp = target->GetExecutableModule ();
log->Printf("PlatformPOSIX::%s set selected target to %p %s", __FUNCTION__, (void *)target,
exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str() : "<null>");
}
process_sp = target->CreateProcess (attach_info.GetListenerForProcess(debugger), attach_info.GetProcessPluginName(), NULL);
if (process_sp)
{
ListenerSP listener_sp = attach_info.GetHijackListener();
if (listener_sp == nullptr)
{
listener_sp = Listener::MakeListener("lldb.PlatformPOSIX.attach.hijack");
attach_info.SetHijackListener(listener_sp);
}
process_sp->HijackProcessEvents(listener_sp);
error = process_sp->Attach (attach_info);
}
}
}
else
{
if (m_remote_platform_sp)
process_sp = m_remote_platform_sp->Attach (attach_info, debugger, target, error);
else
error.SetErrorString ("the platform is not currently connected");
}
return process_sp;
}
lldb::ProcessSP
PlatformPOSIX::DebugProcess (ProcessLaunchInfo &launch_info,
Debugger &debugger,
Target *target, // Can be NULL, if NULL create a new target, else use existing one
Error &error)
{
ProcessSP process_sp;
if (IsHost())
{
// We are going to hand this process off to debugserver which will be in charge of setting the exit status.
// We still need to reap it from lldb but if we let the monitor thread also set the exit status, we set up a
// race between debugserver & us for who will find out about the debugged process's death.
launch_info.GetFlags().Set(eLaunchFlagDontSetExitStatus);
process_sp = Platform::DebugProcess (launch_info, debugger, target, error);
}
else
{
if (m_remote_platform_sp)
process_sp = m_remote_platform_sp->DebugProcess (launch_info, debugger, target, error);
else
error.SetErrorString ("the platform is not currently connected");
}
return process_sp;
}
void
PlatformPOSIX::CalculateTrapHandlerSymbolNames ()
{
m_trap_handlers.push_back (ConstString ("_sigtramp"));
}
Error
PlatformPOSIX::EvaluateLibdlExpression(lldb_private::Process* process,
const char* expr_cstr,
const char* expr_prefix,
lldb::ValueObjectSP& result_valobj_sp)
{
DynamicLoader *loader = process->GetDynamicLoader();
if (loader)
{
Error error = loader->CanLoadImage();
if (error.Fail())
return error;
}
ThreadSP thread_sp(process->GetThreadList().GetExpressionExecutionThread());
if (!thread_sp)
return Error("Selected thread isn't valid");
StackFrameSP frame_sp(thread_sp->GetStackFrameAtIndex(0));
if (!frame_sp)
return Error("Frame 0 isn't valid");
ExecutionContext exe_ctx;
frame_sp->CalculateExecutionContext(exe_ctx);
EvaluateExpressionOptions expr_options;
expr_options.SetUnwindOnError(true);
expr_options.SetIgnoreBreakpoints(true);
expr_options.SetExecutionPolicy(eExecutionPolicyAlways);
expr_options.SetLanguage(eLanguageTypeC_plus_plus);
Error expr_error;
UserExpression::Evaluate(exe_ctx,
expr_options,
expr_cstr,
expr_prefix,
result_valobj_sp,
expr_error);
if (result_valobj_sp->GetError().Fail())
return result_valobj_sp->GetError();
return Error();
}
uint32_t
PlatformPOSIX::DoLoadImage(lldb_private::Process* process,
const lldb_private::FileSpec& remote_file,
lldb_private::Error& error)
{
char path[PATH_MAX];
remote_file.GetPath(path, sizeof(path));
StreamString expr;
expr.Printf(R"(
struct __lldb_dlopen_result { void *image_ptr; const char *error_str; } the_result;
the_result.image_ptr = dlopen ("%s", 2);
if (the_result.image_ptr == (void *) 0x0)
{
the_result.error_str = dlerror();
}
else
{
the_result.error_str = (const char *) 0x0;
}
the_result;
)",
path);
const char *prefix = GetLibdlFunctionDeclarations();
lldb::ValueObjectSP result_valobj_sp;
error = EvaluateLibdlExpression(process, expr.GetData(), prefix, result_valobj_sp);
if (error.Fail())
return LLDB_INVALID_IMAGE_TOKEN;
error = result_valobj_sp->GetError();
if (error.Fail())
return LLDB_INVALID_IMAGE_TOKEN;
Scalar scalar;
ValueObjectSP image_ptr_sp = result_valobj_sp->GetChildAtIndex(0, true);
if (!image_ptr_sp || !image_ptr_sp->ResolveValue(scalar))
{
error.SetErrorStringWithFormat("unable to load '%s'", path);
return LLDB_INVALID_IMAGE_TOKEN;
}
addr_t image_ptr = scalar.ULongLong(LLDB_INVALID_ADDRESS);
if (image_ptr != 0 && image_ptr != LLDB_INVALID_ADDRESS)
return process->AddImageToken(image_ptr);
if (image_ptr == 0)
{
ValueObjectSP error_str_sp = result_valobj_sp->GetChildAtIndex(1, true);
if (error_str_sp && error_str_sp->IsCStringContainer(true))
{
DataBufferSP buffer_sp(new DataBufferHeap(10240,0));
size_t num_chars = error_str_sp->ReadPointedString (buffer_sp, error, 10240).first;
if (error.Success() && num_chars > 0)
error.SetErrorStringWithFormat("dlopen error: %s", buffer_sp->GetBytes());
else
error.SetErrorStringWithFormat("dlopen failed for unknown reasons.");
return LLDB_INVALID_IMAGE_TOKEN;
}
}
error.SetErrorStringWithFormat("unable to load '%s'", path);
return LLDB_INVALID_IMAGE_TOKEN;
}
Error
PlatformPOSIX::UnloadImage (lldb_private::Process* process, uint32_t image_token)
{
const addr_t image_addr = process->GetImagePtrFromToken(image_token);
if (image_addr == LLDB_INVALID_ADDRESS)
return Error("Invalid image token");
StreamString expr;
expr.Printf("dlclose((void *)0x%" PRIx64 ")", image_addr);
const char *prefix = GetLibdlFunctionDeclarations();
lldb::ValueObjectSP result_valobj_sp;
Error error = EvaluateLibdlExpression(process, expr.GetData(), prefix, result_valobj_sp);
if (error.Fail())
return error;
if (result_valobj_sp->GetError().Fail())
return result_valobj_sp->GetError();
Scalar scalar;
if (result_valobj_sp->ResolveValue(scalar))
{
if (scalar.UInt(1))
return Error("expression failed: \"%s\"", expr.GetData());
process->ResetImageToken(image_token);
}
return Error();
}
lldb::ProcessSP
PlatformPOSIX::ConnectProcess (const char* connect_url,
const char* plugin_name,
lldb_private::Debugger &debugger,
lldb_private::Target *target,
lldb_private::Error &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);
}
const char*
PlatformPOSIX::GetLibdlFunctionDeclarations() const
{
return R"(
extern "C" void* dlopen(const char*, int);
extern "C" void* dlsym(void*, const char*);
extern "C" int dlclose(void*);
extern "C" char* dlerror(void);
)";
}
size_t
PlatformPOSIX::ConnectToWaitingProcesses(Debugger& debugger, Error& error)
{
if (m_remote_platform_sp)
return m_remote_platform_sp->ConnectToWaitingProcesses(debugger, error);
return Platform::ConnectToWaitingProcesses(debugger, error);
}