2 This file is part of drd, a thread error detector.
4 Copyright (C) 2006-2020 Bart Van Assche <bvanassche@acm.org>.
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.
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.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <http://www.gnu.org/licenses/>.
19 The GNU General Public License is contained in the file COPYING.
23 #include "drd_clientobj.h"
24 #include "drd_error.h"
25 #include "drd_semaphore.h"
26 #include "drd_suppression.h"
27 #include "pub_tool_errormgr.h" // VG_(maybe_record_error)()
28 #include "pub_tool_libcassert.h" // tl_assert()
29 #include "pub_tool_libcprint.h" // VG_(printf)()
30 #include "pub_tool_machine.h" // VG_(get_IP)()
31 #include "pub_tool_mallocfree.h" // VG_(malloc), VG_(free)
32 #include "pub_tool_threadstate.h" // VG_(get_running_tid)()
35 /* Local functions. */
37 static void semaphore_cleanup(struct semaphore_info
* p
);
40 /* Local variables. */
42 static Bool s_trace_semaphore
;
43 static ULong s_semaphore_segment_creation_count
;
46 /* Function definitions. */
48 /** Push a segment at the end of the queue 'p->last_sem_post_seg'. */
49 static void drd_segment_push(struct semaphore_info
* p
, Segment
* sg
)
54 n
= VG_(addToXA
)(p
->last_sem_post_seg
, &sg
);
56 VG_(message
)(Vg_DebugMsg
, "0x%lx push: added at position %ld/%ld\n",
57 p
->a1
, n
, VG_(sizeXA
)(p
->last_sem_post_seg
));
59 tl_assert(*(Segment
**)VG_(indexXA
)(p
->last_sem_post_seg
, n
) == sg
);
62 /** Pop a segment from the beginning of the queue 'p->last_sem_post_seg'. */
63 static Segment
* drd_segment_pop(struct semaphore_info
* p
)
68 sz
= VG_(sizeXA
)(p
->last_sem_post_seg
);
70 VG_(message
)(Vg_DebugMsg
, "0x%lx pop: removed from position %ld/%ld\n",
76 sg
= *(Segment
**)VG_(indexXA
)(p
->last_sem_post_seg
, sz
- 1);
78 VG_(dropTailXA
)(p
->last_sem_post_seg
, 1);
83 /** Enable or disable tracing of semaphore actions. */
84 void DRD_(semaphore_set_trace
)(const Bool trace_semaphore
)
86 s_trace_semaphore
= trace_semaphore
;
90 * Initialize the memory 'p' points at as a semaphore_info structure for the
91 * client semaphore at client address 'semaphore'.
94 void drd_semaphore_initialize(struct semaphore_info
* const p
,
97 tl_assert(semaphore
!= 0);
98 tl_assert(p
->a1
== semaphore
);
99 tl_assert(p
->type
== ClientSemaphore
);
101 p
->cleanup
= (void(*)(DrdClientobj
*))semaphore_cleanup
;
102 p
->delete_thread
= 0;
103 p
->waits_to_skip
= 0;
106 p
->last_sem_post_tid
= DRD_INVALID_THREADID
;
107 p
->last_sem_post_seg
= VG_(newXA
)(VG_(malloc
), "drd.sg-stack",
108 VG_(free
), sizeof(Segment
*));
112 * Free the memory that was allocated by semaphore_initialize(). Called by
113 * DRD_(clientobj_remove)().
115 static void semaphore_cleanup(struct semaphore_info
* p
)
121 SemaphoreErrInfo sei
= { DRD_(thread_get_running_tid
)(), p
->a1
};
122 VG_(maybe_record_error
)(VG_(get_running_tid
)(),
124 VG_(get_IP
)(VG_(get_running_tid
)()),
125 "Destruction of semaphore that is being waited"
129 while ((sg
= drd_segment_pop(p
)))
131 VG_(deleteXA
)(p
->last_sem_post_seg
);
135 * Return a pointer to the structure with information about the specified
136 * client semaphore. Allocate a new structure if such a structure did not
140 struct semaphore_info
*
141 drd_semaphore_get_or_allocate(const Addr semaphore
)
143 struct semaphore_info
*p
;
145 tl_assert(offsetof(DrdClientobj
, semaphore
) == 0);
146 p
= &(DRD_(clientobj_get
)(semaphore
, ClientSemaphore
)->semaphore
);
149 tl_assert(offsetof(DrdClientobj
, semaphore
) == 0);
150 p
= &(DRD_(clientobj_add
)(semaphore
, ClientSemaphore
)->semaphore
);
151 drd_semaphore_initialize(p
, semaphore
);
157 * Return a pointer to the structure with information about the specified
158 * client semaphore, or null if no such structure was found.
160 static struct semaphore_info
* semaphore_get(const Addr semaphore
)
162 tl_assert(offsetof(DrdClientobj
, semaphore
) == 0);
163 return &(DRD_(clientobj_get
)(semaphore
, ClientSemaphore
)->semaphore
);
166 /** Called before sem_init(). */
167 struct semaphore_info
* DRD_(semaphore_init
)(const Addr semaphore
,
171 struct semaphore_info
* p
;
174 if (s_trace_semaphore
)
175 DRD_(trace_msg
)("[%u] sem_init 0x%lx value %u",
176 DRD_(thread_get_running_tid
)(), semaphore
, value
);
178 p
= semaphore_get(semaphore
);
181 const ThreadId vg_tid
= VG_(get_running_tid
)();
182 SemaphoreErrInfo SEI
= { DRD_(thread_get_running_tid
)(), semaphore
};
183 VG_(maybe_record_error
)(vg_tid
,
186 "Semaphore reinitialization",
188 // Remove all segments from the segment stack.
189 while ((sg
= drd_segment_pop(p
)))
196 #if defined(VGO_darwin)
197 const ThreadId vg_tid
= VG_(get_running_tid
)();
198 GenericErrInfo GEI
= { DRD_(thread_get_running_tid
)(), 0 };
199 VG_(maybe_record_error
)(vg_tid
,
202 "sem_init() is not yet supported on Darwin",
206 p
= drd_semaphore_get_or_allocate(semaphore
);
210 p
->waits_to_skip
= value
;
215 /** Called after sem_destroy(). */
216 void DRD_(semaphore_destroy
)(const Addr semaphore
)
218 struct semaphore_info
* p
;
220 p
= semaphore_get(semaphore
);
222 if (s_trace_semaphore
)
223 DRD_(trace_msg
)("[%u] sem_destroy 0x%lx value %u",
224 DRD_(thread_get_running_tid
)(), semaphore
,
229 GenericErrInfo GEI
= {
230 .tid
= DRD_(thread_get_running_tid
)(),
233 VG_(maybe_record_error
)(VG_(get_running_tid
)(),
235 VG_(get_IP
)(VG_(get_running_tid
)()),
241 DRD_(clientobj_remove
)(semaphore
, ClientSemaphore
);
244 /** Called after sem_open(). */
245 struct semaphore_info
* DRD_(semaphore_open
)(const Addr semaphore
,
246 const HChar
* name
, const Word oflag
,
247 const Word mode
, const UInt value
)
249 struct semaphore_info
* p
;
252 if (s_trace_semaphore
)
253 DRD_(trace_msg
)("[%u] sem_open 0x%lx name %s"
254 " oflag %#lx mode %#lo value %u",
255 DRD_(thread_get_running_tid
)(),
256 semaphore
, name
, (UWord
)oflag
, (UWord
)mode
, value
);
258 /* Return if the sem_open() call failed. */
262 p
= semaphore_get(semaphore
);
265 const ThreadId vg_tid
= VG_(get_running_tid
)();
266 SemaphoreErrInfo SEI
= { DRD_(thread_get_running_tid
)(), semaphore
};
267 VG_(maybe_record_error
)(vg_tid
,
270 "Semaphore reinitialization",
272 // Remove all segments from the segment stack.
273 while ((sg
= drd_segment_pop(p
)))
280 p
= drd_semaphore_get_or_allocate(semaphore
);
283 p
->waits_to_skip
= value
;
288 /** Called before sem_close(). */
289 void DRD_(semaphore_close
)(const Addr semaphore
)
291 struct semaphore_info
* p
;
293 p
= semaphore_get(semaphore
);
295 if (s_trace_semaphore
)
296 DRD_(trace_msg
)("[%u] sem_close 0x%lx value %u",
297 DRD_(thread_get_running_tid
)(), semaphore
,
302 GenericErrInfo GEI
= {
303 .tid
= DRD_(thread_get_running_tid
)(),
306 VG_(maybe_record_error
)(VG_(get_running_tid
)(),
308 VG_(get_IP
)(VG_(get_running_tid
)()),
314 DRD_(clientobj_remove
)(semaphore
, ClientSemaphore
);
317 /** Called before sem_wait(). */
318 void DRD_(semaphore_pre_wait
)(const Addr semaphore
)
320 struct semaphore_info
* p
;
322 tl_assert(semaphore
< semaphore
+ 1);
323 p
= drd_semaphore_get_or_allocate(semaphore
);
327 if ((Word
)(p
->waiters
) <= 0)
329 SemaphoreErrInfo sei
= { DRD_(thread_get_running_tid
)(), semaphore
};
330 VG_(maybe_record_error
)(VG_(get_running_tid
)(),
332 VG_(get_IP
)(VG_(get_running_tid
)()),
339 * Called after sem_wait() finished.
340 * @note Some C libraries do not set the 'waited' value correctly.
342 void DRD_(semaphore_post_wait
)(const DrdThreadId tid
, const Addr semaphore
,
345 struct semaphore_info
* p
;
348 tl_assert(waited
== 0 || waited
== 1);
349 p
= semaphore_get(semaphore
);
350 if (s_trace_semaphore
)
351 DRD_(trace_msg
)("[%u] sem_wait 0x%lx value %u -> %u%s",
352 DRD_(thread_get_running_tid
)(), semaphore
,
353 p
? p
->value
: 0, p
? p
->value
- waited
: 0,
354 waited
? "" : " (did not wait)");
362 * Note: if another thread destroyed and reinitialized a semaphore while
363 * the current thread was waiting in sem_wait, p->waiters may have been
364 * set to zero by drd_semaphore_initialize() after
365 * DRD_(semaphore_pre_wait)() has finished before
366 * DRD_(semaphore_post_wait)() has been called.
368 if (p
== NULL
|| (Int
)(p
->value
) < 0 || (Word
)(p
->waiters
) < 0)
370 SemaphoreErrInfo sei
= { DRD_(thread_get_running_tid
)(), semaphore
};
371 VG_(maybe_record_error
)(VG_(get_running_tid
)(),
373 VG_(get_IP
)(VG_(get_running_tid
)()),
382 if (p
->waits_to_skip
> 0)
386 sg
= drd_segment_pop(p
);
388 if (p
->last_sem_post_tid
!= tid
389 && p
->last_sem_post_tid
!= DRD_INVALID_THREADID
)
391 DRD_(thread_new_segment_and_combine_vc
)(tid
, sg
);
394 DRD_(thread_new_segment
)(tid
);
395 s_semaphore_segment_creation_count
++;
400 /** Called before sem_post(). */
401 void DRD_(semaphore_pre_post
)(const DrdThreadId tid
, const Addr semaphore
)
403 struct semaphore_info
* p
;
406 p
= drd_semaphore_get_or_allocate(semaphore
);
409 if (s_trace_semaphore
)
410 DRD_(trace_msg
)("[%u] sem_post 0x%lx value %u -> %u",
411 DRD_(thread_get_running_tid
)(),
412 semaphore
, p
->value
- 1, p
->value
);
414 p
->last_sem_post_tid
= tid
;
416 DRD_(thread_get_latest_segment
)(&sg
, tid
);
418 drd_segment_push(p
, sg
);
419 DRD_(thread_new_segment
)(tid
);
420 s_semaphore_segment_creation_count
++;
423 /** Called after sem_post() finished. */
424 void DRD_(semaphore_post_post
)(const DrdThreadId tid
, const Addr semaphore
,
425 const Bool succeeded
)
428 * Note: it is hard to implement the sem_post() wrapper correctly in
429 * case sem_post() returns an error code. This is because handling this
430 * case correctly requires restoring the vector clock associated with
431 * the semaphore to its original value here. In order to do that without
432 * introducing a race condition, extra locking has to be added around
433 * each semaphore call. Such extra locking would have to be added in
434 * drd_pthread_intercepts.c. However, it is hard to implement
435 * synchronization in drd_pthread_intercepts.c in a portable way without
436 * calling already redirected functions.
440 ULong
DRD_(get_semaphore_segment_creation_count
)(void)
442 return s_semaphore_segment_creation_count
;