blob: c2e8f9a300b0b698db45be900868120717afb528 [file] [log] [blame]
sewardjb5f6f512005-03-10 23:59:00 +00001
2/*--------------------------------------------------------------------*/
njn984a6362005-06-11 01:07:00 +00003/*--- Pthreads library modelling. m_pthreadmodel.c ---*/
sewardjb5f6f512005-03-10 23:59:00 +00004/*--------------------------------------------------------------------*/
5
6/*
njnc0ae7052005-08-25 22:55:19 +00007 This file is part of Valgrind, a dynamic binary instrumentation
8 framework.
sewardjb5f6f512005-03-10 23:59:00 +00009
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
njnc7561b92005-06-19 01:24:32 +000056#include "pub_core_basics.h"
sewardjb5f6f512005-03-10 23:59:00 +000057
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
njnbe91aae2005-03-27 01:42:41 +0000115static SkipList sk_pthread_map = VG_SKIPLIST_INIT(struct pthread_map, id, pthread_cmp,
sewardjb5f6f512005-03-10 23:59:00 +0000116 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
njnd13e5e62005-03-26 03:50:14 +0000199 // XXX: why use TL_(malloc)() here? What characteristics does this
200 // allocation require?
201 // [Possible: When using a tool that replaces malloc(), we want to call
202 // the replacement version. Otherwise, we want to use VG_(cli_malloc)().
203 // So we go via the default version of TL_(malloc)() in vg_default?]
njne96be672005-05-08 19:08:54 +0000204 tl_assert2(0, "read the comment in the code about this...");
205
206 // XXX: These three lines are going to have to change. They relied on
207 // TL_(malloc) being a weak symbol, and it just doesn't fit with the
208 // VG_(tdict) approach that we've switched to. The right way to do this
209 // will be to provide a function in the core that checks if
njn717cde52005-05-10 02:47:21 +0000210 // VG_(tdict).malloc_malloc has been set; if so, it should
njne96be672005-05-08 19:08:54 +0000211 // call it, if not, it should call VG_(cli_malloc)().
212// VG_(tl_malloc_called_deliberately) = True;
213// data = TL_(malloc)(sizeof(*data));
214// VG_(tl_malloc_called_deliberately) = False;
sewardjb5f6f512005-03-10 23:59:00 +0000215
216 VG_TRACK(pre_mem_write, Vg_CorePThread, tst->tid, "new thread data",
217 (Addr)data, sizeof(*data));
218 data->startfunc = start;
219 data->arg = arg;
220 VG_TRACK(post_mem_write, (Addr)data, sizeof(*data));
221
222 /* Substitute arguments
223 XXX hack: need an API to do this. */
224 ((Word *)tst->arch.m_esp)[3] = startfunc_wrapper;
225 ((Word *)tst->arch.m_esp)[4] = (Word)data;
226
227 if (debug)
228 VG_(printf)("starting thread at wrapper %p\n", startfunc_wrapper);
229
230 n = VG_(arena_malloc)(VG_AR_CORE, sizeof(*n));
231 n->detached = attr && !!attr->__detachstate;
232 n->threadid = threadp;
233
234 return n;
235}
236
237static void after_pthread_create(void *nonce, enum return_type rt, Word retval)
238{
239 struct pthread_create_nonce *n = (struct pthread_create_nonce *)nonce;
240 ThreadId tid = VG_(get_running_tid)();
241
242 if (n == NULL)
243 return;
244
245 if (rt == RT_RETURN && retval == 0) {
246 if (!VG_(tm_thread_exists)(tid))
247 VG_(tm_thread_create)(tid, get_pthread_mapping(*n->threadid),
248 n->detached);
249 else {
250 if (n->detached)
251 VG_(tm_thread_detach)(tid);
252 /* XXX set creator tid as well? */
253 }
254 }
255
256 VG_(arena_free)(VG_AR_CORE, n);
257
258 LEAVE(pthread_create, rt, retval);
259}
260
261static void *before_pthread_join(va_list va)
262{
263 pthread_t pt_joinee = va_arg(va, pthread_t);
264 ThreadId joinee;
265
266 if (!check_wrappings())
267 return NULL;
268
269 ENTER(pthread_join);
270
271 joinee = get_pthread_mapping(pt_joinee);
272
273 VG_(tm_thread_join)(VG_(get_running_tid)(), joinee);
274
275 return NULL;
276}
277
278static void after_pthread_join(void *v, enum return_type rt, Word retval)
279{
280 /* nothing to be done? */
281 if (!check_wrappings())
282 return;
283
284 LEAVE(pthread_join, rt, retval);
285}
286
287struct pthread_detach_data {
288 pthread_t id;
289};
290
291static void *before_pthread_detach(va_list va)
292{
293 pthread_t id = va_arg(va, pthread_t);
294 struct pthread_detach_data *data;
295
296 if (!check_wrappings())
297 return NULL;
298
299 ENTER(pthread_detach);
300
301 data = VG_(arena_malloc)(VG_AR_CORE, sizeof(*data));
302 data->id = id;
303
304 return data;
305}
306
307static void after_pthread_detach(void *nonce, enum return_type rt, Word retval)
308{
309 struct pthread_detach_data *data = (struct pthread_detach_data *)nonce;
310 ThreadId tid;
311
312 if (data == NULL)
313 return;
314
315 tid = get_pthread_mapping(data->id);
316
317 VG_(arena_free)(VG_AR_CORE, data);
318
319 if (rt == RT_RETURN && retval == 0)
320 VG_(tm_thread_detach)(tid);
321
322 LEAVE(pthread_detach, rt, retval);
323}
324
325
326
327static void *before_pthread_self(va_list va)
328{
329 /* If pthread_t is a structure, then this might be passed a pointer
330 to the return value. On Linux/glibc, it's a simple scalar, so it is
331 returned normally. */
332 if (!check_wrappings())
333 return NULL;
334
335 ENTER(pthread_self);
336
337 check_thread_exists(VG_(get_running_tid)());
338 return NULL;
339}
340
341static void after_pthread_self(void *nonce, enum return_type rt, Word retval)
342{
343 pthread_t ret = (pthread_t)retval;
344
345 if (!check_wrappings())
346 return;
347
348 pthread_id_mapping(VG_(get_running_tid)(), (Addr)&ret, sizeof(ret));
349
350 LEAVE(pthread_self, rt, retval);
351}
352
353
354/* If a mutex hasn't been initialized, check it against all the static
355 initializers to see if it appears to have been statically
356 initialized. */
357static void check_mutex_init(ThreadId tid, pthread_mutex_t *mx)
358{
359 static const pthread_mutex_t initializers[] = {
360 PTHREAD_MUTEX_INITIALIZER,
361 PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP,
362 PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP,
363 PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP,
364 };
365 Int i;
366
367 if (VG_(tm_mutex_exists)((Addr)mx))
368 return;
369
370 VG_TRACK(pre_mem_read, Vg_CorePThread, tid, "pthread_mutex_t", (Addr)mx, sizeof(*mx));
371
372 for(i = 0; i < sizeof(initializers)/sizeof(*initializers); i++)
373 if (VG_(memcmp)(&initializers[i], mx, sizeof(*mx)) == 0) {
374 VG_(tm_mutex_init)(tid, (Addr)mx);
375 break;
376 }
377}
378
379static void *before_pthread_mutex_init(va_list va)
380{
381 pthread_mutex_t *mx = va_arg(va, pthread_mutex_t *);
382 const pthread_mutexattr_t *attr = va_arg(va, const pthread_mutexattr_t *);
383
384 if (!check_wrappings())
385 return NULL;
386
387 ENTER(pthread_mutex_init);
388
389 /* XXX look for recursive mutex */
390 /* XXX look for non-process scope */
391 (void)attr;
392
393 return mx;
394}
395
396static void after_pthread_mutex_init(void *nonce, enum return_type rt, Word retval)
397{
398 if (!check_wrappings())
399 return;
400
401 if (rt == RT_RETURN && retval == 0)
402 VG_(tm_mutex_init)(VG_(get_running_tid)(), (Addr)nonce);
403
404 LEAVE(pthread_mutex_init, rt, retval);
405}
406
407static void *before_pthread_mutex_destroy(va_list va)
408{
409 pthread_mutex_t *mx = va_arg(va, pthread_mutex_t *);
410
411 if (!check_wrappings())
412 return NULL;
413
414 ENTER(pthread_mutex_destroy);
415
416 VG_(tm_mutex_destroy)(VG_(get_running_tid)(), (Addr)mx);
417
418 return NULL;
419}
420
421static void after_pthread_mutex_destroy(void *nonce, enum return_type rt, Word retval)
422{
423 if (!check_wrappings())
424 return;
425
426 LEAVE(pthread_mutex_destroy, rt, retval);
427}
428
429static void *before_pthread_mutex_lock(va_list va)
430{
431 pthread_mutex_t *mx = va_arg(va, pthread_mutex_t *);
432
433 if (!check_wrappings())
434 return NULL;
435
436 ENTER(pthread_mutex_lock);
437
438 if (debug)
439 VG_(printf)("%d locking %p\n", VG_(get_running_tid)(), mx);
440 check_thread_exists(VG_(get_running_tid)());
441 check_mutex_init(VG_(get_running_tid)(), mx); /* mutex might be statically initialized */
442 VG_(tm_mutex_trylock)(VG_(get_running_tid)(), (Addr)mx);
443
444 return mx;
445}
446
447static void after_pthread_mutex_lock(void *nonce, enum return_type rt, Word retval)
448{
449 if (!check_wrappings())
450 return;
451
452 if (rt == RT_RETURN && retval == 0)
453 VG_(tm_mutex_acquire)(VG_(get_running_tid)(), (Addr)nonce);
454 else {
455 if (debug)
456 VG_(printf)("after mutex_lock failed: rt=%d ret=%d\n", rt, retval);
457 VG_(tm_mutex_giveup)(VG_(get_running_tid)(), (Addr)nonce);
458 }
459
460 LEAVE(pthread_mutex_lock, rt, retval);
461}
462
463static void *before_pthread_mutex_trylock(va_list va)
464{
465 pthread_mutex_t *mx = va_arg(va, pthread_mutex_t *);
466
467 if (!check_wrappings())
468 return NULL;
469
470 ENTER(pthread_mutex_trylock);
471
472 if (debug)
473 VG_(printf)("%d trylocking %p\n", VG_(get_running_tid)(), mx);
474 check_thread_exists(VG_(get_running_tid)());
475 check_mutex_init(VG_(get_running_tid)(), mx); /* mutex might be statically initialized */
476 VG_(tm_mutex_trylock)(VG_(get_running_tid)(), (Addr)mx);
477
478 return mx;
479}
480
481static void after_pthread_mutex_trylock(void *nonce, enum return_type rt, Word retval)
482{
483 if (nonce == NULL)
484 return;
485
486 if (rt == RT_RETURN && retval == 0)
487 VG_(tm_mutex_acquire)(VG_(get_running_tid)(), (Addr)nonce);
488 else {
489 if (debug)
490 VG_(printf)("after mutex_trylock failed: rt=%d ret=%d\n", rt, retval);
491 VG_(tm_mutex_giveup)(VG_(get_running_tid)(), (Addr)nonce);
492 }
493
494 LEAVE(pthread_mutex_trylock, rt, retval);
495}
496
497static void *before_pthread_mutex_unlock(va_list va)
498{
499 pthread_mutex_t *mx = va_arg(va, pthread_mutex_t *);
500
501 if (!check_wrappings())
502 return NULL;
503
504 ENTER(pthread_mutex_unlock);
505
506 VG_(tm_mutex_tryunlock)(VG_(get_running_tid)(), (Addr)mx);
507
508 return mx;
509}
510
511static void after_pthread_mutex_unlock(void *nonce, enum return_type rt, Word retval)
512{
513 if (nonce == NULL)
514 return;
515
516 if (rt == RT_RETURN && retval == 0)
517 VG_(tm_mutex_unlock)(VG_(get_running_tid)(), (Addr)nonce); /* complete unlock */
518 else
519 VG_(tm_mutex_acquire)(VG_(get_running_tid)(), (Addr)nonce); /* re-acquire */
520
521 LEAVE(pthread_mutex_unlock, rt, retval);
522}
523
524
525static struct pt_wraps {
526 const Char *name;
527 FuncWrapper wrapper;
528 const CodeRedirect *redir;
529} wraps[] = {
530#define WRAP(func, extra) { #func extra, { before_##func, after_##func } }
531 WRAP(pthread_create, "@@GLIBC_2.1"), /* XXX TODO: 2.0 ABI (?) */
532 WRAP(pthread_join, ""),
533 WRAP(pthread_detach, ""),
534
535 WRAP(pthread_self, ""),
536
537 WRAP(pthread_mutex_init, ""),
538 WRAP(pthread_mutex_destroy, ""),
539 WRAP(pthread_mutex_lock, ""),
540 WRAP(pthread_mutex_trylock, ""),
541 WRAP(pthread_mutex_unlock, ""),
542#undef WRAP
543};
544
545/* Check to see if all the wrappers are resolved */
546static Bool check_wrappings()
547{
548 Int i;
549 static Bool ok = True;
550 static Bool checked = False;
551
552 if (checked)
553 return ok;
554
555 for(i = 0; i < sizeof(wraps)/sizeof(*wraps); i++) {
556 if (!VG_(is_resolved)(wraps[i].redir)) {
557 VG_(message)(Vg_DebugMsg, "Pthread wrapper for \"%s\" is not resolved",
558 wraps[i].name);
559 ok = False;
560 }
561 }
562
563 if (startfunc_wrapper == 0) {
564 VG_(message)(Vg_DebugMsg, "Pthread wrapper for thread start function is not resolved");
565 ok = False;
566 }
567
568 if (!ok)
569 VG_(message)(Vg_DebugMsg, "Missing intercepts; model disabled");
570
571 checked = True;
572 return ok;
573}
574
575/*
576 Set up all the wrappers for interesting functions.
577 */
578void VG_(pthread_init)()
579{
580 Int i;
581
582 for(i = 0; i < sizeof(wraps)/sizeof(*wraps); i++) {
583 //VG_(printf)("adding pthread wrapper for %s\n", wraps[i].name);
584 wraps[i].redir = VG_(add_wrapper)("soname:libpthread.so.0",
585 wraps[i].name, &wraps[i].wrapper);
586 }
587 VG_(tm_init)();
588 VG_(tm_thread_create)(VG_INVALID_THREADID, VG_(master_tid), True);
589}
590
591#else /* !0 */
592/* Stubs for now */
sewardj2c5ffbe2005-03-12 13:32:06 +0000593//:: void VG_(pthread_init)()
594//:: {
595//:: }
596//::
597//:: void VG_(pthread_startfunc_wrapper)(Addr wrapper)
598//:: {
599//:: }
sewardjb5f6f512005-03-10 23:59:00 +0000600#endif /* 0 */
njn984a6362005-06-11 01:07:00 +0000601
602/*--------------------------------------------------------------------*/
603/*--- end ---*/
604/*--------------------------------------------------------------------*/