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