summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Widenius <monty@mariadb.org>2018-04-11 02:08:02 +0300
committerMonty <monty@mariadb.org>2018-05-07 00:07:32 +0300
commit9d6dc39ad9a6c1b0a9dd373607fa1a2f9a24c335 (patch)
tree122e05d20ff402a142c7e779cc8581d26d94eab0
parent30ebc3ee9efcab635b1f3e14b9198a58ae93c233 (diff)
downloadmariadb-git-9d6dc39ad9a6c1b0a9dd373607fa1a2f9a24c335.tar.gz
Add checking of correct likely/unlikely
To use: - Compile with -DUSE_MY_LIKELY - Change (with replace) all likely/unlikely to my_likely/my_/unlikely replace likely my_likely unlikely my_unlikely -- *c *h - Start mysqld with -T - run some test - When mysqld has shut down cleanely, report will be on stderr
-rw-r--r--include/my_global.h40
-rw-r--r--mysys/CMakeLists.txt2
-rw-r--r--mysys/my_init.c6
-rw-r--r--mysys/my_likely.c173
-rw-r--r--sql/gen_lex_hash.cc1
-rw-r--r--sql/table_cache.cc2
6 files changed, 212 insertions, 12 deletions
diff --git a/include/my_global.h b/include/my_global.h
index 93ab4ef45b2..9982f6339e4 100644
--- a/include/my_global.h
+++ b/include/my_global.h
@@ -189,15 +189,6 @@
#define __builtin_expect(x, expected_value) (x)
#endif
-/**
- The semantics of builtin_expect() are that
- 1) its two arguments are long
- 2) it's likely that they are ==
- Those of our likely(x) are that x can be bool/int/longlong/pointer.
-*/
-#define likely(x) __builtin_expect(((x) != 0),1)
-#define unlikely(x) __builtin_expect(((x) != 0),0)
-
/* Fix problem with S_ISLNK() on Linux */
#if defined(TARGET_OS_LINUX) || defined(__GLIBC__)
#undef _GNU_SOURCE
@@ -384,6 +375,36 @@ C_MODE_END
#include <crypt.h>
#endif
+/* Add checking if we are using likely/unlikely wrong */
+#ifdef CHECK_UNLIKELY
+C_MODE_START
+extern void init_my_likely(), end_my_likely(FILE *);
+extern int my_likely_ok(const char *file_name, uint line);
+extern int my_likely_fail(const char *file_name, uint line);
+C_MODE_END
+
+#define likely(A) ((A) ? (my_likely_ok(__FILE__, __LINE__),1) : (my_likely_fail(__FILE__, __LINE__), 0))
+#define unlikely(A) ((A) ? (my_likely_fail(__FILE__, __LINE__),1) : (my_likely_ok(__FILE__, __LINE__), 0))
+/*
+ These macros should be used when the check fails often when running benchmarks but
+ we know for sure that the check is correct in a production environment
+*/
+#define checked_likely(A) (A)
+#define checked_unlikely(A) (A)
+#else
+/**
+ The semantics of builtin_expect() are that
+ 1) its two arguments are long
+ 2) it's likely that they are ==
+ Those of our likely(x) are that x can be bool/int/longlong/pointer.
+*/
+
+#define likely(x) __builtin_expect(((x) != 0),1)
+#define unlikely(x) __builtin_expect(((x) != 0),0)
+#define checked_likely(x) likely(x)
+#define checked_unlikely(x) unlikely(x)
+#endif /* CHECK_UNLIKELY */
+
/*
A lot of our programs uses asserts, so better to always include it
This also fixes a problem when people uses DBUG_ASSERT without including
@@ -1279,5 +1300,4 @@ static inline double rint(double x)
#else
#define NOT_FIXED_DEC FLOATING_POINT_DECIMALS
#endif
-
#endif /* my_global_h */
diff --git a/mysys/CMakeLists.txt b/mysys/CMakeLists.txt
index 6988d66376d..d4689a6d759 100644
--- a/mysys/CMakeLists.txt
+++ b/mysys/CMakeLists.txt
@@ -43,7 +43,7 @@ SET(MYSYS_SOURCES array.c charset-def.c charset.c checksum.c my_default.c
my_getncpus.c my_safehash.c my_chmod.c my_rnd.c
my_uuid.c wqueue.c waiting_threads.c ma_dyncol.c ../sql-common/my_time.c
my_rdtsc.c my_context.c psi_noop.c
- my_atomic_writes.c
+ my_atomic_writes.c my_likely.c
file_logger.c my_dlerror.c)
IF (WIN32)
diff --git a/mysys/my_init.c b/mysys/my_init.c
index 9bd1185a3bf..aebcb3c1b8a 100644
--- a/mysys/my_init.c
+++ b/mysys/my_init.c
@@ -122,6 +122,9 @@ my_bool my_init(void)
#ifdef __WIN__
win32_init_tcp_ip();
#endif
+#ifdef CHECK_UNLIKELY
+ init_my_likely();
+#endif
DBUG_RETURN(0);
}
} /* my_init */
@@ -166,6 +169,9 @@ void my_end(int infoflag)
DBUG_PRINT("error", ("%s", ebuff));
my_print_open_files();
}
+#ifdef CHECK_UNLIKELY
+ end_my_likely(info_file);
+#endif
}
free_charsets();
my_error_unregister_all();
diff --git a/mysys/my_likely.c b/mysys/my_likely.c
new file mode 100644
index 00000000000..9b32c65d6a0
--- /dev/null
+++ b/mysys/my_likely.c
@@ -0,0 +1,173 @@
+/* Copyright (c) 2018, MariaDB Corporation Ab.
+
+ 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 Foundation; version 2 of the License.
+
+ 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 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/*
+ Checks that my_likely/my_unlikely is correctly used
+
+ Note that we can't use mysql_mutex or my_malloc here as these
+ uses likely() macros and the likely_mutex would be used twice
+*/
+
+#include "mysys_priv.h"
+#include <hash.h>
+#include <m_ctype.h>
+
+#ifndef CHECK_UNLIKEY
+my_bool likely_inited= 0;
+
+typedef struct st_likely_entry
+{
+ const char *key;
+ size_t key_length;
+ uint line;
+ ulonglong ok,fail;
+} LIKELY_ENTRY;
+
+static uchar *get_likely_key(LIKELY_ENTRY *part, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length= part->key_length;
+ return (uchar*) part->key;
+}
+
+pthread_mutex_t likely_mutex;
+HASH likely_hash;
+
+void init_my_likely()
+{
+ /* Allocate big enough to avoid malloc calls */
+ my_hash_init2(&likely_hash, 10000, &my_charset_bin,
+ 1024, 0, 0,
+ (my_hash_get_key) get_likely_key, 0,
+ free, HASH_UNIQUE);
+ likely_inited= 1;
+ pthread_mutex_init(&likely_mutex, MY_MUTEX_INIT_FAST);
+}
+
+static int likely_cmp(LIKELY_ENTRY **a, LIKELY_ENTRY **b)
+{
+ int cmp;
+ if ((cmp= strcmp((*a)->key, (*b)->key)))
+ return cmp;
+ return (int) ((*a)->line - (*b)->line);
+}
+
+
+void end_my_likely(FILE *out)
+{
+ uint i;
+ FILE *likely_file;
+ my_bool do_close= 0;
+ LIKELY_ENTRY **sort_ptr= 0;
+
+ likely_inited= 0;
+
+ if (!(likely_file= out))
+ {
+ char name[80];
+ sprintf(name, "/tmp/unlikely-%lu.out", (ulong) getpid());
+ if ((likely_file= my_fopen(name, O_TRUNC | O_WRONLY, MYF(MY_WME))))
+ do_close= 1;
+ else
+ likely_file= stderr;
+ }
+ fflush(likely_file);
+ fputs("Wrong likely/unlikely usage:\n", likely_file);
+ if (!(sort_ptr= (LIKELY_ENTRY**)
+ malloc(sizeof(LIKELY_ENTRY*) *likely_hash.records)))
+ {
+ fprintf(stderr, "ERROR: Out of memory in end_my_likely\n");
+ goto err;
+ }
+
+ for (i=0 ; i < likely_hash.records ; i++)
+ sort_ptr[i]= (LIKELY_ENTRY *) my_hash_element(&likely_hash, i);
+
+ my_qsort(sort_ptr, likely_hash.records, sizeof(LIKELY_ENTRY*),
+ (qsort_cmp) likely_cmp);
+
+ for (i=0 ; i < likely_hash.records ; i++)
+ {
+ LIKELY_ENTRY *entry= sort_ptr[i];
+ if (entry->fail > entry->ok)
+ fprintf(likely_file,
+ "%50s line: %6u ok: %8lld fail: %8lld\n",
+ entry->key, entry->line, entry->ok, entry->fail);
+ }
+ fputs("\n", likely_file);
+ fflush(likely_file);
+err:
+ free((void*) sort_ptr);
+ if (do_close)
+ my_fclose(likely_file, MYF(MY_WME));
+ pthread_mutex_destroy(&likely_mutex);
+ my_hash_free(&likely_hash);
+}
+
+
+static LIKELY_ENTRY *my_likely_find(const char *file_name, uint line)
+{
+ char key[80], *pos;
+ LIKELY_ENTRY *entry;
+ uint length;
+
+ if (!likely_inited)
+ return 0;
+
+ pos= strnmov(key, file_name, sizeof(key)-4);
+ int3store(pos+1, line);
+ length= (pos-key)+4;
+
+ pthread_mutex_lock(&likely_mutex);
+ if (!(entry= (LIKELY_ENTRY*) my_hash_search(&likely_hash, (uchar*) key,
+ length)))
+ {
+ if (!(entry= (LIKELY_ENTRY *) malloc(sizeof(*entry) + length)))
+ return 0;
+ entry->key= (char*) (entry+1);
+ memcpy((void*) entry->key, key, length);
+ entry->key_length= length;
+ entry->line= line;
+ entry->ok= entry->fail= 0;
+
+ if (my_hash_insert(&likely_hash, (void*) entry))
+ {
+ pthread_mutex_unlock(&likely_mutex);
+ free(entry);
+ return 0;
+ }
+ }
+ pthread_mutex_unlock(&likely_mutex);
+ return entry;
+}
+
+
+int my_likely_ok(const char *file_name, uint line)
+{
+ LIKELY_ENTRY *entry= my_likely_find(file_name, line);
+ if (entry)
+ entry->ok++;
+ return 0;
+}
+
+
+int my_likely_fail(const char *file_name, uint line)
+{
+ LIKELY_ENTRY *entry= my_likely_find(file_name, line);
+ if (entry)
+ entry->fail++;
+ return 0;
+}
+#endif /* CHECK_UNLIKEY */
diff --git a/sql/gen_lex_hash.cc b/sql/gen_lex_hash.cc
index c81f3ebb1a1..9241dd79113 100644
--- a/sql/gen_lex_hash.cc
+++ b/sql/gen_lex_hash.cc
@@ -78,6 +78,7 @@ So, we can read full search-structure as 32-bit word
*/
#define NO_YACC_SYMBOLS
+#undef CHECK_UNLIKELY
#include "mariadb.h"
#include "mysql_version.h"
#include "lex.h"
diff --git a/sql/table_cache.cc b/sql/table_cache.cc
index 3ec528186eb..2c5d25c0308 100644
--- a/sql/table_cache.cc
+++ b/sql/table_cache.cc
@@ -838,7 +838,7 @@ retry:
/* note that tdc_acquire_share() *always* uses discovery */
open_table_def(thd, share, flags | GTS_USE_DISCOVERY);
- if (unlikely(share->error))
+ if (checked_unlikely(share->error))
{
free_table_share(share);
lf_hash_delete(&tdc_hash, thd->tdc_hash_pins, key, key_length);