List:Commits« Previous MessageNext Message »
From:Marc Alff Date:June 4 2009 7:38pm
Subject:Re: bzr commit into mysql-6.0-perfschema branch (marc.alff:3156)
WL#4876
View as plain text  
Hi Guilhem

Guilhem Bichot wrote:
> Hello Marc,
> 
> Thanks for the patch, this is very aligned with the WL design. There are
> some comments below, prefixed with GB.
> 
> Marc Alff a écrit, Le 28.05.2009 23:33:
>> #At file:///home/malff/BZR-TREE/mysql-6.0-wl4876/ based on
>> revid:marc.alff@stripped
>>
>>  3156 Marc Alff    2009-05-28
>>       WL#4876 Parse options before initializing mysys
> 
>> === modified file 'mysys/my_init.c'
>> --- a/mysys/my_init.c    2009-05-07 06:42:31 +0000
>> +++ b/mysys/my_init.c    2009-05-28 21:33:45 +0000
>> @@ -43,6 +43,7 @@ static void netware_init();
>>  #endif
>>  
>>  my_bool my_init_done= 0;
>> +my_bool my_basic_init_done= 0;
>>  uint    mysys_usage_id= 0;              /* Incremented for each
>> my_init() */
>>  ulong   my_thread_stack_size= 65536;
>>  
>> @@ -66,26 +67,11 @@ static MYSQL_FILE instrumented_stdout;
>>  static MYSQL_FILE instrumented_stderr;
>>  #endif
>>  
>> -/*
>> -  Init my_sys functions and my_sys variabels
>> -
>> -  SYNOPSIS
>> -    my_init()
>> -
>> -  RETURN
>> -    0  ok
>> -    1  Couldn't initialize environment
>> -*/
>> -
>> -my_bool my_init(void)
>> +my_bool my_basic_init(void)
> 
> GB Needs a doxygen comment.
> 

Fixed

>>  {
>> -  char * str;
>> -  if (my_init_done)
>> +  if (my_basic_init_done)
>>      return 0;
>> -  my_init_done=1;
>> -  mysys_usage_id++;
>> -  my_umask= 0660;                       /* Default umask for new
>> files */
>> -  my_umask_dir= 0700;                   /* Default umask for new
>> directories */
> 
> GB This my_basic_init() cherry-picks in the code of the existing
> my_init(). Wouldn't it be safer to preserve the existing order of things
> (to minimize modifications hence risk)? I.e. my_basic_init() would be a
> minimum "prefix" of the existing my_init(), i.e. it would also do
> mysys_usage_id++, my_umask=0660, my_umask_dir=0700, and
> init_glob_errs() and setting my_progname_short (and then my_init()
> wouldn't do that). Those few lines of code don't look harmful to add.

I agree, fixed.

> 
>> +  my_basic_init_done= 1;
>>  
>>  #ifdef HAVE_PSI_INTERFACE
>>    instrumented_stdin.m_file= stdin;
> 
>> === modified file 'mysys/my_thr_init.c'
>> --- a/mysys/my_thr_init.c    2009-05-12 03:15:02 +0000
>> +++ b/mysys/my_thr_init.c    2009-05-28 21:33:45 +0000
>> @@ -102,6 +102,77 @@ void my_threadattr_global_init(void)
>>  
>>  static uint get_thread_lib(void);
>>  
>> +static my_bool my_thread_basic_global_init_done= 0;
>> +
>> +my_bool my_thread_basic_global_init(void)
> 
> GB Needs a doxygen comment.
> 

Fixed

>> +{
>> +  int pth_ret;
>> +
>> +  if (my_thread_basic_global_init_done)
>> +    return 0;
>> +  my_thread_basic_global_init_done= 1;
>> +
>> +  /* Mutex used by my_thread_init() and after
>> my_thread_destroy_mutex() */
>> +  mysql_mutex_init_extra(key_THR_LOCK_threads, &THR_LOCK_threads,
>> MY_MUTEX_INIT_FAST,
>> +                         "THR_LOCK_threads", MYF_NO_DEADLOCK_DETECTION);
>> +  mysql_mutex_init_extra(key_THR_LOCK_malloc, &THR_LOCK_malloc,
>> MY_MUTEX_INIT_FAST,
>> +                         "THR_LOCK_malloc", MYF_NO_DEADLOCK_DETECTION);
>> +  mysql_mutex_init(key_THR_LOCK_open, &THR_LOCK_open,
>> MY_MUTEX_INIT_FAST);
>> +  mysql_mutex_init(key_THR_LOCK_charset, &THR_LOCK_charset,
>> MY_MUTEX_INIT_FAST);
>> +
>> +  if ((pth_ret= pthread_key_create(&THR_KEY_mysys, NULL)) != 0)
>> +  {
>> +    fprintf(stderr, "Can't initialize threads: error %d\n", pth_ret);
>> +    return 1;
>> +  }
>> +
>> +  if (my_thread_init())
>> +    return 1;
>> +
>> +  return 0;
>> +}
> 
> GB So, what moved from my_thread_global_init() to
> my_thread_basic_global_init() is:
> 1) init of 4 mutexes
> 2) creation of THR_KEY_mysys
> 3) call to my_thread_init()
> Moving (2) is required by moving (3). Why was (3) moved? is it because
> code executed in mysqld.cc:main() between my_thread_basic_global_init()
> and my_thread_global_init() needs the settings done in my_thread_init()?
> 

The thread local storage needs to be initialized, in particular for
assignments to my_errno.

>> +my_bool my_thread_basic_global_reinit(void)
>> +{
>> +  struct st_my_thread_var *tmp;
>> +
>> +  DBUG_ASSERT(my_thread_basic_global_init_done);
>> +
>> +  /*
>> +    These mutexes were initialized before the instrumentation.
>> +    Destroy + create them again, now that the instrumentation
>> +    is in place.
>> +    This is safe, since my_thread_basic_global_reinit()
>> +    is called before creating new threads, so the mutexes
>> +    are not in use.
>> +  */
> 
> GB Please transform this nice comment into a function comment.

Fixed

> 
>> +  mysql_mutex_destroy(&THR_LOCK_threads);
>> +  mysql_mutex_init_extra(key_THR_LOCK_threads, &THR_LOCK_threads,
>> MY_MUTEX_INIT_FAST,
>> +                         "THR_LOCK_threads", MYF_NO_DEADLOCK_DETECTION);
>> +
>> +  mysql_mutex_destroy(&THR_LOCK_malloc);
>> +  mysql_mutex_init_extra(key_THR_LOCK_malloc, &THR_LOCK_malloc,
>> MY_MUTEX_INIT_FAST,
>> +                         "THR_LOCK_malloc", MYF_NO_DEADLOCK_DETECTION);
>> +
>> +  mysql_mutex_destroy(&THR_LOCK_open);
>> +  mysql_mutex_init(key_THR_LOCK_open, &THR_LOCK_open,
>> MY_MUTEX_INIT_FAST);
>> +
>> +  mysql_mutex_destroy(&THR_LOCK_charset);
>> +  mysql_mutex_init(key_THR_LOCK_charset, &THR_LOCK_charset,
>> MY_MUTEX_INIT_FAST);
>> +
>> +  tmp= my_pthread_getspecific(struct st_my_thread_var*, THR_KEY_mysys);
>> +  DBUG_ASSERT(tmp);
>> +
>> +  mysql_mutex_destroy(&tmp->mutex);
>> +  mysql_mutex_init(key_my_thread_var_mutex, &tmp->mutex,
>> MY_MUTEX_INIT_FAST);
>> +
>> +  mysql_cond_destroy(&tmp->suspend);
>> +  mysql_cond_init(key_my_thread_var_suspend, &tmp->suspend, NULL);
>> +
>> +  return 0;
> 
> GB it always returns 0; make the function return "void" then.
> 

Changed

>> === modified file 'sql/mysqld.cc'
>> --- a/sql/mysqld.cc    2009-05-26 21:15:12 +0000
>> +++ b/sql/mysqld.cc    2009-05-28 21:33:45 +0000
> 
>> @@ -4501,6 +4496,14 @@ static void test_lc_time_sz()
>>  }
>>  #endif//DBUG_OFF
>>  
>> +#ifdef HAVE_PERFORMANCE_SCHEMA
>> +extern struct my_option my_long_perfschema_options[];
>> +
>> +my_bool get_one_perfschema_option(int, const struct my_option *, char *)
>> +{
>> +  return 0;
>> +}
> 
> GB just reuse get_one_plugin_option().
> 

Fixed handle_option to allow NULL instead.
Removed get_one_plugin_option().

>> +#endif
>>  
>>  #ifdef __WIN__
>>  int win_main(int argc, char **argv)
>> @@ -4508,33 +4511,54 @@ int win_main(int argc, char **argv)
>>  int main(int argc, char **argv)
>>  #endif
>>  {
>> -  MY_INIT(argv[0]);        // init my_sys library & pthreads
>> -  /* nothing should come before this line ^^^ */
>> +  /* Perform basic thread library and malloc initialization. */
> 
> GB please add in the comment something like "to be able to read defaults
> files and parse options".

Done

> 
>> +  if (my_basic_init())
>> +  {
>> +    fprintf(stderr, "my_basic_init() failed.");
>> +    return 1;
>> +  }
>> +
>> +  orig_argc= argc;
>> +  orig_argv= argv;
>> +  load_defaults(MYSQL_CONFIG_NAME, load_default_groups, &argc, &argv);
>> +  defaults_argc= argc;
>> +  defaults_argv= argv;
>>  
>>  #ifdef HAVE_PERFORMANCE_SCHEMA
>> -#ifndef HAVE_WL4876
>> +  {
> 
> GB Here, please add a comment like "Performance Schema needs to be
> initialized as early as possible, before to-be-instrumented objects of
> the server are initialized."
> 

Done

>> +    int ho_error;
>> +
>> +    my_getopt_register_get_addr(NULL);
>> +    my_getopt_error_reporter= option_error_reporter;
> 
> GB If there is an error when parsing a P_S option,
> option_error_reporter() will be called, which will call
> vprint_msg_to_log() which will use non-initialized objects then we get a
> crash; below I used an ambiguous option name to provoke it:
> (gdb) r --performance-schema-max
> safe_mutex: Trying to lock unitialized mutex at log.cc, line 6733
> Program received signal SIGABRT, Aborted.
> 0x00007fbc32a6a015 in raise () from /lib/libc.so.6
> (gdb) bt
> #0  0x00007fbc32a6a015 in raise () from /lib/libc.so.6
> #1  0x00007fbc32a6bb83 in abort () from /lib/libc.so.6
> #2  0x0000000000e020a5 in safe_mutex_lock (mp=0x15db420, my_flags=0,
> file=0x101dfed "log.cc",
>     line=6733) at thr_mutex.c:181
> #3  0x00000000008669da in inline_mysql_mutex_lock (that=0x15db420, flags=0,
>     src_file=0x101dfed "log.cc", src_line=6733) at
> ../include/mysql/psi/mysql_thread.h:413
> #4  0x00000000008696b0 in print_buffer_to_file (level=ERROR_LEVEL,
>     buffer=0x7fff3c3fdd90 "(null): ambiguous option
> '--performance-schema-max' (performance_schema_max_cond,
> performance_schema_max_thread_instruments)") at log.cc:6733
> #5  0x0000000000869875 in vprint_msg_to_log (level=ERROR_LEVEL,
>     format=0x10e3cb0 "%s: ambiguous option '--%s' (%s, %s)",
> args=0x7fff3c3fe1d0) at log.cc:6766
> #6  0x000000000077b91f in option_error_reporter (level=ERROR_LEVEL,
>     format=0x10e3cb0 "%s: ambiguous option '--%s' (%s, %s)") at
> mysqld.cc:9030
> #7  0x0000000000dffbfd in handle_options (argc=0x7fff3c3fe5dc,
> argv=0x7fff3c3fe5d0,
>     longopts=0x14aea60,
>     get_one_option=0x779dc3 <get_one_perfschema_option(int, my_option
> const*, char*)>)
>     at my_getopt.c:353
> #8  0x0000000000784cc5 in main (argc=23, argv=0x24749c8) at mysqld.cc:4537
> Another object used by vprint_msg_to_log():print_buffer_to_file() is
> localtime_r() which may use a mutex (LOCK_localtime_r, in my_pthread.c)
> which is initialized in my_thread_global_init(), too late too.
> Still we want P_S option parsing failures to go to the logger, because
> it's what prints to the Windows Events Log.
> I suggest: for P_S options define a simple option_error_reporter() which
> just copies the errors in a memory buffer (you can use a plain
> sql_string.cc:String instance as recipient, which will automatically
> grow as you append to it). If the handle_options(P_S) failed, skip
> initialization of P_S (by doing "PSI_hook= NULL;"). After having done
> my_init() and logger.init_base() (when log is thus ready), if there is
> an error recorded in the String, pass it as a usual error message, to
> the usual option_error_reporter() (which will print to the ready log).
> Serg said that it's how errors generated by the Linux kernel before
> syslog is up, are sent to syslog - they get buffered for the necessary
> time.
> And add a .test testing the crash scenario above.
> This logger problem was mentioned in the spec, by the way.
> 

Implemented a buffered error reporter, to keep errors until the logger
is available.

For the logger, I don't think logger.init_base() is sufficient:
- the logger needs more setup, and in particular command line options
affecting the logger behavior have not been parsed yet
- mutexes like LOCK_error_log are initialized later

Refactoring the command line options parsing for the logger is not part
of the scope of this patch, but I think every option affecting logging
eventually should be parsed earlier in the server, as well.

I tested manually errors (--performance_schema_max) or warnings
(--performance_schema_max_cond=-1), but could not get that automated
with MTR.

>> +
>> +    /* Skip unknown options so that they may be processed later */
>> +    my_getopt_skip_unknown= TRUE;
> 
> GB Please add a comment here like "options not belonging to Performance
> Schema are not processed here but later because some operate on objects
> which are not yet initialized (like rpl_filter)".

Added comments in the options arrays.

> 
>> +    if ((ho_error= handle_options(&argc, &argv,
>> my_long_perfschema_options,
>> +                                  get_one_perfschema_option)))
>> +      return ho_error;
> 
> GB Please add this comment:
> /** @todo parse --mutex-deadlock-detector here */
> (that's not a todo for you, I'll create an unassigned bug report for it).
> 

It's implemented.

>> +    /* Add back the program name handle_options removes */
>> +    argc++;
>> +    argv--;
> 
> GB If you tweak the MY_INIT() call as explained below, you don't need to
> add back anything, it will be ok to throw away the program's name.

No change

> 
>> +    /* Remove the performance schema options parsed. */
>> +    defaults_argc= argc;
>> +    defaults_argv= argv;
> 
> GB load_defaults() has allocated memory (wityh alloc_root()) and
> defaults_argv has to stay unchanged after that, so that free_defaults()
> has the proper pointer to free. This is explained in
> init_server_components() (look for "We need to eat").
> handle_options() automatically does the removing of parsed P_S options;
> you don't need to do anything for it.
> Could you please also add a ".test" file to verify that there are no
> problems here? It would start mysqld with a ".cnf" file
> (--defaults-file=$MYSQLTEST_VARDIR/tmp/something.cnf, see
> mysqladmin.test for an example) containing P_S options and then other
> options. It would check that all options from .cnf" are well seen. It
> can be modelled on your previous cmdline_bad_*.test test.
> 

Fixed the free_defaults() problem, added clarity in the code.

>> +
>> +    PSI_hook= initialize_performance_schema(& pfs_param);
>> +  }
> <cut>
>> +    Other provider of the instrumentation interface should
>> +    initialize PSI_hook here:
>> +    - HAVE_PSI_INTERFACE is for the instrumentation interface
>> +    - HAVE_PERFORMANCE_SCHEMA is for one implementation of the
>> interface,
>> +    but there could be alternate implementations, which is why
>> +    these two defines are kept separate.
>> +    TODO:
>> +    One possible alternate implementation is: enforce a mutex lock
>> order,
>> +    not between mutex instances as in safe mutex, but between mutex
>> classes,
>> +    and according to a dependency graph declared explicitly.
> 
> GB The TODO comment is probably at a wrong place.
> 

No change, unless there is a better suggestion.

>> +  */
>>  #endif /* HAVE_PERFORMANCE_SCHEMA */
>>  
>>  #ifdef HAVE_PSI_INTERFACE
>> @@ -4560,13 +4584,13 @@ int main(int argc, char **argv)
>>      if (psi)
>>        PSI_server->set_thread(psi);
>>  
>> -#ifndef HAVE_WL4876
>> -    /* TODO: remove with WL#4876: rework the out of order mysys
>> initialization */
>> -    my_reinit_mutexes();
>> -#endif /* HAVE_WL4876 */
> 
> GB Please add a comment here, like "recreate objects which were created
> before initialization of Performance Schema, so that they are
> instrumented".
> 

Done

>> +    if (my_thread_basic_global_reinit())
>> +      return 1;
>>    }
>>  #endif /* HAVE_PSI_INTERFACE */
>>  
>> +  MY_INIT(orig_argv[0]);                       // init my_sys library
>> & pthreads
> 
> GB Given that MY_INIT() happens late now, "my_progname= name;" (which it
> contains) happens late too; as my_progname is sometimes used in error
> messages, to be sure that it's present in error messages output by
> earlier stages of main() I'd put "my_progname= name;" right after the
> start of main(), and replace MY_INIT() above with my_init().
> 

I agree, fixed.

>> @@ -7195,83 +7218,6 @@ The minimum value for this variable is 4
>>     (uchar**) &optimizer_switch_str, (uchar**) &optimizer_switch_str,
>> 0, GET_STR, REQUIRED_ARG,     /*OPTIMIZER_SWITCH_DEFAULT*/0,
>>     0, 0, 0, 0, 0},
>> -#ifdef HAVE_PERFORMANCE_SCHEMA
>> -  {"performance_schema", OPT_PFS_ENABLED,
>> -   "Enable the performance schema.",
>> -   (uchar**) &pfs_param.m_enabled,
>> -   (uchar**) &pfs_param.m_enabled,
> <cut>
>> +#ifdef HAVE_PERFORMANCE_SCHEMA
>> +struct my_option my_long_perfschema_options[] =
>> +{
>> +  {"performance_schema", OPT_PFS_ENABLED,
>> +   "Enable the performance schema.",
>> +   (uchar**) &pfs_param.m_enabled,
>> +   (uchar**) &pfs_param.m_enabled,
> <cut>
>> +  {"performance_schema_max_thread_instruments",
>> OPT_PFS_MAX_THREAD_INSTRUMENTS,
>> +   "Maximum number of thread instruments.",
>> +   (uchar**) &pfs_param.m_thread_class_sizing,
>> +   (uchar**) &pfs_param.m_thread_class_sizing,
>> +   NULL, GET_ULONG, OPT_ARG, PFS_MAX_THREAD_CLASS, 0, ULONG_MAX, 0,
>> 1, NULL},
>> +  {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
>> +};
> 
> GB Please move P_S-specific options into ha_perfschema.cc, like for
> other engines, with the canonical syntax (example: look for MYSQL_SYSVAR
> and PLUGIN_VAR in ha_innodb.cc).
> This way, my_print_help_inc_plugins() will pick them up naturally
> (because it iterates through plugins) and so does not need to be
> augmented with 'more_options'.
> However, the invokation of handle_options() still needs to be done
> early, as you have done (it cannot be done just like for other plugins,
> which happens too late in init_server_components()).
> Having P_S options in ha_perfschema.cc will cause handle_options() of
> init_server_components():plugin_init():test_plugin_options() to be
> called with them in parameter; handle_options() will search argv for P_S
> options which it will not find (as we removed them early). This is
> inefficient but harmless, and it keeps the code better organized (by
> having a plugin's options located in this plugin's code and declared by
> the canonical method MYSQL_SYSVAR_etc). We lose on one side but we reuse
> more of the existing system like my_print_help_inc_plugins(). We make
> P_S less of a special case.
> 

Please clarify how:
- the performance schema options implemented in the engine plugin
- parsing the performance schema options early at server startup
is supposed to work then.


> GB A comment about this piece of main():
>   if (PSI_server)
>   {
>     /*
>       Now that we have parsed the command line arguments, and have
> initialized
>       the performance schema itself, the next step is to register all the
>       server and mysys instruments.
>     */
>     my_init_wt_psi_keys();
>     my_init_mysys_psi_keys();
>     init_server_psi_keys();
>     /* Instrument the main thread */
>     PSI_thread *psi= PSI_server->new_thread(key_thread_main, NULL, 0);
>     if (psi)
>       PSI_server->set_thread(psi);
> 
>     if (my_thread_basic_global_reinit())
>       return 1;
>   }
> #endif /* HAVE_PSI_INTERFACE */
> 
>   MY_INIT(orig_argv[0]);                       // init my_sys library &
> - my_init_wt_psi_keys() should be done inside wt_init(), it will be more
> tidy (letting a module care for its internals and mysqld.cc stay
> ignorant of them)

This one I agree with.
It was reported earlier, but I never got to fix it.
Implemented.

> - my_init_mysys_psi_keys() should be done early in
> my_thread_basic_global_reinit() (same reason)

No.
The code needs a PSI_server != NULL, and therefore can not be executed
earlier.

> - for init_server_psi_keys(), no change needed.
> 
> Thank you for looking into this.
> 

Thanks for your comments.

Regards,
-- Marc

Thread
bzr commit into mysql-6.0-perfschema branch (marc.alff:3156) WL#4876Marc Alff28 May
  • Re: bzr commit into mysql-6.0-perfschema branch (marc.alff:3156)WL#4876Guilhem Bichot2 Jun
    • Re: bzr commit into mysql-6.0-perfschema branch (marc.alff:3156)WL#4876Guilhem Bichot3 Jun
    • Re: bzr commit into mysql-6.0-perfschema branch (marc.alff:3156)WL#4876Marc Alff5 Jun
      • Re: bzr commit into mysql-6.0-perfschema branch (marc.alff:3156)WL#4876Guilhem Bichot5 Jun
        • Re: bzr commit into mysql-6.0-perfschema branch (marc.alff:3156)WL#4876Marc Alff5 Jun
          • Re: bzr commit into mysql-6.0-perfschema branch (marc.alff:3156)WL#4876Guilhem Bichot5 Jun
            • Re: bzr commit into mysql-6.0-perfschema branch (marc.alff:3156)WL#4876Guilhem Bichot5 Jun
            • Re: bzr commit into mysql-6.0-perfschema branch (marc.alff:3156)WL#4876Marc Alff31 Jul