// Copyright 2012 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/isolate.h" #include #include // NOLINT(readability/streams) #include #include "src/ast/ast.h" #include "src/ast/scopeinfo.h" #include "src/base/platform/platform.h" #include "src/base/sys-info.h" #include "src/base/utils/random-number-generator.h" #include "src/basic-block-profiler.h" #include "src/bootstrapper.h" #include "src/codegen.h" #include "src/compilation-cache.h" #include "src/compilation-statistics.h" #include "src/crankshaft/hydrogen.h" #include "src/debug/debug.h" #include "src/deoptimizer.h" #include "src/frames-inl.h" #include "src/ic/stub-cache.h" #include "src/interpreter/interpreter.h" #include "src/isolate-inl.h" #include "src/log.h" #include "src/messages.h" #include "src/profiler/cpu-profiler.h" #include "src/profiler/sampler.h" #include "src/prototype.h" #include "src/regexp/regexp-stack.h" #include "src/runtime-profiler.h" #include "src/simulator.h" #include "src/snapshot/serialize.h" #include "src/v8.h" #include "src/version.h" #include "src/vm-state-inl.h" namespace v8 { namespace internal { base::Atomic32 ThreadId::highest_thread_id_ = 0; int ThreadId::AllocateThreadId() { int new_id = base::NoBarrier_AtomicIncrement(&highest_thread_id_, 1); return new_id; } int ThreadId::GetCurrentThreadId() { int thread_id = base::Thread::GetThreadLocalInt(Isolate::thread_id_key_); if (thread_id == 0) { thread_id = AllocateThreadId(); base::Thread::SetThreadLocalInt(Isolate::thread_id_key_, thread_id); } return thread_id; } ThreadLocalTop::ThreadLocalTop() { InitializeInternal(); } void ThreadLocalTop::InitializeInternal() { c_entry_fp_ = 0; c_function_ = 0; handler_ = 0; #ifdef USE_SIMULATOR simulator_ = NULL; #endif js_entry_sp_ = NULL; external_callback_scope_ = NULL; current_vm_state_ = EXTERNAL; try_catch_handler_ = NULL; context_ = NULL; thread_id_ = ThreadId::Invalid(); external_caught_exception_ = false; failed_access_check_callback_ = NULL; save_context_ = NULL; promise_on_stack_ = NULL; // These members are re-initialized later after deserialization // is complete. pending_exception_ = NULL; rethrowing_message_ = false; pending_message_obj_ = NULL; scheduled_exception_ = NULL; } void ThreadLocalTop::Initialize() { InitializeInternal(); #ifdef USE_SIMULATOR simulator_ = Simulator::current(isolate_); #endif thread_id_ = ThreadId::Current(); } void ThreadLocalTop::Free() { // Match unmatched PopPromise calls. while (promise_on_stack_) isolate_->PopPromise(); } base::Thread::LocalStorageKey Isolate::isolate_key_; base::Thread::LocalStorageKey Isolate::thread_id_key_; base::Thread::LocalStorageKey Isolate::per_isolate_thread_data_key_; base::LazyMutex Isolate::thread_data_table_mutex_ = LAZY_MUTEX_INITIALIZER; Isolate::ThreadDataTable* Isolate::thread_data_table_ = NULL; base::Atomic32 Isolate::isolate_counter_ = 0; #if DEBUG base::Atomic32 Isolate::isolate_key_created_ = 0; #endif Isolate::PerIsolateThreadData* Isolate::FindOrAllocatePerThreadDataForThisThread() { ThreadId thread_id = ThreadId::Current(); PerIsolateThreadData* per_thread = NULL; { base::LockGuard lock_guard(thread_data_table_mutex_.Pointer()); per_thread = thread_data_table_->Lookup(this, thread_id); if (per_thread == NULL) { per_thread = new PerIsolateThreadData(this, thread_id); thread_data_table_->Insert(per_thread); } DCHECK(thread_data_table_->Lookup(this, thread_id) == per_thread); } return per_thread; } void Isolate::DiscardPerThreadDataForThisThread() { int thread_id_int = base::Thread::GetThreadLocalInt(Isolate::thread_id_key_); if (thread_id_int) { ThreadId thread_id = ThreadId(thread_id_int); DCHECK(!thread_manager_->mutex_owner_.Equals(thread_id)); base::LockGuard lock_guard(thread_data_table_mutex_.Pointer()); PerIsolateThreadData* per_thread = thread_data_table_->Lookup(this, thread_id); if (per_thread) { DCHECK(!per_thread->thread_state_); thread_data_table_->Remove(per_thread); } } } Isolate::PerIsolateThreadData* Isolate::FindPerThreadDataForThisThread() { ThreadId thread_id = ThreadId::Current(); return FindPerThreadDataForThread(thread_id); } Isolate::PerIsolateThreadData* Isolate::FindPerThreadDataForThread( ThreadId thread_id) { PerIsolateThreadData* per_thread = NULL; { base::LockGuard lock_guard(thread_data_table_mutex_.Pointer()); per_thread = thread_data_table_->Lookup(this, thread_id); } return per_thread; } void Isolate::InitializeOncePerProcess() { base::LockGuard lock_guard(thread_data_table_mutex_.Pointer()); CHECK(thread_data_table_ == NULL); isolate_key_ = base::Thread::CreateThreadLocalKey(); #if DEBUG base::NoBarrier_Store(&isolate_key_created_, 1); #endif thread_id_key_ = base::Thread::CreateThreadLocalKey(); per_isolate_thread_data_key_ = base::Thread::CreateThreadLocalKey(); thread_data_table_ = new Isolate::ThreadDataTable(); } Address Isolate::get_address_from_id(Isolate::AddressId id) { return isolate_addresses_[id]; } char* Isolate::Iterate(ObjectVisitor* v, char* thread_storage) { ThreadLocalTop* thread = reinterpret_cast(thread_storage); Iterate(v, thread); return thread_storage + sizeof(ThreadLocalTop); } void Isolate::IterateThread(ThreadVisitor* v, char* t) { ThreadLocalTop* thread = reinterpret_cast(t); v->VisitThread(this, thread); } void Isolate::Iterate(ObjectVisitor* v, ThreadLocalTop* thread) { // Visit the roots from the top for a given thread. v->VisitPointer(&thread->pending_exception_); v->VisitPointer(&(thread->pending_message_obj_)); v->VisitPointer(bit_cast(&(thread->context_))); v->VisitPointer(&thread->scheduled_exception_); for (v8::TryCatch* block = thread->try_catch_handler(); block != NULL; block = block->next_) { v->VisitPointer(bit_cast(&(block->exception_))); v->VisitPointer(bit_cast(&(block->message_obj_))); } // Iterate over pointers on native execution stack. for (StackFrameIterator it(this, thread); !it.done(); it.Advance()) { it.frame()->Iterate(v); } } void Isolate::Iterate(ObjectVisitor* v) { ThreadLocalTop* current_t = thread_local_top(); Iterate(v, current_t); } void Isolate::IterateDeferredHandles(ObjectVisitor* visitor) { for (DeferredHandles* deferred = deferred_handles_head_; deferred != NULL; deferred = deferred->next_) { deferred->Iterate(visitor); } } #ifdef DEBUG bool Isolate::IsDeferredHandle(Object** handle) { // Each DeferredHandles instance keeps the handles to one job in the // concurrent recompilation queue, containing a list of blocks. Each block // contains kHandleBlockSize handles except for the first block, which may // not be fully filled. // We iterate through all the blocks to see whether the argument handle // belongs to one of the blocks. If so, it is deferred. for (DeferredHandles* deferred = deferred_handles_head_; deferred != NULL; deferred = deferred->next_) { List* blocks = &deferred->blocks_; for (int i = 0; i < blocks->length(); i++) { Object** block_limit = (i == 0) ? deferred->first_block_limit_ : blocks->at(i) + kHandleBlockSize; if (blocks->at(i) <= handle && handle < block_limit) return true; } } return false; } #endif // DEBUG void Isolate::RegisterTryCatchHandler(v8::TryCatch* that) { thread_local_top()->set_try_catch_handler(that); } void Isolate::UnregisterTryCatchHandler(v8::TryCatch* that) { DCHECK(thread_local_top()->try_catch_handler() == that); thread_local_top()->set_try_catch_handler(that->next_); } Handle Isolate::StackTraceString() { if (stack_trace_nesting_level_ == 0) { stack_trace_nesting_level_++; HeapStringAllocator allocator; StringStream::ClearMentionedObjectCache(this); StringStream accumulator(&allocator); incomplete_message_ = &accumulator; PrintStack(&accumulator); Handle stack_trace = accumulator.ToString(this); incomplete_message_ = NULL; stack_trace_nesting_level_ = 0; return stack_trace; } else if (stack_trace_nesting_level_ == 1) { stack_trace_nesting_level_++; base::OS::PrintError( "\n\nAttempt to print stack while printing stack (double fault)\n"); base::OS::PrintError( "If you are lucky you may find a partial stack dump on stdout.\n\n"); incomplete_message_->OutputToStdOut(); return factory()->empty_string(); } else { base::OS::Abort(); // Unreachable return factory()->empty_string(); } } void Isolate::PushStackTraceAndDie(unsigned int magic, void* ptr1, void* ptr2, unsigned int magic2) { const int kMaxStackTraceSize = 32 * KB; Handle trace = StackTraceString(); uint8_t buffer[kMaxStackTraceSize]; int length = Min(kMaxStackTraceSize - 1, trace->length()); String::WriteToFlat(*trace, buffer, 0, length); buffer[length] = '\0'; // TODO(dcarney): convert buffer to utf8? base::OS::PrintError("Stacktrace (%x-%x) %p %p: %s\n", magic, magic2, ptr1, ptr2, reinterpret_cast(buffer)); base::OS::Abort(); } // Determines whether the given stack frame should be displayed in // a stack trace. The caller is the error constructor that asked // for the stack trace to be collected. The first time a construct // call to this function is encountered it is skipped. The seen_caller // in/out parameter is used to remember if the caller has been seen // yet. static bool IsVisibleInStackTrace(JSFunction* fun, Object* caller, Object* receiver, bool* seen_caller) { if ((fun == caller) && !(*seen_caller)) { *seen_caller = true; return false; } // Skip all frames until we've seen the caller. if (!(*seen_caller)) return false; // Functions defined in native scripts are not visible unless directly // exposed, in which case the native flag is set. // The --builtins-in-stack-traces command line flag allows including // internal call sites in the stack trace for debugging purposes. if (!FLAG_builtins_in_stack_traces && fun->shared()->IsBuiltin()) { return fun->shared()->native(); } return true; } Handle Isolate::CaptureSimpleStackTrace(Handle error_object, Handle caller) { // Get stack trace limit. Handle error = error_function(); Handle stackTraceLimit = factory()->InternalizeUtf8String("stackTraceLimit"); DCHECK(!stackTraceLimit.is_null()); Handle stack_trace_limit = JSReceiver::GetDataProperty(error, stackTraceLimit); if (!stack_trace_limit->IsNumber()) return factory()->undefined_value(); int limit = FastD2IChecked(stack_trace_limit->Number()); limit = Max(limit, 0); // Ensure that limit is not negative. int initial_size = Min(limit, 10); Handle elements = factory()->NewFixedArrayWithHoles(initial_size * 4 + 1); // If the caller parameter is a function we skip frames until we're // under it before starting to collect. bool seen_caller = !caller->IsJSFunction(); // First element is reserved to store the number of sloppy frames. int cursor = 1; int frames_seen = 0; int sloppy_frames = 0; bool encountered_strict_function = false; for (JavaScriptFrameIterator iter(this); !iter.done() && frames_seen < limit; iter.Advance()) { JavaScriptFrame* frame = iter.frame(); // Set initial size to the maximum inlining level + 1 for the outermost // function. List frames(FLAG_max_inlining_levels + 1); frame->Summarize(&frames); for (int i = frames.length() - 1; i >= 0; i--) { Handle fun = frames[i].function(); Handle recv = frames[i].receiver(); // Filter out internal frames that we do not want to show. if (!IsVisibleInStackTrace(*fun, *caller, *recv, &seen_caller)) continue; // Filter out frames from other security contexts. if (!this->context()->HasSameSecurityTokenAs(fun->context())) continue; if (cursor + 4 > elements->length()) { int new_capacity = JSObject::NewElementsCapacity(elements->length()); Handle new_elements = factory()->NewFixedArrayWithHoles(new_capacity); for (int i = 0; i < cursor; i++) { new_elements->set(i, elements->get(i)); } elements = new_elements; } DCHECK(cursor + 4 <= elements->length()); Handle code = frames[i].code(); Handle offset(Smi::FromInt(frames[i].offset()), this); // The stack trace API should not expose receivers and function // objects on frames deeper than the top-most one with a strict // mode function. The number of sloppy frames is stored as // first element in the result array. if (!encountered_strict_function) { if (is_strict(fun->shared()->language_mode())) { encountered_strict_function = true; } else { sloppy_frames++; } } elements->set(cursor++, *recv); elements->set(cursor++, *fun); elements->set(cursor++, *code); elements->set(cursor++, *offset); frames_seen++; } } elements->set(0, Smi::FromInt(sloppy_frames)); elements->Shrink(cursor); Handle result = factory()->NewJSArrayWithElements(elements); result->set_length(Smi::FromInt(cursor)); // TODO(yangguo): Queue this structured stack trace for preprocessing on GC. return result; } MaybeHandle Isolate::CaptureAndSetDetailedStackTrace( Handle error_object) { if (capture_stack_trace_for_uncaught_exceptions_) { // Capture stack trace for a detailed exception message. Handle key = factory()->detailed_stack_trace_symbol(); Handle stack_trace = CaptureCurrentStackTrace( stack_trace_for_uncaught_exceptions_frame_limit_, stack_trace_for_uncaught_exceptions_options_); RETURN_ON_EXCEPTION( this, JSObject::SetProperty(error_object, key, stack_trace, STRICT), JSObject); } return error_object; } MaybeHandle Isolate::CaptureAndSetSimpleStackTrace( Handle error_object, Handle caller) { // Capture stack trace for simple stack trace string formatting. Handle key = factory()->stack_trace_symbol(); Handle stack_trace = CaptureSimpleStackTrace(error_object, caller); RETURN_ON_EXCEPTION( this, JSObject::SetProperty(error_object, key, stack_trace, STRICT), JSObject); return error_object; } Handle Isolate::GetDetailedStackTrace(Handle error_object) { Handle key_detailed = factory()->detailed_stack_trace_symbol(); Handle stack_trace = JSReceiver::GetDataProperty(error_object, key_detailed); if (stack_trace->IsJSArray()) return Handle::cast(stack_trace); if (!capture_stack_trace_for_uncaught_exceptions_) return Handle(); // Try to get details from simple stack trace. Handle detailed_stack_trace = GetDetailedFromSimpleStackTrace(error_object); if (!detailed_stack_trace.is_null()) { // Save the detailed stack since the simple one might be withdrawn later. JSObject::SetProperty(error_object, key_detailed, detailed_stack_trace, STRICT).Assert(); } return detailed_stack_trace; } class CaptureStackTraceHelper { public: CaptureStackTraceHelper(Isolate* isolate, StackTrace::StackTraceOptions options) : isolate_(isolate) { if (options & StackTrace::kColumnOffset) { column_key_ = factory()->InternalizeOneByteString(STATIC_CHAR_VECTOR("column")); } if (options & StackTrace::kLineNumber) { line_key_ = factory()->InternalizeOneByteString(STATIC_CHAR_VECTOR("lineNumber")); } if (options & StackTrace::kScriptId) { script_id_key_ = factory()->InternalizeOneByteString(STATIC_CHAR_VECTOR("scriptId")); } if (options & StackTrace::kScriptName) { script_name_key_ = factory()->InternalizeOneByteString(STATIC_CHAR_VECTOR("scriptName")); } if (options & StackTrace::kScriptNameOrSourceURL) { script_name_or_source_url_key_ = factory()->InternalizeOneByteString( STATIC_CHAR_VECTOR("scriptNameOrSourceURL")); } if (options & StackTrace::kFunctionName) { function_key_ = factory()->InternalizeOneByteString( STATIC_CHAR_VECTOR("functionName")); } if (options & StackTrace::kIsEval) { eval_key_ = factory()->InternalizeOneByteString(STATIC_CHAR_VECTOR("isEval")); } if (options & StackTrace::kIsConstructor) { constructor_key_ = factory()->InternalizeOneByteString( STATIC_CHAR_VECTOR("isConstructor")); } } Handle NewStackFrameObject(Handle fun, int position, bool is_constructor) { Handle stack_frame = factory()->NewJSObject(isolate_->object_function()); Handle