Main Page | Namespace List | Class Hierarchy | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

rk_util.c

Go to the documentation of this file.
00001 /*
00002  * rk_util.c
00003  *
00004  * Copyright (c) 2003, 2004 The University of Utah and the Flux Group.
00005  * All rights reserved.
00006  *
00007  * This file is licensed under the terms of the GNU Public License.  
00008  * See the file "license.terms" for restrictions on redistribution 
00009  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
00010  */
00011 
00012 /**
00013  * @file rk_util.c
00014  *
00015  * Implementation of the functions in rk_util.h.
00016  */
00017 
00018 #include "config.h"
00019 
00020 #include <ctype.h>
00021 #include <errno.h>
00022 #include <stdio.h>
00023 #include <fcntl.h>
00024 #include <string.h>
00025 #include <stdlib.h>
00026 #include <unistd.h>
00027 
00028 #include <sys/types.h>
00029 
00030 #include "assert_pp.h"
00031 #include "instrumentation.h"
00032 
00033 #include <rk.h>
00034 #include "rk_util.h"
00035 
00036 /* BEGIN Instrumentation data */
00037 
00038 #if defined(INSTR_rk_resource_set_get_usage_data)
00039 static struct iPoint INSTR_get_point = {
00040     INSTR_rk_resource_set_get_usage_data,
00041 };
00042 #endif
00043 
00044 #if defined(INSTR_rk_proc_usage_data)
00045 static struct iPoint INSTR_rk_proc_usage_point = {
00046     INSTR_rk_proc_usage_data,
00047 };
00048 #endif
00049 
00050 /* END Instrumentation data */
00051 
00052 rk_resource_set_t rk_resource_set_get_by_name(const char *name)
00053 {
00054     rk_resource_set_t retval = NULL_RESOURCE_SET;
00055 #define MAX_RK_SETS 128
00056     rk_resource_set_t rk_sets[MAX_RK_SETS];
00057     char rs_name[RK_NAME_LEN + 1];
00058     int lpc, actual;
00059 
00060     require(name != NULL);
00061     
00062     actual = rk_resource_sets_get_list(rk_sets, MAX_RK_SETS);
00063     for( lpc = 0; (lpc < actual) && (retval == NULL_RESOURCE_SET); lpc++ )
00064     {
00065         rs_name[0] = '\0';
00066         if( (rk_resource_set_get_name(rk_sets[lpc], rs_name) == 0) &&
00067             (strcmp(rs_name, name) == 0) )
00068         {
00069             retval = rk_sets[lpc];
00070         }
00071     }
00072 #undef MAX_RK_SETS
00073     return( retval );
00074 }
00075 
00076 /**
00077  * Function for comparing two rk_resource_set_proc objects.  Required for
00078  * quicksort.
00079  *
00080  * @param a The left hand side of the comparison.
00081  * @param b The right hand side of the comparison.
00082  * @return Zero if "a" and "b" are equal, -1 if "a" is less than "b", and 1 if
00083  * "a" is greater than "b".
00084  */
00085 static int rk_resource_set_proc_compare(const void *a, const void *b)
00086 {
00087     rk_resource_set_proc_t pca, pcb;
00088     int retval = 0;
00089 
00090     require(a != NULL);
00091     require(b != NULL);
00092 
00093     pca = (rk_resource_set_proc_t)a;
00094     pcb = (rk_resource_set_proc_t)b;
00095     
00096     if( pca->pid < pcb->pid )
00097         retval = -1;
00098     else if( pca->pid == pcb->pid )
00099         retval = 0;
00100     else if( pca->pid > pcb->pid )
00101         retval = 1;
00102 
00103     ensure(retval >= -1);
00104     ensure(retval <=  1);
00105     
00106     return( retval );
00107 }
00108 
00109 /**
00110  * Search for a particular process ID in a process cache.
00111  *
00112  * @param pc The process cache to search.
00113  * @param pid The process ID to search for.
00114  * @return The index in the process cache array where the process ID was found
00115  * or -1 if the process does not exist in the array.
00116  */
00117 static int rk_resource_set_proc_cache_search(rk_resource_set_proc_cache_t pc,
00118                                              pid_t pid)
00119 {
00120     int high, low = 0, probe, retval;
00121 
00122     require(pc != NULL);
00123     require(pc->data != NULL);
00124     require(pc->used <= pc->length);
00125     require(pid != 0);
00126 
00127     /* The array is sorted so we do a binary search. */
00128     high = pc->used;
00129     while( low <= (high - 1) )
00130     {
00131         probe = (low + high) / 2;
00132         if( pid > pc->data[probe].pid )
00133             low = probe + 1;
00134         else
00135             high = probe;
00136     }
00137     if( (high >= pc->used) || (pc->data[high].pid != pid) )
00138     {
00139         retval = -1;
00140     }
00141     else
00142     {
00143         retval = high;
00144     }
00145 
00146     ensure(retval >= -1);
00147     ensure((retval == -1) || (retval < pc->used));
00148     ensure((retval == -1) || (pc->data[retval].pid == pid));
00149     
00150     return( retval );
00151 }
00152 
00153 /**
00154  * Add an element to a process cache.
00155  *
00156  * @param pc The process cache to add an element to.
00157  * @param pid The process ID to add.
00158  * @return The index in the process cache array where the new element can be
00159  * found.
00160  */
00161 static int rk_resource_set_proc_cache_add(rk_resource_set_proc_cache_t pc,
00162                                           pid_t pid)
00163 {
00164     int retval = -1;
00165 
00166     require(pc != NULL);
00167     require(pc->data != NULL);
00168     require(pc->used <= pc->length);
00169     require(pid != 0);
00170 
00171     /* Make sure there is enough room. */
00172     if( pc->used < pc->length )
00173     {
00174         int index;
00175 
00176         /* Allocate an element, */
00177         index = pc->used;
00178         pc->used += 1;
00179         /* ... fill in the fields, */
00180         pc->data[index].pid = pid;
00181         pc->data[index].fd = -1;
00182         pc->data[index].cpu_usage = 0;
00183         /* ... resort the array, and */
00184         qsort(pc->data,
00185               pc->used,
00186               sizeof(struct rk_resource_set_proc),
00187               rk_resource_set_proc_compare);
00188         /* ... find the new location for the element. */
00189         retval = rk_resource_set_proc_cache_search(pc, pid);
00190     }
00191 
00192     ensure(retval >= -1);
00193     ensure(retval < pc->used);
00194     ensure((retval == -1) || (pc->data[retval].pid == pid));
00195     
00196     return( retval );
00197 }
00198 
00199 /**
00200  * Intersect the process cache array with a list of the currently active
00201  * process IDs.
00202  *
00203  * @param pc The process cache to intersect.
00204  * @param procs The list process IDs to intersect with the process cache.
00205  * @param procs_length The length of the "procs" array.
00206  */
00207 static unsigned long long rk_resource_set_proc_cache_intersect(
00208         rk_resource_set_proc_cache_t pc, pid_t *procs, size_t procs_length)
00209 {
00210     unsigned long long retval = 0;
00211     unsigned int lpc;
00212 
00213     require(pc != NULL);
00214     require(pc->data != NULL);
00215     require(pc->used <= pc->length);
00216     require(procs != NULL);
00217 
00218     for( lpc = 0; lpc < pc->used; )
00219     {
00220         int lpc2;
00221 
00222         /* Try to find the cached ID in the active list and */
00223         for( lpc2 = 0;
00224              (lpc2 < procs_length) && (procs[lpc2] != pc->data[lpc].pid);
00225              lpc2++ )
00226         {
00227         }
00228         /* ... remove it if it is not found. */
00229         if( lpc2 == procs_length )
00230         {
00231             close(pc->data[lpc].fd);
00232             retval += pc->data[lpc].cpu_usage;
00233             pc->data[lpc] = pc->data[lpc + 1];
00234             pc->used -= 1;
00235         }
00236         else
00237         {
00238             lpc += 1;
00239         }
00240     }
00241     return( retval );
00242 }
00243 
00244 int rk_resource_set_get_usage(rk_resource_set_t rs,
00245                               rk_resource_set_usage_t rsu_inout,
00246                               rk_resource_set_proc_cache_t pc_inout)
00247 {
00248     int retval = 0;
00249 
00250     if( (rs == NULL) || (rsu_inout == NULL) )
00251     {
00252         retval = EINVAL;
00253     }
00254     else if( (pc_inout != NULL) && (pc_inout->used > pc_inout->length) )
00255     {
00256         retval = EINVAL;
00257     }
00258     else
00259     {
00260 #define MAX_PID_COUNT 128
00261         pid_t procs[MAX_PID_COUNT];
00262         int lpc;
00263         
00264         /* Get the list of active processes and */
00265         rsu_inout->proc_count =
00266             rk_resource_set_get_proclist(rs, procs, MAX_PID_COUNT);
00267         rsu_inout->active_cpu_usage = 0;
00268         /* ... compute their total CPU usage. */
00269         for( lpc = 0;
00270              (lpc < rsu_inout->proc_count) && (retval == 0);
00271              lpc++ )
00272         {
00273             int index = -1, file = -1, cached;
00274             char scratch[1024]; // XXX
00275             
00276             /* Try to get a handle on the process' "stat" file and */
00277             if( (pc_inout == NULL) ||
00278                 (index = rk_resource_set_proc_cache_search(
00279                 pc_inout, procs[lpc])) == -1 )
00280             {
00281                 /* The file is not yet cached, open it and */
00282                 snprintf(scratch,
00283                          sizeof(scratch),
00284                          "/proc/%d/stat",
00285                          procs[lpc]);
00286                 if( (file = open(scratch, O_RDONLY)) == -1 )
00287                 {
00288                     cached = 0;
00289                     retval = errno;
00290                 }
00291                 /* ... try to add it to the cache. */
00292                 else if( (pc_inout == NULL) ||
00293                          (index = rk_resource_set_proc_cache_add(
00294                         pc_inout,
00295                         procs[lpc])) == -1 )
00296                 {
00297                     cached = 0;
00298                 }
00299                 else
00300                 {
00301                     pc_inout->data[index].fd = file;
00302                     cached = 1;
00303                 }
00304             }
00305             else
00306             {
00307                 file = pc_inout->data[index].fd;
00308                 if( lseek(file, 0, SEEK_SET) == -1 )
00309                 {
00310                     perror("lseek");
00311                     ensure(0);
00312                 }
00313                 cached = 1;
00314             }
00315             /* ... read the CPU usage. */
00316             if( file != -1 )
00317             {
00318                 unsigned long long usage = 0;
00319                 struct timeval utime, stime;
00320                 off_t off = 0;
00321                 int rc;
00322                 
00323                 memset(&utime, 0, sizeof(utime));
00324                 memset(&stime, 0, sizeof(stime));
00325                 
00326                 while( (rc = read(file,
00327                                   &scratch[off],
00328                                   sizeof(scratch) - off)) > 0 )
00329                 {
00330                     off += rc;
00331                     if( scratch[off - 1] == '\n' ) break;
00332                 }
00333                 if( rc == -1 )
00334                 {
00335                     retval = errno;
00336                 }
00337                 else
00338                 {
00339                     int index, saved_index, space_count = 0, len;
00340                     
00341                     scratch[off] = '\0';
00342                     len = strlen(scratch);
00343                     /* BEGIN Hack */
00344                     /*
00345                      * Alas, there is some glitch that causes extra data
00346                      * to be read, so we have to back track a bit to try
00347                      * and avoid it.
00348                      */
00349                     saved_index = len - 1;
00350                     for( index = len - 1; index > (len - 20); index-- )
00351                     {
00352                         if( scratch[index] == '\n' )
00353                             saved_index = index;
00354                     }
00355                     /* END Hack */
00356                     index = saved_index;
00357                     for( ; space_count < 4; index-- )
00358                     {
00359                         if( isspace(scratch[index]) )
00360                         {
00361                             space_count += 1;
00362                         }
00363                     }
00364                     if( sscanf(&scratch[index],
00365                                "%ld %ld %ld %ld",
00366                                &utime.tv_sec,
00367                                &utime.tv_usec,
00368                                &stime.tv_sec,
00369                                &stime.tv_usec) != 4 )
00370                     {
00371                         fprintf(stderr,
00372                                 "BEGIN oscratch\n%s\nEND\n",
00373                                 scratch);
00374                         fprintf(stderr, "scratch %s\n", &scratch[index]);
00375                         retval = ENOSYS;
00376                     }
00377                 }
00378                 
00379                 usage += utime.tv_sec * 1000000;
00380                 usage += utime.tv_usec;
00381                 usage += stime.tv_sec * 1000000;
00382                 usage += stime.tv_usec;
00383                 INSTR_rk_proc_usage(&INSTR_rk_proc_usage_point, usage);
00384                 rsu_inout->active_cpu_usage += usage;
00385                 if( index >= 0 )
00386                 {
00387                     pc_inout->data[index].cpu_usage = usage;
00388                 }
00389                 if( !cached )
00390                 {
00391                     close(file);
00392                 }
00393             }
00394         }
00395         /*
00396          * If the set of processes has changed, remove any dead ones from
00397          * the cache.
00398          */
00399         if( (pc_inout != NULL) &&
00400             (pc_inout->used > rsu_inout->proc_count) )
00401         {
00402             rsu_inout->inactive_cpu_usage +=
00403                 rk_resource_set_proc_cache_intersect(pc_inout,
00404                                                      procs,
00405                                                      rsu_inout->proc_count);
00406         }
00407     }
00408     return( retval );
00409 }
00410 
00411 void rk_resource_set_proc_cache_release(rk_resource_set_proc_cache_t pc)
00412 {
00413     int lpc;
00414     
00415     require(pc != NULL);
00416     require(pc->data != NULL);
00417     require(pc->used <= pc->length);
00418     
00419     for( lpc = 0; lpc < pc->used; lpc++ )
00420     {
00421         close(pc->data[lpc].fd);
00422         pc->data[lpc].fd = -1;
00423     }
00424     pc->used = 0;
00425     pc = NULL;
00426 }

Generated on Fri Oct 22 07:50:24 2004 for CPU Broker by  doxygen 1.3.9.1