libunwind-devel
[Top][All Lists]
Advanced

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

[Libunwind-devel] [patch] Reduce Linux stack use.


From: Paul Pluzhnikov
Subject: [Libunwind-devel] [patch] Reduce Linux stack use.
Date: Mon, 5 Oct 2009 17:09:59 -0700 (PDT)

Greetings,

Currently, libunwind allocates several PATH_MAX entries on stack, while
trying to find a binary via /proc/.../maps.

However stack space may be at premium (especially when sigaltstack is used),
and PATH_MAX on Linux is 4096, while SIGSTKSZ is only 8192 on x86.

Attached patch eliminates multiple PATH_MAX stack allocations, and simplifies
code in maps_next, at the cost of being unable to do anything if we can't
mmap one page. It appears to me that under such low-memory conditions,
libunwind will fail shortly elsewhere anyway.

This patch also disables more of debug_frame-handling code when
CONFIG_DEBUG_FRAME is undefined.

Tested on Linux/x86_64 with and without CONFIG_DEBUG_FRAME, no regressions.

Thanks,
--
Paul Pluzhnikov

commit f7ff774240ee84a37687910a0b5b4ec70745d6ef
Author: Paul Pluzhnikov <address@hidden>
Date:   Mon Oct 5 16:39:35 2009 -0700

    Reduce stack use, and make more of debug_frame conditional on
    CONFIG_DEBUG_FRAME.

diff --git a/src/dwarf/Gfind_proc_info-lsb.c b/src/dwarf/Gfind_proc_info-lsb.c
index 01d91f6..9484289 100644
--- a/src/dwarf/Gfind_proc_info-lsb.c
+++ b/src/dwarf/Gfind_proc_info-lsb.c
@@ -221,14 +221,6 @@ load_debug_frame (const char *file, char **buf, size_t 
*bufsize, int is_local)
 
   return 0;
 }
-#else
-static int
-load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local)
-{
-  return 1;
-}
-#endif /* CONFIG_DEBUG_FRAME */
-
 
 /* Locate the binary which originated the contents of address ADDR. Return
    the name of the binary in *name (space is allocated by the caller)
@@ -244,10 +236,16 @@ find_binary_for_address (unw_word_t ip, char *name, 
size_t name_size)
   unsigned long segbase, mapoff, hi;
 
   maps_init (&mi, pid);
-  while (maps_next (&mi, &segbase, &hi, &mapoff, name, name_size))
+  while (maps_next (&mi, &segbase, &hi, &mapoff))
     if (ip >= segbase && ip < hi)
       {
-       found = 1;
+       size_t len = strlen (mi.path);
+
+       if (len + 1 <= name_size)
+         {
+           memcpy (name, mi.path, len + 1);
+           found = 1;
+         }
        break;
       }
   maps_close (&mi);
@@ -400,6 +398,8 @@ debug_frame_tab_compare (const void *a, const void *b)
     return 0;
 }
 
+#endif /* CONFIG_DEBUG_FRAME */
+
 /* ptr is a pointer to a callback_data structure and, on entry,
    member ip contains the instruction-pointer we're looking
    for.  */
@@ -575,6 +575,8 @@ callback (struct dl_phdr_info *info, size_t size, void *ptr)
        }
     }
 
+#ifdef CONFIG_DEBUG_FRAME
+
   Debug (15, "Trying to find .debug_frame\n");
   di = &cb_data->di_debug;
   fdesc = locate_debug_info (unw_local_addr_space, info, ip, info->dlpi_name);
@@ -707,6 +709,7 @@ callback (struct dl_phdr_info *info, size_t size, void *ptr)
             (long) di->u.ti.segbase, (long) di->u.ti.table_len,
             (long) di->gp, (long) di->u.ti.table_data);
     }
+#endif  /* CONFIG_DEBUG_FRAME */
 
   return found;
 }
diff --git a/src/os-linux.c b/src/os-linux.c
index ae37d8f..210edd0 100644
--- a/src/os-linux.c
+++ b/src/os-linux.c
@@ -36,23 +36,28 @@ tdep_get_elf_image (struct elf_image *ei, pid_t pid, 
unw_word_t ip,
                    unsigned long *segbase, unsigned long *mapoff)
 {
   struct map_iterator mi;
-  char path[PATH_MAX];
-  int found = 0;
+  int found = 0, rc;
   unsigned long hi;
 
-  maps_init (&mi, pid);
-  while (maps_next (&mi, segbase, &hi, mapoff, path, sizeof (path)))
+  if (maps_init (&mi, pid) < 0)
+    return -1;
+
+  while (maps_next (&mi, segbase, &hi, mapoff))
     if (ip >= *segbase && ip < hi)
       {
        found = 1;
        break;
       }
-  maps_close (&mi);
 
   if (!found)
-    return -1;
+    {
+      maps_close (&mi);
+      return -1;
+    }
 
-  return elf_map_image (ei, path);
+  rc = elf_map_image (ei, mi.path);
+  maps_close (&mi);
+  return rc;
 }
 
 #endif /* UNW_REMOTE_ONLY */
diff --git a/src/os-linux.h b/src/os-linux.h
index b2c9a8b..c444922 100644
--- a/src/os-linux.h
+++ b/src/os-linux.h
@@ -34,6 +34,7 @@ struct map_iterator
     size_t buf_size;
     char *buf;
     char *buf_end;
+    char *path;
   };
 
 static inline char *
@@ -61,33 +62,33 @@ ltoa (char *buf, long val)
   return buf + len;
 }
 
-static inline void
+static inline int
 maps_init (struct map_iterator *mi, pid_t pid)
 {
-  char path[PATH_MAX], *cp;
+  char path[sizeof ("/proc/01234567879/maps")], *cp;
 
   memcpy (path, "/proc/", 6);
   cp = ltoa (path + 6, pid);
+  assert (cp + 6 < path + sizeof (path));
   memcpy (cp, "/maps", 6);
 
   mi->fd = open (path, O_RDONLY);
-  mi->offset = 0;
-  mi->buf_size = 0;
-
-  cp = NULL;
   if (mi->fd >= 0)
     {
-      /* Try to allocate a page-sized buffer.  If that fails, we'll
-        fall back on reading one line at a time.  */
+      /* Try to allocate a page-sized buffer.  */
       mi->buf_size = getpagesize ();
       cp = mmap (0, mi->buf_size, PROT_READ | PROT_WRITE,
                 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
       if (cp == MAP_FAILED)
-       cp = NULL;
+       return -1;
       else
-       cp += mi->buf_size;
+       {
+         mi->offset = 0;
+         mi->buf = mi->buf_end = cp + mi->buf_size;
+         return 0;
+       }
     }
-  mi->buf = mi->buf_end = cp;
+  return -1;
 }
 
 static inline char *
@@ -184,7 +185,7 @@ scan_string (char *cp, char *valp, size_t buf_size)
 
   while (*cp != ' ' && *cp != '\t' && *cp != '\0')
     {
-      if (i < buf_size - 1)
+      if (valp != NULL && i < buf_size - 1)
        valp[i++] = *cp;
       ++cp;
     }
@@ -196,10 +197,9 @@ scan_string (char *cp, char *valp, size_t buf_size)
 
 static inline int
 maps_next (struct map_iterator *mi,
-          unsigned long *low, unsigned long *high, unsigned long *offset,
-          char *path, size_t path_size)
+          unsigned long *low, unsigned long *high, unsigned long *offset)
 {
-  char line[256 + PATH_MAX], perm[16], dash, colon, *cp;
+  char perm[16], dash, colon, *cp;
   unsigned long major, minor, inum;
   size_t to_read = 256;        /* most lines fit in 256 characters easy */
   ssize_t i, nread;
@@ -209,80 +209,52 @@ maps_next (struct map_iterator *mi,
 
   while (1)
     {
-      if (mi->buf)
-       {
-         ssize_t bytes_left = mi->buf_end - mi->buf;
-         char *eol = NULL;
+      ssize_t bytes_left = mi->buf_end - mi->buf;
+      char *eol = NULL;
 
-         for (i = 0; i < bytes_left; ++i)
-           {
-             if (mi->buf[i] == '\n')
-               {
-                 eol = mi->buf + i;
-                 break;
-               }
-             else if (mi->buf[i] == '\0')
-               break;
-           }
-         if (!eol)
+      for (i = 0; i < bytes_left; ++i)
+       {
+         if (mi->buf[i] == '\n')
            {
-             /* copy down the remaining bytes, if any */
-             if (bytes_left > 0)
-               memmove (mi->buf_end - mi->buf_size, mi->buf, bytes_left);
-
-             mi->buf = mi->buf_end - mi->buf_size;
-             nread = read (mi->fd, mi->buf + bytes_left,
-                           mi->buf_size - bytes_left);
-             if (nread <= 0)
-               return 0;
-             else if ((size_t) (nread + bytes_left) < mi->buf_size)
-               {
-                 /* Move contents to the end of the buffer so we
-                    maintain the invariant that all bytes between
-                    mi->buf and mi->buf_end are valid.  */
-                 memmove (mi->buf_end - nread - bytes_left, mi->buf,
-                         nread + bytes_left);
-                 mi->buf = mi->buf_end - nread - bytes_left;
-               }
-
-             eol = mi->buf + bytes_left + nread - 1;
-
-             for (i = bytes_left; i < bytes_left + nread; ++i)
-               if (mi->buf[i] == '\n')
-                 {
-                   eol = mi->buf + i;
-                   break;
-                 }
+             eol = mi->buf + i;
+             break;
            }
-         cp = mi->buf;
-         mi->buf = eol + 1;
-         *eol = '\0';
+         else if (mi->buf[i] == '\0')
+           break;
        }
-      else
+      if (!eol)
        {
-         /* maps_init() wasn't able to allocate a buffer; do it the
-            slow way.  */
-         lseek (mi->fd, mi->offset, SEEK_SET);
-
-         if ((nread = read (mi->fd, line, to_read)) <= 0)
+         /* copy down the remaining bytes, if any */
+         if (bytes_left > 0)
+           memmove (mi->buf_end - mi->buf_size, mi->buf, bytes_left);
+
+         mi->buf = mi->buf_end - mi->buf_size;
+         nread = read (mi->fd, mi->buf + bytes_left,
+                       mi->buf_size - bytes_left);
+         if (nread <= 0)
            return 0;
-         for (i = 0; i < nread && line[i] != '\n'; ++i)
-           /* skip */;
-         if (i < nread)
-           {
-             line[i] = '\0';
-             mi->offset += i + 1;
-           }
-         else
+         else if ((size_t) (nread + bytes_left) < mi->buf_size)
            {
-             if (to_read < sizeof (line))
-               to_read = sizeof (line) - 1;
-             else
-               mi->offset += nread;    /* not supposed to happen... */
-             continue; /* duh, no newline found */
+             /* Move contents to the end of the buffer so we
+                maintain the invariant that all bytes between
+                mi->buf and mi->buf_end are valid.  */
+             memmove (mi->buf_end - nread - bytes_left, mi->buf,
+                      nread + bytes_left);
+             mi->buf = mi->buf_end - nread - bytes_left;
            }
-         cp = line;
+
+         eol = mi->buf + bytes_left + nread - 1;
+
+         for (i = bytes_left; i < bytes_left + nread; ++i)
+           if (mi->buf[i] == '\n')
+             {
+               eol = mi->buf + i;
+               break;
+             }
        }
+      cp = mi->buf;
+      mi->buf = eol + 1;
+      *eol = '\0';
 
       /* scan: "LOW-HIGH PERM OFFSET MAJOR:MINOR INUM PATH" */
       cp = scan_hex (cp, low);
@@ -294,7 +266,10 @@ maps_next (struct map_iterator *mi,
       cp = scan_char (cp, &colon);
       cp = scan_hex (cp, &minor);
       cp = scan_dec (cp, &inum);
-      cp = scan_string (cp, path, path_size);
+      cp = mi->path = skip_whitespace (cp);
+      if (!cp)
+       continue;
+      cp = scan_string (cp, NULL, 0);
       if (!cp || dash != '-' || colon != ':')
        continue;       /* skip line with unknown or bad format */
       return 1;
diff --git a/tests/Gtest-bt.c b/tests/Gtest-bt.c
index 8211f73..9b07ec4 100644
--- a/tests/Gtest-bt.c
+++ b/tests/Gtest-bt.c
@@ -38,12 +38,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
SOFTWARE.  */
 #include <unistd.h>
 #include <libunwind.h>
 
-#if UNW_TARGET_X86 || UNW_TARGET_X86_64 || UNW_TARGET_ARM
-# define STACK_SIZE    (128*1024)      /* On x86/-64 and ARM, SIGSTKSZ is too 
small */
-#else
-# define STACK_SIZE    SIGSTKSZ
-#endif
-
 #define panic(args...)                         \
        { fprintf (stderr, args); exit (-1); }
 
@@ -219,10 +213,10 @@ main (int argc, char **argv)
 
   if (verbose)
     printf ("\nBacktrace across signal handler on alternate stack:\n");
-  stk.ss_sp = malloc (STACK_SIZE);
+  stk.ss_sp = malloc (SIGSTKSZ);
   if (!stk.ss_sp)
     panic ("failed to allocate SIGSTKSZ (%u) bytes\n", SIGSTKSZ);
-  stk.ss_size = STACK_SIZE;
+  stk.ss_size = SIGSTKSZ;
   stk.ss_flags = 0;
   if (sigaltstack (&stk, NULL) < 0)
     panic ("sigaltstack: %s\n", strerror (errno));




reply via email to

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