Commit ff7e6def authored by lpy's avatar lpy Committed by Commit bot

Get rid of UnsafeCurrent in Sampler

Currently we are using UnsafeCurrent in async signal handler to acquire the
isolate of VM thread, but we want to get rid of that since it prevents V8 from
being thread agnostic.

This patch replaces UnsafeCurrent with a static map, where we store a map of
samplers for threads, and makes it accessible by signal handler.

BUG=v8:4889
LOG=n

Review URL: https://codereview.chromium.org/1900473002

Cr-Commit-Position: refs/heads/master@{#35722}
parent 67298383
......@@ -495,14 +495,6 @@ class Isolate {
return isolate;
}
// Like Current, but skips the check that |isolate_key_| was initialized.
// Callers have to ensure that themselves.
// DO NOT USE. The only remaining callsite will be deleted soon.
INLINE(static Isolate* UnsafeCurrent()) {
return reinterpret_cast<Isolate*>(
base::Thread::GetThreadLocal(isolate_key_));
}
// Usually called by Init(), but can be called early e.g. to allow
// testing components that require logging but not the whole
// isolate.
......
......@@ -42,6 +42,7 @@
#endif
#include "src/atomic-utils.h"
#include "src/base/platform/platform.h"
#include "src/flags.h"
#include "src/frames-inl.h"
......@@ -236,6 +237,52 @@ bool IsNoFrameRegion(Address address) {
return false;
}
typedef List<Sampler*> SamplerList;
#if defined(USE_SIGNALS)
class AtomicGuard {
public:
explicit AtomicGuard(AtomicValue<int>* atomic, bool is_block = true)
: atomic_(atomic),
is_success_(false) {
do {
// Use Acquire_Load to gain mutual exclusion.
USE(atomic_->Value());
is_success_ = atomic_->TrySetValue(0, 1);
} while (is_block && !is_success_);
}
bool is_success() { return is_success_; }
~AtomicGuard() {
if (is_success_) {
atomic_->SetValue(0);
}
atomic_ = NULL;
}
private:
AtomicValue<int>* atomic_;
bool is_success_;
};
// Returns key for hash map.
void* ThreadKey(pthread_t thread_id) {
return reinterpret_cast<void*>(thread_id);
}
// Returns hash value for hash map.
uint32_t ThreadHash(pthread_t thread_id) {
#if V8_OS_MACOSX
return static_cast<uint32_t>(reinterpret_cast<intptr_t>(thread_id));
#else
return static_cast<uint32_t>(thread_id);
#endif
}
#endif // USE_SIGNALS
} // namespace
#if defined(USE_SIGNALS)
......@@ -374,6 +421,10 @@ class SignalHandler : public AllStatic {
return signal_handler_installed_;
}
#if !V8_OS_NACL
static void CollectSample(void* context, Sampler* sampler);
#endif
private:
static void Install() {
#if !V8_OS_NACL
......@@ -418,23 +469,21 @@ bool SignalHandler::signal_handler_installed_ = false;
// As Native Client does not support signal handling, profiling is disabled.
#if !V8_OS_NACL
void SignalHandler::HandleProfilerSignal(int signal, siginfo_t* info,
void* context) {
USE(info);
if (signal != SIGPROF) return;
Isolate* isolate = Isolate::UnsafeCurrent();
if (isolate == NULL || !isolate->IsInUse()) {
// We require a fully initialized and entered isolate.
void SignalHandler::CollectSample(void* context, Sampler* sampler) {
if (sampler == NULL || (!sampler->IsProfiling() &&
!sampler->IsRegistered())) {
return;
}
Isolate* isolate = sampler->isolate();
// We require a fully initialized and entered isolate.
if (isolate == NULL || !isolate->IsInUse()) return;
if (v8::Locker::IsActive() &&
!isolate->thread_manager()->IsLockedByCurrentThread()) {
return;
}
Sampler* sampler = isolate->logger()->sampler();
if (sampler == NULL) return;
v8::RegisterState state;
#if defined(USE_SIMULATOR)
......@@ -582,7 +631,7 @@ void SignalHandler::HandleProfilerSignal(int signal, siginfo_t* info,
}
#endif // V8_OS_NACL
#endif
#endif // USE_SIGNALS
class SamplerThread : public base::Thread {
......@@ -607,19 +656,46 @@ class SamplerThread : public base::Thread {
}
DCHECK(sampler->IsActive());
DCHECK(!instance_->active_samplers_.Contains(sampler));
DCHECK(instance_->interval_ == sampler->interval());
#if defined(USE_SIGNALS)
AddSampler(sampler);
#else
DCHECK(!instance_->active_samplers_.Contains(sampler));
instance_->active_samplers_.Add(sampler);
#endif // USE_SIGNALS
if (need_to_start) instance_->StartSynchronously();
}
static void RemoveActiveSampler(Sampler* sampler) {
static void RemoveSampler(Sampler* sampler) {
SamplerThread* instance_to_remove = NULL;
{
base::LockGuard<base::Mutex> lock_guard(mutex_);
DCHECK(sampler->IsActive());
DCHECK(sampler->IsActive() || sampler->IsRegistered());
#if defined(USE_SIGNALS)
{
AtomicGuard atomic_guard(&sampler_list_access_counter_);
// Remove sampler from map.
pthread_t thread_id = sampler->platform_data()->vm_tid();
void* thread_key = ThreadKey(thread_id);
uint32_t thread_hash = ThreadHash(thread_id);
HashMap::Entry* entry =
thread_id_to_samplers_.Get().Lookup(thread_key, thread_hash);
DCHECK(entry != NULL);
SamplerList* samplers = reinterpret_cast<SamplerList*>(entry->value);
samplers->RemoveElement(sampler);
if (samplers->is_empty()) {
thread_id_to_samplers_.Pointer()->Remove(thread_key, thread_hash);
delete samplers;
}
if (thread_id_to_samplers_.Get().occupancy() == 0) {
instance_to_remove = instance_;
instance_ = NULL;
}
}
#else
bool removed = instance_->active_samplers_.RemoveElement(sampler);
DCHECK(removed);
USE(removed);
......@@ -630,6 +706,7 @@ class SamplerThread : public base::Thread {
instance_to_remove = instance_;
instance_ = NULL;
}
#endif // USE_SIGNALS
}
if (!instance_to_remove) return;
......@@ -637,11 +714,30 @@ class SamplerThread : public base::Thread {
delete instance_to_remove;
}
// Unlike AddActiveSampler, this method only adds a sampler,
// but won't start the sampler thread.
static void RegisterSampler(Sampler* sampler) {
base::LockGuard<base::Mutex> lock_guard(mutex_);
#if defined(USE_SIGNALS)
AddSampler(sampler);
#endif // USE_SIGNALS
}
// Implement Thread::Run().
virtual void Run() {
while (true) {
{
base::LockGuard<base::Mutex> lock_guard(mutex_);
#if defined(USE_SIGNALS)
if (thread_id_to_samplers_.Get().occupancy() == 0) break;
if (SignalHandler::Installed()) {
for (HashMap::Entry *p = thread_id_to_samplers_.Get().Start();
p != NULL; p = thread_id_to_samplers_.Get().Next(p)) {
pthread_t thread_id = reinterpret_cast<pthread_t>(p->key);
pthread_kill(thread_id, SIGPROF);
}
}
#else
if (active_samplers_.is_empty()) break;
// When CPU profiling is enabled both JavaScript and C++ code is
// profiled. We must not suspend.
......@@ -650,6 +746,7 @@ class SamplerThread : public base::Thread {
if (!sampler->IsProfiling()) continue;
sampler->DoSample();
}
#endif // USE_SIGNALS
}
base::OS::Sleep(base::TimeDelta::FromMilliseconds(interval_));
}
......@@ -661,7 +758,38 @@ class SamplerThread : public base::Thread {
static SamplerThread* instance_;
const int interval_;
List<Sampler*> active_samplers_;
#if defined(USE_SIGNALS)
struct HashMapCreateTrait {
static void Construct(HashMap* allocated_ptr) {
new (allocated_ptr) HashMap(HashMap::PointersMatch);
}
};
friend class SignalHandler;
static base::LazyInstance<HashMap, HashMapCreateTrait>::type
thread_id_to_samplers_;
static AtomicValue<int> sampler_list_access_counter_;
static void AddSampler(Sampler* sampler) {
AtomicGuard atomic_guard(&sampler_list_access_counter_);
// Add sampler into map if needed.
pthread_t thread_id = sampler->platform_data()->vm_tid();
HashMap::Entry *entry =
thread_id_to_samplers_.Pointer()->LookupOrInsert(ThreadKey(thread_id),
ThreadHash(thread_id));
if (entry->value == NULL) {
SamplerList* samplers = new SamplerList();
samplers->Add(sampler);
entry->value = samplers;
} else {
SamplerList* samplers = reinterpret_cast<SamplerList*>(entry->value);
if (!samplers->Contains(sampler)) {
samplers->Add(sampler);
}
}
}
#else
SamplerList active_samplers_;
#endif // USE_SIGNALS
DISALLOW_COPY_AND_ASSIGN(SamplerThread);
};
......@@ -669,6 +797,33 @@ class SamplerThread : public base::Thread {
base::Mutex* SamplerThread::mutex_ = NULL;
SamplerThread* SamplerThread::instance_ = NULL;
#if defined(USE_SIGNALS)
base::LazyInstance<HashMap, SamplerThread::HashMapCreateTrait>::type
SamplerThread::thread_id_to_samplers_ = LAZY_INSTANCE_INITIALIZER;
AtomicValue<int> SamplerThread::sampler_list_access_counter_(0);
// As Native Client does not support signal handling, profiling is disabled.
#if !V8_OS_NACL
void SignalHandler::HandleProfilerSignal(int signal, siginfo_t* info,
void* context) {
USE(info);
if (signal != SIGPROF) return;
AtomicGuard atomic_guard(&SamplerThread::sampler_list_access_counter_, false);
if (!atomic_guard.is_success()) return;
pthread_t thread_id = pthread_self();
HashMap::Entry* entry =
SamplerThread::thread_id_to_samplers_.Pointer()->Lookup(
ThreadKey(thread_id), ThreadHash(thread_id));
if (entry == NULL)
return;
SamplerList* samplers = reinterpret_cast<SamplerList*>(entry->value);
for (int i = 0; i < samplers->length(); ++i) {
Sampler* sampler = samplers->at(i);
CollectSample(context, sampler);
}
}
#endif // !V8_OS_NACL
#endif // USE_SIGNALs
//
......@@ -789,6 +944,7 @@ Sampler::Sampler(Isolate* isolate, int interval)
profiling_(false),
has_processing_thread_(false),
active_(false),
registered_(false),
is_counting_samples_(false),
js_sample_count_(0),
external_sample_count_(0) {
......@@ -797,6 +953,9 @@ Sampler::Sampler(Isolate* isolate, int interval)
Sampler::~Sampler() {
DCHECK(!IsActive());
if (IsRegistered()) {
SamplerThread::RemoveSampler(this);
}
delete data_;
}
......@@ -809,8 +968,9 @@ void Sampler::Start() {
void Sampler::Stop() {
DCHECK(IsActive());
SamplerThread::RemoveActiveSampler(this);
SamplerThread::RemoveSampler(this);
SetActive(false);
SetRegistered(false);
}
......@@ -850,6 +1010,10 @@ void Sampler::SampleStack(const v8::RegisterState& state) {
void Sampler::DoSample() {
if (!SignalHandler::Installed()) return;
if (!IsActive() && !IsRegistered()) {
SamplerThread::RegisterSampler(this);
SetRegistered(true);
}
pthread_kill(platform_data()->vm_tid(), SIGPROF);
}
......
......@@ -92,6 +92,11 @@ class Sampler {
// Whether the sampler is running (that is, consumes resources).
bool IsActive() const { return base::NoBarrier_Load(&active_); }
// CpuProfiler collects samples by calling DoSample directly
// without calling Start. To keep it working, we register the sampler
// with the CpuProfiler.
bool IsRegistered() const { return base::NoBarrier_Load(&registered_); }
void DoSample();
// If true next sample must be initiated on the profiler event processor
// thread right after latest sample is processed.
......@@ -119,11 +124,14 @@ class Sampler {
private:
void SetActive(bool value) { base::NoBarrier_Store(&active_, value); }
void SetRegistered(bool value) { base::NoBarrier_Store(&registered_, value); }
Isolate* isolate_;
const int interval_;
base::Atomic32 profiling_;
base::Atomic32 has_processing_thread_;
base::Atomic32 active_;
base::Atomic32 registered_;
PlatformData* data_; // Platform specific data.
// Counts stack samples taken in various VM states.
bool is_counting_samples_;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment