From patchwork Thu May 19 14:48:06 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pedro Alves X-Patchwork-Id: 12384 Received: (qmail 76859 invoked by alias); 19 May 2016 14:48:29 -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 76787 invoked by uid 89); 19 May 2016 14:48:29 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-3.3 required=5.0 tests=BAYES_00, RP_MATCHES_RCVD, SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=thousand, thousands, scanning 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; Thu, 19 May 2016 14:48:18 +0000 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 18B1A6265D for ; Thu, 19 May 2016 14:48:14 +0000 (UTC) Received: from cascais.lan (ovpn01.gateway.prod.ext.ams2.redhat.com [10.39.146.11]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u4JEmBLp009731 for ; Thu, 19 May 2016 10:48:13 -0400 From: Pedro Alves To: gdb-patches@sourceware.org Subject: [PATCH 2/6] [Linux] Read vDSO range from /proc/PID/task/PID/maps instead of /proc/PID/maps Date: Thu, 19 May 2016 15:48:06 +0100 Message-Id: <1463669290-30415-3-git-send-email-palves@redhat.com> In-Reply-To: <1463669290-30415-1-git-send-email-palves@redhat.com> References: <1463669290-30415-1-git-send-email-palves@redhat.com> ... as it's _much_ faster. Hacking the gdb.threads/attach-many-short-lived-threads.exp test to spawn thousands of threads instead of dozens to stress and debug timeout problems with gdb.threads/attach-many-short-lived-threads.exp, I saw that GDB would spend several seconds just reading the /proc/PID/smaps file, to determine the vDSO mapping range. GDB opens and reads the whole file just once, and caches the result, but even that is too slow. For example, with almost 8000 threads: $ ls /proc/3518/task/ | wc -l 7906 reading the /proc/PID/smaps file grepping for "vdso" takes over 15 seconds : $ time cat /proc/3518/smaps | grep vdso 7ffdbafee000-7ffdbaff0000 r-xp 00000000 00:00 0 [vdso] real 0m15.371s user 0m0.008s sys 0m15.017s Looking around the web for hints, I found a nice description of the issue here: http://backtrace.io/blog/blog/2014/11/12/large-thread-counts-and-slow-process-maps/ The problem is that /proc/PID/smaps wants to show the mappings as being thread stack, and that has the kernel iterating over all threads in the thread group, for each mapping. The fix is to use the "map" file under /proc/PID/task/PID/ instead of the /proc/PID/ one, as the former doesn't mark thread stacks for all threads. That alone drops the timing to the millisecond range on my machine: $ time cat /proc/3518/task/3518/smaps | grep vdso 7ffdbafee000-7ffdbaff0000 r-xp 00000000 00:00 0 [vdso] real 0m0.150s user 0m0.009s sys 0m0.084s And since we only need the vdso mapping's address range, we can use "maps" file instead of "smaps", and it's even cheaper: /proc/PID/task/PID/maps : $ time cat /proc/3518/task/3518/maps | grep vdso 7ffdbafee000-7ffdbaff0000 r-xp 00000000 00:00 0 [vdso] real 0m0.027s user 0m0.000s sys 0m0.017s gdb/ChangeLog: yyyy-mm-dd Pedro Alves PR gdb/19828 * linux-tdep.c (find_mapping_size): Delete. (linux_vsyscall_range_raw): Rewrite reading from /proc/PID/task/PID/maps directly instead of using gdbarch_find_memory_regions. --- gdb/linux-tdep.c | 77 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 23 deletions(-) diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c index b8d063f..ab110b0 100644 --- a/gdb/linux-tdep.c +++ b/gdb/linux-tdep.c @@ -2277,39 +2277,70 @@ linux_gdb_signal_to_target (struct gdbarch *gdbarch, return -1; } -/* Rummage through mappings to find a mapping's size. */ - -static int -find_mapping_size (CORE_ADDR vaddr, unsigned long size, - int read, int write, int exec, int modified, - void *data) -{ - struct mem_range *range = (struct mem_range *) data; - - if (vaddr == range->start) - { - range->length = size; - return 1; - } - return 0; -} - /* Helper for linux_vsyscall_range that does the real work of finding the vsyscall's address range. */ static int linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range) { + char filename[100]; + long pid; + char *data; + + /* Can't access /proc if debugging a core file. */ + if (!target_has_execution) + return 0; + + /* We need to know the real target PID to access /proc. */ + if (current_inferior ()->fake_pid_p) + return 0; + if (target_auxv_search (¤t_target, AT_SYSINFO_EHDR, &range->start) <= 0) return 0; - /* This is installed by linux_init_abi below, so should always be - available. */ - gdb_assert (gdbarch_find_memory_regions_p (target_gdbarch ())); + pid = current_inferior ()->pid; - range->length = 0; - gdbarch_find_memory_regions (gdbarch, find_mapping_size, range); - return 1; + /* Note that reading /proc/PID/task/PID/maps (1) is much faster than + reading /proc/PID/maps (2). The later identifies thread stacks + in the output, which requires scanning every thread in the thread + group to check whether a VMA is actually a thread's stack. With + Linux 4.4 on an Intel i7-4810MQ @ 2.80GHz, with an inferior with + a few thousand threads, (1) takes a few miliseconds, while (2) + takes several seconds. Also note that "smaps", what we read for + determining core dump mappings, is even slower than "maps". */ + xsnprintf (filename, sizeof filename, "/proc/%ld/task/%ld/maps", pid, pid); + data = target_fileio_read_stralloc (NULL, filename); + if (data != NULL) + { + struct cleanup *cleanup = make_cleanup (xfree, data); + char *line; + char *saveptr = NULL; + + for (line = strtok_r (data, "\n", &saveptr); + line != NULL; + line = strtok_r (NULL, "\n", &saveptr)) + { + ULONGEST addr, endaddr; + const char *p = line; + + addr = strtoulst (p, &p, 16); + if (addr == range->start) + { + if (*p == '-') + p++; + endaddr = strtoulst (p, &p, 16); + range->length = endaddr - addr; + do_cleanups (cleanup); + return 1; + } + } + + do_cleanups (cleanup); + } + else + warning (_("unable to open /proc file '%s'"), filename); + + return 0; } /* Implementation of the "vsyscall_range" gdbarch hook. Handles