[compiler-rt] r176179 - add Linux syscall wrappers and ThreadLister to sanitizer_common

Kostya Serebryany kcc at google.com
Wed Feb 27 03:22:41 PST 2013


Author: kcc
Date: Wed Feb 27 05:22:40 2013
New Revision: 176179

URL: http://llvm.org/viewvc/llvm-project?rev=176179&view=rev
Log:
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

Added:
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.h
    compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_linux_test.cc
Modified:
    compiler-rt/trunk/lib/interception/interception.h
    compiler-rt/trunk/lib/sanitizer_common/CMakeLists.txt
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_internal_defs.h
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_libc.h
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.cc
    compiler-rt/trunk/lib/sanitizer_common/tests/CMakeLists.txt

Modified: compiler-rt/trunk/lib/interception/interception.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/interception/interception.h?rev=176179&r1=176178&r2=176179&view=diff
==============================================================================
--- compiler-rt/trunk/lib/interception/interception.h (original)
+++ compiler-rt/trunk/lib/interception/interception.h Wed Feb 27 05:22:40 2013
@@ -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):

Modified: compiler-rt/trunk/lib/sanitizer_common/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/CMakeLists.txt?rev=176179&r1=176178&r2=176179&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/CMakeLists.txt (original)
+++ compiler-rt/trunk/lib/sanitizer_common/CMakeLists.txt Wed Feb 27 05:22:40 2013
@@ -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

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_internal_defs.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_internal_defs.h?rev=176179&r1=176178&r2=176179&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_internal_defs.h (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_internal_defs.h Wed Feb 27 05:22:40 2013
@@ -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" {

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_libc.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_libc.h?rev=176179&r1=176178&r2=176179&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_libc.h (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_libc.h Wed Feb 27 05:22:40 2013
@@ -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();

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.cc?rev=176179&r1=176178&r2=176179&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.cc (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.cc Wed Feb 27 05:22:40 2013
@@ -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__

Added: compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.h?rev=176179&view=auto
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.h (added)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.h Wed Feb 27 05:22:40 2013
@@ -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

Modified: compiler-rt/trunk/lib/sanitizer_common/tests/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/tests/CMakeLists.txt?rev=176179&r1=176178&r2=176179&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/tests/CMakeLists.txt (original)
+++ compiler-rt/trunk/lib/sanitizer_common/tests/CMakeLists.txt Wed Feb 27 05:22:40 2013
@@ -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

Added: compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_linux_test.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_linux_test.cc?rev=176179&view=auto
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_linux_test.cc (added)
+++ compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_linux_test.cc Wed Feb 27 05:22:40 2013
@@ -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__





More information about the llvm-commits mailing list