| 1 | /* Copyright (c) 2018, MariaDB Corporation Ab. |
| 2 | |
| 3 | This program is free software; you can redistribute it and/or modify |
| 4 | it under the terms of the GNU General Public License as published by |
| 5 | the Free Software Foundation; version 2 of the License. |
| 6 | |
| 7 | This program is distributed in the hope that it will be useful, |
| 8 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 10 | GNU General Public License for more details. |
| 11 | |
| 12 | You should have received a copy of the GNU General Public License |
| 13 | along with this program; if not, write to the Free Software |
| 14 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ |
| 15 | |
| 16 | /* |
| 17 | Checks that my_likely/my_unlikely is correctly used |
| 18 | |
| 19 | Note that we can't use mysql_mutex or my_malloc here as these |
| 20 | uses likely() macros and the likely_mutex would be used twice |
| 21 | */ |
| 22 | |
| 23 | #include "mysys_priv.h" |
| 24 | #include <hash.h> |
| 25 | #include <m_ctype.h> |
| 26 | |
| 27 | #ifndef CHECK_UNLIKEY |
| 28 | my_bool likely_inited= 0; |
| 29 | |
| 30 | typedef struct st_likely_entry |
| 31 | { |
| 32 | const char *key; |
| 33 | size_t key_length; |
| 34 | uint line; |
| 35 | ulonglong ok,fail; |
| 36 | } LIKELY_ENTRY; |
| 37 | |
| 38 | static uchar *get_likely_key(LIKELY_ENTRY *part, size_t *length, |
| 39 | my_bool not_used __attribute__((unused))) |
| 40 | { |
| 41 | *length= part->key_length; |
| 42 | return (uchar*) part->key; |
| 43 | } |
| 44 | |
| 45 | pthread_mutex_t likely_mutex; |
| 46 | HASH likely_hash; |
| 47 | |
| 48 | void init_my_likely() |
| 49 | { |
| 50 | /* Allocate big enough to avoid malloc calls */ |
| 51 | my_hash_init2(&likely_hash, 10000, &my_charset_bin, |
| 52 | 1024, 0, 0, |
| 53 | (my_hash_get_key) get_likely_key, 0, |
| 54 | free, HASH_UNIQUE); |
| 55 | likely_inited= 1; |
| 56 | pthread_mutex_init(&likely_mutex, MY_MUTEX_INIT_FAST); |
| 57 | } |
| 58 | |
| 59 | static int likely_cmp(LIKELY_ENTRY **a, LIKELY_ENTRY **b) |
| 60 | { |
| 61 | int cmp; |
| 62 | if ((cmp= strcmp((*a)->key, (*b)->key))) |
| 63 | return cmp; |
| 64 | return (int) ((*a)->line - (*b)->line); |
| 65 | } |
| 66 | |
| 67 | |
| 68 | void end_my_likely(FILE *out) |
| 69 | { |
| 70 | uint i; |
| 71 | FILE *likely_file; |
| 72 | my_bool do_close= 0; |
| 73 | LIKELY_ENTRY **sort_ptr= 0; |
| 74 | |
| 75 | likely_inited= 0; |
| 76 | |
| 77 | if (!(likely_file= out)) |
| 78 | { |
| 79 | char name[80]; |
| 80 | sprintf(name, "/tmp/unlikely-%lu.out" , (ulong) getpid()); |
| 81 | if ((likely_file= my_fopen(name, O_TRUNC | O_WRONLY, MYF(MY_WME)))) |
| 82 | do_close= 1; |
| 83 | else |
| 84 | likely_file= stderr; |
| 85 | } |
| 86 | fflush(likely_file); |
| 87 | fputs("Wrong likely/unlikely usage:\n" , likely_file); |
| 88 | if (!(sort_ptr= (LIKELY_ENTRY**) |
| 89 | malloc(sizeof(LIKELY_ENTRY*) *likely_hash.records))) |
| 90 | { |
| 91 | fprintf(stderr, "ERROR: Out of memory in end_my_likely\n" ); |
| 92 | goto err; |
| 93 | } |
| 94 | |
| 95 | for (i=0 ; i < likely_hash.records ; i++) |
| 96 | sort_ptr[i]= (LIKELY_ENTRY *) my_hash_element(&likely_hash, i); |
| 97 | |
| 98 | my_qsort(sort_ptr, likely_hash.records, sizeof(LIKELY_ENTRY*), |
| 99 | (qsort_cmp) likely_cmp); |
| 100 | |
| 101 | for (i=0 ; i < likely_hash.records ; i++) |
| 102 | { |
| 103 | LIKELY_ENTRY *entry= sort_ptr[i]; |
| 104 | if (entry->fail > entry->ok) |
| 105 | fprintf(likely_file, |
| 106 | "%50s line: %6u ok: %8lld fail: %8lld\n" , |
| 107 | entry->key, entry->line, entry->ok, entry->fail); |
| 108 | } |
| 109 | fputs("\n" , likely_file); |
| 110 | fflush(likely_file); |
| 111 | err: |
| 112 | free((void*) sort_ptr); |
| 113 | if (do_close) |
| 114 | my_fclose(likely_file, MYF(MY_WME)); |
| 115 | pthread_mutex_destroy(&likely_mutex); |
| 116 | my_hash_free(&likely_hash); |
| 117 | } |
| 118 | |
| 119 | |
| 120 | static LIKELY_ENTRY *my_likely_find(const char *file_name, uint line) |
| 121 | { |
| 122 | char key[80], *pos; |
| 123 | LIKELY_ENTRY *entry; |
| 124 | size_t length; |
| 125 | |
| 126 | if (!likely_inited) |
| 127 | return 0; |
| 128 | |
| 129 | pos= strnmov(key, file_name, sizeof(key)-4); |
| 130 | int3store(pos+1, line); |
| 131 | length= (size_t) (pos-key)+4; |
| 132 | |
| 133 | pthread_mutex_lock(&likely_mutex); |
| 134 | if (!(entry= (LIKELY_ENTRY*) my_hash_search(&likely_hash, (uchar*) key, |
| 135 | length))) |
| 136 | { |
| 137 | if (!(entry= (LIKELY_ENTRY *) malloc(sizeof(*entry) + length))) |
| 138 | return 0; |
| 139 | entry->key= (char*) (entry+1); |
| 140 | memcpy((void*) entry->key, key, length); |
| 141 | entry->key_length= length; |
| 142 | entry->line= line; |
| 143 | entry->ok= entry->fail= 0; |
| 144 | |
| 145 | if (my_hash_insert(&likely_hash, (void*) entry)) |
| 146 | { |
| 147 | pthread_mutex_unlock(&likely_mutex); |
| 148 | free(entry); |
| 149 | return 0; |
| 150 | } |
| 151 | } |
| 152 | pthread_mutex_unlock(&likely_mutex); |
| 153 | return entry; |
| 154 | } |
| 155 | |
| 156 | |
| 157 | int my_likely_ok(const char *file_name, uint line) |
| 158 | { |
| 159 | LIKELY_ENTRY *entry= my_likely_find(file_name, line); |
| 160 | if (entry) |
| 161 | entry->ok++; |
| 162 | return 0; |
| 163 | } |
| 164 | |
| 165 | |
| 166 | int my_likely_fail(const char *file_name, uint line) |
| 167 | { |
| 168 | LIKELY_ENTRY *entry= my_likely_find(file_name, line); |
| 169 | if (entry) |
| 170 | entry->fail++; |
| 171 | return 0; |
| 172 | } |
| 173 | #endif /* CHECK_UNLIKEY */ |
| 174 | |