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

llvm-svn: 176179
This commit is contained in:
Kostya Serebryany 2013-02-27 11:22:40 +00:00
parent bfb0cd355f
commit f0b8f989e9
8 changed files with 296 additions and 14 deletions

View File

@ -27,16 +27,8 @@ 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::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):

View File

@ -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

View File

@ -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" {

View File

@ -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();

View File

@ -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__

View File

@ -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

View File

@ -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

View File

@ -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__