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 | |