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(&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 
likely_cmp(LIKELY_ENTRY ** a,LIKELY_ENTRY ** b)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 
end_my_likely(FILE * out)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 
my_likely_find(const char * file_name,uint line)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 
my_likely_ok(const char * file_name,uint line)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 
my_likely_fail(const char * file_name,uint line)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