1    | /***************************************
2    |   $Revision: 1.11 $
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,2000,2001,2002               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 "rip.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   |     newtas = (ta_str_t *)UT_calloc(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 %4lu %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  | 	   (long unsigned)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  |     UT_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  |   bigbuf = (char *)UT_malloc(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  |     bigbuf = (char *)UT_realloc(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  | }