Skip to content

Commit

Permalink
Add checking of correct likely/unlikely
Browse files Browse the repository at this point in the history
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
  • Loading branch information
montywi committed May 6, 2018
1 parent 30ebc3e commit 9d6dc39
Show file tree
Hide file tree
Showing 6 changed files with 212 additions and 12 deletions.
40 changes: 30 additions & 10 deletions include/my_global.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1279,5 +1300,4 @@ static inline double rint(double x)
#else
#define NOT_FIXED_DEC FLOATING_POINT_DECIMALS
#endif

#endif /* my_global_h */
2 changes: 1 addition & 1 deletion mysys/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions mysys/my_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ my_bool my_init(void)
DBUG_PRINT("exit", ("home: '%s'", home_dir));
#ifdef __WIN__
win32_init_tcp_ip();
#endif
#ifdef CHECK_UNLIKELY
init_my_likely();
#endif
DBUG_RETURN(0);
}
Expand Down Expand Up @@ -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();
Expand Down
173 changes: 173 additions & 0 deletions mysys/my_likely.c
Original file line number Diff line number Diff line change
@@ -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 */
1 change: 1 addition & 0 deletions sql/gen_lex_hash.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion sql/table_cache.cc
Original file line number Diff line number Diff line change
Expand Up @@ -838,7 +838,7 @@ TABLE_SHARE *tdc_acquire_share(THD *thd, TABLE_LIST *tl, uint flags,
/* 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);
Expand Down

0 comments on commit 9d6dc39

Please sign in to comment.