New files to support pthreads.
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@51 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/vg_libpthread.c b/vg_libpthread.c
new file mode 100644
index 0000000..e8d6043
--- /dev/null
+++ b/vg_libpthread.c
@@ -0,0 +1,411 @@
+
+/* This is a replacement for the standard libpthread.so. It is loaded
+ as part of the client's image (if required) and directs pthread
+ calls through to Valgrind's request mechanism.
+
+ A couple of caveats.
+
+ 1. Since it's a binary-compatible replacement for an existing library,
+ we must take care to used exactly the same data layouts, etc, as
+ the standard pthread.so does.
+
+ 2. Since this runs as part of the client, there are no specific
+ restrictions on what headers etc we can include, so long as
+ this libpthread.so does not end up having dependencies on .so's
+ which the real one doesn't.
+
+ Later ... it appears we cannot call file-related stuff in libc here,
+ perhaps fair enough. Be careful what you call from here. Even exit()
+ doesn't work (gives infinite recursion and then stack overflow); hence
+ myexit(). Also fprintf doesn't seem safe.
+*/
+
+#include "valgrind.h" /* For the request-passing mechanism */
+#include "vg_include.h" /* For the VG_USERREQ__* constants */
+
+#include <pthread.h>
+#include <unistd.h>
+#include <string.h>
+
+
+/* ---------------------------------------------------------------------
+ Helpers. We have to be pretty self-sufficient.
+ ------------------------------------------------------------------ */
+
+static
+void myexit ( int arg )
+{
+ int __res;
+ __asm__ volatile ("movl %%ecx, %%ebx ; int $0x80"
+ : "=a" (__res)
+ : "0" (__NR_exit),
+ "c" (arg) );
+ /* We don't bother to mention the fact that this asm trashes %ebx,
+ since it won't return. If you ever do let it return ... fix
+ this! */
+}
+
+
+/* Give up without using printf etc, since they seem to give
+ segfaults. */
+static
+void ensure_valgrind ( char* caller )
+{
+ char* str;
+ int is_valgrind = RUNNING_ON_VALGRIND;
+ if (!is_valgrind) {
+ str = "\nvalgrind-ed process: vg_libpthread.so: "
+ "pthread call when\n";
+ write(2, str, strlen(str));
+ str = "not running on valgrind; aborting! "
+ "This is probably a bug in\n";
+ write(2, str, strlen(str));
+ str = "valgrind. Please report it to me at: "
+ "jseward@acm.org. Thanks.\n";
+ write(2, str, strlen(str));
+ str = "unexpectedly called function is: ";
+ write(2, str, strlen(str));
+ write(2, caller, strlen(caller));
+ str = "\n\n";
+ write(2, str, strlen(str));
+ myexit(1);
+ }
+}
+
+
+static
+void barf ( char* str )
+{
+ char buf[100];
+ buf[0] = 0;
+ strcat(buf, "\nvg_libpthread.so: ");
+ strcat(buf, str);
+ strcat(buf, "\n\n");
+ write(2, buf, strlen(buf));
+ myexit(1);
+}
+
+
+
+/* ---------------------------------------------------------------------
+ Pass pthread_ calls to Valgrind's request mechanism.
+ ------------------------------------------------------------------ */
+
+int
+pthread_create (pthread_t *__restrict __thread,
+ __const pthread_attr_t *__restrict __attr,
+ void *(*__start_routine) (void *),
+ void *__restrict __arg)
+{
+ int res;
+ ensure_valgrind("pthread_create");
+ VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+ VG_USERREQ__PTHREAD_CREATE,
+ __thread, __attr, __start_routine, __arg);
+ return res;
+}
+
+
+
+int
+pthread_join (pthread_t __th, void **__thread_return)
+{
+ int res;
+ ensure_valgrind("pthread_join");
+ VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+ VG_USERREQ__PTHREAD_JOIN,
+ __th, __thread_return, 0, 0);
+ return res;
+}
+
+
+/* What are these? Anybody know? I don't. */
+
+void _pthread_cleanup_push_defer ( void )
+{
+ // char* str = "_pthread_cleanup_push_defer\n";
+ // write(2, str, strlen(str));
+}
+
+void _pthread_cleanup_pop_restore ( void )
+{
+ // char* str = "_pthread_cleanup_pop_restore\n";
+ // write(2, str, strlen(str));
+}
+
+
+static int thread_specific_errno[VG_N_THREADS];
+
+int* __errno_location ( void )
+{
+ int tid;
+ ensure_valgrind("__errno_location");
+ VALGRIND_MAGIC_SEQUENCE(tid, 0 /* default */,
+ VG_USERREQ__PTHREAD_GET_THREADID,
+ 0, 0, 0, 0);
+ /* 'cos I'm paranoid ... */
+ if (tid < 0 || tid >= VG_N_THREADS)
+ barf("__errno_location: invalid ThreadId");
+ return & thread_specific_errno[tid];
+}
+
+
+int pthread_mutexattr_init(pthread_mutexattr_t *attr)
+{
+ char* str = "pthread_mutexattr_init\n";
+ write(2, str, strlen(str));
+ return 0;
+}
+
+int pthread_mutex_init(pthread_mutex_t *mutex,
+ const pthread_mutexattr_t *mutexattr)
+{
+ int res;
+ char* str = "pthread_mutex_init\n";
+ write(2, str, strlen(str));
+ ensure_valgrind("pthread_mutex_init");
+ VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+ VG_USERREQ__PTHREAD_MUTEX_INIT,
+ mutex, mutexattr, 0, 0);
+ return res;
+}
+
+int pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
+{
+ char* str = "pthread_mutexattr_destroy\n";
+ write(2, str, strlen(str));
+ return 0;
+}
+
+int pthread_mutex_lock(pthread_mutex_t *mutex)
+{
+ int res;
+ if (!(RUNNING_ON_VALGRIND)) {
+ char* str = "pthread_mutex_lock-NOT-INSIDE-VALGRIND\n";
+ write(2, str, strlen(str));
+ return 0;
+ } else {
+ VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+ VG_USERREQ__PTHREAD_MUTEX_LOCK,
+ mutex, 0, 0, 0);
+ return res;
+ }
+}
+
+int pthread_mutex_unlock(pthread_mutex_t *mutex)
+{
+ int res;
+ if (!(RUNNING_ON_VALGRIND)) {
+ char* str = "pthread_mutex_unlock-NOT-INSIDE-VALGRIND\n";
+ write(2, str, strlen(str));
+ return 0;
+ } else {
+ VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+ VG_USERREQ__PTHREAD_MUTEX_UNLOCK,
+ mutex, 0, 0, 0);
+ return res;
+ }
+}
+
+pthread_t pthread_self(void)
+{
+ int tid;
+ ensure_valgrind("pthread_self");
+ VALGRIND_MAGIC_SEQUENCE(tid, 0 /* default */,
+ VG_USERREQ__PTHREAD_GET_THREADID,
+ 0, 0, 0, 0);
+ if (tid < 0 || tid >= VG_N_THREADS)
+ barf("pthread_self: invalid ThreadId");
+ return tid;
+}
+
+int pthread_mutex_destroy(pthread_mutex_t *mutex)
+{
+ int res;
+ if (!(RUNNING_ON_VALGRIND)) {
+ char* str = "pthread_mutex_destroy-NOT-INSIDE-VALGRIND\n";
+ write(2, str, strlen(str));
+ return 0;
+ } else {
+ VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+ VG_USERREQ__PTHREAD_MUTEX_DESTROY,
+ mutex, 0, 0, 0);
+ }
+ return res;
+}
+
+
+int pthread_setcanceltype(int type, int *oldtype)
+{
+ char* str = "pthread_setcanceltype\n";
+ write(2, str, strlen(str));
+ return 0;
+}
+
+
+int pthread_cancel(pthread_t thread)
+{
+ int res;
+ ensure_valgrind("pthread_cancel");
+ VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+ VG_USERREQ__PTHREAD_CANCEL,
+ thread, 0, 0, 0);
+ return res;
+}
+
+
+/* ---------------------------------------------------------------------
+ These are here (I think) because they are deemed cancellation
+ points by POSIX. For the moment we'll simply pass the call along
+ to the corresponding thread-unaware (?) libc routine.
+ ------------------------------------------------------------------ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+extern
+int __libc_sigaction
+ (int signum,
+ const struct sigaction *act,
+ struct sigaction *oldact);
+int sigaction(int signum,
+ const struct sigaction *act,
+ struct sigaction *oldact)
+{
+ // char* str = "sigaction\n";
+ // write(2, str, strlen(str));
+ return __libc_sigaction(signum, act, oldact);
+}
+
+
+extern
+int __libc_connect(int sockfd,
+ const struct sockaddr *serv_addr,
+ socklen_t addrlen);
+int connect(int sockfd,
+ const struct sockaddr *serv_addr,
+ socklen_t addrlen)
+{
+ // char* str = "connect\n";
+ // write(2, str, strlen(str));
+ return __libc_connect(sockfd, serv_addr, addrlen);
+}
+
+
+extern
+int __libc_fcntl(int fd, int cmd, long arg);
+int fcntl(int fd, int cmd, long arg)
+{
+ // char* str = "fcntl\n";
+ // write(2, str, strlen(str));
+ return __libc_fcntl(fd, cmd, arg);
+}
+
+
+extern
+ssize_t __libc_write(int fd, const void *buf, size_t count);
+ssize_t write(int fd, const void *buf, size_t count)
+{
+ // char* str = "write\n";
+ // write(2, str, strlen(str));
+ return __libc_write(fd, buf, count);
+}
+
+
+extern
+ssize_t __libc_read(int fd, void *buf, size_t count);
+ssize_t read(int fd, void *buf, size_t count)
+{
+ // char* str = "read\n";
+ // write(2, str, strlen(str));
+ return __libc_read(fd, buf, count);
+}
+
+
+extern
+int __libc_open(const char *pathname, int flags);
+int open(const char *pathname, int flags)
+{
+ // char* str = "open\n";
+ // write(2, str, strlen(str));
+ return __libc_open(pathname, flags);
+}
+
+
+extern
+int __libc_close(int fd);
+int close(int fd)
+{
+ // char* str = "open\n";
+ // write(2, str, strlen(str));
+ return __libc_close(fd);
+}
+
+
+extern
+int __libc_accept(int s, struct sockaddr *addr, socklen_t *addrlen);
+int accept(int s, struct sockaddr *addr, socklen_t *addrlen)
+{
+ // char* str = "accept\n";
+ // write(2, str, strlen(str));
+ return __libc_accept(s, addr, addrlen);
+}
+
+
+extern
+pid_t __libc_fork(void);
+pid_t fork(void)
+{
+ // char* str = "fork\n";
+ // write(2, str, strlen(str));
+ return __libc_fork();
+}
+
+
+extern
+pid_t __libc_waitpid(pid_t pid, int *status, int options);
+pid_t waitpid(pid_t pid, int *status, int options)
+{
+ // char* str = "waitpid\n";
+ // write(2, str, strlen(str));
+ return __libc_waitpid(pid, status, options);
+}
+
+
+extern
+int __libc_nanosleep(const struct timespec *req, struct timespec *rem);
+int nanosleep(const struct timespec *req, struct timespec *rem)
+{
+ return __libc_nanosleep(req, rem);
+}
+
+extern
+int __libc_fsync(int fd);
+int fsync(int fd)
+{
+ return __libc_fsync(fd);
+}
+
+/* I've no idea what these are, but they get called quite a lot.
+ Anybody know? */
+
+#undef _IO_flockfile
+void _IO_flockfile ( _IO_FILE * file )
+{
+ // char* str = "_IO_flockfile\n";
+ // write(2, str, strlen(str));
+ // barf("_IO_flockfile");
+}
+
+#undef _IO_funlockfile
+void _IO_funlockfile ( _IO_FILE * file )
+{
+ // char* str = "_IO_funlockfile\n";
+ // write(2, str, strlen(str));
+ //barf("_IO_funlockfile");
+}
+