[2/2] Initialize tunable list with the GLIBC_TUNABLES environment variable

Message ID 1467479562-11357-3-git-send-email-siddhesh@sourceware.org
State Superseded
Headers

Commit Message

Siddhesh Poyarekar July 2, 2016, 5:12 p.m. UTC
  Read tunables values from the users using the GLIBC_TUNABLES
environment variable.  The value of this variable is a colon-separated
list of name=value pairs.  So a typical string would look like this:

GLIBC_TUNABLES=glibc.malloc.mmap_threshold=2048:glibc.malloc.trim_threshold=1024

	* elf/dl-tunables.c: Include string.h.
	(parse_tunables): New function.
	(GLIBC_TUNABLES): New macro.
	(__tunables_init): Use it.
---
 elf/dl-tunables.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 69 insertions(+)
  

Comments

H.J. Lu July 3, 2016, 2:38 p.m. UTC | #1
On Sat, Jul 2, 2016 at 10:12 AM, Siddhesh Poyarekar
<siddhesh@sourceware.org> wrote:
> Read tunables values from the users using the GLIBC_TUNABLES
> environment variable.  The value of this variable is a colon-separated
> list of name=value pairs.  So a typical string would look like this:
>
> GLIBC_TUNABLES=glibc.malloc.mmap_threshold=2048:glibc.malloc.trim_threshold=1024
>
>         * elf/dl-tunables.c: Include string.h.
>         (parse_tunables): New function.
>         (GLIBC_TUNABLES): New macro.
>         (__tunables_init): Use it.
> ---
>  elf/dl-tunables.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 69 insertions(+)
>
> diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
> index 8a26dfc..04d1d52 100644
> --- a/elf/dl-tunables.c
> +++ b/elf/dl-tunables.c
> @@ -22,10 +22,13 @@
>  #include <stdbool.h>
>  #include <unistd.h>
>  #include <stdlib.h>
> +#include <string.h>
>
>  #define TUNABLES_INTERNAL 1
>  #include "dl-tunables.h"
>
> +#define GLIBC_TUNABLES "GLIBC_TUNABLES"
> +
>  /* Compare environment names, bounded by the name hardcoded in glibc.  */
>  static bool
>  is_name (const char *orig, const char *envname)
> @@ -104,6 +107,66 @@ tunable_initialize (tunable_t *cur)
>      }
>  }
>
> +static void
> +parse_tunables (char *tunestr)
> +{
> +  if (tunestr == NULL || *tunestr == '\0')
> +    return;
> +
> +  char *p = tunestr;
> +
> +  while (true)
> +    {
> +      char *name = p;
> +      size_t len = 0;
> +
> +      /* First, find where the name ends.  */
> +      while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
> +       len++;
> +
> +      /* If we reach the end of the string before getting a valid name-value
> +        pair, bail out.  */
> +      if (p[len] == '\0')
> +       return;
> +
> +      /* We did not find a valid name-value pair before encountering the
> +        colon.  */
> +      if (p[len]== ':')
> +       {
> +         p += len + 1;
> +         continue;
> +       }
> +
> +      p += len + 1;
> +
> +      char *value = p;
> +      len = 0;
> +
> +      while (p[len] != ':' && p[len] != '\0')
> +       len++;
> +
> +      char end = p[len];
> +      p[len] = '\0';
> +
> +      /* Add the tunable if it exists.  */
> +      for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
> +       {
> +         if (is_name (tunable_list[i].name, name))

Can you add the length of name to tunable_list field and check
if the length matches first?

> +           {
> +             /* Tunable values take precedence over the env_alias.  */
> +             tunable_list[i].strval = value;
> +             tunable_initialize (&tunable_list[i]);
> +             break;
> +           }
> +       }
> +
> +      if (end == ':')
> +       p += len + 1;
> +      else
> +       return;
> +    }
> +}
> +
>  /* Initialize the tunables list from the environment.  For now we only use the
>     ENV_ALIAS to find values.  Later we will also use the tunable names to find
>     values.  */
> @@ -116,6 +179,12 @@ __tunables_init (char **envp)
>
>    while (get_next_env (&envp, &envname, &len, &envval))
>      {
> +      if (is_name (GLIBC_TUNABLES, envname))
> +       {
> +         parse_tunables (strdup (envval));
> +         continue;
> +       }
> +
>        for (int i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
>         {
>           tunable_t *cur = &tunable_list[i];
> --
> 2.5.5
>
  
H.J. Lu July 3, 2016, 3:53 p.m. UTC | #2
On Sat, Jul 2, 2016 at 10:12 AM, Siddhesh Poyarekar
<siddhesh@sourceware.org> wrote:
> Read tunables values from the users using the GLIBC_TUNABLES
> environment variable.  The value of this variable is a colon-separated
> list of name=value pairs.  So a typical string would look like this:
>
> GLIBC_TUNABLES=glibc.malloc.mmap_threshold=2048:glibc.malloc.trim_threshold=1024
>
>         * elf/dl-tunables.c: Include string.h.
>         (parse_tunables): New function.
>         (GLIBC_TUNABLES): New macro.
>         (__tunables_init): Use it.

I didn't see any tests for GLIBC_TUNABLES=glibc.malloc and I don't think
it works for static executable:

gdb) bt
#0  __tunable_set_val (id=glibc_malloc_check, valp=0x6b67d0 <check_action>,
    callback=0x414460 <_dl_tunable_set_mallopt_check>) at dl-tunables.c:221
#1  0x0000000000413cda in ptmalloc_init () at arena.c:299
#2  0x0000000000414dfe in ptmalloc_init () at hooks.c:29
#3  malloc_hook_ini (sz=66, caller=<optimized out>) at hooks.c:31
#4  0x0000000000418b2a in __strdup (
    s=0x7fffffffef66
"glibc.malloc.mmap_threshold=2048:glibc.malloc.trim_threshold=1024")
at strdup.c:42
#5  0x0000000000437e01 in __tunables_init (envp=0x7fffffffe088,
    envp@entry=0x7fffffffdeb8) at dl-tunables.c:184
#6  0x000000000043908b in __libc_init_first (argc=argc@entry=1,
    argv=argv@entry=0x7fffffffdea8, envp=0x7fffffffdeb8)
    at ../csu/init-first.c:81
#7  0x00000000004012d5 in generic_start_main (main=0x400550 <main>, argc=1,
    argv=argv@entry=0x7fffffffdea8,
    init=init@entry=0x401b20 <__libc_csu_init>,
    fini=0x401bb0 <__libc_csu_fini>, rtld_fini=0x0, stack_end=0x7fffffffde98)
    at ../csu/libc-start.c:225
#8  0x00000000004015c7 in __libc_start_main (main=<optimized out>,
    argc=<optimized out>, argv=0x7fffffffdea8,
    init=0x401b20 <__libc_csu_init>, fini=0x401bb0 <__libc_csu_fini>,
    rtld_fini=<optimized out>, stack_end=0x7fffffffde98)
    at ../sysdeps/x86/libc-start.c:38
---Type <return> to continue, or q <return> to quit---
#9  0x0000000000400e1a in _start () at ../sysdeps/x86_64/start.S:120

     if (is_name (GLIBC_TUNABLES, envname))
        {
          parse_tunables (strdup (envval));
                                ^^^^^^^^^  Which calls malloc and
other library functions.
          continue;
        }

Since tunable code is called very early, it can't use any C library functions.
  
Siddhesh Poyarekar July 3, 2016, 5:15 p.m. UTC | #3
On Sun, Jul 03, 2016 at 07:38:54AM -0700, H.J. Lu wrote:
> Can you add the length of name to tunable_list field and check
> if the length matches first?

Similar to passing the length in is_name, can we do this as an addon
if we see a significant performance impact?

Siddhesh
  
Siddhesh Poyarekar July 3, 2016, 5:17 p.m. UTC | #4
On Sun, Jul 03, 2016 at 08:53:12AM -0700, H.J. Lu wrote:
> I didn't see any tests for GLIBC_TUNABLES=glibc.malloc and I don't think
> it works for static executable:

Hmm, right, I need to fix this.  I had manually run the
tst-malloc-usable patch with GLIBC_TUNABLES but didn't do it for
static case again.  I'll fix this up, write those tests and repost.

Thanks,
Siddhesh
  

Patch

diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
index 8a26dfc..04d1d52 100644
--- a/elf/dl-tunables.c
+++ b/elf/dl-tunables.c
@@ -22,10 +22,13 @@ 
 #include <stdbool.h>
 #include <unistd.h>
 #include <stdlib.h>
+#include <string.h>
 
 #define TUNABLES_INTERNAL 1
 #include "dl-tunables.h"
 
+#define GLIBC_TUNABLES "GLIBC_TUNABLES"
+
 /* Compare environment names, bounded by the name hardcoded in glibc.  */
 static bool
 is_name (const char *orig, const char *envname)
@@ -104,6 +107,66 @@  tunable_initialize (tunable_t *cur)
     }
 }
 
+static void
+parse_tunables (char *tunestr)
+{
+  if (tunestr == NULL || *tunestr == '\0')
+    return;
+
+  char *p = tunestr;
+
+  while (true)
+    {
+      char *name = p;
+      size_t len = 0;
+
+      /* First, find where the name ends.  */
+      while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
+	len++;
+
+      /* If we reach the end of the string before getting a valid name-value
+	 pair, bail out.  */
+      if (p[len] == '\0')
+	return;
+
+      /* We did not find a valid name-value pair before encountering the
+	 colon.  */
+      if (p[len]== ':')
+	{
+	  p += len + 1;
+	  continue;
+	}
+
+      p += len + 1;
+
+      char *value = p;
+      len = 0;
+
+      while (p[len] != ':' && p[len] != '\0')
+	len++;
+
+      char end = p[len];
+      p[len] = '\0';
+
+      /* Add the tunable if it exists.  */
+      for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
+	{
+	  if (is_name (tunable_list[i].name, name))
+	    {
+	      /* Tunable values take precedence over the env_alias.  */
+	      tunable_list[i].strval = value;
+	      tunable_initialize (&tunable_list[i]);
+	      break;
+	    }
+	}
+
+      if (end == ':')
+	p += len + 1;
+      else
+	return;
+    }
+}
+
 /* Initialize the tunables list from the environment.  For now we only use the
    ENV_ALIAS to find values.  Later we will also use the tunable names to find
    values.  */
@@ -116,6 +179,12 @@  __tunables_init (char **envp)
 
   while (get_next_env (&envp, &envname, &len, &envval))
     {
+      if (is_name (GLIBC_TUNABLES, envname))
+	{
+	  parse_tunables (strdup (envval));
+	  continue;
+	}
+
       for (int i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
 	{
 	  tunable_t *cur = &tunable_list[i];