1 /* 2 This file is part of drd, a thread error detector. 3 4 Copyright (C) 2006-2013 Bart Van Assche <bvanassche (at) acm.org>. 5 6 This program is free software; you can redistribute it and/or 7 modify it under the terms of the GNU General Public License as 8 published by the Free Software Foundation; either version 2 of the 9 License, or (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software 18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 19 02111-1307, USA. 20 21 The GNU General Public License is contained in the file COPYING. 22 */ 23 24 25 #include "drd_clientobj.h" 26 #include "drd_error.h" 27 #include "drd_rwlock.h" 28 #include "pub_tool_vki.h" 29 #include "pub_tool_errormgr.h" // VG_(maybe_record_error)() 30 #include "pub_tool_libcassert.h" // tl_assert() 31 #include "pub_tool_libcprint.h" // VG_(message)() 32 #include "pub_tool_libcproc.h" // VG_(read_millisecond_timer)() 33 #include "pub_tool_machine.h" // VG_(get_IP)() 34 #include "pub_tool_mallocfree.h" // VG_(malloc)(), VG_(free)() 35 #include "pub_tool_threadstate.h" // VG_(get_running_tid)() 36 37 38 /* Local type definitions. */ 39 40 struct rwlock_thread_info 41 { 42 UWord tid; // DrdThreadId. 43 UInt reader_nesting_count; 44 UInt writer_nesting_count; 45 // Segment of last unlock call by this thread that unlocked a writer lock. 46 Segment* latest_wrlocked_segment; 47 // Segment of last unlock call by this thread that unlocked a reader lock. 48 Segment* latest_rdlocked_segment; 49 }; 50 51 52 /* Local functions. */ 53 54 static void rwlock_cleanup(struct rwlock_info* p); 55 static void rwlock_delete_thread(struct rwlock_info* const p, 56 const DrdThreadId tid); 57 58 59 /* Local variables. */ 60 61 static Bool DRD_(s_trace_rwlock); 62 static UInt DRD_(s_exclusive_threshold_ms); 63 static UInt DRD_(s_shared_threshold_ms); 64 static ULong DRD_(s_rwlock_segment_creation_count); 65 66 67 /* Function definitions. */ 68 69 void DRD_(rwlock_set_trace)(const Bool trace_rwlock) 70 { 71 tl_assert(trace_rwlock == False || trace_rwlock == True); 72 DRD_(s_trace_rwlock) = trace_rwlock; 73 } 74 75 void DRD_(rwlock_set_exclusive_threshold)(const UInt exclusive_threshold_ms) 76 { 77 DRD_(s_exclusive_threshold_ms) = exclusive_threshold_ms; 78 } 79 80 void DRD_(rwlock_set_shared_threshold)(const UInt shared_threshold_ms) 81 { 82 DRD_(s_shared_threshold_ms) = shared_threshold_ms; 83 } 84 85 static Bool DRD_(rwlock_is_rdlocked)(struct rwlock_info* p) 86 { 87 struct rwlock_thread_info* q; 88 89 VG_(OSetGen_ResetIter)(p->thread_info); 90 for ( ; (q = VG_(OSetGen_Next)(p->thread_info)) != 0; ) 91 { 92 return q->reader_nesting_count > 0; 93 } 94 return False; 95 } 96 97 static Bool DRD_(rwlock_is_wrlocked)(struct rwlock_info* p) 98 { 99 struct rwlock_thread_info* q; 100 101 VG_(OSetGen_ResetIter)(p->thread_info); 102 for ( ; (q = VG_(OSetGen_Next)(p->thread_info)) != 0; ) 103 { 104 return q->writer_nesting_count > 0; 105 } 106 return False; 107 } 108 109 static Bool DRD_(rwlock_is_locked)(struct rwlock_info* p) 110 { 111 return DRD_(rwlock_is_rdlocked)(p) || DRD_(rwlock_is_wrlocked)(p); 112 } 113 114 static Bool DRD_(rwlock_is_rdlocked_by)(struct rwlock_info* p, 115 const DrdThreadId tid) 116 { 117 const UWord uword_tid = tid; 118 struct rwlock_thread_info* q; 119 120 q = VG_(OSetGen_Lookup)(p->thread_info, &uword_tid); 121 return q && q->reader_nesting_count > 0; 122 } 123 124 static Bool DRD_(rwlock_is_wrlocked_by)(struct rwlock_info* p, 125 const DrdThreadId tid) 126 { 127 const UWord uword_tid = tid; 128 struct rwlock_thread_info* q; 129 130 q = VG_(OSetGen_Lookup)(p->thread_info, &uword_tid); 131 return q && q->writer_nesting_count > 0; 132 } 133 134 static Bool DRD_(rwlock_is_locked_by)(struct rwlock_info* p, 135 const DrdThreadId tid) 136 { 137 return (DRD_(rwlock_is_rdlocked_by)(p, tid) 138 || DRD_(rwlock_is_wrlocked_by)(p, tid)); 139 } 140 141 /** Either look up or insert a node corresponding to DRD thread id 'tid'. */ 142 static 143 struct rwlock_thread_info* 144 DRD_(lookup_or_insert_node)(OSet* oset, const UWord tid) 145 { 146 struct rwlock_thread_info* q; 147 148 q = VG_(OSetGen_Lookup)(oset, &tid); 149 if (q == 0) 150 { 151 q = VG_(OSetGen_AllocNode)(oset, sizeof(*q)); 152 q->tid = tid; 153 q->reader_nesting_count = 0; 154 q->writer_nesting_count = 0; 155 q->latest_wrlocked_segment = 0; 156 q->latest_rdlocked_segment = 0; 157 VG_(OSetGen_Insert)(oset, q); 158 } 159 tl_assert(q); 160 return q; 161 } 162 163 /** 164 * Combine the vector clock corresponding to the last unlock operation of 165 * reader-writer lock p into the vector clock of thread 'tid'. 166 */ 167 static void DRD_(rwlock_combine_other_vc)(struct rwlock_info* const p, 168 const DrdThreadId tid, 169 const Bool readers_too) 170 { 171 struct rwlock_thread_info* q; 172 VectorClock old_vc; 173 174 DRD_(vc_copy)(&old_vc, DRD_(thread_get_vc)(tid)); 175 VG_(OSetGen_ResetIter)(p->thread_info); 176 for ( ; (q = VG_(OSetGen_Next)(p->thread_info)) != 0; ) { 177 if (q->tid != tid) { 178 if (q->latest_wrlocked_segment) 179 DRD_(vc_combine)(DRD_(thread_get_vc)(tid), 180 &q->latest_wrlocked_segment->vc); 181 if (readers_too && q->latest_rdlocked_segment) 182 DRD_(vc_combine)(DRD_(thread_get_vc)(tid), 183 &q->latest_rdlocked_segment->vc); 184 } 185 } 186 DRD_(thread_update_conflict_set)(tid, &old_vc); 187 DRD_(vc_cleanup)(&old_vc); 188 } 189 190 /** 191 * Compare the type of the rwlock specified at initialization time with 192 * the type passed as an argument, and complain if these two types do not 193 * match. 194 */ 195 static Bool drd_rwlock_check_type(struct rwlock_info* const p, 196 const RwLockT rwlock_type) 197 { 198 tl_assert(p); 199 /* The code below has to be updated if additional rwlock types are added. */ 200 tl_assert(rwlock_type == pthread_rwlock || rwlock_type == user_rwlock); 201 tl_assert(p->rwlock_type == pthread_rwlock || p->rwlock_type == user_rwlock); 202 203 if (p->rwlock_type == rwlock_type) 204 return True; 205 206 { 207 RwlockErrInfo REI = { DRD_(thread_get_running_tid)(), p->a1 }; 208 VG_(maybe_record_error) 209 (VG_(get_running_tid)(), 210 RwlockErr, 211 VG_(get_IP)(VG_(get_running_tid)()), 212 rwlock_type == pthread_rwlock 213 ? "Attempt to use a user-defined rwlock as a POSIX rwlock" 214 : "Attempt to use a POSIX rwlock as a user-defined rwlock", 215 &REI); 216 } 217 return False; 218 } 219 220 /** Initialize the rwlock_info data structure *p. */ 221 static 222 void DRD_(rwlock_initialize)(struct rwlock_info* const p, const Addr rwlock, 223 const RwLockT rwlock_type) 224 { 225 tl_assert(rwlock != 0); 226 tl_assert(p->a1 == rwlock); 227 tl_assert(p->type == ClientRwlock); 228 229 p->cleanup = (void(*)(DrdClientobj*))rwlock_cleanup; 230 p->delete_thread 231 = (void(*)(DrdClientobj*, DrdThreadId))rwlock_delete_thread; 232 p->rwlock_type = rwlock_type; 233 p->thread_info = VG_(OSetGen_Create)( 234 0, 0, VG_(malloc), "drd.rwlock.ri.1", VG_(free)); 235 p->acquiry_time_ms = 0; 236 p->acquired_at = 0; 237 } 238 239 /** Deallocate the memory that was allocated by rwlock_initialize(). */ 240 static void rwlock_cleanup(struct rwlock_info* p) 241 { 242 struct rwlock_thread_info* q; 243 244 tl_assert(p); 245 246 if (DRD_(s_trace_rwlock)) 247 DRD_(trace_msg)("[%d] rwlock_destroy 0x%lx", 248 DRD_(thread_get_running_tid)(), p->a1); 249 250 if (DRD_(rwlock_is_locked)(p)) 251 { 252 RwlockErrInfo REI = { DRD_(thread_get_running_tid)(), p->a1 }; 253 VG_(maybe_record_error)(VG_(get_running_tid)(), 254 RwlockErr, 255 VG_(get_IP)(VG_(get_running_tid)()), 256 "Destroying locked rwlock", 257 &REI); 258 } 259 260 VG_(OSetGen_ResetIter)(p->thread_info); 261 for ( ; (q = VG_(OSetGen_Next)(p->thread_info)) != 0; ) 262 { 263 DRD_(sg_put)(q->latest_wrlocked_segment); 264 DRD_(sg_put)(q->latest_rdlocked_segment); 265 } 266 267 VG_(OSetGen_Destroy)(p->thread_info); 268 } 269 270 static 271 struct rwlock_info* 272 DRD_(rwlock_get_or_allocate)(const Addr rwlock, const RwLockT rwlock_type) 273 { 274 struct rwlock_info* p; 275 276 tl_assert(offsetof(DrdClientobj, rwlock) == 0); 277 p = &(DRD_(clientobj_get)(rwlock, ClientRwlock)->rwlock); 278 if (p) 279 { 280 drd_rwlock_check_type(p, rwlock_type); 281 return p; 282 } 283 284 if (DRD_(clientobj_present)(rwlock, rwlock + 1)) 285 { 286 GenericErrInfo GEI = { 287 .tid = DRD_(thread_get_running_tid)(), 288 .addr = rwlock, 289 }; 290 VG_(maybe_record_error)(VG_(get_running_tid)(), 291 GenericErr, 292 VG_(get_IP)(VG_(get_running_tid)()), 293 "Not a reader-writer lock", 294 &GEI); 295 return 0; 296 } 297 298 p = &(DRD_(clientobj_add)(rwlock, ClientRwlock)->rwlock); 299 DRD_(rwlock_initialize)(p, rwlock, rwlock_type); 300 return p; 301 } 302 303 static struct rwlock_info* DRD_(rwlock_get)(const Addr rwlock) 304 { 305 tl_assert(offsetof(DrdClientobj, rwlock) == 0); 306 return &(DRD_(clientobj_get)(rwlock, ClientRwlock)->rwlock); 307 } 308 309 /** Called before pthread_rwlock_init(). */ 310 struct rwlock_info* DRD_(rwlock_pre_init)(const Addr rwlock, 311 const RwLockT rwlock_type) 312 { 313 struct rwlock_info* p; 314 315 if (DRD_(s_trace_rwlock)) 316 DRD_(trace_msg)("[%d] rwlock_init 0x%lx", 317 DRD_(thread_get_running_tid)(), rwlock); 318 319 p = DRD_(rwlock_get)(rwlock); 320 321 if (p) 322 drd_rwlock_check_type(p, rwlock_type); 323 324 if (p) 325 { 326 const ThreadId vg_tid = VG_(get_running_tid)(); 327 RwlockErrInfo REI = { DRD_(thread_get_running_tid)(), p->a1 }; 328 VG_(maybe_record_error)(vg_tid, 329 RwlockErr, 330 VG_(get_IP)(vg_tid), 331 "Reader-writer lock reinitialization", 332 &REI); 333 return p; 334 } 335 336 p = DRD_(rwlock_get_or_allocate)(rwlock, rwlock_type); 337 338 return p; 339 } 340 341 /** Called after pthread_rwlock_destroy(). */ 342 void DRD_(rwlock_post_destroy)(const Addr rwlock, const RwLockT rwlock_type) 343 { 344 struct rwlock_info* p; 345 346 p = DRD_(rwlock_get)(rwlock); 347 if (p == 0) 348 { 349 GenericErrInfo GEI = { 350 .tid = DRD_(thread_get_running_tid)(), 351 .addr = rwlock, 352 }; 353 VG_(maybe_record_error)(VG_(get_running_tid)(), 354 GenericErr, 355 VG_(get_IP)(VG_(get_running_tid)()), 356 "Not a reader-writer lock", 357 &GEI); 358 return; 359 } 360 361 drd_rwlock_check_type(p, rwlock_type); 362 363 DRD_(clientobj_remove)(rwlock, ClientRwlock); 364 } 365 366 /** 367 * Called before pthread_rwlock_rdlock() is invoked. If a data structure for 368 * the client-side object was not yet created, do this now. Also check whether 369 * an attempt is made to lock recursively a synchronization object that must 370 * not be locked recursively. 371 */ 372 void DRD_(rwlock_pre_rdlock)(const Addr rwlock, const RwLockT rwlock_type) 373 { 374 struct rwlock_info* p; 375 376 if (DRD_(s_trace_rwlock)) 377 DRD_(trace_msg)("[%d] pre_rwlock_rdlock 0x%lx", 378 DRD_(thread_get_running_tid)(), rwlock); 379 380 p = DRD_(rwlock_get_or_allocate)(rwlock, rwlock_type); 381 tl_assert(p); 382 383 if (DRD_(rwlock_is_wrlocked_by)(p, DRD_(thread_get_running_tid)())) { 384 RwlockErrInfo REI = { DRD_(thread_get_running_tid)(), p->a1 }; 385 VG_(maybe_record_error)(VG_(get_running_tid)(), 386 RwlockErr, 387 VG_(get_IP)(VG_(get_running_tid)()), 388 "Already locked for writing by calling thread", 389 &REI); 390 } 391 } 392 393 /** 394 * Update rwlock_info state when locking the pthread_rwlock_t mutex. 395 * Note: this function must be called after pthread_rwlock_rdlock() has been 396 * called, or a race condition is triggered ! 397 */ 398 void DRD_(rwlock_post_rdlock)(const Addr rwlock, const RwLockT rwlock_type, 399 const Bool took_lock) 400 { 401 const DrdThreadId drd_tid = DRD_(thread_get_running_tid)(); 402 struct rwlock_info* p; 403 struct rwlock_thread_info* q; 404 405 if (DRD_(s_trace_rwlock)) 406 DRD_(trace_msg)("[%d] post_rwlock_rdlock 0x%lx", drd_tid, rwlock); 407 408 p = DRD_(rwlock_get)(rwlock); 409 410 if (! p || ! took_lock) 411 return; 412 413 tl_assert(! DRD_(rwlock_is_wrlocked)(p)); 414 415 q = DRD_(lookup_or_insert_node)(p->thread_info, drd_tid); 416 if (++q->reader_nesting_count == 1) 417 { 418 DRD_(thread_new_segment)(drd_tid); 419 DRD_(s_rwlock_segment_creation_count)++; 420 DRD_(rwlock_combine_other_vc)(p, drd_tid, False); 421 422 p->acquiry_time_ms = VG_(read_millisecond_timer)(); 423 p->acquired_at = VG_(record_ExeContext)(VG_(get_running_tid)(), 0); 424 } 425 } 426 427 /** 428 * Called before pthread_rwlock_wrlock() is invoked. If a data structure for 429 * the client-side object was not yet created, do this now. Also check whether 430 * an attempt is made to lock recursively a synchronization object that must 431 * not be locked recursively. 432 */ 433 void DRD_(rwlock_pre_wrlock)(const Addr rwlock, const RwLockT rwlock_type) 434 { 435 struct rwlock_info* p; 436 437 p = DRD_(rwlock_get)(rwlock); 438 439 if (DRD_(s_trace_rwlock)) 440 DRD_(trace_msg)("[%d] pre_rwlock_wrlock 0x%lx", 441 DRD_(thread_get_running_tid)(), rwlock); 442 443 if (p == 0) 444 p = DRD_(rwlock_get_or_allocate)(rwlock, rwlock_type); 445 446 tl_assert(p); 447 448 if (DRD_(rwlock_is_wrlocked_by)(p, DRD_(thread_get_running_tid)())) 449 { 450 RwlockErrInfo REI = { DRD_(thread_get_running_tid)(), p->a1 }; 451 VG_(maybe_record_error)(VG_(get_running_tid)(), 452 RwlockErr, 453 VG_(get_IP)(VG_(get_running_tid)()), 454 "Recursive writer locking not allowed", 455 &REI); 456 } 457 } 458 459 /** 460 * Update rwlock_info state when locking the pthread_rwlock_t rwlock. 461 * Note: this function must be called after pthread_rwlock_wrlock() has 462 * finished, or a race condition is triggered ! 463 */ 464 void DRD_(rwlock_post_wrlock)(const Addr rwlock, const RwLockT rwlock_type, 465 const Bool took_lock) 466 { 467 const DrdThreadId drd_tid = DRD_(thread_get_running_tid)(); 468 struct rwlock_info* p; 469 struct rwlock_thread_info* q; 470 471 p = DRD_(rwlock_get)(rwlock); 472 473 if (DRD_(s_trace_rwlock)) 474 DRD_(trace_msg)("[%d] post_rwlock_wrlock 0x%lx", drd_tid, rwlock); 475 476 if (! p || ! took_lock) 477 return; 478 479 q = DRD_(lookup_or_insert_node)(p->thread_info, 480 DRD_(thread_get_running_tid)()); 481 tl_assert(q->writer_nesting_count == 0); 482 q->writer_nesting_count++; 483 tl_assert(q->writer_nesting_count == 1); 484 DRD_(thread_new_segment)(drd_tid); 485 DRD_(s_rwlock_segment_creation_count)++; 486 DRD_(rwlock_combine_other_vc)(p, drd_tid, True); 487 p->acquiry_time_ms = VG_(read_millisecond_timer)(); 488 p->acquired_at = VG_(record_ExeContext)(VG_(get_running_tid)(), 0); 489 } 490 491 /** 492 * Update rwlock_info state when unlocking the pthread_rwlock_t rwlock. 493 * 494 * @param rwlock Pointer to pthread_rwlock_t data structure in the client space. 495 * 496 * @return New value of the rwlock recursion count. 497 * 498 * @note This function must be called before pthread_rwlock_unlock() is called, 499 * or a race condition is triggered ! 500 */ 501 void DRD_(rwlock_pre_unlock)(const Addr rwlock, const RwLockT rwlock_type) 502 { 503 const DrdThreadId drd_tid = DRD_(thread_get_running_tid)(); 504 const ThreadId vg_tid = VG_(get_running_tid)(); 505 struct rwlock_info* p; 506 struct rwlock_thread_info* q; 507 508 if (DRD_(s_trace_rwlock)) 509 DRD_(trace_msg)("[%d] rwlock_unlock 0x%lx", drd_tid, rwlock); 510 511 p = DRD_(rwlock_get)(rwlock); 512 if (p == 0) 513 { 514 GenericErrInfo GEI = { 515 .tid = DRD_(thread_get_running_tid)(), 516 .addr = rwlock, 517 }; 518 VG_(maybe_record_error)(VG_(get_running_tid)(), 519 GenericErr, 520 VG_(get_IP)(VG_(get_running_tid)()), 521 "Not a reader-writer lock", 522 &GEI); 523 return; 524 } 525 526 drd_rwlock_check_type(p, rwlock_type); 527 528 if (! DRD_(rwlock_is_locked_by)(p, drd_tid)) 529 { 530 RwlockErrInfo REI = { DRD_(thread_get_running_tid)(), p->a1 }; 531 VG_(maybe_record_error)(vg_tid, 532 RwlockErr, 533 VG_(get_IP)(vg_tid), 534 "Reader-writer lock not locked by calling thread", 535 &REI); 536 return; 537 } 538 q = DRD_(lookup_or_insert_node)(p->thread_info, drd_tid); 539 tl_assert(q); 540 if (q->reader_nesting_count > 0) 541 { 542 q->reader_nesting_count--; 543 if (q->reader_nesting_count == 0 && DRD_(s_shared_threshold_ms) > 0) 544 { 545 Long held = VG_(read_millisecond_timer)() - p->acquiry_time_ms; 546 if (held > DRD_(s_shared_threshold_ms)) 547 { 548 HoldtimeErrInfo HEI 549 = { DRD_(thread_get_running_tid)(), 550 rwlock, p->acquired_at, held, DRD_(s_shared_threshold_ms) }; 551 VG_(maybe_record_error)(vg_tid, 552 HoldtimeErr, 553 VG_(get_IP)(vg_tid), 554 "rwlock", 555 &HEI); 556 } 557 } 558 if (q->reader_nesting_count == 0 && q->writer_nesting_count == 0) 559 { 560 /* 561 * This pthread_rwlock_unlock() call really unlocks the rwlock. Save 562 * the current vector clock of the thread such that it is available 563 * when this rwlock is locked again. 564 */ 565 DRD_(thread_get_latest_segment)(&q->latest_rdlocked_segment, drd_tid); 566 DRD_(thread_new_segment)(drd_tid); 567 DRD_(s_rwlock_segment_creation_count)++; 568 } 569 } 570 else if (q->writer_nesting_count > 0) 571 { 572 q->writer_nesting_count--; 573 if (q->writer_nesting_count == 0 && DRD_(s_exclusive_threshold_ms) > 0) 574 { 575 Long held = VG_(read_millisecond_timer)() - p->acquiry_time_ms; 576 if (held > DRD_(s_exclusive_threshold_ms)) 577 { 578 HoldtimeErrInfo HEI 579 = { DRD_(thread_get_running_tid)(), 580 rwlock, p->acquired_at, held, 581 DRD_(s_exclusive_threshold_ms) }; 582 VG_(maybe_record_error)(vg_tid, 583 HoldtimeErr, 584 VG_(get_IP)(vg_tid), 585 "rwlock", 586 &HEI); 587 } 588 } 589 if (q->reader_nesting_count == 0 && q->writer_nesting_count == 0) 590 { 591 /* 592 * This pthread_rwlock_unlock() call really unlocks the rwlock. Save 593 * the current vector clock of the thread such that it is available 594 * when this rwlock is locked again. 595 */ 596 DRD_(thread_get_latest_segment)(&q->latest_wrlocked_segment, drd_tid); 597 DRD_(thread_new_segment)(drd_tid); 598 DRD_(s_rwlock_segment_creation_count)++; 599 } 600 } 601 else 602 { 603 tl_assert(False); 604 } 605 } 606 607 /** Called when thread tid stops to exist. */ 608 static void rwlock_delete_thread(struct rwlock_info* const p, 609 const DrdThreadId tid) 610 { 611 struct rwlock_thread_info* q; 612 613 if (DRD_(rwlock_is_locked_by)(p, tid)) 614 { 615 RwlockErrInfo REI = { DRD_(thread_get_running_tid)(), p->a1 }; 616 VG_(maybe_record_error)(VG_(get_running_tid)(), 617 RwlockErr, 618 VG_(get_IP)(VG_(get_running_tid)()), 619 "Reader-writer lock still locked at thread exit", 620 &REI); 621 q = DRD_(lookup_or_insert_node)(p->thread_info, tid); 622 q->reader_nesting_count = 0; 623 q->writer_nesting_count = 0; 624 } 625 } 626 627 ULong DRD_(get_rwlock_segment_creation_count)(void) 628 { 629 return DRD_(s_rwlock_segment_creation_count); 630 } 631