Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

lock_timing.C

Go to the documentation of this file.
00001 // Copyright (C) 2004, Kenneth C. Schalk
00002 // 
00003 // This file is part of Vesta.
00004 // 
00005 // Vesta is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU Lesser General Public
00007 // License as published by the Free Software Foundation; either
00008 // version 2.1 of the License, or (at your option) any later version.
00009 // 
00010 // Vesta is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 // Lesser General Public License for more details.
00014 // 
00015 // You should have received a copy of the GNU Lesser General Public
00016 // License along with Vesta; if not, write to the Free Software
00017 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 
00019 // lock_timing.C - Implementation of performance debug of StableLock
00020 // and VolatileRootLock.
00021 
00022 // Last modified on Tue Dec 13 16:28:30 EST 2005 by ken@xorian.net
00023 
00024 #include <Basics.H>
00025 #include <BufStream.H>
00026 #include <FS.H>
00027 
00028 #include <iomanip>
00029 
00030 #include "logging.H"
00031 
00032 #include "lock_timing.H"
00033 #include "VRConcurrency.H"
00034 
00035 #include <stdio.h> // FILE *
00036 #include <sys/time.h> /* struct timeval */
00037 #include <assert.h>
00038 
00039 using std::setw;
00040 using std::setfill;
00041 using Basics::OBufStream;
00042 
00043 // This is from AccessControl.C, but not declared in a header file.
00044 extern ReadersWritersLock userGroup_lock;
00045 
00046 #define TIMING_RECORDER_BUF_SIZE 32768
00047 
00048 enum which_lock_t
00049   {
00050     stable,
00051     volatileRoot,
00052     userGroup
00053   };
00054 
00055 class Lock_Timing_Recorder
00056 {
00057 private:
00058   // Global list of all timing recorders.
00059   static Basics::mutex list_mu;
00060   static Lock_Timing_Recorder *list_head;
00061   Lock_Timing_Recorder *list_next;
00062 
00063   // Mutex protecting the other fields of this class.  This is only
00064   // needed so that a background thread can
00065   Basics::mutex mu;
00066 
00067   // Integer that identifies the thread this object corresponds to.
00068   unsigned long my_thread_index;
00069 
00070   // File descriptor we're writing our data to.
00071   FILE *timing_record_stream;
00072 
00073   // The time at which we should switch to a new timing file.
00074   unsigned long file_end_time;
00075 
00076   // Buffer with data prepared to write out.
00077   char write_buffer[TIMING_RECORDER_BUF_SIZE];
00078 
00079   // Number of bytes in the buffer so far.
00080   unsigned int current_byte_count;
00081 
00082   // Does this thread have either or both of the locks?
00083   bool stable_locked, volatileRoot_locked, userGroup_locked;
00084 
00085   // Time of last record for each lock.  This gets set each time a
00086   // record is recorded for this lock.  The next record is recorded as
00087   // a delta from this time, so each record records the time since the
00088   // last record.
00089   struct timeval stable_time, volatileRoot_time, userGroup_time;
00090 
00091   // Record 6 bytes for a delta from the last time.
00092   void record_delta(struct timeval *last_time);
00093 
00094   // When we need to, start a new timing file.
00095   void start_new_file(struct timeval &current_time);
00096 
00097   // Pick data structures based on which lock we're dealing with
00098   void lock_data(which_lock_t lock,
00099                  /*OUT*/ struct timeval *&last_time_p,
00100                  /*OUT*/ bool *&locked_p,
00101                  /*OUT*/ char &lock_marker);
00102 
00103   void write_delta_record(char record_marker,
00104                           char lock_marker,
00105                           struct timeval *last_time_p);
00106 
00107 public:
00108   Lock_Timing_Recorder();
00109   ~Lock_Timing_Recorder();
00110 
00111   void acquire_read_start(which_lock_t lock);
00112 
00113   void acquire_read_done(which_lock_t lock, bool failed = false);
00114 
00115   void acquire_write_start(which_lock_t lock);
00116 
00117   void acquire_write_done(which_lock_t lock, bool failed = false);
00118 
00119   void release_start(which_lock_t lock);
00120 
00121   void release_done(which_lock_t lock);
00122 
00123   void locked_reason(which_lock_t lock, const char *reason);
00124 
00125   // Thread that periodically checks to ensure that lock timing files
00126   // have been closed, even if some threads aren't acquiring or
00127   // releasing any locks.
00128   static void *new_file_check(void *arg);
00129 };
00130 
00131 Basics::mutex Lock_Timing_Recorder::list_mu;
00132 Lock_Timing_Recorder *Lock_Timing_Recorder::list_head = 0;
00133 
00134 static pthread_key_t lock_timing_key;
00135 static pthread_once_t lock_timing_once = PTHREAD_ONCE_INIT;
00136 
00137 // Should we be recording right now?  (Note: *not* protected by a
00138 // mutex, which makes for a race condition.  We don't really care
00139 // though.  A timing recorder should see its value on the next
00140 // transaction anyway.)
00141 static bool recording_timing = false;
00142 
00143 extern Basics::mutex popen_mu;
00144 
00145 static unsigned int timing_file_minutes = 60;
00146 
00147 extern "C"
00148 {
00149   static void lock_timing_cleanup(void *tsd) throw()
00150   {
00151     if(tsd != 0)
00152       {
00153         Lock_Timing_Recorder *recorder = (Lock_Timing_Recorder *) tsd;
00154 
00155         delete recorder;
00156       }
00157   }
00158 }
00159 
00160 #if !defined(__linux__)
00161 // We can't have another thread close lock timing files on Linux due
00162 // to the fact that only the forking thread can waitpid for a child
00163 // process.
00164 static Basics::thread new_file_check_thread;
00165 #endif
00166 
00167 extern "C"
00168 {
00169   static void lock_timing_init() throw()
00170   {
00171     int err = pthread_key_create(&lock_timing_key, lock_timing_cleanup);
00172     assert(err == 0);
00173 
00174     try
00175       {
00176         Text unused;
00177         if(VestaConfig::get("Repository",
00178                             "timing_file_minutes", unused))
00179           timing_file_minutes = VestaConfig::get_int("Repository",
00180                                                      "timing_file_minutes");
00181       }
00182     catch (VestaConfig::failure f)
00183       {
00184         Repos::dprintf(DBG_ALWAYS,
00185                        "VestaConfig::failure in VestaSourceServerExport: %s\n",
00186                        f.msg.cchars());
00187         abort();
00188       }
00189 
00190 #if !defined(__linux__)
00191     // We can't have another thread close lock timing files on Linux due
00192     // to the fact that only the forking thread can waitpid for a child
00193     // process.
00194 
00195     // Start a thread to periodically make idle threads close their lock
00196     // timing files.
00197     Basics::thread_attr new_file_check_attr;
00198 #if defined (_POSIX_THREAD_PRIORITY_SCHEDULING) && !defined(__linux__)
00199     // Linux only allows the superuser to use SCHED_RR
00200     new_file_check_attr.set_schedpolicy(SCHED_RR);
00201     new_file_check_attr.set_inheritsched(PTHREAD_EXPLICIT_SCHED);
00202     new_file_check_attr.set_sched_priority(sched_get_priority_min(SCHED_RR));
00203 #endif
00204     new_file_check_thread.fork_and_detach(Lock_Timing_Recorder::new_file_check,
00205                                           0,
00206                                           new_file_check_attr);
00207 #endif
00208   }
00209 }
00210 
00211 static Lock_Timing_Recorder *find_recorder(bool create = false)
00212 {
00213   // Make sure we've been initialized.
00214   pthread_once(&lock_timing_once, lock_timing_init);
00215 
00216   // Find/create the timing recorder for this thread
00217   Lock_Timing_Recorder *recorder =
00218     (Lock_Timing_Recorder *) pthread_getspecific(lock_timing_key);
00219   if((recorder == 0) && recording_timing && create)
00220     {
00221       // Create the recorder.  (Note: constructor calls
00222       // pthread_setspecific to register this recorder as the
00223       // recoreder for this thread.)
00224       recorder = NEW(Lock_Timing_Recorder);
00225     }
00226 
00227   return recorder;
00228 }
00229 
00230 static bool determine_which_lock(ReadersWritersLock *lock_p,
00231                                  /*OUT*/ which_lock_t &lock)
00232 {
00233   // Figure out which lock this is.
00234   if(lock_p == &StableLock)
00235     {
00236       lock = stable;
00237       return true;
00238     }
00239   else if(lock_p == &VolatileRootLock)
00240     {
00241       lock = volatileRoot;
00242       return true;
00243     }
00244   else if(lock_p == &userGroup_lock)
00245     {
00246       lock = userGroup;
00247       return true;
00248     }
00249 
00250   // We only care about the big two.
00251   return false;
00252 }
00253 
00254 void rwlock_acquire_read_start(ReadersWritersLock *lock_p)
00255 {
00256   // Figure out if we're interested in this lock, and which one it is.
00257   which_lock_t lock;
00258   if(!determine_which_lock(lock_p, /*OUT*/ lock))
00259     return;
00260 
00261   // Find/create the lock timing recorder for this thread
00262   Lock_Timing_Recorder *recorder = find_recorder(true);
00263 
00264   if(recorder != 0)
00265     {
00266       recorder->acquire_read_start(lock);
00267     }
00268 }
00269 
00270 void rwlock_acquire_read_done(ReadersWritersLock *lock_p, bool failed)
00271 {
00272   // Figure out if we're interested in this lock, and which one it is.
00273   which_lock_t lock;
00274   if(!determine_which_lock(lock_p, /*OUT*/ lock))
00275     return;
00276 
00277   // Find the lock timing recorder for this thread
00278   Lock_Timing_Recorder *recorder = find_recorder();
00279 
00280   if(recorder != 0)
00281     {
00282       recorder->acquire_read_done(lock, failed);
00283     }
00284 }
00285 
00286 void rwlock_acquire_write_start(ReadersWritersLock *lock_p)
00287 {
00288   // Figure out if we're interested in this lock, and which one it is.
00289   which_lock_t lock;
00290   if(!determine_which_lock(lock_p, /*OUT*/ lock))
00291     return;
00292 
00293   // Find/create the lock timing recorder for this thread
00294   Lock_Timing_Recorder *recorder = find_recorder(true);
00295 
00296   if(recorder != 0)
00297     {
00298       recorder->acquire_write_start(lock);
00299     }
00300 }
00301 
00302 void rwlock_acquire_write_done(ReadersWritersLock *lock_p,bool failed)
00303 {
00304   // Figure out if we're interested in this lock, and which one it is.
00305   which_lock_t lock;
00306   if(!determine_which_lock(lock_p, /*OUT*/ lock))
00307     return;
00308 
00309   // Find the lock timing recorder for this thread
00310   Lock_Timing_Recorder *recorder = find_recorder();
00311 
00312   if(recorder != 0)
00313     {
00314       recorder->acquire_write_done(lock, failed);
00315     }
00316 }
00317 
00318 void rwlock_release_start(ReadersWritersLock *lock_p)
00319 {
00320   // Figure out if we're interested in this lock, and which one it is.
00321   which_lock_t lock;
00322   if(!determine_which_lock(lock_p, /*OUT*/ lock))
00323     return;
00324 
00325   // Find the lock timing recorder for this thread
00326   Lock_Timing_Recorder *recorder = find_recorder();
00327 
00328   if(recorder != 0)
00329     {
00330       recorder->release_start(lock);
00331     }
00332 }
00333 
00334 void rwlock_release_done(ReadersWritersLock *lock_p)
00335 {
00336   // Figure out if we're interested in this lock, and which one it is.
00337   which_lock_t lock;
00338   if(!determine_which_lock(lock_p, /*OUT*/ lock))
00339     return;
00340 
00341   // Find the lock timing recorder for this thread
00342   Lock_Timing_Recorder *recorder = find_recorder();
00343 
00344   if(recorder != 0)
00345     {
00346       recorder->release_done(lock);
00347     }
00348 }
00349 
00350 void rwlock_locked_reason(ReadersWritersLock *lock_p, const char *reason)
00351 {
00352   // Figure out if we're interested in this lock, and which one it is.
00353   which_lock_t lock;
00354   if(!determine_which_lock(lock_p, /*OUT*/ lock))
00355     return;
00356 
00357   // Find the lock timing recorder for this thread
00358   Lock_Timing_Recorder *recorder = find_recorder();
00359 
00360   if(recorder != 0)
00361     {
00362       recorder->locked_reason(lock, reason);
00363     }
00364 }
00365 
00366 void rwlock_timing_control(bool enable)
00367 {
00368   recording_timing = enable;
00369 }
00370 
00371 Lock_Timing_Recorder::Lock_Timing_Recorder()
00372   : current_byte_count(0), timing_record_stream(0),
00373     stable_locked(false), volatileRoot_locked(false), userGroup_locked(false),
00374     file_end_time(0), list_next(0)
00375 {
00376   pthread_once(&lock_timing_once, lock_timing_init);
00377 
00378   stable_time.tv_sec = 0;
00379   stable_time.tv_usec = 0;
00380   volatileRoot_time.tv_sec = 0;
00381   volatileRoot_time.tv_usec = 0;
00382   userGroup_time.tv_sec = 0;
00383   userGroup_time.tv_usec = 0;
00384 
00385   // Remember our thread index.
00386 #if defined(__linux__)
00387   my_thread_index = pthread_self()/1024;
00388 #elif defined(__osf__)
00389   my_thread_index = pthread_getsequence_np(pthread_self());
00390 #else
00391 #error Need way to get thread index for this platform
00392 #endif
00393 
00394   // Only one timing recorder per thread
00395   assert(pthread_getspecific(lock_timing_key) == 0);
00396 
00397   // This is the timing recorder for this thread.
00398   int err = pthread_setspecific(lock_timing_key, (void *) this);
00399   assert(err == 0);
00400 
00401   // Insert ourselves into the global list of lock timing recorders.
00402   Lock_Timing_Recorder::list_mu.lock();
00403   this->list_next = Lock_Timing_Recorder::list_head;
00404   Lock_Timing_Recorder::list_head = this;
00405   Lock_Timing_Recorder::list_mu.unlock();
00406 }
00407 
00408 void Lock_Timing_Recorder::start_new_file(struct timeval &current_time)
00409 {
00410   // Close existing stream, if any.
00411   if(timing_record_stream != 0)
00412     {
00413       fflush(timing_record_stream);
00414       popen_mu.lock();
00415       pclose(timing_record_stream);
00416       popen_mu.unlock();
00417     }
00418 
00419   // Get the year/month/day/hour for use in the timing filename
00420 
00421   // If this is the first file we're opening, use the current time
00422   // rounded down to the specified number of minutes.
00423   struct tm time_detail;
00424   if(file_end_time == 0)
00425     {
00426       localtime_r(&(current_time.tv_sec), &time_detail);
00427       time_detail.tm_sec = 0;
00428       time_detail.tm_min -= (time_detail.tm_min%timing_file_minutes);
00429     }
00430   // Use the end time for the last file to label the next file
00431   else
00432     {
00433       time_t file_label_time = file_end_time;
00434       localtime_r(&file_label_time, &time_detail);
00435     }
00436 
00437   // Generate a name for a directory to put the timings into, and
00438   // create it if it doesn't exist.
00439   OBufStream l_dirname;
00440   l_dirname << "timings."
00441             << (time_detail.tm_year+1900) << "-"
00442             << setw(2) << setfill('0') << (time_detail.tm_mon + 1) << "-"
00443             << setw(2) << setfill('0') << time_detail.tm_mday;
00444   if(!FS::Exists(l_dirname.str()))
00445     {
00446       mkdir(l_dirname.str(), 0755);
00447     }
00448 
00449   // Open the stream we'll record our results into.  We start a gzip
00450   // process to compress them as we write them.
00451   OBufStream l_cmd;
00452   l_cmd << "exec gzip - > " << l_dirname.str()
00453         << "/rwlocks.pid_" << getpid()
00454         << ".thread_" << my_thread_index
00455         << "."
00456         << setw(2) << setfill('0') << time_detail.tm_hour << "_"
00457         << setw(2) << setfill('0') << time_detail.tm_min;
00458   popen_mu.lock();
00459   timing_record_stream = popen(l_cmd.str(), "w");
00460   popen_mu.unlock();
00461   assert(timing_record_stream != 0);
00462 
00463   // Compute the time at which we should start a new file (the
00464   // beginning of the next interval).
00465   file_end_time = mktime(&time_detail) + 60*timing_file_minutes;
00466 }
00467 
00468 Lock_Timing_Recorder::~Lock_Timing_Recorder()
00469 {
00470   if(timing_record_stream != 0)
00471     {
00472       fflush(timing_record_stream);
00473       popen_mu.lock();
00474       pclose(timing_record_stream);
00475       popen_mu.unlock();
00476     }
00477 
00478   // Remove ourselves from the global list of lock timing recorders.
00479   Lock_Timing_Recorder::list_mu.lock();
00480   Lock_Timing_Recorder **points_to_me = &Lock_Timing_Recorder::list_head;
00481   while((*points_to_me != this) &&
00482         (*points_to_me != 0))
00483     {
00484       points_to_me = &((*points_to_me)->list_next);
00485     }
00486   if(*points_to_me == this)
00487     {
00488       *points_to_me = this->list_next;
00489     }
00490   Lock_Timing_Recorder::list_mu.unlock();
00491 
00492 }
00493 
00494 void Lock_Timing_Recorder::lock_data(which_lock_t lock,
00495                                      /*OUT*/ struct timeval *&last_time_p,
00496                                      /*OUT*/ bool *&locked_p,
00497                                      /*OUT*/ char &lock_marker)
00498 {
00499   last_time_p = 0;
00500   locked_p = 0;
00501   lock_marker = 0;
00502 
00503   // Pick the data structures for the lock we're working with.
00504   switch(lock)
00505     {
00506     case stable:
00507       last_time_p = &(this->stable_time);
00508       locked_p = &(this->stable_locked);
00509       lock_marker = 1;
00510       break;
00511     case volatileRoot:
00512       last_time_p = &(this->volatileRoot_time);
00513       locked_p = &(this->volatileRoot_locked);
00514       lock_marker = 2;
00515       break;
00516     case userGroup:
00517       last_time_p = &(this->userGroup_time);
00518       locked_p = &(this->userGroup_locked);
00519       lock_marker = 3;
00520       break;
00521     }
00522   assert(last_time_p != 0);
00523   assert(locked_p != 0);
00524   assert(lock_marker != 0);
00525 }
00526 
00527 #define USECS_PER_SEC 1000000
00528 
00529 static struct timeval delta_time(const struct timeval &time_a,
00530                                  const struct timeval &time_b)
00531 {
00532   struct timeval result;
00533 
00534   if(time_a.tv_sec == time_b.tv_sec)
00535     {
00536       result.tv_sec = 0;
00537       // Need to handle time moving backwards thanks to ntp
00538       result.tv_usec = ((time_b.tv_usec >= time_a.tv_usec)
00539                         ? time_b.tv_usec - time_a.tv_usec
00540                         : 0);
00541     }
00542   else if(time_b.tv_sec > time_a.tv_sec)
00543     {
00544       result.tv_sec = (time_b.tv_sec - time_a.tv_sec) - 1;
00545       result.tv_usec = (time_b.tv_usec + USECS_PER_SEC) - time_a.tv_usec;
00546       if(result.tv_usec > USECS_PER_SEC)
00547         {
00548           result.tv_sec += result.tv_usec/USECS_PER_SEC;
00549           result.tv_usec %= USECS_PER_SEC;
00550         }
00551     }
00552   else
00553     {
00554       // Need to handle time moving backwards thanks to ntp
00555       result.tv_sec = 0;
00556       result.tv_usec = 0;
00557     }
00558 
00559   return result;
00560 }
00561 
00562 void Lock_Timing_Recorder::record_delta(struct timeval *last_time)
00563 {
00564   // Get the current time
00565   struct timeval current_time;
00566   struct timezone unused_tz;
00567   int err = gettimeofday(&current_time, &unused_tz);
00568   assert(err == 0);
00569 
00570   // Find the time delta
00571   struct timeval delta = delta_time(*last_time, current_time);
00572 
00573   // Seconds delta from start
00574   unsigned short sec_delta = delta.tv_sec;
00575   assert(sec_delta == delta.tv_sec);
00576   memcpy(&(write_buffer[current_byte_count]),
00577          (void *) &sec_delta, sizeof(unsigned short));
00578   assert(sizeof(unsigned short) == 2);
00579   current_byte_count += sizeof(unsigned short);
00580 
00581   // Microseconds delta from start
00582   unsigned int usec_delta = delta.tv_usec;
00583   memcpy(&(write_buffer[current_byte_count]),
00584          (void *) &usec_delta, sizeof(unsigned int));
00585   assert(sizeof(unsigned int) == 4);
00586   current_byte_count += sizeof(unsigned int);
00587 
00588   // Change the last time to the current time so that he next delta
00589   // will be relative to this time.
00590   *last_time = current_time;
00591 }
00592 
00593 void Lock_Timing_Recorder::write_delta_record(char record_marker,
00594                                               char lock_marker,
00595                                               struct timeval *last_time_p)
00596 {
00597   // Record which lock this is.
00598   write_buffer[current_byte_count++] = lock_marker;
00599 
00600   // Record the type of record (read lock acquired, start release,
00601   // release done, etc.)
00602   write_buffer[current_byte_count++] = record_marker;
00603 
00604   // Record the time since we started with this lock.
00605   record_delta(last_time_p);
00606 
00607   // Make sure we haven't run out of buffer space.
00608   assert(current_byte_count < TIMING_RECORDER_BUF_SIZE);
00609 }
00610 
00611 void Lock_Timing_Recorder::acquire_read_start(which_lock_t lock)
00612 {
00613   this->mu.lock();
00614 
00615   struct timeval *last_time_p;
00616   bool *locked_p;
00617   char lock_marker;
00618 
00619   // If we're not supposed to be recording timing, do nothing.
00620   if(!recording_timing)
00621     {
00622       this->mu.unlock();
00623       return;
00624     }
00625 
00626   // Pick the data structures for the lock we're working with.
00627   lock_data(lock, last_time_p, locked_p, lock_marker);
00628 
00629   // It better not already be locked by this thread.
00630   assert(!(*locked_p));
00631 
00632   // Get the start time
00633   struct timezone unused_tz;
00634   int err = gettimeofday(last_time_p, &unused_tz);
00635   assert(err == 0);
00636 
00637   // Start a new timing file if we should
00638   if((timing_record_stream == 0) || (last_time_p->tv_sec >= file_end_time))
00639     {
00640       start_new_file(*last_time_p);
00641 
00642       // Get the time again to avoid counting the change of file for
00643       // this call.
00644       err = gettimeofday(last_time_p, &unused_tz);
00645       assert(err == 0);
00646     }
00647 
00648   // ------------------------------------------------------------
00649   // Place the acquireRead start record into the buffer
00650   // ------------------------------------------------------------
00651 
00652   // Which lock?
00653   write_buffer[current_byte_count++] = lock_marker;
00654 
00655   // acquireRead start marker byte
00656   write_buffer[current_byte_count++] = 1;
00657 
00658   // Start time seconds
00659   unsigned int start_sec = last_time_p->tv_sec;
00660   memcpy(&(write_buffer[current_byte_count]), (void *) &start_sec,
00661          sizeof(unsigned int));
00662   current_byte_count += sizeof(unsigned int);
00663 
00664   // Start time microseconds
00665   unsigned int start_usec = last_time_p->tv_usec;
00666   memcpy(&(write_buffer[current_byte_count]), (void *) &start_usec,
00667          sizeof(unsigned int));
00668   current_byte_count += sizeof(unsigned int);
00669 
00670   // Make sure we haven't run out of buffer space.
00671   assert(current_byte_count < TIMING_RECORDER_BUF_SIZE);
00672 
00673   this->mu.unlock();
00674 }
00675 
00676 void Lock_Timing_Recorder::acquire_read_done(which_lock_t lock,
00677                                              bool failed)
00678 {
00679   this->mu.lock();
00680 
00681   // If we haven't recorded an earlier record for this thread, we've
00682   // got nothing to do.
00683   if(current_byte_count == 0)
00684     {
00685       this->mu.unlock();
00686       return;
00687     }
00688 
00689   struct timeval *last_time_p;
00690   bool *locked_p;
00691   char lock_marker;
00692 
00693   // Pick the data structures for the lock we're working with.
00694   lock_data(lock, last_time_p, locked_p, lock_marker);
00695 
00696   // If we don't have a start time forthis lock, we've got nothing to
00697   // do.
00698   if(last_time_p->tv_sec == 0)
00699     {
00700       this->mu.unlock();
00701       return;
00702     }
00703 
00704   // This thread now holds this lock.
00705   assert(!(*locked_p));
00706   *locked_p = true;
00707 
00708   // Write the record for this point.  (2 indicates a read lock was
00709   // acquired, 3 indicates a failed tryRead.)
00710   write_delta_record(failed ? 3 : 2,
00711                      lock_marker,
00712                      last_time_p);
00713 
00714   this->mu.unlock();
00715 }
00716 
00717 void Lock_Timing_Recorder::acquire_write_start(which_lock_t lock)
00718 {
00719   this->mu.lock();
00720 
00721   struct timeval *last_time_p;
00722   bool *locked_p;
00723   char lock_marker;
00724 
00725   // If we're not supposed to be recording timing, do nothing.
00726   if(!recording_timing)
00727     {
00728       this->mu.unlock();
00729       return;
00730     }
00731 
00732   // Pick the data structures for the lock we're working with.
00733   lock_data(lock, last_time_p, locked_p, lock_marker);
00734 
00735   // It better not already be locked by this thread.
00736   assert(!(*locked_p));
00737 
00738   // Get the start time
00739   struct timezone unused_tz;
00740   int err = gettimeofday(last_time_p, &unused_tz);
00741   assert(err == 0);
00742 
00743   // Start a new timing file if we should
00744   if((timing_record_stream == 0) || (last_time_p->tv_sec >= file_end_time))
00745     {
00746       start_new_file(*last_time_p);
00747 
00748       // Get the time again to avoid counting the change of file for
00749       // this call.
00750       err = gettimeofday(last_time_p, &unused_tz);
00751       assert(err == 0);
00752     }
00753 
00754   // ------------------------------------------------------------
00755   // Place the acquireWrite start record into the buffer
00756   // ------------------------------------------------------------
00757 
00758   // Which lock?
00759   write_buffer[current_byte_count++] = lock_marker;
00760 
00761   // acquireWrite start marker byte
00762   write_buffer[current_byte_count++] = 4;
00763 
00764   // Start time seconds
00765   unsigned int start_sec = last_time_p->tv_sec;
00766   memcpy(&(write_buffer[current_byte_count]), (void *) &start_sec,
00767          sizeof(unsigned int));
00768   current_byte_count += sizeof(unsigned int);
00769 
00770   // Start time microseconds
00771   unsigned int start_usec = last_time_p->tv_usec;
00772   memcpy(&(write_buffer[current_byte_count]), (void *) &start_usec,
00773          sizeof(unsigned int));
00774   current_byte_count += sizeof(unsigned int);
00775 
00776   // Make sure we haven't run out of buffer space.
00777   assert(current_byte_count < TIMING_RECORDER_BUF_SIZE);
00778 
00779   this->mu.unlock();
00780 }
00781 
00782 void Lock_Timing_Recorder::acquire_write_done(which_lock_t lock,
00783                                               bool failed)
00784 {
00785   this->mu.lock();
00786 
00787   // If we haven't recorded an earlier record for this thread, we've
00788   // got nothing to do.
00789   if(current_byte_count == 0)
00790     {
00791       this->mu.unlock();
00792       return;
00793     }
00794 
00795   struct timeval *last_time_p;
00796   bool *locked_p;
00797   char lock_marker;
00798 
00799   // Pick the data structures for the lock we're working with.
00800   lock_data(lock, last_time_p, locked_p, lock_marker);
00801 
00802   // If we don't have a start time forthis lock, we've got nothing to
00803   // do.
00804   if(last_time_p->tv_sec == 0)
00805     {
00806       this->mu.unlock();
00807       return;
00808     }
00809 
00810   // This thread now holds this lock.
00811   assert(!(*locked_p));
00812   *locked_p = true;
00813 
00814   // Write the record for this point.  (5 indicates a write lock was
00815   // acquired, 5 indicates a failed tryWrite.)
00816   write_delta_record(failed ? 6 : 5,
00817                      lock_marker,
00818                      last_time_p);
00819 
00820   this->mu.unlock();
00821 }
00822 
00823 void Lock_Timing_Recorder::release_start(which_lock_t lock)
00824 {
00825   this->mu.lock();
00826 
00827   // If we haven't recorded an earlier record for this thread, we've
00828   // got nothing to do.
00829   if(current_byte_count == 0)
00830     {
00831       this->mu.unlock();
00832       return;
00833     }
00834 
00835   struct timeval *last_time_p;
00836   bool *locked_p;
00837   char lock_marker;
00838 
00839   // Pick the data structures for the lock we're working with.
00840   lock_data(lock, last_time_p, locked_p, lock_marker);
00841 
00842   // If we have no record of this thread holding this lock (which
00843   // can happen when lock timing is first turned on), do nothing.
00844   if(!*locked_p)
00845     {
00846       this->mu.unlock();
00847       return;
00848     }
00849 
00850   // Write the record for this point.
00851   write_delta_record(7,
00852                      lock_marker,
00853                      last_time_p);
00854 
00855   this->mu.unlock();
00856 }
00857 
00858 void Lock_Timing_Recorder::release_done(which_lock_t lock)
00859 {
00860   this->mu.lock();
00861 
00862   // If we haven't recorded an earlier record for this thread, we've
00863   // got nothing to do.
00864   if(current_byte_count == 0)
00865     {
00866       this->mu.unlock();
00867       return;
00868     }
00869 
00870   struct timeval *last_time_p;
00871   bool *locked_p;
00872   char lock_marker;
00873 
00874   // Pick the data structures for the lock we're working with.
00875   lock_data(lock, last_time_p, locked_p, lock_marker);
00876 
00877   // If we have no record of this thread holding this lock (which
00878   // can happen when lock timing is first turned on), do nothing.
00879   if(!*locked_p)
00880     {
00881       this->mu.unlock();
00882       return;
00883     }
00884 
00885   // This thread is giving up this lock.
00886   *locked_p = false;
00887 
00888   // Write the record for this point.
00889   write_delta_record(8,
00890                      lock_marker,
00891                      last_time_p);
00892 
00893   // Reset the last time for this lock.
00894   last_time_p->tv_sec = 0;
00895   last_time_p->tv_usec = 0;
00896 
00897   // If neither lock is now held by this thread, it's time to flush
00898   // our write buffer.
00899   if(!stable_locked && !volatileRoot_locked && !userGroup_locked)
00900     {
00901       // Write the entire record for this call
00902       size_t written = fwrite(write_buffer, current_byte_count, 1,
00903                               timing_record_stream);
00904       assert(written == 1);
00905       fflush(timing_record_stream);
00906 
00907       // Reset the byte count
00908       current_byte_count = 0;
00909     }
00910 
00911   this->mu.unlock();
00912 }
00913 
00914 void Lock_Timing_Recorder::locked_reason(which_lock_t lock,
00915                                          const char *reason)
00916 {
00917   this->mu.lock();
00918 
00919   // If we haven't recorded an earlier record for this thread, we've
00920   // got nothing to do.
00921   if(current_byte_count == 0)
00922     {
00923       this->mu.unlock();
00924       return;
00925     }
00926 
00927   struct timeval *last_time_p;
00928   bool *locked_p;
00929   char lock_marker;
00930 
00931   // Pick the data structures for the lock we're working with.
00932   lock_data(lock, last_time_p, locked_p, lock_marker);
00933 
00934   // If we have no record of this thread holding this lock (which
00935   // can happen when lock timing is first turned on), do nothing.
00936   if(!*locked_p)
00937     {
00938       this->mu.unlock();
00939       return;
00940     }
00941 
00942   // ------------------------------------------------------------
00943   // Place the locked reason record into the buffer
00944   // ------------------------------------------------------------
00945 
00946   // Which lock?
00947   write_buffer[current_byte_count++] = lock_marker;
00948 
00949   // Locked reason marker byte
00950   write_buffer[current_byte_count++] = 9;
00951 
00952   // Locked reason string
00953   strcpy(&(write_buffer[current_byte_count]), reason);
00954   current_byte_count += strlen(reason)+1;
00955 
00956   // Make sure we haven't run out of buffer space.
00957   assert(current_byte_count < TIMING_RECORDER_BUF_SIZE);
00958 
00959   this->mu.unlock();
00960 }
00961 
00962 void *Lock_Timing_Recorder::new_file_check(void *arg)
00963 {
00964   // Sleep for one quarter the period of a lock timing file.  This
00965   // offsets us a bit to avoid falling exaqctly on the time when other
00966   // threads would be starting a new file anyway.
00967   unsigned int to_sleep = timing_file_minutes * 15;
00968   while(to_sleep > 0)
00969     to_sleep = sleep(to_sleep);
00970 
00971   while(1)
00972     {
00973       // Sleep for half the period of a lock timing file.  This gives
00974       // us two chances to close each lock timing file during the
00975       // period of the file.  We may need that if we hit one while it
00976       // happens to hold a lock.
00977       to_sleep = timing_file_minutes * 30;
00978       while(to_sleep > 0)
00979         to_sleep = sleep(to_sleep);
00980 
00981       // Get the current time.
00982       struct timeval current_time;
00983       struct timezone unused_tz;
00984       int err = gettimeofday(&current_time, &unused_tz);
00985       assert(err == 0);
00986 
00987       // Loop over the list of all lock timing recorders.
00988       Lock_Timing_Recorder::list_mu.lock();
00989       Lock_Timing_Recorder *cur = Lock_Timing_Recorder::list_head;
00990       while(cur != 0)
00991         {
00992           cur->mu.lock();
00993           // If this lock timing recorder has an open stream, and its
00994           // thread holds neither lock, and this lock timing file is
00995           // due to be closed, make it start a new one.
00996           if((cur->timing_record_stream != 0) &&
00997              !cur->stable_locked && !cur->volatileRoot_locked && !cur->userGroup_locked &&
00998              (current_time.tv_sec > cur->file_end_time))
00999             {
01000               cur->start_new_file(current_time);
01001             }
01002           cur->mu.unlock();
01003 
01004           // Move on to the next element in the list.
01005           cur = cur->list_next;
01006         }
01007       Lock_Timing_Recorder::list_mu.unlock();
01008     }
01009 
01010   // Unreachable.
01011   return 0;
01012 }

Generated on Mon May 8 00:48:44 2006 for Vesta by  doxygen 1.4.2