blob: 8748f0c9013a0d959b85fe1e411900c0cbf11db0 [file] [log] [blame]
sewardjb5f6f512005-03-10 23:59:00 +00001
2/*--------------------------------------------------------------------*/
3/*--- Pthreads library modelling. vg_pthreadmodel.c ---*/
4/*--------------------------------------------------------------------*/
5
6/*
7 This file is part of Valgrind, an extensible x86 protected-mode
8 emulator for monitoring program execution on x86-Unixes.
9
10 Copyright (C) 2005 Jeremy Fitzhardinge
11 jeremy@goop.org
12
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation; either version 2 of the
16 License, or (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful, but
19 WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
26 02111-1307, USA.
27
28 The GNU General Public License is contained in the file COPYING.
29*/
30
31/*
32 This file wraps the client's use of libpthread functions and calls
33 on vg_threadmodel.c to model the state of the the client's threads.
34 The intent is to 1) look for problem's in the client's use of the
35 pthread API, and 2) tell tools which care about thread events (eg,
36 helgrind).
37
38 This file is intended to be implementation-independent. It assumes
39 that the client is using the same pthread.h as the one we include
40 here, but makes minimal assumptions about the actual structures
41 defined and so on (ie, the exact nature of pthread_t).
42
43 (For now we assume there's a 1:1 relationship between pthread_t's
44 and Valgrind-visible threads; N:M implementations will need further
45 work.)
46
47 The model is based on the pthread standard rather than any
48 particular implementation, in order to encourage portable use of
49 libpthread. On the other hand, we will probably need to implement
50 particular implementation extensions if they're widely used.
51
52 One tricky problem we need to solve is the mapping between
53 pthread_t identifiers and internal thread identifiers.
54 */
55
56#include "core.h"
57
58#if 0
59
60#define __USE_GNU
61#define __USE_UNIX98
62#include <pthread.h>
63
64static const Bool debug = False;
65
66static Bool check_wrappings(void);
67
68#define ENTER(x) \
69 do { \
70 if (VG_(clo_trace_pthreads)) \
71 VG_(message)(Vg_DebugMsg, ">>> %d entering %s", \
72 VG_(get_running_tid)(), #x); \
73 } while(0)
74
75static const Char *pp_retval(enum return_type rt, Word retval)
76{
77 static Char buf[50];
78
79 switch(rt) {
80 case RT_RETURN:
81 VG_(sprintf)(buf, "return %d 0x%x", retval, retval);
82 return buf;
83
84 case RT_LONGJMP:
85 return "LONGJMPed out";
86
87 case RT_EXIT:
88 return "thread exit";
89 }
90 return "??";
91}
92
93#define LEAVE(x, rt, retval) \
94 do { \
95 if (VG_(clo_trace_pthreads)) \
96 VG_(message)(Vg_DebugMsg, "<<< %d leaving %s -> %s", \
97 VG_(get_running_tid)(), #x, pp_retval(rt, retval)); \
98 } while(0)
99
100struct pthread_map
101{
102 pthread_t id;
103
104 ThreadId tid;
105};
106
107static Int pthread_cmp(const void *v1, const void *v2)
108{
109 const pthread_t *a = (const pthread_t *)v1;
110 const pthread_t *b = (const pthread_t *)v2;
111
112 return VG_(memcmp)(a, b, sizeof(*a));
113}
114
115static SkipList sk_pthread_map = SKIPLIST_INIT(struct pthread_map, id, pthread_cmp,
116 NULL, VG_AR_CORE);
117
118/* Find a ThreadId for a particular pthread_t; block until it becomes available */
119static ThreadId get_pthread_mapping(pthread_t id)
120{
121 /* Nasty little spin loop; revise if this turns out to be a
122 problem. This should only spin for as long as it takes for the
123 child thread to register the pthread_t. */
124 for(;;) {
125 struct pthread_map *m = VG_(SkipList_Find_Exact)(&sk_pthread_map, &id);
126
127 if (m && m->tid != VG_INVALID_THREADID)
128 return m->tid;
129
130 //VG_(printf)("find %x -> %p\n", id, m);
131 VG_(vg_yield)();
132 }
133}
134
135/* Create a mapping between a ThreadId and a pthread_t */
136static void pthread_id_mapping(ThreadId tid, Addr idp, UInt idsz)
137{
138 pthread_t id = *(pthread_t *)idp;
139 struct pthread_map *m = VG_(SkipList_Find_Exact)(&sk_pthread_map, &id);
140
141 if (debug)
142 VG_(printf)("Thread %d maps to %p\n", tid, id);
143
144 if (m == NULL) {
145 m = VG_(SkipNode_Alloc)(&sk_pthread_map);
146 m->id = id;
147 m->tid = tid;
148 VG_(SkipList_Insert)(&sk_pthread_map, m);
149 } else {
150 if (m->tid != VG_INVALID_THREADID && m->tid != tid)
151 VG_(message)(Vg_UserMsg, "Thread %d is creating duplicate mapping for pthread identifier %x; previously mapped to %d\n",
152 tid, (UInt)id, m->tid);
153 m->tid = tid;
154 }
155}
156
157static void check_thread_exists(ThreadId tid)
158{
159 if (!VG_(tm_thread_exists)(tid)) {
160 if (debug)
161 VG_(printf)("creating thread %d\n", tid);
162 VG_(tm_thread_create)(VG_INVALID_THREADID, tid, False);
163 }
164}
165
166static Addr startfunc_wrapper = 0;
167
168void VG_(pthread_startfunc_wrapper)(Addr wrapper)
169{
170 startfunc_wrapper = wrapper;
171}
172
173struct pthread_create_nonce {
174 Bool detached;
175 pthread_t *threadid;
176};
177
178static void *before_pthread_create(va_list va)
179{
180 pthread_t *threadp = va_arg(va, pthread_t *);
181 const pthread_attr_t *attr = va_arg(va, const pthread_attr_t *);
182 void *(*start)(void *) = va_arg(va, void *(*)(void *));
183 void *arg = va_arg(va, void *);
184 struct pthread_create_nonce *n;
185 struct vg_pthread_newthread_data *data;
186 ThreadState *tst;
187
188 if (!check_wrappings())
189 return NULL;
190
191 ENTER(pthread_create);
192
193 /* Data is in the client heap and is freed by the client in the
194 startfunc_wrapper. */
195 vg_assert(startfunc_wrapper != 0);
196
197 tst = VG_(get_ThreadState)(VG_(get_running_tid)());
198
199 VG_(sk_malloc_called_by_scheduler) = True;
200 data = SK_(malloc)(sizeof(*data));
201 VG_(sk_malloc_called_by_scheduler) = False;
202
203 VG_TRACK(pre_mem_write, Vg_CorePThread, tst->tid, "new thread data",
204 (Addr)data, sizeof(*data));
205 data->startfunc = start;
206 data->arg = arg;
207 VG_TRACK(post_mem_write, (Addr)data, sizeof(*data));
208
209 /* Substitute arguments
210 XXX hack: need an API to do this. */
211 ((Word *)tst->arch.m_esp)[3] = startfunc_wrapper;
212 ((Word *)tst->arch.m_esp)[4] = (Word)data;
213
214 if (debug)
215 VG_(printf)("starting thread at wrapper %p\n", startfunc_wrapper);
216
217 n = VG_(arena_malloc)(VG_AR_CORE, sizeof(*n));
218 n->detached = attr && !!attr->__detachstate;
219 n->threadid = threadp;
220
221 return n;
222}
223
224static void after_pthread_create(void *nonce, enum return_type rt, Word retval)
225{
226 struct pthread_create_nonce *n = (struct pthread_create_nonce *)nonce;
227 ThreadId tid = VG_(get_running_tid)();
228
229 if (n == NULL)
230 return;
231
232 if (rt == RT_RETURN && retval == 0) {
233 if (!VG_(tm_thread_exists)(tid))
234 VG_(tm_thread_create)(tid, get_pthread_mapping(*n->threadid),
235 n->detached);
236 else {
237 if (n->detached)
238 VG_(tm_thread_detach)(tid);
239 /* XXX set creator tid as well? */
240 }
241 }
242
243 VG_(arena_free)(VG_AR_CORE, n);
244
245 LEAVE(pthread_create, rt, retval);
246}
247
248static void *before_pthread_join(va_list va)
249{
250 pthread_t pt_joinee = va_arg(va, pthread_t);
251 ThreadId joinee;
252
253 if (!check_wrappings())
254 return NULL;
255
256 ENTER(pthread_join);
257
258 joinee = get_pthread_mapping(pt_joinee);
259
260 VG_(tm_thread_join)(VG_(get_running_tid)(), joinee);
261
262 return NULL;
263}
264
265static void after_pthread_join(void *v, enum return_type rt, Word retval)
266{
267 /* nothing to be done? */
268 if (!check_wrappings())
269 return;
270
271 LEAVE(pthread_join, rt, retval);
272}
273
274struct pthread_detach_data {
275 pthread_t id;
276};
277
278static void *before_pthread_detach(va_list va)
279{
280 pthread_t id = va_arg(va, pthread_t);
281 struct pthread_detach_data *data;
282
283 if (!check_wrappings())
284 return NULL;
285
286 ENTER(pthread_detach);
287
288 data = VG_(arena_malloc)(VG_AR_CORE, sizeof(*data));
289 data->id = id;
290
291 return data;
292}
293
294static void after_pthread_detach(void *nonce, enum return_type rt, Word retval)
295{
296 struct pthread_detach_data *data = (struct pthread_detach_data *)nonce;
297 ThreadId tid;
298
299 if (data == NULL)
300 return;
301
302 tid = get_pthread_mapping(data->id);
303
304 VG_(arena_free)(VG_AR_CORE, data);
305
306 if (rt == RT_RETURN && retval == 0)
307 VG_(tm_thread_detach)(tid);
308
309 LEAVE(pthread_detach, rt, retval);
310}
311
312
313
314static void *before_pthread_self(va_list va)
315{
316 /* If pthread_t is a structure, then this might be passed a pointer
317 to the return value. On Linux/glibc, it's a simple scalar, so it is
318 returned normally. */
319 if (!check_wrappings())
320 return NULL;
321
322 ENTER(pthread_self);
323
324 check_thread_exists(VG_(get_running_tid)());
325 return NULL;
326}
327
328static void after_pthread_self(void *nonce, enum return_type rt, Word retval)
329{
330 pthread_t ret = (pthread_t)retval;
331
332 if (!check_wrappings())
333 return;
334
335 pthread_id_mapping(VG_(get_running_tid)(), (Addr)&ret, sizeof(ret));
336
337 LEAVE(pthread_self, rt, retval);
338}
339
340
341/* If a mutex hasn't been initialized, check it against all the static
342 initializers to see if it appears to have been statically
343 initialized. */
344static void check_mutex_init(ThreadId tid, pthread_mutex_t *mx)
345{
346 static const pthread_mutex_t initializers[] = {
347 PTHREAD_MUTEX_INITIALIZER,
348 PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP,
349 PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP,
350 PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP,
351 };
352 Int i;
353
354 if (VG_(tm_mutex_exists)((Addr)mx))
355 return;
356
357 VG_TRACK(pre_mem_read, Vg_CorePThread, tid, "pthread_mutex_t", (Addr)mx, sizeof(*mx));
358
359 for(i = 0; i < sizeof(initializers)/sizeof(*initializers); i++)
360 if (VG_(memcmp)(&initializers[i], mx, sizeof(*mx)) == 0) {
361 VG_(tm_mutex_init)(tid, (Addr)mx);
362 break;
363 }
364}
365
366static void *before_pthread_mutex_init(va_list va)
367{
368 pthread_mutex_t *mx = va_arg(va, pthread_mutex_t *);
369 const pthread_mutexattr_t *attr = va_arg(va, const pthread_mutexattr_t *);
370
371 if (!check_wrappings())
372 return NULL;
373
374 ENTER(pthread_mutex_init);
375
376 /* XXX look for recursive mutex */
377 /* XXX look for non-process scope */
378 (void)attr;
379
380 return mx;
381}
382
383static void after_pthread_mutex_init(void *nonce, enum return_type rt, Word retval)
384{
385 if (!check_wrappings())
386 return;
387
388 if (rt == RT_RETURN && retval == 0)
389 VG_(tm_mutex_init)(VG_(get_running_tid)(), (Addr)nonce);
390
391 LEAVE(pthread_mutex_init, rt, retval);
392}
393
394static void *before_pthread_mutex_destroy(va_list va)
395{
396 pthread_mutex_t *mx = va_arg(va, pthread_mutex_t *);
397
398 if (!check_wrappings())
399 return NULL;
400
401 ENTER(pthread_mutex_destroy);
402
403 VG_(tm_mutex_destroy)(VG_(get_running_tid)(), (Addr)mx);
404
405 return NULL;
406}
407
408static void after_pthread_mutex_destroy(void *nonce, enum return_type rt, Word retval)
409{
410 if (!check_wrappings())
411 return;
412
413 LEAVE(pthread_mutex_destroy, rt, retval);
414}
415
416static void *before_pthread_mutex_lock(va_list va)
417{
418 pthread_mutex_t *mx = va_arg(va, pthread_mutex_t *);
419
420 if (!check_wrappings())
421 return NULL;
422
423 ENTER(pthread_mutex_lock);
424
425 if (debug)
426 VG_(printf)("%d locking %p\n", VG_(get_running_tid)(), mx);
427 check_thread_exists(VG_(get_running_tid)());
428 check_mutex_init(VG_(get_running_tid)(), mx); /* mutex might be statically initialized */
429 VG_(tm_mutex_trylock)(VG_(get_running_tid)(), (Addr)mx);
430
431 return mx;
432}
433
434static void after_pthread_mutex_lock(void *nonce, enum return_type rt, Word retval)
435{
436 if (!check_wrappings())
437 return;
438
439 if (rt == RT_RETURN && retval == 0)
440 VG_(tm_mutex_acquire)(VG_(get_running_tid)(), (Addr)nonce);
441 else {
442 if (debug)
443 VG_(printf)("after mutex_lock failed: rt=%d ret=%d\n", rt, retval);
444 VG_(tm_mutex_giveup)(VG_(get_running_tid)(), (Addr)nonce);
445 }
446
447 LEAVE(pthread_mutex_lock, rt, retval);
448}
449
450static void *before_pthread_mutex_trylock(va_list va)
451{
452 pthread_mutex_t *mx = va_arg(va, pthread_mutex_t *);
453
454 if (!check_wrappings())
455 return NULL;
456
457 ENTER(pthread_mutex_trylock);
458
459 if (debug)
460 VG_(printf)("%d trylocking %p\n", VG_(get_running_tid)(), mx);
461 check_thread_exists(VG_(get_running_tid)());
462 check_mutex_init(VG_(get_running_tid)(), mx); /* mutex might be statically initialized */
463 VG_(tm_mutex_trylock)(VG_(get_running_tid)(), (Addr)mx);
464
465 return mx;
466}
467
468static void after_pthread_mutex_trylock(void *nonce, enum return_type rt, Word retval)
469{
470 if (nonce == NULL)
471 return;
472
473 if (rt == RT_RETURN && retval == 0)
474 VG_(tm_mutex_acquire)(VG_(get_running_tid)(), (Addr)nonce);
475 else {
476 if (debug)
477 VG_(printf)("after mutex_trylock failed: rt=%d ret=%d\n", rt, retval);
478 VG_(tm_mutex_giveup)(VG_(get_running_tid)(), (Addr)nonce);
479 }
480
481 LEAVE(pthread_mutex_trylock, rt, retval);
482}
483
484static void *before_pthread_mutex_unlock(va_list va)
485{
486 pthread_mutex_t *mx = va_arg(va, pthread_mutex_t *);
487
488 if (!check_wrappings())
489 return NULL;
490
491 ENTER(pthread_mutex_unlock);
492
493 VG_(tm_mutex_tryunlock)(VG_(get_running_tid)(), (Addr)mx);
494
495 return mx;
496}
497
498static void after_pthread_mutex_unlock(void *nonce, enum return_type rt, Word retval)
499{
500 if (nonce == NULL)
501 return;
502
503 if (rt == RT_RETURN && retval == 0)
504 VG_(tm_mutex_unlock)(VG_(get_running_tid)(), (Addr)nonce); /* complete unlock */
505 else
506 VG_(tm_mutex_acquire)(VG_(get_running_tid)(), (Addr)nonce); /* re-acquire */
507
508 LEAVE(pthread_mutex_unlock, rt, retval);
509}
510
511
512static struct pt_wraps {
513 const Char *name;
514 FuncWrapper wrapper;
515 const CodeRedirect *redir;
516} wraps[] = {
517#define WRAP(func, extra) { #func extra, { before_##func, after_##func } }
518 WRAP(pthread_create, "@@GLIBC_2.1"), /* XXX TODO: 2.0 ABI (?) */
519 WRAP(pthread_join, ""),
520 WRAP(pthread_detach, ""),
521
522 WRAP(pthread_self, ""),
523
524 WRAP(pthread_mutex_init, ""),
525 WRAP(pthread_mutex_destroy, ""),
526 WRAP(pthread_mutex_lock, ""),
527 WRAP(pthread_mutex_trylock, ""),
528 WRAP(pthread_mutex_unlock, ""),
529#undef WRAP
530};
531
532/* Check to see if all the wrappers are resolved */
533static Bool check_wrappings()
534{
535 Int i;
536 static Bool ok = True;
537 static Bool checked = False;
538
539 if (checked)
540 return ok;
541
542 for(i = 0; i < sizeof(wraps)/sizeof(*wraps); i++) {
543 if (!VG_(is_resolved)(wraps[i].redir)) {
544 VG_(message)(Vg_DebugMsg, "Pthread wrapper for \"%s\" is not resolved",
545 wraps[i].name);
546 ok = False;
547 }
548 }
549
550 if (startfunc_wrapper == 0) {
551 VG_(message)(Vg_DebugMsg, "Pthread wrapper for thread start function is not resolved");
552 ok = False;
553 }
554
555 if (!ok)
556 VG_(message)(Vg_DebugMsg, "Missing intercepts; model disabled");
557
558 checked = True;
559 return ok;
560}
561
562/*
563 Set up all the wrappers for interesting functions.
564 */
565void VG_(pthread_init)()
566{
567 Int i;
568
569 for(i = 0; i < sizeof(wraps)/sizeof(*wraps); i++) {
570 //VG_(printf)("adding pthread wrapper for %s\n", wraps[i].name);
571 wraps[i].redir = VG_(add_wrapper)("soname:libpthread.so.0",
572 wraps[i].name, &wraps[i].wrapper);
573 }
574 VG_(tm_init)();
575 VG_(tm_thread_create)(VG_INVALID_THREADID, VG_(master_tid), True);
576}
577
578#else /* !0 */
579/* Stubs for now */
580
581void VG_(pthread_init)()
582{
583}
584
585void VG_(pthread_startfunc_wrapper)(Addr wrapper)
586{
587}
588#endif /* 0 */