libunwind-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Libunwind-devel] [PATCH 1/2] ptrace: cache mechanism for /proc/$pid/map


From: Masatake YAMATO
Subject: [Libunwind-devel] [PATCH 1/2] ptrace: cache mechanism for /proc/$pid/maps
Date: Tue, 19 Nov 2013 11:47:11 +0900

I'm working on adding stack trace feature to strace(strace-k).  In
addition to the name of invoked system call, strace-k prints the stack
trace of the process.

libunwind is main part of the feature. After some testing I found
`_UPT_get_proc_name' is a performance bottleneck of the fuature. On
linux the function opens and scans /proc/$pid/maps(MAP) for each name
resolving. Generally MAP should be reread for each name resolution
because memory mapping of a process can be altered. However, strace
knows well when the memory mapping of the target process is altered
because strace tracks the invocations of system calls which modify
memory mapping. Therefore rereading is just inefficient for strace.

This patch introduces cache mechanism for MAP. An application can
activate the mechanism with `_UPT_set_map_caching_policy(upt_info, 1)'.
Application activating the mechanism must track the memory mapping;
when detecting the alteration of the memory mapping, the application
must notify it to libunwind with `_UPT_flush_map_cache(upt_info)'.

With activating the cache mechanism, execution of strace-k is 3 ~ 4
times faster.

By default the cache mechanism is disabled; there is no impact on
existing applicatoins.

Signed-off-by: Masatake YAMATO <address@hidden>
---
 Makefile.am                              |   3 +-
 include/libunwind-ptrace.h               |   9 ++
 include/map_cache.h                      |  58 ++++++++++++
 src/Makefile.am                          |   6 +-
 src/elfxx.c                              |  43 +++++++--
 src/elfxx.h                              |   6 ++
 src/os-linux.c                           | 149 +++++++++++++++++++++++++++++++
 src/os-linux.h                           |   7 ++
 src/ptrace/_UPT_destroy.c                |   3 +
 src/ptrace/_UPT_flush_map_cache.c        |  36 ++++++++
 src/ptrace/_UPT_get_proc_name.c          |   8 +-
 src/ptrace/_UPT_internal.h               |   2 +
 src/ptrace/_UPT_set_map_caching_policy.c |  38 ++++++++
 13 files changed, 357 insertions(+), 11 deletions(-)
 create mode 100644 include/map_cache.h
 create mode 100644 src/ptrace/_UPT_flush_map_cache.c
 create mode 100644 src/ptrace/_UPT_set_map_caching_policy.c

diff --git a/Makefile.am b/Makefile.am
index e24fe1e..a2b88dc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -42,7 +42,8 @@ nodist_include_HEADERS = include/libunwind-common.h
 SUBDIRS = src tests doc
 
 noinst_HEADERS = include/dwarf.h include/dwarf_i.h include/dwarf-eh.h  \
-       include/compiler.h include/libunwind_i.h include/mempool.h      \
+       include/compiler.h include/libunwind_i.h include/map_cache.h    \
+       include/mempool.h                                               \
        include/remote.h                                                \
        include/tdep-aarch64/dwarf-config.h                             \
        include/tdep-aarch64/jmpbuf.h                                   \
diff --git a/include/libunwind-ptrace.h b/include/libunwind-ptrace.h
index 7fca205..a6229b9 100644
--- a/include/libunwind-ptrace.h
+++ b/include/libunwind-ptrace.h
@@ -37,8 +37,17 @@ extern "C" {
    aren't really part of the libunwind API.  They are implemented in a
    archive library called libunwind-ptrace.a.  */
 
+typedef enum
+  {
+    UPT_MAP_CACHE_NONE,
+    UNW_MAP_CACHE_ENABLE,
+  }
+upt_map_caching_policy_t;
+  
 extern void *_UPT_create (pid_t);
 extern void _UPT_destroy (void *);
+extern void _UPT_set_map_caching_policy (void*, upt_map_caching_policy_t);
+extern void _UPT_flush_map_cache (void *);
 extern int _UPT_find_proc_info (unw_addr_space_t, unw_word_t,
                                unw_proc_info_t *, int, void *);
 extern void _UPT_put_unwind_info (unw_addr_space_t, unw_proc_info_t *, void *);
diff --git a/include/map_cache.h b/include/map_cache.h
new file mode 100644
index 0000000..32a87ab
--- /dev/null
+++ b/include/map_cache.h
@@ -0,0 +1,58 @@
+/* libunwind - a platform-independent unwind library
+ * Copyright (C) 2013 Red Hat, Inc.  All rights reserved.
+       Contributed by Masatake YAMATO <address@hidden>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
+
+#ifndef map_cache_h
+#define map_cache_h
+
+struct unw_map_image
+  {
+    struct elf_image ei;
+    char *path;
+    unsigned long low;
+    unsigned long hi;
+    unsigned long mapoff;
+  };
+struct unw_map_cache;
+
+#if __linux__
+# define tdep_get_map_cache     UNW_ARCH_OBJ(get_map_cache)
+# define tdep_flush_map_cache   UNW_ARCH_OBJ(flush_map_cache)
+# define tdep_destroy_map_cache UNW_ARCH_OBJ(destroy_map_cache)
+#else
+# define tdep_get_map_cache(pptr,pid,ip,imgpp) 0 /* Not implemented */
+# define tdep_flush_map_cache(ptr)            do {} while (0)
+# define tdep_destroy_map_cache(pptr)         do {} while (0)
+#endif /* __linux__ */
+
+#if __linux__
+int  tdep_get_map_cache (void **map_cache_slot,
+                        pid_t pid,
+                        unw_word_t ip,
+                        struct unw_map_image **image);
+void tdep_flush_map_cache (void *map_cache);
+void tdep_destroy_map_cache (void **map_cache_slot);
+#endif /* __linux__ */
+
+#endif /* map_cache_h */
diff --git a/src/Makefile.am b/src/Makefile.am
index cf137c0..1836d98 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -46,9 +46,11 @@ libunwind_ptrace_la_SOURCES =                                
                  \
        ptrace/_UPT_accessors.c ptrace/_UPT_access_fpreg.c                \
        ptrace/_UPT_access_mem.c ptrace/_UPT_access_reg.c                 \
        ptrace/_UPT_create.c ptrace/_UPT_destroy.c                        \
-       ptrace/_UPT_find_proc_info.c ptrace/_UPT_get_dyn_info_list_addr.c \
+       ptrace/_UPT_find_proc_info.c ptrace/_UPT_flush_map_cache.c        \
+       ptrace/_UPT_get_dyn_info_list_addr.c                              \
        ptrace/_UPT_put_unwind_info.c ptrace/_UPT_get_proc_name.c         \
-       ptrace/_UPT_reg_offset.c ptrace/_UPT_resume.c
+       ptrace/_UPT_reg_offset.c ptrace/_UPT_resume.c                     \
+       ptrace/_UPT_set_map_caching_policy.c                              \
 noinst_HEADERS += ptrace/_UPT_internal.h
 
 ### libunwind-coredump:
diff --git a/src/elfxx.c b/src/elfxx.c
index 3d87331..54647a2 100644
--- a/src/elfxx.c
+++ b/src/elfxx.c
@@ -25,6 +25,7 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
CONNECTION
 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
 
 #include "libunwind_i.h"
+#include "map_cache.h"
 
 #include <stdio.h>
 #include <sys/param.h>
@@ -344,18 +345,48 @@ HIDDEN int
 elf_w (get_proc_name) (unw_addr_space_t as, pid_t pid, unw_word_t ip,
                       char *buf, size_t buf_len, unw_word_t *offp)
 {
+  return elf_w (get_proc_name_with_map_cache_slot) (as, pid, ip,
+                                                   buf, buf_len, offp,
+                                                   NULL);
+}
+
+HIDDEN int
+elf_w (get_proc_name_with_map_cache_slot) (unw_addr_space_t as, pid_t pid, 
unw_word_t ip,
+                                          char *buf, size_t buf_len, 
unw_word_t *offp,
+                                          void **map_cache_slot)
+{
   unsigned long segbase, mapoff;
-  struct elf_image ei;
+  struct elf_image *ei, ei_;
   int ret;
+  struct unw_map_image *image = NULL;
+
+  ei = &ei_;
+  if (map_cache_slot)
+    {
+      ret = tdep_get_map_cache (map_cache_slot, pid, ip, &image);
+      if (ret < 0)
+       return ret;
+      else if (ret == 0)
+       goto slow_path;
+
+      ei = &image->ei;
+      segbase = image->low;
+      mapoff = image->mapoff;
+      goto resolve;
+    }
 
-  ret = tdep_get_elf_image (&ei, pid, ip, &segbase, &mapoff, NULL, 0);
+ slow_path:
+  ret = tdep_get_elf_image (ei, pid, ip, &segbase, &mapoff, NULL, 0);
   if (ret < 0)
     return ret;
 
-  ret = elf_w (get_proc_name_in_image) (as, &ei, segbase, mapoff, ip, buf, 
buf_len, offp);
-
-  munmap (ei.image, ei.size);
-  ei.image = NULL;
+ resolve:
+  ret = elf_w (get_proc_name_in_image) (as, ei, segbase, mapoff, ip, buf, 
buf_len, offp);
 
+  if (!image)
+    {
+      munmap (ei->image, ei->size);
+      ei->image = NULL;
+    }
   return ret;
 }
diff --git a/src/elfxx.h b/src/elfxx.h
index dd1e346..ee15fbc 100644
--- a/src/elfxx.h
+++ b/src/elfxx.h
@@ -47,6 +47,12 @@ extern int elf_w (get_proc_name) (unw_addr_space_t as,
                                  char *buf, size_t len,
                                  unw_word_t *offp);
 
+extern int elf_w (get_proc_name_with_map_cache_slot) (unw_addr_space_t as,
+                                                     pid_t pid, unw_word_t ip,
+                                                     char *buf, size_t len,
+                                                     unw_word_t *offp,
+                                                     void **map_cache_slot);
+
 extern int elf_w (get_proc_name_in_image) (unw_addr_space_t as,
                                           struct elf_image *ei,
                                           unsigned long segbase,
diff --git a/src/os-linux.c b/src/os-linux.c
index bcc4036..10d012e 100644
--- a/src/os-linux.c
+++ b/src/os-linux.c
@@ -25,9 +25,11 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
SOFTWARE.  */
 
 #include <limits.h>
 #include <stdio.h>
+#include <stdlib.h>
 
 #include "libunwind_i.h"
 #include "os-linux.h"
+#include "map_cache.h"
 
 PROTECTED int
 tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip,
@@ -61,3 +63,150 @@ tdep_get_elf_image (struct elf_image *ei, pid_t pid, 
unw_word_t ip,
   maps_close (&mi);
   return rc;
 }
+
+static int
+image_compare (const void *key, const void *candidate)
+{
+  const unw_word_t ip = *(const unw_word_t *)key;
+  const struct unw_map_image *image = candidate;
+
+  if (image->low >= ip)
+    return -1;
+  else if (image->hi <= ip)
+    return 1;
+  else
+    return 0;
+}
+
+static int
+map_cache_extend (map_cache_t *map_cache)
+{
+  struct unw_map_image *tmp;
+  unsigned int c;
+
+#define BASE_CACHE_LENGTH 64
+  c = (map_cache->n_allocated
+       ? (map_cache->n_allocated * 2)
+       : BASE_CACHE_LENGTH);
+  tmp = realloc (map_cache->images, sizeof(*tmp) * c);
+  if (tmp == NULL)
+    return -1;
+
+  map_cache->images = tmp;
+  map_cache->n_allocated = c;
+  return 0;
+}
+
+
+static void
+map_cache_load (map_cache_t *map_cache, pid_t pid)
+{
+  struct map_iterator mi;
+  unsigned long segbase, hi, mapoff;
+  struct unw_map_image *image;
+
+  if (maps_init (&mi, pid) < 0)
+    return;
+
+  while (maps_next (&mi, &segbase, &hi, &mapoff))
+    {
+      if (map_cache->n_used == map_cache->n_allocated)
+       {
+         if (map_cache_extend (map_cache) < 0)
+           goto cleanup;
+       }
+      image = &(map_cache->images[map_cache->n_used]);
+      image->low = segbase;
+      image->hi  = hi;
+      image->mapoff = mapoff;
+      image->ei.image = NULL;
+      image->ei.size = 0;
+      image->path = mi.path? strdup (mi.path): NULL;
+      if (mi.path && (image->path == NULL))
+       goto cleanup;
+      map_cache->n_used++;
+    }
+  maps_close (&mi);
+  return;
+
+cleanup:
+  tdep_flush_map_cache (map_cache);
+}
+
+PROTECTED int
+tdep_get_map_cache (void **map_cache_slot,
+                   pid_t pid,
+                   unw_word_t ip,
+                   struct unw_map_image **image)
+{
+  map_cache_t *map_cache;
+  struct elf_image *ei;
+  const char *path;
+
+  if (*map_cache_slot == NULL)
+    {
+      map_cache = calloc (1, sizeof(*map_cache));
+      if (map_cache == NULL)
+       return -1;
+      *map_cache_slot = map_cache;
+    }
+  else
+    map_cache = *map_cache_slot;
+
+  if (map_cache->images == NULL)
+    map_cache_load (map_cache, pid);
+  if (map_cache->images == NULL)
+    return -1;
+
+  *image = bsearch (&ip, map_cache->images, map_cache->n_used,
+                   sizeof(**image),
+                   image_compare);
+  if (*image == NULL)
+    return -1;
+
+  ei = &(*image)->ei;
+  path = (*image)->path;
+  if (ei->image == MAP_FAILED)
+    return -1;
+  else if(ei->image == NULL)
+    {
+      if (elf_map_image (ei, path) < 0)
+       {
+         (*image)->ei.image = MAP_FAILED;
+         return -1;
+       }
+    }
+
+  return 1;
+}
+
+PROTECTED void
+tdep_flush_map_cache (void *map_cache_)
+{
+  map_cache_t *map_cache = map_cache_;
+  unsigned int aindex;
+  struct unw_map_image *image;
+
+  if (map_cache->images)
+    {
+      for (aindex = 0; aindex < map_cache->n_used; aindex++)
+       {
+         image = &(map_cache->images[aindex]);
+         if (image->path)
+           free (image->path);
+         if (image->ei.image != NULL && image->ei.image != MAP_FAILED)
+           munmap (image->ei.image, image->ei.size);
+         memset (image, 0, sizeof(*image));
+       }
+      free (map_cache->images);
+    }
+  memset (map_cache, 0, sizeof(*map_cache));
+}
+
+PROTECTED void
+tdep_destroy_map_cache (void **map_cache_slot)
+{
+  tdep_flush_map_cache (*map_cache_slot);
+  free (*map_cache_slot);
+  *map_cache_slot = NULL;
+}
diff --git a/src/os-linux.h b/src/os-linux.h
index ad9d675..134f06f 100644
--- a/src/os-linux.h
+++ b/src/os-linux.h
@@ -27,6 +27,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
SOFTWARE.  */
 #ifndef os_linux_h
 #define os_linux_h
 
+typedef struct map_cache
+  {
+    unsigned int n_used;
+    unsigned int n_allocated;
+    struct unw_map_image *images;
+  } map_cache_t;
+
 struct map_iterator
   {
     off_t offset;
diff --git a/src/ptrace/_UPT_destroy.c b/src/ptrace/_UPT_destroy.c
index 04ea22d..3307f8d 100644
--- a/src/ptrace/_UPT_destroy.c
+++ b/src/ptrace/_UPT_destroy.c
@@ -24,11 +24,14 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
CONNECTION
 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
 
 #include "_UPT_internal.h"
+#include "map_cache.h"
 
 void
 _UPT_destroy (void *ptr)
 {
   struct UPT_info *ui = (struct UPT_info *) ptr;
   invalidate_edi (&ui->edi);
+  if (ui->map_caching_policy && ui->map_cache)
+    tdep_destroy_map_cache (&ui->map_cache);
   free (ptr);
 }
diff --git a/src/ptrace/_UPT_flush_map_cache.c 
b/src/ptrace/_UPT_flush_map_cache.c
new file mode 100644
index 0000000..4445328
--- /dev/null
+++ b/src/ptrace/_UPT_flush_map_cache.c
@@ -0,0 +1,36 @@
+/* libunwind - a platform-independent unwind library
+   Copyright (C) 2003-2005 Hewlett-Packard Co
+       Contributed by David Mosberger-Tang <address@hidden>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
+
+#include "_UPT_internal.h"
+#include "map_cache.h"
+
+void
+_UPT_flush_map_cache (void *ptr)
+{
+  struct UPT_info *ui = (struct UPT_info *) ptr;
+
+  if (ui->map_caching_policy && ui->map_cache)
+    tdep_flush_map_cache (ui->map_cache);
+}
diff --git a/src/ptrace/_UPT_get_proc_name.c b/src/ptrace/_UPT_get_proc_name.c
index 6ac85a0..1867325 100644
--- a/src/ptrace/_UPT_get_proc_name.c
+++ b/src/ptrace/_UPT_get_proc_name.c
@@ -31,11 +31,15 @@ _UPT_get_proc_name (unw_addr_space_t as, unw_word_t ip,
                    char *buf, size_t buf_len, unw_word_t *offp, void *arg)
 {
   struct UPT_info *ui = arg;
+  void **map_cache_slot = ui->map_caching_policy? &ui->map_cache: NULL;
 
 #if ELF_CLASS == ELFCLASS64
-  return _Uelf64_get_proc_name (as, ui->pid, ip, buf, buf_len, offp);
+  return _Uelf64_get_proc_name_with_map_cache_slot (as, ui->pid, ip, buf, 
buf_len, offp,
+                                                   map_cache_slot);
 #elif ELF_CLASS == ELFCLASS32
-  return _Uelf32_get_proc_name (as, ui->pid, ip, buf, buf_len, offp);
+  return _Uelf32_get_proc_name_with_map_cache_slot (as, ui->pid, ip, buf, 
buf_len, offp,
+                                                   ui->map_caching_policy? 
&ui->map_cache,
+                                                   map_cache_slot);
 #else
   return -UNW_ENOINFO;
 #endif
diff --git a/src/ptrace/_UPT_internal.h b/src/ptrace/_UPT_internal.h
index 2283dc4..e76a7df 100644
--- a/src/ptrace/_UPT_internal.h
+++ b/src/ptrace/_UPT_internal.h
@@ -52,6 +52,8 @@ struct UPT_info
   {
     pid_t pid;         /* the process-id of the child we're unwinding */
     struct elf_dyn_info edi;
+    int map_caching_policy; /* 0: none, 1: use cache */
+    void *map_cache;
   };
 
 extern const int _UPT_reg_offset[UNW_REG_LAST + 1];
diff --git a/src/ptrace/_UPT_set_map_caching_policy.c 
b/src/ptrace/_UPT_set_map_caching_policy.c
new file mode 100644
index 0000000..fe773e7
--- /dev/null
+++ b/src/ptrace/_UPT_set_map_caching_policy.c
@@ -0,0 +1,38 @@
+/* libunwind - a platform-independent unwind library
+ * Copyright (C) 2013 Red Hat, Inc.  All rights reserved.
+       Contributed by Masatake YAMATO <address@hidden>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
+
+#include "_UPT_internal.h"
+#include "map_cache.h"
+
+void
+_UPT_set_map_caching_policy (void *ptr, upt_map_caching_policy_t policy)
+{
+  struct UPT_info *ui = (struct UPT_info *) ptr;
+
+  if (policy == UPT_MAP_CACHE_NONE && ui->map_cache)
+    tdep_destroy_map_cache (&ui->map_cache);
+
+  ui->map_caching_policy = policy;
+}
-- 
1.8.3.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]