diff options
author | Mark Wielaard <mark@klomp.org> | 2018-03-18 00:24:08 +0100 |
---|---|---|
committer | Mark Wielaard <mark@klomp.org> | 2018-03-28 16:30:27 +0200 |
commit | afffdff29228db03e2131af577f58a22aec6c1fe (patch) | |
tree | 1de9a2890a9976fbb49c779ec38fa2fe97af02ce /libdwfl | |
parent | f0d7b3e14779cdf5facede98edc924ef1266b785 (diff) | |
download | elfutils-afffdff29228db03e2131af577f58a22aec6c1fe.tar.gz |
libdwfl: Use process_vm_readv when available.
If possible use process_vm_readv to read 4K blocks instead of fetching
each word individually with ptrace. For unwinding this often means we
only have to do one process_vm_readv of the stack instead of dozens of
ptrace calls. There is one 4K cache per process, cleared whenever a
thread is detached.
Signed-off-by: Mark Wielaard <mark@klomp.org>
Diffstat (limited to 'libdwfl')
-rw-r--r-- | libdwfl/ChangeLog | 11 | ||||
-rw-r--r-- | libdwfl/libdwflP.h | 14 | ||||
-rw-r--r-- | libdwfl/linux-pid-attach.c | 84 |
3 files changed, 107 insertions, 2 deletions
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog index 1515c410..9776f1cb 100644 --- a/libdwfl/ChangeLog +++ b/libdwfl/ChangeLog @@ -1,3 +1,14 @@ +2018-03-17 Mark Wielaard <mark@klomp.org> + + * libdwflP.h (struct __libdwfl_remote_mem_cache): New. + (struct __libdwfl_pid_arg): Add mem_cache field. + * linux-pid-attach.c (read_cached_memory): New function. + (clear_cached_memory): Likewise. + (pid_memory_read): Call read_cached_memory. + (pid_detach): Free mem_cache. + (pid_thread_detach): Call clear_cached_memory. + (dwfl_linux_proc_attach): Initialize mem_cache to NULL. + 2018-03-05 Mark Wielaard <mark@klomp.org> * dwfl_build_id_find_elf.c (__libdwfl_open_by_build_id): Use diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h index 7d5f795c..15ca0a11 100644 --- a/libdwfl/libdwflP.h +++ b/libdwfl/libdwflP.h @@ -1,5 +1,5 @@ /* Internal definitions for libdwfl. - Copyright (C) 2005-2015 Red Hat, Inc. + Copyright (C) 2005-2015, 2018 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -401,6 +401,14 @@ struct dwfl_arange size_t arange; /* Index in Dwarf_Aranges. */ }; +#define __LIBDWFL_REMOTE_MEM_CACHE_SIZE 4096 +/* Structure for caching remote memory reads as used by __libdwfl_pid_arg. */ +struct __libdwfl_remote_mem_cache +{ + Dwarf_Addr addr; /* Remote address. */ + Dwarf_Off len; /* Zero if cleared, otherwise likely 4K. */ + unsigned char buf[__LIBDWFL_REMOTE_MEM_CACHE_SIZE]; /* The actual cache. */ +}; /* Structure used for keeping track of ptrace attaching a thread. Shared by linux-pid-attach and linux-proc-maps. If it has been setup @@ -411,6 +419,10 @@ struct __libdwfl_pid_arg DIR *dir; /* Elf for /proc/PID/exe. Set to NULL if it couldn't be opened. */ Elf *elf; + /* Remote memory cache, NULL if there is no memory cached. + Should be cleared on detachment (because that makes the thread + runnable and the cache invalid). */ + struct __libdwfl_remote_mem_cache *mem_cache; /* fd for /proc/PID/exe. Set to -1 if it couldn't be opened. */ int elf_fd; /* It is 0 if not used. */ diff --git a/libdwfl/linux-pid-attach.c b/libdwfl/linux-pid-attach.c index 2ab4109c..1133db6c 100644 --- a/libdwfl/linux-pid-attach.c +++ b/libdwfl/linux-pid-attach.c @@ -1,5 +1,5 @@ /* Get Dwarf Frame state for target live PID process. - Copyright (C) 2013, 2014, 2015 Red Hat, Inc. + Copyright (C) 2013, 2014, 2015, 2018 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -34,6 +34,7 @@ #include "libdwflP.h" #include <sys/types.h> #include <sys/stat.h> +#include <sys/uio.h> #include <fcntl.h> #include <dirent.h> #include <unistd.h> @@ -115,12 +116,90 @@ __libdwfl_ptrace_attach (pid_t tid, bool *tid_was_stoppedp) return true; } +#ifdef HAVE_PROCESS_VM_READV +/* Note that the result word size depends on the architecture word size. + That is sizeof long. */ +static bool +read_cached_memory (struct __libdwfl_pid_arg *pid_arg, + Dwarf_Addr addr, Dwarf_Word *result) +{ + /* Let the ptrace fallback deal with the corner case of the address + possibly crossing a page boundery. */ + if ((addr & ((Dwarf_Addr)__LIBDWFL_REMOTE_MEM_CACHE_SIZE - 1)) + > (Dwarf_Addr)__LIBDWFL_REMOTE_MEM_CACHE_SIZE - sizeof (unsigned long)) + return false; + + struct __libdwfl_remote_mem_cache *mem_cache = pid_arg->mem_cache; + if (mem_cache == NULL) + { + size_t mem_cache_size = sizeof (struct __libdwfl_remote_mem_cache); + mem_cache = (struct __libdwfl_remote_mem_cache *) malloc (mem_cache_size); + if (mem_cache == NULL) + return false; + + mem_cache->addr = 0; + mem_cache->len = 0; + pid_arg->mem_cache = mem_cache; + } + + unsigned char *d; + if (addr >= mem_cache->addr && addr - mem_cache->addr < mem_cache->len) + { + d = &mem_cache->buf[addr - mem_cache->addr]; + if ((((uintptr_t) d) & (sizeof (unsigned long) - 1)) == 0) + *result = *(unsigned long *) d; + else + memcpy (result, d, sizeof (unsigned long)); + return true; + } + + struct iovec local, remote; + mem_cache->addr = addr & ~((Dwarf_Addr)__LIBDWFL_REMOTE_MEM_CACHE_SIZE - 1); + local.iov_base = mem_cache->buf; + local.iov_len = __LIBDWFL_REMOTE_MEM_CACHE_SIZE; + remote.iov_base = (void *) (uintptr_t) mem_cache->addr; + remote.iov_len = __LIBDWFL_REMOTE_MEM_CACHE_SIZE; + + ssize_t res = process_vm_readv (pid_arg->tid_attached, + &local, 1, &remote, 1, 0); + if (res != __LIBDWFL_REMOTE_MEM_CACHE_SIZE) + { + mem_cache->len = 0; + return false; + } + + mem_cache->len = res; + d = &mem_cache->buf[addr - mem_cache->addr]; + if ((((uintptr_t) d) & (sizeof (unsigned long) - 1)) == 0) + *result = *(unsigned long *) d; + else + memcpy (result, d, sizeof (unsigned long)); + return true; +} +#endif /* HAVE_PROCESS_VM_READV */ + +static void +clear_cached_memory (struct __libdwfl_pid_arg *pid_arg) +{ + struct __libdwfl_remote_mem_cache *mem_cache = pid_arg->mem_cache; + if (mem_cache != NULL) + mem_cache->len = 0; +} + +/* Note that the result word size depends on the architecture word size. + That is sizeof long. */ static bool pid_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result, void *arg) { struct __libdwfl_pid_arg *pid_arg = arg; pid_t tid = pid_arg->tid_attached; assert (tid > 0); + +#ifdef HAVE_PROCESS_VM_READV + if (read_cached_memory (pid_arg, addr, result)) + return true; +#endif + Dwfl_Process *process = dwfl->process; if (ebl_get_elfclass (process->ebl) == ELFCLASS64) { @@ -253,6 +332,7 @@ pid_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg) { struct __libdwfl_pid_arg *pid_arg = dwfl_arg; elf_end (pid_arg->elf); + free (pid_arg->mem_cache); close (pid_arg->elf_fd); closedir (pid_arg->dir); free (pid_arg); @@ -278,6 +358,7 @@ pid_thread_detach (Dwfl_Thread *thread, void *thread_arg) pid_t tid = INTUSE(dwfl_thread_tid) (thread); assert (pid_arg->tid_attached == tid); pid_arg->tid_attached = 0; + clear_cached_memory (pid_arg); if (! pid_arg->assume_ptrace_stopped) __libdwfl_ptrace_detach (tid, pid_arg->tid_was_stopped); } @@ -379,6 +460,7 @@ dwfl_linux_proc_attach (Dwfl *dwfl, pid_t pid, bool assume_ptrace_stopped) pid_arg->dir = dir; pid_arg->elf = elf; pid_arg->elf_fd = elf_fd; + pid_arg->mem_cache = NULL; pid_arg->tid_attached = 0; pid_arg->assume_ptrace_stopped = assume_ptrace_stopped; if (! INTUSE(dwfl_attach_state) (dwfl, elf, pid, &pid_thread_callbacks, |