diff options
author | Michael Widenius <monty@mariadb.org> | 2018-04-11 02:08:02 +0300 |
---|---|---|
committer | Monty <monty@mariadb.org> | 2018-05-07 00:07:32 +0300 |
commit | 9d6dc39ad9a6c1b0a9dd373607fa1a2f9a24c335 (patch) | |
tree | 122e05d20ff402a142c7e779cc8581d26d94eab0 | |
parent | 30ebc3ee9efcab635b1f3e14b9198a58ae93c233 (diff) | |
download | mariadb-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.h | 40 | ||||
-rw-r--r-- | mysys/CMakeLists.txt | 2 | ||||
-rw-r--r-- | mysys/my_init.c | 6 | ||||
-rw-r--r-- | mysys/my_likely.c | 173 | ||||
-rw-r--r-- | sql/gen_lex_hash.cc | 1 | ||||
-rw-r--r-- | sql/table_cache.cc | 2 |
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); |