| //===-- 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); |
| expr_options.SetTimeoutUsec(2000000); // 2 seconds |
| |
| 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) |
| { |
| 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); |
| } |