Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(10)

Unified Diff: third_party/tcmalloc/chromium/src/profile-handler.cc

Issue 9416072: Fix gperftools to not arm the profiling timer by default. Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/tcmalloc/chromium/src/profile-handler.h ('k') | third_party/tcmalloc/chromium/src/profiler.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/tcmalloc/chromium/src/profile-handler.cc
diff --git a/third_party/tcmalloc/chromium/src/profile-handler.cc b/third_party/tcmalloc/chromium/src/profile-handler.cc
index 20e5cca90cfcba984248c93968bf83e9e2d26527..1abc922efc475dfd7cb03b33c2734657607b06a8 100644
--- a/third_party/tcmalloc/chromium/src/profile-handler.cc
+++ b/third_party/tcmalloc/chromium/src/profile-handler.cc
@@ -38,11 +38,12 @@
#if !(defined(__CYGWIN__) || defined(__CYGWIN32__))
-#include <stdio.h>
#include <errno.h>
+#include <stdio.h>
#include <sys/time.h>
#include <list>
+#include <map>
#include <string>
#include "base/dynamic_annotations.h"
@@ -52,6 +53,7 @@
#include "maybe_threads.h"
using std::list;
+using std::map;
using std::string;
// This structure is used by ProfileHandlerRegisterCallback and
@@ -69,39 +71,47 @@ struct ProfileHandlerToken {
void* callback_arg;
};
+// Blocks a signal from being delivered to the current thread while the object
+// is alive. Restores previous state upon destruction.
+class ScopedSignalBlocker {
+ public:
+ ScopedSignalBlocker(int signo) {
+ sigemptyset(&sig_set_);
+ sigaddset(&sig_set_, signo);
+ RAW_CHECK(sigprocmask(SIG_BLOCK, &sig_set_, NULL) == 0,
+ "sigprocmask (block)");
+ }
+ ~ScopedSignalBlocker() {
+ RAW_CHECK(sigprocmask(SIG_UNBLOCK, &sig_set_, NULL) == 0,
+ "sigprocmask (unblock)");
+ }
+
+ private:
+ sigset_t sig_set_;
+};
+
// This class manages profile timers and associated signal handler. This is a
// a singleton.
class ProfileHandler {
public:
- // Registers the current thread with the profile handler. On systems which
- // have a separate interval timer for each thread, this function starts the
- // timer for the current thread.
- //
- // The function also attempts to determine whether or not timers are shared by
- // all threads in the process. (With LinuxThreads, and with NPTL on some
- // Linux kernel versions, each thread has separate timers.)
- //
- // Prior to determining whether timers are shared, this function will
- // unconditionally start the timer. However, if this function determines
- // that timers are shared, then it will stop the timer if no callbacks are
- // currently registered.
+ // Registers the current thread with the profile handler.
void RegisterThread();
+ // Unregisters the current thread with the profile handler.
+ void UnregisterThread();
+
// Registers a callback routine to receive profile timer ticks. The returned
// token is to be used when unregistering this callback and must not be
- // deleted by the caller. Registration of the first callback enables the
- // SIGPROF handler (or SIGALRM if using ITIMER_REAL).
+ // deleted by the caller.
ProfileHandlerToken* RegisterCallback(ProfileHandlerCallback callback,
void* callback_arg);
// Unregisters a previously registered callback. Expects the token returned
- // by the corresponding RegisterCallback routine. Unregistering the last
- // callback disables the SIGPROF handler (or SIGALRM if using ITIMER_REAL).
+ // by the corresponding RegisterCallback routine.
void UnregisterCallback(ProfileHandlerToken* token)
NO_THREAD_SAFETY_ANALYSIS;
- // Unregisters all the callbacks, stops the timer if shared, disables the
- // SIGPROF (or SIGALRM) handler and clears the timer_sharing_ state.
+ // Unregisters all the callbacks and stops the timer(s).
void Reset();
// Gets the current state of profile handler.
@@ -125,6 +135,12 @@ class ProfileHandler {
// pthread_once_t for one time initialization of ProfileHandler singleton.
static pthread_once_t once_;
+#if defined(HAVE_TLS)
+ // Timer state as configured previously. This bit is kept in
+ // |registered_threads_| if TLS is not available.
+ static __thread bool timer_running_;
+#endif
+
// Initializes the ProfileHandler singleton via GoogleOnceInit.
static void Init();
@@ -143,19 +159,6 @@ class ProfileHandler {
// Is profiling allowed at all?
bool allowed_;
- // Whether or not the threading system provides interval timers that are
- // shared by all threads in a process.
- enum {
- // No timer initialization attempted yet.
- TIMERS_UNTOUCHED,
- // First thread has registered and set timer.
- TIMERS_ONE_SET,
- // Timers are shared by all threads.
- TIMERS_SHARED,
- // Timers are separate in each thread.
- TIMERS_SEPARATE
- } timer_sharing_ GUARDED_BY(control_lock_);
-
// This lock serializes the registration of threads and protects the
// callbacks_ list below.
// Locking order:
@@ -165,6 +168,13 @@ class ProfileHandler {
SpinLock control_lock_ ACQUIRED_BEFORE(signal_lock_);
SpinLock signal_lock_;
+ // Set of threads registered through RegisterThread. This is consulted
+ // whenever timers are switched on or off to post a signal to these threads
+ // so they can arm their timers. Locking rules as for |callbacks_| below. If
+ // TLS is not available, this map is also used to keep the thread's timer
+ // state.
+ map<pthread_t, bool> registered_threads_ GUARDED_BY(signal_lock_);
+
// Holds the list of registered callbacks. We expect the list to be pretty
// small. Currently, the cpu profiler (base/profiler) and thread module
// (base/thread.h) are the only two components registering callbacks.
@@ -182,26 +192,18 @@ class ProfileHandler {
typedef CallbackList::iterator CallbackIterator;
CallbackList callbacks_ GUARDED_BY(signal_lock_);
- // Starts the interval timer. If the thread library shares timers between
- // threads, this function starts the shared timer. Otherwise, this will start
- // the timer in the current thread.
- void StartTimer() EXCLUSIVE_LOCKS_REQUIRED(control_lock_);
-
- // Stops the interval timer. If the thread library shares timers between
- // threads, this fucntion stops the shared timer. Otherwise, this will stop
- // the timer in the current thread.
- void StopTimer() EXCLUSIVE_LOCKS_REQUIRED(control_lock_);
-
- // Returns true if the profile interval timer is enabled in the current
- // thread. This actually checks the kernel's interval timer setting. (It is
- // used to detect whether timers are shared or separate.)
- bool IsTimerRunning() EXCLUSIVE_LOCKS_REQUIRED(control_lock_);
+ // Returns the signal to be used with |timer_type_| (SIGPROF or SIGALARM).
+ int signal_number() {
+ return (timer_type_ == ITIMER_PROF ? SIGPROF : SIGALRM);
+ }
- // Sets the timer interrupt signal handler.
- void EnableHandler() EXCLUSIVE_LOCKS_REQUIRED(control_lock_);
+ // Starts or stops the interval timer(s). In case of non-shared timers, this
+ // will post a signal to all registered threads, causing them to arm their
+ // timers.
+ void UpdateTimers(bool enable) EXCLUSIVE_LOCKS_REQUIRED(signal_lock_);
- // Disables (ignores) the timer interrupt signal.
- void DisableHandler() EXCLUSIVE_LOCKS_REQUIRED(control_lock_);
+ // Configures the timer. If |frequency| is 0, the timer will be disabled.
+ void SetupTimer(int32 frequency) EXCLUSIVE_LOCKS_REQUIRED(signal_lock_);
// Returns true if the handler is not being used by something else.
// This checks the kernel's signal handler table.
@@ -216,15 +218,21 @@ class ProfileHandler {
ProfileHandler* ProfileHandler::instance_ = NULL;
pthread_once_t ProfileHandler::once_ = PTHREAD_ONCE_INIT;
+#if defined(HAVE_TLS)
+bool __thread ProfileHandler::timer_running_ = false;
+#endif
+
const int32 ProfileHandler::kMaxFrequency;
const int32 ProfileHandler::kDefaultFrequency;
-// If we are LD_PRELOAD-ed against a non-pthreads app, then
-// pthread_once won't be defined. We declare it here, for that
-// case (with weak linkage) which will cause the non-definition to
-// resolve to NULL. We can then check for NULL or not in Instance.
+// If we are LD_PRELOAD-ed against a non-pthreads app, then pthread_* functions
+// won't be defined. We declare them here, for that case (with weak linkage)
+// which will cause the non-definition to resolve to NULL. We can then check
+// for NULL or not in Instance.
extern "C" int pthread_once(pthread_once_t *, void (*)(void))
ATTRIBUTE_WEAK;
+extern "C" int pthread_kill(pthread_t thread_id, int signo)
+ ATTRIBUTE_WEAK;
void ProfileHandler::Init() {
instance_ = new ProfileHandler();
@@ -247,8 +255,7 @@ ProfileHandler* ProfileHandler::Instance() {
ProfileHandler::ProfileHandler()
: interrupts_(0),
callback_count_(0),
- allowed_(true),
- timer_sharing_(TIMERS_UNTOUCHED) {
+ allowed_(true) {
SpinLockHolder cl(&control_lock_);
timer_type_ = (getenv("CPUPROFILE_REALTIME") ? ITIMER_REAL : ITIMER_PROF);
@@ -272,14 +279,17 @@ ProfileHandler::ProfileHandler()
// assume it has priority over us and stop.
if (!IsSignalHandlerAvailable()) {
RAW_LOG(INFO, "Disabling profiler because %s handler is already in use.",
- timer_type_ == ITIMER_REAL ? "SIGALRM" : "SIGPROF");
+ signal_number());
allowed_ = false;
return;
}
- // Ignore signals until we decide to turn profiling on. (Paranoia;
- // should already be ignored.)
- DisableHandler();
+ // Install the signal handler.
+ struct sigaction sa;
+ sa.sa_sigaction = SignalHandler;
+ sa.sa_flags = SA_RESTART | SA_SIGINFO;
+ sigemptyset(&sa.sa_mask);
+ RAW_CHECK(sigaction(signal_number(), &sa, NULL) == 0, "sigprof (enable)");
}
ProfileHandler::~ProfileHandler() {
@@ -293,47 +303,17 @@ void ProfileHandler::RegisterThread() {
return;
}
- // We try to detect whether timers are being shared by setting a
- // timer in the first call to this function, then checking whether
- // it's set in the second call.
- //
- // Note that this detection method requires that the first two calls
- // to RegisterThread must be made from different threads. (Subsequent
- // calls will see timer_sharing_ set to either TIMERS_SEPARATE or
- // TIMERS_SHARED, and won't try to detect the timer sharing type.)
- //
- // Also note that if timer settings were inherited across new thread
- // creation but *not* shared, this approach wouldn't work. That's
- // not an issue for any Linux threading implementation, and should
- // not be a problem for a POSIX-compliant threads implementation.
- switch (timer_sharing_) {
- case TIMERS_UNTOUCHED:
- StartTimer();
- timer_sharing_ = TIMERS_ONE_SET;
- break;
- case TIMERS_ONE_SET:
- // If the timer is running, that means that the main thread's
- // timer setup is seen in this (second) thread -- and therefore
- // that timers are shared.
- if (IsTimerRunning()) {
- timer_sharing_ = TIMERS_SHARED;
- // If callback is already registered, we have to keep the timer
- // running. If not, we disable the timer here.
- if (callback_count_ == 0) {
- StopTimer();
- }
- } else {
- timer_sharing_ = TIMERS_SEPARATE;
- StartTimer();
- }
- break;
- case TIMERS_SHARED:
- // Nothing needed.
- break;
- case TIMERS_SEPARATE:
- StartTimer();
- break;
- }
+ // Record the thread identifier and start the timer if profiling is on.
+ ScopedSignalBlocker block(signal_number());
+ SpinLockHolder sl(&signal_lock_);
+ registered_threads_[pthread_self()] = false;
+ SetupTimer(callback_count_ > 0 ? frequency_ : 0);
+}
+
+void ProfileHandler::UnregisterThread() {
+ ScopedSignalBlocker block(signal_number());
+ SpinLockHolder sl(&signal_lock_);
+ registered_threads_.erase(pthread_self());
}
ProfileHandlerToken* ProfileHandler::RegisterCallback(
@@ -342,17 +322,13 @@ ProfileHandlerToken* ProfileHandler::RegisterCallback(
ProfileHandlerToken* token = new ProfileHandlerToken(callback, callback_arg);
SpinLockHolder cl(&control_lock_);
- DisableHandler();
{
+ ScopedSignalBlocker block(signal_number());
SpinLockHolder sl(&signal_lock_);
callbacks_.push_back(token);
+ ++callback_count_;
+ UpdateTimers(true);
}
- // Start the timer if timer is shared and this is a first callback.
- if ((callback_count_ == 0) && (timer_sharing_ == TIMERS_SHARED)) {
- StartTimer();
- }
- ++callback_count_;
- EnableHandler();
return token;
}
@@ -362,17 +338,14 @@ void ProfileHandler::UnregisterCallback(ProfileHandlerToken* token) {
++it) {
if ((*it) == token) {
RAW_CHECK(callback_count_ > 0, "Invalid callback count");
- DisableHandler();
{
+ ScopedSignalBlocker block(signal_number());
SpinLockHolder sl(&signal_lock_);
delete *it;
callbacks_.erase(it);
- }
- --callback_count_;
- if (callback_count_ > 0) {
- EnableHandler();
- } else if (timer_sharing_ == TIMERS_SHARED) {
- StopTimer();
+ --callback_count_;
+ if (callback_count_ == 0)
+ UpdateTimers(false);
}
return;
}
@@ -383,8 +356,8 @@ void ProfileHandler::UnregisterCallback(ProfileHandlerToken* token) {
void ProfileHandler::Reset() {
SpinLockHolder cl(&control_lock_);
- DisableHandler();
{
+ ScopedSignalBlocker block(signal_number());
SpinLockHolder sl(&signal_lock_);
CallbackIterator it = callbacks_.begin();
while (it != callbacks_.end()) {
@@ -393,87 +366,66 @@ void ProfileHandler::Reset() {
delete *tmp;
callbacks_.erase(tmp);
}
+ callback_count_ = 0;
+ UpdateTimers(false);
}
- callback_count_ = 0;
- if (timer_sharing_ == TIMERS_SHARED) {
- StopTimer();
- }
- timer_sharing_ = TIMERS_UNTOUCHED;
}
void ProfileHandler::GetState(ProfileHandlerState* state) {
SpinLockHolder cl(&control_lock_);
- DisableHandler();
{
+ ScopedSignalBlocker block(signal_number());
SpinLockHolder sl(&signal_lock_); // Protects interrupts_.
state->interrupts = interrupts_;
}
- if (callback_count_ > 0) {
- EnableHandler();
- }
state->frequency = frequency_;
state->callback_count = callback_count_;
state->allowed = allowed_;
}
-void ProfileHandler::StartTimer() {
+void ProfileHandler::UpdateTimers(bool enabled) {
if (!allowed_) {
return;
}
- struct itimerval timer;
- timer.it_interval.tv_sec = 0;
- timer.it_interval.tv_usec = 1000000 / frequency_;
- timer.it_value = timer.it_interval;
- setitimer(timer_type_, &timer, 0);
+ SetupTimer(enabled ? frequency_ : 0);
+
+ // Tell other threads to start their timers.
+ for (map<pthread_t, bool>::const_iterator thread(registered_threads_.begin());
+ thread != registered_threads_.end();
+ ++thread) {
+ if (thread->first != pthread_self() && pthread_kill) {
+ int err = pthread_kill(thread->first, signal_number());
+ if (err == ESRCH) {
+ // Thread exited.
+ registered_threads_.erase(thread->first);
+ } else {
+ RAW_CHECK(err == 0, "pthread_kill");
+ }
+ }
+ }
}
-void ProfileHandler::StopTimer() {
- if (!allowed_) {
+void ProfileHandler::SetupTimer(int32 frequency) {
+ bool enable = (frequency > 0);
+#if defined(HAVE_TLS)
+ bool& timer_running = timer_running_;
+#else
+ bool& timer_running = registered_threads_[pthread_self()];
+#endif
+ if (enable == timer_running)
return;
- }
struct itimerval timer;
- memset(&timer, 0, sizeof timer);
+ timer.it_interval.tv_sec = 0;
+ timer.it_interval.tv_usec = (enable ? (1000000 / frequency) : 0);
+ timer.it_value = timer.it_interval;
setitimer(timer_type_, &timer, 0);
-}
-
-bool ProfileHandler::IsTimerRunning() {
- if (!allowed_) {
- return false;
- }
- struct itimerval current_timer;
- RAW_CHECK(0 == getitimer(timer_type_, &current_timer), "getitimer");
- return (current_timer.it_value.tv_sec != 0 ||
- current_timer.it_value.tv_usec != 0);
-}
-
-void ProfileHandler::EnableHandler() {
- if (!allowed_) {
- return;
- }
- struct sigaction sa;
- sa.sa_sigaction = SignalHandler;
- sa.sa_flags = SA_RESTART | SA_SIGINFO;
- sigemptyset(&sa.sa_mask);
- const int signal_number = (timer_type_ == ITIMER_PROF ? SIGPROF : SIGALRM);
- RAW_CHECK(sigaction(signal_number, &sa, NULL) == 0, "sigprof (enable)");
-}
-
-void ProfileHandler::DisableHandler() {
- if (!allowed_) {
- return;
- }
- struct sigaction sa;
- sa.sa_handler = SIG_IGN;
- sa.sa_flags = SA_RESTART;
- sigemptyset(&sa.sa_mask);
- const int signal_number = (timer_type_ == ITIMER_PROF ? SIGPROF : SIGALRM);
- RAW_CHECK(sigaction(signal_number, &sa, NULL) == 0, "sigprof (disable)");
+ timer_running = enable;
}
bool ProfileHandler::IsSignalHandlerAvailable() {
struct sigaction sa;
- const int signal_number = (timer_type_ == ITIMER_PROF ? SIGPROF : SIGALRM);
- RAW_CHECK(sigaction(signal_number, NULL, &sa) == 0, "is-signal-handler avail");
+ RAW_CHECK(sigaction(signal_number(), NULL, &sa) == 0,
+ "is-signal-handler avail");
// We only take over the handler if the current one is unset.
// It must be SIG_IGN or SIG_DFL, not some other function.
@@ -495,6 +447,9 @@ void ProfileHandler::SignalHandler(int sig, siginfo_t* sinfo, void* ucontext) {
{
SpinLockHolder sl(&instance->signal_lock_);
++instance->interrupts_;
+ // Enable/Disable the timer if necessary.
+ instance->SetupTimer(
+ instance->callbacks_.empty() ? 0 : instance->frequency_);
for (CallbackIterator it = instance->callbacks_.begin();
it != instance->callbacks_.end();
++it) {
@@ -512,6 +467,10 @@ extern "C" void ProfileHandlerRegisterThread() {
ProfileHandler::Instance()->RegisterThread();
}
+extern "C" void ProfileHandlerUnregisterThread() {
+ ProfileHandler::Instance()->UnregisterThread();
+}
+
extern "C" ProfileHandlerToken* ProfileHandlerRegisterCallback(
ProfileHandlerCallback callback, void* callback_arg) {
return ProfileHandler::Instance()->RegisterCallback(callback, callback_arg);
@@ -538,6 +497,9 @@ extern "C" void ProfileHandlerGetState(ProfileHandlerState* state) {
extern "C" void ProfileHandlerRegisterThread() {
}
+extern "C" void ProfileHandlerUnregisterThread() {
+}
+
extern "C" ProfileHandlerToken* ProfileHandlerRegisterCallback(
ProfileHandlerCallback callback, void* callback_arg) {
return NULL;
« no previous file with comments | « third_party/tcmalloc/chromium/src/profile-handler.h ('k') | third_party/tcmalloc/chromium/src/profiler.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698