/* Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License, version 2.0,
  as published by the Free Software Foundation.

  This program is also distributed with certain software (including
  but not limited to OpenSSL) that is licensed under separate terms,
  as designated in a particular file or component or in included license
  documentation.  The authors of MySQL hereby grant you an additional
  permission to link the program and your derivative works with the
  separately licensed software that they have included with MySQL.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License, version 2.0, for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software Foundation,
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */

#include <my_global.h>
#include <my_thread.h>
#include <pfs_server.h>
#include <pfs_instr_class.h>
#include <pfs_instr.h>
#include <pfs_global.h>
#include <pfs_buffer_container.h>
#include <tap.h>

#include <string.h>
#include <memory.h>

#include "stub_print_error.h"
#include "stub_pfs_defaults.h"
#include "stub_global_status_var.h"

void unload_performance_schema();

/* test helpers, to simulate the setup */

void setup_thread(PSI_thread *t, bool enabled)
{
  PFS_thread *t2= (PFS_thread*) t;
  t2->m_enabled= enabled;
}

/* test helpers, to inspect data */

PFS_file* lookup_file_by_name(const char* name)
{
  PFS_file *pfs;
  size_t len= strlen(name);
  size_t dirlen;
  const char *filename;
  size_t filename_length;

  PFS_file_iterator it= global_file_container.iterate();
  pfs= it.scan_next();

  while (pfs != NULL)
  {
    /*
      When a file "foo" is instrumented, the name is normalized
      to "/path/to/current/directory/foo", so we remove the
      directory name here to find it back.
    */
    dirlen= dirname_length(pfs->m_filename);
    filename= pfs->m_filename + dirlen;
    filename_length= pfs->m_filename_length - dirlen;
    if ((len == filename_length) &&
        (strncmp(name, filename, filename_length) == 0))
      return pfs;

    pfs= it.scan_next();
  }

  return NULL;
}

/* tests */

void test_bootstrap()
{
  void *psi;
  void *psi_2;
  PSI_bootstrap *boot;
  PFS_global_param param;

  diag("test_bootstrap");

  memset(& param, 0xFF, sizeof(param));
  param.m_enabled= true;
  param.m_mutex_class_sizing= 0;
  param.m_rwlock_class_sizing= 0;
  param.m_cond_class_sizing= 0;
  param.m_thread_class_sizing= 0;
  param.m_table_share_sizing= 0;
  param.m_file_class_sizing= 0;
  param.m_socket_class_sizing= 0;
  param.m_mutex_sizing= 0;
  param.m_rwlock_sizing= 0;
  param.m_cond_sizing= 0;
  param.m_thread_sizing= 0;
  param.m_table_sizing= 0;
  param.m_file_sizing= 0;
  param.m_file_handle_sizing= 0;
  param.m_socket_sizing= 0;
  param.m_events_waits_history_sizing= 0;
  param.m_events_waits_history_long_sizing= 0;
  param.m_setup_actor_sizing= 0;
  param.m_setup_object_sizing= 0;
  param.m_user_sizing= 0;
  param.m_account_sizing= 0;
  param.m_host_sizing= 0;
  param.m_stage_class_sizing= 0;
  param.m_events_stages_history_sizing= 0;
  param.m_events_stages_history_long_sizing= 0;
  param.m_statement_class_sizing= 0;
  param.m_events_statements_history_sizing= 0;
  param.m_events_statements_history_long_sizing= 0;
  param.m_events_transactions_history_sizing= 0;
  param.m_events_transactions_history_long_sizing= 0;
  param.m_digest_sizing= 0;
  param.m_session_connect_attrs_sizing= 0;
  param.m_program_sizing= 0;
  param.m_statement_stack_sizing= 0;
  param.m_memory_class_sizing= 0;
  param.m_metadata_lock_sizing= 0;
  param.m_max_digest_length= 0;
  param.m_max_sql_text_length= 0;

  param.m_hints.m_table_definition_cache = 100;
  param.m_hints.m_table_open_cache       = 100;
  param.m_hints.m_max_connections        = 100;
  param.m_hints.m_open_files_limit       = 100;
  param.m_hints.m_max_prepared_stmt_count= 100;

  pre_initialize_performance_schema();
  boot= initialize_performance_schema(& param);
  ok(boot != NULL, "boot");
  ok(boot->get_interface != NULL, "boot->get_interface");

  psi= boot->get_interface(0);
  ok(psi == NULL, "no version 0");

  psi= boot->get_interface(PSI_VERSION_1);
  ok(psi != NULL, "version 1");

  psi_2= boot->get_interface(PSI_VERSION_2);
  ok(psi_2 == NULL, "version 2");

  unload_performance_schema();
}

/*
  Not a test, helper for testing pfs.cc
*/
PSI * load_perfschema()
{
  PSI *psi;
  PSI_bootstrap *boot;
  PFS_global_param param;

  memset(& param, 0xFF, sizeof(param));
  param.m_enabled= true;
  param.m_mutex_class_sizing= 10;
  param.m_rwlock_class_sizing= 10;
  param.m_cond_class_sizing= 10;
  param.m_thread_class_sizing= 10;
  param.m_table_share_sizing= 10;
  param.m_file_class_sizing= 10;
  param.m_socket_class_sizing= 10;
  param.m_mutex_sizing= 10;
  param.m_rwlock_sizing= 10;
  param.m_cond_sizing= 10;
  param.m_thread_sizing= 10;
  param.m_table_sizing= 10;
  param.m_file_sizing= 10;
  param.m_file_handle_sizing= 50;
  param.m_socket_sizing= 10;
  param.m_events_waits_history_sizing= 10;
  param.m_events_waits_history_long_sizing= 10;
  param.m_setup_actor_sizing= 0;
  param.m_setup_object_sizing= 0;
  param.m_user_sizing= 0;
  param.m_account_sizing= 0;
  param.m_host_sizing= 0;
  param.m_stage_class_sizing= 0;
  param.m_events_stages_history_sizing= 0;
  param.m_events_stages_history_long_sizing= 0;
  param.m_statement_class_sizing= 0;
  param.m_events_statements_history_sizing= 0;
  param.m_events_statements_history_long_sizing= 0;
  param.m_events_transactions_history_sizing= 0;
  param.m_events_transactions_history_long_sizing= 0;
  param.m_digest_sizing= 0;
  param.m_session_connect_attrs_sizing= 0;
  param.m_program_sizing= 0;
  param.m_statement_stack_sizing= 10;
  param.m_memory_class_sizing= 10;
  param.m_metadata_lock_sizing= 10;
  param.m_max_digest_length= 0;
  param.m_max_sql_text_length= 1000;

  param.m_hints.m_table_definition_cache = 100;
  param.m_hints.m_table_open_cache       = 100;
  param.m_hints.m_max_connections        = 100;
  param.m_hints.m_open_files_limit       = 100;
  param.m_hints.m_max_prepared_stmt_count= 100;

  pre_initialize_performance_schema();
  /* test_bootstrap() covered this, assuming it just works */
  boot= initialize_performance_schema(& param);
  psi= (PSI *)boot->get_interface(PSI_VERSION_1);

  /* Reset every consumer to a known state */
  flag_global_instrumentation= true;
  flag_thread_instrumentation= true;

  return (PSI*) psi;
}

void unload_performance_schema()
{
  cleanup_table_share();
  cleanup_instruments();
  cleanup_sync_class();
  cleanup_thread_class();
  cleanup_table_share();
  cleanup_file_class();
  cleanup_stage_class();
  cleanup_statement_class();
  cleanup_socket_class();
  cleanup_events_waits_history_long();
  cleanup_events_stages_history_long();
  cleanup_events_statements_history_long();
  cleanup_table_share_hash();
  cleanup_file_hash();
  cleanup_digest();

  shutdown_performance_schema();
}

void test_bad_registration()
{
  PSI *psi;

  diag("test_bad_registration");

  psi= load_perfschema();

  /*
    Test that length('wait/synch/mutex/' (17) + category + '/' (1)) < 32
    --> category can be up to 13 chars for a mutex.
  */

  PSI_mutex_key dummy_mutex_key= 9999;
  PSI_mutex_info bad_mutex_1[]=
  {
    { & dummy_mutex_key, "X", 0}
  };

  psi->register_mutex("/", bad_mutex_1, 1);
  ok(dummy_mutex_key == 0, "zero key");
  dummy_mutex_key= 9999;
  psi->register_mutex("a/", bad_mutex_1, 1);
  ok(dummy_mutex_key == 0, "zero key");
  dummy_mutex_key= 9999;
  psi->register_mutex("/b", bad_mutex_1, 1);
  ok(dummy_mutex_key == 0, "zero key");
  dummy_mutex_key= 9999;
  psi->register_mutex("a/b", bad_mutex_1, 1);
  ok(dummy_mutex_key == 0, "zero key");
  dummy_mutex_key= 9999;
  psi->register_mutex("12345678901234", bad_mutex_1, 1);
  ok(dummy_mutex_key == 0, "zero key");
  dummy_mutex_key= 9999;
  psi->register_mutex("1234567890123", bad_mutex_1, 1);
  ok(dummy_mutex_key == 1, "assigned key");

  /*
    Test that length('wait/synch/mutex/' (17) + category + '/' (1) + name) <= 128
    --> category + name can be up to 110 chars for a mutex.
  */

  dummy_mutex_key= 9999;
  PSI_mutex_info bad_mutex_2[]=
  {
    { & dummy_mutex_key,
      /* 110 chars name */
      "12345678901234567890123456789012345678901234567890"
      "12345678901234567890123456789012345678901234567890"
      "1234567890",
      0}
  };

  psi->register_mutex("X", bad_mutex_2, 1);
  ok(dummy_mutex_key == 0, "zero key");

  dummy_mutex_key= 9999;
  PSI_mutex_info bad_mutex_3[]=
  {
    { & dummy_mutex_key,
      /* 109 chars name */
      "12345678901234567890123456789012345678901234567890"
      "12345678901234567890123456789012345678901234567890"
      "123456789",
      0}
  };

  psi->register_mutex("XX", bad_mutex_3, 1);
  ok(dummy_mutex_key == 0, "zero key");

  psi->register_mutex("X", bad_mutex_3, 1);
  ok(dummy_mutex_key == 2, "assigned key");

  /*
    Test that length('wait/synch/rwlock/' (18) + category + '/' (1)) < 32
    --> category can be up to 12 chars for a rwlock.
  */

  PSI_rwlock_key dummy_rwlock_key= 9999;
  PSI_rwlock_info bad_rwlock_1[]=
  {
    { & dummy_rwlock_key, "X", 0}
  };

  psi->register_rwlock("/", bad_rwlock_1, 1);
  ok(dummy_rwlock_key == 0, "zero key");
  dummy_rwlock_key= 9999;
  psi->register_rwlock("a/", bad_rwlock_1, 1);
  ok(dummy_rwlock_key == 0, "zero key");
  dummy_rwlock_key= 9999;
  psi->register_rwlock("/b", bad_rwlock_1, 1);
  ok(dummy_rwlock_key == 0, "zero key");
  dummy_rwlock_key= 9999;
  psi->register_rwlock("a/b", bad_rwlock_1, 1);
  ok(dummy_rwlock_key == 0, "zero key");
  dummy_rwlock_key= 9999;
  psi->register_rwlock("1234567890123", bad_rwlock_1, 1);
  ok(dummy_rwlock_key == 0, "zero key");
  dummy_rwlock_key= 9999;
  psi->register_rwlock("123456789012", bad_rwlock_1, 1);
  ok(dummy_rwlock_key == 1, "assigned key");

  /*
    Test that length('wait/synch/rwlock/' (18) + category + '/' (1) + name) <= 128
    --> category + name can be up to 109 chars for a rwlock.
  */

  dummy_rwlock_key= 9999;
  PSI_rwlock_info bad_rwlock_2[]=
  {
    { & dummy_rwlock_key,
      /* 109 chars name */
      "12345678901234567890123456789012345678901234567890"
      "12345678901234567890123456789012345678901234567890"
      "123456789",
      0}
  };

  psi->register_rwlock("X", bad_rwlock_2, 1);
  ok(dummy_rwlock_key == 0, "zero key");

  dummy_rwlock_key= 9999;
  PSI_rwlock_info bad_rwlock_2_sx[]=
  {
    { & dummy_rwlock_key,
      /* 109 chars name */
      "12345678901234567890123456789012345678901234567890"
      "12345678901234567890123456789012345678901234567890"
      "123456789",
      PSI_RWLOCK_FLAG_SX}
  };

  psi->register_rwlock("Y", bad_rwlock_2_sx, 1);
  ok(dummy_rwlock_key == 0, "zero key SX");

  dummy_rwlock_key= 9999;
  PSI_rwlock_info bad_rwlock_3[]=
  {
    { & dummy_rwlock_key,
      /* 108 chars name */
      "12345678901234567890123456789012345678901234567890"
      "12345678901234567890123456789012345678901234567890"
      "12345678",
      0}
  };

  psi->register_rwlock("XX", bad_rwlock_3, 1);
  ok(dummy_rwlock_key == 0, "zero key");

  psi->register_rwlock("X", bad_rwlock_3, 1);
  ok(dummy_rwlock_key == 2, "assigned key");

  dummy_rwlock_key= 9999;
  PSI_rwlock_info bad_rwlock_3_sx[]=
  {
    { & dummy_rwlock_key,
      /* 108 chars name */
      "12345678901234567890123456789012345678901234567890"
      "12345678901234567890123456789012345678901234567890"
      "12345678",
      PSI_RWLOCK_FLAG_SX}
  };

  psi->register_rwlock("YY", bad_rwlock_3_sx, 1);
  ok(dummy_rwlock_key == 0, "zero key SX");

  psi->register_rwlock("Y", bad_rwlock_3_sx, 1);
  ok(dummy_rwlock_key == 3, "assigned key SX");

  /*
    Test that length('wait/synch/cond/' (16) + category + '/' (1)) < 32
    --> category can be up to 14 chars for a cond.
  */

  PSI_cond_key dummy_cond_key= 9999;
  PSI_cond_info bad_cond_1[]=
  {
    { & dummy_cond_key, "X", 0}
  };

  psi->register_cond("/", bad_cond_1, 1);
  ok(dummy_cond_key == 0, "zero key");
  dummy_cond_key= 9999;
  psi->register_cond("a/", bad_cond_1, 1);
  ok(dummy_cond_key == 0, "zero key");
  dummy_cond_key= 9999;
  psi->register_cond("/b", bad_cond_1, 1);
  ok(dummy_cond_key == 0, "zero key");
  dummy_cond_key= 9999;
  psi->register_cond("a/b", bad_cond_1, 1);
  ok(dummy_cond_key == 0, "zero key");
  dummy_cond_key= 9999;
  psi->register_cond("123456789012345", bad_cond_1, 1);
  ok(dummy_cond_key == 0, "zero key");
  dummy_cond_key= 9999;
  psi->register_cond("12345678901234", bad_cond_1, 1);
  ok(dummy_cond_key == 1, "assigned key");

  /*
    Test that length('wait/synch/cond/' (16) + category + '/' (1) + name) <= 128
    --> category + name can be up to 111 chars for a cond.
  */

  dummy_cond_key= 9999;
  PSI_cond_info bad_cond_2[]=
  {
    { & dummy_cond_key,
      /* 111 chars name */
      "12345678901234567890123456789012345678901234567890"
      "12345678901234567890123456789012345678901234567890"
      "12345678901",
      0}
  };

  psi->register_cond("X", bad_cond_2, 1);
  ok(dummy_cond_key == 0, "zero key");

  dummy_cond_key= 9999;
  PSI_cond_info bad_cond_3[]=
  {
    { & dummy_cond_key,
      /* 110 chars name */
      "12345678901234567890123456789012345678901234567890"
      "12345678901234567890123456789012345678901234567890"
      "1234567890",
      0}
  };

  psi->register_cond("XX", bad_cond_3, 1);
  ok(dummy_cond_key == 0, "zero key");

  psi->register_cond("X", bad_cond_3, 1);
  ok(dummy_cond_key == 2, "assigned key");

  /*
    Test that length('thread/' (7) + category + '/' (1)) < 32
    --> category can be up to 23 chars for a thread.
  */

  PSI_thread_key dummy_thread_key= 9999;
  PSI_thread_info bad_thread_1[]=
  {
    { & dummy_thread_key, "X", 0}
  };

  psi->register_thread("/", bad_thread_1, 1);
  ok(dummy_thread_key == 0, "zero key");
  dummy_thread_key= 9999;
  psi->register_thread("a/", bad_thread_1, 1);
  ok(dummy_thread_key == 0, "zero key");
  dummy_thread_key= 9999;
  psi->register_thread("/b", bad_thread_1, 1);
  ok(dummy_thread_key == 0, "zero key");
  dummy_thread_key= 9999;
  psi->register_thread("a/b", bad_thread_1, 1);
  ok(dummy_thread_key == 0, "zero key");
  dummy_thread_key= 9999;
  psi->register_thread("123456789012345678901234", bad_thread_1, 1);
  ok(dummy_thread_key == 0, "zero key");
  dummy_thread_key= 9999;
  psi->register_thread("12345678901234567890123", bad_thread_1, 1);
  ok(dummy_thread_key == 1, "assigned key");

  /*
    Test that length('thread/' (7) + category + '/' (1) + name) <= 128
    --> category + name can be up to 120 chars for a thread.
  */

  dummy_thread_key= 9999;
  PSI_thread_info bad_thread_2[]=
  {
    { & dummy_thread_key,
      /* 120 chars name */
      "12345678901234567890123456789012345678901234567890"
      "12345678901234567890123456789012345678901234567890"
      "12345678901234567890",
      0}
  };

  psi->register_thread("X", bad_thread_2, 1);
  ok(dummy_thread_key == 0, "zero key");

  dummy_thread_key= 9999;
  PSI_thread_info bad_thread_3[]=
  {
    { & dummy_thread_key,
      /* 119 chars name */
      "12345678901234567890123456789012345678901234567890"
      "12345678901234567890123456789012345678901234567890"
      "1234567890123456789",
      0}
  };

  psi->register_thread("XX", bad_thread_3, 1);
  ok(dummy_thread_key == 0, "zero key");

  psi->register_thread("X", bad_thread_3, 1);
  ok(dummy_thread_key == 2, "assigned key");

  /*
    Test that length('wait/io/file/' (13) + category + '/' (1)) < 32
    --> category can be up to 17 chars for a file.
  */

  PSI_file_key dummy_file_key= 9999;
  PSI_file_info bad_file_1[]=
  {
    { & dummy_file_key, "X", 0}
  };

  psi->register_file("/", bad_file_1, 1);
  ok(dummy_file_key == 0, "zero key");
  dummy_file_key= 9999;
  psi->register_file("a/", bad_file_1, 1);
  ok(dummy_file_key == 0, "zero key");
  dummy_file_key= 9999;
  psi->register_file("/b", bad_file_1, 1);
  ok(dummy_file_key == 0, "zero key");
  dummy_file_key= 9999;
  psi->register_file("a/b", bad_file_1, 1);
  ok(dummy_file_key == 0, "zero key");
  dummy_file_key= 9999;
  psi->register_file("123456789012345678", bad_file_1, 1);
  ok(dummy_file_key == 0, "zero key");
  dummy_file_key= 9999;
  psi->register_file("12345678901234567", bad_file_1, 1);
  ok(dummy_file_key == 1, "assigned key");

  /*
    Test that length('wait/io/file/' (13) + category + '/' (1) + name) <= 128
    --> category + name can be up to 114 chars for a file.
  */

  dummy_file_key= 9999;
  PSI_file_info bad_file_2[]=
  {
    { & dummy_file_key,
      /* 114 chars name */
      "12345678901234567890123456789012345678901234567890"
      "12345678901234567890123456789012345678901234567890"
      "12345678901234",
      0}
  };

  psi->register_file("X", bad_file_2, 1);
  ok(dummy_file_key == 0, "zero key");

  dummy_file_key= 9999;
  PSI_file_info bad_file_3[]=
  {
    { & dummy_file_key,
      /* 113 chars name */
      "12345678901234567890123456789012345678901234567890"
      "12345678901234567890123456789012345678901234567890"
      "1234567890123",
      0}
  };

  psi->register_file("XX", bad_file_3, 1);
  ok(dummy_file_key == 0, "zero key");

  psi->register_file("X", bad_file_3, 1);
  ok(dummy_file_key == 2, "assigned key");

 /*
    Test that length('wait/io/socket/' (15) + category + '/' (1)) < 32
    --> category can be up to 15 chars for a socket.
  */

  PSI_socket_key dummy_socket_key= 9999;
  PSI_socket_info bad_socket_1[]=
  {
    { & dummy_socket_key, "X", 0}
  };

  psi->register_socket("/", bad_socket_1, 1);
  ok(dummy_socket_key == 0, "zero key");
  dummy_socket_key= 9999;
  psi->register_socket("a/", bad_socket_1, 1);
  ok(dummy_socket_key == 0, "zero key");
  dummy_socket_key= 9999;
  psi->register_socket("/b", bad_socket_1, 1);
  ok(dummy_socket_key == 0, "zero key");
  dummy_socket_key= 9999;
  psi->register_socket("a/b", bad_socket_1, 1);
  ok(dummy_socket_key == 0, "zero key");
  dummy_socket_key= 9999;
  psi->register_socket("1234567890123456", bad_socket_1, 1);
  ok(dummy_socket_key == 0, "zero key");
  dummy_socket_key= 9999;
  psi->register_socket("123456789012345", bad_socket_1, 1);
  ok(dummy_socket_key == 1, "assigned key");

  /*
    Test that length('wait/io/socket/' (15) + category + '/' (1) + name) <= 128
    --> category + name can be up to 112 chars for a socket.
  */

  dummy_socket_key= 9999;
  PSI_socket_info bad_socket_2[]=
  {
    { & dummy_socket_key,
      /* 112 chars name */
      "12345678901234567890123456789012345678901234567890"
      "12345678901234567890123456789012345678901234567890"
      "123456789012",
      0}
  };

  psi->register_socket("X", bad_socket_2, 1);
  ok(dummy_socket_key == 0, "zero key");

  dummy_socket_key= 9999;
  PSI_socket_info bad_socket_3[]=
  {
    { & dummy_socket_key,
      /* 111 chars name */
      "12345678901234567890123456789012345678901234567890"
      "12345678901234567890123456789012345678901234567890"
      "12345678901",
      0}
  };

  psi->register_socket("XX", bad_socket_3, 1);
  ok(dummy_socket_key == 0, "zero key");

  psi->register_socket("X", bad_socket_3, 1);
  ok(dummy_socket_key == 2, "assigned key");

  unload_performance_schema();
}

void test_init_disabled()
{
  PSI *psi;

  diag("test_init_disabled");

  psi= load_perfschema();

  PSI_mutex_key mutex_key_A;
  PSI_mutex_info all_mutex[]=
  {
    { & mutex_key_A, "M-A", 0}
  };

  PSI_rwlock_key rwlock_key_A;
  PSI_rwlock_info all_rwlock[]=
  {
    { & rwlock_key_A, "RW-A", 0}
  };

  PSI_cond_key cond_key_A;
  PSI_cond_info all_cond[]=
  {
    { & cond_key_A, "C-A", 0}
  };

  PSI_file_key file_key_A;
  PSI_file_info all_file[]=
  {
    { & file_key_A, "F-A", 0}
  };

  PSI_socket_key socket_key_A;
  PSI_socket_info all_socket[]=
  {
    { & socket_key_A, "S-A", 0}
  };

  PSI_thread_key thread_key_1;
  PSI_thread_info all_thread[]=
  {
    { & thread_key_1, "T-1", 0}
  };

  psi->register_mutex("test", all_mutex, 1);
  psi->register_rwlock("test", all_rwlock, 1);
  psi->register_cond("test", all_cond, 1);
  psi->register_file("test", all_file, 1);
  psi->register_socket("test", all_socket, 1);
  psi->register_thread("test", all_thread, 1);

  PFS_mutex_class *mutex_class_A;
  PFS_rwlock_class *rwlock_class_A;
  PFS_cond_class *cond_class_A;
  PFS_file_class *file_class_A;
  PFS_socket_class *socket_class_A;
  PSI_mutex *mutex_A1;
  PSI_rwlock *rwlock_A1;
  PSI_cond *cond_A1;
  PFS_file *file_A1;
  PSI_socket *socket_A1;
  PSI_thread *thread_1;

  /* Preparation */

  thread_1= psi->new_thread(thread_key_1, NULL, 0);
  ok(thread_1 != NULL, "T-1");
  psi->set_thread_id(thread_1, 1);

  mutex_class_A= find_mutex_class(mutex_key_A);
  ok(mutex_class_A != NULL, "mutex class A");

  rwlock_class_A= find_rwlock_class(rwlock_key_A);
  ok(rwlock_class_A != NULL, "rwlock class A");

  cond_class_A= find_cond_class(cond_key_A);
  ok(cond_class_A != NULL, "cond class A");

  file_class_A= find_file_class(file_key_A);
  ok(file_class_A != NULL, "file class A");

  socket_class_A= find_socket_class(socket_key_A);
  ok(socket_class_A != NULL, "socket class A");

  /*
    Pretend thread T-1 is running, and disabled, with thread_instrumentation.
    Disabled instruments are still created so they can be enabled later.
  */

  /* ------------------------------------------------------------------------ */

  psi->set_thread(thread_1);
  setup_thread(thread_1, false);

  /* disabled M-A + disabled T-1: instrumentation */

  mutex_class_A->m_enabled= false;
  mutex_A1= psi->init_mutex(mutex_key_A, NULL);
  ok(mutex_A1 != NULL, "mutex_A1 disabled, instrumented");

  /* enabled M-A + disabled T-1: instrumentation (for later) */

  mutex_class_A->m_enabled= true;
  mutex_A1= psi->init_mutex(mutex_key_A, NULL);
  ok(mutex_A1 != NULL, "mutex_A1 enabled, instrumented");

  /* broken key + disabled T-1: no instrumentation */

  mutex_class_A->m_enabled= true;
  mutex_A1= psi->init_mutex(0, NULL);
  ok(mutex_A1 == NULL, "mutex key 0 not instrumented");
  mutex_A1= psi->init_mutex(99, NULL);
  ok(mutex_A1 == NULL, "broken mutex key not instrumented");

  /* disabled RW-A + disabled T-1: no instrumentation */

  rwlock_class_A->m_enabled= false;
  rwlock_A1= psi->init_rwlock(rwlock_key_A, NULL);
  ok(rwlock_A1 != NULL, "rwlock_A1 disabled, instrumented");

  /* enabled RW-A + disabled T-1: instrumentation (for later) */

  rwlock_class_A->m_enabled= true;
  rwlock_A1= psi->init_rwlock(rwlock_key_A, NULL);
  ok(rwlock_A1 != NULL, "rwlock_A1 enabled, instrumented");

  /* broken key + disabled T-1: no instrumentation */

  rwlock_class_A->m_enabled= true;
  rwlock_A1= psi->init_rwlock(0, NULL);
  ok(rwlock_A1 == NULL, "rwlock key 0 not instrumented");
  rwlock_A1= psi->init_rwlock(99, NULL);
  ok(rwlock_A1 == NULL, "broken rwlock key not instrumented");

  /* disabled C-A + disabled T-1: no instrumentation */

  cond_class_A->m_enabled= false;
  cond_A1= psi->init_cond(cond_key_A, NULL);
  ok(cond_A1 != NULL, "cond_A1 disabled, instrumented");

  /* enabled C-A + disabled T-1: instrumentation (for later) */

  cond_class_A->m_enabled= true;
  cond_A1= psi->init_cond(cond_key_A, NULL);
  ok(cond_A1 != NULL, "cond_A1 enabled, instrumented");

  /* broken key + disabled T-1: no instrumentation */

  cond_class_A->m_enabled= true;
  cond_A1= psi->init_cond(0, NULL);
  ok(cond_A1 == NULL, "cond key 0 not instrumented");
  cond_A1= psi->init_cond(99, NULL);
  ok(cond_A1 == NULL, "broken cond key not instrumented");

  /* disabled F-A + disabled T-1: no instrumentation */

  file_class_A->m_enabled= false;
  psi->create_file(file_key_A, "foo", (File) 12);
  file_A1= lookup_file_by_name("foo");
  ok(file_A1 == NULL, "not instrumented");

  /* enabled F-A + disabled T-1: no instrumentation */

  file_class_A->m_enabled= true;
  psi->create_file(file_key_A, "foo", (File) 12);
  file_A1= lookup_file_by_name("foo");
  ok(file_A1 == NULL, "not instrumented");

  /* broken key + disabled T-1: no instrumentation */

  file_class_A->m_enabled= true;
  psi->create_file(0, "foo", (File) 12);
  file_A1= lookup_file_by_name("foo");
  ok(file_A1 == NULL, "file_A1 not instrumented");
  psi->create_file(99, "foo", (File) 12);
  file_A1= lookup_file_by_name("foo");
  ok(file_A1 == NULL, "file_A1 not instrumented");

  /* disabled S-A + disabled T-1: no instrumentation */

  socket_class_A->m_enabled= false;
  socket_A1= psi->init_socket(socket_key_A, NULL, NULL, 0);
  ok(socket_A1 != NULL, "socket_A1 disabled, instrumented");

  /* enabled S-A + disabled T-1: instrumentation (for later) */

  socket_class_A->m_enabled= true;
  socket_A1= psi->init_socket(socket_key_A, NULL, NULL, 0);
  ok(socket_A1 != NULL, "socket_A1 enabled, instrumented");

  /* broken key + disabled T-1: no instrumentation */

  socket_class_A->m_enabled= true;
  socket_A1= psi->init_socket(0, NULL, NULL, 0);
  ok(socket_A1 == NULL, "socket key 0 not instrumented");
  socket_A1= psi->init_socket(99, NULL, NULL, 0);
  ok(socket_A1 == NULL, "broken socket key not instrumented");

  /* Pretend thread T-1 is enabled */
  /* ----------------------------- */

  setup_thread(thread_1, true);

  /* disabled M-A + enabled T-1: no instrumentation */

  mutex_class_A->m_enabled= false;
  mutex_A1= psi->init_mutex(mutex_key_A, NULL);
  ok(mutex_A1 != NULL, "mutex_A1 disabled, instrumented");

  /* enabled M-A + enabled T-1: instrumentation */

  mutex_class_A->m_enabled= true;
  mutex_A1= psi->init_mutex(mutex_key_A, NULL);
  ok(mutex_A1 != NULL, "mutex_A1 enabled, instrumented");
  psi->destroy_mutex(mutex_A1);

  /* broken key + enabled T-1: no instrumentation */

  mutex_class_A->m_enabled= true;
  mutex_A1= psi->init_mutex(0, NULL);
  ok(mutex_A1 == NULL, "mutex_A1 not instrumented");
  mutex_A1= psi->init_mutex(99, NULL);
  ok(mutex_A1 == NULL, "mutex_A1 not instrumented");

  /* disabled RW-A + enabled T-1: no instrumentation */

  rwlock_class_A->m_enabled= false;
  rwlock_A1= psi->init_rwlock(rwlock_key_A, NULL);
  ok(rwlock_A1 != NULL, "rwlock_A1 disabled, instrumented");

  /* enabled RW-A + enabled T-1: instrumentation */

  rwlock_class_A->m_enabled= true;
  rwlock_A1= psi->init_rwlock(rwlock_key_A, NULL);
  ok(rwlock_A1 != NULL, "rwlock_A1 enabled, instrumented");
  psi->destroy_rwlock(rwlock_A1);

  /* broken key + enabled T-1: no instrumentation */

  rwlock_class_A->m_enabled= true;
  rwlock_A1= psi->init_rwlock(0, NULL);
  ok(rwlock_A1 == NULL, "rwlock_A1 not instrumented");
  rwlock_A1= psi->init_rwlock(99, NULL);
  ok(rwlock_A1 == NULL, "rwlock_A1 not instrumented");

  /* disabled C-A + enabled T-1: no instrumentation */

  cond_class_A->m_enabled= false;
  cond_A1= psi->init_cond(cond_key_A, NULL);
  ok(cond_A1 != NULL, "cond_A1 disabled, instrumented");

  /* enabled C-A + enabled T-1: instrumentation */

  cond_class_A->m_enabled= true;
  cond_A1= psi->init_cond(cond_key_A, NULL);
  ok(cond_A1 != NULL, "cond_A1 enabled, instrumented");
  psi->destroy_cond(cond_A1);

  /* broken key + enabled T-1: no instrumentation */

  cond_class_A->m_enabled= true;
  cond_A1= psi->init_cond(0, NULL);
  ok(cond_A1 == NULL, "cond_A1 not instrumented");
  cond_A1= psi->init_cond(99, NULL);
  ok(cond_A1 == NULL, "cond_A1 not instrumented");

  /* disabled F-A + enabled T-1: no instrumentation */

  file_class_A->m_enabled= false;
  psi->create_file(file_key_A, "foo", (File) 12);
  file_A1= lookup_file_by_name("foo");
  ok(file_A1 == NULL, "file_A1 not instrumented");

  /* enabled F-A + open failed + enabled T-1: no instrumentation */

  file_class_A->m_enabled= true;
  psi->create_file(file_key_A, "foo", (File) -1);
  file_A1= lookup_file_by_name("foo");
  ok(file_A1 == NULL, "file_A1 not instrumented");

  /* enabled F-A + out-of-descriptors + enabled T-1: no instrumentation */

  file_class_A->m_enabled= true;
  psi->create_file(file_key_A, "foo", (File) 65000);
  file_A1= lookup_file_by_name("foo");
  ok(file_A1 == NULL, "file_A1 not instrumented");
  ok(file_handle_lost == 1, "lost a file handle");
  file_handle_lost= 0;

  /* enabled F-A + enabled T-1: instrumentation */

  file_class_A->m_enabled= true;
  psi->create_file(file_key_A, "foo-instrumented", (File) 12);
  file_A1= lookup_file_by_name("foo-instrumented");
  ok(file_A1 != NULL, "file_A1 instrumented");
  destroy_file(reinterpret_cast<PFS_thread*>(psi->get_thread()), file_A1);

  /* broken key + enabled T-1: no instrumentation */

  file_class_A->m_enabled= true;
  psi->create_file(0, "foo", (File) 12);
  file_A1= lookup_file_by_name("foo");
  ok(file_A1 == NULL, "file key 0 not instrumented");
  psi->create_file(99, "foo", (File) 12);
  file_A1= lookup_file_by_name("foo");
  ok(file_A1 == NULL, "broken file key not instrumented");

  /* disabled S-A + enabled T-1: no instrumentation */

  socket_class_A->m_enabled= false;
  ok(socket_A1 == NULL, "socket_A1 not instrumented");

  /* enabled S-A + enabled T-1: instrumentation */

  socket_class_A->m_enabled= true;
  socket_A1= psi->init_socket(socket_key_A, NULL, NULL, 0);
  ok(socket_A1 != NULL, "socket_A1 instrumented");
  psi->destroy_socket(socket_A1);

  /* broken key + enabled T-1: no instrumentation */

  socket_class_A->m_enabled= true;
  socket_A1= psi->init_socket(0, NULL, NULL, 0);
  ok(socket_A1 == NULL, "socket_A1 not instrumented");
  socket_A1= psi->init_socket(99, NULL, NULL, 0);
  ok(socket_A1 == NULL, "socket_A1 not instrumented");

  /* Pretend the running thread is not instrumented */
  /* ---------------------------------------------- */

  psi->delete_current_thread();

  /* disabled M-A + unknown thread: no instrumentation */

  mutex_class_A->m_enabled= false;
  mutex_A1= psi->init_mutex(mutex_key_A, NULL);
  ok(mutex_A1 != NULL, "mutex_A1 disabled, instrumented");

  /* enabled M-A + unknown thread: instrumentation (for later) */

  mutex_class_A->m_enabled= true;
  mutex_A1= psi->init_mutex(mutex_key_A, NULL);
  ok(mutex_A1 != NULL, "mutex_A1 enabled, instrumented");

  /* broken key + unknown thread: no instrumentation */

  mutex_class_A->m_enabled= true;
  mutex_A1= psi->init_mutex(0, NULL);
  ok(mutex_A1 == NULL, "mutex key 0 not instrumented");
  mutex_A1= psi->init_mutex(99, NULL);
  ok(mutex_A1 == NULL, "broken mutex key not instrumented");

  /* disabled RW-A + unknown thread: no instrumentation */

  rwlock_class_A->m_enabled= false;
  rwlock_A1= psi->init_rwlock(rwlock_key_A, NULL);
  ok(rwlock_A1 != NULL, "rwlock_A1 disabled, instrumented");

  /* enabled RW-A + unknown thread: instrumentation (for later) */

  rwlock_class_A->m_enabled= true;
  rwlock_A1= psi->init_rwlock(rwlock_key_A, NULL);
  ok(rwlock_A1 != NULL, "rwlock_A1 enabled, instrumented");

  /* broken key + unknown thread: no instrumentation */

  rwlock_class_A->m_enabled= true;
  rwlock_A1= psi->init_rwlock(0, NULL);
  ok(rwlock_A1 == NULL, "rwlock key 0 not instrumented");
  rwlock_A1= psi->init_rwlock(99, NULL);
  ok(rwlock_A1 == NULL, "broken rwlock key not instrumented");

  /* disabled C-A + unknown thread: no instrumentation */

  cond_class_A->m_enabled= false;
  cond_A1= psi->init_cond(cond_key_A, NULL);
  ok(cond_A1 != NULL, "cond_A1 disabled, instrumented");

  /* enabled C-A + unknown thread: instrumentation (for later) */

  cond_class_A->m_enabled= true;
  cond_A1= psi->init_cond(cond_key_A, NULL);
  ok(cond_A1 != NULL, "cond_A1 enabled, instrumented");

  /* broken key + unknown thread: no instrumentation */

  cond_class_A->m_enabled= true;
  cond_A1= psi->init_cond(0, NULL);
  ok(cond_A1 == NULL, "cond key 0 not instrumented");
  cond_A1= psi->init_cond(99, NULL);
  ok(cond_A1 == NULL, "broken cond key not instrumented");

  /* disabled F-A + unknown thread: no instrumentation */

  file_class_A->m_enabled= false;
  psi->create_file(file_key_A, "foo", (File) 12);
  file_A1= lookup_file_by_name("foo");
  ok(file_A1 == NULL, "file_A1 not instrumented");

  /* enabled F-A + unknown thread: no instrumentation */

  file_class_A->m_enabled= true;
  psi->create_file(file_key_A, "foo", (File) 12);
  file_A1= lookup_file_by_name("foo");
  ok(file_A1 == NULL, "file_A1 not instrumented");

  /* broken key + unknown thread: no instrumentation */

  file_class_A->m_enabled= true;
  psi->create_file(0, "foo", (File) 12);
  file_A1= lookup_file_by_name("foo");
  ok(file_A1 == NULL, "not instrumented");
  psi->create_file(99, "foo", (File) 12);
  file_A1= lookup_file_by_name("foo");
  ok(file_A1 == NULL, "not instrumented");

  /* disabled S-A + unknown thread: no instrumentation */

  socket_class_A->m_enabled= false;
  socket_A1= psi->init_socket(socket_key_A, NULL, NULL, 0);
  ok(socket_A1 != NULL, "socket_A1 disabled, instrumented");

  /* enabled S-A + unknown thread: instrumentation (for later) */

  socket_class_A->m_enabled= true;
  socket_A1= psi->init_socket(socket_key_A, NULL, NULL, 0);
  ok(socket_A1 != NULL, "socket_A1 enabled, instrumented");

  /* broken key + unknown thread: no instrumentation */

  socket_class_A->m_enabled= true;
  socket_A1= psi->init_socket(0, NULL, NULL, 0);
  ok(socket_A1 == NULL, "socket key 0 not instrumented");
  socket_A1= psi->init_socket(99, NULL, NULL, 0);
  ok(socket_A1 == NULL, "broken socket key not instrumented");
  
  unload_performance_schema();
}

void test_locker_disabled()
{
  PSI *psi;

  diag("test_locker_disabled");

  psi= load_perfschema();

  PSI_mutex_key mutex_key_A;
  PSI_mutex_info all_mutex[]=
  {
    { & mutex_key_A, "M-A", 0}
  };

  PSI_rwlock_key rwlock_key_A;
  PSI_rwlock_info all_rwlock[]=
  {
    { & rwlock_key_A, "RW-A", 0}
  };

  PSI_cond_key cond_key_A;
  PSI_cond_info all_cond[]=
  {
    { & cond_key_A, "C-A", 0}
  };

  PSI_file_key file_key_A;
  PSI_file_info all_file[]=
  {
    { & file_key_A, "F-A", 0}
  };

  PSI_socket_key socket_key_A;
  PSI_socket_info all_socket[]=
  {
    { & socket_key_A, "S-A", 0}
  };

  PSI_thread_key thread_key_1;
  PSI_thread_info all_thread[]=
  {
    { & thread_key_1, "T-1", 0}
  };

  psi->register_mutex("test", all_mutex, 1);
  psi->register_rwlock("test", all_rwlock, 1);
  psi->register_cond("test", all_cond, 1);
  psi->register_file("test", all_file, 1);
  psi->register_socket("test", all_socket, 1);
  psi->register_thread("test", all_thread, 1);

  PFS_mutex_class *mutex_class_A;
  PFS_rwlock_class *rwlock_class_A;
  PFS_cond_class *cond_class_A;
  PFS_file_class *file_class_A;
  PFS_socket_class *socket_class_A;
  PSI_mutex *mutex_A1;
  PSI_rwlock *rwlock_A1;
  PSI_cond *cond_A1;
  PSI_file *file_A1;
  PSI_socket *socket_A1;
  PSI_thread *thread_1;

  /* Preparation */

  thread_1= psi->new_thread(thread_key_1, NULL, 0);
  ok(thread_1 != NULL, "T-1");
  psi->set_thread_id(thread_1, 1);

  mutex_class_A= find_mutex_class(mutex_key_A);
  ok(mutex_class_A != NULL, "mutex info A");

  rwlock_class_A= find_rwlock_class(rwlock_key_A);
  ok(rwlock_class_A != NULL, "rwlock info A");

  cond_class_A= find_cond_class(cond_key_A);
  ok(cond_class_A != NULL, "cond info A");

  file_class_A= find_file_class(file_key_A);
  ok(file_class_A != NULL, "file info A");

  socket_class_A= find_socket_class(socket_key_A);
  ok(socket_class_A != NULL, "socket info A");

  /* Pretend thread T-1 is running, and enabled */
  /* ------------------------------------------ */

  psi->set_thread(thread_1);
  setup_thread(thread_1, true);

  /* Enable all instruments, instantiate objects */

  mutex_class_A->m_enabled= true;
  mutex_A1= psi->init_mutex(mutex_key_A, NULL);
  ok(mutex_A1 != NULL, "instrumented");

  rwlock_class_A->m_enabled= true;
  rwlock_A1= psi->init_rwlock(rwlock_key_A, NULL);
  ok(rwlock_A1 != NULL, "instrumented");

  cond_class_A->m_enabled= true;
  cond_A1= psi->init_cond(cond_key_A, NULL);
  ok(cond_A1 != NULL, "instrumented");

  file_class_A->m_enabled= true;
  psi->create_file(file_key_A, "foo", (File) 12);
  file_A1= (PSI_file*) lookup_file_by_name("foo");
  ok(file_A1 != NULL, "instrumented");
  destroy_file(reinterpret_cast<PFS_thread*>(psi->get_thread()),
               reinterpret_cast<PFS_file*>(file_A1));

  socket_class_A->m_enabled= true;
  socket_A1= psi->init_socket(socket_key_A, NULL, NULL, 0);
  ok(socket_A1 != NULL, "instrumented");

  /* Socket lockers require a thread owner */
  psi->set_socket_thread_owner(socket_A1);

  PSI_mutex_locker *mutex_locker;
  PSI_mutex_locker_state mutex_state;
  PSI_rwlock_locker *rwlock_locker;
  PSI_rwlock_locker_state rwlock_state;
  PSI_cond_locker *cond_locker;
  PSI_cond_locker_state cond_state;
  PSI_file_locker *file_locker;
  PSI_file_locker_state file_state;
  PSI_socket_locker *socket_locker;
  PSI_socket_locker_state socket_state;

  /* Pretend thread T-1 is disabled */
  /* ------------------------------ */

  setup_thread(thread_1, false);
  flag_events_waits_current= true;
  mutex_class_A->m_enabled= true;
  rwlock_class_A->m_enabled= true;
  cond_class_A->m_enabled= true;
  file_class_A->m_enabled= true;
  socket_class_A->m_enabled= true;

  mutex_locker= psi->start_mutex_wait(&mutex_state, mutex_A1, PSI_MUTEX_LOCK, "foo.cc", 12);
  ok(mutex_locker == NULL, "no locker (T-1 disabled)");
  rwlock_locker= psi->start_rwlock_rdwait(&rwlock_state, rwlock_A1, PSI_RWLOCK_READLOCK, "foo.cc", 12);
  ok(rwlock_locker == NULL, "no locker (T-1 disabled)");
  cond_locker= psi->start_cond_wait(&cond_state, cond_A1, mutex_A1, PSI_COND_WAIT, "foo.cc", 12);
  ok(cond_locker == NULL, "no locker (T-1 disabled)");
  file_locker= psi->get_thread_file_name_locker(&file_state, file_key_A, PSI_FILE_OPEN, "xxx", NULL);
  ok(file_locker == NULL, "no locker (T-1 disabled)");
  file_locker= psi->get_thread_file_stream_locker(&file_state, file_A1, PSI_FILE_READ);
  ok(file_locker == NULL, "no locker (T-1 disabled)");
  file_locker= psi->get_thread_file_descriptor_locker(&file_state, (File) 12, PSI_FILE_READ);
  ok(file_locker == NULL, "no locker (T-1 disabled)");
  socket_locker= psi->start_socket_wait(&socket_state, socket_A1, PSI_SOCKET_SEND, 12, "foo.cc", 12);
  ok(socket_locker == NULL, "no locker (T-1 disabled)");

  /* Pretend the global consumer is disabled */
  /* --------------------------------------- */

  setup_thread(thread_1, true);
  flag_global_instrumentation= false;
  mutex_class_A->m_enabled= true;
  rwlock_class_A->m_enabled= true;
  cond_class_A->m_enabled= true;
  file_class_A->m_enabled= true;
  socket_class_A->m_enabled= true;
  update_instruments_derived_flags();

  mutex_locker= psi->start_mutex_wait(&mutex_state, mutex_A1, PSI_MUTEX_LOCK, "foo.cc", 12);
  ok(mutex_locker == NULL, "no locker (global disabled)");
  rwlock_locker= psi->start_rwlock_rdwait(&rwlock_state, rwlock_A1, PSI_RWLOCK_READLOCK, "foo.cc", 12);
  ok(rwlock_locker == NULL, "no locker (global disabled)");
  cond_locker= psi->start_cond_wait(&cond_state, cond_A1, mutex_A1, PSI_COND_WAIT, "foo.cc", 12);
  ok(cond_locker == NULL, "no locker (global disabled)");
  file_locker= psi->get_thread_file_name_locker(&file_state, file_key_A, PSI_FILE_OPEN, "xxx", NULL);
  ok(file_locker == NULL, "no locker (global disabled)");
  file_locker= psi->get_thread_file_stream_locker(&file_state, file_A1, PSI_FILE_READ);
  ok(file_locker == NULL, "no locker (global disabled)");
  file_locker= psi->get_thread_file_descriptor_locker(&file_state, (File) 12, PSI_FILE_READ);
  ok(file_locker == NULL, "no locker (global disabled)");
  socket_locker= psi->start_socket_wait(&socket_state, socket_A1, PSI_SOCKET_SEND, 12, "foo.cc", 12);
  ok(socket_locker == NULL, "no locker (global disabled)");

  /* Pretend the mode is global, counted only */
  /* ---------------------------------------- */

  setup_thread(thread_1, true);
  flag_global_instrumentation= true;
  flag_thread_instrumentation= false;
  mutex_class_A->m_enabled= true;
  mutex_class_A->m_timed= false;
  rwlock_class_A->m_enabled= true;
  rwlock_class_A->m_timed= false;
  cond_class_A->m_enabled= true;
  cond_class_A->m_timed= false;
  file_class_A->m_enabled= true;
  file_class_A->m_timed= false;
  socket_class_A->m_enabled= true;
  socket_class_A->m_timed= false;
  update_instruments_derived_flags();

  mutex_locker= psi->start_mutex_wait(&mutex_state, mutex_A1, PSI_MUTEX_LOCK, "foo.cc", 12);
  ok(mutex_locker == NULL, "no locker (global counted)");
  rwlock_locker= psi->start_rwlock_rdwait(&rwlock_state, rwlock_A1, PSI_RWLOCK_READLOCK, "foo.cc", 12);
  ok(rwlock_locker == NULL, "no locker (global counted)");
  cond_locker= psi->start_cond_wait(&cond_state, cond_A1, mutex_A1, PSI_COND_WAIT, "foo.cc", 12);
  ok(cond_locker == NULL, "no locker (global counted)");
  file_locker= psi->get_thread_file_name_locker(&file_state, file_key_A, PSI_FILE_OPEN, "xxx", NULL);
  ok(file_locker != NULL, "locker (global counted)");
  psi->start_file_wait(file_locker, 10, __FILE__, __LINE__);
  psi->end_file_wait(file_locker, 10);
  file_locker= psi->get_thread_file_stream_locker(&file_state, file_A1, PSI_FILE_READ);
  ok(file_locker != NULL, "locker (global counted)");
  psi->start_file_wait(file_locker, 10, __FILE__, __LINE__);
  psi->end_file_wait(file_locker, 10);
  file_locker= psi->get_thread_file_descriptor_locker(&file_state, (File) 12, PSI_FILE_READ);
  ok(file_locker != NULL, "locker (global counted)");
  psi->start_file_wait(file_locker, 10, __FILE__, __LINE__);
  psi->end_file_wait(file_locker, 10);
  /* The null locker shortcut applies only to socket ops with no byte count */
  socket_locker= psi->start_socket_wait(&socket_state, socket_A1, PSI_SOCKET_BIND, 0, "foo.cc", 12);
  ok(socket_locker == NULL, "no locker (global counted)");

  /* TODO */

  /* Pretend the instrument is disabled */
  /* ---------------------------------- */

  setup_thread(thread_1, true);
  flag_global_instrumentation= true;
  flag_events_waits_current= true;
  mutex_class_A->m_enabled= false;
  rwlock_class_A->m_enabled= false;
  cond_class_A->m_enabled= false;
  file_class_A->m_enabled= false;
  socket_class_A->m_enabled= false;
  update_instruments_derived_flags();

  mutex_locker= psi->start_mutex_wait(&mutex_state, mutex_A1, PSI_MUTEX_LOCK, "foo.cc", 12);
  ok(mutex_locker == NULL, "no locker");
  rwlock_locker= psi->start_rwlock_rdwait(&rwlock_state, rwlock_A1, PSI_RWLOCK_READLOCK, "foo.cc", 12);
  ok(rwlock_locker == NULL, "no locker");
  cond_locker= psi->start_cond_wait(&cond_state, cond_A1, mutex_A1, PSI_COND_WAIT, "foo.cc", 12);
  ok(cond_locker == NULL, "no locker");
  file_locker= psi->get_thread_file_name_locker(&file_state, file_key_A, PSI_FILE_OPEN, "xxx", NULL);
  ok(file_locker == NULL, "no locker");
  file_locker= psi->get_thread_file_stream_locker(&file_state, file_A1, PSI_FILE_READ);
  ok(file_locker == NULL, "no locker");
  file_locker= psi->get_thread_file_descriptor_locker(&file_state, (File) 12, PSI_FILE_READ);
  ok(file_locker == NULL, "no locker");
  socket_locker= psi->start_socket_wait(&socket_state, socket_A1, PSI_SOCKET_SEND, 12, "foo.cc", 12);
  ok(socket_locker == NULL, "no locker");

  /* Pretend everything is enabled and timed */
  /* --------------------------------------- */

  setup_thread(thread_1, true);
  flag_global_instrumentation= true;
  flag_thread_instrumentation= true;
  flag_events_waits_current= true;
  mutex_class_A->m_enabled= true;
  mutex_class_A->m_timed= true;
  rwlock_class_A->m_enabled= true;
  rwlock_class_A->m_timed= true;
  cond_class_A->m_enabled= true;
  cond_class_A->m_timed= true;
  file_class_A->m_enabled= true;
  file_class_A->m_timed= true;
  socket_class_A->m_enabled= true;
  socket_class_A->m_timed= true;
  update_instruments_derived_flags();

  mutex_locker= psi->start_mutex_wait(&mutex_state, mutex_A1, PSI_MUTEX_LOCK, __FILE__, __LINE__);
  ok(mutex_locker != NULL, "locker");
  psi->end_mutex_wait(mutex_locker, 0);
  rwlock_locker= psi->start_rwlock_rdwait(&rwlock_state, rwlock_A1, PSI_RWLOCK_READLOCK, __FILE__, __LINE__);
  ok(rwlock_locker != NULL, "locker");
  psi->end_rwlock_rdwait(rwlock_locker, 0);
  cond_locker= psi->start_cond_wait(&cond_state, cond_A1, mutex_A1, PSI_COND_WAIT, __FILE__, __LINE__);
  ok(cond_locker != NULL, "locker");
  psi->end_cond_wait(cond_locker, 0);
  file_locker= psi->get_thread_file_name_locker(&file_state, file_key_A, PSI_FILE_STREAM_OPEN, "xxx", NULL);
  ok(file_locker != NULL, "locker");
  psi->start_file_open_wait(file_locker, __FILE__, __LINE__);
  psi->end_file_open_wait(file_locker, NULL);
  file_locker= psi->get_thread_file_stream_locker(&file_state, file_A1, PSI_FILE_READ);
  ok(file_locker != NULL, "locker");
  psi->start_file_wait(file_locker, 10, __FILE__, __LINE__);
  psi->end_file_wait(file_locker, 10);
  file_locker= psi->get_thread_file_descriptor_locker(&file_state, (File) 12, PSI_FILE_READ);
  ok(file_locker != NULL, "locker");
  psi->start_file_wait(file_locker, 10, __FILE__, __LINE__);
  psi->end_file_wait(file_locker, 10);
  socket_locker= psi->start_socket_wait(&socket_state, socket_A1, PSI_SOCKET_SEND, 12, "foo.cc", 12);
  ok(socket_locker != NULL, "locker");
  psi->end_socket_wait(socket_locker, 10);

  /* Pretend the socket does not have a thread owner */
  /* ---------------------------------------------- */

  socket_class_A->m_enabled= true;
  socket_A1= psi->init_socket(socket_key_A, NULL, NULL, 0);
  ok(socket_A1 != NULL, "instrumented");
  /* Socket thread owner has not been set */
  socket_locker= psi->start_socket_wait(&socket_state, socket_A1, PSI_SOCKET_SEND, 12, "foo.cc", 12);
  ok(socket_locker != NULL, "locker (owner not used)");
  psi->end_socket_wait(socket_locker, 10);

  /* Pretend the running thread is not instrumented */
  /* ---------------------------------------------- */

  psi->delete_current_thread();
  flag_events_waits_current= true;
  mutex_class_A->m_enabled= true;
  rwlock_class_A->m_enabled= true;
  cond_class_A->m_enabled= true;
  file_class_A->m_enabled= true;
  socket_class_A->m_enabled= true;
  update_instruments_derived_flags();

  mutex_locker= psi->start_mutex_wait(&mutex_state, mutex_A1, PSI_MUTEX_LOCK, "foo.cc", 12);
  ok(mutex_locker == NULL, "no locker");
  rwlock_locker= psi->start_rwlock_rdwait(&rwlock_state, rwlock_A1, PSI_RWLOCK_READLOCK, "foo.cc", 12);
  ok(rwlock_locker == NULL, "no locker");
  cond_locker= psi->start_cond_wait(&cond_state, cond_A1, mutex_A1, PSI_COND_WAIT, "foo.cc", 12);
  ok(cond_locker == NULL, "no locker");
  file_locker= psi->get_thread_file_name_locker(&file_state, file_key_A, PSI_FILE_OPEN, "xxx", NULL);
  ok(file_locker == NULL, "no locker");
  file_locker= psi->get_thread_file_stream_locker(&file_state, file_A1, PSI_FILE_READ);
  ok(file_locker == NULL, "no locker");
  file_locker= psi->get_thread_file_descriptor_locker(&file_state, (File) 12, PSI_FILE_READ);
  ok(file_locker == NULL, "no locker");
  socket_locker= psi->start_socket_wait(&socket_state, socket_A1, PSI_SOCKET_SEND, 12, "foo.cc", 12);
  ok(socket_locker == NULL, "no locker");

  unload_performance_schema();
}

void test_file_instrumentation_leak()
{
  PSI *psi;

  diag("test_file_instrumentation_leak");

  psi= load_perfschema();

  PSI_file_key file_key_A;
  PSI_file_key file_key_B;
  PSI_file_info all_file[]=
  {
    { & file_key_A, "F-A", 0},
    { & file_key_B, "F-B", 0}
  };

  PSI_thread_key thread_key_1;
  PSI_thread_info all_thread[]=
  {
    { & thread_key_1, "T-1", 0}
  };

  psi->register_file("test", all_file, 2);
  psi->register_thread("test", all_thread, 1);

  PFS_file_class *file_class_A;
  PFS_file_class *file_class_B;
  PSI_file_locker_state file_state;
  PSI_thread *thread_1;

  /* Preparation */

  thread_1= psi->new_thread(thread_key_1, NULL, 0);
  ok(thread_1 != NULL, "T-1");
  psi->set_thread_id(thread_1, 1);

  file_class_A= find_file_class(file_key_A);
  ok(file_class_A != NULL, "file info A");

  file_class_B= find_file_class(file_key_B);
  ok(file_class_B != NULL, "file info B");

  psi->set_thread(thread_1);

  /* Pretend everything is enabled */
  /* ----------------------------- */

  setup_thread(thread_1, true);
  flag_events_waits_current= true;
  file_class_A->m_enabled= true;
  file_class_B->m_enabled= true;

  PSI_file_locker *file_locker;

  /* Simulate OPEN + READ of 100 bytes + CLOSE on descriptor 12 */

  file_locker= psi->get_thread_file_name_locker(&file_state, file_key_A, PSI_FILE_OPEN, "AAA", NULL);
  ok(file_locker != NULL, "locker");
  psi->start_file_open_wait(file_locker, __FILE__, __LINE__);
  psi->end_file_open_wait_and_bind_to_descriptor(file_locker, 12);

  file_locker= psi->get_thread_file_descriptor_locker(&file_state, (File) 12, PSI_FILE_READ);
  ok(file_locker != NULL, "locker");
  psi->start_file_wait(file_locker, 100, __FILE__, __LINE__);
  psi->end_file_wait(file_locker, 100);

  file_locker= psi->get_thread_file_descriptor_locker(&file_state, (File) 12, PSI_FILE_CLOSE);
  ok(file_locker != NULL, "locker");
  psi->start_file_wait(file_locker, 0, __FILE__, __LINE__);
  psi->end_file_wait(file_locker, 0);

  /* Simulate uninstrumented-OPEN + WRITE on descriptor 24 */

  file_locker= psi->get_thread_file_descriptor_locker(&file_state, (File) 24, PSI_FILE_WRITE);
  ok(file_locker == NULL, "no locker, since the open was not instrumented");

  /*
    Simulate uninstrumented-OPEN + WRITE on descriptor 12 :
    the instrumentation should not leak (don't charge the file io on unknown B to "AAA")
  */

  file_locker= psi->get_thread_file_descriptor_locker(&file_state, (File) 12, PSI_FILE_WRITE);
  ok(file_locker == NULL, "no locker, no leak");

  unload_performance_schema();
}

void test_enabled()
{
#ifdef LATER
  PSI *psi;

  diag("test_enabled");

  psi= load_perfschema();

  PSI_mutex_key mutex_key_A;
  PSI_mutex_key mutex_key_B;
  PSI_mutex_info all_mutex[]=
  {
    { & mutex_key_A, "M-A", 0},
    { & mutex_key_B, "M-B", 0}
  };

  PSI_rwlock_key rwlock_key_A;
  PSI_rwlock_key rwlock_key_B;
  PSI_rwlock_info all_rwlock[]=
  {
    { & rwlock_key_A, "RW-A", 0},
    { & rwlock_key_B, "RW-B", 0}
  };

  PSI_cond_key cond_key_A;
  PSI_cond_key cond_key_B;
  PSI_cond_info all_cond[]=
  {
    { & cond_key_A, "C-A", 0},
    { & cond_key_B, "C-B", 0}
  };

  unload_performance_schema();
#endif
}

void test_event_name_index()
{
  PSI *psi;
  PSI_bootstrap *boot;
  PFS_global_param param;

  diag("test_event_name_index");

  memset(& param, 0xFF, sizeof(param));
  param.m_enabled= true;

  /* NOTE: Need to add 4 to each index: table io, table lock, idle, metadata lock */

  /* Per mutex info waits should be at [0..9] */
  param.m_mutex_class_sizing= 10;
  /* Per rwlock info waits should be at [10..29] */
  param.m_rwlock_class_sizing= 20;
  /* Per cond info waits should be at [30..69] */
  param.m_cond_class_sizing= 40;
  /* Per file info waits should be at [70..149] */
  param.m_file_class_sizing= 80;
  /* Per socket info waits should be at [150..309] */
  param.m_socket_class_sizing= 160;
  /* Per table info waits should be at [310] */
  param.m_table_share_sizing= 320;

  param.m_thread_class_sizing= 0;
  param.m_user_sizing= 0;
  param.m_account_sizing= 0;
  param.m_host_sizing= 0;
  param.m_stage_class_sizing= 0;
  param.m_events_stages_history_sizing= 0;
  param.m_events_stages_history_long_sizing= 0;
  param.m_statement_class_sizing= 0;
  param.m_events_statements_history_sizing= 0;
  param.m_events_statements_history_long_sizing= 0;
  param.m_events_transactions_history_sizing= 0;
  param.m_events_transactions_history_long_sizing= 0;
  param.m_digest_sizing= 0;
  param.m_session_connect_attrs_sizing= 0;
  param.m_program_sizing= 0;
  param.m_statement_stack_sizing= 10;
  param.m_memory_class_sizing= 12;
  param.m_metadata_lock_sizing= 10;
  param.m_max_digest_length= 0;
  param.m_max_sql_text_length= 1000;

  param.m_mutex_sizing= 0;
  param.m_rwlock_sizing= 0;
  param.m_cond_sizing= 0;
  param.m_thread_sizing= 0;
  param.m_table_sizing= 0;
  param.m_file_sizing= 0;
  param.m_file_handle_sizing= 0;
  param.m_socket_sizing= 0;
  param.m_events_waits_history_sizing= 0;
  param.m_events_waits_history_long_sizing= 0;
  param.m_setup_actor_sizing= 0;
  param.m_setup_object_sizing= 0;

  param.m_hints.m_table_definition_cache = 100;
  param.m_hints.m_table_open_cache       = 100;
  param.m_hints.m_max_connections        = 100;
  param.m_hints.m_open_files_limit       = 100;
  param.m_hints.m_max_prepared_stmt_count= 100;

  pre_initialize_performance_schema();
  boot= initialize_performance_schema(& param);
  ok(boot != NULL, "bootstrap");
  psi= (PSI*) boot->get_interface(PSI_VERSION_1);
  ok(psi != NULL, "psi");

  PFS_mutex_class *mutex_class;
  PSI_mutex_key dummy_mutex_key_1;
  PSI_mutex_key dummy_mutex_key_2;
  PSI_mutex_info dummy_mutexes[]=
  {
    { & dummy_mutex_key_1, "M-1", 0},
    { & dummy_mutex_key_2, "M-2", 0}
  };

  psi->register_mutex("X", dummy_mutexes, 2);
  mutex_class= find_mutex_class(dummy_mutex_key_1);
  ok(mutex_class != NULL, "mutex class 1");
  ok(mutex_class->m_event_name_index == 4, "index 4");
  mutex_class= find_mutex_class(dummy_mutex_key_2);
  ok(mutex_class != NULL, "mutex class 2");
  ok(mutex_class->m_event_name_index == 5, "index 5");

  PFS_rwlock_class *rwlock_class;
  PSI_rwlock_key dummy_rwlock_key_1;
  PSI_rwlock_key dummy_rwlock_key_2;
  PSI_rwlock_info dummy_rwlocks[]=
  {
    { & dummy_rwlock_key_1, "RW-1", 0},
    { & dummy_rwlock_key_2, "RW-2", 0}
  };

  psi->register_rwlock("X", dummy_rwlocks, 2);
  rwlock_class= find_rwlock_class(dummy_rwlock_key_1);
  ok(rwlock_class != NULL, "rwlock class 1");
  ok(rwlock_class->m_event_name_index == 14, "index 14");
  rwlock_class= find_rwlock_class(dummy_rwlock_key_2);
  ok(rwlock_class != NULL, "rwlock class 2");
  ok(rwlock_class->m_event_name_index == 15, "index 15");

  PFS_cond_class *cond_class;
  PSI_cond_key dummy_cond_key_1;
  PSI_cond_key dummy_cond_key_2;
  PSI_cond_info dummy_conds[]=
  {
    { & dummy_cond_key_1, "C-1", 0},
    { & dummy_cond_key_2, "C-2", 0}
  };

  psi->register_cond("X", dummy_conds, 2);
  cond_class= find_cond_class(dummy_cond_key_1);
  ok(cond_class != NULL, "cond class 1");
  ok(cond_class->m_event_name_index == 34, "index 34");
  cond_class= find_cond_class(dummy_cond_key_2);
  ok(cond_class != NULL, "cond class 2");
  ok(cond_class->m_event_name_index == 35, "index 35");

  PFS_file_class *file_class;
  PSI_file_key dummy_file_key_1;
  PSI_file_key dummy_file_key_2;
  PSI_file_info dummy_files[]=
  {
    { & dummy_file_key_1, "F-1", 0},
    { & dummy_file_key_2, "F-2", 0}
  };

  psi->register_file("X", dummy_files, 2);
  file_class= find_file_class(dummy_file_key_1);
  ok(file_class != NULL, "file class 1");
  ok(file_class->m_event_name_index == 74, "index 74");
  file_class= find_file_class(dummy_file_key_2);
  ok(file_class != NULL, "file class 2");
  ok(file_class->m_event_name_index == 75, "index 75");

  PFS_socket_class *socket_class;
  PSI_socket_key dummy_socket_key_1;
  PSI_socket_key dummy_socket_key_2;
  PSI_socket_info dummy_sockets[]=
  {
    { & dummy_socket_key_1, "S-1", 0},
    { & dummy_socket_key_2, "S-2", 0}
  };

  psi->register_socket("X", dummy_sockets, 2);
  socket_class= find_socket_class(dummy_socket_key_1);
  ok(socket_class != NULL, "socket class 1");
  ok(socket_class->m_event_name_index == 154, "index 154");
  socket_class= find_socket_class(dummy_socket_key_2);
  ok(socket_class != NULL, "socket class 2");
  ok(socket_class->m_event_name_index == 155, "index 155");

  ok(global_table_io_class.m_event_name_index == 0, "index 0");
  ok(global_table_lock_class.m_event_name_index == 1, "index 1");
  ok(wait_class_max= 314, "314 event names"); // 4 global classes

  shutdown_performance_schema();
}

void test_memory_instruments()
{
  PSI *psi;
  PSI_thread *owner;

  diag("test_memory_instruments");

  psi= load_perfschema();

  PSI_memory_key memory_key_A;
  PSI_memory_info all_memory[]=
  {
    { & memory_key_A, "M-A", 0}
  };

  PSI_thread_key thread_key_1;
  PSI_thread_info all_thread[]=
  {
    { & thread_key_1, "T-1", 0}
  };

  psi->register_memory("test", all_memory, 1);
  psi->register_thread("test", all_thread, 1);

  PFS_memory_class *memory_class_A;
  PSI_thread *thread_1;
  PSI_memory_key key;

  /* Preparation */

  thread_1= psi->new_thread(thread_key_1, NULL, 0);
  ok(thread_1 != NULL, "T-1");
  psi->set_thread_id(thread_1, 1);

  memory_class_A= find_memory_class(memory_key_A);
  ok(memory_class_A != NULL, "memory info A");

  /* Pretend thread T-1 is running, and enabled */
  /* ------------------------------------------ */

  psi->set_thread(thread_1);
  setup_thread(thread_1, true);

  /* Enable all instruments */

  memory_class_A->m_enabled= true;

  /* for coverage, need to print stats collected. */

  key= psi->memory_alloc(memory_key_A, 100, & owner);
  ok(key == memory_key_A, "alloc memory info A");
  key= psi->memory_realloc(memory_key_A, 100, 200, & owner);
  ok(key == memory_key_A, "realloc memory info A");
  key= psi->memory_realloc(memory_key_A, 200, 300, & owner);
  ok(key == memory_key_A, "realloc up memory info A");
  key= psi->memory_realloc(memory_key_A, 300, 50, & owner);
  ok(key == memory_key_A, "realloc down memory info A");
  psi->memory_free(memory_key_A, 50, owner);

  /* Use global instrumentation only */
  /* ------------------------------- */

  flag_thread_instrumentation= false;

  key= psi->memory_alloc(memory_key_A, 100, & owner);
  ok(key == memory_key_A, "alloc memory info A");
  key= psi->memory_realloc(memory_key_A, 100, 200, & owner);
  ok(key == memory_key_A, "realloc memory info A");
  key= psi->memory_realloc(memory_key_A, 200, 300, & owner);
  ok(key == memory_key_A, "realloc up memory info A");
  key= psi->memory_realloc(memory_key_A, 300, 50, & owner);
  ok(key == memory_key_A, "realloc down memory info A");
  psi->memory_free(memory_key_A, 50, owner);

  /* Garbage, for robustness */
  /* ----------------------- */

  key= psi->memory_alloc(9999, 100, & owner);
  ok(key == PSI_NOT_INSTRUMENTED, "alloc with unknown key");
  key= psi->memory_realloc(PSI_NOT_INSTRUMENTED, 100, 200, & owner);
  ok(key == PSI_NOT_INSTRUMENTED, "realloc with unknown key");
  psi->memory_free(PSI_NOT_INSTRUMENTED, 200, owner);

  shutdown_performance_schema();
}

void test_leaks()
{
  PSI_bootstrap *boot;
  PFS_global_param param;

  /* Allocate everything, to make sure cleanup does not forget anything. */

  memset(& param, 0xFF, sizeof(param));
  param.m_enabled= true;
  param.m_mutex_class_sizing= 10;
  param.m_rwlock_class_sizing= 10;
  param.m_cond_class_sizing= 10;
  param.m_thread_class_sizing= 10;
  param.m_table_share_sizing= 10;
  param.m_file_class_sizing= 10;
  param.m_socket_class_sizing= 10;
  param.m_mutex_sizing= 1000;
  param.m_rwlock_sizing= 1000;
  param.m_cond_sizing= 1000;
  param.m_thread_sizing= 1000;
  param.m_table_sizing= 1000;
  param.m_file_sizing= 1000;
  param.m_file_handle_sizing= 1000;
  param.m_socket_sizing= 1000;
  param.m_events_waits_history_sizing= 10;
  param.m_events_waits_history_long_sizing= 1000;
  param.m_setup_actor_sizing= 1000;
  param.m_setup_object_sizing= 1000;
  param.m_host_sizing= 1000;
  param.m_user_sizing= 1000;
  param.m_account_sizing= 1000;
  param.m_stage_class_sizing= 10;
  param.m_events_stages_history_sizing= 10;
  param.m_events_stages_history_long_sizing= 1000;
  param.m_statement_class_sizing= 10;
  param.m_events_statements_history_sizing= 10;
  param.m_events_statements_history_long_sizing= 1000;
  param.m_session_connect_attrs_sizing= 1000;
  param.m_memory_class_sizing= 10;
  param.m_metadata_lock_sizing= 1000;
  param.m_digest_sizing= 1000;
  param.m_program_sizing= 1000;
  param.m_statement_stack_sizing= 10;
  param.m_max_digest_length= 1000;
  param.m_max_sql_text_length= 1000;

  param.m_hints.m_table_definition_cache = 100;
  param.m_hints.m_table_open_cache       = 100;
  param.m_hints.m_max_connections        = 100;
  param.m_hints.m_open_files_limit       = 100;
  param.m_hints.m_max_prepared_stmt_count= 100;

  pre_initialize_performance_schema();
  boot= initialize_performance_schema(& param);
  ok(boot != NULL, "bootstrap");
  shutdown_performance_schema();

  /* Leaks will be reported with valgrind */
}

void do_all_tests()
{
  /* Using initialize_performance_schema(), no partial init needed. */

  test_bootstrap();
  test_bad_registration();
  test_init_disabled();
  test_locker_disabled();
  test_file_instrumentation_leak();
  test_event_name_index();
  test_memory_instruments();
  test_leaks();
}

int main(int argc, char **argv)
{
  plan(232);
  MY_INIT(argv[0]);
  do_all_tests();
  my_end(0);
  return (exit_status());
}
