From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 87069 invoked by alias); 18 Mar 2018 00:43:40 -0000 Mailing-List: contact elfutils-devel-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Post: List-Help: List-Subscribe: Sender: elfutils-devel-owner@sourceware.org Received: (qmail 87058 invoked by uid 89); 18 Mar 2018 00:43:40 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Checked: by ClamAV 0.99.4 on sourceware.org X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,SPF_PASS autolearn=ham version=3.3.2 spammy=detachment X-Spam-Status: No, score=-26.9 required=5.0 tests=BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,SPF_PASS autolearn=ham version=3.3.2 X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on sourceware.org X-Spam-Level: X-HELO: gnu.wildebeest.org Received: from wildebeest.demon.nl (HELO gnu.wildebeest.org) (212.238.236.112) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sun, 18 Mar 2018 00:43:33 +0000 Received: from librem.wildebeest.org (deer0x01.wildebeest.org [172.31.17.131]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by gnu.wildebeest.org (Postfix) with ESMTPSA id C678D302748A; Sun, 18 Mar 2018 01:43:27 +0100 (CET) Received: by librem.wildebeest.org (Postfix, from userid 1000) id 99C5F13FAFC; Sun, 18 Mar 2018 01:43:27 +0100 (CET) From: Mark Wielaard To: elfutils-devel@sourceware.org Cc: Mark Wielaard Subject: [PATCH] libdwfl: Use process_vm_readv when available. Date: Sun, 18 Mar 2018 00:43:00 -0000 Message-Id: <20180318004323.21340-1-mark@klomp.org> X-Mailer: git-send-email 2.16.1 X-Spam-Flag: NO X-IsSubscribed: yes X-SW-Source: 2018-q1/txt/msg00081.txt.bz2 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 --- ChangeLog | 4 +++ configure.ac | 6 ++-- libdwfl/ChangeLog | 11 +++++++ libdwfl/libdwflP.h | 14 ++++++++- libdwfl/linux-pid-attach.c | 74 +++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 105 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 279c3b2b..81542414 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2018-03-17 Mark Wielaard + + * configure.ac (CHECK_FUNCS): Check for process_vm_readv. + 2018-02-09 Joshua Watt * configure.ac (HAVE_FALLTHROUGH): New define. diff --git a/configure.ac b/configure.ac index 4cdb12af..ab32cbc8 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ dnl Process this file with autoconf to produce a configure script. dnl Configure input file for elfutils. -*-autoconf-*- dnl -dnl Copyright (C) 1996-2017 Red Hat, Inc. +dnl Copyright (C) 1996-2018 Red Hat, Inc. dnl dnl This file is part of elfutils. dnl @@ -41,7 +41,7 @@ fi AC_CONFIG_AUX_DIR([config]) AC_CONFIG_FILES([config/Makefile]) -AC_COPYRIGHT([Copyright (C) 1996-2017 The elfutils developers.]) +AC_COPYRIGHT([Copyright (C) 1996-2018 The elfutils developers.]) AC_PREREQ(2.63) dnl Minimum Autoconf version required. dnl We use GNU make extensions; automake 1.10 defaults to -Wportability. @@ -370,6 +370,8 @@ AC_CHECK_DECLS([mempcpy],[],[], [#define _GNU_SOURCE #include ]) +AC_CHECK_FUNCS([process_vm_readv]) + AC_CHECK_LIB([stdc++], [__cxa_demangle], [dnl AC_DEFINE([USE_DEMANGLE], [1], [Defined if demangling is enabled])]) AM_CONDITIONAL(DEMANGLE, test "x$ac_cv_lib_stdcpp___cxa_demangle" = "xyes") 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 + + * 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 * 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..ea65618f 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 #include +#include #include #include #include @@ -115,12 +116,80 @@ __libdwfl_ptrace_attach (pid_t tid, bool *tid_was_stoppedp) return true; } +#ifdef HAVE_PROCESS_VM_READV +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]; + *result = *(unsigned long *) d; + 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]; + *result = *((unsigned long *) d); + 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; +} + 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 +322,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 +348,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 +450,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, -- 2.16.1