modules/seas/statistics.c

Go to the documentation of this file.
00001 /* $Id: statistics.c 4518 2008-07-28 15:39:28Z henningw $
00002  *
00003  * Copyright (C) 2006-2007 VozTelecom Sistemas S.L
00004  *
00005  * This file is part of Kamailio, a free SIP server.
00006  *
00007  * Kamailio is free software; you can redistribute it and/or modify
00008  * it under the terms of the GNU General Public License as published by
00009  * the Free Software Foundation; either version 2 of the License, or
00010  * (at your option) any later version
00011  *
00012  * Kamailio is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License 
00018  * along with this program; if not, write to the Free Software 
00019  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020  */
00021 
00022 #include <stdlib.h>
00023 #include <time.h>
00024 #include <netdb.h>
00025 #include <sys/types.h>
00026 #include <sys/socket.h>
00027 #include <sys/time.h>
00028 #include <netinet/in.h>
00029 #include <netinet/in_systm.h>
00030 #include <netinet/ip.h> /* superset of previous */
00031 #include <string.h>
00032 #include <unistd.h>
00033 #include <stdio.h>
00034 #include <signal.h>
00035 
00036 #include "statistics.h"
00037 #include "seas.h" /*SLOG*/
00038 #include "../../mem/shm_mem.h"
00039 #include "../../resolve.h"
00040 #include "../../ut.h"
00041 #include "../../dprint.h"
00042 #include "../../locking.h"
00043 #define STATS_PAY 101
00044 
00045 struct statstable*  seas_stats_table;
00046 int stats_fd;
00047 char use_stats=0;
00048 pid_t pid;
00049 
00050 static void sig_handler(int signo)
00051 {
00052    switch(signo){
00053       case SIGTERM:
00054     LM_ERR("stats process caught SIGTERM, shutting down..\n");
00055     close(stats_fd);
00056     destroy_seas_stats_table();
00057     exit(0);
00058       default:
00059     LM_DBG("caught signal %d\n",signo);
00060    }
00061    LM_WARN("statistics process:caught signal (%d)\n",signo);
00062 }
00063 
00064 struct statstable* init_seas_stats_table(void)
00065 {
00066    /*allocs the table*/
00067    seas_stats_table= (struct statstable*)shm_malloc( sizeof( struct statstable ) );
00068    if (!seas_stats_table) {
00069       LM_ERR("no shmem for stats table (%d bytes)\n",(int)sizeof(struct statstable));
00070       return 0;
00071    }
00072    memset(seas_stats_table, 0, sizeof(struct statstable) );
00073    if(0==(seas_stats_table->mutex=lock_alloc())){
00074       LM_ERR("couldn't alloc mutex (get_lock_t)\n");
00075       shm_free(seas_stats_table);
00076       return 0;
00077    }
00078    lock_init(seas_stats_table->mutex);
00079    return seas_stats_table;
00080 }
00081 
00082 inline void destroy_seas_stats_table(void)
00083 {
00084    /*deallocs the table*/
00085    if(seas_stats_table){
00086       lock_destroy(seas_stats_table->mutex);
00087       shm_free(seas_stats_table);
00088       seas_stats_table=(struct statstable *)0;
00089    }
00090 }
00091 
00092 /** This will be called from within w_as_relay()
00093  *
00094  * TODO handle locking ?
00095  */
00096 inline void as_relay_stat(struct cell *t)
00097 {
00098    struct statscell *s;
00099    struct totag_elem *to;
00100    if(t==0)
00101       return;
00102    if(t->fwded_totags != 0){
00103       LM_DBG("seas:as_relay_stat() unable to put a payload "
00104            "in fwded_totags because it is being used !!\n");
00105       return;
00106    }
00107    if(!(s=shm_malloc(sizeof(struct statscell)))){
00108       return;
00109    }
00110    if(!(to=shm_malloc(sizeof(struct totag_elem)))){
00111       shm_free(s);
00112       return;
00113    }
00114    memset(s,0,sizeof(struct statscell));
00115    gettimeofday(&(s->u.uas.as_relay),NULL);
00116    s->type=UAS_T;
00117    to->tag.len=0;
00118    to->tag.s=(char *)s;
00119    to->next=0;
00120    to->acked=STATS_PAY;
00121    t->fwded_totags=to;
00122    lock_get(seas_stats_table->mutex);
00123    (seas_stats_table->started_transactions)++;
00124    lock_release(seas_stats_table->mutex);
00125 }
00126 
00127 /** this will be called from the SEAS event dispatcher
00128  * when it writes the event to the socket
00129  *
00130  * Parameters: a cell OR its hash_index and its label
00131  *
00132  * TODO handle locking/mutexing ?
00133  */
00134 inline void event_stat(struct cell *t)
00135 {
00136    struct statscell *s;
00137    struct totag_elem *to;
00138    if(t==0){
00139       /*seas_f.tmb.t_lookup_ident(&t,hash_index,label); BAD bcos it refcounts,
00140       * and there's no way to simply unrefcount from outside TM*/
00141       return;
00142    }
00143    if(t->fwded_totags == 0){
00144       LM_DBG("seas:event_stat() unabe to set the event_stat timeval:"
00145            " no payload found at cell!! (fwded_totags=0)\n");
00146       return;
00147    }
00148    /*esto da un CORE DUMP cuando hay mucha carga.. warning*/
00149    to=t->fwded_totags;
00150    while(to){
00151       if(to->acked==STATS_PAY){
00152     s=(struct statscell *)to->tag.s;
00153     gettimeofday(&(s->u.uas.event_sent),NULL);
00154     return;
00155       }else
00156     to=to->next;
00157    }
00158    return;
00159 }
00160 
00161 /** param i is in milliseconds*/
00162 static inline int assignIndex(int i)
00163 {
00164    return (i/100)>14?14:(i/100);
00165 }
00166 
00167 /** this will be called from the SEAS action dispatcher
00168  * when it receives the action from the socket
00169  */
00170 inline void action_stat(struct cell *t)
00171 {
00172    unsigned int seas_dispatch,as_delay;
00173    struct timeval *t1,*t2,*t3;
00174    struct statscell *s;
00175    struct totag_elem *to;
00176    if(t==0)
00177       return;
00178    if(t->fwded_totags == 0){
00179       LM_DBG("seas:event_stat() unable to set the event_stat timeval:"
00180            " no payload found at cell!! (fwded_totags=0)\n");
00181       return;
00182    }
00183    to=t->fwded_totags;
00184    while(to){
00185       if(to->acked==STATS_PAY){
00186     s=(struct statscell *)to->tag.s;
00187     gettimeofday(&(s->u.uas.action_recvd),NULL);
00188     break;
00189       }else
00190     to=to->next;
00191    }
00192    /**no statistics found**/
00193    if(to==0)
00194       return;
00195    t1=&(s->u.uas.as_relay);
00196    t2=&(s->u.uas.event_sent);
00197    t3=&(s->u.uas.action_recvd);
00198    seas_dispatch = (t2->tv_sec - t1->tv_sec)*1000 + (t2->tv_usec-t1->tv_usec)/1000;
00199    as_delay = (t3->tv_sec - t2->tv_sec)*1000 + (t3->tv_usec-t2->tv_usec)/1000;
00200 
00201    lock_get(seas_stats_table->mutex);
00202    {
00203       seas_stats_table->dispatch[assignIndex(seas_dispatch)]++;
00204       seas_stats_table->event[assignIndex(seas_dispatch)]++;
00205       (seas_stats_table->finished_transactions)++;
00206    }
00207    lock_release(seas_stats_table->mutex);
00208 }
00209 
00210 
00211 /**
00212  * stats socket sould be an IP_address:port or unix://path/to_file
00213  * TODO handling unix sockets and IPv6 !!
00214  *
00215  * returns 
00216  * 0 if no stats
00217  * 1 if stats properly started
00218  * -1 if error
00219  */
00220 int start_stats_server(char *stats_socket)
00221 {
00222    char *p,*port;
00223    unsigned short stats_port;
00224    struct hostent *he;
00225    /*use sockaddr_storage ??*/
00226    struct sockaddr_in su;
00227    int optval;
00228 
00229    use_stats=0;
00230    port=(char *)0;
00231    he=(struct hostent *)0;
00232    stats_fd=-1;
00233    p=stats_socket;
00234 
00235    if(p==0 || *p==0)
00236       return 0;
00237 
00238    if(!init_seas_stats_table()){
00239       LM_ERR("unable to init stats table, disabling statistics\n");
00240       return -1;
00241    }
00242    while(*p){
00243       if(*p == ':'){
00244     *p=0;
00245     port=p+1;
00246     break;
00247       }
00248    }
00249    if(!(he=resolvehost(stats_socket,0)))
00250       goto error;
00251    if(port==(char*)0 || *port==0)
00252       stats_port=5088;
00253    else if(!(stats_port=str2s(port,strlen(port),0))){
00254     LM_ERR("invalid port %s\n",port);
00255     goto error;
00256       }
00257    if((stats_fd=socket(he->h_addrtype, SOCK_STREAM, 0))==-1){
00258       LM_ERR("trying to open server socket (%s)\n",strerror(errno));
00259       goto error;
00260    }
00261    optval=1;
00262    if (setsockopt(stats_fd, SOL_SOCKET, SO_REUSEADDR, (void*)&optval, sizeof(optval))==-1) {
00263       LM_ERR("setsockopt (%s)\n",strerror(errno));
00264       goto error;
00265    }
00266    su.sin_family = he->h_addrtype;
00267    su.sin_port=htons(stats_port);
00268    memcpy(&su.sin_addr,he->h_addr_list[0],4);
00269    if((bind(stats_fd,(struct sockaddr*)&su,sizeof(struct sockaddr_in)))==-1){
00270       LM_ERR( "bind (%s)\n",strerror(errno));
00271       goto error;
00272    }
00273    if(listen(stats_fd, 10)==-1){
00274       LM_ERR( "listen (%s)\n",strerror(errno));
00275       goto error;
00276    }
00277    if(!(pid=fork())){/*child*/
00278       signal(SIGTERM,sig_handler);
00279       serve_stats(stats_fd);
00280       printf("statistics Server Process exits !!\n");
00281       exit(0);
00282    }else if(pid>0){/*parent*/
00283       close(stats_fd);
00284    }else{/*error*/
00285       LM_ERR("failed to create stats server process\n");
00286       goto error;
00287    }
00288    use_stats=1;
00289    return 1;
00290 error:
00291    if(stats_fd!=-1)
00292       close(stats_fd);
00293    destroy_seas_stats_table();
00294    return -1;
00295 }
00296 
00297 /**
00298  * stats socket sould be an IP_address:port or unix://path/to_file
00299  * TODO handling unix sockets and IPv6 !!
00300  *
00301  * returns 
00302  * 0 if no stats
00303  * 1 if stats properly started
00304  * -1 if error
00305  */
00306 inline int stop_stats_server(void)
00307 {
00308    if(pid)
00309       kill(SIGTERM,pid);
00310    return 0;
00311 }
00312 
00313 void serve_stats(int fd)
00314 {
00315    union sockaddr_union su;
00316    int sock,i,retrn;
00317    socklen_t su_len;
00318    char f;
00319    /* we install our signal handler..*/
00320    signal(SIGTERM,sig_handler);
00321    signal(SIGHUP,sig_handler);
00322    signal(SIGPIPE,sig_handler);
00323    signal(SIGQUIT,sig_handler);
00324    signal(SIGINT,sig_handler);
00325    signal(SIGCHLD,sig_handler);
00326 
00327    while(1){
00328       su_len = sizeof(union sockaddr_union);
00329       sock=-1;
00330       sock=accept(fd, &su.s, &su_len);
00331       if(sock==-1){
00332     if(errno==EINTR){
00333        continue;
00334     }else{
00335        LM_ERR("failed to accept connection: %s\n", strerror(errno));
00336        return ;
00337     }
00338       }
00339       while(0!=(i=read(sock,&f,1))){
00340     if(i==-1){
00341        if(errno==EINTR){
00342           continue;
00343        }else{
00344           LM_ERR("unknown error reading from socket\n"); 
00345           close(sock);
00346           /** and continue accept()'ing*/
00347           break;
00348        }
00349     }
00350     retrn=print_stats_info(f,sock);
00351     if(retrn==-1){
00352        /**simple error happened, dont worry*/
00353           LM_ERR("printing statisticss \n");
00354           continue;
00355     }else if(retrn==-2){
00356        /**let's go to the outer loop, and receive more Statistics clients*/
00357        LM_ERR("statistics client left\n");
00358        close(sock);
00359        break;
00360     }
00361       }
00362    }
00363 }
00364 
00365 /**
00366  * (from snprintf manual)
00367  * "The  functions  snprintf()  and vsnprintf() do not write more than size bytes (including the trailing '\\0').  If the output was truncated due to
00368  * this limit then the return value is the number of characters (not including the trailing '\\0') which would have been written to the final string
00369  * if  enough space had been available. Thus, a return value of size or more means that the output was truncated."
00370  */
00371 inline int print_stats_info(int f,int sock)
00372 {
00373 #define STATS_BUF_SIZE  400
00374    int j,k,writen;
00375    char buf[STATS_BUF_SIZE];
00376 
00377    writen=0;
00378    if(0>(k=snprintf(buf,STATS_BUF_SIZE, "Timings:      0-1   1-2   2-3   3-4   4-5   5-6   6-7   7-8   8-9   9-10  10-11 11-12 12-13 13-14 14+\n"))){
00379       goto error;
00380    }else{
00381       if(k>STATS_BUF_SIZE){
00382     j=STATS_BUF_SIZE;
00383     goto send;
00384       }
00385       j=k;
00386    }
00387    lock_get(seas_stats_table->mutex);
00388    if(0>(k=snprintf(&buf[j],STATS_BUF_SIZE-j,"UAS:dispatch: %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d\n",\
00389           seas_stats_table->dispatch[0],seas_stats_table->dispatch[1],seas_stats_table->dispatch[2],seas_stats_table->dispatch[3],seas_stats_table->dispatch[4]\
00390           ,seas_stats_table->dispatch[5],seas_stats_table->dispatch[6],seas_stats_table->dispatch[7],seas_stats_table->dispatch[8],seas_stats_table->dispatch[9],\
00391           seas_stats_table->dispatch[10],seas_stats_table->dispatch[11],seas_stats_table->dispatch[12],seas_stats_table->dispatch[13],seas_stats_table->dispatch[14]))){
00392       goto error;
00393    }else{
00394       if(k>(STATS_BUF_SIZE-j)){
00395     j=STATS_BUF_SIZE;
00396     goto send;
00397       }
00398       j+=k;
00399    }
00400    if(0>(k=snprintf(&buf[j],STATS_BUF_SIZE-j,"UAS:event:    %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d\n",\
00401           seas_stats_table->event[0],seas_stats_table->event[1],seas_stats_table->event[2],seas_stats_table->event[3],seas_stats_table->event[4]\
00402           ,seas_stats_table->event[5],seas_stats_table->event[6],seas_stats_table->event[7],seas_stats_table->event[8],seas_stats_table->event[9],\
00403           seas_stats_table->event[10],seas_stats_table->event[11],seas_stats_table->event[12],seas_stats_table->event[13],seas_stats_table->event[14]))){
00404       goto error;
00405    }else{
00406       if(k>STATS_BUF_SIZE-j){
00407     j=STATS_BUF_SIZE;
00408     goto send;
00409       }
00410       j+=k;
00411    }
00412    if(0>(k=snprintf(&buf[j],STATS_BUF_SIZE-j,"Started Transactions: %d\nTerminated Transactions:%d\nReceived replies:%d\nReceived:%d\n",\
00413           seas_stats_table->started_transactions,seas_stats_table->finished_transactions,seas_stats_table->received_replies,seas_stats_table->received))){
00414       goto error;
00415    }else{
00416       if(k>STATS_BUF_SIZE-j){
00417     j=STATS_BUF_SIZE;
00418     goto send;
00419       }
00420       j+=k;
00421    }
00422 send:
00423    lock_release(seas_stats_table->mutex);
00424 again:/*mutex is released*/
00425    k=write(sock,buf,j);
00426    if(k<0){
00427       switch(errno){
00428     case EINTR:
00429        goto again;
00430     case EPIPE:
00431        return -2;
00432       }
00433    }
00434    writen+=k;
00435    if(writen<j)
00436       goto again;
00437    return writen;
00438 error:/*mutex is locked*/
00439    lock_release(seas_stats_table->mutex);
00440    return -1;
00441 }
00442 
00443 inline void stats_reply(void)
00444 {
00445    lock_get(seas_stats_table->mutex);
00446    seas_stats_table->received_replies++;
00447    lock_release(seas_stats_table->mutex);
00448 }
00449 
00450 

Generated on Wed May 23 20:00:31 2012 for Kamailio - The Open Source SIP Server by  doxygen 1.5.6