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