Commit af4734f1 authored by kasper.lund's avatar kasper.lund

Added support for storing JavaScript stack traces in a stack allocated buffer...

Added support for storing JavaScript stack traces in a stack allocated buffer to make it visible in shallow core dumps. Controlled by the --preallocate-message-memory flag which is disabled by default.


git-svn-id: http://v8.googlecode.com/svn/trunk@7 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 44510671
2008-07-28: Version 0.1.4 (128918)
Added support for storing JavaScript stack traces in a stack
allocated buffer to make it visible in shallow core dumps.
Controlled by the --preallocate-message-memory flag which is
disabled by default.
2008-07-25: Version 0.1.3 (128832) 2008-07-25: Version 0.1.3 (128832)
Fixed bug in JSObject::GetPropertyAttributePostInterceptor where Fixed bug in JSObject::GetPropertyAttributePostInterceptor where
......
...@@ -346,7 +346,7 @@ def ConfigureBuild(): ...@@ -346,7 +346,7 @@ def ConfigureBuild():
# compile it. # compile it.
library_files = [s for s in LIBRARY_FILES] library_files = [s for s in LIBRARY_FILES]
library_files.append('macros.py') library_files.append('macros.py')
libraries_src, libraries_empty_src = env.JS2C(['libraries.cc', 'libraries_empty.cc'], library_files) libraries_src, libraries_empty_src = env.JS2C(['libraries.cc', 'libraries-empty.cc'], library_files)
libraries_obj = BuildObject(env, libraries_src, CPPPATH=['.']) libraries_obj = BuildObject(env, libraries_src, CPPPATH=['.'])
# Build JSCRE. # Build JSCRE.
......
...@@ -49,6 +49,13 @@ NoAllocationStringAllocator::NoAllocationStringAllocator(unsigned bytes) { ...@@ -49,6 +49,13 @@ NoAllocationStringAllocator::NoAllocationStringAllocator(unsigned bytes) {
} }
NoAllocationStringAllocator::NoAllocationStringAllocator(char* memory,
unsigned size) {
size_ = size;
space_ = memory;
}
bool StringStream::Put(char c) { bool StringStream::Put(char c) {
if (space() == 0) return false; if (space() == 0) return false;
if (length_ >= capacity_ - 1) { if (length_ >= capacity_ - 1) {
......
...@@ -61,6 +61,7 @@ class HeapStringAllocator: public StringAllocator { ...@@ -61,6 +61,7 @@ class HeapStringAllocator: public StringAllocator {
class NoAllocationStringAllocator: public StringAllocator { class NoAllocationStringAllocator: public StringAllocator {
public: public:
explicit NoAllocationStringAllocator(unsigned bytes); explicit NoAllocationStringAllocator(unsigned bytes);
NoAllocationStringAllocator(char* memory, unsigned size);
char* allocate(unsigned bytes) { return space_; } char* allocate(unsigned bytes) { return space_; }
char* grow(unsigned* bytes); char* grow(unsigned* bytes);
private: private:
......
...@@ -38,9 +38,8 @@ namespace v8 { namespace internal { ...@@ -38,9 +38,8 @@ namespace v8 { namespace internal {
DEFINE_bool(trace_exception, false, DEFINE_bool(trace_exception, false,
"print stack trace when throwing exceptions"); "print stack trace when throwing exceptions");
DEFINE_int(preallocated_stack_trace_memory, 0, DEFINE_bool(preallocate_message_memory, false,
"preallocate some space to build stack traces. " "preallocate some memory to build stack traces.");
"Default is not to preallocate.");
ThreadLocalTop Top::thread_local_; ThreadLocalTop Top::thread_local_;
Mutex* Top::break_access_ = OS::CreateMutex(); Mutex* Top::break_access_ = OS::CreateMutex();
...@@ -48,7 +47,7 @@ StackFrame::Id Top::break_frame_id_; ...@@ -48,7 +47,7 @@ StackFrame::Id Top::break_frame_id_;
int Top::break_count_; int Top::break_count_;
int Top::break_id_; int Top::break_id_;
NoAllocationStringAllocator* preallocated_message_space; NoAllocationStringAllocator* preallocated_message_space = NULL;
Address top_addresses[] = { Address top_addresses[] = {
#define C(name) reinterpret_cast<Address>(Top::name()), #define C(name) reinterpret_cast<Address>(Top::name()),
...@@ -111,21 +110,149 @@ void Top::InitializeThreadLocal() { ...@@ -111,21 +110,149 @@ void Top::InitializeThreadLocal() {
} }
// 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() {
char local_buffer[16 * 1024];
// Initialize the buffer with a known good value.
strncpy(local_buffer, "Trace data was not generated.\n",
sizeof(local_buffer));
// Publish the local buffer and signal its availability.
data_ = &local_buffer[0];
length_ = sizeof(local_buffer);
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.
strncpy(local_buffer, "PreallocatedMemoryThread shutting down.\n",
sizeof(local_buffer));
}
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_EVIL_CONSTRUCTORS(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() { void Top::Initialize() {
CHECK(!initialized);
InitializeThreadLocal(); InitializeThreadLocal();
break_frame_id_ = StackFrame::NO_ID; break_frame_id_ = StackFrame::NO_ID;
break_count_ = 0; break_count_ = 0;
break_id_ = 0; break_id_ = 0;
if (FLAG_preallocated_stack_trace_memory != 0) { // Only preallocate on the first initialization.
if (FLAG_preallocated_stack_trace_memory < StringStream::kInitialCapacity) if (FLAG_preallocate_message_memory && (preallocated_message_space == NULL)) {
FLAG_preallocated_stack_trace_memory = StringStream::kInitialCapacity; // Start the thread which will set aside some memory.
// 3/4 is allocated to the message and 1/4 is allocated to the work area. PreallocatedMemoryThread::StartThread();
preallocated_message_space = preallocated_message_space =
new NoAllocationStringAllocator( new NoAllocationStringAllocator(PreallocatedMemoryThread::data(),
FLAG_preallocated_stack_trace_memory * 3 / 4); PreallocatedMemoryThread::length());
PreallocatedStorage::Init(FLAG_preallocated_stack_trace_memory / 4); 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;
} }
} }
...@@ -250,14 +377,14 @@ void Top::PrintStack() { ...@@ -250,14 +377,14 @@ void Top::PrintStack() {
stack_trace_nesting_level++; stack_trace_nesting_level++;
StringAllocator* allocator; StringAllocator* allocator;
if (FLAG_preallocated_stack_trace_memory == 0) { if (preallocated_message_space == NULL) {
allocator = new HeapStringAllocator(); allocator = new HeapStringAllocator();
} else { } else {
allocator = preallocated_message_space; allocator = preallocated_message_space;
} }
NativeAllocationChecker allocation_checker( NativeAllocationChecker allocation_checker(
FLAG_preallocated_stack_trace_memory == 0 ? !FLAG_preallocate_message_memory ?
NativeAllocationChecker::ALLOW : NativeAllocationChecker::ALLOW :
NativeAllocationChecker::DISALLOW); NativeAllocationChecker::DISALLOW);
...@@ -269,7 +396,8 @@ void Top::PrintStack() { ...@@ -269,7 +396,8 @@ void Top::PrintStack() {
accumulator.Log(); accumulator.Log();
incomplete_message = NULL; incomplete_message = NULL;
stack_trace_nesting_level = 0; stack_trace_nesting_level = 0;
if (FLAG_preallocated_stack_trace_memory == 0) { if (preallocated_message_space == NULL) {
// Remove the HeapStringAllocator created above.
delete allocator; delete allocator;
} }
} else if (stack_trace_nesting_level == 1) { } else if (stack_trace_nesting_level == 1) {
...@@ -735,7 +863,7 @@ Object* Top::LookupSpecialFunction(JSObject* receiver, ...@@ -735,7 +863,7 @@ Object* Top::LookupSpecialFunction(JSObject* receiver,
char* Top::ArchiveThread(char* to) { char* Top::ArchiveThread(char* to) {
memcpy(to, reinterpret_cast<char*>(&thread_local_), sizeof(thread_local_)); memcpy(to, reinterpret_cast<char*>(&thread_local_), sizeof(thread_local_));
Initialize(); InitializeThreadLocal();
return to + sizeof(thread_local_); return to + sizeof(thread_local_);
} }
......
...@@ -235,6 +235,7 @@ class Top { ...@@ -235,6 +235,7 @@ class Top {
// Administration // Administration
static void Initialize(); static void Initialize();
static void TearDown();
static void Iterate(ObjectVisitor* v); static void Iterate(ObjectVisitor* v);
static void Iterate(ObjectVisitor* v, ThreadLocalTop* t); static void Iterate(ObjectVisitor* v, ThreadLocalTop* t);
static char* Iterate(ObjectVisitor* v, char* t); static char* Iterate(ObjectVisitor* v, char* t);
......
...@@ -102,6 +102,8 @@ void V8::TearDown() { ...@@ -102,6 +102,8 @@ void V8::TearDown() {
Builtins::TearDown(); Builtins::TearDown();
Bootstrapper::TearDown(); Bootstrapper::TearDown();
Top::TearDown();
Heap::TearDown(); Heap::TearDown();
Logger::TearDown(); Logger::TearDown();
......
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