| 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_, ¤t_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;
|
|
|