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