drd_semaphore.c revision 86562bd89ac23ce795d19c71fabcb9d1c8f956d3
1/*
2  This file is part of drd, a thread error detector.
3
4  Copyright (C) 2006-2009 Bart Van Assche <bart.vanassche@gmail.com>.
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_semaphore.h"
28#include "drd_suppression.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_(printf)()
32#include "pub_tool_machine.h"     // VG_(get_IP)()
33#include "pub_tool_mallocfree.h"  // VG_(malloc), VG_(free)
34#include "pub_tool_threadstate.h" // VG_(get_running_tid)()
35
36
37/* Local functions. */
38
39static void DRD_(semaphore_cleanup)(struct semaphore_info* p);
40
41
42/* Local variables. */
43
44static Bool DRD_(s_trace_semaphore);
45static ULong DRD_(s_semaphore_segment_creation_count);
46
47
48/* Function definitions. */
49
50/** Push a segment at the end of the queue 'p->last_sem_post_seg'. */
51static void DRD_(segment_push)(struct semaphore_info* p, Segment* sg)
52{
53  Word n;
54
55  tl_assert(sg);
56  n = VG_(addToXA)(p->last_sem_post_seg, &sg);
57#if 0
58  VG_(message)(Vg_UserMsg, "0x%lx push: added at position %ld/%ld",
59               p->a1, n, VG_(sizeXA)(p->last_sem_post_seg));
60#endif
61  tl_assert(*(Segment**)VG_(indexXA)(p->last_sem_post_seg, n) == sg);
62}
63
64/** Pop a segment from the beginning of the queue 'p->last_sem_post_seg'. */
65static Segment* DRD_(segment_pop)(struct semaphore_info* p)
66{
67  Word sz;
68  Segment* sg;
69
70  sz = VG_(sizeXA)(p->last_sem_post_seg);
71#if 0
72  VG_(message)(Vg_UserMsg, "0x%lx pop:  removed from position %ld/%ld",
73               p->a1, sz - 1, sz);
74#endif
75  sg = 0;
76  if (sz > 0)
77  {
78    sg = *(Segment**)VG_(indexXA)(p->last_sem_post_seg, sz - 1);
79    tl_assert(sg);
80    VG_(dropTailXA)(p->last_sem_post_seg, 1);
81  }
82  return sg;
83}
84
85/** Enable or disable tracing of semaphore actions. */
86void DRD_(semaphore_set_trace)(const Bool trace_semaphore)
87{
88  DRD_(s_trace_semaphore) = trace_semaphore;
89}
90
91/**
92 * Initialize the memory 'p' points at as a semaphore_info structure for the
93 * client semaphore at client addres 'semaphore'.
94 */
95static
96void DRD_(semaphore_initialize)(struct semaphore_info* const p,
97                                const Addr semaphore)
98{
99  tl_assert(semaphore != 0);
100  tl_assert(p->a1 == semaphore);
101  tl_assert(p->type == ClientSemaphore);
102
103  p->cleanup           = (void(*)(DrdClientobj*))(DRD_(semaphore_cleanup));
104  p->waits_to_skip     = 0;
105  p->value             = 0;
106  p->waiters           = 0;
107  p->last_sem_post_tid = DRD_INVALID_THREADID;
108  p->last_sem_post_seg = VG_(newXA)(VG_(malloc), "drd.sg-stack",
109                                    VG_(free), sizeof(Segment*));
110}
111
112/**
113 * Free the memory that was allocated by semaphore_initialize(). Called by
114 * DRD_(clientobj_remove)().
115 */
116static void DRD_(semaphore_cleanup)(struct semaphore_info* p)
117{
118  Segment* sg;
119
120  if (p->waiters > 0)
121  {
122    SemaphoreErrInfo sei = { p->a1 };
123    VG_(maybe_record_error)(VG_(get_running_tid)(),
124                            SemaphoreErr,
125                            VG_(get_IP)(VG_(get_running_tid)()),
126                            "Destruction of semaphore that is being waited"
127                            " upon",
128                            &sei);
129  }
130  while ((sg = DRD_(segment_pop)(p)))
131    DRD_(sg_put)(sg);
132  VG_(deleteXA)(p->last_sem_post_seg);
133}
134
135/**
136 * Return a pointer to the structure with information about the specified
137 * client semaphore. Allocate a new structure if such a structure did not
138 * yet exist.
139 */
140static
141struct semaphore_info*
142DRD_(semaphore_get_or_allocate)(const Addr semaphore)
143{
144  struct semaphore_info *p;
145
146  tl_assert(offsetof(DrdClientobj, semaphore) == 0);
147  p = &(DRD_(clientobj_get)(semaphore, ClientSemaphore)->semaphore);
148  if (p == 0)
149  {
150    tl_assert(offsetof(DrdClientobj, semaphore) == 0);
151    p = &(DRD_(clientobj_add)(semaphore, ClientSemaphore)->semaphore);
152    DRD_(semaphore_initialize)(p, semaphore);
153  }
154  return p;
155}
156
157/**
158 * Return a pointer to the structure with information about the specified
159 * client semaphore, or null if no such structure was found.
160 */
161static struct semaphore_info* DRD_(semaphore_get)(const Addr semaphore)
162{
163  tl_assert(offsetof(DrdClientobj, semaphore) == 0);
164  return &(DRD_(clientobj_get)(semaphore, ClientSemaphore)->semaphore);
165}
166
167/** Called before sem_init(). */
168struct semaphore_info* DRD_(semaphore_init)(const Addr semaphore,
169                                            const Word pshared,
170                                            const UInt value)
171{
172  struct semaphore_info* p;
173  Segment* sg;
174
175  if (DRD_(s_trace_semaphore))
176  {
177    VG_(message)(Vg_UserMsg,
178                 "[%d/%d] semaphore_init      0x%lx value %u",
179                 VG_(get_running_tid)(),
180                 DRD_(thread_get_running_tid)(),
181                 semaphore,
182                 value);
183  }
184  p = DRD_(semaphore_get)(semaphore);
185  if (p)
186  {
187    const ThreadId vg_tid = VG_(get_running_tid)();
188    SemaphoreErrInfo SEI = { semaphore };
189    VG_(maybe_record_error)(vg_tid,
190                            SemaphoreErr,
191                            VG_(get_IP)(vg_tid),
192                            "Semaphore reinitialization",
193                            &SEI);
194    // Remove all segments from the segment stack.
195    while ((sg = DRD_(segment_pop)(p)))
196    {
197      DRD_(sg_put)(sg);
198    }
199  }
200  else
201  {
202    p = DRD_(semaphore_get_or_allocate)(semaphore);
203  }
204  tl_assert(p);
205  p->waits_to_skip = value;
206  p->value         = value;
207  return p;
208}
209
210/** Called after sem_destroy(). */
211void DRD_(semaphore_destroy)(const Addr semaphore)
212{
213  struct semaphore_info* p;
214
215  p = DRD_(semaphore_get)(semaphore);
216
217  if (DRD_(s_trace_semaphore))
218  {
219    VG_(message)(Vg_UserMsg,
220                 "[%d/%d] semaphore_destroy   0x%lx value %u",
221                 VG_(get_running_tid)(),
222                 DRD_(thread_get_running_tid)(),
223                 semaphore,
224                 p ? p->value : 0);
225  }
226
227  if (p == 0)
228  {
229    GenericErrInfo GEI;
230    VG_(maybe_record_error)(VG_(get_running_tid)(),
231                            GenericErr,
232                            VG_(get_IP)(VG_(get_running_tid)()),
233                            "Not a semaphore",
234                            &GEI);
235    return;
236  }
237
238  DRD_(clientobj_remove)(semaphore, ClientSemaphore);
239}
240
241/** Called before sem_wait(). */
242void DRD_(semaphore_pre_wait)(const Addr semaphore)
243{
244  struct semaphore_info* p;
245
246  p = DRD_(semaphore_get_or_allocate)(semaphore);
247  tl_assert(p);
248  tl_assert((int)p->waiters >= 0);
249  p->waiters++;
250  tl_assert(p->waiters > 0);
251}
252
253/**
254 * Called after sem_wait() finished.
255 * @note Do not rely on the value of 'waited' -- some glibc versions do
256 *       not set it correctly.
257 */
258void DRD_(semaphore_post_wait)(const DrdThreadId tid, const Addr semaphore,
259                               const Bool waited)
260{
261  struct semaphore_info* p;
262  Segment* sg;
263
264  p = DRD_(semaphore_get)(semaphore);
265  if (DRD_(s_trace_semaphore))
266  {
267    VG_(message)(Vg_UserMsg,
268                 "[%d/%d] semaphore_wait      0x%lx value %u -> %u",
269                 VG_(get_running_tid)(),
270                 DRD_(thread_get_running_tid)(),
271                 semaphore,
272                 p ? p->value : 0,
273                 p ? p->value - 1 : 0);
274  }
275  tl_assert(p);
276  tl_assert(p->waiters > 0);
277  p->waiters--;
278  tl_assert((int)p->waiters >= 0);
279  tl_assert((int)p->value >= 0);
280  if (p->value == 0)
281  {
282    SemaphoreErrInfo sei = { semaphore };
283    VG_(maybe_record_error)(VG_(get_running_tid)(),
284                            SemaphoreErr,
285                            VG_(get_IP)(VG_(get_running_tid)()),
286                            "Invalid semaphore",
287                            &sei);
288    return;
289  }
290  p->value--;
291  tl_assert((int)p->value >= 0);
292  if (p->waits_to_skip > 0)
293    p->waits_to_skip--;
294  else
295  {
296    sg = DRD_(segment_pop)(p);
297    tl_assert(sg);
298    if (sg)
299    {
300      if (p->last_sem_post_tid != tid
301          && p->last_sem_post_tid != DRD_INVALID_THREADID)
302      {
303        DRD_(thread_combine_vc2)(tid, &sg->vc);
304      }
305      DRD_(sg_put)(sg);
306      DRD_(thread_new_segment)(tid);
307      DRD_(s_semaphore_segment_creation_count)++;
308    }
309  }
310}
311
312/** Called before sem_post(). */
313void DRD_(semaphore_pre_post)(const DrdThreadId tid, const Addr semaphore)
314{
315  struct semaphore_info* p;
316  Segment* sg;
317
318  p = DRD_(semaphore_get_or_allocate)(semaphore);
319  p->value++;
320
321  if (DRD_(s_trace_semaphore))
322  {
323    VG_(message)(Vg_UserMsg,
324                 "[%d/%d] semaphore_post      0x%lx value %u -> %u",
325                 VG_(get_running_tid)(),
326                 DRD_(thread_get_running_tid)(),
327                 semaphore,
328                 p->value - 1, p->value);
329  }
330
331  p->last_sem_post_tid = tid;
332  DRD_(thread_new_segment)(tid);
333  sg = 0;
334  DRD_(thread_get_latest_segment)(&sg, tid);
335  tl_assert(sg);
336  DRD_(segment_push)(p, sg);
337  DRD_(s_semaphore_segment_creation_count)++;
338}
339
340/** Called after sem_post() finished successfully. */
341void DRD_(semaphore_post_post)(const DrdThreadId tid, const Addr semaphore,
342                               const Bool waited)
343{
344  /* Note: it is hard to implement the sem_post() wrapper correctly in     */
345  /* case sem_post() returns an error code. This is because handling this  */
346  /* case correctly requires restoring the vector clock associated with    */
347  /* the semaphore to its original value here. In order to do that without */
348  /* introducing a race condition, extra locking has to be added around    */
349  /* each semaphore call. Such extra locking would have to be added in     */
350  /* drd_intercepts.c. However, it is hard to implement synchronization    */
351  /* in drd_intercepts.c in a portable way without calling already         */
352  /* redirected functions.                                                 */
353}
354
355void DRD_(semaphore_thread_delete)(const DrdThreadId threadid)
356{ }
357
358ULong DRD_(get_semaphore_segment_creation_count)(void)
359{
360  return DRD_(s_semaphore_segment_creation_count);
361}
362