Skip to content

File fault_injection.c

File List > base > fault_injection.c

Go to the documentation of this file

/*
 * Copyright 2008 Search Solution Corporation
 * Copyright 2016 CUBRID Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

/*
 * fault_injection.c :
 *
 */

#ident "$Id$"

#include "fault_injection.h"

#if defined (SERVER_MODE) || defined (SA_MODE)
#include "log_impl.h"
#endif /* defined (SERVER_MODE) || defined (SA_MODE) */
#include "porting.h"
#include "system_parameter.h"
#if defined (SERVER_MODE) || defined (SA_MODE)
#include "thread_manager.hpp"   // for thread_get_thread_entry_info
#endif /* defined (SERVER_MODE) || defined (SA_MODE) */

#include <assert.h>
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"

#if !defined(NDEBUG)

static int fi_handler_exit (THREAD_ENTRY * thread_p, void *arg, const char *caller_file, const int caller_line);
static int fi_handler_random_exit (THREAD_ENTRY * thread_p, void *arg, const char *caller_file, const int caller_line);
static int fi_handler_random_fail (THREAD_ENTRY * thread_p, void *arg, const char *caller_file, const int caller_line);
static int fi_handler_hang (THREAD_ENTRY * thread_p, void *arg, const char *caller_file, const int caller_line);

static FI_TEST_ITEM *fi_code_item (THREAD_ENTRY * thread_p, FI_TEST_CODE code);

/******************************************************************************
 *
 * FI test scenario array
 *
 * Register new scenario in here with new FI_TEST_CODE & handler function
 *
 *******************************************************************************/
FI_TEST_ITEM fi_Test_array[] = {
  {FI_TEST_HANG, fi_handler_hang, FI_INIT_STATE},
  {FI_TEST_FILE_IO_FORMAT, fi_handler_random_exit, FI_INIT_STATE},
  {FI_TEST_DISK_MANAGER_VOLUME_ADD, fi_handler_random_exit, FI_INIT_STATE},
  {FI_TEST_DISK_MANAGER_VOLUME_EXPAND, fi_handler_random_exit, FI_INIT_STATE},
  {FI_TEST_FILE_IO_WRITE_PARTS1, fi_handler_random_exit, FI_INIT_STATE},
  {FI_TEST_FILE_IO_WRITE_PARTS2, fi_handler_random_exit, FI_INIT_STATE},
  {FI_TEST_FILE_MANAGER_UNDO_TRACKER_REGISTER, fi_handler_exit, FI_INIT_STATE},
  {FI_TEST_BTREE_MANAGER_RANDOM_EXIT, fi_handler_random_exit, FI_INIT_STATE},
  {FI_TEST_LOG_MANAGER_RANDOM_EXIT_AT_RUN_POSTPONE, fi_handler_random_exit, FI_INIT_STATE},
  {FI_TEST_LOG_MANAGER_RANDOM_EXIT_AT_END_SYSTEMOP, fi_handler_random_exit, FI_INIT_STATE},
  {FI_TEST_BTREE_MANAGER_PAGE_DEALLOC_FAIL, fi_handler_random_fail, FI_INIT_STATE}
};

const FI_TEST_CODE fi_Group_none[] = {
  FI_TEST_NONE
};

const FI_TEST_CODE fi_Group_recovery[] = {
  FI_TEST_LOG_MANAGER_RANDOM_EXIT_AT_RUN_POSTPONE,
  FI_TEST_LOG_MANAGER_RANDOM_EXIT_AT_END_SYSTEMOP,
  FI_TEST_BTREE_MANAGER_RANDOM_EXIT,
  FI_TEST_BTREE_MANAGER_PAGE_DEALLOC_FAIL,
  FI_TEST_NONE
};

const FI_TEST_CODE *const fi_Groups[FI_GROUP_MAX + 1] = {
  fi_Group_none,
  fi_Group_recovery
};

/*
 * fi_thread_init -
 *
 * return: NO_ERROR or ER_FAILED
 *
 *   thread_p(in):
 */
int
fi_thread_init (THREAD_ENTRY * thread_p)
{
  FI_TEST_ITEM *fi_test_array = NULL;
  unsigned int i;


#if defined (SERVER_MODE)
  if (thread_p == NULL)
    {
      thread_p = thread_get_thread_entry_info ();
    }
  if (thread_p == NULL)
    {
      assert (thread_p != NULL);

      return ER_FAILED;
    }

  if (thread_p->fi_test_array == NULL)
    {
      thread_p->fi_test_array = (FI_TEST_ITEM *) malloc (sizeof (fi_Test_array));
      if (thread_p->fi_test_array == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (fi_Test_array));
      return ER_OUT_OF_VIRTUAL_MEMORY;
    }
    }

  fi_test_array = thread_p->fi_test_array;

  memcpy (fi_test_array, fi_Test_array, sizeof (fi_Test_array));

#else
  fi_test_array = fi_Test_array;
#endif

  for (i = 0; i < DIM (fi_Test_array); i++)
    {
      fi_test_array[i].state = FI_INIT_STATE;
    }

  return NO_ERROR;
}

/*
 * fi_thread_final -
 *
 * return: NO_ERROR or ER_FAILED
 *
 *   thread_p(in):
 */
int
fi_thread_final (THREAD_ENTRY * thread_p)
{
#if defined (SERVER_MODE)
  if (thread_p == NULL)
    {
      thread_p = thread_get_thread_entry_info ();
    }
  if (thread_p == NULL)
    {
      assert (thread_p != NULL);

      return ER_FAILED;
    }

  if (thread_p->fi_test_array != NULL)
    {
      free_and_init (thread_p->fi_test_array);
    }
#endif

  return NO_ERROR;
}

/*
 * fi_code_item -
 *
 * return: NO_ERROR or ER_FAILED
 *
 *   code(in):
 *   state(in):
 */
static FI_TEST_ITEM *
fi_code_item (THREAD_ENTRY * thread_p, FI_TEST_CODE code)
{
  FI_TEST_ITEM *fi_test_array;
  FI_TEST_ITEM *item;
  unsigned int i;

#if defined(SERVER_MODE)
  if (thread_p == NULL)
    {
      thread_p = thread_get_thread_entry_info ();
    }
  if (thread_p == NULL)
    {
      assert (thread_p != NULL);

      return NULL;
    }

  fi_test_array = thread_p->fi_test_array;
#else
  fi_test_array = fi_Test_array;
#endif

  item = NULL;
  for (i = 0; i < DIM (fi_Test_array); i++)
    {
      if (fi_test_array[i].code == code)
    {
      item = &fi_test_array[i];
      break;
    }
    }

  assert (item != NULL);
  return item;
}

/*
 * fi_set -
 *
 * return: NO_ERROR or ER_FAILED
 *
 *   code(in):
 *   state(in):
 */
int
fi_set (THREAD_ENTRY * thread_p, FI_TEST_CODE code, int state)
{
  FI_TEST_ITEM *item = NULL;


  if (sysprm_find_fi_code_in_integer_list (PRM_ID_FAULT_INJECTION_IDS, (int) code) == false)
    {
      return NO_ERROR;
    }

  item = fi_code_item (thread_p, code);
  if (item == NULL)
    {
      assert (item != NULL);
      return ER_FAILED;
    }

  if (item->state == state - 1)
    {
      item->state = state;
    }

  return NO_ERROR;
}

/*
 * fi_set_force -
 *
 * return: NO_ERROR or error code
 *
 *   code(in):
 *   state(in):
 */
int
fi_set_force (THREAD_ENTRY * thread_p, FI_TEST_CODE code, int state)
{
  FI_TEST_ITEM *item = NULL;

  if (sysprm_find_fi_code_in_integer_list (PRM_ID_FAULT_INJECTION_IDS, (int) code) == false)
    {
      return NO_ERROR;
    }

  item = fi_code_item (thread_p, code);
  if (item == NULL)
    {
      assert (item != NULL);
      return ER_FAILED;
    }

  item->state = state;

  return NO_ERROR;
}

/*
 * fi_reset -
 *
 * return:
 *
 *   code(in):
 */
void
fi_reset (THREAD_ENTRY * thread_p, FI_TEST_CODE code)
{
  FI_TEST_ITEM *item = NULL;

  item = fi_code_item (thread_p, code);
  item->state = FI_INIT_STATE;
}

/*
 * fi_test -
 *
 * return: NO_ERROR or error code
 *
 *   code(in):
 *   arg(in):
 *   state(in):
 */
int
fi_test (THREAD_ENTRY * thread_p, FI_TEST_CODE code, void *arg, int state, const char *caller_file,
     const int caller_line)
{
  FI_TEST_ITEM *item = NULL;

  if (sysprm_find_fi_code_in_integer_list (PRM_ID_FAULT_INJECTION_IDS, (int) code) == false)
    {
      return NO_ERROR;
    }

  item = fi_code_item (thread_p, code);
  if (item == NULL)
    {
      assert (item != NULL);
      return ER_FAILED;
    }

  if (item->state == state)
    {
      return (*item->func) (thread_p, arg, caller_file, caller_line);
    }

  return NO_ERROR;
}

/*
 * fi_state -
 *
 * return:
 *
 *   code(in):
 */
int
fi_state (THREAD_ENTRY * thread_p, FI_TEST_CODE code)
{
  FI_TEST_ITEM *item = NULL;

  if (sysprm_find_fi_code_in_integer_list (PRM_ID_FAULT_INJECTION_IDS, (int) code) == false)
    {
      return FI_INIT_STATE;
    }

  item = fi_code_item (thread_p, code);
  assert (item != NULL);

  return item->state;
}

/*
 * fi_test_on -
 *
 * return: true or false
 *
 *   code(in):
 */
bool
fi_test_on (FI_TEST_CODE code)
{
  return sysprm_find_fi_code_in_integer_list (PRM_ID_FAULT_INJECTION_IDS, (int) code);
}


/*
 * fi_handler_exit -
 *
 * return: NO_ERROR
 *
 *   arg(in):
 */
static int
fi_handler_exit (THREAD_ENTRY * thread_p, void *arg, const char *caller_file, const int caller_line)
{
  exit (0);

  return NO_ERROR;
}

/*
 * fi_handler_hang -
 *
 * return: NO_ERROR
 *
 *   arg(in):
 */
static int
fi_handler_hang (THREAD_ENTRY * thread_p, void *arg, const char *caller_file, const int caller_line)
{
  while (true)
    {
      sleep (1);
    }

  return NO_ERROR;
}

static int
fi_handler_random_exit (THREAD_ENTRY * thread_p, void *arg, const char *caller_file, const int caller_line)
{
  static bool init = false;
  int r;
  int mod_factor;

  if (arg == NULL)
    {
      mod_factor = 20000;
    }
  else
    {
      mod_factor = *((int *) arg);
    }

  if (init == false)
    {
      srand ((unsigned int) time (NULL));
      init = true;
    }
  r = rand ();

#if 0
  if ((r % 10) == 0)
    {
      /* todo: what is the purpose of this? */
      LOG_CS_ENTER (thread_p);
      logpb_flush_pages_direct (thread_p);
      LOG_CS_EXIT (thread_p);
    }
#endif
  if ((r % mod_factor) == 0)
    {
      er_print_callstack (ARG_FILE_LINE, "FAULT INJECTION: RANDOM EXIT\n");
      er_set (ER_NOTIFICATION_SEVERITY, caller_file, caller_line, ER_FAILED_ASSERTION, 1,
          "fault injection: random exit");

      if (prm_get_bool_value (PRM_ID_FAULT_INJECTION_ACTION_PREFER_ABORT_TO_EXIT))
    {
      abort ();
    }
      else
    {
      _exit (0);
    }
    }

  return NO_ERROR;
}

static int
fi_handler_random_fail (THREAD_ENTRY * thread_p, void *arg, const char *caller_file, const int caller_line)
{
  static bool init = false;
  int r;
  int mod_factor;

  if (arg == NULL)
    {
      mod_factor = 20000;
    }
  else
    {
      mod_factor = *((int *) arg);
    }

  if (init == false)
    {
      srand ((unsigned int) time (NULL));
      init = true;
    }
  r = rand ();

  if ((r % mod_factor) == 0)
    {
      er_set (ER_NOTIFICATION_SEVERITY, caller_file, caller_line, ER_FAILED_ASSERTION, 1,
          "fault injection: random fail");

      return ER_FAILED;
    }

  return NO_ERROR;
}
#endif