1 | /***************************************
2 | $Revision: 1.8 $
3 |
4 | thread accounting (ta). ta.c - functions to keep track of activities
5 | of threads within the server
6 |
7 | Status: NOT REVUED, TESTED, COMPLETE
8 |
9 | Design and implementation by: Marek Bukowy
10 |
11 | ******************/ /******************
12 | Copyright (c) 1999 RIPE NCC
13 |
14 | All Rights Reserved
15 |
16 | Permission to use, copy, modify, and distribute this software and its
17 | documentation for any purpose and without fee is hereby granted,
18 | provided that the above copyright notice appear in all copies and that
19 | both that copyright notice and this permission notice appear in
20 | supporting documentation, and that the name of the author not be
21 | used in advertising or publicity pertaining to distribution of the
22 | software without specific, written prior permission.
23 |
24 | THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
25 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
26 | AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
27 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
28 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
29 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
30 | ***************************************/
31 |
32 | #define TA_IMPL
33 | #include <ta.h>
34 |
35 |
36 | /*++++++++++++++++++++++++++++++++++++++
37 |
38 | Finds a thread by thread_id. Assumes locked list.
39 |
40 | ta_str_t *ta_findonly_l returns a pointer to the thread's record
41 |
42 | GList **list thread list
43 |
44 | pthread_t thread_id thread id
45 |
46 | ++++++++++++++++++++++++++++++++++++++*/
47 | static
48 | ta_str_t *ta_findonly_l( GList **list, pthread_t thread_id )
49 | {
50 | GList *item;
51 |
52 | /* try to find first */
53 | for(item = g_list_first(*list);
54 | item != NULL;
55 | item = g_list_next(item)) {
56 | ta_str_t *tas = (ta_str_t *) (item->data);
57 |
58 | if( tas->thread_id == thread_id ) {
59 | return tas;
60 | }
61 | }
62 | return NULL;
63 | }
64 |
65 |
66 | /*++++++++++++++++++++++++++++++++++++++
67 |
68 | Finds a thread by thread_id, or creates a new entry if there isn't one.
69 | Assumes locked list.
70 |
71 | ta_str_t *ta_findcreate_l returns a pointer to the thread's record
72 |
73 | GList **list thread list
74 |
75 | pthread_t thread_id thread id
76 |
77 | ++++++++++++++++++++++++++++++++++++++*/
78 | static
79 | ta_str_t *ta_findcreate_l( GList **list, pthread_t thread_id )
80 | {
81 | ta_str_t *newtas;
82 |
83 | if( (newtas = ta_findonly_l(list, thread_id)) == NULL) {
84 |
85 | /* not found => add */ /* zero everything*/
86 | dieif( !NOERR( wr_calloc( (void **) &newtas, 1, sizeof( ta_str_t ))));
87 | newtas->thread_id = thread_id;
88 |
89 | *list = g_list_append( *list, newtas );
90 | }
91 |
92 | return newtas;
93 | }
94 |
95 |
96 |
97 | /*++++++++++++++++++++++++++++++++++++++
98 |
99 | finds and removes an entry for a thread given by thread_id.
100 | Assumes locked list.
101 |
102 | GList **list thread list
103 |
104 | pthread_t thread_id thread id
105 |
106 | ++++++++++++++++++++++++++++++++++++++*/
107 | static
108 | void ta_remove_l(GList **list, pthread_t thread_id )
109 | {
110 | GList *item;
111 |
112 | for(item = g_list_first(*list);
113 | item != NULL;
114 | item = g_list_next(item)) {
115 | ta_str_t *tas = (ta_str_t *) (item->data);
116 |
117 | if( tas->thread_id == thread_id ) {
118 | *list = g_list_remove_link(*list, item);
119 | wr_clear_list( &item );
120 | break;
121 | }
122 | }
123 |
124 | return;
125 | }
126 |
127 |
128 | /*++++++++++++++++++++++++++++++++++++++
129 |
130 | sets the activity field in the given thread's record.
131 | Truncates the string if too long.
132 |
133 | ta_str_t *tas pointer to the thread's record
134 |
135 | char *activity new value for the activity field
136 | ++++++++++++++++++++++++++++++++++++++*/
137 | static
138 | void ta_setactivity_l(ta_str_t *tas, char *activity)
139 | {
140 | char *nl;
141 |
142 | strncpy(tas->activity, activity, TA_ACT_LEN-1);
143 | tas->activity[TA_ACT_LEN]=0;
144 | /* convert last newline to a space, if any */
145 | if( (nl=strrchr(tas->activity, '\n')) != NULL ) {
146 | *nl=' ';
147 | }
148 | }
149 |
150 |
151 | #define TA_HEADER "%-8s %15s %4s %4s %5s %5s %4s %6s %s\n"
152 | #define TA_FORMAT "%-8s %15s %4d %4d %5.1f %5.1f %4d %6.3f %s\n"
153 |
154 |
155 | /*++++++++++++++++++++++++++++++++++++++
156 |
157 | prints a header for a list of threads to a specified buffer.
158 | The output is truncated if the buffer is too small.
159 |
160 | char *buf pointer to the buffer
161 |
162 | unsigned length buffer size
163 | ++++++++++++++++++++++++++++++++++++++*/
164 | static
165 | void ta_print_header(char *buf, unsigned length)
166 | {
167 | snprintf(buf, length, TA_HEADER,
168 | "type", "from", "sock", "thr", "sess", "task", "#",
169 | "avg", "current"
170 | );
171 | }
172 |
173 |
174 | /*++++++++++++++++++++++++++++++++++++++
175 |
176 | Formats the data from one thread's record to an entry for a list of
177 | threads. Prints to a specified buffer. Calculates the average time
178 | spent per event as well as the lifetime of the thread. Checks the
179 | address of the peer on the socket. The output is truncated if the
180 | buffer is too small.
181 |
182 | ta_str_t *tas pointer to the thread's record
183 |
184 | char *buf pointer to the buffer
185 |
186 | unsigned length buffer size
187 |
188 | ut_timer_t *reftime current time
189 |
190 | ++++++++++++++++++++++++++++++++++++++*/
191 | static
192 | void ta_printone_l(ta_str_t *tas, char *buf, unsigned length,
193 | ut_timer_t *reftime)
194 | {
195 | float session, task; /* duration of the session/task */
196 | char *address = SK_getpeername(tas->sock); /* allocated! */
197 | /* can be NULL for example if the socket has just closed
198 | or the file descriptor is not a socket */
199 |
200 | session = UT_timediff( &tas->sessionstart, reftime );
201 | task = UT_timediff( &tas->taskstart, reftime );
202 |
203 | snprintf(buf, length, TA_FORMAT ,
204 | tas->type,
205 | address ? address : "",
206 | tas->sock,
207 | tas->thread_id,
208 | session,
209 | task,
210 | tas->tasks,
211 | (tas->tasks > 0) ? session / tas->tasks : 0,
212 | tas->activity);
213 |
214 | if (address) {
215 | wr_free(address);
216 | }
217 | }
218 |
219 |
220 | /*++++++++++++++++++++++++++++++++++++++
221 |
222 | Public adding function - adds the current thread to the list,
223 | storing the given socket and type string along.
224 |
225 | int sock associated socket (if any, or 0 if not)
226 |
227 | char *type type string
228 |
229 | ++++++++++++++++++++++++++++++++++++++*/
230 | void TA_add(int sock, char *type)
231 | {
232 | ta_str_t *newtas;
233 |
234 | /* lock the list */
235 | pthread_mutex_lock( &ta_mutex );
236 |
237 | /* find/create node and set peer/thread_id */
238 | newtas = ta_findcreate_l( &ta_list, pthread_self());
239 | newtas->sock = sock;
240 | newtas->tasks = 0;
241 | newtas->condat = NULL;
242 | UT_timeget( &newtas->sessionstart );
243 | UT_timeget( &newtas->taskstart ); /* just to get it a reasonable value */
244 |
245 | snprintf(newtas->type, TA_TYPE_LEN, type);
246 | ta_setactivity_l(newtas,"--");
247 |
248 | /* unlock */
249 | pthread_mutex_unlock( &ta_mutex );
250 | }
251 |
252 |
253 | /*++++++++++++++++++++++++++++++++++++++
254 |
255 | Public deletion function - deletes the current thread from the list.
256 |
257 | ++++++++++++++++++++++++++++++++++++++*/
258 | void TA_delete(void)
259 | {
260 | /* lock the list */
261 | pthread_mutex_lock( &ta_mutex );
262 |
263 | /* find & remove */
264 | ta_remove_l( &ta_list, pthread_self() );
265 |
266 | /* unlock */
267 | pthread_mutex_unlock( &ta_mutex );
268 | }
269 |
270 |
271 | /*++++++++++++++++++++++++++++++++++++++
272 |
273 | Public activity-setting function - sets the current activity string
274 | for the current thread.
275 |
276 | char *activity new value
277 |
278 | ++++++++++++++++++++++++++++++++++++++*/
279 | void TA_setactivity(char *activity)
280 | {
281 | ta_str_t *newtas;
282 |
283 | /* lock the list */
284 | pthread_mutex_lock( &ta_mutex );
285 |
286 | /* find */
287 | newtas = ta_findcreate_l( &ta_list, pthread_self());
288 |
289 | /* set the activity field */
290 | ta_setactivity_l(newtas, activity);
291 |
292 | /* unlock */
293 | pthread_mutex_unlock( &ta_mutex );
294 | }
295 |
296 |
297 | /*++++++++++++++++++++++++++++++++++++++
298 |
299 | Public condat-setting function - associates a connection data
300 | structure with the current thread.
301 |
302 | sk_conn_st *condat pointer to a connection data structure
303 |
304 | ++++++++++++++++++++++++++++++++++++++*/
305 | void TA_setcondat(sk_conn_st *condat)
306 | {
307 | ta_str_t *newtas;
308 |
309 | /* lock the list */
310 | pthread_mutex_lock( &ta_mutex );
311 |
312 | /* find */
313 | newtas = ta_findcreate_l( &ta_list, pthread_self());
314 |
315 | /* set the condat field */
316 | newtas->condat = condat;
317 |
318 | /* unlock */
319 | pthread_mutex_unlock( &ta_mutex );
320 | }
321 |
322 |
323 | /*++++++++++++++++++++++++++++++++++++++
324 |
325 | increments the event counter for the current thread.
326 |
327 | ++++++++++++++++++++++++++++++++++++++*/
328 | void TA_increment(void)
329 | {
330 | ta_str_t *newtas;
331 |
332 | /* lock the list */
333 | pthread_mutex_lock( &ta_mutex );
334 |
335 | /* find */
336 | newtas = ta_findcreate_l( &ta_list, pthread_self());
337 | /* increment task */
338 | newtas->tasks++;
339 | /* set task starting time */
340 | UT_timeget( &newtas->taskstart );
341 |
342 | /* unlock */
343 | pthread_mutex_unlock( &ta_mutex );
344 | }
345 |
346 |
347 | /*++++++++++++++++++++++++++++++++++++++
348 |
349 | resets the time and event counter of a specified thread.
350 |
351 | pthread_t thread_id thread_id
352 |
353 | ++++++++++++++++++++++++++++++++++++++*/
354 | void TA_reset_counters(pthread_t thread_id)
355 | {
356 | ta_str_t *tas;
357 |
358 | /* lock the list */
359 | pthread_mutex_lock( &ta_mutex );
360 |
361 | if( (tas = ta_findonly_l(&ta_list, thread_id)) != NULL) {
362 | UT_timeget( &tas->sessionstart );
363 | UT_timeget( &tas->taskstart ); /* just to get it a reasonable value */
364 | tas->tasks = 0;
365 | }
366 |
367 | pthread_mutex_unlock( &ta_mutex );
368 | }
369 |
370 |
371 | /*++++++++++++++++++++++++++++++++++++++
372 |
373 | Compiles a list of the threads' data records as text.
374 |
375 | char * TA_tostring returns an allocated text, must be freed after use.
376 |
377 | ++++++++++++++++++++++++++++++++++++++*/
378 | char * TA_tostring(void)
379 | {
380 | GList *item;
381 | char *bigbuf = NULL;
382 | char smallbuf[TA_PRINT_LEN];
383 | ut_timer_t reftime;
384 |
385 | ta_print_header(smallbuf, TA_PRINT_LEN);
386 | dieif( !NOERR(wr_malloc( (void **) &bigbuf, strlen(smallbuf)+2 )));
387 | strcpy(bigbuf, smallbuf);
388 | strcat(bigbuf, "\n");
389 |
390 | /* lock the list */
391 | pthread_mutex_lock( &ta_mutex );
392 |
393 | /* get reference time */
394 | UT_timeget( &reftime );
395 |
396 | /* iterate */
397 | for(item = g_list_first(ta_list);
398 | item != NULL;
399 | item = g_list_next(item)) {
400 | ta_str_t *tas = (ta_str_t *) (item->data);
401 | unsigned smalllen;
402 | unsigned biglen = ( bigbuf == NULL ) ? 0 : strlen(bigbuf);
403 |
404 | ta_printone_l(tas, smallbuf, TA_PRINT_LEN, &reftime);
405 | smalllen = strlen(smallbuf);
406 |
407 | dieif( !NOERR(wr_realloc( (void **) &bigbuf, biglen+smalllen+3 )));
408 |
409 | strcat(bigbuf, smallbuf);
410 | }
411 | /* unlock */
412 | pthread_mutex_unlock( &ta_mutex );
413 |
414 | return bigbuf;
415 | }
416 |
417 |
418 |
419 | /*++++++++++++++++++++++++++++++++++++++
420 |
421 | Finds a thread of the matching type, socket file descriptor and thread id
422 | and executes the watchdog's triggers if it's defined.
423 |
424 | char *type thread type string
425 |
426 | int sock socket #
427 |
428 | pthread_t thread_id thread id
429 |
430 | ++++++++++++++++++++++++++++++++++++++*/
431 | void TA_trigger(char *type, int sock, pthread_t thread_id)
432 | {
433 | ta_str_t *tas;
434 |
435 | /* lock the list */
436 | pthread_mutex_lock( &ta_mutex );
437 |
438 | if( (tas = ta_findonly_l(&ta_list, thread_id)) != NULL
439 | && tas->sock == sock
440 | && strcmp(tas->type, type) == 0
441 | && tas->condat != NULL
442 | && tas->condat->sock == sock
443 | ) {
444 | SK_watchtrigger(tas->condat);
445 | }
446 |
447 | /* unlock */
448 | pthread_mutex_unlock( &ta_mutex );
449 |
450 | }