1 /*------------------------------------------------------------------------------
2 *
3 * Copyright (c) 2011-2021, EURid vzw. All rights reserved.
4 * The YADIFA TM software product is provided under the BSD 3-clause license:
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of EURid nor the names of its contributors may be
16 * used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 *------------------------------------------------------------------------------
32 *
33 */
34
35 /** @defgroup threading Threading, pools, queues, ...
36 * @ingroup dnscore
37 * @brief
38 *
39 *
40 *
41 * @{
42 *
43 *----------------------------------------------------------------------------*/
44
45 #include "dnscore/dnscore-config.h"
46
47 #if HAS_PTHREAD_SETNAME_NP
48 #if DEBUG
49 #define _GNU_SOURCE 1
50 #endif
51 #endif
52
53 #include <sys/types.h>
54 #include <unistd.h>
55
56 #include <dnscore/thread.h>
57 #include <sys/wait.h>
58
59 #include "dnscore/threaded_queue.h"
60 #include "dnscore/thread_pool.h"
61 #include "dnscore/logger.h"
62 #include "dnscore/format.h"
63 #include "dnscore/u32_set.h"
64 #include "dnscore/zalloc.h"
65 #include "dnscore/process.h"
66 #include "dnscore/mutex.h"
67 #include "dnscore/thread-tag.h"
68
69 /* 0 = nothing, 1 = warns and worse, 2 = info and worse, 3 = debug and worse */
70 #define VERBOSE_THREAD_LOG 0
71
72 #define DEBUG_THREAD_TAG_COLLISIONS 0
73
74 /* Disable when in release mode */
75
76 #if !DEBUG
77 #undef VERBOSE_THREAD_LOG
78 #define VERBOSE_THREAD_LOG 0
79 #endif
80
81 #define MODULE_MSG_HANDLE g_system_logger
82
83 #if DNSCORE_HAS_LOG_THREAD_TAG
84
85 #define THREAD_TAG_HASH_PRIME 8191
86 #define THREAD_TAG_HASH_SIZE (THREAD_TAG_HASH_PRIME + 1)
87
88 #if __SIZEOF_POINTER__ == 8
89 struct thread_tag_entry_s
90 {
91 thread_t id;
92 char tag[8];
93 thread_t thread_id;
94 intptr reserved;
95
96 };
97 #else
98 struct thread_tag_entry_s
99 {
100 thread_t id;
101 char tag[8];
102 thread_t thread_id;
103 };
104 #endif
105
106 typedef struct thread_tag_entry_s thread_tag_entry_s;
107
108 static const char thread_tag_unknown[THREAD_TAG_SIZE] = {'-','-','-','-','-','-','-',' '};
109 #if __SIZEOF_POINTER__ == 8
110 static thread_tag_entry_s thread_tag_entry[THREAD_TAG_HASH_SIZE] = {{0,{0,0,0,0,0,0,0,0},0,0}};
111 #else
112 static thread_tag_entry_s thread_tag_entry[THREAD_TAG_HASH_SIZE] = {{0,{0,0,0,0,0,0,0,0},0}};
113 #endif
114 static mutex_t thread_tag_mtx = MUTEX_INITIALIZER;
115
116 static int
thread_id_key(thread_t id_)117 thread_id_key(thread_t id_)
118 {
119 intptr id = (intptr)id_;
120 unsigned int key = (u32)id;
121 if(sizeof(id) == 8)
122 {
123 key ^= (u32)(id >> 32);
124 }
125 return key % THREAD_TAG_HASH_PRIME;
126 }
127
128 const char *
thread_get_tag_with_pid_and_tid(pid_t pid_,thread_t tid_)129 thread_get_tag_with_pid_and_tid(pid_t pid_, thread_t tid_)
130 {
131 intptr pid = (intptr)pid_;
132 intptr tid = (intptr)tid_;
133
134 thread_t id = (thread_t)(tid ^ pid);
135 int key = thread_id_key(id);
136
137 for(int c = THREAD_TAG_HASH_SIZE;;) // no need to mtx this
138 {
139 if(thread_tag_entry[key].id == id)
140 {
141 #if VERBOSE_THREAD_LOG
142 const char *tag = thread_tag_entry[key].tag;
143 osformatln(termout, "[%i] thread_get_tag_with_pid_and_tid(%i,%p) => %p = %c%c%c%c%c%c%c%c (%i)", getpid(), pid, tid, id,
144 tag[0],tag[1],tag[2],tag[3],
145 tag[4],tag[5],tag[6],tag[7], key);
146 #endif
147 return thread_tag_entry[key].tag;
148 }
149
150 if(--c == 0)
151 {
152 #if VERBOSE_THREAD_LOG
153 osformatln(termout, "[%i] thread_get_tag_with_pid_and_tid(%i,%p) => %p = unknown (%i)", getpid(), pid, tid, id, thread_id_key(id));
154 #endif
155 return thread_tag_unknown;
156 }
157
158 key = (key + 1) & THREAD_TAG_HASH_PRIME;
159 }
160
161 // should never be reached
162
163 // return thread_tag_unknown;
164 }
165
166 char *
thread_copy_tag_with_pid_and_tid(pid_t pid,thread_t tid,char * out_9_bytes)167 thread_copy_tag_with_pid_and_tid(pid_t pid, thread_t tid, char *out_9_bytes)
168 {
169 memcpy(out_9_bytes, thread_get_tag_with_pid_and_tid(pid, tid), 9);
170 out_9_bytes[8] = '\0';
171 return out_9_bytes;
172 }
173
174 #if DEBUG_THREAD_TAG_COLLISIONS
175 static int thread_set_tag_with_pid_and_tid_collisions = 0;
176 #endif
177
178 void
thread_set_tag_with_pid_and_tid(pid_t pid_,thread_t tid_,const char * tag8chars)179 thread_set_tag_with_pid_and_tid(pid_t pid_, thread_t tid_, const char *tag8chars)
180 {
181 intptr pid = (intptr)pid_;
182 intptr tid = (intptr)tid_;
183
184 thread_t id = (thread_t)(tid ^ pid);
185 int key = thread_id_key(id);
186
187 #if VERBOSE_THREAD_LOG
188 const char *tag = tag8chars;
189 osformatln(termout, "[%i] thread_set_tag_with_pid_and_tid(%i,%p, %c%c%c%c%c%c%c%c) => %p (%i)", getpid(), pid, tid,
190 tag[0],tag[1],tag[2],tag[3],
191 tag[4],tag[5],tag[6],tag[7],
192 id, key);
193 flushout();
194 #endif
195
196 #if VERBOSE_THREAD_LOG >= 3
197 log_info("thread-tag: %i::%p: base key is %i '%c%c%c%c%c%c%c%c'", pid, tid, key,
198 tag8chars[0],tag8chars[1],tag8chars[2],tag8chars[3],tag8chars[4],tag8chars[5],tag8chars[6],tag8chars[7]);
199 #endif
200
201 mutex_lock(&thread_tag_mtx);
202 for(int c = THREAD_TAG_HASH_SIZE;;)
203 {
204 if((thread_tag_entry[key].id == 0) || (thread_tag_entry[key].id == id))
205 {
206 thread_tag_entry[key].id = id;
207
208 int i;
209 for(i = 0; i < THREAD_TAG_SIZE; ++i)
210 {
211 if(tag8chars[i] == '\0')
212 {
213 break;
214 }
215 thread_tag_entry[key].tag[i] = tag8chars[i];
216 }
217
218 for(; i < THREAD_TAG_SIZE; ++i)
219 {
220 thread_tag_entry[key].tag[i] = ' ';
221 }
222
223 thread_tag_entry[key].thread_id = (thread_t)tid;
224
225 mutex_unlock(&thread_tag_mtx);
226
227 #if VERBOSE_THREAD_LOG >= 3
228 log_warn("[%i] thread-tag: %i::%p: last key is %i, %i collisions", getpid(), pid, tid, key, THREAD_TAG_HASH_SIZE - c);
229 #endif
230 return;
231 }
232
233 if(--c == 0)
234 {
235 mutex_unlock(&thread_tag_mtx);
236 return; // ignore
237 }
238
239 key = (key + 1) & THREAD_TAG_HASH_PRIME;
240
241 #if DEBUG_THREAD_TAG_COLLISIONS
242 ++thread_set_tag_with_pid_and_tid_collisions;
243 formatln("thread_set_tag_with_pid_and_tid_collisions = %i", thread_set_tag_with_pid_and_tid_collisions);
244 #endif
245 }
246 }
247
248 void
thread_clear_tag_with_pid_and_tid(pid_t pid_,thread_t tid_)249 thread_clear_tag_with_pid_and_tid(pid_t pid_, thread_t tid_)
250 {
251 intptr pid = (intptr)pid_;
252 intptr tid = (intptr)tid_;
253
254 thread_t id = (thread_t)(tid ^ pid);
255 int key = thread_id_key(id);
256
257 #if VERBOSE_THREAD_LOG
258 osformatln(termout, "[%i] thread_clear_tag_with_pid_and_tid(%i,%p) => %p (%i)", getpid(), pid, tid, id, key);
259 flushout();
260 #endif
261
262 mutex_lock(&thread_tag_mtx);
263 for(int c = THREAD_TAG_HASH_SIZE;;)
264 {
265 if(thread_tag_entry[key].id == id)
266 {
267 thread_tag_entry[key].id = 0;
268 thread_tag_entry[key].tag[0] = 0;
269 mutex_unlock(&thread_tag_mtx);
270 return;
271 }
272
273 if(--c == 0)
274 {
275 mutex_unlock(&thread_tag_mtx);
276 return; // ignore
277 }
278
279 key = (key + 1) & THREAD_TAG_HASH_PRIME;
280 }
281 }
282
283 void
thread_tag_log_tags()284 thread_tag_log_tags()
285 {
286 for(int key = 0; key < THREAD_TAG_HASH_SIZE; ++key)
287 {
288 if(thread_tag_entry[key].id != 0)
289 {
290 log_info("thread-tag: id=%08i tag=%c%c%c%c%c%c%c%c",
291 thread_tag_entry[key].id,
292 thread_tag_entry[key].tag[0],thread_tag_entry[key].tag[1],thread_tag_entry[key].tag[2],thread_tag_entry[key].tag[3],
293 thread_tag_entry[key].tag[4],thread_tag_entry[key].tag[5],thread_tag_entry[key].tag[6],thread_tag_entry[key].tag[7]);
294 }
295 }
296 }
297
298 void
thread_tag_push_tags()299 thread_tag_push_tags()
300 {
301 for(int key = 0; key < THREAD_TAG_HASH_SIZE; ++key)
302 {
303 if(thread_tag_entry[key].id != 0)
304 {
305 #if DEBUG
306 debug_osformatln(termout, "thread-tag: pushing id=%p tag=%c%c%c%c%c%c%c%c",
307 thread_tag_entry[key].thread_id,
308 thread_tag_entry[key].tag[0],thread_tag_entry[key].tag[1],thread_tag_entry[key].tag[2],thread_tag_entry[key].tag[3],
309 thread_tag_entry[key].tag[4],thread_tag_entry[key].tag[5],thread_tag_entry[key].tag[6],thread_tag_entry[key].tag[7]);
310 /*
311 log_debug("thread-tag: pushing id=%p tag=%c%c%c%c%c%c%c%c",
312 thread_tag_entry[key].thread_id,
313 thread_tag_entry[key].tag[0],thread_tag_entry[key].tag[1],thread_tag_entry[key].tag[2],thread_tag_entry[key].tag[3],
314 thread_tag_entry[key].tag[4],thread_tag_entry[key].tag[5],thread_tag_entry[key].tag[6],thread_tag_entry[key].tag[7]);
315 */
316 #endif
317 logger_handle_set_thread_tag_with_pid_and_tid(getpid(), thread_tag_entry[key].thread_id, thread_tag_entry[key].tag);
318 }
319 }
320 }
321
322
323
thread_make_tag(const char * prefix,u32 index,u32 count,char * out_service_tag)324 void thread_make_tag(const char *prefix, u32 index, u32 count, char *out_service_tag)
325 {
326 char service_tag[THREAD_TAG_SIZE + 1];
327
328 if(prefix == NULL)
329 {
330 prefix = "X";
331 }
332
333 memset(out_service_tag, '-', THREAD_TAG_SIZE);
334
335 size_t prefix_len = strlen(prefix);
336
337 if(prefix_len > THREAD_TAG_SIZE)
338 {
339 prefix_len = THREAD_TAG_SIZE;
340 }
341 memcpy(service_tag, prefix, prefix_len);
342 for(size_t i = prefix_len; i < THREAD_TAG_SIZE; ++i)
343 {
344 service_tag[i] = ' ';
345 }
346 service_tag[THREAD_TAG_SIZE] = '\0';
347
348 if(count <= 1)
349 {
350 // good as it is
351 }
352 else if(count <= 0x10) // [ 0 ; 0x10 [ => 1 byte
353 {
354 snformat(&service_tag[7], 2, "%x", index);
355 }
356 else if(count <= 0x100)
357 {
358 snformat(&service_tag[6], 3, "%02x", index);
359 }
360 else if(count <= 0x1000)
361 {
362 snformat(&service_tag[5], 4, "%03x", index);
363 }
364 else if(count <= 0x10000)
365 {
366 snformat(&service_tag[4], 5, "%04x", index);
367 }
368 else if(count <= 0x100000)
369 {
370 snformat(&service_tag[3], 6, "%05x", index);
371 }
372 else
373 {
374 snformat(&service_tag[1], THREAD_TAG_SIZE, "%x", index);
375 }
376
377 memcpy(out_service_tag, service_tag, THREAD_TAG_SIZE);
378
379 #if VERBOSE_THREAD_LOG > 1
380 osformatln(termout, "[%i] thread_make_tag(%s,%i,%i,&): %i (%i) %p) = %c%c%c%c%c%c%c%c",
381 getpid(),
382 prefix, index, count, getpid_ex(), getpid(), thread_self(),
383 service_tag[0], service_tag[1], service_tag[2], service_tag[3],
384 service_tag[4], service_tag[5], service_tag[6], service_tag[7]);
385 #endif
386
387 }
388
389 #endif
390
391 /** @} */
392