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