[NativeProcessLinux] Use fast memory reads, if the system supports it
authorPavel Labath <labath@google.com>
Wed, 17 Jun 2015 18:38:49 +0000 (18:38 +0000)
committerPavel Labath <labath@google.com>
Wed, 17 Jun 2015 18:38:49 +0000 (18:38 +0000)
Summary:
Memory reads using the ptrace API need to be executed on a designated thread
and in 4-byte increments. The process_vm_read syscall has no such requirements
and it is about 50 times faster. This patch makes lldb-server use the faster
API if the target kernel supports it. Kernel support for this feature is
determined at runtime. Using process_vm_writev in the same manner is more
complicated since this syscall (unlike ptrace) respects page protection settings
and so it cannot be used to set a breakpoint, since code pages are typically
read-only. However, memory writes are not currently a performance bottleneck as
they happen much more rarely.

Test Plan: all tests continue to pass

Reviewers: ovyalov, vharron

Subscribers: tberghammer, lldb-commits

Differential Revision: http://reviews.llvm.org/D10488

llvm-svn: 239924

lldb/include/lldb/Host/linux/Uio.h [new file with mode: 0644]
lldb/source/Host/android/LibcGlue.cpp
lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp

diff --git a/lldb/include/lldb/Host/linux/Uio.h b/lldb/include/lldb/Host/linux/Uio.h
new file mode 100644 (file)
index 0000000..5f2be07
--- /dev/null
@@ -0,0 +1,23 @@
+//===-- Uio.h ---------------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_Host_linux_Uio_h_
+#define liblldb_Host_linux_Uio_h_
+
+#include <sys/uio.h>
+
+// Android does not define the process_vm_readv wrapper
+#ifdef __ANDROID_NDK__
+ssize_t process_vm_readv(::pid_t pid,
+                        const struct iovec *local_iov, unsigned long liovcnt,
+                        const struct iovec *remote_iov, unsigned long riovcnt,
+                        unsigned long flags);
+#endif
+
+#endif // liblldb_Host_linux_Uio_h_
index d443a92..d127d6b 100644 (file)
 
 #include <android/api-level.h>
 
+#include <sys/syscall.h>
+#include <lldb/Host/linux/Uio.h>
+
 #if __ANDROID_API__ < 21
 
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
-#include <sys/syscall.h>
 #include <signal.h>
 
 #include "lldb/Host/Time.h"
@@ -37,3 +39,11 @@ int posix_openpt(int flags)
 }
 
 #endif
+
+ssize_t process_vm_readv(::pid_t pid,
+                        const struct iovec *local_iov, unsigned long liovcnt,
+                        const struct iovec *remote_iov, unsigned long riovcnt,
+                        unsigned long flags)
+{
+    return syscall(__NR_process_vm_readv, pid, local_iov, liovcnt, remote_iov, riovcnt, flags);
+}
index c180000..f6cefa9 100644 (file)
@@ -18,6 +18,7 @@
 
 // C++ Includes
 #include <fstream>
+#include <mutex>
 #include <sstream>
 #include <string>
 #include <unordered_map>
 #include <linux/unistd.h>
 #include <sys/socket.h>
 
+#include <sys/syscall.h>
 #include <sys/types.h>
-#include <sys/uio.h>
 #include <sys/user.h>
 #include <sys/wait.h>
 
 #include "lldb/Host/linux/Personality.h"
 #include "lldb/Host/linux/Ptrace.h"
 #include "lldb/Host/linux/Signalfd.h"
+#include "lldb/Host/linux/Uio.h"
 #include "lldb/Host/android/Android.h"
 
 #define LLDB_PERSONALITY_GET_CURRENT_SETTINGS  0xffffffff
@@ -75,6 +77,40 @@ using namespace lldb_private::process_linux;
 using namespace llvm;
 
 // Private bits we only need internally.
+
+static bool ProcessVmReadvSupported()
+{
+    static bool is_supported;
+    static std::once_flag flag;
+
+    std::call_once(flag, [] {
+        Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+
+        uint32_t source = 0x47424742;
+        uint32_t dest = 0;
+
+        struct iovec local, remote;
+        remote.iov_base = &source;
+        local.iov_base = &dest;
+        remote.iov_len = local.iov_len = sizeof source;
+
+        // We shall try if cross-process-memory reads work by attempting to read a value from our own process.
+        ssize_t res = process_vm_readv(getpid(), &local, 1, &remote, 1, 0);
+        is_supported = (res == sizeof(source) && source == dest);
+        if (log)
+        {
+            if (is_supported)
+                log->Printf("%s: Detected kernel support for process_vm_readv syscall. Fast memory reads enabled.",
+                        __FUNCTION__);
+            else
+                log->Printf("%s: syscall process_vm_readv failed (error: %s). Fast memory reads disabled.",
+                        __FUNCTION__, strerror(errno));
+        }
+    });
+
+    return is_supported;
+}
+
 namespace
 {
     const UnixSignals&
@@ -242,7 +278,6 @@ namespace
                 log->Printf ("NativeProcessLinux::%s() [%p]:0x%lx (0x%lx)", __FUNCTION__,
                         (void*)vm_addr, print_dst, (unsigned long)data);
             }
-
             vm_addr += word_size;
             dst += word_size;
         }
@@ -3245,6 +3280,32 @@ NativeProcessLinux::RemoveWatchpoint (lldb::addr_t addr)
 Error
 NativeProcessLinux::ReadMemory (lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read)
 {
+    if (ProcessVmReadvSupported()) {
+        // The process_vm_readv path is about 50 times faster than ptrace api. We want to use
+        // this syscall if it is supported.
+
+        const ::pid_t pid = GetID();
+
+        struct iovec local_iov, remote_iov;
+        local_iov.iov_base = buf;
+        local_iov.iov_len = size;
+        remote_iov.iov_base = reinterpret_cast<void *>(addr);
+        remote_iov.iov_len = size;
+
+        bytes_read = process_vm_readv(pid, &local_iov, 1, &remote_iov, 1, 0);
+        const bool success = bytes_read == size;
+
+        Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+        if (log)
+            log->Printf ("NativeProcessLinux::%s using process_vm_readv to read %zd bytes from inferior address 0x%" PRIx64": %s",
+                    __FUNCTION__, size, addr, success ? "Success" : strerror(errno));
+
+        if (success)
+            return Error();
+        // else
+        //     the call failed for some reason, let's retry the read using ptrace api.
+    }
+
     ReadOperation op(addr, buf, size, bytes_read);
     m_monitor_up->DoOperation(&op);
     return op.GetError ();