Switch profiler from isolates to threads

- API breakage: Dart_IsolateBlocked, Dart_IsolateUnblocked -> Dart_ThreadDisableProfiling, Dart_ThreadEnableProfiling.
- Remove IsolateProfilerData.
- Move thread at blocking call count from isolate to thread.
- Always interrupt threads unless they are blocked.
- We can no longer count "idle" ticks.
- Only record sample if thread is the current mutator of an isolate.
- Refactor ThreadInterrupterCallback to ensure that Thread* is valid.

Threads are only ever sent signals if ThreadInterruptsEnabled is true. Which is controlled by two functions:

void DisableThreadInterrupts();
void EnableThreadInterrupts();

R=asiva@google.com, iposva@google.com

Review URL: https://codereview.chromium.org/1423473004 .
This commit is contained in:
John McCutchan 2015-11-04 07:59:16 -08:00
parent b297a4ffb4
commit ceb12c4f69
27 changed files with 232 additions and 555 deletions

View file

@ -581,11 +581,11 @@ class CObjectExternalUint8Array : public CObject {
class ScopedBlockingCall {
public:
ScopedBlockingCall() {
Dart_IsolateBlocked();
Dart_ThreadDisableProfiling();
}
~ScopedBlockingCall() {
Dart_IsolateUnblocked();
Dart_ThreadEnableProfiling();
}
};

View file

@ -1027,14 +1027,25 @@ DART_EXPORT Dart_Handle Dart_DebugName();
DART_EXPORT void Dart_EnterIsolate(Dart_Isolate isolate);
/**
* Notifies the VM that the current isolate is about to make a blocking call.
* Notifies the VM that the current thread should not be profiled until a
* matching call to Dart_ThreadEnableProfiling is made.
*
* NOTE: By default, if a thread has entered an isolate it will be profiled.
* This function should be used when an embedder knows a thread is about
* to make a blocking call and wants to avoid unnecessary interrupts by
* the profiler.
*/
DART_EXPORT void Dart_IsolateBlocked();
DART_EXPORT void Dart_ThreadDisableProfiling();
/**
* Notifies the VM that the current isolate is no longer blocked.
* Notifies the VM that the current thread should be profiled.
*
* NOTE: It is only legal to call this function *after* calling
* Dart_ThreadDisableProfiling.
*
* NOTE: By default, if a thread has entered an isolate it will be profiled.
*/
DART_EXPORT void Dart_IsolateUnblocked();
DART_EXPORT void Dart_ThreadEnableProfiling();
/**
* Exits an isolate. After this call, Dart_CurrentIsolate will

View file

@ -25,28 +25,11 @@ cc/VerifyExplicit_Crash: Crash
[ $system == windows ]
cc/Dart2JSCompileAll: Skip
cc/ExternalizeConstantStrings: Skip
cc/ThreadInterrupterHigh: Skip
cc/ThreadInterrupterMedium: Skip
cc/ThreadInterrupterLow: Skip
cc/Service_Profile: Skip
cc/Dart2JSCompilerStats: Skip
cc/CorelibCompilerStats: Skip
[ $system == linux ]
cc/ThreadInterrupterHigh: Skip
cc/ThreadInterrupterMedium: Skip
cc/ThreadInterrupterLow: Skip
[ $system == macos ]
cc/ThreadInterrupterHigh: Skip
cc/ThreadInterrupterMedium: Skip
cc/ThreadInterrupterLow: Skip
[ $arch == simarm || $arch == simarmv5te || $arch == simarm64 || $arch == simmips ]
cc/ThreadInterrupterHigh: Skip
cc/ThreadInterrupterMedium: Skip
cc/ThreadInterrupterLow: Skip
cc/Service_Profile: Skip
[ $arch == arm ]

View file

@ -296,9 +296,6 @@ RawError* Dart::InitializeIsolate(const uint8_t* snapshot_buffer, void* data) {
ObjectStore::Init(I);
}
// Setup for profiling.
Profiler::InitProfilingForIsolate(I);
const Error& error = Error::Handle(Object::Init(I));
if (!error.IsNull()) {
return error.raw();

View file

@ -1464,25 +1464,21 @@ DART_EXPORT void Dart_EnterIsolate(Dart_Isolate isolate) {
}
DART_EXPORT void Dart_IsolateBlocked() {
Isolate* isolate = Isolate::Current();
CHECK_ISOLATE(isolate);
IsolateProfilerData* profiler_data = isolate->profiler_data();
if (profiler_data == NULL) {
DART_EXPORT void Dart_ThreadDisableProfiling() {
Thread* T = Thread::Current();
if (T == NULL) {
return;
}
profiler_data->Block();
T->DisableThreadInterrupts();
}
DART_EXPORT void Dart_IsolateUnblocked() {
Isolate* isolate = Isolate::Current();
CHECK_ISOLATE(isolate);
IsolateProfilerData* profiler_data = isolate->profiler_data();
if (profiler_data == NULL) {
DART_EXPORT void Dart_ThreadEnableProfiling() {
Thread* T = Thread::Current();
if (T == NULL) {
return;
}
profiler_data->Unblock();
T->EnableThreadInterrupts();
}

View file

@ -2515,7 +2515,12 @@ void Debugger::Pause(DebuggerEvent* event) {
pause_event_->UpdateTimestamp();
obj_cache_ = new RemoteObjectCache(64);
// We are about to invoke the debuggers event handler. Disable interrupts
// for this thread while waiting for debug commands over the service protocol.
{
DisableThreadInterruptsScope dtis(Thread::Current());
InvokeEventHandler(event);
}
pause_event_ = NULL;
obj_cache_ = NULL; // Zone allocated
@ -2556,11 +2561,6 @@ void Debugger::HandleSteppingRequest(DebuggerStackTrace* stack_trace) {
}
}
}
if (!isolate_->single_step()) {
// We are no longer single stepping, make sure that the ThreadInterrupter
// is awake.
ThreadInterrupter::WakeUp();
}
}

View file

@ -784,7 +784,6 @@ Isolate::Isolate(const Dart_IsolateFlags& api_flags)
last_allocationprofile_gc_timestamp_(0),
object_id_ring_(NULL),
trace_buffer_(NULL),
profiler_data_(NULL),
tag_table_(GrowableObjectArray::null()),
deoptimized_code_array_(GrowableObjectArray::null()),
background_compiler_(NULL),
@ -1706,7 +1705,6 @@ void Isolate::Shutdown() {
Thread::ExitIsolate();
// All threads should have exited by now.
thread_registry()->CheckNotScheduled(this);
Profiler::ShutdownProfilingForIsolate(this);
}
@ -1911,62 +1909,6 @@ void Isolate::PrintJSON(JSONStream* stream, bool ref) {
}
intptr_t Isolate::ProfileInterrupt() {
// Other threads might be modifying these fields. Save them in locals so that
// we can at least trust the NULL check.
IsolateProfilerData* prof_data = profiler_data();
if (prof_data == NULL) {
// Profiler not setup for isolate.
return 0;
}
if (prof_data->blocked()) {
// Profiler blocked for this isolate.
return 0;
}
Debugger* debug = debugger();
if ((debug != NULL) && debug->IsPaused()) {
// Paused at breakpoint. Don't tick.
return 0;
}
MessageHandler* msg_handler = message_handler();
if ((msg_handler != NULL) &&
(msg_handler->paused_on_start() ||
msg_handler->paused_on_exit())) {
// Paused at start / exit . Don't tick.
return 0;
}
// Make sure that the isolate's mutator thread does not change behind our
// backs. Otherwise we find the entry in the registry and end up reading
// the field again. Only by that time it has been reset to NULL because the
// thread was in process of exiting the isolate.
Thread* mutator = mutator_thread_;
if (mutator == NULL) {
// No active mutator.
ProfileIdle();
return 1;
}
// TODO(johnmccutchan): Sample all threads, not just the mutator thread.
// TODO(johnmccutchan): Keep a global list of threads and use that
// instead of Isolate.
ThreadRegistry::EntryIterator it(thread_registry());
while (it.HasNext()) {
const ThreadRegistry::Entry& entry = it.Next();
if (entry.thread == mutator) {
ThreadInterrupter::InterruptThread(mutator);
break;
}
}
return 1;
}
void Isolate::ProfileIdle() {
// Currently we are only sampling the mutator thread.
vm_tag_counters_.Increment(VMTag::kIdleTagId);
}
void Isolate::set_tag_table(const GrowableObjectArray& value) {
tag_table_ = value.raw();
}

View file

@ -549,27 +549,12 @@ class Isolate : public BaseIsolate {
return defer_finalization_count_ == 0;
}
Mutex* profiler_data_mutex() {
return &profiler_data_mutex_;
}
void set_profiler_data(IsolateProfilerData* profiler_data) {
profiler_data_ = profiler_data;
}
IsolateProfilerData* profiler_data() const {
return profiler_data_;
}
void PrintJSON(JSONStream* stream, bool ref = true);
CompilerStats* compiler_stats() {
return compiler_stats_;
}
// Returns the number of sampled threads.
intptr_t ProfileInterrupt();
VMTagCounters* vm_tag_counters() {
return &vm_tag_counters_;
}
@ -795,9 +780,6 @@ class Isolate : public BaseIsolate {
// Trace buffer support.
TraceBuffer* trace_buffer_;
IsolateProfilerData* profiler_data_;
Mutex profiler_data_mutex_;
VMTagCounters vm_tag_counters_;
RawGrowableObjectArray* tag_table_;

View file

@ -183,10 +183,6 @@ MessageHandler::MessageStatus MessageHandler::HandleMessages(
// If isolate() returns NULL StartIsolateScope does nothing.
StartIsolateScope start_isolate(isolate());
// ThreadInterrupter may have gone to sleep while waiting for
// an isolate to start handling messages.
ThreadInterrupter::WakeUp();
MessageStatus max_status = kOK;
Message::Priority min_priority = ((allow_normal_messages && !paused())
? Message::kNormalPriority

View file

@ -1859,7 +1859,7 @@ RawObject* Object::Allocate(intptr_t cls_id,
}
const Class& cls = Class::Handle(class_table->At(cls_id));
if (cls.TraceAllocation(isolate)) {
Profiler::RecordAllocation(thread, cls_id);
Profiler::SampleAllocation(thread, cls_id);
}
NoSafepointScope no_safepoint;
InitializeObject(address, cls_id, size, (isolate == Dart::vm_isolate()));

View file

@ -9,10 +9,12 @@
#include "vm/allocation.h"
#include "vm/atomic.h"
#include "vm/code_patcher.h"
#include "vm/debugger.h"
#include "vm/instructions.h"
#include "vm/isolate.h"
#include "vm/json_stream.h"
#include "vm/lockers.h"
#include "vm/message_handler.h"
#include "vm/native_symbol.h"
#include "vm/object.h"
#include "vm/os.h"
@ -104,120 +106,6 @@ void Profiler::SetSamplePeriod(intptr_t period) {
}
void Profiler::InitProfilingForIsolate(Isolate* isolate, bool shared_buffer) {
if (!FLAG_profile) {
return;
}
ASSERT(isolate == Isolate::Current());
ASSERT(isolate != NULL);
ASSERT(sample_buffer_ != NULL);
{
MutexLocker profiler_data_lock(isolate->profiler_data_mutex());
SampleBuffer* sample_buffer = sample_buffer_;
if (!shared_buffer) {
sample_buffer = new SampleBuffer();
}
IsolateProfilerData* profiler_data =
new IsolateProfilerData(sample_buffer, !shared_buffer);
ASSERT(profiler_data != NULL);
isolate->set_profiler_data(profiler_data);
if (FLAG_trace_profiled_isolates) {
OS::Print("Profiler Setup %p %s\n", isolate, isolate->name());
}
}
BeginExecution(isolate);
}
void Profiler::ShutdownProfilingForIsolate(Isolate* isolate) {
ASSERT(isolate != NULL);
if (!FLAG_profile) {
return;
}
// We do not have a current isolate.
ASSERT(Isolate::Current() == NULL);
{
MutexLocker profiler_data_lock(isolate->profiler_data_mutex());
IsolateProfilerData* profiler_data = isolate->profiler_data();
if (profiler_data == NULL) {
// Already freed.
return;
}
isolate->set_profiler_data(NULL);
delete profiler_data;
if (FLAG_trace_profiled_isolates) {
OS::Print("Profiler Shutdown %p %s\n", isolate, isolate->name());
}
}
}
void Profiler::BeginExecution(Isolate* isolate) {
if (isolate == NULL) {
return;
}
if (!FLAG_profile) {
return;
}
ASSERT(initialized_);
IsolateProfilerData* profiler_data = isolate->profiler_data();
if (profiler_data == NULL) {
return;
}
Thread* thread = Thread::Current();
thread->SetThreadInterrupter(RecordSampleInterruptCallback, thread);
ThreadInterrupter::WakeUp();
}
void Profiler::EndExecution(Isolate* isolate) {
if (isolate == NULL) {
return;
}
if (!FLAG_profile) {
return;
}
ASSERT(initialized_);
Thread* thread = Thread::Current();
thread->SetThreadInterrupter(NULL, NULL);
}
IsolateProfilerData::IsolateProfilerData(SampleBuffer* sample_buffer,
bool own_sample_buffer) {
ASSERT(sample_buffer != NULL);
sample_buffer_ = sample_buffer;
own_sample_buffer_ = own_sample_buffer;
block_count_ = 0;
}
IsolateProfilerData::~IsolateProfilerData() {
if (own_sample_buffer_) {
delete sample_buffer_;
sample_buffer_ = NULL;
own_sample_buffer_ = false;
}
}
void IsolateProfilerData::Block() {
block_count_++;
}
void IsolateProfilerData::Unblock() {
block_count_--;
if (block_count_ < 0) {
FATAL("Too many calls to Dart_IsolateUnblocked.");
}
if (!blocked()) {
// We just unblocked this isolate, wake up the thread interrupter.
ThreadInterrupter::WakeUp();
}
}
intptr_t Sample::pcs_length_ = 0;
intptr_t Sample::instance_size_ = 0;
@ -817,10 +705,10 @@ static void CollectSample(Isolate* isolate,
if (FLAG_profile_vm) {
// Always walk the native stack collecting both native and Dart frames.
native_stack_walker->walk();
} else if (exited_dart_code) {
} else if (StubCode::HasBeenInitialized() && exited_dart_code) {
// We have a valid exit frame info, use the Dart stack walker.
dart_exit_stack_walker->walk();
} else if (in_dart_code) {
} else if (StubCode::HasBeenInitialized() && in_dart_code) {
// We are executing Dart code. We have frame pointers.
dart_stack_walker->walk();
} else {
@ -851,26 +739,11 @@ static void CollectSample(Isolate* isolate,
}
// Is |thread| executing Dart code?
static bool ExecutingDart(Thread* thread) {
ASSERT(thread != NULL);
return (thread->top_exit_frame_info() == 0) &&
(thread->vm_tag() == VMTag::kDartTagId);
}
// Has |thread| exited Dart code?
static bool ExitedDart(Thread* thread) {
return (thread->top_exit_frame_info() != 0) &&
(thread->vm_tag() != VMTag::kDartTagId);
}
// Get |isolate|'s stack boundary and verify that |sp| and |fp| are within
// it. Return |false| if anything looks suspicious.
static bool GetAndValidateIsolateStackBounds(Thread* thread,
uintptr_t sp,
uintptr_t fp,
uintptr_t sp,
uword* stack_lower,
uword* stack_upper) {
ASSERT(thread != NULL);
@ -879,7 +752,7 @@ static bool GetAndValidateIsolateStackBounds(Thread* thread,
ASSERT(stack_lower != NULL);
ASSERT(stack_upper != NULL);
#if defined(USING_SIMULATOR)
const bool in_dart_code = ExecutingDart(thread);
const bool in_dart_code = thread->IsExecutingDartCode();
if (in_dart_code) {
Simulator* simulator = isolate->simulator();
*stack_lower = simulator->StackBase();
@ -922,8 +795,8 @@ static bool GetAndValidateIsolateStackBounds(Thread* thread,
}
// Some simple sanity checking of |pc|, |sp|, and |fp|.
static bool InitialRegisterCheck(uintptr_t pc, uintptr_t sp, uintptr_t fp) {
// Some simple sanity checking of |pc|, |fp|, and |sp|.
static bool InitialRegisterCheck(uintptr_t pc, uintptr_t fp, uintptr_t sp) {
if ((sp == 0) || (fp == 0) || (pc == 0)) {
// None of these registers should be zero.
return false;
@ -939,18 +812,6 @@ static bool InitialRegisterCheck(uintptr_t pc, uintptr_t sp, uintptr_t fp) {
}
// Return |isolate|'s sample buffer.
static SampleBuffer* GetSampleBuffer(Isolate* isolate) {
IsolateProfilerData* profiler_data = isolate->profiler_data();
if (profiler_data == NULL) {
// Profiler not initialized.
return NULL;
}
SampleBuffer* sample_buffer = profiler_data->sample_buffer();
return sample_buffer;
}
static Sample* SetupSample(Thread* thread,
SampleBuffer* sample_buffer,
ThreadId tid) {
@ -980,8 +841,7 @@ static bool CheckIsolate(Isolate* isolate) {
// No isolate.
return false;
}
ASSERT(isolate != Dart::vm_isolate());
return true;
return isolate != Dart::vm_isolate();
}
@ -997,16 +857,16 @@ static uintptr_t __attribute__((noinline)) GetProgramCounter() {
}
#endif
void Profiler::RecordAllocation(Thread* thread, intptr_t cid) {
void Profiler::SampleAllocation(Thread* thread, intptr_t cid) {
ASSERT(thread != NULL);
Isolate* isolate = thread->isolate();
if (!CheckIsolate(isolate)) {
return;
}
const bool exited_dart_code = ExitedDart(thread);
const bool exited_dart_code = thread->HasExitedDartCode();
SampleBuffer* sample_buffer = GetSampleBuffer(isolate);
SampleBuffer* sample_buffer = Profiler::sample_buffer();
if (sample_buffer == NULL) {
// Profiler not initialized.
return;
@ -1022,13 +882,13 @@ void Profiler::RecordAllocation(Thread* thread, intptr_t cid) {
uword stack_lower = 0;
uword stack_upper = 0;
if (!InitialRegisterCheck(pc, sp, fp)) {
if (!InitialRegisterCheck(pc, fp, sp)) {
return;
}
if (!GetAndValidateIsolateStackBounds(thread,
sp,
fp,
sp,
&stack_lower,
&stack_upper)) {
// Could not get stack boundary.
@ -1071,25 +931,17 @@ void Profiler::RecordAllocation(Thread* thread, intptr_t cid) {
}
void Profiler::RecordSampleInterruptCallback(
const InterruptedThreadState& state,
void* data) {
Thread* thread = reinterpret_cast<Thread*>(data);
Isolate* isolate = thread->isolate();
if ((isolate == NULL) || (Dart::vm_isolate() == NULL)) {
// No isolate.
return;
}
ASSERT(isolate != Dart::vm_isolate());
SampleBuffer* sample_buffer = GetSampleBuffer(isolate);
if (sample_buffer == NULL) {
// Profiler not initialized.
void Profiler::SampleThread(Thread* thread,
const InterruptedThreadState& state) {
if (StubCode::HasBeenInitialized() &&
StubCode::InJumpToExceptionHandlerStub(state.pc)) {
// The JumpToExceptionHandler stub manually adjusts the stack pointer,
// frame pointer, and some isolate state before jumping to a catch entry.
// It is not safe to walk the stack when executing this stub.
return;
}
const bool exited_dart_code = ExitedDart(thread);
const bool in_dart_code = ExecutingDart(thread);
const bool in_dart_code = thread->IsExecutingDartCode();
uintptr_t sp = 0;
uintptr_t fp = state.fp;
@ -1113,22 +965,27 @@ void Profiler::RecordSampleInterruptCallback(
sp = state.csp;
}
if (!InitialRegisterCheck(pc, sp, fp)) {
if (!InitialRegisterCheck(pc, fp, sp)) {
return;
}
if (StubCode::InJumpToExceptionHandlerStub(pc)) {
// The JumpToExceptionHandler stub manually adjusts the stack pointer,
// frame pointer, and some isolate state before jumping to a catch entry.
// It is not safe to walk the stack when executing this stub.
ASSERT(thread != NULL);
Isolate* isolate = thread->isolate();
if (!CheckIsolate(isolate)) {
return;
}
if (!thread->IsMutatorThread()) {
// Not a mutator thread.
// TODO(johnmccutchan): Profile all threads with an isolate.
return;
}
uword stack_lower = 0;
uword stack_upper = 0;
if (!GetAndValidateIsolateStackBounds(thread,
sp,
fp,
sp,
&stack_lower,
&stack_upper)) {
// Could not get stack boundary.
@ -1137,6 +994,11 @@ void Profiler::RecordSampleInterruptCallback(
// At this point we have a valid stack boundary for this isolate and
// know that our initial stack and frame pointers are within the boundary.
SampleBuffer* sample_buffer = Profiler::sample_buffer();
if (sample_buffer == NULL) {
// Profiler not initialized.
return;
}
// Setup sample.
Sample* sample = SetupSample(thread,
@ -1170,6 +1032,8 @@ void Profiler::RecordSampleInterruptCallback(
fp,
sp);
const bool exited_dart_code = thread->HasExitedDartCode();
// All memory access is done inside CollectSample.
CollectSample(isolate,
exited_dart_code,

View file

@ -33,55 +33,21 @@ class Profiler : public AllStatic {
static void SetSampleDepth(intptr_t depth);
static void SetSamplePeriod(intptr_t period);
static void InitProfilingForIsolate(Isolate* isolate,
bool shared_buffer = true);
static void ShutdownProfilingForIsolate(Isolate* isolate);
static void BeginExecution(Isolate* isolate);
static void EndExecution(Isolate* isolate);
static SampleBuffer* sample_buffer() {
return sample_buffer_;
}
static void RecordAllocation(Thread* thread, intptr_t cid);
static void SampleAllocation(Thread* thread, intptr_t cid);
static void SampleThread(Thread* thread,
const InterruptedThreadState& state);
private:
static bool initialized_;
static Monitor* monitor_;
static void RecordSampleInterruptCallback(const InterruptedThreadState& state,
void* data);
static SampleBuffer* sample_buffer_;
};
class IsolateProfilerData {
public:
IsolateProfilerData(SampleBuffer* sample_buffer, bool own_sample_buffer);
~IsolateProfilerData();
SampleBuffer* sample_buffer() const { return sample_buffer_; }
void set_sample_buffer(SampleBuffer* sample_buffer) {
sample_buffer_ = sample_buffer;
}
bool blocked() const {
return block_count_ > 0;
}
void Block();
void Unblock();
private:
SampleBuffer* sample_buffer_;
bool own_sample_buffer_;
intptr_t block_count_;
DISALLOW_COPY_AND_ASSIGN(IsolateProfilerData);
friend class Thread;
};

View file

@ -1051,13 +1051,7 @@ class ProfileBuilder : public ValueObject {
void FilterSamples() {
ScopeTimer sw("ProfileBuilder::FilterSamples", FLAG_trace_profiler);
Isolate* isolate = thread_->isolate();
MutexLocker profiler_data_lock(isolate->profiler_data_mutex());
IsolateProfilerData* profiler_data = isolate->profiler_data();
if (profiler_data == NULL) {
return;
}
SampleBuffer* sample_buffer = profiler_data->sample_buffer();
SampleBuffer* sample_buffer = Profiler::sample_buffer();
if (sample_buffer == NULL) {
return;
}
@ -2219,17 +2213,14 @@ void ProfilerService::PrintJSONImpl(Thread* thread,
intptr_t extra_tags,
SampleFilter* filter) {
Isolate* isolate = thread->isolate();
// Disable profile interrupts while processing the buffer.
Profiler::EndExecution(isolate);
// Disable thread interrupts while processing the buffer.
DisableThreadInterruptsScope dtis(thread);
{
MutexLocker profiler_data_lock(isolate->profiler_data_mutex());
IsolateProfilerData* profiler_data = isolate->profiler_data();
if (profiler_data == NULL) {
SampleBuffer* sample_buffer = Profiler::sample_buffer();
if (sample_buffer == NULL) {
stream->PrintError(kFeatureDisabled, NULL);
return;
}
}
{
StackZone zone(thread);
@ -2238,9 +2229,6 @@ void ProfilerService::PrintJSONImpl(Thread* thread,
profile.Build(thread, filter, tag_order, extra_tags);
profile.PrintJSON(stream);
}
// Enable profile interrupts.
Profiler::BeginExecution(isolate);
}
@ -2295,24 +2283,19 @@ void ProfilerService::PrintAllocationJSON(JSONStream* stream,
void ProfilerService::ClearSamples() {
Isolate* isolate = Isolate::Current();
// Disable profile interrupts while processing the buffer.
Profiler::EndExecution(isolate);
MutexLocker profiler_data_lock(isolate->profiler_data_mutex());
IsolateProfilerData* profiler_data = isolate->profiler_data();
if (profiler_data == NULL) {
SampleBuffer* sample_buffer = Profiler::sample_buffer();
if (sample_buffer == NULL) {
return;
}
SampleBuffer* sample_buffer = profiler_data->sample_buffer();
ASSERT(sample_buffer != NULL);
Thread* thread = Thread::Current();
Isolate* isolate = thread->isolate();
// Disable thread interrupts while processing the buffer.
DisableThreadInterruptsScope dtis(thread);
ClearProfileVisitor clear_profile(isolate);
sample_buffer->VisitSamples(&clear_profile);
// Enable profile interrupts.
Profiler::BeginExecution(isolate);
}
} // namespace dart

View file

@ -846,7 +846,7 @@ TEST_CASE(Profiler_ClassAllocation) {
// TODO(johnmccutchan): Hookup native symbol resolver on Windows.
EXPECT_SUBSTRING("[Native]", walker.CurrentName());
#else
EXPECT_SUBSTRING("dart::Profiler::RecordAllocation", walker.CurrentName());
EXPECT_SUBSTRING("dart::Profiler::SampleAllocation", walker.CurrentName());
#endif
EXPECT(!walker.Down());
}

View file

@ -79,7 +79,16 @@ void StubCode::VisitObjectPointers(ObjectPointerVisitor* visitor) {
}
bool StubCode::HasBeenInitialized() {
// Use JumpToExceptionHandler and InvokeDart as canaries.
const StubEntry* entry_1 = StubCode::JumpToExceptionHandler_entry();
const StubEntry* entry_2 = StubCode::InvokeDartCode_entry();
return (entry_1 != NULL) && (entry_2 != NULL);
}
bool StubCode::InInvocationStub(uword pc) {
ASSERT(HasBeenInitialized());
uword entry = StubCode::InvokeDartCode_entry()->EntryPoint();
uword size = StubCode::InvokeDartCodeSize();
return (pc >= entry) && (pc < (entry + size));
@ -87,6 +96,7 @@ bool StubCode::InInvocationStub(uword pc) {
bool StubCode::InJumpToExceptionHandlerStub(uword pc) {
ASSERT(HasBeenInitialized());
uword entry = StubCode::JumpToExceptionHandler_entry()->EntryPoint();
uword size = StubCode::JumpToExceptionHandlerSize();
return (pc >= entry) && (pc < (entry + size));

View file

@ -109,6 +109,9 @@ class StubCode : public AllStatic {
static void VisitObjectPointers(ObjectPointerVisitor* visitor);
// Returns true if stub code has been initialized.
static bool HasBeenInitialized();
// Check if specified pc is in the dart invocation stub used for
// transitioning into dart code.
static bool InInvocationStub(uword pc);

View file

@ -214,8 +214,7 @@ void Thread::EnsureInit() {
Thread::Thread(bool init_vm_constants)
: id_(OSThread::GetCurrentThreadId()),
join_id_(OSThread::GetCurrentThreadJoinId()),
thread_interrupt_callback_(NULL),
thread_interrupt_data_(NULL),
thread_interrupt_disabled_(1), // Thread interrupts disabled by default.
isolate_(NULL),
heap_(NULL),
timeline_block_(NULL),
@ -331,8 +330,7 @@ void Thread::EnterIsolate(Isolate* isolate) {
ASSERT(isolate->heap() != NULL);
thread->heap_ = isolate->heap();
thread->Schedule(isolate);
// TODO(koda): Migrate profiler interface to use Thread.
Profiler::BeginExecution(isolate);
thread->EnableThreadInterrupts();
}
@ -343,10 +341,10 @@ void Thread::ExitIsolate() {
#if defined(DEBUG)
ASSERT(!thread->IsAnyReusableHandleScopeActive());
#endif // DEBUG
thread->DisableThreadInterrupts();
// Clear since GC will not visit the thread once it is unscheduled.
thread->ClearReusableHandles();
Isolate* isolate = thread->isolate();
Profiler::EndExecution(isolate);
thread->Unschedule();
// TODO(koda): Move store_buffer_block_ into State.
thread->StoreBufferRelease();
@ -373,17 +371,17 @@ void Thread::EnterIsolateAsHelper(Isolate* isolate, bool bypass_safepoint) {
thread->isolate()->store_buffer()->PopEmptyBlock();
ASSERT(isolate->heap() != NULL);
thread->heap_ = isolate->heap();
ASSERT(thread->thread_interrupt_callback_ == NULL);
ASSERT(thread->thread_interrupt_data_ == NULL);
// Do not update isolate->mutator_thread, but perform sanity check:
// this thread should not be both the main mutator and helper.
ASSERT(!thread->IsMutatorThread());
thread->Schedule(isolate, bypass_safepoint);
thread->EnableThreadInterrupts();
}
void Thread::ExitIsolateAsHelper(bool bypass_safepoint) {
Thread* thread = Thread::Current();
thread->DisableThreadInterrupts();
Isolate* isolate = thread->isolate();
ASSERT(isolate != NULL);
thread->Unschedule(bypass_safepoint);
@ -447,6 +445,18 @@ bool Thread::IsMutatorThread() const {
}
bool Thread::IsExecutingDartCode() const {
return (top_exit_frame_info() == 0) &&
(vm_tag() == VMTag::kDartTagId);
}
bool Thread::HasExitedDartCode() const {
return (top_exit_frame_info() != 0) &&
(vm_tag() != VMTag::kDartTagId);
}
CHA* Thread::cha() const {
ASSERT(isolate_ != NULL);
return cha_;
@ -493,29 +503,31 @@ void Thread::VisitObjectPointers(ObjectPointerVisitor* visitor) {
}
void Thread::SetThreadInterrupter(ThreadInterruptCallback callback,
void* data) {
void Thread::DisableThreadInterrupts() {
ASSERT(Thread::Current() == this);
thread_interrupt_callback_ = callback;
thread_interrupt_data_ = data;
AtomicOperations::FetchAndIncrement(&thread_interrupt_disabled_);
}
bool Thread::IsThreadInterrupterEnabled(ThreadInterruptCallback* callback,
void** data) const {
#if defined(TARGET_OS_WINDOWS)
// On Windows we expect this to be called from the thread interrupter thread.
ASSERT(id() != OSThread::GetCurrentThreadId());
#else
// On posix platforms, we expect this to be called from signal handler.
ASSERT(id() == OSThread::GetCurrentThreadId());
#endif
ASSERT(callback != NULL);
ASSERT(data != NULL);
*callback = thread_interrupt_callback_;
*data = thread_interrupt_data_;
return (*callback != NULL) &&
(*data != NULL);
void Thread::EnableThreadInterrupts() {
ASSERT(Thread::Current() == this);
uintptr_t old =
AtomicOperations::FetchAndDecrement(&thread_interrupt_disabled_);
if (old == 1) {
// We just decremented from 1 to 0.
// Make sure the thread interrupter is awake.
ThreadInterrupter::WakeUp();
}
if (old == 0) {
// We just decremented from 0, this means we've got a mismatched pair
// of calls to EnableThreadInterrupts and DisableThreadInterrupts.
FATAL("Invalid call to Thread::EnableThreadInterrupts()");
}
}
bool Thread::ThreadInterruptsEnabled() {
return AtomicOperations::LoadRelaxed(&thread_interrupt_disabled_) == 0;
}
@ -590,4 +602,18 @@ Thread* ThreadIterator::Next() {
}
DisableThreadInterruptsScope::DisableThreadInterruptsScope(Thread* thread)
: StackResource(thread) {
if (thread != NULL) {
thread->DisableThreadInterrupts();
}
}
DisableThreadInterruptsScope::~DisableThreadInterruptsScope() {
if (thread() != NULL) {
thread()->EnableThreadInterrupts();
}
}
} // namespace dart

View file

@ -92,7 +92,6 @@ class Zone;
CACHED_ADDRESSES_LIST(V) \
struct InterruptedThreadState {
ThreadId tid;
uintptr_t pc;
uintptr_t csp;
uintptr_t dsp;
@ -109,8 +108,8 @@ struct InterruptedThreadState {
// * Allocating memory -- Because this takes locks which may already be held,
// resulting in a dead lock.
// * Taking a lock -- See above.
typedef void (*ThreadInterruptCallback)(const InterruptedThreadState& state,
void* data);
typedef void (*ThreadInterruptCallback)(Thread* thread,
const InterruptedThreadState& state);
// A VM thread; may be executing Dart code or performing helper tasks like
// garbage collection or compilation. The Thread structure associated with
@ -163,6 +162,12 @@ class Thread {
}
bool IsMutatorThread() const;
// Is |this| executing Dart code?
bool IsExecutingDartCode() const;
// Has |this| exited Dart code?
bool HasExitedDartCode() const;
// The (topmost) CHA for the compilation in this thread.
CHA* cha() const;
void set_cha(CHA* value);
@ -377,10 +382,10 @@ LEAF_RUNTIME_ENTRY_LIST(DEFINE_OFFSET_METHOD)
return join_id_;
}
void SetThreadInterrupter(ThreadInterruptCallback callback, void* data);
bool IsThreadInterrupterEnabled(ThreadInterruptCallback* callback,
void** data) const;
// Used to temporarily disable or enable thread interrupts.
void DisableThreadInterrupts();
void EnableThreadInterrupts();
bool ThreadInterruptsEnabled();
#if defined(DEBUG)
#define REUSABLE_HANDLE_SCOPE_ACCESSORS(object) \
@ -424,8 +429,7 @@ LEAF_RUNTIME_ENTRY_LIST(DEFINE_OFFSET_METHOD)
const ThreadId id_;
const ThreadId join_id_;
ThreadInterruptCallback thread_interrupt_callback_;
void* thread_interrupt_data_;
uintptr_t thread_interrupt_disabled_;
Isolate* isolate_;
Heap* heap_;
State state_;
@ -542,6 +546,14 @@ class ThreadIterator : public ValueObject {
void WindowsThreadCleanUp();
#endif
// Disable thread interrupts.
class DisableThreadInterruptsScope : public StackResource {
public:
explicit DisableThreadInterruptsScope(Thread* thread);
~DisableThreadInterruptsScope();
};
} // namespace dart
#endif // VM_THREAD_H_

View file

@ -51,6 +51,7 @@ DEFINE_FLAG(bool, trace_thread_interrupter, false,
bool ThreadInterrupter::initialized_ = false;
bool ThreadInterrupter::shutdown_ = false;
bool ThreadInterrupter::thread_running_ = false;
bool ThreadInterrupter::woken_up_ = false;
ThreadJoinId ThreadInterrupter::interrupter_thread_id_ =
OSThread::kInvalidThreadJoinId;
Monitor* ThreadInterrupter::monitor_ = NULL;
@ -125,9 +126,14 @@ void ThreadInterrupter::SetInterruptPeriod(intptr_t period) {
void ThreadInterrupter::WakeUp() {
if (!initialized_) {
// Early call.
return;
}
ASSERT(initialized_);
{
MonitorLocker ml(monitor_);
woken_up_ = true;
if (!InDeepSleep()) {
// No need to notify, regularly waking up.
return;
@ -138,35 +144,6 @@ void ThreadInterrupter::WakeUp() {
}
void ThreadInterruptNoOp(const InterruptedThreadState& state, void* data) {
// NoOp.
}
class ThreadInterrupterVisitIsolates : public IsolateVisitor {
public:
ThreadInterrupterVisitIsolates() {
profiled_thread_count_ = 0;
}
void VisitIsolate(Isolate* isolate) {
ASSERT(isolate != NULL);
profiled_thread_count_ += isolate->ProfileInterrupt();
}
intptr_t profiled_thread_count() const {
return profiled_thread_count_;
}
void set_profiled_thread_count(intptr_t profiled_thread_count) {
profiled_thread_count_ = profiled_thread_count;
}
private:
intptr_t profiled_thread_count_;
};
void ThreadInterrupter::ThreadMain(uword parameters) {
ASSERT(initialized_);
InstallSignalHandler();
@ -181,35 +158,55 @@ void ThreadInterrupter::ThreadMain(uword parameters) {
startup_ml.Notify();
}
{
ThreadInterrupterVisitIsolates visitor;
intptr_t interrupted_thread_count = 0;
current_wait_time_ = interrupt_period_;
MonitorLocker wait_ml(monitor_);
while (!shutdown_) {
intptr_t r = wait_ml.WaitMicros(current_wait_time_);
if ((r == Monitor::kNotified) && shutdown_) {
if (shutdown_) {
break;
}
if ((r == Monitor::kNotified) && InDeepSleep()) {
// Woken up from deep sleep.
ASSERT(visitor.profiled_thread_count() == 0);
ASSERT(interrupted_thread_count == 0);
// Return to regular interrupts.
current_wait_time_ = interrupt_period_;
}
// Reset count before visiting isolates.
visitor.set_profiled_thread_count(0);
Isolate::VisitIsolates(&visitor);
// Reset count before interrupting any threads.
interrupted_thread_count = 0;
if (visitor.profiled_thread_count() == 0) {
// No isolates were profiled. In order to reduce unnecessary CPU
// load, we will wait until we are notified before attempting to
// interrupt again.
// Temporarily drop the monitor while we interrupt threads.
monitor_->Exit();
{
ThreadIterator it;
while (it.HasNext()) {
Thread* thread = it.Next();
if (thread->ThreadInterruptsEnabled()) {
interrupted_thread_count++;
InterruptThread(thread);
}
}
}
// Take the monitor lock again.
monitor_->Enter();
// Now that we have the lock, check if we were signaled to wake up while
// interrupting threads.
if (!woken_up_ && (interrupted_thread_count == 0)) {
// No threads were interrupted and we were not signaled to interrupt
// new threads. In order to reduce unnecessary CPU load, we will wait
// until we are notified before attempting to interrupt again.
current_wait_time_ = Monitor::kNoTimeout;
continue;
}
woken_up_ = false;
ASSERT(current_wait_time_ != Monitor::kNoTimeout);
}
}

View file

@ -25,13 +25,6 @@ class ThreadInterrupter : public AllStatic {
// Wake up the thread interrupter thread.
static void WakeUp();
// Register the currently running thread for interrupts. If the current thread
// is already registered, callback and data will be updated.
static void Register(ThreadInterruptCallback callback, void* data);
// Unregister the currently running thread for interrupts.
static void Unregister();
// Interrupt a thread.
static void InterruptThread(Thread* thread);
@ -40,6 +33,7 @@ class ThreadInterrupter : public AllStatic {
static bool initialized_;
static bool shutdown_;
static bool thread_running_;
static bool woken_up_;
static ThreadJoinId interrupter_thread_id_;
static Monitor* monitor_;
static intptr_t interrupt_period_;
@ -60,8 +54,6 @@ class ThreadInterrupter : public AllStatic {
friend class ThreadInterrupterVisitIsolates;
};
void ThreadInterruptNoOp(const InterruptedThreadState& state, void* data);
} // namespace dart
#endif // VM_THREAD_INTERRUPTER_H_

View file

@ -6,9 +6,11 @@
#if defined(TARGET_OS_ANDROID)
#include <sys/syscall.h> // NOLINT
#include <errno.h> // NOLINT
#include "vm/flags.h"
#include "vm/os.h"
#include "vm/profiler.h"
#include "vm/signal_handler.h"
#include "vm/thread_interrupter.h"
@ -28,22 +30,16 @@ class ThreadInterrupterAndroid : public AllStatic {
if (thread == NULL) {
return;
}
ThreadInterruptCallback callback = NULL;
void* callback_data = NULL;
if (!thread->IsThreadInterrupterEnabled(&callback, &callback_data)) {
return;
}
// Extract thread state.
ucontext_t* context = reinterpret_cast<ucontext_t*>(context_);
mcontext_t mcontext = context->uc_mcontext;
InterruptedThreadState its;
its.tid = thread->id();
its.pc = SignalHandler::GetProgramCounter(mcontext);
its.fp = SignalHandler::GetFramePointer(mcontext);
its.csp = SignalHandler::GetCStackPointer(mcontext);
its.dsp = SignalHandler::GetDartStackPointer(mcontext);
its.lr = SignalHandler::GetLinkRegister(mcontext);
callback(its, callback_data);
Profiler::SampleThread(thread, its);
}
};
@ -54,7 +50,7 @@ void ThreadInterrupter::InterruptThread(Thread* thread) {
reinterpret_cast<void*>(thread->id()));
}
int result = syscall(__NR_tgkill, getpid(), thread->id(), SIGPROF);
ASSERT(result == 0);
ASSERT((result == 0) || (result == ESRCH));
}

View file

@ -5,8 +5,11 @@
#include "platform/globals.h"
#if defined(TARGET_OS_LINUX)
#include <errno.h> // NOLINT
#include "vm/flags.h"
#include "vm/os.h"
#include "vm/profiler.h"
#include "vm/signal_handler.h"
#include "vm/thread_interrupter.h"
@ -26,22 +29,16 @@ class ThreadInterrupterLinux : public AllStatic {
if (thread == NULL) {
return;
}
ThreadInterruptCallback callback = NULL;
void* callback_data = NULL;
if (!thread->IsThreadInterrupterEnabled(&callback, &callback_data)) {
return;
}
// Extract thread state.
ucontext_t* context = reinterpret_cast<ucontext_t*>(context_);
mcontext_t mcontext = context->uc_mcontext;
InterruptedThreadState its;
its.tid = thread->id();
its.pc = SignalHandler::GetProgramCounter(mcontext);
its.fp = SignalHandler::GetFramePointer(mcontext);
its.csp = SignalHandler::GetCStackPointer(mcontext);
its.dsp = SignalHandler::GetDartStackPointer(mcontext);
its.lr = SignalHandler::GetLinkRegister(mcontext);
callback(its, callback_data);
Profiler::SampleThread(thread, its);
}
};
@ -52,7 +49,7 @@ void ThreadInterrupter::InterruptThread(Thread* thread) {
reinterpret_cast<void*>(thread->id()));
}
int result = pthread_kill(thread->id(), SIGPROF);
ASSERT(result == 0);
ASSERT((result == 0) || (result == ESRCH));
}

View file

@ -5,8 +5,11 @@
#include "platform/globals.h"
#if defined(TARGET_OS_MACOS)
#include <errno.h> // NOLINT
#include "vm/flags.h"
#include "vm/os.h"
#include "vm/profiler.h"
#include "vm/signal_handler.h"
#include "vm/thread_interrupter.h"
@ -26,22 +29,16 @@ class ThreadInterrupterMacOS : public AllStatic {
if (thread == NULL) {
return;
}
ThreadInterruptCallback callback = NULL;
void* callback_data = NULL;
if (!thread->IsThreadInterrupterEnabled(&callback, &callback_data)) {
return;
}
// Extract thread state.
ucontext_t* context = reinterpret_cast<ucontext_t*>(context_);
mcontext_t mcontext = context->uc_mcontext;
InterruptedThreadState its;
its.tid = thread->id();
its.pc = SignalHandler::GetProgramCounter(mcontext);
its.fp = SignalHandler::GetFramePointer(mcontext);
its.csp = SignalHandler::GetCStackPointer(mcontext);
its.dsp = SignalHandler::GetDartStackPointer(mcontext);
its.lr = SignalHandler::GetLinkRegister(mcontext);
callback(its, callback_data);
Profiler::SampleThread(thread, its);
}
};
@ -51,7 +48,7 @@ void ThreadInterrupter::InterruptThread(Thread* thread) {
OS::Print("ThreadInterrupter interrupting %p\n", thread->id());
}
int result = pthread_kill(thread->id(), SIGPROF);
ASSERT(result == 0);
ASSERT((result == 0) || (result == ESRCH));
}

View file

@ -1,66 +0,0 @@
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "platform/assert.h"
#include "vm/dart_api_impl.h"
#include "vm/dart_api_state.h"
#include "vm/globals.h"
#include "vm/thread_interrupter.h"
#include "vm/unit_test.h"
namespace dart {
class ThreadInterrupterTestHelper : public AllStatic {
public:
static void InterruptTest(const intptr_t run_time, const intptr_t period) {
const double allowed_error = 0.25; // +/- 25%
intptr_t count = 0;
Thread::EnsureInit();
Thread* thread = Thread::Current();
thread->SetThreadInterrupter(IncrementCallback, &count);
ThreadInterrupter::SetInterruptPeriod(period);
OS::Sleep(run_time * kMillisecondsPerSecond);
thread->SetThreadInterrupter(NULL, NULL);
intptr_t run_time_micros = run_time * kMicrosecondsPerSecond;
intptr_t expected_interrupts = run_time_micros / period;
intptr_t error = allowed_error * expected_interrupts;
intptr_t low_bar = expected_interrupts - error;
intptr_t high_bar = expected_interrupts + error;
EXPECT_GE(count, low_bar);
EXPECT_LE(count, high_bar);
}
static void IncrementCallback(const InterruptedThreadState& state,
void* data) {
ASSERT(data != NULL);
intptr_t* counter = reinterpret_cast<intptr_t*>(data);
*counter = *counter + 1;
}
};
TEST_CASE(ThreadInterrupterHigh) {
const intptr_t kRunTimeSeconds = 5;
const intptr_t kInterruptPeriodMicros = 250;
ThreadInterrupterTestHelper::InterruptTest(kRunTimeSeconds,
kInterruptPeriodMicros);
}
TEST_CASE(ThreadInterrupterMedium) {
const intptr_t kRunTimeSeconds = 5;
const intptr_t kInterruptPeriodMicros = 500;
ThreadInterrupterTestHelper::InterruptTest(kRunTimeSeconds,
kInterruptPeriodMicros);
}
TEST_CASE(ThreadInterrupterLow) {
const intptr_t kRunTimeSeconds = 5;
const intptr_t kInterruptPeriodMicros = 1000;
ThreadInterrupterTestHelper::InterruptTest(kRunTimeSeconds,
kInterruptPeriodMicros);
}
} // namespace dart

View file

@ -7,6 +7,7 @@
#include "vm/flags.h"
#include "vm/os.h"
#include "vm/profiler.h"
#include "vm/thread_interrupter.h"
namespace dart {
@ -69,7 +70,6 @@ class ThreadInterrupterWin : public AllStatic {
return;
}
InterruptedThreadState its;
its.tid = thread->id();
if (!GrabRegisters(handle, &its)) {
// Failed to get thread registers.
ResumeThread(handle);
@ -80,11 +80,7 @@ class ThreadInterrupterWin : public AllStatic {
CloseHandle(handle);
return;
}
ThreadInterruptCallback callback = NULL;
void* callback_data = NULL;
if (thread->IsThreadInterrupterEnabled(&callback, &callback_data)) {
callback(its, callback_data);
}
Profiler::SampleThread(thread, its);
ResumeThread(handle);
CloseHandle(handle);
}

View file

@ -39,10 +39,8 @@ UNIT_TEST_CASE(Monitor) {
Dart_CreateIsolate(
NULL, NULL, bin::isolate_snapshot_buffer, NULL, NULL, NULL);
Thread* thread = Thread::Current();
Isolate* isolate = thread->isolate();
// Thread interrupter interferes with this test, disable interrupts.
thread->SetThreadInterrupter(NULL, NULL);
Profiler::EndExecution(isolate);
thread->DisableThreadInterrupts();
Monitor* monitor = new Monitor();
monitor->Enter();
monitor->Exit();

View file

@ -451,7 +451,6 @@
'thread_interrupter_android.cc',
'thread_interrupter_linux.cc',
'thread_interrupter_macos.cc',
'thread_interrupter_test.cc',
'thread_interrupter_win.cc',
'thread_pool.cc',
'thread_pool.h',