summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorchappedm@gmail.com <chappedm@gmail.com@6b5cf1ce-ec42-a296-1ba9-69fdba395a50>2012-09-18 00:00:20 +0000
committerchappedm@gmail.com <chappedm@gmail.com@6b5cf1ce-ec42-a296-1ba9-69fdba395a50>2012-09-18 00:00:20 +0000
commitfa0209f261c5e065d523bb1858f84fd91eb2f39a (patch)
treece4ff1c7da95243acddc721037a30b2029315d5c
parentcd723b43ff783a05321f0c0ba79a82494185b23c (diff)
downloadgperftools-fa0209f261c5e065d523bb1858f84fd91eb2f39a.tar.gz
issue-437 Fixed issues related to new glibc shipped with Ubuntu 10.10
1. ptrace permissions were modifed to be a bit more strict which required us to programatically set the permissions while syncing up to the profiling thread. 2. Order of destructors registered with atexit changed which was casuing us to miss generating the backtrace when heap checker was finished. Seems that we initially fixed this for FreeBSD and now linux has changed their behaviour to be the same. We are now a bit stricter on the rules here accross all platforms. git-svn-id: http://gperftools.googlecode.com/svn/trunk@152 6b5cf1ce-ec42-a296-1ba9-69fdba395a50
-rw-r--r--src/base/linuxthreads.cc87
-rw-r--r--src/heap-checker.cc4
2 files changed, 60 insertions, 31 deletions
diff --git a/src/base/linuxthreads.cc b/src/base/linuxthreads.cc
index 19da400..9d144ef 100644
--- a/src/base/linuxthreads.cc
+++ b/src/base/linuxthreads.cc
@@ -45,6 +45,8 @@ extern "C" {
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/wait.h>
+#include <sys/prctl.h>
+#include <semaphore.h>
#include "base/linux_syscall_support.h"
#include "base/thread_lister.h"
@@ -240,6 +242,7 @@ struct ListerParams {
ListAllProcessThreadsCallBack callback;
void *parameter;
va_list ap;
+ sem_t *lock;
};
@@ -254,6 +257,13 @@ static void ListerThread(struct ListerParams *args) {
struct kernel_stat marker_sb, proc_sb;
stack_t altstack;
+ /* Wait for parent thread to set appropriate permissions
+ * to allow ptrace activity
+ */
+ if (sem_wait(args->lock) < 0) {
+ goto failure;
+ }
+
/* Create "marker" that we can use to detect threads sharing the same
* address space and the same file handles. By setting the FD_CLOEXEC flag
* we minimize the risk of misidentifying child processes as threads;
@@ -536,6 +546,7 @@ int ListAllProcessThreads(void *parameter,
pid_t clone_pid;
int dumpable = 1, sig;
struct kernel_sigset_t sig_blocked, sig_old;
+ sem_t lock;
va_start(args.ap, callback);
@@ -565,6 +576,7 @@ int ListAllProcessThreads(void *parameter,
args.altstack_mem = altstack_mem;
args.parameter = parameter;
args.callback = callback;
+ args.lock = &lock;
/* Before cloning the thread lister, block all asynchronous signals, as we */
/* are not prepared to handle them. */
@@ -596,42 +608,59 @@ int ListAllProcessThreads(void *parameter,
#undef SYS_LINUX_SYSCALL_SUPPORT_H
#include "linux_syscall_support.h"
#endif
-
- int clone_errno;
- clone_pid = local_clone((int (*)(void *))ListerThread, &args);
- clone_errno = errno;
- sys_sigprocmask(SIG_SETMASK, &sig_old, &sig_old);
+ /* Lock before clone so that parent
+ * can set ptrace permissions prior
+ * to ListerThread actually executing
+ */
+ if (sem_init(&lock, 0, 0) == 0) {
- if (clone_pid >= 0) {
- int status, rc;
- while ((rc = sys0_waitpid(clone_pid, &status, __WALL)) < 0 &&
- ERRNO == EINTR) {
- /* Keep waiting */
- }
- if (rc < 0) {
- args.err = ERRNO;
- args.result = -1;
- } else if (WIFEXITED(status)) {
- switch (WEXITSTATUS(status)) {
- case 0: break; /* Normal process termination */
- case 2: args.err = EFAULT; /* Some fault (e.g. SIGSEGV) detected */
- args.result = -1;
- break;
- case 3: args.err = EPERM; /* Process is already being traced */
- args.result = -1;
- break;
- default:args.err = ECHILD; /* Child died unexpectedly */
- args.result = -1;
- break;
+ int clone_errno;
+ clone_pid = local_clone((int (*)(void *))ListerThread, &args);
+ clone_errno = errno;
+
+ sys_sigprocmask(SIG_SETMASK, &sig_old, &sig_old);
+
+ if (clone_pid >= 0) {
+ /* Allow child process to ptrace us
+ * and then release lock so that
+ * ListerThread then executes
+ */
+ prctl(PR_SET_PTRACER, clone_pid, 0, 0, 0);
+ sem_post(&lock);
+ int status, rc;
+ while ((rc = sys0_waitpid(clone_pid, &status, __WALL)) < 0 &&
+ ERRNO == EINTR) {
+ /* Keep waiting */
+ }
+ if (rc < 0) {
+ args.err = ERRNO;
+ args.result = -1;
+ } else if (WIFEXITED(status)) {
+ switch (WEXITSTATUS(status)) {
+ case 0: break; /* Normal process termination */
+ case 2: args.err = EFAULT; /* Some fault (e.g. SIGSEGV) detected */
+ args.result = -1;
+ break;
+ case 3: args.err = EPERM; /* Process is already being traced */
+ args.result = -1;
+ break;
+ default:args.err = ECHILD; /* Child died unexpectedly */
+ args.result = -1;
+ break;
+ }
+ } else if (!WIFEXITED(status)) {
+ args.err = EFAULT; /* Terminated due to an unhandled signal*/
+ args.result = -1;
}
- } else if (!WIFEXITED(status)) {
- args.err = EFAULT; /* Terminated due to an unhandled signal*/
+ sem_destroy(&lock);
+ } else {
args.result = -1;
+ args.err = clone_errno;
}
} else {
args.result = -1;
- args.err = clone_errno;
+ args.err = errno;
}
}
diff --git a/src/heap-checker.cc b/src/heap-checker.cc
index 5967b02..f55f356 100644
--- a/src/heap-checker.cc
+++ b/src/heap-checker.cc
@@ -2025,9 +2025,9 @@ void HeapLeakChecker_InternalInitStart() {
// at the right time, on FreeBSD we always check after, even in the
// less strict modes. This just means FreeBSD is always a bit
// stricter in its checking than other OSes.
-#ifdef __FreeBSD__
+ // This now appears to be the case in other OSes as well;
+ // so always check afterwards.
FLAGS_heap_check_after_destructors = true;
-#endif
{ SpinLockHolder l(&heap_checker_lock);
RAW_DCHECK(heap_checker_pid == getpid(), "");