From 648fb98eb6834781e5ff7bd13f26e7b3755fdf81 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= <jplindst@mariadb.org>
Date: Tue, 8 Jul 2014 18:51:34 +0300
Subject: [PATCH] MDEV-6348: mariadb crash signal 11

Analysis: sync array output function, should make sure that all
used pointers are valid before using them.

Merge revision 4225 from lp:maria/5.5.
---
 .../include/innodb_simulate_comp_failures.inc | 147 ++++++++++++++++++
 .../r/innodb_simulate_comp_failures.result    |   8 +
 ...innodb_simulate_comp_failures_small.result |   8 +
 .../innodb_simulate_comp_failures-master.opt  |   2 +
 .../t/innodb_simulate_comp_failures.test      |   8 +
 ...db_simulate_comp_failures_small-master.opt |   2 +
 .../innodb_simulate_comp_failures_small.test  |   5 +
 .../innodb_simulate_comp_failures_basic.test  |  64 ++++++++
 storage/innobase/handler/ha_innodb.cc         |   6 +
 storage/innobase/include/srv0srv.h            |   4 +
 storage/innobase/page/page0zip.cc             |  22 +++
 storage/innobase/srv/srv0srv.cc               |   4 +
 storage/innobase/sync/sync0arr.cc             |  72 +++++----
 storage/xtradb/handler/ha_innodb.cc           |   6 +
 storage/xtradb/include/srv0srv.h              |   3 +
 storage/xtradb/page/page0zip.cc               |  23 +++
 storage/xtradb/srv/srv0srv.cc                 |   4 +
 storage/xtradb/sync/sync0arr.cc               | 102 ++++++------
 18 files changed, 410 insertions(+), 80 deletions(-)
 create mode 100644 mysql-test/suite/innodb/include/innodb_simulate_comp_failures.inc
 create mode 100644 mysql-test/suite/innodb/r/innodb_simulate_comp_failures.result
 create mode 100644 mysql-test/suite/innodb/r/innodb_simulate_comp_failures_small.result
 create mode 100644 mysql-test/suite/innodb/t/innodb_simulate_comp_failures-master.opt
 create mode 100644 mysql-test/suite/innodb/t/innodb_simulate_comp_failures.test
 create mode 100644 mysql-test/suite/innodb/t/innodb_simulate_comp_failures_small-master.opt
 create mode 100644 mysql-test/suite/innodb/t/innodb_simulate_comp_failures_small.test
 create mode 100644 mysql-test/suite/sys_vars/t/innodb_simulate_comp_failures_basic.test

diff --git a/mysql-test/suite/innodb/include/innodb_simulate_comp_failures.inc b/mysql-test/suite/innodb/include/innodb_simulate_comp_failures.inc
new file mode 100644
index 0000000000..47494d6037
--- /dev/null
+++ b/mysql-test/suite/innodb/include/innodb_simulate_comp_failures.inc
@@ -0,0 +1,147 @@
+--echo #
+--echo # Testing robustness against random compression failures
+--echo #
+
+--source include/not_embedded.inc
+--source include/have_innodb.inc
+
+--disable_query_log
+# record the file format in order to restore in the end.
+--let $file_format_save = `SELECT @@innodb_file_format`
+--let $file_format_max_save = `SELECT @@innodb_file_format_max`
+--let $simulate_comp_failures_save = `SELECT @@innodb_simulate_comp_failures`
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+SET GLOBAL INNODB_FILE_FORMAT='Barracuda';
+--enable_warnings
+
+# since this test generates lot of errors in log, suppress checking errors
+call mtr.add_suppression(".*");
+--enable_query_log
+
+# create the table with compressed pages of size 8K.
+CREATE TABLE t1(id INT AUTO_INCREMENT PRIMARY KEY, msg VARCHAR(255), KEY msg_i(msg)) ENGINE=INNODB ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8;
+
+# percentage of compressions that will be forced to fail
+SET GLOBAL innodb_simulate_comp_failures = 25;
+
+--disable_query_log
+--disable_result_log
+
+let $num_inserts_ind = $num_inserts;
+while ($num_inserts_ind)
+{
+  let $repeat = `select floor(rand() * 10)`;
+  eval
+INSERT INTO t1(id, msg)
+VALUES ($num_inserts_ind, REPEAT('abcdefghijklmnopqrstuvwxyz', $repeat));
+  dec $num_inserts_ind;
+}
+
+--enable_query_log
+--enable_result_log
+
+SELECT COUNT(*) FROM t1;
+
+--disable_query_log
+--disable_result_log
+
+# do random ops, making sure that some pages will get fragmented and reorganized.
+let $num_ops_ind = $num_ops;
+
+while($num_ops_ind)
+{
+  let $idx = `select floor(rand()*$num_inserts)`;
+	let $insert_or_update = `select floor(rand()*3)`;
+
+  let $repeat = `select floor(rand() * 9) + 1`;
+
+  let $msg = query_get_value(`select repeat('abcdefghijklmnopqrstuvwxyz', $repeat) as x`, x, 1);
+
+  let $single_or_multi = `select floor(rand()*10)`;
+
+  if ($insert_or_update)
+  {
+    let $cnt = query_get_value(SELECT COUNT(*) cnt FROM t1 WHERE id=$idx, cnt, 1);
+
+    if ($cnt)
+    {
+      let $update = `select floor(rand()*2)`;
+
+      if ($update)
+      {
+        if ($single_or_multi)
+        {
+          eval UPDATE t1 SET msg=\"$msg\" WHERE id=$idx;
+        }
+
+        if (!$single_or_multi)
+        {
+          eval UPDATE t1 SET msg=\"$msg\" WHERE id >= $idx - 100 AND id <= $idx + 100;
+        }
+
+      }
+
+      if (!$update)
+      {
+        if ($single_or_multi)
+        {
+          eval INSERT INTO t1(msg, id) VALUES (\"$msg\", $idx) ON DUPLICATE KEY UPDATE msg=VALUES(msg), id = VALUES(id);
+        }
+
+        if (!$single_or_multi)
+        {
+          let $diff = 200;
+
+          while ($diff)
+          {
+            eval INSERT INTO t1(msg, id) VALUES (\"$msg\", $idx + 100 - $diff) ON DUPLICATE KEY UPDATE msg=VALUES(msg), id=VALUES(id);
+
+            dec $diff;
+          }
+        }
+      }
+    }
+
+    if (!$cnt)
+    {
+      let $null_msg = `select floor(rand()*2)`;
+
+      if ($null_msg)
+      {
+        eval INSERT INTO t1(id,msg) VALUES ($idx, NULL);
+      }
+
+      if (!$null_msg)
+      {
+        eval INSERT INTO t1(id, msg) VALUES ($idx, \"$msg\");
+      }
+    }
+  }
+
+  if (!$insert_or_update)
+  {
+    if ($single_or_multi)
+    {
+      eval DELETE from t1 WHERE id=$idx;
+    }
+
+    if (!$single_or_multi)
+    {
+      eval DELETE from t1 WHERE id >= $idx - 100 AND id <= $idx + 100;
+    }
+  }
+
+  dec $num_ops_ind;
+}
+
+# final cleanup
+DROP TABLE t1;
+
+# restore innodb_file_format and innodb_file_format_max
+eval SET GLOBAL innodb_file_format = \"$file_format_save\";
+eval SET GLOBAL innodb_file_format_max = \"$file_format_max_save\";
+eval SET GLOBAL innodb_simulate_comp_failures = $simulate_comp_failures_save;
+
+--enable_query_log
diff --git a/mysql-test/suite/innodb/r/innodb_simulate_comp_failures.result b/mysql-test/suite/innodb/r/innodb_simulate_comp_failures.result
new file mode 100644
index 0000000000..cb7a3b9c28
--- /dev/null
+++ b/mysql-test/suite/innodb/r/innodb_simulate_comp_failures.result
@@ -0,0 +1,8 @@
+#
+# Testing robustness against random compression failures
+#
+CREATE TABLE t1(id INT AUTO_INCREMENT PRIMARY KEY, msg VARCHAR(255), KEY msg_i(msg)) ENGINE=INNODB ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8;
+SET GLOBAL innodb_simulate_comp_failures = 25;
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+100000
diff --git a/mysql-test/suite/innodb/r/innodb_simulate_comp_failures_small.result b/mysql-test/suite/innodb/r/innodb_simulate_comp_failures_small.result
new file mode 100644
index 0000000000..1cb2cf7705
--- /dev/null
+++ b/mysql-test/suite/innodb/r/innodb_simulate_comp_failures_small.result
@@ -0,0 +1,8 @@
+#
+# Testing robustness against random compression failures
+#
+CREATE TABLE t1(id INT AUTO_INCREMENT PRIMARY KEY, msg VARCHAR(255), KEY msg_i(msg)) ENGINE=INNODB ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8;
+SET GLOBAL innodb_simulate_comp_failures = 25;
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+10000
diff --git a/mysql-test/suite/innodb/t/innodb_simulate_comp_failures-master.opt b/mysql-test/suite/innodb/t/innodb_simulate_comp_failures-master.opt
new file mode 100644
index 0000000000..fae3205924
--- /dev/null
+++ b/mysql-test/suite/innodb/t/innodb_simulate_comp_failures-master.opt
@@ -0,0 +1,2 @@
+--innodb-file-per-table
+
diff --git a/mysql-test/suite/innodb/t/innodb_simulate_comp_failures.test b/mysql-test/suite/innodb/t/innodb_simulate_comp_failures.test
new file mode 100644
index 0000000000..a940a926f8
--- /dev/null
+++ b/mysql-test/suite/innodb/t/innodb_simulate_comp_failures.test
@@ -0,0 +1,8 @@
+--source include/big_test.inc
+# test takes too long with valgrind
+--source include/not_valgrind.inc
+--let $num_inserts = 100000
+--let $num_ops = 30000
+--source suite/innodb/include/innodb_simulate_comp_failures.inc
+# clean exit
+--exit
diff --git a/mysql-test/suite/innodb/t/innodb_simulate_comp_failures_small-master.opt b/mysql-test/suite/innodb/t/innodb_simulate_comp_failures_small-master.opt
new file mode 100644
index 0000000000..fae3205924
--- /dev/null
+++ b/mysql-test/suite/innodb/t/innodb_simulate_comp_failures_small-master.opt
@@ -0,0 +1,2 @@
+--innodb-file-per-table
+
diff --git a/mysql-test/suite/innodb/t/innodb_simulate_comp_failures_small.test b/mysql-test/suite/innodb/t/innodb_simulate_comp_failures_small.test
new file mode 100644
index 0000000000..7332a8c6ba
--- /dev/null
+++ b/mysql-test/suite/innodb/t/innodb_simulate_comp_failures_small.test
@@ -0,0 +1,5 @@
+--let $num_inserts = 10000
+--let $num_ops = 3000
+--source suite/innodb/include/innodb_simulate_comp_failures.inc
+# clean exit
+--exit
diff --git a/mysql-test/suite/sys_vars/t/innodb_simulate_comp_failures_basic.test b/mysql-test/suite/sys_vars/t/innodb_simulate_comp_failures_basic.test
new file mode 100644
index 0000000000..97e69e3f32
--- /dev/null
+++ b/mysql-test/suite/sys_vars/t/innodb_simulate_comp_failures_basic.test
@@ -0,0 +1,64 @@
+--source include/have_innodb.inc
+
+SET @start_global_value = @@global.innodb_simulate_comp_failures;
+SELECT @start_global_value;
+
+#
+# exists as global only
+#
+
+--echo Valid values are between 0 and 99
+select @@global.innodb_simulate_comp_failures between 0 and 99;
+select @@global.innodb_simulate_comp_failures;
+
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+select @@session.innodb_simulate_comp_failures;
+
+show global variables like 'innodb_simulate_comp_failures';
+show session variables like 'innodb_simulate_comp_failures';
+select * from information_schema.global_variables where variable_name='innodb_simulate_comp_failures';
+select * from information_schema.session_variables where variable_name='innodb_simulate_comp_failures';
+
+#
+# show that it's writable
+#
+
+set global innodb_simulate_comp_failures=10;
+select @@global.innodb_simulate_comp_failures;
+select * from information_schema.global_variables where variable_name='innodb_simulate_comp_failures';
+select * from information_schema.session_variables where variable_name='innodb_simulate_comp_failures';
+
+--error ER_GLOBAL_VARIABLE
+set session innodb_simulate_comp_failures=1;
+
+#
+# incorrect types
+#
+
+--error ER_WRONG_TYPE_FOR_VAR
+set global innodb_simulate_comp_failures=1.1;
+--error ER_WRONG_TYPE_FOR_VAR
+set global innodb_simulate_comp_failures=1e1;
+--error ER_WRONG_TYPE_FOR_VAR
+set global innodb_simulate_comp_failures="foo";
+
+set global innodb_simulate_comp_failures=-7;
+select @@global.innodb_simulate_comp_failures;
+select * from information_schema.global_variables where variable_name='innodb_simulate_comp_failures';
+set global innodb_simulate_comp_failures=106;
+select @@global.innodb_simulate_comp_failures;
+select * from information_schema.global_variables where variable_name='innodb_simulate_comp_failures';
+
+#
+# min/max/DEFAULT values
+#
+
+set global innodb_simulate_comp_failures=0;
+select @@global.innodb_simulate_comp_failures;
+set global innodb_simulate_comp_failures=99;
+select @@global.innodb_simulate_comp_failures;
+set global innodb_simulate_comp_failures=DEFAULT;
+select @@global.innodb_simulate_comp_failures;
+
+SET @@global.innodb_simulate_comp_failures = @start_global_value;
+SELECT @@global.innodb_simulate_comp_failures;
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index 3673dd2906..009ba9bd70 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -16793,6 +16793,11 @@ static MYSQL_SYSVAR_ULONG(saved_page_number_debug,
   NULL, innodb_save_page_no, 0, 0, UINT_MAX32, 0);
 #endif /* UNIV_DEBUG */
 
+static MYSQL_SYSVAR_UINT(simulate_comp_failures, srv_simulate_comp_failures,
+  PLUGIN_VAR_NOCMDARG,
+  "Simulate compression failures.",
+  NULL, NULL, 0, 0, 99, 0);
+
 static struct st_mysql_sys_var* innobase_system_variables[]= {
   MYSQL_SYSVAR(additional_mem_pool_size),
   MYSQL_SYSVAR(api_trx_level),
@@ -16950,6 +16955,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= {
   MYSQL_SYSVAR(fil_make_page_dirty_debug),
   MYSQL_SYSVAR(saved_page_number_debug),
 #endif /* UNIV_DEBUG */
+  MYSQL_SYSVAR(simulate_comp_failures),
   NULL
 };
 
diff --git a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h
index 79eaee7375..7bf698f0a6 100644
--- a/storage/innobase/include/srv0srv.h
+++ b/storage/innobase/include/srv0srv.h
@@ -3,6 +3,7 @@
 Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
 Copyright (c) 2008, 2009, Google Inc.
 Copyright (c) 2009, Percona Inc.
+Copyright (c) 2013, 2014, SkySQL Ab. All Rights Reserved.
 
 Portions of this file contain modifications contributed and copyrighted by
 Google, Inc. Those modifications are gratefully acknowledged and are described
@@ -449,6 +450,9 @@ extern struct export_var_t export_vars;
 /** Global counters */
 extern srv_stats_t	srv_stats;
 
+/** Simulate compression failures. */
+extern uint srv_simulate_comp_failures;
+
 # ifdef UNIV_PFS_THREAD
 /* Keys to register InnoDB threads with performance schema */
 extern mysql_pfs_key_t	buf_page_cleaner_thread_key;
diff --git a/storage/innobase/page/page0zip.cc b/storage/innobase/page/page0zip.cc
index ab7a19795a..3b80236eb4 100644
--- a/storage/innobase/page/page0zip.cc
+++ b/storage/innobase/page/page0zip.cc
@@ -1309,6 +1309,28 @@ page_zip_compress(
 
 	MONITOR_INC(MONITOR_PAGE_COMPRESS);
 
+	/* Simulate a compression failure with a probability determined by
+	innodb_simulate_comp_failures, only if the page has 2 or more
+	records. */
+
+	if (srv_simulate_comp_failures
+	    && page_get_n_recs(page) >= 2
+	    && ((ulint)(rand() % 100) < srv_simulate_comp_failures)) {
+
+#ifdef UNIV_DEBUG
+		fprintf(stderr,
+			"InnoDB: Simulating a compression failure"
+			" for table %s, index %s, page %lu (%s)\n",
+			index->table_name,
+			index->name,
+			page_get_page_no(page),
+			page_is_leaf(page) ? "leaf" : "non-leaf");
+
+#endif
+
+		goto err_exit;
+	}
+
 	heap = mem_heap_create(page_zip_get_size(page_zip)
 			       + n_fields * (2 + sizeof(ulint))
 			       + REC_OFFS_HEADER_SIZE
diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc
index 9b52ba4215..d9ace82834 100644
--- a/storage/innobase/srv/srv0srv.cc
+++ b/storage/innobase/srv/srv0srv.cc
@@ -3,6 +3,7 @@
 Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved.
 Copyright (c) 2008, 2009 Google Inc.
 Copyright (c) 2009, Percona Inc.
+Copyright (c) 2013, 2014, SkySQL Ab. All Rights Reserved.
 
 Portions of this file contain modifications contributed and copyrighted by
 Google, Inc. Those modifications are gratefully acknowledged and are described
@@ -465,6 +466,9 @@ current_time % 5 != 0. */
 #endif /* MEM_PERIODIC_CHECK */
 # define	SRV_MASTER_DICT_LRU_INTERVAL		(47)
 
+/** Simulate compression failures. */
+UNIV_INTERN uint srv_simulate_comp_failures = 0;
+
 /** Acquire the system_mutex. */
 #define srv_sys_mutex_enter() do {			\
 	mutex_enter(&srv_sys->mutex);			\
diff --git a/storage/innobase/sync/sync0arr.cc b/storage/innobase/sync/sync0arr.cc
index 2cfb693f8b..8e1b8180eb 100644
--- a/storage/innobase/sync/sync0arr.cc
+++ b/storage/innobase/sync/sync0arr.cc
@@ -454,19 +454,21 @@ sync_array_cell_print(
 		been freed meanwhile */
 		mutex = cell->old_wait_mutex;
 
-		fprintf(file,
-			"Mutex at %p created file %s line %lu, lock var %lu\n"
+		if (mutex) {
+			fprintf(file,
+				"Mutex at %p created file %s line %lu, lock var %lu\n"
 #ifdef UNIV_SYNC_DEBUG
-			"Last time reserved in file %s line %lu, "
+				"Last time reserved in file %s line %lu, "
 #endif /* UNIV_SYNC_DEBUG */
-			"waiters flag %lu\n",
-			(void*) mutex, innobase_basename(mutex->cfile_name),
-			(ulong) mutex->cline,
-			(ulong) mutex->lock_word,
+				"waiters flag %lu\n",
+				(void*) mutex, innobase_basename(mutex->cfile_name),
+				(ulong) mutex->cline,
+				(ulong) mutex->lock_word,
 #ifdef UNIV_SYNC_DEBUG
-			mutex->file_name, (ulong) mutex->line,
+				mutex->file_name, (ulong) mutex->line,
 #endif /* UNIV_SYNC_DEBUG */
-			(ulong) mutex->waiters);
+				(ulong) mutex->waiters);
+		}
 
 	} else if (type == RW_LOCK_EX
 		   || type == RW_LOCK_WAIT_EX
@@ -478,33 +480,35 @@ sync_array_cell_print(
 
 		rwlock = cell->old_wait_rw_lock;
 
-		fprintf(file,
-			" RW-latch at %p created in file %s line %lu\n",
-			(void*) rwlock, innobase_basename(rwlock->cfile_name),
-			(ulong) rwlock->cline);
-		writer = rw_lock_get_writer(rwlock);
-		if (writer != RW_LOCK_NOT_LOCKED) {
+		if (rwlock) {
 			fprintf(file,
-				"a writer (thread id %lu) has"
-				" reserved it in mode %s",
-				(ulong) os_thread_pf(rwlock->writer_thread),
-				writer == RW_LOCK_EX
-				? " exclusive\n"
-				: " wait exclusive\n");
-		}
+				" RW-latch at %p created in file %s line %lu\n",
+				(void*) rwlock, innobase_basename(rwlock->cfile_name),
+				(ulong) rwlock->cline);
+			writer = rw_lock_get_writer(rwlock);
+			if (writer != RW_LOCK_NOT_LOCKED) {
+				fprintf(file,
+					"a writer (thread id %lu) has"
+					" reserved it in mode %s",
+					(ulong) os_thread_pf(rwlock->writer_thread),
+					writer == RW_LOCK_EX
+					? " exclusive\n"
+					: " wait exclusive\n");
+			}
 
-		fprintf(file,
-			"number of readers %lu, waiters flag %lu, "
-                        "lock_word: %lx\n"
-			"Last time read locked in file %s line %lu\n"
-			"Last time write locked in file %s line %lu\n",
-			(ulong) rw_lock_get_reader_count(rwlock),
-			(ulong) rwlock->waiters,
-			rwlock->lock_word,
-			innobase_basename(rwlock->last_s_file_name),
-			(ulong) rwlock->last_s_line,
-			rwlock->last_x_file_name,
-			(ulong) rwlock->last_x_line);
+			fprintf(file,
+				"number of readers %lu, waiters flag %lu, "
+				"lock_word: %lx\n"
+				"Last time read locked in file %s line %lu\n"
+				"Last time write locked in file %s line %lu\n",
+				(ulong) rw_lock_get_reader_count(rwlock),
+				(ulong) rwlock->waiters,
+				rwlock->lock_word,
+				innobase_basename(rwlock->last_s_file_name),
+				(ulong) rwlock->last_s_line,
+				rwlock->last_x_file_name,
+				(ulong) rwlock->last_x_line);
+		}
 	} else {
 		ut_error;
 	}
diff --git a/storage/xtradb/handler/ha_innodb.cc b/storage/xtradb/handler/ha_innodb.cc
index d5f0966cda..9159def162 100644
--- a/storage/xtradb/handler/ha_innodb.cc
+++ b/storage/xtradb/handler/ha_innodb.cc
@@ -17938,6 +17938,11 @@ static MYSQL_SYSVAR_ULONG(saved_page_number_debug,
   NULL, innodb_save_page_no, 0, 0, UINT_MAX32, 0);
 #endif /* UNIV_DEBUG */
 
+static MYSQL_SYSVAR_UINT(simulate_comp_failures, srv_simulate_comp_failures,
+  PLUGIN_VAR_NOCMDARG,
+  "Simulate compression failures.",
+  NULL, NULL, 0, 0, 99, 0);
+
 const char *corrupt_table_action_names[]=
 {
   "assert", /* 0 */
@@ -18165,6 +18170,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= {
   MYSQL_SYSVAR(fake_changes),
   MYSQL_SYSVAR(locking_fake_changes),
   MYSQL_SYSVAR(use_stacktrace),
+  MYSQL_SYSVAR(simulate_comp_failures),
   NULL
 };
 
diff --git a/storage/xtradb/include/srv0srv.h b/storage/xtradb/include/srv0srv.h
index 4bbace02eb..d7e77842b7 100644
--- a/storage/xtradb/include/srv0srv.h
+++ b/storage/xtradb/include/srv0srv.h
@@ -3,6 +3,7 @@
 Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
 Copyright (c) 2008, 2009, Google Inc.
 Copyright (c) 2009, Percona Inc.
+Copyright (c) 2013, 2014, SkySQL Ab. All Rights Reserved.
 
 Portions of this file contain modifications contributed and copyrighted by
 Google, Inc. Those modifications are gratefully acknowledged and are described
@@ -587,6 +588,8 @@ extern srv_stats_t	srv_stats;
 When FALSE, row locks are not taken at all. */
 extern my_bool srv_fake_changes_locks;
 
+/** Simulate compression failures. */
+extern uint srv_simulate_comp_failures;
 
 # ifdef UNIV_PFS_THREAD
 /* Keys to register InnoDB threads with performance schema */
diff --git a/storage/xtradb/page/page0zip.cc b/storage/xtradb/page/page0zip.cc
index 1e533a4bbb..923684964a 100644
--- a/storage/xtradb/page/page0zip.cc
+++ b/storage/xtradb/page/page0zip.cc
@@ -2,6 +2,7 @@
 
 Copyright (c) 2005, 2014, Oracle and/or its affiliates. All Rights Reserved.
 Copyright (c) 2012, Facebook Inc.
+Copyright (c) 2014, SkySQL Ab. 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 as published by the Free Software
@@ -1313,6 +1314,28 @@ page_zip_compress(
 
 	MONITOR_INC(MONITOR_PAGE_COMPRESS);
 
+	/* Simulate a compression failure with a probability determined by
+	innodb_simulate_comp_failures, only if the page has 2 or more
+	records. */
+
+	if (srv_simulate_comp_failures
+	    && page_get_n_recs(page) >= 2
+	    && ((ulint)(rand() % 100) < srv_simulate_comp_failures)) {
+
+#ifdef UNIV_DEBUG
+		fprintf(stderr,
+			"InnoDB: Simulating a compression failure"
+			" for table %s, index %s, page %lu (%s)\n",
+			index->table_name,
+			index->name,
+			page_get_page_no(page),
+			page_is_leaf(page) ? "leaf" : "non-leaf");
+
+#endif
+
+		goto err_exit;
+	}
+
 	heap = mem_heap_create(page_zip_get_size(page_zip)
 			       + n_fields * (2 + sizeof(ulint))
 			       + REC_OFFS_HEADER_SIZE
diff --git a/storage/xtradb/srv/srv0srv.cc b/storage/xtradb/srv/srv0srv.cc
index b9ed26b9c9..001a9257c9 100644
--- a/storage/xtradb/srv/srv0srv.cc
+++ b/storage/xtradb/srv/srv0srv.cc
@@ -3,6 +3,7 @@
 Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved.
 Copyright (c) 2008, 2009 Google Inc.
 Copyright (c) 2009, Percona Inc.
+Copyright (c) 2013, 2014, SkySQL Ab. All Rights Reserved.
 
 Portions of this file contain modifications contributed and copyrighted by
 Google, Inc. Those modifications are gratefully acknowledged and are described
@@ -619,6 +620,9 @@ current_time % 5 != 0. */
 	 ? thd_lock_wait_timeout((trx)->mysql_thd)	\
 	 : 0)
 
+/** Simulate compression failures. */
+UNIV_INTERN uint srv_simulate_comp_failures = 0;
+
 /*
 	IMPLEMENTATION OF THE SERVER MAIN PROGRAM
 	=========================================
diff --git a/storage/xtradb/sync/sync0arr.cc b/storage/xtradb/sync/sync0arr.cc
index 126cf8de0d..880f47237d 100644
--- a/storage/xtradb/sync/sync0arr.cc
+++ b/storage/xtradb/sync/sync0arr.cc
@@ -2,6 +2,7 @@
 
 Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved.
 Copyright (c) 2008, Google Inc.
+Copyright (c) 2013, 2014, SkySQL Ab. All Rights Reserved.
 
 Portions of this file contain modifications contributed and copyrighted by
 Google, Inc. Those modifications are gratefully acknowledged and are described
@@ -460,16 +461,9 @@ sync_array_cell_print(
 		innobase_basename(cell->file), (ulong) cell->line,
 		difftime(time(NULL), cell->reservation_time));
 
-	/* If stacktrace feature is enabled we will send a SIGUSR2
-	signal to thread waiting for the semaphore. Signal handler
-	will then dump the current stack to error log. */
-	if (srv_use_stacktrace) {
-#ifdef __linux__
-		pthread_kill(cell->thread, SIGUSR2);
-#endif
-	}
 
 	if (type == SYNC_MUTEX || type == SYNC_PRIO_MUTEX) {
+
 		/* We use old_wait_mutex in case the cell has already
 		been freed meanwhile */
 		if (type == SYNC_MUTEX) {
@@ -483,18 +477,29 @@ sync_array_cell_print(
 		}
 
 
-		fprintf(file,
-			"Mutex at %p '%s', lock var %lu\n"
+		if (mutex) {
+			fprintf(file,
+				"Mutex at %p '%s', lock var %lu\n"
 #ifdef UNIV_SYNC_DEBUG
-			"Last time reserved in file %s line %lu, "
+				"Last time reserved in file %s line %lu, "
 #endif /* UNIV_SYNC_DEBUG */
-			"waiters flag %lu\n",
-			(void*) mutex, mutex->cmutex_name,
-			(ulong) mutex->lock_word,
+				"waiters flag %lu\n",
+				(void*) mutex, mutex->cmutex_name,
+				(ulong) mutex->lock_word,
 #ifdef UNIV_SYNC_DEBUG
-			mutex->file_name, (ulong) mutex->line,
+				mutex->file_name, (ulong) mutex->line,
 #endif /* UNIV_SYNC_DEBUG */
-			(ulong) mutex->waiters);
+				(ulong) mutex->waiters);
+		}
+
+		/* If stacktrace feature is enabled we will send a SIGUSR2
+		signal to thread waiting for the semaphore. Signal handler
+		will then dump the current stack to error log. */
+		if (srv_use_stacktrace && cell && cell->thread) {
+#ifdef __linux__
+			pthread_kill(cell->thread, SIGUSR2);
+#endif
+		}
 
 		if (type == SYNC_PRIO_MUTEX) {
 
@@ -529,40 +534,45 @@ sync_array_cell_print(
 			rwlock = &prio_rwlock->base_lock;
 		}
 
-		fprintf(file,
-			" RW-latch at %p '%s'\n",
-			(void*) rwlock, rwlock->lock_name);
-		writer = rw_lock_get_writer(rwlock);
-		if (writer != RW_LOCK_NOT_LOCKED) {
+		if (rwlock) {
 			fprintf(file,
-				"a writer (thread id %lu) has"
-				" reserved it in mode %s",
-				(ulong) os_thread_pf(rwlock->writer_thread),
-				writer == RW_LOCK_EX
-				? " exclusive\n"
-				: " wait exclusive\n");
-		}
-
-		fprintf(file,
-			"number of readers %lu, waiters flag %lu, "
-                        "lock_word: %lx\n"
-			"Last time read locked in file %s line %lu\n"
-			"Last time write locked in file %s line %lu\n",
-			(ulong) rw_lock_get_reader_count(rwlock),
-			(ulong) rwlock->waiters,
-			rwlock->lock_word,
-			innobase_basename(rwlock->last_s_file_name),
-			(ulong) rwlock->last_s_line,
-			rwlock->last_x_file_name,
-			(ulong) rwlock->last_x_line);
+				" RW-latch at %p '%s'\n",
+				(void*) rwlock, rwlock->lock_name);
+
+			writer = rw_lock_get_writer(rwlock);
+
+			if (writer && writer != RW_LOCK_NOT_LOCKED) {
+				fprintf(file,
+					"a writer (thread id %lu) has"
+					" reserved it in mode %s",
+					(ulong) os_thread_pf(rwlock->writer_thread),
+					writer == RW_LOCK_EX
+					? " exclusive\n"
+					: " wait exclusive\n");
+			}
 
-		/* If stacktrace feature is enabled we will send a SIGUSR2
-		signal to thread that has locked RW-latch with write mode.
-		Signal handler will then dump the current stack to error log. */
-		if (writer != RW_LOCK_NOT_LOCKED && srv_use_stacktrace) {
+			fprintf(file,
+				"number of readers %lu, waiters flag %lu, "
+				"lock_word: %lx\n"
+				"Last time read locked in file %s line %lu\n"
+				"Last time write locked in file %s line %lu\n",
+				(ulong) rw_lock_get_reader_count(rwlock),
+				(ulong) rwlock->waiters,
+				rwlock->lock_word,
+				innobase_basename(rwlock->last_s_file_name),
+				(ulong) rwlock->last_s_line,
+				rwlock->last_x_file_name,
+				(ulong) rwlock->last_x_line);
+
+			/* If stacktrace feature is enabled we will send a SIGUSR2
+			signal to thread that has locked RW-latch with write mode.
+			Signal handler will then dump the current stack to error log. */
+			if (writer != RW_LOCK_NOT_LOCKED && srv_use_stacktrace &&
+				rwlock && rwlock->writer_thread) {
 #ifdef __linux__
-			pthread_kill(rwlock->writer_thread, SIGUSR2);
+				pthread_kill(rwlock->writer_thread, SIGUSR2);
 #endif
+			}
 		}
 
 		if (prio_rwlock) {
-- 
2.30.9