1 /* -*- mode: C; c-basic-offset: 3; indent-tabs-mode: nil; -*- */ 2 /* 3 This file is part of drd, a thread error detector. 4 5 Copyright (C) 2006-2011 Bart Van Assche <bvanassche (at) acm.org>. 6 7 This program is free software; you can redistribute it and/or 8 modify it under the terms of the GNU General Public License as 9 published by the Free Software Foundation; either version 2 of the 10 License, or (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software 19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 20 02111-1307, USA. 21 22 The GNU General Public License is contained in the file COPYING. 23 */ 24 25 26 #include "drd_basics.h" 27 #include "drd_clientobj.h" 28 #include "drd_error.h" 29 #include "drd_mutex.h" 30 #include "pub_tool_vki.h" 31 #include "pub_tool_errormgr.h" /* VG_(maybe_record_error)() */ 32 #include "pub_tool_libcassert.h" /* tl_assert() */ 33 #include "pub_tool_libcbase.h" /* VG_(strlen) */ 34 #include "pub_tool_libcprint.h" /* VG_(message)() */ 35 #include "pub_tool_libcproc.h" /* VG_(read_millisecond_timer)() */ 36 #include "pub_tool_machine.h" /* VG_(get_IP)() */ 37 #include "pub_tool_threadstate.h" /* VG_(get_running_tid)() */ 38 39 40 /* Local functions. */ 41 42 static void mutex_cleanup(struct mutex_info* p); 43 static Bool mutex_is_locked(struct mutex_info* const p); 44 static void mutex_delete_thread(struct mutex_info* p, const DrdThreadId tid); 45 46 47 /* Local variables. */ 48 49 static Bool s_trace_mutex; 50 static ULong s_mutex_lock_count; 51 static ULong s_mutex_segment_creation_count; 52 static UInt s_mutex_lock_threshold_ms; 53 54 55 /* Function definitions. */ 56 57 void DRD_(mutex_set_trace)(const Bool trace_mutex) 58 { 59 tl_assert(!! trace_mutex == trace_mutex); 60 s_trace_mutex = trace_mutex; 61 } 62 63 void DRD_(mutex_set_lock_threshold)(const UInt lock_threshold_ms) 64 { 65 s_mutex_lock_threshold_ms = lock_threshold_ms; 66 } 67 68 static 69 void DRD_(mutex_initialize)(struct mutex_info* const p, 70 const Addr mutex, const MutexT mutex_type) 71 { 72 tl_assert(mutex); 73 tl_assert(p->a1 == mutex); 74 75 p->cleanup = (void(*)(DrdClientobj*))mutex_cleanup; 76 p->delete_thread 77 = (void(*)(DrdClientobj*, DrdThreadId))mutex_delete_thread; 78 p->mutex_type = mutex_type; 79 p->recursion_count = 0; 80 p->owner = DRD_INVALID_THREADID; 81 p->last_locked_segment = 0; 82 p->acquiry_time_ms = 0; 83 p->acquired_at = 0; 84 } 85 86 /** Deallocate the memory that was allocated by mutex_initialize(). */ 87 static void mutex_cleanup(struct mutex_info* p) 88 { 89 tl_assert(p); 90 91 if (s_trace_mutex) 92 DRD_(trace_msg)("[%d] mutex_destroy %s 0x%lx rc %d owner %d", 93 DRD_(thread_get_running_tid)(), 94 DRD_(mutex_get_typename)(p), p->a1, 95 p ? p->recursion_count : -1, 96 p ? p->owner : DRD_INVALID_THREADID); 97 98 if (mutex_is_locked(p)) 99 { 100 MutexErrInfo MEI = { DRD_(thread_get_running_tid)(), 101 p->a1, p->recursion_count, p->owner }; 102 VG_(maybe_record_error)(VG_(get_running_tid)(), 103 MutexErr, 104 VG_(get_IP)(VG_(get_running_tid)()), 105 "Destroying locked mutex", 106 &MEI); 107 } 108 109 DRD_(sg_put)(p->last_locked_segment); 110 p->last_locked_segment = 0; 111 } 112 113 /** Report that address 'mutex' is not the address of a mutex object. */ 114 void DRD_(not_a_mutex)(const Addr mutex) 115 { 116 MutexErrInfo MEI = { DRD_(thread_get_running_tid)(), 117 mutex, -1, DRD_INVALID_THREADID }; 118 VG_(maybe_record_error)(VG_(get_running_tid)(), 119 MutexErr, 120 VG_(get_IP)(VG_(get_running_tid)()), 121 "Not a mutex", 122 &MEI); 123 } 124 125 /** 126 * Report that address 'mutex' is not the address of a mutex object of the 127 * expected type. 128 */ 129 static void wrong_mutex_type(const Addr mutex) 130 { 131 MutexErrInfo MEI = { DRD_(thread_get_running_tid)(), 132 mutex, -1, DRD_INVALID_THREADID }; 133 VG_(maybe_record_error)(VG_(get_running_tid)(), 134 MutexErr, 135 VG_(get_IP)(VG_(get_running_tid)()), 136 "Mutex type mismatch", 137 &MEI); 138 } 139 140 static 141 struct mutex_info* 142 DRD_(mutex_get_or_allocate)(const Addr mutex, const MutexT mutex_type) 143 { 144 struct mutex_info* p; 145 146 tl_assert(offsetof(DrdClientobj, mutex) == 0); 147 p = &(DRD_(clientobj_get)(mutex, ClientMutex)->mutex); 148 if (p) 149 { 150 if (mutex_type == mutex_type_unknown || p->mutex_type == mutex_type) 151 return p; 152 else 153 { 154 wrong_mutex_type(mutex); 155 return 0; 156 } 157 } 158 159 if (DRD_(clientobj_present)(mutex, mutex + 1)) 160 { 161 DRD_(not_a_mutex)(mutex); 162 return 0; 163 } 164 165 p = &(DRD_(clientobj_add)(mutex, ClientMutex)->mutex); 166 DRD_(mutex_initialize)(p, mutex, mutex_type); 167 return p; 168 } 169 170 struct mutex_info* DRD_(mutex_get)(const Addr mutex) 171 { 172 tl_assert(offsetof(DrdClientobj, mutex) == 0); 173 return &(DRD_(clientobj_get)(mutex, ClientMutex)->mutex); 174 } 175 176 /** Called before pthread_mutex_init(). */ 177 struct mutex_info* 178 DRD_(mutex_init)(const Addr mutex, const MutexT mutex_type) 179 { 180 struct mutex_info* p; 181 182 if (s_trace_mutex) 183 DRD_(trace_msg)("[%d] mutex_init %s 0x%lx", 184 DRD_(thread_get_running_tid)(), 185 DRD_(mutex_type_name)(mutex_type), 186 mutex); 187 188 if (mutex_type == mutex_type_invalid_mutex) 189 { 190 DRD_(not_a_mutex)(mutex); 191 return 0; 192 } 193 194 p = DRD_(mutex_get)(mutex); 195 if (p) 196 { 197 const ThreadId vg_tid = VG_(get_running_tid)(); 198 MutexErrInfo MEI = { DRD_(thread_get_running_tid)(), 199 p->a1, p->recursion_count, p->owner }; 200 VG_(maybe_record_error)(vg_tid, 201 MutexErr, 202 VG_(get_IP)(vg_tid), 203 "Mutex reinitialization", 204 &MEI); 205 p->mutex_type = mutex_type; 206 return p; 207 } 208 p = DRD_(mutex_get_or_allocate)(mutex, mutex_type); 209 210 return p; 211 } 212 213 /** Called after pthread_mutex_destroy(). */ 214 void DRD_(mutex_post_destroy)(const Addr mutex) 215 { 216 struct mutex_info* p; 217 218 p = DRD_(mutex_get)(mutex); 219 if (p == 0) 220 { 221 DRD_(not_a_mutex)(mutex); 222 return; 223 } 224 225 DRD_(clientobj_remove)(mutex, ClientMutex); 226 } 227 228 /** 229 * Called before pthread_mutex_lock() is invoked. If a data structure for the 230 * client-side object was not yet created, do this now. Also check whether an 231 * attempt is made to lock recursively a synchronization object that must not 232 * be locked recursively. 233 */ 234 void DRD_(mutex_pre_lock)(const Addr mutex, MutexT mutex_type, 235 const Bool trylock) 236 { 237 struct mutex_info* p; 238 239 p = DRD_(mutex_get_or_allocate)(mutex, mutex_type); 240 if (p && mutex_type == mutex_type_unknown) 241 mutex_type = p->mutex_type; 242 243 if (s_trace_mutex) 244 DRD_(trace_msg)("[%d] %s %s 0x%lx rc %d owner %d", 245 DRD_(thread_get_running_tid)(), 246 trylock ? "pre_mutex_lock " : "mutex_trylock ", 247 p ? DRD_(mutex_get_typename)(p) : "(?)", 248 mutex, p ? p->recursion_count : -1, 249 p ? p->owner : DRD_INVALID_THREADID); 250 251 if (p == 0) 252 { 253 DRD_(not_a_mutex)(mutex); 254 return; 255 } 256 257 tl_assert(p); 258 259 if (mutex_type == mutex_type_invalid_mutex) 260 { 261 DRD_(not_a_mutex)(mutex); 262 return; 263 } 264 265 if (! trylock 266 && p->owner == DRD_(thread_get_running_tid)() 267 && p->recursion_count >= 1 268 && mutex_type != mutex_type_recursive_mutex) 269 { 270 MutexErrInfo MEI = { DRD_(thread_get_running_tid)(), 271 p->a1, p->recursion_count, p->owner }; 272 VG_(maybe_record_error)(VG_(get_running_tid)(), 273 MutexErr, 274 VG_(get_IP)(VG_(get_running_tid)()), 275 "Recursive locking not allowed", 276 &MEI); 277 } 278 } 279 280 /** 281 * Update mutex_info state when locking the pthread_mutex_t mutex. 282 * Note: this function must be called after pthread_mutex_lock() has been 283 * called, or a race condition is triggered ! 284 */ 285 void DRD_(mutex_post_lock)(const Addr mutex, const Bool took_lock, 286 const Bool post_cond_wait) 287 { 288 const DrdThreadId drd_tid = DRD_(thread_get_running_tid)(); 289 struct mutex_info* p; 290 291 p = DRD_(mutex_get)(mutex); 292 293 if (s_trace_mutex) 294 DRD_(trace_msg)("[%d] %s %s 0x%lx rc %d owner %d%s", 295 drd_tid, 296 post_cond_wait ? "cond_post_wait " : "post_mutex_lock", 297 p ? DRD_(mutex_get_typename)(p) : "(?)", 298 mutex, p ? p->recursion_count : 0, 299 p ? p->owner : VG_INVALID_THREADID, 300 took_lock ? "" : " (locking failed)"); 301 302 if (! p || ! took_lock) 303 return; 304 305 if (p->recursion_count == 0) { 306 if (p->owner != drd_tid && p->owner != DRD_INVALID_THREADID) 307 { 308 tl_assert(p->last_locked_segment); 309 310 DRD_(thread_new_segment_and_combine_vc)(drd_tid, 311 p->last_locked_segment); 312 } 313 else 314 DRD_(thread_new_segment)(drd_tid); 315 316 s_mutex_segment_creation_count++; 317 318 p->owner = drd_tid; 319 p->acquiry_time_ms = VG_(read_millisecond_timer)(); 320 p->acquired_at = VG_(record_ExeContext)(VG_(get_running_tid)(), 0); 321 s_mutex_lock_count++; 322 } else if (p->owner != drd_tid) { 323 const ThreadId vg_tid = VG_(get_running_tid)(); 324 MutexErrInfo MEI = { DRD_(thread_get_running_tid)(), 325 p->a1, p->recursion_count, p->owner }; 326 VG_(maybe_record_error)(vg_tid, 327 MutexErr, 328 VG_(get_IP)(vg_tid), 329 "The impossible happened: mutex is locked" 330 " simultaneously by two threads", 331 &MEI); 332 p->owner = drd_tid; 333 } 334 p->recursion_count++; 335 } 336 337 /** 338 * Update mutex_info state when unlocking the pthread_mutex_t mutex. 339 * 340 * @param[in] mutex Address of the client mutex. 341 * @param[in] mutex_type Mutex type. 342 * 343 * @return New value of the mutex recursion count. 344 * 345 * @note This function must be called before pthread_mutex_unlock() is called, 346 * or a race condition is triggered ! 347 */ 348 void DRD_(mutex_unlock)(const Addr mutex, MutexT mutex_type) 349 { 350 const DrdThreadId drd_tid = DRD_(thread_get_running_tid)(); 351 const ThreadId vg_tid = VG_(get_running_tid)(); 352 struct mutex_info* p; 353 354 p = DRD_(mutex_get)(mutex); 355 if (p && mutex_type == mutex_type_unknown) 356 mutex_type = p->mutex_type; 357 358 if (s_trace_mutex) { 359 DRD_(trace_msg)("[%d] mutex_unlock %s 0x%lx rc %d", 360 drd_tid, p ? DRD_(mutex_get_typename)(p) : "(?)", 361 mutex, p ? p->recursion_count : 0); 362 } 363 364 if (p == 0 || mutex_type == mutex_type_invalid_mutex) 365 { 366 DRD_(not_a_mutex)(mutex); 367 return; 368 } 369 370 if (p->owner == DRD_INVALID_THREADID) 371 { 372 MutexErrInfo MEI = { DRD_(thread_get_running_tid)(), 373 p->a1, p->recursion_count, p->owner }; 374 VG_(maybe_record_error)(vg_tid, 375 MutexErr, 376 VG_(get_IP)(vg_tid), 377 "Mutex not locked", 378 &MEI); 379 return; 380 } 381 382 tl_assert(p); 383 if (p->mutex_type != mutex_type) { 384 MutexErrInfo MEI = { DRD_(thread_get_running_tid)(), 385 p->a1, p->recursion_count, p->owner }; 386 VG_(maybe_record_error)(vg_tid, MutexErr, VG_(get_IP)(vg_tid), 387 "Mutex type changed", &MEI); 388 } 389 tl_assert(p->mutex_type == mutex_type); 390 tl_assert(p->owner != DRD_INVALID_THREADID); 391 392 if (p->owner != drd_tid || p->recursion_count <= 0) 393 { 394 MutexErrInfo MEI = { DRD_(thread_get_running_tid)(), 395 p->a1, p->recursion_count, p->owner }; 396 VG_(maybe_record_error)(vg_tid, 397 MutexErr, 398 VG_(get_IP)(vg_tid), 399 "Mutex not locked by calling thread", 400 &MEI); 401 return; 402 } 403 tl_assert(p->recursion_count > 0); 404 p->recursion_count--; 405 tl_assert(p->recursion_count >= 0); 406 407 if (p->recursion_count == 0) 408 { 409 if (s_mutex_lock_threshold_ms > 0) 410 { 411 Long held = VG_(read_millisecond_timer)() - p->acquiry_time_ms; 412 if (held > s_mutex_lock_threshold_ms) 413 { 414 HoldtimeErrInfo HEI 415 = { DRD_(thread_get_running_tid)(), 416 mutex, p->acquired_at, held, s_mutex_lock_threshold_ms }; 417 VG_(maybe_record_error)(vg_tid, 418 HoldtimeErr, 419 VG_(get_IP)(vg_tid), 420 "mutex", 421 &HEI); 422 } 423 } 424 425 /* This pthread_mutex_unlock() call really unlocks the mutex. Save the */ 426 /* current vector clock of the thread such that it is available when */ 427 /* this mutex is locked again. */ 428 429 DRD_(thread_get_latest_segment)(&p->last_locked_segment, drd_tid); 430 DRD_(thread_new_segment)(drd_tid); 431 p->acquired_at = 0; 432 s_mutex_segment_creation_count++; 433 } 434 } 435 436 void DRD_(spinlock_init_or_unlock)(const Addr spinlock) 437 { 438 struct mutex_info* mutex_p = DRD_(mutex_get)(spinlock); 439 if (mutex_p) 440 { 441 DRD_(mutex_unlock)(spinlock, mutex_type_spinlock); 442 } 443 else 444 { 445 DRD_(mutex_init)(spinlock, mutex_type_spinlock); 446 } 447 } 448 449 const char* DRD_(mutex_get_typename)(struct mutex_info* const p) 450 { 451 tl_assert(p); 452 453 return DRD_(mutex_type_name)(p->mutex_type); 454 } 455 456 const char* DRD_(mutex_type_name)(const MutexT mt) 457 { 458 switch (mt) 459 { 460 case mutex_type_unknown: 461 return "mutex"; 462 case mutex_type_invalid_mutex: 463 return "invalid mutex"; 464 case mutex_type_recursive_mutex: 465 return "recursive mutex"; 466 case mutex_type_errorcheck_mutex: 467 return "error checking mutex"; 468 case mutex_type_default_mutex: 469 return "mutex"; 470 case mutex_type_spinlock: 471 return "spinlock"; 472 } 473 tl_assert(0); 474 return "?"; 475 } 476 477 /** Return true if the specified mutex is locked by any thread. */ 478 static Bool mutex_is_locked(struct mutex_info* const p) 479 { 480 tl_assert(p); 481 return (p->recursion_count > 0); 482 } 483 484 Bool DRD_(mutex_is_locked_by)(const Addr mutex, const DrdThreadId tid) 485 { 486 struct mutex_info* const p = DRD_(mutex_get)(mutex); 487 if (p) 488 { 489 return (p->recursion_count > 0 && p->owner == tid); 490 } 491 return False; 492 } 493 494 int DRD_(mutex_get_recursion_count)(const Addr mutex) 495 { 496 struct mutex_info* const p = DRD_(mutex_get)(mutex); 497 tl_assert(p); 498 return p->recursion_count; 499 } 500 501 /** 502 * Call this function when thread tid stops to exist, such that the 503 * "last owner" field can be cleared if it still refers to that thread. 504 */ 505 static void mutex_delete_thread(struct mutex_info* p, const DrdThreadId tid) 506 { 507 tl_assert(p); 508 509 if (p->owner == tid && p->recursion_count > 0) 510 { 511 MutexErrInfo MEI = { DRD_(thread_get_running_tid)(), 512 p->a1, p->recursion_count, p->owner }; 513 VG_(maybe_record_error)(VG_(get_running_tid)(), 514 MutexErr, 515 VG_(get_IP)(VG_(get_running_tid)()), 516 "Mutex still locked at thread exit", 517 &MEI); 518 p->owner = VG_INVALID_THREADID; 519 } 520 } 521 522 ULong DRD_(get_mutex_lock_count)(void) 523 { 524 return s_mutex_lock_count; 525 } 526 527 ULong DRD_(get_mutex_segment_creation_count)(void) 528 { 529 return s_mutex_segment_creation_count; 530 } 531