1 /* 2 * Copyright (C) 2004, 2005, 2007, 2008, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 2000-2002 Internet Software Consortium. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 * PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 /* $Id: mutex.c,v 1.18 2011/01/04 23:47:14 tbox Exp $ */ 19 20 /*! \file */ 21 22 #include <config.h> 23 24 #include <stdio.h> 25 #include <time.h> 26 #include <sys/time.h> 27 #include <errno.h> 28 29 #include <isc/mutex.h> 30 #include <isc/util.h> 31 #include <isc/strerror.h> 32 33 #if HAVE_PTHREADS < 5 /* HP-UX 10.20 has 4, needs this */ 34 # define pthread_mutex_init(m, a) \ 35 pthread_mutex_init(m, (a) \ 36 ? *(const pthread_mutexattr_t *)(a) \ 37 : pthread_mutexattr_default) 38 # define PTHREAD_MUTEX_RECURSIVE MUTEX_RECURSIVE_NP 39 # define pthread_mutexattr_settype pthread_mutexattr_setkind_np 40 #endif 41 42 #if ISC_MUTEX_PROFILE 43 44 /*@{*/ 45 /*% Operations on timevals; adapted from FreeBSD's sys/time.h */ 46 #define timevalclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0) 47 #define timevaladd(vvp, uvp) \ 48 do { \ 49 (vvp)->tv_sec += (uvp)->tv_sec; \ 50 (vvp)->tv_usec += (uvp)->tv_usec; \ 51 if ((vvp)->tv_usec >= 1000000) { \ 52 (vvp)->tv_sec++; \ 53 (vvp)->tv_usec -= 1000000; \ 54 } \ 55 } while (0) 56 #define timevalsub(vvp, uvp) \ 57 do { \ 58 (vvp)->tv_sec -= (uvp)->tv_sec; \ 59 (vvp)->tv_usec -= (uvp)->tv_usec; \ 60 if ((vvp)->tv_usec < 0) { \ 61 (vvp)->tv_sec--; \ 62 (vvp)->tv_usec += 1000000; \ 63 } \ 64 } while (0) 65 66 /*@}*/ 67 68 #define ISC_MUTEX_MAX_LOCKERS 32 69 70 typedef struct { 71 const char * file; 72 int line; 73 unsigned count; 74 struct timeval locked_total; 75 struct timeval wait_total; 76 } isc_mutexlocker_t; 77 78 struct isc_mutexstats { 79 const char * file; /*%< File mutex was created in. */ 80 int line; /*%< Line mutex was created on. */ 81 unsigned count; 82 struct timeval lock_t; 83 struct timeval locked_total; 84 struct timeval wait_total; 85 isc_mutexlocker_t * cur_locker; 86 isc_mutexlocker_t lockers[ISC_MUTEX_MAX_LOCKERS]; 87 }; 88 89 #ifndef ISC_MUTEX_PROFTABLESIZE 90 #define ISC_MUTEX_PROFTABLESIZE (1024 * 1024) 91 #endif 92 static isc_mutexstats_t stats[ISC_MUTEX_PROFTABLESIZE]; 93 static int stats_next = 0; 94 static isc_boolean_t stats_init = ISC_FALSE; 95 static pthread_mutex_t statslock = PTHREAD_MUTEX_INITIALIZER; 96 97 98 isc_result_t 99 isc_mutex_init_profile(isc_mutex_t *mp, const char *file, int line) { 100 int i, err; 101 102 err = pthread_mutex_init(&mp->mutex, NULL); 103 if (err == ENOMEM) 104 return (ISC_R_NOMEMORY); 105 if (err != 0) 106 return (ISC_R_UNEXPECTED); 107 108 RUNTIME_CHECK(pthread_mutex_lock(&statslock) == 0); 109 110 if (stats_init == ISC_FALSE) 111 stats_init = ISC_TRUE; 112 113 /* 114 * If all statistics entries have been used, give up and trigger an 115 * assertion failure. There would be no other way to deal with this 116 * because we'd like to keep record of all locks for the purpose of 117 * debugging and the number of necessary locks is unpredictable. 118 * If this failure is triggered while debugging, named should be 119 * rebuilt with an increased ISC_MUTEX_PROFTABLESIZE. 120 */ 121 RUNTIME_CHECK(stats_next < ISC_MUTEX_PROFTABLESIZE); 122 mp->stats = &stats[stats_next++]; 123 124 RUNTIME_CHECK(pthread_mutex_unlock(&statslock) == 0); 125 126 mp->stats->file = file; 127 mp->stats->line = line; 128 mp->stats->count = 0; 129 timevalclear(&mp->stats->locked_total); 130 timevalclear(&mp->stats->wait_total); 131 for (i = 0; i < ISC_MUTEX_MAX_LOCKERS; i++) { 132 mp->stats->lockers[i].file = NULL; 133 mp->stats->lockers[i].line = 0; 134 mp->stats->lockers[i].count = 0; 135 timevalclear(&mp->stats->lockers[i].locked_total); 136 timevalclear(&mp->stats->lockers[i].wait_total); 137 } 138 139 return (ISC_R_SUCCESS); 140 } 141 142 isc_result_t 143 isc_mutex_lock_profile(isc_mutex_t *mp, const char *file, int line) { 144 struct timeval prelock_t; 145 struct timeval postlock_t; 146 isc_mutexlocker_t *locker = NULL; 147 int i; 148 149 gettimeofday(&prelock_t, NULL); 150 151 if (pthread_mutex_lock(&mp->mutex) != 0) 152 return (ISC_R_UNEXPECTED); 153 154 gettimeofday(&postlock_t, NULL); 155 mp->stats->lock_t = postlock_t; 156 157 timevalsub(&postlock_t, &prelock_t); 158 159 mp->stats->count++; 160 timevaladd(&mp->stats->wait_total, &postlock_t); 161 162 for (i = 0; i < ISC_MUTEX_MAX_LOCKERS; i++) { 163 if (mp->stats->lockers[i].file == NULL) { 164 locker = &mp->stats->lockers[i]; 165 locker->file = file; 166 locker->line = line; 167 break; 168 } else if (mp->stats->lockers[i].file == file && 169 mp->stats->lockers[i].line == line) { 170 locker = &mp->stats->lockers[i]; 171 break; 172 } 173 } 174 175 if (locker != NULL) { 176 locker->count++; 177 timevaladd(&locker->wait_total, &postlock_t); 178 } 179 180 mp->stats->cur_locker = locker; 181 182 return (ISC_R_SUCCESS); 183 } 184 185 isc_result_t 186 isc_mutex_unlock_profile(isc_mutex_t *mp, const char *file, int line) { 187 struct timeval unlock_t; 188 189 UNUSED(file); 190 UNUSED(line); 191 192 if (mp->stats->cur_locker != NULL) { 193 gettimeofday(&unlock_t, NULL); 194 timevalsub(&unlock_t, &mp->stats->lock_t); 195 timevaladd(&mp->stats->locked_total, &unlock_t); 196 timevaladd(&mp->stats->cur_locker->locked_total, &unlock_t); 197 mp->stats->cur_locker = NULL; 198 } 199 200 return ((pthread_mutex_unlock((&mp->mutex)) == 0) ? \ 201 ISC_R_SUCCESS : ISC_R_UNEXPECTED); 202 } 203 204 205 void 206 isc_mutex_statsprofile(FILE *fp) { 207 isc_mutexlocker_t *locker; 208 int i, j; 209 210 fprintf(fp, "Mutex stats (in us)\n"); 211 for (i = 0; i < stats_next; i++) { 212 fprintf(fp, "%-12s %4d: %10u %lu.%06lu %lu.%06lu %5d\n", 213 stats[i].file, stats[i].line, stats[i].count, 214 stats[i].locked_total.tv_sec, 215 stats[i].locked_total.tv_usec, 216 stats[i].wait_total.tv_sec, 217 stats[i].wait_total.tv_usec, 218 i); 219 for (j = 0; j < ISC_MUTEX_MAX_LOCKERS; j++) { 220 locker = &stats[i].lockers[j]; 221 if (locker->file == NULL) 222 continue; 223 fprintf(fp, " %-11s %4d: %10u %lu.%06lu %lu.%06lu %5d\n", 224 locker->file, locker->line, locker->count, 225 locker->locked_total.tv_sec, 226 locker->locked_total.tv_usec, 227 locker->wait_total.tv_sec, 228 locker->wait_total.tv_usec, 229 i); 230 } 231 } 232 } 233 234 #endif /* ISC_MUTEX_PROFILE */ 235 236 #if ISC_MUTEX_DEBUG && defined(PTHREAD_MUTEX_ERRORCHECK) 237 isc_result_t 238 isc_mutex_init_errcheck(isc_mutex_t *mp) 239 { 240 pthread_mutexattr_t attr; 241 int err; 242 243 if (pthread_mutexattr_init(&attr) != 0) 244 return (ISC_R_UNEXPECTED); 245 246 if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK) != 0) { 247 pthread_mutexattr_destroy(&attr); 248 return (ISC_R_UNEXPECTED); 249 } 250 251 err = pthread_mutex_init(mp, &attr) != 0) 252 pthread_mutexattr_destroy(&attr); 253 if (err == ENOMEM) 254 return (ISC_R_NOMEMORY); 255 return ((err == 0) ? ISC_R_SUCCESS : ISC_R_UNEXPECTED); 256 } 257 #endif 258 259 #if ISC_MUTEX_DEBUG && defined(__NetBSD__) && defined(PTHREAD_MUTEX_ERRORCHECK) 260 pthread_mutexattr_t isc__mutex_attrs = { 261 PTHREAD_MUTEX_ERRORCHECK, /* m_type */ 262 0 /* m_flags, which appears to be unused. */ 263 }; 264 #endif 265 266 #if !(ISC_MUTEX_DEBUG && defined(PTHREAD_MUTEX_ERRORCHECK)) && !ISC_MUTEX_PROFILE 267 isc_result_t 268 isc__mutex_init(isc_mutex_t *mp, const char *file, unsigned int line) { 269 char strbuf[ISC_STRERRORSIZE]; 270 isc_result_t result = ISC_R_SUCCESS; 271 int err; 272 273 err = pthread_mutex_init(mp, ISC__MUTEX_ATTRS); 274 if (err == ENOMEM) 275 return (ISC_R_NOMEMORY); 276 if (err != 0) { 277 isc__strerror(errno, strbuf, sizeof(strbuf)); 278 UNEXPECTED_ERROR(file, line, "isc_mutex_init() failed: %s", 279 strbuf); 280 result = ISC_R_UNEXPECTED; 281 } 282 return (result); 283 } 284 #endif 285