From patchwork Fri Sep 26 12:25:29 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pedro Alves X-Patchwork-Id: 2998 Received: (qmail 7237 invoked by alias); 26 Sep 2014 12:25:40 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 7161 invoked by uid 89); 26 Sep 2014 12:25:39 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.3 required=5.0 tests=AWL, BAYES_00, RP_MATCHES_RCVD, SPF_HELO_PASS, SPF_PASS autolearn=ham version=3.3.2 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Fri, 26 Sep 2014 12:25:37 +0000 Received: from int-mx13.intmail.prod.int.phx2.redhat.com (int-mx13.intmail.prod.int.phx2.redhat.com [10.5.11.26]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s8QCPZCB025386 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL) for ; Fri, 26 Sep 2014 08:25:35 -0400 Received: from brno.lan (ovpn01.gateway.prod.ext.ams2.redhat.com [10.39.146.11]) by int-mx13.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id s8QCPTNO001088 for ; Fri, 26 Sep 2014 08:25:34 -0400 From: Pedro Alves To: gdb-patches@sourceware.org Subject: [PATCH 3/3] Cache the vsyscall/vDSO range per-inferior Date: Fri, 26 Sep 2014 13:25:29 +0100 Message-Id: <1411734329-4707-4-git-send-email-palves@redhat.com> In-Reply-To: <1411734329-4707-1-git-send-email-palves@redhat.com> References: <1411734329-4707-1-git-send-email-palves@redhat.com> We're now doing a vsyscall/vDSO address range lookup whenever we fetch shared libraries, either through an explicit "info shared", or when the target reports new libraries have been loaded, in order to filter out the vDSO from glibc's DSO list. Before we started doing that, GDB would only ever lookup the vsyscall's address range once in the process's lifetime. Looking up the vDSO address range requires an auxv lookup (which is already cached, so no problem), but also reading the process's mappings from /proc to find out the vDSO's mapping's size. That generates extra RSP traffic when remote debugging. Particularly annoying when the process's mappings grow linearly as more libraries are mapped in, and we went through the trouble of making incremental DSO list updates work against gdbserver (when the probes-based dynamic linker interface is available). The vsyscall/vDSO is mapped by the kernel when the process is initially mapped in, and doesn't change throughout the process's lifetime, so we can cache its address range. Caching at this level brings GDB back to one and only one vsyscall address range lookup per process. Tested on x86_64 Fedora 20. gdb/ 2014-09-26 Pedro Alves * linux-tdep.c: Include observer.h. (linux_inferior_data): New global. (struct linux_info): New structure. (invalidate_linux_cache_inf, linux_inferior_data_cleanup) (get_linux_inferior_data): New functions. (linux_vsyscall_range): Rename to ... (linux_vsyscall_range_raw): ... this. (linux_vsyscall_range): New function; handles caching. (_initialize_linux_tdep): Register linux_inferior_data. Install inferior_exit and inferior_appeared observers. --- gdb/linux-tdep.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 100 insertions(+), 2 deletions(-) diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c index 2a5e57d..550c97d 100644 --- a/gdb/linux-tdep.c +++ b/gdb/linux-tdep.c @@ -32,6 +32,7 @@ #include "cli/cli-utils.h" #include "arch-utils.h" #include "gdb_obstack.h" +#include "observer.h" #include @@ -119,6 +120,72 @@ get_linux_gdbarch_data (struct gdbarch *gdbarch) return gdbarch_data (gdbarch, linux_gdbarch_data_handle); } +/* Per-inferior data key. */ +static const struct inferior_data *linux_inferior_data; + +/* Linux-specific cached data. This is used by GDB for caching + purposes for each inferior. This helps reduce the overhead of + transfering data from a remote target to the local host. */ +struct linux_info +{ + /* Cache of the inferior's vsyscall/vDSO mapping range. Only valid + if VSYSCALL_RANGE_P is positive. This is cached because getting + at this info requires an auxv lookup (which is itself cached), + and looking through the inferior's mappings (which change + throughout execution and therefore cannot be cached). */ + struct mem_range vsyscall_range; + + /* Zero if we haven't tried looking up the vsyscall's range before + yet. Positive if we tried looking it up, and found it. Negative + if we tried looking it up but failed. */ + int vsyscall_range_p; +}; + +/* Frees whatever allocated space there is to be freed and sets INF's + linux cache data pointer to NULL. */ + +static void +invalidate_linux_cache_inf (struct inferior *inf) +{ + struct linux_info *info; + + info = inferior_data (inf, linux_inferior_data); + if (info != NULL) + { + xfree (info); + set_inferior_data (inf, linux_inferior_data, NULL); + } +} + +/* Handles the cleanup of the linux cache for inferior INF. ARG is + ignored. Callback for the inferior_appeared and inferior_exit + events. */ + +static void +linux_inferior_data_cleanup (struct inferior *inf, void *arg) +{ + invalidate_linux_cache_inf (inf); +} + +/* Fetch the linux cache info for INF. This function always returns a + valid INFO pointer. */ + +static struct linux_info * +get_linux_inferior_data (void) +{ + struct linux_info *info; + struct inferior *inf = current_inferior (); + + info = inferior_data (inf, linux_inferior_data); + if (info == NULL) + { + info = XCNEW (struct linux_info); + set_inferior_data (inf, linux_inferior_data, info); + } + + return info; +} + /* This function is suitable for architectures that don't extend/override the standard siginfo structure. */ @@ -1799,10 +1866,11 @@ find_mapping_size (CORE_ADDR vaddr, unsigned long size, return 0; } -/* Implementation of the "vsyscall_range" gdbarch hook. */ +/* Helper for linux_vsyscall_range that does the real work of finding + the vsyscall's address range. */ static int -linux_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range) +linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range) { if (target_auxv_search (¤t_target, AT_SYSINFO_EHDR, &range->start) <= 0) return 0; @@ -1816,6 +1884,29 @@ linux_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range) return 1; } +/* Implementation of the "vsyscall_range" gdbarch hook. Handles + caching, and defers the real work to linux_vsyscall_range_raw. */ + +static int +linux_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range) +{ + struct linux_info *info = get_linux_inferior_data (); + + if (info->vsyscall_range_p == 0) + { + if (linux_vsyscall_range_raw (gdbarch, &info->vsyscall_range)) + info->vsyscall_range_p = 1; + else + info->vsyscall_range_p = -1; + } + + if (info->vsyscall_range_p < 0) + return 0; + + *range = info->vsyscall_range; + return 1; +} + /* To be called from the various GDB_OSABI_LINUX handlers for the various GNU/Linux architectures and machine types. */ @@ -1844,4 +1935,11 @@ _initialize_linux_tdep (void) { linux_gdbarch_data_handle = gdbarch_data_register_post_init (init_linux_gdbarch_data); + + /* Set a cache per-inferior. */ + linux_inferior_data + = register_inferior_data_with_cleanup (NULL, linux_inferior_data_cleanup); + /* Observers used to invalidate the cache when needed. */ + observer_attach_inferior_exit (invalidate_linux_cache_inf); + observer_attach_inferior_appeared (invalidate_linux_cache_inf); }