• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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 
DRD_(mutex_set_trace)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 
DRD_(mutex_set_lock_threshold)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
DRD_(mutex_initialize)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(). */
mutex_cleanup(struct mutex_info * p)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. */
DRD_(not_a_mutex)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  */
wrong_mutex_type(const Addr mutex)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*
DRD_(mutex_get_or_allocate)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 
DRD_(mutex_get)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*
DRD_(mutex_init)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(). */
DRD_(mutex_post_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  */
DRD_(mutex_pre_lock)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  */
DRD_(mutex_post_lock)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  */
DRD_(mutex_unlock)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 
DRD_(spinlock_init_or_unlock)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 
DRD_(mutex_get_typename)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 
DRD_(mutex_type_name)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. */
mutex_is_locked(struct mutex_info * const p)478 static Bool mutex_is_locked(struct mutex_info* const p)
479 {
480    tl_assert(p);
481    return (p->recursion_count > 0);
482 }
483 
DRD_(mutex_is_locked_by)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 
DRD_(mutex_get_recursion_count)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  */
mutex_delete_thread(struct mutex_info * p,const DrdThreadId tid)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 
DRD_(get_mutex_lock_count)522 ULong DRD_(get_mutex_lock_count)(void)
523 {
524    return s_mutex_lock_count;
525 }
526 
DRD_(get_mutex_segment_creation_count)527 ULong DRD_(get_mutex_segment_creation_count)(void)
528 {
529    return s_mutex_segment_creation_count;
530 }
531