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_clientobj.h" 27 #include "drd_cond.h" 28 #include "drd_error.h" 29 #include "drd_mutex.h" 30 #include "pub_tool_errormgr.h" /* VG_(maybe_record_error)() */ 31 #include "pub_tool_libcassert.h" /* tl_assert() */ 32 #include "pub_tool_libcbase.h" /* VG_(memcmp)() */ 33 #include "pub_tool_libcprint.h" /* VG_(printf)() */ 34 #include "pub_tool_machine.h" /* VG_(get_IP)() */ 35 #include "pub_tool_threadstate.h" /* VG_(get_running_tid)() */ 36 37 38 /* Local functions. */ 39 40 static void DRD_(cond_cleanup)(struct cond_info* p); 41 42 43 /* Local variables. */ 44 45 static Bool DRD_(s_report_signal_unlocked) = True; 46 static Bool DRD_(s_trace_cond); 47 48 49 /* Function definitions. */ 50 51 void DRD_(cond_set_report_signal_unlocked)(const Bool r) 52 { 53 DRD_(s_report_signal_unlocked) = r; 54 } 55 56 void DRD_(cond_set_trace)(const Bool trace_cond) 57 { 58 DRD_(s_trace_cond) = trace_cond; 59 } 60 61 static 62 void DRD_(cond_initialize)(struct cond_info* const p, const Addr cond) 63 { 64 tl_assert(cond != 0); 65 tl_assert(p->a1 == cond); 66 tl_assert(p->type == ClientCondvar); 67 68 p->cleanup = (void(*)(DrdClientobj*))(DRD_(cond_cleanup)); 69 p->delete_thread = 0; 70 p->waiter_count = 0; 71 p->mutex = 0; 72 } 73 74 /** 75 * Free the memory that was allocated by cond_initialize(). Called by 76 * DRD_(clientobj_remove)(). 77 */ 78 static void DRD_(cond_cleanup)(struct cond_info* p) 79 { 80 tl_assert(p); 81 if (p->mutex) 82 { 83 struct mutex_info* q; 84 q = &(DRD_(clientobj_get)(p->mutex, ClientMutex)->mutex); 85 { 86 CondDestrErrInfo cde = { 87 DRD_(thread_get_running_tid)(), 88 p->a1, 89 q ? q->a1 : 0, 90 q ? q->owner : DRD_INVALID_THREADID 91 }; 92 VG_(maybe_record_error)(VG_(get_running_tid)(), 93 CondDestrErr, 94 VG_(get_IP)(VG_(get_running_tid)()), 95 "Destroying condition variable that is being" 96 " waited upon", 97 &cde); 98 } 99 } 100 } 101 102 /** 103 * Report that the synchronization object at address 'addr' is of the 104 * wrong type. 105 */ 106 static void wrong_type(const Addr addr) 107 { 108 GenericErrInfo gei = { 109 .tid = DRD_(thread_get_running_tid)(), 110 .addr = addr, 111 }; 112 VG_(maybe_record_error)(VG_(get_running_tid)(), 113 GenericErr, 114 VG_(get_IP)(VG_(get_running_tid)()), 115 "wrong type of synchronization object", 116 &gei); 117 } 118 119 static struct cond_info* cond_get_or_allocate(const Addr cond) 120 { 121 struct cond_info *p; 122 123 tl_assert(offsetof(DrdClientobj, cond) == 0); 124 p = &(DRD_(clientobj_get)(cond, ClientCondvar)->cond); 125 if (p) 126 return p; 127 128 if (DRD_(clientobj_present)(cond, cond + 1)) 129 { 130 wrong_type(cond); 131 return 0; 132 } 133 134 p = &(DRD_(clientobj_add)(cond, ClientCondvar)->cond); 135 DRD_(cond_initialize)(p, cond); 136 return p; 137 } 138 139 struct cond_info* DRD_(cond_get)(const Addr cond) 140 { 141 tl_assert(offsetof(DrdClientobj, cond) == 0); 142 return &(DRD_(clientobj_get)(cond, ClientCondvar)->cond); 143 } 144 145 /** Called before pthread_cond_init(). */ 146 void DRD_(cond_pre_init)(const Addr cond) 147 { 148 struct cond_info* p; 149 150 if (DRD_(s_trace_cond)) 151 DRD_(trace_msg)("[%d] cond_init cond 0x%lx", 152 DRD_(thread_get_running_tid)(), cond); 153 154 p = DRD_(cond_get)(cond); 155 156 if (p) 157 { 158 CondErrInfo cei = { .tid = DRD_(thread_get_running_tid)(), .cond = cond }; 159 VG_(maybe_record_error)(VG_(get_running_tid)(), 160 CondErr, 161 VG_(get_IP)(VG_(get_running_tid)()), 162 "initialized twice", 163 &cei); 164 } 165 166 p = cond_get_or_allocate(cond); 167 } 168 169 /** Called after pthread_cond_destroy(). */ 170 void DRD_(cond_post_destroy)(const Addr cond) 171 { 172 struct cond_info* p; 173 174 if (DRD_(s_trace_cond)) 175 DRD_(trace_msg)("[%d] cond_destroy cond 0x%lx", 176 DRD_(thread_get_running_tid)(), cond); 177 178 p = DRD_(cond_get)(cond); 179 if (p == 0) 180 { 181 CondErrInfo cei = { .tid = DRD_(thread_get_running_tid)(), .cond = cond }; 182 VG_(maybe_record_error)(VG_(get_running_tid)(), 183 CondErr, 184 VG_(get_IP)(VG_(get_running_tid)()), 185 "not a condition variable", 186 &cei); 187 return; 188 } 189 190 if (p->waiter_count != 0) 191 { 192 CondErrInfo cei = { .tid = DRD_(thread_get_running_tid)(), .cond = cond }; 193 VG_(maybe_record_error)(VG_(get_running_tid)(), 194 CondErr, 195 VG_(get_IP)(VG_(get_running_tid)()), 196 "destruction of condition variable being waited" 197 " upon", 198 &cei); 199 } 200 201 DRD_(clientobj_remove)(p->a1, ClientCondvar); 202 } 203 204 /** 205 * Called before pthread_cond_wait(). Note: before this function is called, 206 * mutex_unlock() has already been called from drd_clientreq.c. 207 */ 208 void DRD_(cond_pre_wait)(const Addr cond, const Addr mutex) 209 { 210 struct cond_info* p; 211 struct mutex_info* q; 212 213 if (DRD_(s_trace_cond)) 214 DRD_(trace_msg)("[%d] cond_pre_wait cond 0x%lx", 215 DRD_(thread_get_running_tid)(), cond); 216 217 p = cond_get_or_allocate(cond); 218 if (!p) 219 { 220 CondErrInfo cei = { .tid = DRD_(thread_get_running_tid)(), .cond = cond }; 221 VG_(maybe_record_error)(VG_(get_running_tid)(), 222 CondErr, 223 VG_(get_IP)(VG_(get_running_tid)()), 224 "not a condition variable", 225 &cei); 226 return; 227 } 228 229 if (p->waiter_count == 0) 230 { 231 p->mutex = mutex; 232 } 233 else if (p->mutex != mutex) 234 { 235 CondWaitErrInfo cwei 236 = { .tid = DRD_(thread_get_running_tid)(), 237 .cond = cond, .mutex1 = p->mutex, .mutex2 = mutex }; 238 VG_(maybe_record_error)(VG_(get_running_tid)(), 239 CondWaitErr, 240 VG_(get_IP)(VG_(get_running_tid)()), 241 "Inconsistent association of condition variable" 242 " and mutex", 243 &cwei); 244 } 245 tl_assert(p->mutex); 246 q = DRD_(mutex_get)(p->mutex); 247 if (q 248 && q->owner == DRD_(thread_get_running_tid)() && q->recursion_count > 0) 249 { 250 const ThreadId vg_tid = VG_(get_running_tid)(); 251 MutexErrInfo MEI = { DRD_(thread_get_running_tid)(), 252 q->a1, q->recursion_count, q->owner }; 253 VG_(maybe_record_error)(vg_tid, 254 MutexErr, 255 VG_(get_IP)(vg_tid), 256 "Mutex locked recursively", 257 &MEI); 258 } 259 else if (q == 0) 260 { 261 DRD_(not_a_mutex)(p->mutex); 262 } 263 264 ++p->waiter_count; 265 } 266 267 /** 268 * Called after pthread_cond_wait(). 269 */ 270 void DRD_(cond_post_wait)(const Addr cond) 271 { 272 struct cond_info* p; 273 274 if (DRD_(s_trace_cond)) 275 DRD_(trace_msg)("[%d] cond_post_wait cond 0x%lx", 276 DRD_(thread_get_running_tid)(), cond); 277 278 p = DRD_(cond_get)(cond); 279 if (!p) 280 { 281 CondDestrErrInfo cde = { 282 DRD_(thread_get_running_tid)(), cond, 0, DRD_INVALID_THREADID 283 }; 284 VG_(maybe_record_error)(VG_(get_running_tid)(), 285 CondDestrErr, 286 VG_(get_IP)(VG_(get_running_tid)()), 287 "condition variable has been destroyed while" 288 " being waited upon", 289 &cde); 290 return; 291 } 292 293 if (p->waiter_count > 0) 294 { 295 --p->waiter_count; 296 if (p->waiter_count == 0) 297 { 298 p->mutex = 0; 299 } 300 } 301 } 302 303 static void cond_signal(const DrdThreadId tid, struct cond_info* const cond_p) 304 { 305 const ThreadId vg_tid = VG_(get_running_tid)(); 306 const DrdThreadId drd_tid = DRD_(VgThreadIdToDrdThreadId)(vg_tid); 307 308 tl_assert(cond_p); 309 310 if (cond_p->waiter_count > 0) 311 { 312 if (DRD_(s_report_signal_unlocked) 313 && ! DRD_(mutex_is_locked_by)(cond_p->mutex, drd_tid)) 314 { 315 /* 316 * A signal is sent while the associated mutex has not been locked. 317 * This can indicate but is not necessarily a race condition. 318 */ 319 CondRaceErrInfo cei = { .tid = DRD_(thread_get_running_tid)(), 320 .cond = cond_p->a1, 321 .mutex = cond_p->mutex, 322 }; 323 VG_(maybe_record_error)(vg_tid, 324 CondRaceErr, 325 VG_(get_IP)(vg_tid), 326 "CondErr", 327 &cei); 328 } 329 } 330 else 331 { 332 /* 333 * No other thread is waiting for the signal, hence the signal will 334 * be lost. This is normal in a POSIX threads application. 335 */ 336 } 337 } 338 339 static void not_initialized(Addr const cond) 340 { 341 CondErrInfo cei = { .tid = DRD_(thread_get_running_tid)(), .cond = cond }; 342 VG_(maybe_record_error)(VG_(get_running_tid)(), 343 CondErr, 344 VG_(get_IP)(VG_(get_running_tid)()), 345 "condition variable has not been initialized", 346 &cei); 347 } 348 349 /** Called before pthread_cond_signal(). */ 350 void DRD_(cond_pre_signal)(Addr const cond) 351 { 352 struct cond_info* p; 353 354 p = DRD_(cond_get)(cond); 355 if (DRD_(s_trace_cond)) 356 DRD_(trace_msg)("[%d] cond_signal cond 0x%lx", 357 DRD_(thread_get_running_tid)(), cond); 358 359 tl_assert(DRD_(pthread_cond_initializer)); 360 if (!p && VG_(memcmp)((void*)cond, (void*)DRD_(pthread_cond_initializer), 361 DRD_(pthread_cond_initializer_size)) != 0) 362 { 363 not_initialized(cond); 364 return; 365 } 366 367 if (!p) 368 p = cond_get_or_allocate(cond); 369 370 cond_signal(DRD_(thread_get_running_tid)(), p); 371 } 372 373 /** Called before pthread_cond_broadcast(). */ 374 void DRD_(cond_pre_broadcast)(Addr const cond) 375 { 376 struct cond_info* p; 377 378 if (DRD_(s_trace_cond)) 379 DRD_(trace_msg)("[%d] cond_broadcast cond 0x%lx", 380 DRD_(thread_get_running_tid)(), cond); 381 382 p = DRD_(cond_get)(cond); 383 tl_assert(DRD_(pthread_cond_initializer)); 384 if (!p && VG_(memcmp)((void*)cond, (void*)DRD_(pthread_cond_initializer), 385 DRD_(pthread_cond_initializer_size)) != 0) 386 { 387 not_initialized(cond); 388 return; 389 } 390 391 if (!p) 392 p = cond_get_or_allocate(cond); 393 394 cond_signal(DRD_(thread_get_running_tid)(), p); 395 } 396