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 
get_likely_key(LIKELY_ENTRY * part,size_t * length,my_bool not_used)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 
init_my_likely()48 void init_my_likely()
49 {
50   /* Allocate big enough to avoid malloc calls */
51   my_hash_init2(PSI_NOT_INSTRUMENTED, &likely_hash, 10000, &my_charset_bin,
52                 1024, 0, 0, (my_hash_get_key) get_likely_key, 0, free,
53                 HASH_UNIQUE);
54   likely_inited= 1;
55   pthread_mutex_init(&likely_mutex, MY_MUTEX_INIT_FAST);
56 }
57 
likely_cmp(LIKELY_ENTRY ** a,LIKELY_ENTRY ** b)58 static int likely_cmp(LIKELY_ENTRY **a, LIKELY_ENTRY **b)
59 {
60   int cmp;
61   if ((cmp= strcmp((*a)->key, (*b)->key)))
62     return cmp;
63   return (int) ((*a)->line - (*b)->line);
64 }
65 
66 
end_my_likely(FILE * out)67 void end_my_likely(FILE *out)
68 {
69   uint i;
70   FILE *likely_file;
71   my_bool do_close= 0;
72   LIKELY_ENTRY **sort_ptr= 0;
73 
74   likely_inited= 0;
75 
76   if (!(likely_file= out))
77   {
78     char name[80];
79     sprintf(name, "/tmp/unlikely-%lu.out", (ulong) getpid());
80     if ((likely_file= my_fopen(name, O_TRUNC | O_WRONLY, MYF(MY_WME))))
81       do_close= 1;
82     else
83       likely_file= stderr;
84   }
85   fflush(likely_file);
86   fputs("Wrong likely/unlikely usage:\n", likely_file);
87   if (!(sort_ptr= (LIKELY_ENTRY**)
88         malloc(sizeof(LIKELY_ENTRY*) *likely_hash.records)))
89   {
90     fprintf(stderr, "ERROR: Out of memory in end_my_likely\n");
91     goto err;
92   }
93 
94   for (i=0 ; i < likely_hash.records ; i++)
95     sort_ptr[i]= (LIKELY_ENTRY *) my_hash_element(&likely_hash, i);
96 
97   my_qsort(sort_ptr, likely_hash.records, sizeof(LIKELY_ENTRY*),
98            (qsort_cmp) likely_cmp);
99 
100   for (i=0 ; i < likely_hash.records ; i++)
101   {
102     LIKELY_ENTRY *entry= sort_ptr[i];
103     if (entry->fail > entry->ok)
104       fprintf(likely_file,
105               "%50s  line: %6u  ok: %8lld  fail: %8lld\n",
106               entry->key, entry->line, entry->ok, entry->fail);
107   }
108   fputs("\n", likely_file);
109   fflush(likely_file);
110 err:
111   free((void*) sort_ptr);
112   if (do_close)
113     my_fclose(likely_file, MYF(MY_WME));
114   pthread_mutex_destroy(&likely_mutex);
115   my_hash_free(&likely_hash);
116 }
117 
118 
my_likely_find(const char * file_name,uint line)119 static LIKELY_ENTRY *my_likely_find(const char *file_name, uint line)
120 {
121   char key[80], *pos;
122   LIKELY_ENTRY *entry;
123   size_t length;
124 
125   if (!likely_inited)
126     return 0;
127 
128   pos= strnmov(key, file_name, sizeof(key)-4);
129   int3store(pos+1, line);
130   length= (size_t) (pos-key)+4;
131 
132   pthread_mutex_lock(&likely_mutex);
133   if (!(entry= (LIKELY_ENTRY*) my_hash_search(&likely_hash, (uchar*) key,
134                                               length)))
135   {
136     if (!(entry= (LIKELY_ENTRY *) malloc(sizeof(*entry) + length)))
137       return 0;
138     entry->key= (char*) (entry+1);
139     memcpy((void*) entry->key, key, length);
140     entry->key_length= length;
141     entry->line= line;
142     entry->ok= entry->fail= 0;
143 
144     if (my_hash_insert(&likely_hash, (void*) entry))
145     {
146       pthread_mutex_unlock(&likely_mutex);
147       free(entry);
148       return 0;
149     }
150   }
151   pthread_mutex_unlock(&likely_mutex);
152   return entry;
153 }
154 
155 
my_likely_ok(const char * file_name,uint line)156 int my_likely_ok(const char *file_name, uint line)
157 {
158   LIKELY_ENTRY *entry= my_likely_find(file_name, line);
159   if (entry)
160     entry->ok++;
161   return 0;
162 }
163 
164 
my_likely_fail(const char * file_name,uint line)165 int my_likely_fail(const char *file_name, uint line)
166 {
167   LIKELY_ENTRY *entry= my_likely_find(file_name, line);
168   if (entry)
169     entry->fail++;
170   return 0;
171 }
172 #endif /* CHECK_UNLIKEY */
173