blob: 1d776d026518ce291e69c602e500bdc2c8165709 [file] [log] [blame]
sewardje663cb92002-04-12 10:26:32 +00001
2/* This is a replacement for the standard libpthread.so. It is loaded
3 as part of the client's image (if required) and directs pthread
4 calls through to Valgrind's request mechanism.
5
6 A couple of caveats.
7
8 1. Since it's a binary-compatible replacement for an existing library,
9 we must take care to used exactly the same data layouts, etc, as
10 the standard pthread.so does.
11
12 2. Since this runs as part of the client, there are no specific
13 restrictions on what headers etc we can include, so long as
14 this libpthread.so does not end up having dependencies on .so's
15 which the real one doesn't.
16
17 Later ... it appears we cannot call file-related stuff in libc here,
18 perhaps fair enough. Be careful what you call from here. Even exit()
19 doesn't work (gives infinite recursion and then stack overflow); hence
20 myexit(). Also fprintf doesn't seem safe.
21*/
22
23#include "valgrind.h" /* For the request-passing mechanism */
24#include "vg_include.h" /* For the VG_USERREQ__* constants */
25
26#include <pthread.h>
27#include <unistd.h>
28#include <string.h>
29
30
31/* ---------------------------------------------------------------------
32 Helpers. We have to be pretty self-sufficient.
33 ------------------------------------------------------------------ */
34
35static
36void myexit ( int arg )
37{
38 int __res;
39 __asm__ volatile ("movl %%ecx, %%ebx ; int $0x80"
40 : "=a" (__res)
41 : "0" (__NR_exit),
42 "c" (arg) );
43 /* We don't bother to mention the fact that this asm trashes %ebx,
44 since it won't return. If you ever do let it return ... fix
45 this! */
46}
47
48
49/* Give up without using printf etc, since they seem to give
50 segfaults. */
51static
52void ensure_valgrind ( char* caller )
53{
54 char* str;
55 int is_valgrind = RUNNING_ON_VALGRIND;
56 if (!is_valgrind) {
57 str = "\nvalgrind-ed process: vg_libpthread.so: "
58 "pthread call when\n";
59 write(2, str, strlen(str));
60 str = "not running on valgrind; aborting! "
61 "This is probably a bug in\n";
62 write(2, str, strlen(str));
63 str = "valgrind. Please report it to me at: "
64 "jseward@acm.org. Thanks.\n";
65 write(2, str, strlen(str));
66 str = "unexpectedly called function is: ";
67 write(2, str, strlen(str));
68 write(2, caller, strlen(caller));
69 str = "\n\n";
70 write(2, str, strlen(str));
71 myexit(1);
72 }
73}
74
75
76static
77void barf ( char* str )
78{
79 char buf[100];
80 buf[0] = 0;
81 strcat(buf, "\nvg_libpthread.so: ");
82 strcat(buf, str);
83 strcat(buf, "\n\n");
84 write(2, buf, strlen(buf));
85 myexit(1);
86}
87
88
89
90/* ---------------------------------------------------------------------
91 Pass pthread_ calls to Valgrind's request mechanism.
92 ------------------------------------------------------------------ */
93
94int
95pthread_create (pthread_t *__restrict __thread,
96 __const pthread_attr_t *__restrict __attr,
97 void *(*__start_routine) (void *),
98 void *__restrict __arg)
99{
100 int res;
101 ensure_valgrind("pthread_create");
102 VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
103 VG_USERREQ__PTHREAD_CREATE,
104 __thread, __attr, __start_routine, __arg);
105 return res;
106}
107
108
109
110int
111pthread_join (pthread_t __th, void **__thread_return)
112{
113 int res;
114 ensure_valgrind("pthread_join");
115 VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
116 VG_USERREQ__PTHREAD_JOIN,
117 __th, __thread_return, 0, 0);
118 return res;
119}
120
121
122/* What are these? Anybody know? I don't. */
123
124void _pthread_cleanup_push_defer ( void )
125{
126 // char* str = "_pthread_cleanup_push_defer\n";
127 // write(2, str, strlen(str));
128}
129
130void _pthread_cleanup_pop_restore ( void )
131{
132 // char* str = "_pthread_cleanup_pop_restore\n";
133 // write(2, str, strlen(str));
134}
135
136
137static int thread_specific_errno[VG_N_THREADS];
138
139int* __errno_location ( void )
140{
141 int tid;
142 ensure_valgrind("__errno_location");
143 VALGRIND_MAGIC_SEQUENCE(tid, 0 /* default */,
144 VG_USERREQ__PTHREAD_GET_THREADID,
145 0, 0, 0, 0);
146 /* 'cos I'm paranoid ... */
147 if (tid < 0 || tid >= VG_N_THREADS)
148 barf("__errno_location: invalid ThreadId");
149 return & thread_specific_errno[tid];
150}
151
152
153int pthread_mutexattr_init(pthread_mutexattr_t *attr)
154{
sewardj8937c812002-04-12 20:12:20 +0000155 char* str = "IGNORED pthread_mutexattr_init\n";
156 write(2, str, strlen(str));
157 return 0;
sewardje663cb92002-04-12 10:26:32 +0000158}
159
160int pthread_mutex_init(pthread_mutex_t *mutex,
161 const pthread_mutexattr_t *mutexattr)
162{
163 int res;
sewardj8937c812002-04-12 20:12:20 +0000164 // char* str = "pthread_mutex_init\n";
165 // write(2, str, strlen(str));
sewardje663cb92002-04-12 10:26:32 +0000166 ensure_valgrind("pthread_mutex_init");
167 VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
168 VG_USERREQ__PTHREAD_MUTEX_INIT,
169 mutex, mutexattr, 0, 0);
170 return res;
171}
172
173int pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
174{
sewardj8937c812002-04-12 20:12:20 +0000175 char* str = "IGNORED pthread_mutexattr_destroy\n";
sewardje663cb92002-04-12 10:26:32 +0000176 write(2, str, strlen(str));
177 return 0;
178}
179
180int pthread_mutex_lock(pthread_mutex_t *mutex)
181{
182 int res;
183 if (!(RUNNING_ON_VALGRIND)) {
184 char* str = "pthread_mutex_lock-NOT-INSIDE-VALGRIND\n";
185 write(2, str, strlen(str));
186 return 0;
187 } else {
188 VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
189 VG_USERREQ__PTHREAD_MUTEX_LOCK,
190 mutex, 0, 0, 0);
191 return res;
192 }
193}
194
195int pthread_mutex_unlock(pthread_mutex_t *mutex)
196{
197 int res;
198 if (!(RUNNING_ON_VALGRIND)) {
199 char* str = "pthread_mutex_unlock-NOT-INSIDE-VALGRIND\n";
200 write(2, str, strlen(str));
201 return 0;
202 } else {
203 VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
204 VG_USERREQ__PTHREAD_MUTEX_UNLOCK,
205 mutex, 0, 0, 0);
206 return res;
207 }
208}
209
210pthread_t pthread_self(void)
211{
212 int tid;
213 ensure_valgrind("pthread_self");
214 VALGRIND_MAGIC_SEQUENCE(tid, 0 /* default */,
215 VG_USERREQ__PTHREAD_GET_THREADID,
216 0, 0, 0, 0);
217 if (tid < 0 || tid >= VG_N_THREADS)
218 barf("pthread_self: invalid ThreadId");
219 return tid;
220}
221
222int pthread_mutex_destroy(pthread_mutex_t *mutex)
223{
224 int res;
225 if (!(RUNNING_ON_VALGRIND)) {
226 char* str = "pthread_mutex_destroy-NOT-INSIDE-VALGRIND\n";
227 write(2, str, strlen(str));
228 return 0;
229 } else {
230 VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
231 VG_USERREQ__PTHREAD_MUTEX_DESTROY,
232 mutex, 0, 0, 0);
233 }
234 return res;
235}
236
237
238int pthread_setcanceltype(int type, int *oldtype)
239{
sewardj8937c812002-04-12 20:12:20 +0000240 char* str = "IGNORED pthread_setcanceltype\n";
sewardje663cb92002-04-12 10:26:32 +0000241 write(2, str, strlen(str));
242 return 0;
243}
244
245
246int pthread_cancel(pthread_t thread)
247{
248 int res;
249 ensure_valgrind("pthread_cancel");
250 VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
251 VG_USERREQ__PTHREAD_CANCEL,
252 thread, 0, 0, 0);
253 return res;
254}
255
256
257/* ---------------------------------------------------------------------
258 These are here (I think) because they are deemed cancellation
259 points by POSIX. For the moment we'll simply pass the call along
260 to the corresponding thread-unaware (?) libc routine.
261 ------------------------------------------------------------------ */
262
263#include <stdio.h>
264#include <stdlib.h>
265#include <signal.h>
266#include <errno.h>
267#include <sys/types.h>
268#include <sys/socket.h>
269
270extern
271int __libc_sigaction
272 (int signum,
273 const struct sigaction *act,
274 struct sigaction *oldact);
275int sigaction(int signum,
276 const struct sigaction *act,
277 struct sigaction *oldact)
278{
279 // char* str = "sigaction\n";
280 // write(2, str, strlen(str));
281 return __libc_sigaction(signum, act, oldact);
282}
283
284
285extern
286int __libc_connect(int sockfd,
287 const struct sockaddr *serv_addr,
288 socklen_t addrlen);
289int connect(int sockfd,
290 const struct sockaddr *serv_addr,
291 socklen_t addrlen)
292{
293 // char* str = "connect\n";
294 // write(2, str, strlen(str));
295 return __libc_connect(sockfd, serv_addr, addrlen);
296}
297
298
299extern
300int __libc_fcntl(int fd, int cmd, long arg);
301int fcntl(int fd, int cmd, long arg)
302{
303 // char* str = "fcntl\n";
304 // write(2, str, strlen(str));
305 return __libc_fcntl(fd, cmd, arg);
306}
307
308
309extern
310ssize_t __libc_write(int fd, const void *buf, size_t count);
311ssize_t write(int fd, const void *buf, size_t count)
312{
313 // char* str = "write\n";
314 // write(2, str, strlen(str));
315 return __libc_write(fd, buf, count);
316}
317
318
319extern
320ssize_t __libc_read(int fd, void *buf, size_t count);
321ssize_t read(int fd, void *buf, size_t count)
322{
323 // char* str = "read\n";
324 // write(2, str, strlen(str));
325 return __libc_read(fd, buf, count);
326}
327
328
329extern
330int __libc_open(const char *pathname, int flags);
331int open(const char *pathname, int flags)
332{
333 // char* str = "open\n";
334 // write(2, str, strlen(str));
335 return __libc_open(pathname, flags);
336}
337
338
339extern
340int __libc_close(int fd);
341int close(int fd)
342{
343 // char* str = "open\n";
344 // write(2, str, strlen(str));
345 return __libc_close(fd);
346}
347
348
349extern
350int __libc_accept(int s, struct sockaddr *addr, socklen_t *addrlen);
351int accept(int s, struct sockaddr *addr, socklen_t *addrlen)
352{
353 // char* str = "accept\n";
354 // write(2, str, strlen(str));
355 return __libc_accept(s, addr, addrlen);
356}
357
358
359extern
360pid_t __libc_fork(void);
361pid_t fork(void)
362{
363 // char* str = "fork\n";
364 // write(2, str, strlen(str));
365 return __libc_fork();
366}
367
368
369extern
370pid_t __libc_waitpid(pid_t pid, int *status, int options);
371pid_t waitpid(pid_t pid, int *status, int options)
372{
373 // char* str = "waitpid\n";
374 // write(2, str, strlen(str));
375 return __libc_waitpid(pid, status, options);
376}
377
378
379extern
380int __libc_nanosleep(const struct timespec *req, struct timespec *rem);
381int nanosleep(const struct timespec *req, struct timespec *rem)
382{
383 return __libc_nanosleep(req, rem);
384}
385
386extern
387int __libc_fsync(int fd);
388int fsync(int fd)
389{
390 return __libc_fsync(fd);
391}
392
sewardj70c75362002-04-13 04:18:32 +0000393extern
394off_t __libc_lseek(int fildes, off_t offset, int whence);
395off_t lseek(int fildes, off_t offset, int whence)
396{
397 return __libc_lseek(fildes, offset, whence);
398}
399
400/*--------------------------------------------------*/
401
sewardje663cb92002-04-12 10:26:32 +0000402/* I've no idea what these are, but they get called quite a lot.
403 Anybody know? */
404
405#undef _IO_flockfile
406void _IO_flockfile ( _IO_FILE * file )
407{
408 // char* str = "_IO_flockfile\n";
409 // write(2, str, strlen(str));
410 // barf("_IO_flockfile");
411}
412
413#undef _IO_funlockfile
414void _IO_funlockfile ( _IO_FILE * file )
415{
416 // char* str = "_IO_funlockfile\n";
417 // write(2, str, strlen(str));
418 //barf("_IO_funlockfile");
419}
420
sewardj08a4c3f2002-04-13 03:45:44 +0000421/*--------------------------------------------------*/
422
423#include "vg_kerneliface.h"
424
425static
426__inline__
427int is_kerror ( int res )
428{
429 if (res >= -4095 && res <= -1)
430 return 1;
431 else
432 return 0;
433}
434
435
436static
437int my_do_syscall1 ( int syscallno, int arg1 )
438{
439 int __res;
440 __asm__ volatile ("pushl %%ebx; movl %%edx,%%ebx ; int $0x80 ; popl %%ebx"
441 : "=a" (__res)
442 : "0" (syscallno),
443 "d" (arg1) );
444 return __res;
445}
446
447static
448int my_do_syscall2 ( int syscallno,
449 int arg1, int arg2 )
450{
451 int __res;
452 __asm__ volatile ("pushl %%ebx; movl %%edx,%%ebx ; int $0x80 ; popl %%ebx"
453 : "=a" (__res)
454 : "0" (syscallno),
455 "d" (arg1),
456 "c" (arg2) );
457 return __res;
458}
459
460static
461int do_syscall_select( int n,
462 vki_fd_set* readfds,
463 vki_fd_set* writefds,
464 vki_fd_set* exceptfds,
465 struct vki_timeval * timeout )
466{
467 int res;
468 int args[5];
469 args[0] = n;
470 args[1] = (int)readfds;
471 args[2] = (int)writefds;
472 args[3] = (int)exceptfds;
473 args[4] = (int)timeout;
474 res = my_do_syscall1(__NR_select, (int)(&(args[0])) );
475 if (is_kerror(res)) {
476 * (__errno_location()) = -res;
477 return -1;
478 } else {
479 return res;
480 }
481}
482
483
484/* This is a wrapper round select(), which makes it thread-safe,
485 meaning that only this thread will block, rather than the entire
486 process. This wrapper in turn depends on nanosleep() not to block
487 the entire process, but I think (hope? suspect?) that POSIX
488 pthreads guarantees that to be the case.
489
490 Basic idea is: modify the timeout parameter to select so that it
491 returns immediately. Poll like this until select returns non-zero,
492 indicating something interesting happened, or until our time is up.
493 Space out the polls with nanosleeps of say 20 milliseconds, which
494 is required to be nonblocking; this allows other threads to run.
495*/
496#include <assert.h>
497
498
499int select ( int n,
500 fd_set *rfds,
501 fd_set *wfds,
502 fd_set *xfds,
503 struct timeval *timeout )
504{
505 int res;
506 fd_set rfds_copy;
507 fd_set wfds_copy;
508 fd_set xfds_copy;
509 struct vki_timeval t_now;
510 struct vki_timeval t_end;
511 struct vki_timeval zero_timeout;
512 struct vki_timespec nanosleep_interval;
513
514 ensure_valgrind("select");
515
516 /* We assume that the kernel and libc data layouts are identical
517 for the following types. These asserts provide a crude
518 check. */
519 if (sizeof(fd_set) != sizeof(vki_fd_set)
520 || sizeof(struct timeval) != sizeof(struct vki_timeval))
521 barf("valgrind's hacky non-blocking select(): data sizes error");
522
523 /* If a zero timeout specified, this call is harmless. */
524 if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0)
525 return do_syscall_select( n, (vki_fd_set*)rfds,
526 (vki_fd_set*)wfds,
527 (vki_fd_set*)xfds,
528 (struct vki_timeval*)timeout);
529
530 /* If a timeout was specified, set t_end to be the end wallclock
531 time. */
532 if (timeout) {
533 res = my_do_syscall2(__NR_gettimeofday, (int)&t_now, (int)NULL);
534 assert(res == 0);
535 t_end = t_now;
536 t_end.tv_usec += timeout->tv_usec;
537 t_end.tv_sec += timeout->tv_sec;
538 if (t_end.tv_usec >= 1000000) {
539 t_end.tv_usec -= 1000000;
540 t_end.tv_sec += 1;
541 }
542 /* Stay sane ... */
543 assert (t_end.tv_sec > t_now.tv_sec
544 || (t_end.tv_sec == t_now.tv_sec
545 && t_end.tv_usec >= t_now.tv_usec));
546 }
547
548 /* fprintf(stderr, "MY_SELECT: before loop\n"); */
549
550 /* Either timeout == NULL, meaning wait indefinitely, or timeout !=
551 NULL, in which case t_end holds the end time. */
552 while (1) {
553 if (timeout) {
554 res = my_do_syscall2(__NR_gettimeofday, (int)&t_now, (int)NULL);
555 assert(res == 0);
556 if (t_now.tv_sec > t_end.tv_sec
557 || (t_now.tv_sec == t_end.tv_sec
558 && t_now.tv_usec > t_end.tv_usec)) {
559 /* timeout; nothing interesting happened. */
560 if (rfds) FD_ZERO(rfds);
561 if (wfds) FD_ZERO(wfds);
562 if (xfds) FD_ZERO(xfds);
563 return 0;
564 }
565 }
566
567 /* These could be trashed each time round the loop, so restore
568 them each time. */
569 if (rfds) rfds_copy = *rfds;
570 if (wfds) wfds_copy = *wfds;
571 if (xfds) xfds_copy = *xfds;
572
573 zero_timeout.tv_sec = zero_timeout.tv_usec = 0;
574
575 res = do_syscall_select( n,
576 rfds ? (vki_fd_set*)(&rfds_copy) : NULL,
577 wfds ? (vki_fd_set*)(&wfds_copy) : NULL,
578 xfds ? (vki_fd_set*)(&xfds_copy) : NULL,
579 & zero_timeout );
580 if (res < 0) {
581 /* some kind of error (including EINTR); errno is set, so just
582 return. The sets are unspecified in this case. */
583 return res;
584 }
585 if (res > 0) {
586 /* one or more fds is ready. Copy out resulting sets and
587 return. */
588 if (rfds) *rfds = rfds_copy;
589 if (wfds) *wfds = wfds_copy;
590 if (xfds) *xfds = xfds_copy;
591 return res;
592 }
593 /* fprintf(stderr, "MY_SELECT: nanosleep\n"); */
594 /* nanosleep and go round again */
595 nanosleep_interval.tv_sec = 0;
596 nanosleep_interval.tv_nsec = 20 * 1000 * 1000; /* 20 milliseconds */
597 /* It's critical here that valgrind's nanosleep implementation
598 is nonblocking. */
599 (void)my_do_syscall2(__NR_nanosleep,
600 (int)(&nanosleep_interval), (int)NULL);
601 }
602}