diff options
author | mnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-14 14:02:24 +0000 |
---|---|---|
committer | mnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-14 14:02:24 +0000 |
commit | e410b5f18f1e98c64a1a617ea3c73b49ce9388a7 (patch) | |
tree | 34f01ba4152399555acd13b1962ecd8ba4a4033d /third_party/tcmalloc | |
parent | 6644f3ae3288b60f310d1b4310b6bfa9c2e7cce3 (diff) | |
download | chromium_src-e410b5f18f1e98c64a1a617ea3c73b49ce9388a7.zip chromium_src-e410b5f18f1e98c64a1a617ea3c73b49ce9388a7.tar.gz chromium_src-e410b5f18f1e98c64a1a617ea3c73b49ce9388a7.tar.bz2 |
Switch prefs::kExtensionInstallForceList to be a dictionary.
This makes lots of code easier. The conversion from policy to prefs is
now done in the right place, reporting errors in about:policy.
BUG=chromium:166067
TEST=unit tests.
Review URL: https://codereview.chromium.org/11566024
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@173151 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'third_party/tcmalloc')
6 files changed, 157 insertions, 171 deletions
diff --git a/third_party/tcmalloc/chromium/src/gperftools/profiler.h b/third_party/tcmalloc/chromium/src/gperftools/profiler.h index 07323e4..72a74ba 100644 --- a/third_party/tcmalloc/chromium/src/gperftools/profiler.h +++ b/third_party/tcmalloc/chromium/src/gperftools/profiler.h @@ -150,6 +150,10 @@ PERFTOOLS_DLL_DECL int ProfilingIsEnabledForAllThreads(); */ PERFTOOLS_DLL_DECL void ProfilerRegisterThread(); +/* Routine for unregistering new threads with the profiler. + */ +PERFTOOLS_DLL_DECL void ProfilerUnregisterThread(); + /* Stores state about profiler's current status into "*state". */ struct ProfilerState { int enabled; /* Is profiling currently enabled? */ diff --git a/third_party/tcmalloc/chromium/src/profile-handler.cc b/third_party/tcmalloc/chromium/src/profile-handler.cc index 20e5cca..1abc922 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; diff --git a/third_party/tcmalloc/chromium/src/profile-handler.h b/third_party/tcmalloc/chromium/src/profile-handler.h index 4b078ec..cc50dbb 100644 --- a/third_party/tcmalloc/chromium/src/profile-handler.h +++ b/third_party/tcmalloc/chromium/src/profile-handler.h @@ -102,6 +102,18 @@ typedef void (*ProfileHandlerCallback)(int sig, siginfo_t* sig_info, void ProfileHandlerRegisterThread(); /* + * Unregisters a thread before exiting. This is not strictly necessary in most + * cases, because the pthread library will detect that previously-valid thread + * identifiers have become invalid. For dead-thread detection to work, pthread + * identifiers are accessed even after the thread has been destroyed, which may + * fail in some edge cases (thread ID recycling) and even crash in exotic cases + * (for example, when using user-allocated stacks with NPTL and unmapping the + * memory after the thread terminates). Thus, it is generally good practice to + * call ProfileHandlerUnregisterThread() just before exiting the thread. + */ +void ProfileHandlerUnregisterThread(); + +/* * Registers a callback routine. This callback function will be called in the * context of SIGPROF handler, so must be async-signal-safe. The returned token * is to be used when unregistering this callback via diff --git a/third_party/tcmalloc/chromium/src/profiler.cc b/third_party/tcmalloc/chromium/src/profiler.cc index dfb6aab..65879f6 100644 --- a/third_party/tcmalloc/chromium/src/profiler.cc +++ b/third_party/tcmalloc/chromium/src/profiler.cc @@ -290,6 +290,10 @@ extern "C" PERFTOOLS_DLL_DECL void ProfilerRegisterThread() { ProfileHandlerRegisterThread(); } +extern "C" PERFTOOLS_DLL_DECL void ProfilerUnregisterThread() { + ProfileHandlerUnregisterThread(); +} + extern "C" PERFTOOLS_DLL_DECL void ProfilerFlush() { CpuProfiler::instance_.FlushTable(); } @@ -323,6 +327,7 @@ extern "C" PERFTOOLS_DLL_DECL void ProfilerGetCurrentState( // these issues, unless a specific need is identified, profiler support is // disabled under Cygwin. extern "C" void ProfilerRegisterThread() { } +extern "C" void ProfilerUnregisterThread() { } extern "C" void ProfilerFlush() { } extern "C" int ProfilingIsEnabledForAllThreads() { return 0; } extern "C" int ProfilerStart(const char* fname) { return 0; } diff --git a/third_party/tcmalloc/chromium/src/tests/profile-handler_unittest.cc b/third_party/tcmalloc/chromium/src/tests/profile-handler_unittest.cc index 98cfe6d..e46ab83 100644 --- a/third_party/tcmalloc/chromium/src/tests/profile-handler_unittest.cc +++ b/third_party/tcmalloc/chromium/src/tests/profile-handler_unittest.cc @@ -58,6 +58,7 @@ class Thread { static void* DoRun(void* cls) { ProfileHandlerRegisterThread(); reinterpret_cast<Thread*>(cls)->Run(); + ProfileHandlerUnregisterThread(); return NULL; } pthread_t thread_; diff --git a/third_party/tcmalloc/chromium/src/tests/profiler_unittest.cc b/third_party/tcmalloc/chromium/src/tests/profiler_unittest.cc index 399891b..2afe209 100644 --- a/third_party/tcmalloc/chromium/src/tests/profiler_unittest.cc +++ b/third_party/tcmalloc/chromium/src/tests/profiler_unittest.cc @@ -63,6 +63,8 @@ static void test_other_thread() { } snprintf(b, sizeof(b), "other: %d", result); // get some libc action } + + ProfilerUnregisterThread(); #endif } |