00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <BufStream.H>
00025 #include <iomanip>
00026
00027 #include <Basics.H>
00028
00029 #include "VestaSource.H"
00030 #include <VestaConfig.H>
00031
00032 #include "logging.H"
00033 #include "timing.H"
00034
00035 #include <assert.h>
00036 #include <pthread.h>
00037 #include <unistd.h>
00038 #include <string.h>
00039 #include <stdlib.h>
00040
00041 using std::setw;
00042 using std::setfill;
00043 using Basics::OBufStream;
00044
00045 static pthread_key_t timing_key;
00046 static pthread_once_t timing_once = PTHREAD_ONCE_INIT;
00047
00048
00049
00050
00051
00052 static bool recording_timing = false;
00053
00054 Basics::mutex popen_mu;
00055
00056 static unsigned int timing_file_minutes = 60;
00057
00058 extern "C"
00059 {
00060 static void timing_init()
00061 {
00062 int err = pthread_key_create(&timing_key, NULL);
00063 assert(err == 0);
00064
00065 try
00066 {
00067 Text unused;
00068 if(VestaConfig::get("Repository",
00069 "timing_file_minutes", unused))
00070 timing_file_minutes = VestaConfig::get_int("Repository",
00071 "timing_file_minutes");
00072 }
00073 catch (VestaConfig::failure f)
00074 {
00075 Repos::dprintf(DBG_ALWAYS,
00076 "VestaConfig::failure in VestaSourceServerExport: %s\n",
00077 f.msg.cchars());
00078 abort();
00079 }
00080
00081 }
00082 }
00083
00084 void timing_start_call(unsigned int proc)
00085 {
00086 pthread_once(&timing_once, timing_init);
00087 Timing_Recorder *recorder =
00088 (Timing_Recorder *) pthread_getspecific(timing_key);
00089
00090 if(recorder != 0)
00091 {
00092 recorder->start(proc);
00093 }
00094 }
00095
00096 void timing_dupe_status(int new_call)
00097 {
00098 pthread_once(&timing_once, timing_init);
00099 Timing_Recorder *recorder =
00100 (Timing_Recorder *) pthread_getspecific(timing_key);
00101
00102 if(recorder != 0)
00103 {
00104 recorder->dupe_status(new_call != 0);
00105 }
00106 }
00107
00108 void timing_record_time_point(const char *file, unsigned int line)
00109 {
00110 pthread_once(&timing_once, timing_init);
00111 Timing_Recorder *recorder =
00112 (Timing_Recorder *) pthread_getspecific(timing_key);
00113
00114 if(recorder != 0)
00115 {
00116 recorder->time_point(file, line);
00117 }
00118 }
00119
00120 void timing_end_call( struct timeval *delta_time)
00121 {
00122 pthread_once(&timing_once, timing_init);
00123 Timing_Recorder *recorder =
00124 (Timing_Recorder *) pthread_getspecific(timing_key);
00125
00126 if(recorder != 0)
00127 {
00128 struct timeval delta = recorder->end();
00129 if(delta_time != 0)
00130 {
00131 *delta_time = delta;
00132 }
00133 }
00134 else if(delta_time != 0)
00135 {
00136 delta_time->tv_sec = 0;
00137 delta_time->tv_usec = 0;
00138 }
00139 }
00140
00141 int timing_in_call()
00142 {
00143 pthread_once(&timing_once, timing_init);
00144 Timing_Recorder *recorder =
00145 (Timing_Recorder *) pthread_getspecific(timing_key);
00146
00147 if(recorder != 0)
00148 {
00149 return (recorder->in_call() ? 1 : 0);
00150 }
00151 return 0;
00152 }
00153
00154 void timing_control(bool enable)
00155 {
00156 recording_timing = enable;
00157 }
00158
00159 void timing_record_LongId(const LongId &lid)
00160 {
00161 pthread_once(&timing_once, timing_init);
00162 Timing_Recorder *recorder =
00163 (Timing_Recorder *) pthread_getspecific(timing_key);
00164
00165 if(recorder != 0)
00166 {
00167 recorder->record_LongId(lid);
00168 }
00169 }
00170
00171
00172 static unsigned long thread_index()
00173 {
00174 #if defined(__linux__)
00175 return pthread_self()/1024;
00176 #elif defined(__osf__)
00177 return pthread_getsequence_np(pthread_self());
00178 #else
00179 #error Need way to get thread index for this platform
00180 #endif
00181 }
00182
00183 #define USECS_PER_SEC 1000000
00184
00185 static struct timeval delta_time(const struct timeval &time_a,
00186 const struct timeval &time_b)
00187 {
00188 struct timeval result;
00189
00190 if(time_a.tv_sec == time_b.tv_sec)
00191 {
00192 result.tv_sec = 0;
00193
00194 result.tv_usec = ((time_b.tv_usec >= time_a.tv_usec)
00195 ? time_b.tv_usec - time_a.tv_usec
00196 : 0);
00197 }
00198 else if(time_b.tv_sec > time_a.tv_sec)
00199 {
00200 result.tv_sec = (time_b.tv_sec - time_a.tv_sec) - 1;
00201 result.tv_usec = (time_b.tv_usec + USECS_PER_SEC) - time_a.tv_usec;
00202 if(result.tv_usec > USECS_PER_SEC)
00203 {
00204 result.tv_sec += result.tv_usec/USECS_PER_SEC;
00205 result.tv_usec %= USECS_PER_SEC;
00206 }
00207 }
00208 else
00209 {
00210
00211 result.tv_sec = 0;
00212 result.tv_usec = 0;
00213 }
00214
00215 return result;
00216 }
00217
00218 static pthread_once_t timing_cleanup_once = PTHREAD_ONCE_INIT;
00219 static Basics::mutex recorder_list_mu;
00220 Timing_Recorder *Timing_Recorder::first = 0;
00221
00222 extern "C"
00223 {
00224 void Timing_Recorder_exit_cleanup() throw()
00225 {
00226 recorder_list_mu.lock();
00227 Timing_Recorder *cur = Timing_Recorder::first;
00228 while(cur)
00229 {
00230 Timing_Recorder *next = cur->next;
00231 delete cur;
00232 cur = next;
00233 }
00234 Timing_Recorder::first = 0;
00235 recorder_list_mu.unlock();
00236 }
00237
00238 void Timing_Recorder_exit_cleanup_setup() throw()
00239 {
00240 atexit(Timing_Recorder_exit_cleanup);
00241 }
00242 }
00243
00244 Timing_Recorder::Timing_Recorder()
00245 : current_byte_count(0), timing_record_stream(0), file_end_time(0)
00246 {
00247 pthread_once(&timing_once, timing_init);
00248
00249
00250 assert(pthread_getspecific(timing_key) == 0);
00251
00252
00253 int err = pthread_setspecific(timing_key, (void *) this);
00254 assert(err == 0);
00255
00256
00257 pthread_once(&timing_cleanup_once, Timing_Recorder_exit_cleanup_setup);
00258
00259
00260 recorder_list_mu.lock();
00261 this->next = Timing_Recorder::first;
00262 Timing_Recorder::first = this;
00263 recorder_list_mu.unlock();
00264 }
00265
00266 void Timing_Recorder::start_new_file(struct timeval *current_time)
00267 {
00268
00269 if(timing_record_stream != 0)
00270 {
00271 fflush(timing_record_stream);
00272 popen_mu.lock();
00273 pclose(timing_record_stream);
00274 popen_mu.unlock();
00275 }
00276
00277
00278
00279
00280
00281 struct tm time_detail;
00282 if(file_end_time == 0)
00283 {
00284
00285 struct timeval temp_time;
00286 if(current_time == 0)
00287 {
00288 struct timezone unused_tz;
00289
00290 gettimeofday(&temp_time, &unused_tz);
00291
00292 current_time = &temp_time;
00293 }
00294
00295 localtime_r(&(current_time->tv_sec), &time_detail);
00296 time_detail.tm_sec = 0;
00297 time_detail.tm_min -= (time_detail.tm_min%timing_file_minutes);
00298 }
00299
00300 else
00301 {
00302 time_t file_label_time = file_end_time;
00303 localtime_r(&file_label_time, &time_detail);
00304 }
00305
00306
00307
00308 OBufStream l_dirname;
00309 l_dirname << "timings."
00310 << (time_detail.tm_year+1900) << "-"
00311 << setw(2) << setfill('0') << (time_detail.tm_mon + 1) << "-"
00312 << setw(2) << setfill('0') << time_detail.tm_mday;
00313 if(!FS::Exists(l_dirname.str()))
00314 {
00315 mkdir(l_dirname.str(), 0755);
00316 }
00317
00318
00319
00320 OBufStream l_cmd;
00321 l_cmd << "exec gzip - > " << l_dirname.str()
00322 << "/nfs_calls.pid_" << getpid()
00323 << ".thread_" << thread_index()
00324 << "."
00325 << setw(2) << setfill('0') << time_detail.tm_hour << "_"
00326 << setw(2) << setfill('0') << time_detail.tm_min;
00327 popen_mu.lock();
00328 timing_record_stream = popen(l_cmd.str(), "w");
00329 popen_mu.unlock();
00330 assert(timing_record_stream != 0);
00331
00332
00333
00334 file_end_time = mktime(&time_detail) + 60*timing_file_minutes;
00335 }
00336
00337 Timing_Recorder::~Timing_Recorder()
00338 {
00339 if(timing_record_stream != 0)
00340 {
00341 fflush(timing_record_stream);
00342 popen_mu.lock();
00343 pclose(timing_record_stream);
00344 popen_mu.unlock();
00345 }
00346 }
00347
00348 void Timing_Recorder::start(unsigned int proc)
00349 {
00350
00351 assert(current_byte_count == 0);
00352
00353
00354 struct timezone unused_tz;
00355 int err = gettimeofday(&(this->start_time), &unused_tz);
00356 assert(err == 0);
00357
00358
00359 if(!recording_timing)
00360 return;
00361
00362
00363 if((timing_record_stream == 0) || (this->start_time.tv_sec >= file_end_time))
00364 {
00365 start_new_file(&(this->start_time));
00366
00367
00368
00369 err = gettimeofday(&(this->start_time), &unused_tz);
00370 assert(err == 0);
00371 }
00372
00373
00374
00375
00376
00377
00378 write_buffer[0] = 1;
00379
00380
00381 write_buffer[1] = proc;
00382 assert(write_buffer[1] == proc);
00383
00384
00385 unsigned int start_sec = this->start_time.tv_sec;
00386 memcpy(&(write_buffer[2]), (void *) &start_sec, sizeof(unsigned int));
00387
00388
00389 unsigned int start_usec = this->start_time.tv_usec;
00390 memcpy(&(write_buffer[6]), (void *) &start_usec, sizeof(unsigned int));
00391
00392
00393 current_byte_count = 2+(sizeof(unsigned int)*2);
00394 assert(current_byte_count == 10);
00395 }
00396
00397 struct timeval Timing_Recorder::record_delta()
00398 {
00399
00400 struct timeval current_time;
00401 struct timezone unused_tz;
00402 int err = gettimeofday(¤t_time, &unused_tz);
00403 assert(err == 0);
00404
00405
00406 struct timeval delta = delta_time(this->start_time, current_time);
00407
00408
00409 unsigned short sec_delta = delta.tv_sec;
00410 assert(sec_delta == delta.tv_sec);
00411 memcpy(&(write_buffer[current_byte_count]),
00412 (void *) &sec_delta, sizeof(unsigned short));
00413 assert(sizeof(unsigned short) == 2);
00414 current_byte_count += sizeof(unsigned short);
00415
00416
00417 unsigned int usec_delta = delta.tv_usec;
00418 memcpy(&(write_buffer[current_byte_count]),
00419 (void *) &usec_delta, sizeof(unsigned int));
00420 assert(sizeof(unsigned int) == 4);
00421 current_byte_count += sizeof(unsigned int);
00422
00423 return delta;
00424 }
00425
00426 void Timing_Recorder::dupe_status(bool new_call)
00427 {
00428
00429 if(!in_call())
00430 return;
00431
00432
00433
00434
00435
00436
00437 write_buffer[current_byte_count++] = (new_call ? 4 : 5);
00438
00439
00440 (void) record_delta();
00441
00442
00443 assert(current_byte_count < TIMING_RECORDER_BUF_SIZE);
00444 }
00445
00446 void Timing_Recorder::time_point(const char *file, unsigned short line)
00447 {
00448
00449 if(!in_call())
00450 return;
00451
00452
00453
00454
00455
00456
00457 write_buffer[current_byte_count++] = 2;
00458
00459
00460 (void) record_delta();
00461
00462
00463 strcpy(&(write_buffer[current_byte_count]), file);
00464 current_byte_count += strlen(file)+1;
00465
00466
00467 memcpy(&(write_buffer[current_byte_count]),
00468 (void *) &line, sizeof(unsigned short));
00469 current_byte_count += sizeof(unsigned short);
00470
00471
00472 assert(current_byte_count < TIMING_RECORDER_BUF_SIZE);
00473 }
00474
00475 struct timeval Timing_Recorder::end()
00476 {
00477
00478 if(!in_call())
00479 {
00480
00481 struct timeval current_time;
00482 struct timezone unused_tz;
00483 int err = gettimeofday(¤t_time, &unused_tz);
00484 assert(err == 0);
00485
00486
00487 return delta_time(this->start_time, current_time);
00488 }
00489
00490
00491
00492
00493
00494
00495 write_buffer[current_byte_count++] = 3;
00496
00497
00498 struct timeval delta = record_delta();
00499
00500
00501 assert(current_byte_count < TIMING_RECORDER_BUF_SIZE);
00502
00503
00504 size_t written = fwrite(write_buffer, current_byte_count, 1,
00505 timing_record_stream);
00506 assert(written == 1);
00507 fflush(timing_record_stream);
00508
00509
00510 current_byte_count = 0;
00511
00512
00513 return delta;
00514 }
00515
00516 void Timing_Recorder::record_LongId(const LongId &lid)
00517 {
00518
00519 if(!in_call())
00520 return;
00521
00522
00523
00524
00525
00526
00527 write_buffer[current_byte_count++] = 6;
00528
00529
00530 memcpy(&(write_buffer[current_byte_count]), &(lid.value.byte[0]),
00531 sizeof(lid.value));
00532 current_byte_count += sizeof(lid.value);
00533
00534
00535 assert(current_byte_count < TIMING_RECORDER_BUF_SIZE);
00536 }