summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorKostya Serebryany <kcc@google.com>2013-02-27 11:22:40 +0000
committerKostya Serebryany <kcc@google.com>2013-02-27 11:22:40 +0000
commit6fb47af2d2d305adbfc3d41bea589d1527a364a9 (patch)
treebf89a18e0c1e0b5585d7fe41d94fb79bcf515580 /lib
parent1d2ed3ab71a574aaa0f4cea21333ae2bb786c93a (diff)
add Linux syscall wrappers and ThreadLister to sanitizer_common
ThreadLister is a Linux-specific class for obtaining the thread IDs of a process from procfs (/proc/<pid>/task/). It will be used by leak checking code. Also add several syscall wrappers which will be required by the same code that uses ThreadLister, but are not used in ThreadLister itself. Patch by Sergey Matveev git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@176179 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib')
-rw-r--r--lib/interception/interception.h20
-rw-r--r--lib/sanitizer_common/CMakeLists.txt1
-rw-r--r--lib/sanitizer_common/sanitizer_internal_defs.h10
-rw-r--r--lib/sanitizer_common/sanitizer_libc.h5
-rw-r--r--lib/sanitizer_common/sanitizer_linux.cc111
-rw-r--r--lib/sanitizer_common/sanitizer_linux.h53
-rw-r--r--lib/sanitizer_common/tests/CMakeLists.txt1
-rw-r--r--lib/sanitizer_common/tests/sanitizer_linux_test.cc109
8 files changed, 296 insertions, 14 deletions
diff --git a/lib/interception/interception.h b/lib/interception/interception.h
index b6be72c69..2ccc90387 100644
--- a/lib/interception/interception.h
+++ b/lib/interception/interception.h
@@ -23,20 +23,12 @@
// These typedefs should be used only in the interceptor definitions to replace
// the standard system types (e.g. SSIZE_T instead of ssize_t)
-typedef __sanitizer::uptr SIZE_T;
-typedef __sanitizer::sptr SSIZE_T;
-typedef __sanitizer::sptr PTRDIFF_T;
-typedef __sanitizer::s64 INTMAX_T;
-// WARNING: OFF_T may be different from OS type off_t, depending on the value of
-// _FILE_OFFSET_BITS. This definition of OFF_T matches the ABI of system calls
-// like pread and mmap, as opposed to pread64 and mmap64.
-// Mac and Linux/x86-64 are special.
-#if defined(__APPLE__) || (defined(__linux__) && defined(__x86_64__))
-typedef __sanitizer::u64 OFF_T;
-#else
-typedef __sanitizer::uptr OFF_T;
-#endif
-typedef __sanitizer::u64 OFF64_T;
+typedef __sanitizer::uptr SIZE_T;
+typedef __sanitizer::sptr SSIZE_T;
+typedef __sanitizer::sptr PTRDIFF_T;
+typedef __sanitizer::s64 INTMAX_T;
+typedef __sanitizer::OFF_T OFF_T;
+typedef __sanitizer::OFF64_T OFF64_T;
// How to add an interceptor:
// Suppose you need to wrap/replace system function (generally, from libc):
diff --git a/lib/sanitizer_common/CMakeLists.txt b/lib/sanitizer_common/CMakeLists.txt
index 56aa3f730..b7628551b 100644
--- a/lib/sanitizer_common/CMakeLists.txt
+++ b/lib/sanitizer_common/CMakeLists.txt
@@ -36,6 +36,7 @@ set(SANITIZER_HEADERS
sanitizer_internal_defs.h
sanitizer_lfstack.h
sanitizer_libc.h
+ sanitizer_linux.h
sanitizer_list.h
sanitizer_mutex.h
sanitizer_placement_new.h
diff --git a/lib/sanitizer_common/sanitizer_internal_defs.h b/lib/sanitizer_common/sanitizer_internal_defs.h
index 083761bbc..e052cbdcc 100644
--- a/lib/sanitizer_common/sanitizer_internal_defs.h
+++ b/lib/sanitizer_common/sanitizer_internal_defs.h
@@ -66,6 +66,16 @@ typedef signed int s32;
typedef signed long long s64; // NOLINT
typedef int fd_t;
+// WARNING: OFF_T may be different from OS type off_t, depending on the value of
+// _FILE_OFFSET_BITS. This definition of OFF_T matches the ABI of system calls
+// like pread and mmap, as opposed to pread64 and mmap64.
+// Mac and Linux/x86-64 are special.
+#if defined(__APPLE__) || (defined(__linux__) && defined(__x86_64__))
+typedef u64 OFF_T;
+#else
+typedef uptr OFF_T;
+#endif
+typedef u64 OFF64_T;
} // namespace __sanitizer
extern "C" {
diff --git a/lib/sanitizer_common/sanitizer_libc.h b/lib/sanitizer_common/sanitizer_libc.h
index d4e954c26..7c2a1b857 100644
--- a/lib/sanitizer_common/sanitizer_libc.h
+++ b/lib/sanitizer_common/sanitizer_libc.h
@@ -80,6 +80,11 @@ int internal_fstat(fd_t fd, void *buf);
int internal_dup2(int oldfd, int newfd);
uptr internal_readlink(const char *path, char *buf, uptr bufsize);
void NORETURN internal__exit(int exitcode);
+OFF_T internal_lseek(fd_t fd, OFF_T offset, int whence);
+
+long internal_ptrace(int request, int pid, void *addr, void *data);
+int internal_waitpid(int pid, int *status, int options);
+int internal_getppid();
// Threading
int internal_sched_yield();
diff --git a/lib/sanitizer_common/sanitizer_linux.cc b/lib/sanitizer_common/sanitizer_linux.cc
index 11cec22f9..6e29437f9 100644
--- a/lib/sanitizer_common/sanitizer_linux.cc
+++ b/lib/sanitizer_common/sanitizer_linux.cc
@@ -16,6 +16,7 @@
#include "sanitizer_common.h"
#include "sanitizer_internal_defs.h"
#include "sanitizer_libc.h"
+#include "sanitizer_linux.h"
#include "sanitizer_mutex.h"
#include "sanitizer_placement_new.h"
#include "sanitizer_procmaps.h"
@@ -25,7 +26,9 @@
#include <pthread.h>
#include <sched.h>
#include <sys/mman.h>
+#include <sys/ptrace.h>
#include <sys/resource.h>
+#include <sys/signal.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/time.h>
@@ -539,6 +542,114 @@ void BlockingMutex::Unlock() {
syscall(__NR_futex, m, FUTEX_WAKE, 1, 0, 0, 0);
}
+// ----------------- sanitizer_linux.h
+// The actual size of this structure is specified by d_reclen.
+// Note that getdents64 uses a different structure format. We only provide the
+// 32-bit syscall here.
+struct linux_dirent {
+ unsigned long d_ino;
+ unsigned long d_off;
+ unsigned short d_reclen;
+ char d_name[256];
+};
+
+// Syscall wrappers.
+long internal_ptrace(int request, int pid, void *addr, void *data) {
+ return syscall(__NR_ptrace, request, pid, addr, data);
+}
+
+int internal_waitpid(int pid, int *status, int options) {
+ return syscall(__NR_wait4, pid, status, options, NULL /* rusage */);
+}
+
+int internal_getppid() {
+ return syscall(__NR_getppid);
+}
+
+int internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count) {
+ return syscall(__NR_getdents, fd, dirp, count);
+}
+
+OFF_T internal_lseek(fd_t fd, OFF_T offset, int whence) {
+ return syscall(__NR_lseek, fd, offset, whence);
+}
+
+int internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5) {
+ return syscall(__NR_prctl, option, arg2, arg3, arg4, arg5);
+}
+
+int internal_sigaltstack(const struct sigaltstack *ss,
+ struct sigaltstack *oss) {
+ return syscall(__NR_sigaltstack, ss, oss);
+}
+
+
+// ThreadLister implementation.
+ThreadLister::ThreadLister(int pid)
+ : pid_(pid),
+ descriptor_(-1),
+ error_(true),
+ entry_((linux_dirent *)buffer_),
+ bytes_read_(0) {
+ char task_directory_path[80];
+ internal_snprintf(task_directory_path, sizeof(task_directory_path),
+ "/proc/%d/task/", pid);
+ descriptor_ = internal_open(task_directory_path, O_RDONLY | O_DIRECTORY);
+ if (descriptor_ < 0) {
+ error_ = true;
+ Report("Can't open /proc/%d/task for reading.\n", pid);
+ } else {
+ error_ = false;
+ }
+}
+
+int ThreadLister::GetNextTID() {
+ int tid = -1;
+ do {
+ if (error_)
+ return -1;
+ if ((char *)entry_ >= &buffer_[bytes_read_] && !GetDirectoryEntries())
+ return -1;
+ if (entry_->d_ino != 0 && entry_->d_name[0] >= '0' &&
+ entry_->d_name[0] <= '9') {
+ // Found a valid tid.
+ tid = (int)internal_atoll(entry_->d_name);
+ }
+ entry_ = (struct linux_dirent *)(((char *)entry_) + entry_->d_reclen);
+ } while (tid < 0);
+ return tid;
+}
+
+void ThreadLister::Reset() {
+ if (error_ || descriptor_ < 0)
+ return;
+ internal_lseek(descriptor_, 0, SEEK_SET);
+}
+
+ThreadLister::~ThreadLister() {
+ if (descriptor_ >= 0)
+ internal_close(descriptor_);
+}
+
+bool ThreadLister::error() { return error_; }
+
+bool ThreadLister::GetDirectoryEntries() {
+ CHECK_GE(descriptor_, 0);
+ CHECK_NE(error_, true);
+ bytes_read_ = internal_getdents(descriptor_,
+ (struct linux_dirent *)buffer_,
+ sizeof(buffer_));
+ if (bytes_read_ < 0) {
+ Report("Can't read directory entries from /proc/%d/task.\n", pid_);
+ error_ = true;
+ return false;
+ } else if (bytes_read_ == 0) {
+ return false;
+ }
+ entry_ = (struct linux_dirent *)buffer_;
+ return true;
+}
+
} // namespace __sanitizer
#endif // __linux__
diff --git a/lib/sanitizer_common/sanitizer_linux.h b/lib/sanitizer_common/sanitizer_linux.h
new file mode 100644
index 000000000..b4ac31082
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_linux.h
@@ -0,0 +1,53 @@
+//===-- sanitizer_linux.h ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Linux-specific syscall wrappers and classes.
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_LINUX_H
+#define SANITIZER_LINUX_H
+
+#include "sanitizer_internal_defs.h"
+
+struct sigaltstack;
+
+namespace __sanitizer {
+// Dirent structure for getdents(). Note that this structure is different from
+// the one in <dirent.h>, which is used by readdir().
+struct linux_dirent;
+
+// Syscall wrappers.
+int internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count);
+int internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5);
+int internal_sigaltstack(const struct sigaltstack *ss, struct sigaltstack *oss);
+
+// This class reads thread IDs from /proc/<pid>/task using only syscalls.
+class ThreadLister {
+ public:
+ explicit ThreadLister(int pid);
+ ~ThreadLister();
+ // GetNextTID returns -1 if the list of threads is exhausted, or if there has
+ // been an error.
+ int GetNextTID();
+ void Reset();
+ bool error();
+
+ private:
+ bool GetDirectoryEntries();
+
+ int pid_;
+ int descriptor_;
+ char buffer_[4096];
+ bool error_;
+ struct linux_dirent* entry_;
+ int bytes_read_;
+};
+} // namespace __sanitizer
+
+#endif // SANITIZER_LINUX_H
diff --git a/lib/sanitizer_common/tests/CMakeLists.txt b/lib/sanitizer_common/tests/CMakeLists.txt
index 111dfeefd..5b414b2f0 100644
--- a/lib/sanitizer_common/tests/CMakeLists.txt
+++ b/lib/sanitizer_common/tests/CMakeLists.txt
@@ -5,6 +5,7 @@ set(SANITIZER_UNITTESTS
sanitizer_common_test.cc
sanitizer_flags_test.cc
sanitizer_libc_test.cc
+ sanitizer_linux_test.cc
sanitizer_list_test.cc
sanitizer_mutex_test.cc
sanitizer_printf_test.cc
diff --git a/lib/sanitizer_common/tests/sanitizer_linux_test.cc b/lib/sanitizer_common/tests/sanitizer_linux_test.cc
new file mode 100644
index 000000000..953b5c7ae
--- /dev/null
+++ b/lib/sanitizer_common/tests/sanitizer_linux_test.cc
@@ -0,0 +1,109 @@
+//===-- sanitizer_linux_test.cc -------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Tests for sanitizer_linux.h
+//
+//===----------------------------------------------------------------------===//
+
+#ifdef __linux__
+
+#include "sanitizer_common/sanitizer_linux.h"
+#include "gtest/gtest.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+
+#include <pthread.h>
+#include <sched.h>
+
+#include <set>
+
+namespace __sanitizer {
+// In a single-threaded process, ThreadLister should produce the TID (which
+// coincides with the PID) of the current task.
+TEST(SanitizerLinux, ThreadListerSingleThread) {
+ pid_t pid = getpid();
+ ThreadLister thread_lister(pid);
+ EXPECT_FALSE(thread_lister.error());
+ EXPECT_EQ(thread_lister.GetNextTID(), pid);
+ EXPECT_FALSE(thread_lister.error());
+ EXPECT_LT(thread_lister.GetNextTID(), 0);
+ EXPECT_FALSE(thread_lister.error());
+}
+
+static pthread_cond_t thread_exit_cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t thread_exit_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t tid_reported_cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t tid_reported_mutex = PTHREAD_MUTEX_INITIALIZER;
+static bool thread_exit;
+
+void *TIDReporterThread(void *tid_storage) {
+ pthread_mutex_lock(&tid_reported_mutex);
+ *(pid_t *)tid_storage = GetTid();
+ pthread_cond_broadcast(&tid_reported_cond);
+ pthread_mutex_unlock(&tid_reported_mutex);
+
+ pthread_mutex_lock(&thread_exit_mutex);
+ while (!thread_exit)
+ pthread_cond_wait(&thread_exit_cond, &thread_exit_mutex);
+ pthread_mutex_unlock(&thread_exit_mutex);
+ return NULL;
+}
+
+// In a process with multiple threads, ThreadLister should produce their TIDs
+// in some order.
+// Calling ThreadLister::Reset() should not change this.
+TEST(SanitizerLinux, ThreadListerMultiThreaded) {
+ const uptr kThreadCount = 20; // does not include the main thread
+ pthread_t thread_ids[kThreadCount];
+ pid_t thread_tids[kThreadCount];
+ pid_t pid = getpid();
+ pid_t self_tid = GetTid();
+ thread_exit = false;
+ pthread_mutex_lock(&tid_reported_mutex);
+ for (uptr i = 0; i < kThreadCount; i++) {
+ int pthread_create_result;
+ thread_tids[i] = -1;
+ pthread_create_result = pthread_create(&thread_ids[i], NULL,
+ TIDReporterThread,
+ &thread_tids[i]);
+ ASSERT_EQ(pthread_create_result, 0);
+ while (thread_tids[i] == -1)
+ pthread_cond_wait(&tid_reported_cond, &tid_reported_mutex);
+ }
+ pthread_mutex_unlock(&tid_reported_mutex);
+ std::set<pid_t> reported_tids(thread_tids, thread_tids + kThreadCount);
+ reported_tids.insert(self_tid);
+
+ ThreadLister thread_lister(pid);
+ // There's a Reset() call between the first and second iteration.
+ for (uptr i = 0; i < 2; i++) {
+ std::set<pid_t> listed_tids;
+
+ EXPECT_FALSE(thread_lister.error());
+ for (uptr i = 0; i < kThreadCount + 1; i++) {
+ pid_t tid = thread_lister.GetNextTID();
+ EXPECT_GE(tid, 0);
+ EXPECT_FALSE(thread_lister.error());
+ listed_tids.insert(tid);
+ }
+ pid_t tid = thread_lister.GetNextTID();
+ EXPECT_LT(tid, 0);
+ EXPECT_FALSE(thread_lister.error());
+
+ EXPECT_EQ(listed_tids, reported_tids);
+ thread_lister.Reset();
+ }
+ pthread_mutex_lock(&thread_exit_mutex);
+ thread_exit = true;
+ pthread_cond_broadcast(&thread_exit_cond);
+ pthread_mutex_unlock(&thread_exit_mutex);
+}
+} // namespace __sanitizer
+
+#endif // __linux__