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