blob: 25b4ef13b59c57a6117383082f443f36b5b2587a [file] [log] [blame]
sewardjaf44c822007-11-25 14:01:38 +00001/*
2 This file is part of drd, a data race detector.
3
4 Copyright (C) 2006-2007 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_error.h"
27#include "drd_mutex.h"
28#include "drd_suppression.h"
29#include "pthread_object_size.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_threadstate.h" // VG_(get_running_tid)()
35
36
37// Type definitions.
38
39struct mutex_info
40{
41 Addr mutex; // Pointer to client mutex.
42 SizeT size; // Size in bytes of client-side object.
43 int recursion_count; // 0 if free, >= 1 if locked.
44 DrdThreadId owner; // owner if locked, last owner if free.
45 VectorClock vc; // vector clock associated with last unlock.
46};
47
48
49// Local variables.
50
51static Bool s_trace_mutex;
52static ULong s_mutex_lock_count;
53struct mutex_info s_mutex[256];
54
55
56// Function definitions.
57
58void mutex_set_trace(const Bool trace_mutex)
59{
60 tl_assert(!! trace_mutex == trace_mutex);
61 s_trace_mutex = trace_mutex;
62}
63
64static
65void mutex_initialize(struct mutex_info* const p,
66 const Addr mutex,
67 const SizeT size)
68{
69 tl_assert(mutex != 0);
70 tl_assert(size > 0);
71
72 p->mutex = mutex;
73 p->size = size;
74 p->recursion_count = 0;
75 p->owner = DRD_INVALID_THREADID;
76 vc_init(&p->vc, 0, 0);
77}
78
79static
80struct mutex_info* mutex_get_or_allocate(const Addr mutex, const SizeT size)
81{
82 int i;
83 for (i = 0; i < sizeof(s_mutex)/sizeof(s_mutex[0]); i++)
84 if (s_mutex[i].mutex == mutex)
85 return &s_mutex[i];
86 for (i = 0; i < sizeof(s_mutex)/sizeof(s_mutex[0]); i++)
87 {
88 if (s_mutex[i].mutex == 0)
89 {
90 mutex_initialize(&s_mutex[i], mutex, size);
91 drd_start_suppression(mutex, mutex + size,
92 mutex_get_typename(&s_mutex[i]));
93 return &s_mutex[i];
94 }
95 }
96 tl_assert(0);
97 return 0;
98}
99
100struct mutex_info* mutex_init(const Addr mutex, const SizeT size)
101{
102 struct mutex_info* mutex_p;
103
104 tl_assert(mutex_get(mutex) == 0);
105 mutex_p = mutex_get_or_allocate(mutex, size);
106
107 if (s_trace_mutex)
108 {
109 const ThreadId vg_tid = VG_(get_running_tid)();
110 const DrdThreadId drd_tid = VgThreadIdToDrdThreadId(vg_tid);
111 VG_(message)(Vg_DebugMsg,
112 "drd_post_mutex_init tid = %d/%d, %s 0x%lx",
113 vg_tid, drd_tid,
114 mutex_get_typename(mutex_p),
115 mutex);
116 }
117
118 return mutex_p;
119}
120
121void mutex_destroy(struct mutex_info* const p)
122{
123 if (s_trace_mutex)
124 {
125 const ThreadId vg_tid = VG_(get_running_tid)();
126 const DrdThreadId drd_tid = VgThreadIdToDrdThreadId(vg_tid);
127 VG_(message)(Vg_DebugMsg,
128 "drd_pre_mutex_destroy tid = %d/%d, %s 0x%lx",
129 vg_tid, drd_tid,
130 mutex_get_typename(p),
131 p->mutex);
132 }
133
134 drd_finish_suppression(p->mutex, p->mutex + p->size);
135
136 vc_cleanup(&p->vc);
137 p->mutex = 0;
138}
139
140struct mutex_info* mutex_get(const Addr mutex)
141{
142 int i;
143 for (i = 0; i < sizeof(s_mutex)/sizeof(s_mutex[0]); i++)
144 if (s_mutex[i].mutex == mutex)
145 return &s_mutex[i];
146 return 0;
147}
148
149/**
150 * Update mutex_info state when locking the pthread_mutex_t mutex.
151 * Note: this function must be called after pthread_mutex_lock() has been
152 * called, or a race condition is triggered !
153 */
154int mutex_lock(const Addr mutex, const SizeT size)
155{
156 const DrdThreadId drd_tid = VgThreadIdToDrdThreadId(VG_(get_running_tid)());
157 struct mutex_info* const p = mutex_get_or_allocate(mutex, size);
158 const DrdThreadId last_owner = p->owner;
159
160 if (s_trace_mutex)
161 {
162 const ThreadId tid = DrdThreadIdToVgThreadId(drd_tid);
163 VG_(message)(Vg_DebugMsg,
164 "drd_post_mutex_lock tid = %d/%d, %s 0x%lx rc %d owner %d",
165 tid,
166 drd_tid,
167 mutex_get_typename(p),
168 mutex,
169 p ? p->recursion_count : 0,
170 p ? p->owner : VG_INVALID_THREADID);
171 }
172
173 if (p->recursion_count >= 1 && p->size == PTHREAD_SPINLOCK_SIZE)
174 {
175 // TO DO: tell the user in a more friendly way that it is not allowed to
176 // lock spinlocks recursively.
177 tl_assert(0);
178 }
179
180 if (p->recursion_count == 0)
181 {
182 p->owner = drd_tid;
183 s_mutex_lock_count++;
184 }
185 else if (p->owner != drd_tid)
186 {
187 VG_(message)(Vg_DebugMsg,
188 "The impossible happened: mutex 0x%lx is locked"
189 " simultaneously by two threads (recursion count %d,"
190 " owners %d and %d) !",
191 p->mutex, p->recursion_count, p->owner, drd_tid);
192 tl_assert(0);
193 }
194 p->recursion_count++;
195
196 if (p->recursion_count == 1)
197 {
198 if (last_owner != drd_tid && last_owner != DRD_INVALID_THREADID)
199 thread_combine_vc2(drd_tid, mutex_get_last_vc(mutex));
200 thread_new_segment(drd_tid);
201 }
202
203 return p->recursion_count;
204}
205
206/**
207 * Update mutex_info state when unlocking the pthread_mutex_t mutex.
208 * Note: this function must be called before pthread_mutex_unlock() is called,
209 * or a race condition is triggered !
210 * @param mutex Pointer to pthread_mutex_t data structure in the client space.
211 * @param tid ThreadId of the thread calling pthread_mutex_unlock().
212 * @param vc Pointer to the current vector clock of thread tid.
213 */
214int mutex_unlock(const Addr mutex)
215{
216 const DrdThreadId drd_tid = VgThreadIdToDrdThreadId(VG_(get_running_tid)());
217 const ThreadId vg_tid = DrdThreadIdToVgThreadId(drd_tid);
218 const VectorClock* const vc = thread_get_vc(drd_tid);
219 struct mutex_info* const p = mutex_get(mutex);
220
221 if (s_trace_mutex)
222 {
223 VG_(message)(Vg_DebugMsg,
224 "drd_pre_mutex_unlock tid = %d/%d, %s 0x%lx rc %d",
225 vg_tid, drd_tid,
226 mutex_get_typename(p),
227 mutex,
228 p->recursion_count,
229 p->owner);
230 }
231
232 tl_assert(p);
233 tl_assert(p->owner != DRD_INVALID_THREADID);
234 if (p->owner != drd_tid)
235 {
236 MutexErrInfo MEI = { p->mutex, p->recursion_count, p->owner };
237 VG_(maybe_record_error)(vg_tid,
238 MutexErr,
239 VG_(get_IP)(vg_tid),
240 "Mutex not unlocked by owner thread",
241 &MEI);
242 }
243 p->recursion_count--;
244 tl_assert(p->recursion_count >= 0);
245 if (p->recursion_count == 0)
246 {
247 /* This pthread_mutex_unlock() call really unlocks the mutex. Save the */
248 /* current vector clock of the thread such that it is available when */
249 /* this mutex is locked again. */
250 vc_copy(&p->vc, vc);
251
252 thread_new_segment(drd_tid);
253 }
254 return p->recursion_count;
255}
256
257const char* mutex_get_typename(struct mutex_info* const p)
258{
259 tl_assert(p);
260 switch (p->size)
261 {
262 case PTHREAD_MUTEX_SIZE:
263 return "mutex";
264 case PTHREAD_SPINLOCK_SIZE:
265 return "spinlock";
266 default:
267 tl_assert(0);
268 }
269 return "?";
270}
271
272Bool mutex_is_locked_by(const Addr mutex, const DrdThreadId tid)
273{
274 struct mutex_info* const p = mutex_get(mutex);
275 tl_assert(p);
276 if (p)
277 {
278 return (p->recursion_count > 0 && p->owner == tid);
279 }
280 return False;
281}
282
283const VectorClock* mutex_get_last_vc(const Addr mutex)
284{
285 struct mutex_info* const p = mutex_get(mutex);
286 return p ? &p->vc : 0;
287}
288
289int mutex_get_recursion_count(const Addr mutex)
290{
291 struct mutex_info* const p = mutex_get(mutex);
292 tl_assert(p);
293 return p->recursion_count;
294}
295
296/**
297 * Call this function when thread threadid stops to exist, such that the
298 * "last owner" field can be cleared if it still refers to that thread.
299 * TO DO: print an error message if a thread exits while it still has some
300 * mutexes locked.
301 */
302void mutex_thread_delete(const DrdThreadId threadid)
303{
304 int i;
305 for (i = 0; i < sizeof(s_mutex)/sizeof(s_mutex[0]); i++)
306 {
307 struct mutex_info* const p = &s_mutex[i];
308 if (p->mutex && p->owner == threadid)
309 {
310 p->owner = VG_INVALID_THREADID;
311 }
312 }
313}
314
315void mutex_stop_using_mem(const Addr a1, const Addr a2)
316{
317 unsigned i;
318 for (i = 0; i < sizeof(s_mutex)/sizeof(s_mutex[0]); i++)
319 {
320 if (a1 <= s_mutex[i].mutex && s_mutex[i].mutex < a2)
321 {
322 tl_assert(s_mutex[i].mutex + s_mutex[i].size <= a2);
323 mutex_destroy(&s_mutex[i]);
324 }
325 }
326}
327
328ULong get_mutex_lock_count(void)
329{
330 return s_mutex_lock_count;
331}