fe548c4bcd
- Dump per thread timing information when an isolate shuts down. DeltaBlue(RunTime): 3654.6660583941607 us. Timing for isolate DeltaBlue.dart$main-109078706 (from 2 threads) Thread 0 (f68f8b40): HandleMessage : 2163.455 ms total on stack; 940.038 ms total executing; 2114.231 ms max on stack; 930.085 ms max executing. CompileFunction : 81.020 ms total on stack; 81.020 ms total executing; 7.745 ms max on stack; 7.745 ms max executing. CompileOptimizedFunction : 109.817 ms total on stack; 109.817 ms total executing; 10.443 ms max on stack; 10.443 ms max executing. CollectNewGeneration : 1032.580 ms total on stack; 1032.580 ms total executing; 7.677 ms max on stack; 7.677 ms max executing. Thread 1 (f7419700): InitializeIsolate : 12.121 ms total on stack; 0.418 ms total executing; 12.121 ms max on stack; 0.418 ms max executing. ObjectStore::Init : 0.002 ms total on stack; 0.002 ms total executing; 0.002 ms max on stack; 0.002 ms max executing. Object::Init : 0.268 ms total on stack; 0.268 ms total executing; 0.268 ms max on stack; 0.268 ms max executing. IsolateSnapshotReader : 11.433 ms total on stack; 11.433 ms total executing; 11.433 ms max on stack; 11.433 ms max executing. CompileFunction : 111.849 ms total on stack; 111.849 ms total executing; 10.798 ms max on stack; 10.704 ms max executing. Totals: HandleMessage : 2163.455 ms total on stack; 940.038 ms total executing; 2114.231 ms max on stack; 930.085 ms max executing. CompileFunction : 192.869 ms total on stack; 192.869 ms total executing; 10.798 ms max on stack; 10.704 ms max executing. CompileOptimizedFunction : 109.817 ms total on stack; 109.817 ms total executing; 10.443 ms max on stack; 10.443 ms max executing. CollectNewGeneration : 1032.580 ms total on stack; 1032.580 ms total executing; 7.677 ms max on stack; 7.677 ms max executing. InitializeIsolate : 12.121 ms total on stack; 0.418 ms total executing; 12.121 ms max on stack; 0.418 ms max executing. ObjectStore::Init : 0.002 ms total on stack; 0.002 ms total executing; 0.002 ms max on stack; 0.002 ms max executing. Object::Init : 0.268 ms total on stack; 0.268 ms total executing; 0.268 ms max on stack; 0.268 ms max executing. IsolateSnapshotReader : 11.433 ms total on stack; 11.433 ms total executing; 11.433 ms max on stack; 11.433 ms max executing. R=rmacnak@google.com Review URL: https://codereview.chromium.org//1296353002 .
393 lines
11 KiB
C++
393 lines
11 KiB
C++
// Copyright (c) 2015, 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 "vm/thread.h"
|
|
|
|
#include "vm/growable_array.h"
|
|
#include "vm/isolate.h"
|
|
#include "vm/lockers.h"
|
|
#include "vm/log.h"
|
|
#include "vm/object.h"
|
|
#include "vm/os_thread.h"
|
|
#include "vm/profiler.h"
|
|
#include "vm/runtime_entry.h"
|
|
#include "vm/stub_code.h"
|
|
#include "vm/symbols.h"
|
|
#include "vm/thread_interrupter.h"
|
|
#include "vm/thread_registry.h"
|
|
|
|
namespace dart {
|
|
|
|
// The single thread local key which stores all the thread local data
|
|
// for a thread.
|
|
ThreadLocalKey Thread::thread_key_ = OSThread::kUnsetThreadLocalKey;
|
|
|
|
|
|
// Remove |thread| from each isolate's thread registry.
|
|
class ThreadPruner : public IsolateVisitor {
|
|
public:
|
|
explicit ThreadPruner(Thread* thread)
|
|
: thread_(thread) {
|
|
ASSERT(thread_ != NULL);
|
|
}
|
|
|
|
void VisitIsolate(Isolate* isolate) {
|
|
ThreadRegistry* registry = isolate->thread_registry();
|
|
ASSERT(registry != NULL);
|
|
registry->PruneThread(thread_);
|
|
}
|
|
private:
|
|
Thread* thread_;
|
|
};
|
|
|
|
|
|
static void DeleteThread(void* thread) {
|
|
delete reinterpret_cast<Thread*>(thread);
|
|
}
|
|
|
|
|
|
Thread::~Thread() {
|
|
// We should cleanly exit any isolate before destruction.
|
|
ASSERT(isolate_ == NULL);
|
|
// Clear |this| from all isolate's thread registry.
|
|
ThreadPruner pruner(this);
|
|
Isolate::VisitIsolates(&pruner);
|
|
delete log_;
|
|
log_ = NULL;
|
|
}
|
|
|
|
|
|
void Thread::InitOnceBeforeIsolate() {
|
|
ASSERT(thread_key_ == OSThread::kUnsetThreadLocalKey);
|
|
thread_key_ = OSThread::CreateThreadLocal(DeleteThread);
|
|
ASSERT(thread_key_ != OSThread::kUnsetThreadLocalKey);
|
|
ASSERT(Thread::Current() == NULL);
|
|
// Allocate a new Thread and postpone initialization of VM constants for
|
|
// this first thread.
|
|
Thread* thread = new Thread(false);
|
|
// Verify that current thread was set.
|
|
ASSERT(Thread::Current() == thread);
|
|
}
|
|
|
|
|
|
void Thread::InitOnceAfterObjectAndStubCode() {
|
|
Thread* thread = Thread::Current();
|
|
ASSERT(thread != NULL);
|
|
ASSERT(thread->isolate() == Dart::vm_isolate());
|
|
thread->InitVMConstants();
|
|
}
|
|
|
|
|
|
void Thread::SetCurrent(Thread* current) {
|
|
OSThread::SetThreadLocal(thread_key_, reinterpret_cast<uword>(current));
|
|
}
|
|
|
|
|
|
void Thread::EnsureInit() {
|
|
if (Thread::Current() == NULL) {
|
|
// Allocate a new Thread.
|
|
Thread* thread = new Thread();
|
|
// Verify that current thread was set.
|
|
ASSERT(Thread::Current() == thread);
|
|
}
|
|
}
|
|
|
|
|
|
#if defined(TARGET_OS_WINDOWS)
|
|
void Thread::CleanUp() {
|
|
Thread* current = Current();
|
|
if (current != NULL) {
|
|
SetCurrent(NULL);
|
|
delete current;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
Thread::Thread(bool init_vm_constants)
|
|
: id_(OSThread::GetCurrentThreadId()),
|
|
thread_interrupt_callback_(NULL),
|
|
thread_interrupt_data_(NULL),
|
|
isolate_(NULL),
|
|
heap_(NULL),
|
|
store_buffer_block_(NULL),
|
|
log_(new class Log()) {
|
|
ClearState();
|
|
|
|
#define DEFAULT_INIT(type_name, member_name, init_expr, default_init_value) \
|
|
member_name = default_init_value;
|
|
CACHED_CONSTANTS_LIST(DEFAULT_INIT)
|
|
#undef DEFAULT_INIT
|
|
|
|
#define DEFAULT_INIT(name) \
|
|
name##_entry_point_ = 0;
|
|
RUNTIME_ENTRY_LIST(DEFAULT_INIT)
|
|
#undef DEFAULT_INIT
|
|
|
|
#define DEFAULT_INIT(returntype, name, ...) \
|
|
name##_entry_point_ = 0;
|
|
LEAF_RUNTIME_ENTRY_LIST(DEFAULT_INIT)
|
|
#undef DEFAULT_INIT
|
|
|
|
if (init_vm_constants) {
|
|
InitVMConstants();
|
|
}
|
|
SetCurrent(this);
|
|
}
|
|
|
|
|
|
void Thread::InitVMConstants() {
|
|
#define ASSERT_VM_HEAP(type_name, member_name, init_expr, default_init_value) \
|
|
ASSERT((init_expr)->IsOldObject());
|
|
CACHED_VM_OBJECTS_LIST(ASSERT_VM_HEAP)
|
|
#undef ASSERT_VM_HEAP
|
|
|
|
#define INIT_VALUE(type_name, member_name, init_expr, default_init_value) \
|
|
ASSERT(member_name == default_init_value); \
|
|
member_name = (init_expr);
|
|
CACHED_CONSTANTS_LIST(INIT_VALUE)
|
|
#undef INIT_VALUE
|
|
|
|
#define INIT_VALUE(name) \
|
|
ASSERT(name##_entry_point_ == 0); \
|
|
name##_entry_point_ = k##name##RuntimeEntry.GetEntryPoint();
|
|
RUNTIME_ENTRY_LIST(INIT_VALUE)
|
|
#undef INIT_VALUE
|
|
|
|
#define INIT_VALUE(returntype, name, ...) \
|
|
ASSERT(name##_entry_point_ == 0); \
|
|
name##_entry_point_ = k##name##RuntimeEntry.GetEntryPoint();
|
|
LEAF_RUNTIME_ENTRY_LIST(INIT_VALUE)
|
|
#undef INIT_VALUE
|
|
}
|
|
|
|
|
|
void Thread::Schedule(Isolate* isolate, bool bypass_safepoint) {
|
|
State st;
|
|
if (isolate->thread_registry()->RestoreStateTo(this, &st, bypass_safepoint)) {
|
|
ASSERT(isolate->thread_registry()->Contains(this));
|
|
state_ = st;
|
|
}
|
|
}
|
|
|
|
|
|
void Thread::Unschedule(bool bypass_safepoint) {
|
|
ThreadRegistry* reg = isolate_->thread_registry();
|
|
ASSERT(reg->Contains(this));
|
|
reg->SaveStateFrom(this, state_, bypass_safepoint);
|
|
ClearState();
|
|
}
|
|
|
|
|
|
void Thread::EnterIsolate(Isolate* isolate) {
|
|
Thread* thread = Thread::Current();
|
|
ASSERT(thread != NULL);
|
|
ASSERT(thread->isolate() == NULL);
|
|
ASSERT(!isolate->HasMutatorThread());
|
|
thread->isolate_ = isolate;
|
|
isolate->MakeCurrentThreadMutator(thread);
|
|
isolate->set_vm_tag(VMTag::kVMTagId);
|
|
ASSERT(thread->store_buffer_block_ == NULL);
|
|
thread->StoreBufferAcquire();
|
|
ASSERT(isolate->heap() != NULL);
|
|
thread->heap_ = isolate->heap();
|
|
thread->Schedule(isolate);
|
|
// TODO(koda): Migrate profiler interface to use Thread.
|
|
Profiler::BeginExecution(isolate);
|
|
}
|
|
|
|
|
|
void Thread::ExitIsolate() {
|
|
Thread* thread = Thread::Current();
|
|
// TODO(koda): Audit callers; they should know whether they're in an isolate.
|
|
if (thread == NULL || thread->isolate() == NULL) return;
|
|
Isolate* isolate = thread->isolate();
|
|
Profiler::EndExecution(isolate);
|
|
thread->Unschedule();
|
|
// TODO(koda): Move store_buffer_block_ into State.
|
|
thread->StoreBufferRelease();
|
|
if (isolate->is_runnable()) {
|
|
isolate->set_vm_tag(VMTag::kIdleTagId);
|
|
} else {
|
|
isolate->set_vm_tag(VMTag::kLoadWaitTagId);
|
|
}
|
|
isolate->ClearMutatorThread();
|
|
thread->isolate_ = NULL;
|
|
ASSERT(Isolate::Current() == NULL);
|
|
thread->heap_ = NULL;
|
|
}
|
|
|
|
|
|
void Thread::EnterIsolateAsHelper(Isolate* isolate, bool bypass_safepoint) {
|
|
Thread* thread = Thread::Current();
|
|
ASSERT(thread != NULL);
|
|
ASSERT(thread->isolate() == NULL);
|
|
thread->isolate_ = isolate;
|
|
ASSERT(thread->store_buffer_block_ == NULL);
|
|
// TODO(koda): Use StoreBufferAcquire once we properly flush before Scavenge.
|
|
thread->store_buffer_block_ =
|
|
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(!isolate->MutatorThreadIsCurrentThread());
|
|
thread->Schedule(isolate, bypass_safepoint);
|
|
}
|
|
|
|
|
|
void Thread::ExitIsolateAsHelper(bool bypass_safepoint) {
|
|
Thread* thread = Thread::Current();
|
|
Isolate* isolate = thread->isolate();
|
|
ASSERT(isolate != NULL);
|
|
thread->Unschedule(bypass_safepoint);
|
|
// TODO(koda): Move store_buffer_block_ into State.
|
|
thread->StoreBufferRelease();
|
|
thread->isolate_ = NULL;
|
|
thread->heap_ = NULL;
|
|
ASSERT(!isolate->MutatorThreadIsCurrentThread());
|
|
}
|
|
|
|
|
|
// TODO(koda): Make non-static and invoke in SafepointThreads.
|
|
void Thread::PrepareForGC() {
|
|
Thread* thread = Thread::Current();
|
|
const bool kDoNotCheckThreshold = false; // Prevent scheduling another GC.
|
|
thread->StoreBufferRelease(kDoNotCheckThreshold);
|
|
// Make sure to get an *empty* block; the isolate needs all entries
|
|
// at GC time.
|
|
// TODO(koda): Replace with an epilogue (PrepareAfterGC) that acquires.
|
|
thread->store_buffer_block_ =
|
|
thread->isolate()->store_buffer()->PopEmptyBlock();
|
|
}
|
|
|
|
|
|
void Thread::StoreBufferBlockProcess(bool check_threshold) {
|
|
StoreBufferRelease(check_threshold);
|
|
StoreBufferAcquire();
|
|
}
|
|
|
|
|
|
void Thread::StoreBufferAddObject(RawObject* obj) {
|
|
store_buffer_block_->Push(obj);
|
|
if (store_buffer_block_->IsFull()) {
|
|
StoreBufferBlockProcess(true);
|
|
}
|
|
}
|
|
|
|
|
|
void Thread::StoreBufferAddObjectGC(RawObject* obj) {
|
|
store_buffer_block_->Push(obj);
|
|
if (store_buffer_block_->IsFull()) {
|
|
StoreBufferBlockProcess(false);
|
|
}
|
|
}
|
|
|
|
|
|
void Thread::StoreBufferRelease(bool check_threshold) {
|
|
StoreBufferBlock* block = store_buffer_block_;
|
|
store_buffer_block_ = NULL;
|
|
isolate_->store_buffer()->PushBlock(block, check_threshold);
|
|
}
|
|
|
|
|
|
void Thread::StoreBufferAcquire() {
|
|
store_buffer_block_ = isolate()->store_buffer()->PopNonFullBlock();
|
|
}
|
|
|
|
|
|
CHA* Thread::cha() const {
|
|
ASSERT(isolate_ != NULL);
|
|
return isolate_->cha_;
|
|
}
|
|
|
|
|
|
void Thread::set_cha(CHA* value) {
|
|
ASSERT(isolate_ != NULL);
|
|
isolate_->cha_ = value;
|
|
}
|
|
|
|
|
|
Log* Thread::log() const {
|
|
return log_;
|
|
}
|
|
|
|
|
|
void Thread::SetThreadInterrupter(ThreadInterruptCallback callback,
|
|
void* data) {
|
|
ASSERT(Thread::Current() == this);
|
|
thread_interrupt_callback_ = callback;
|
|
thread_interrupt_data_ = data;
|
|
}
|
|
|
|
|
|
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::CloseTimelineBlock() {
|
|
if (timeline_block() != NULL) {
|
|
timeline_block()->Finish();
|
|
set_timeline_block(NULL);
|
|
}
|
|
}
|
|
|
|
|
|
bool Thread::CanLoadFromThread(const Object& object) {
|
|
#define CHECK_OBJECT(type_name, member_name, expr, default_init_value) \
|
|
if (object.raw() == expr) return true;
|
|
CACHED_VM_OBJECTS_LIST(CHECK_OBJECT)
|
|
#undef CHECK_OBJECT
|
|
return false;
|
|
}
|
|
|
|
|
|
intptr_t Thread::OffsetFromThread(const Object& object) {
|
|
#define COMPUTE_OFFSET(type_name, member_name, expr, default_init_value) \
|
|
ASSERT((expr)->IsVMHeapObject()); \
|
|
if (object.raw() == expr) return Thread::member_name##offset();
|
|
CACHED_VM_OBJECTS_LIST(COMPUTE_OFFSET)
|
|
#undef COMPUTE_OFFSET
|
|
UNREACHABLE();
|
|
return -1;
|
|
}
|
|
|
|
|
|
intptr_t Thread::OffsetFromThread(const RuntimeEntry* runtime_entry) {
|
|
#define COMPUTE_OFFSET(name) \
|
|
if (runtime_entry->function() == k##name##RuntimeEntry.function()) { \
|
|
return Thread::name##_entry_point_offset(); \
|
|
}
|
|
RUNTIME_ENTRY_LIST(COMPUTE_OFFSET)
|
|
#undef COMPUTE_OFFSET
|
|
|
|
#define COMPUTE_OFFSET(returntype, name, ...) \
|
|
if (runtime_entry->function() == k##name##RuntimeEntry.function()) { \
|
|
return Thread::name##_entry_point_offset(); \
|
|
}
|
|
LEAF_RUNTIME_ENTRY_LIST(COMPUTE_OFFSET)
|
|
#undef COMPUTE_OFFSET
|
|
|
|
UNREACHABLE();
|
|
return -1;
|
|
}
|
|
|
|
} // namespace dart
|