// Copyright 2006-2008 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "v8.h" #include "api.h" #include "bootstrapper.h" #include "debug.h" #include "execution.h" #include "messages.h" #include "platform.h" #include "simulator.h" #include "string-stream.h" namespace v8 { namespace internal { ThreadLocalTop Top::thread_local_; Mutex* Top::break_access_ = OS::CreateMutex(); NoAllocationStringAllocator* preallocated_message_space = NULL; bool capture_stack_trace_for_uncaught_exceptions = false; int stack_trace_for_uncaught_exceptions_frame_limit = 0; StackTrace::StackTraceOptions stack_trace_for_uncaught_exceptions_options = StackTrace::kOverview; Address top_addresses[] = { #define C(name) reinterpret_cast
(Top::name()), TOP_ADDRESS_LIST(C) TOP_ADDRESS_LIST_PROF(C) #undef C NULL }; v8::TryCatch* ThreadLocalTop::TryCatchHandler() { return TRY_CATCH_FROM_ADDRESS(try_catch_handler_address()); } void ThreadLocalTop::Initialize() { c_entry_fp_ = 0; handler_ = 0; #ifdef USE_SIMULATOR #ifdef V8_TARGET_ARCH_ARM simulator_ = assembler::arm::Simulator::current(); #elif V8_TARGET_ARCH_MIPS simulator_ = assembler::mips::Simulator::current(); #endif #endif #ifdef ENABLE_LOGGING_AND_PROFILING js_entry_sp_ = 0; #endif #ifdef ENABLE_VMSTATE_TRACKING current_vm_state_ = NULL; #endif try_catch_handler_address_ = NULL; context_ = NULL; int id = ThreadManager::CurrentId(); thread_id_ = (id == 0) ? ThreadManager::kInvalidId : id; external_caught_exception_ = false; failed_access_check_callback_ = NULL; save_context_ = NULL; catcher_ = NULL; } Address Top::get_address_from_id(Top::AddressId id) { return top_addresses[id]; } char* Top::Iterate(ObjectVisitor* v, char* thread_storage) { ThreadLocalTop* thread = reinterpret_cast(thread_storage); Iterate(v, thread); return thread_storage + sizeof(ThreadLocalTop); } void Top::IterateThread(ThreadVisitor* v) { v->VisitThread(&thread_local_); } void Top::IterateThread(ThreadVisitor* v, char* t) { ThreadLocalTop* thread = reinterpret_cast(t); v->VisitThread(thread); } void Top::Iterate(ObjectVisitor* v, ThreadLocalTop* thread) { // Visit the roots from the top for a given thread. Object *pending; // The pending exception can sometimes be a failure. We can't show // that to the GC, which only understands objects. if (thread->pending_exception_->ToObject(&pending)) { v->VisitPointer(&pending); thread->pending_exception_ = pending; // In case GC updated it. } v->VisitPointer(&(thread->pending_message_obj_)); v->VisitPointer(BitCast(&(thread->pending_message_script_))); v->VisitPointer(BitCast(&(thread->context_))); Object* scheduled; if (thread->scheduled_exception_->ToObject(&scheduled)) { v->VisitPointer(&scheduled); thread->scheduled_exception_ = scheduled; } for (v8::TryCatch* block = thread->TryCatchHandler(); block != NULL; block = TRY_CATCH_FROM_ADDRESS(block->next_)) { v->VisitPointer(BitCast(&(block->exception_))); v->VisitPointer(BitCast(&(block->message_))); } // Iterate over pointers on native execution stack. for (StackFrameIterator it(thread); !it.done(); it.Advance()) { it.frame()->Iterate(v); } } void Top::Iterate(ObjectVisitor* v) { ThreadLocalTop* current_t = &thread_local_; Iterate(v, current_t); } void Top::InitializeThreadLocal() { thread_local_.Initialize(); clear_pending_exception(); clear_pending_message(); clear_scheduled_exception(); } // Create a dummy thread that will wait forever on a semaphore. The only // purpose for this thread is to have some stack area to save essential data // into for use by a stacks only core dump (aka minidump). class PreallocatedMemoryThread: public Thread { public: PreallocatedMemoryThread() : keep_running_(true) { wait_for_ever_semaphore_ = OS::CreateSemaphore(0); data_ready_semaphore_ = OS::CreateSemaphore(0); } // When the thread starts running it will allocate a fixed number of bytes // on the stack and publish the location of this memory for others to use. void Run() { EmbeddedVector local_buffer; // Initialize the buffer with a known good value. OS::StrNCpy(local_buffer, "Trace data was not generated.\n", local_buffer.length()); // Publish the local buffer and signal its availability. data_ = local_buffer.start(); length_ = local_buffer.length(); data_ready_semaphore_->Signal(); while (keep_running_) { // This thread will wait here until the end of time. wait_for_ever_semaphore_->Wait(); } // Make sure we access the buffer after the wait to remove all possibility // of it being optimized away. OS::StrNCpy(local_buffer, "PreallocatedMemoryThread shutting down.\n", local_buffer.length()); } static char* data() { if (data_ready_semaphore_ != NULL) { // Initial access is guarded until the data has been published. data_ready_semaphore_->Wait(); delete data_ready_semaphore_; data_ready_semaphore_ = NULL; } return data_; } static unsigned length() { if (data_ready_semaphore_ != NULL) { // Initial access is guarded until the data has been published. data_ready_semaphore_->Wait(); delete data_ready_semaphore_; data_ready_semaphore_ = NULL; } return length_; } static void StartThread() { if (the_thread_ != NULL) return; the_thread_ = new PreallocatedMemoryThread(); the_thread_->Start(); } // Stop the PreallocatedMemoryThread and release its resources. static void StopThread() { if (the_thread_ == NULL) return; the_thread_->keep_running_ = false; wait_for_ever_semaphore_->Signal(); // Wait for the thread to terminate. the_thread_->Join(); if (data_ready_semaphore_ != NULL) { delete data_ready_semaphore_; data_ready_semaphore_ = NULL; } delete wait_for_ever_semaphore_; wait_for_ever_semaphore_ = NULL; // Done with the thread entirely. delete the_thread_; the_thread_ = NULL; } private: // Used to make sure that the thread keeps looping even for spurious wakeups. bool keep_running_; // The preallocated memory thread singleton. static PreallocatedMemoryThread* the_thread_; // This semaphore is used by the PreallocatedMemoryThread to wait for ever. static Semaphore* wait_for_ever_semaphore_; // Semaphore to signal that the data has been initialized. static Semaphore* data_ready_semaphore_; // Location and size of the preallocated memory block. static char* data_; static unsigned length_; DISALLOW_COPY_AND_ASSIGN(PreallocatedMemoryThread); }; PreallocatedMemoryThread* PreallocatedMemoryThread::the_thread_ = NULL; Semaphore* PreallocatedMemoryThread::wait_for_ever_semaphore_ = NULL; Semaphore* PreallocatedMemoryThread::data_ready_semaphore_ = NULL; char* PreallocatedMemoryThread::data_ = NULL; unsigned PreallocatedMemoryThread::length_ = 0; static bool initialized = false; void Top::Initialize() { CHECK(!initialized); InitializeThreadLocal(); // Only preallocate on the first initialization. if (FLAG_preallocate_message_memory && (preallocated_message_space == NULL)) { // Start the thread which will set aside some memory. PreallocatedMemoryThread::StartThread(); preallocated_message_space = new NoAllocationStringAllocator(PreallocatedMemoryThread::data(), PreallocatedMemoryThread::length()); PreallocatedStorage::Init(PreallocatedMemoryThread::length() / 4); } initialized = true; } void Top::TearDown() { if (initialized) { // Remove the external reference to the preallocated stack memory. if (preallocated_message_space != NULL) { delete preallocated_message_space; preallocated_message_space = NULL; } PreallocatedMemoryThread::StopThread(); initialized = false; } } void Top::RegisterTryCatchHandler(v8::TryCatch* that) { // The ARM simulator has a separate JS stack. We therefore register // the C++ try catch handler with the simulator and get back an // address that can be used for comparisons with addresses into the // JS stack. When running without the simulator, the address // returned will be the address of the C++ try catch handler itself. Address address = reinterpret_cast
( SimulatorStack::RegisterCTryCatch(reinterpret_cast(that))); thread_local_.set_try_catch_handler_address(address); } void Top::UnregisterTryCatchHandler(v8::TryCatch* that) { ASSERT(thread_local_.TryCatchHandler() == that); thread_local_.set_try_catch_handler_address( reinterpret_cast
(that->next_)); thread_local_.catcher_ = NULL; SimulatorStack::UnregisterCTryCatch(); } static int stack_trace_nesting_level = 0; static StringStream* incomplete_message = NULL; Handle Top::StackTraceString() { if (stack_trace_nesting_level == 0) { stack_trace_nesting_level++; HeapStringAllocator allocator; StringStream::ClearMentionedObjectCache(); StringStream accumulator(&allocator); incomplete_message = &accumulator; PrintStack(&accumulator); Handle stack_trace = accumulator.ToString(); incomplete_message = NULL; stack_trace_nesting_level = 0; return stack_trace; } else if (stack_trace_nesting_level == 1) { stack_trace_nesting_level++; OS::PrintError( "\n\nAttempt to print stack while printing stack (double fault)\n"); OS::PrintError( "If you are lucky you may find a partial stack dump on stdout.\n\n"); incomplete_message->OutputToStdOut(); return Factory::empty_symbol(); } else { OS::Abort(); // Unreachable return Factory::empty_symbol(); } } Handle Top::CaptureCurrentStackTrace( int frame_limit, StackTrace::StackTraceOptions options) { // Ensure no negative values. int limit = Max(frame_limit, 0); Handle stack_trace = Factory::NewJSArray(frame_limit); Handle column_key = Factory::LookupAsciiSymbol("column"); Handle line_key = Factory::LookupAsciiSymbol("lineNumber"); Handle script_key = Factory::LookupAsciiSymbol("scriptName"); Handle name_or_source_url_key = Factory::LookupAsciiSymbol("nameOrSourceURL"); Handle script_name_or_source_url_key = Factory::LookupAsciiSymbol("scriptNameOrSourceURL"); Handle function_key = Factory::LookupAsciiSymbol("functionName"); Handle eval_key = Factory::LookupAsciiSymbol("isEval"); Handle constructor_key = Factory::LookupAsciiSymbol("isConstructor"); StackTraceFrameIterator it; int frames_seen = 0; while (!it.done() && (frames_seen < limit)) { // Create a JSObject to hold the information for the StackFrame. Handle stackFrame = Factory::NewJSObject(object_function()); JavaScriptFrame* frame = it.frame(); Handle fun(JSFunction::cast(frame->function())); Handle