Files
clang-p2996/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
Daniel Malea e0f8f574c7 merge lldb-platform-work branch (and assorted fixes) into trunk
Summary:
    This merge brings in the improved 'platform' command that knows how to
    interface with remote machines; that is, query OS/kernel information, push
    and pull files, run shell commands, etc... and implementation for the new
    communication packets that back that interface, at least on Darwin based
    operating systems via the POSIXPlatform class. Linux support is coming soon.

    Verified the test suite runs cleanly on Linux (x86_64), build OK on Mac OS
    X Mountain Lion.

    Additional improvements (not in the source SVN branch 'lldb-platform-work'):
    - cmake build scripts for lldb-platform
    - cleanup test suite
    - documentation stub for qPlatform_RunCommand
    - use log class instead of printf() directly
    - reverted work-in-progress-looking changes from test/types/TestAbstract.py that work towards running the test suite remotely.
    - add new logging category 'platform'

    Reviewers: Matt Kopec, Greg Clayton

    Review: http://llvm-reviews.chandlerc.com/D1493

llvm-svn: 189295
2013-08-26 23:57:52 +00:00

542 lines
19 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/Log.h"
#include "lldb/Core/StreamString.h"
#include "lldb/Host/File.h"
#include "lldb/Host/FileSpec.h"
#include "lldb/Host/Host.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_remote_platform_sp ()
{
}
//------------------------------------------------------------------
/// Destructor.
///
/// The destructor is virtual since this class is designed to be
/// inherited from by the plug-in instance.
//------------------------------------------------------------------
PlatformPOSIX::~PlatformPOSIX()
{
}
lldb_private::OptionGroupOptions*
PlatformPOSIX::GetConnectionOptions (lldb_private::CommandInterpreter& interpreter)
{
if (m_options.get() == NULL)
{
m_options.reset(new OptionGroupOptions(interpreter));
m_options->Append(new OptionGroupPlatformRSync());
m_options->Append(new OptionGroupPlatformSSH());
m_options->Append(new OptionGroupPlatformCaching());
}
return m_options.get();
}
lldb_private::Error
PlatformPOSIX::RunShellCommand (const char *command, // Shouldn't be NULL
const char *working_dir, // Pass NULL 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");
}
}
uint32_t
PlatformPOSIX::MakeDirectory (const std::string &path,
mode_t mode)
{
if (IsHost())
{
return Host::MakeDirectory (path.c_str(), mode);
}
if (IsRemote() && m_remote_platform_sp)
return m_remote_platform_sp->MakeDirectory(path, mode);
return Platform::MakeDirectory(path,mode);
}
lldb::user_id_t
PlatformPOSIX::OpenFile (const FileSpec& file_spec,
uint32_t flags,
mode_t mode,
Error &error)
{
if (IsHost())
{
return Host::OpenFile(file_spec, flags, mode, error);
}
if (IsRemote() && m_remote_platform_sp)
return m_remote_platform_sp->OpenFile(file_spec, flags, mode, error);
return Platform::OpenFile(file_spec, flags, mode, error);
}
bool
PlatformPOSIX::CloseFile (lldb::user_id_t fd, Error &error)
{
if (IsHost())
{
return Host::CloseFile(fd, error);
}
if (IsRemote() && m_remote_platform_sp)
return m_remote_platform_sp->CloseFile(fd, error);
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 Host::ReadFile(fd, offset, dst, dst_len, error);
}
if (IsRemote() && 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
PlatformPOSIX::WriteFile (lldb::user_id_t fd,
uint64_t offset,
const void* src,
uint64_t src_len,
Error &error)
{
if (IsHost())
{
return Host::WriteFile(fd, offset, src, src_len, error);
}
if (IsRemote() && 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);
}
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)
{
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 (IsRemote() && 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());
Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
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
}
// open
// read, write, read, write, ...
// close
// chown uid:gid dst
Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
if (log)
log->Printf("[PutFile] Using block by block transfer....\n");
File source_file(source, File::eOpenOptionRead, File::ePermissionsUserRW);
if (!source_file.IsValid())
return Error("unable to open source file");
Error error;
lldb::user_id_t dest_file = OpenFile (destination,
File::eOpenOptionCanCreate | File::eOpenOptionWrite | File::eOpenOptionTruncate,
File::ePermissionsUserRWX | File::ePermissionsGroupRX | File::ePermissionsWorldRX,
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;
while (error.Success())
{
size_t bytes_read = buffer_sp->GetByteSize();
error = source_file.Read(buffer_sp->GetBytes(), bytes_read);
if (bytes_read)
{
WriteFile(dest_file, offset, buffer_sp->GetBytes(), bytes_read, error);
offset += bytes_read;
}
else
break;
}
CloseFile(dest_file, error);
if (uid == UINT32_MAX && gid == UINT32_MAX)
return error;
// This is remopve, don't chown a local file...
// std::string dst_path (destination.GetPath());
// if (chown_file(this,dst_path.c_str(),uid,gid) != 0)
// return Error("unable to perform chown");
return error;
}
return Platform::PutFile(source,destination,uid,gid);
}
lldb::user_id_t
PlatformPOSIX::GetFileSize (const FileSpec& file_spec)
{
if (IsHost())
{
return Host::GetFileSize(file_spec);
}
if (IsRemote() && m_remote_platform_sp)
return m_remote_platform_sp->GetFileSize(file_spec);
return Platform::GetFileSize(file_spec);
}
bool
PlatformPOSIX::GetFileExists (const FileSpec& file_spec)
{
if (IsHost())
{
return file_spec.Exists();
}
if (IsRemote() && m_remote_platform_sp)
return m_remote_platform_sp->GetFileExists(file_spec);
return Platform::GetFileExists(file_spec);
}
uint32_t
PlatformPOSIX::GetFilePermissions (const lldb_private::FileSpec &file_spec,
lldb_private::Error &error)
{
if (IsHost())
{
return File::GetPermissions(file_spec.GetPath().c_str(), error);
}
if (IsRemote() && m_remote_platform_sp)
return m_remote_platform_sp->GetFilePermissions(file_spec, error);
return Platform::GetFilePermissions(file_spec, error);
}
lldb_private::Error
PlatformPOSIX::GetFile (const lldb_private::FileSpec& source /* remote file path */,
const lldb_private::FileSpec& destination /* local file path */)
{
// 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 (IsRemote() && 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());
Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
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
Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
if (log)
log->Printf("[GetFile] Using block by block transfer....\n");
Error error;
user_id_t fd_src = OpenFile (source,
File::eOpenOptionRead,
File::ePermissionsDefault,
error);
if (fd_src == UINT64_MAX)
return Error("unable to open source file");
uint32_t permissions = GetFilePermissions(source, error);
if (permissions == 0)
permissions = File::ePermissionsDefault;
user_id_t fd_dst = Host::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 (Host::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 && !Host::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;
}