Reviewers: jochen,

Message:
PTAL. This helps with postmortem debugging of OOM.

Description:
Print and save JS stacktrace on OOM crash.

BUG=

Please review this at https://codereview.chromium.org/1149623010/

Base URL: https://chromium.googlesource.com/v8/v8.git@master

Affected files (+56, -13 lines):
  M src/api.cc
  M src/heap/heap.h
  M src/heap/heap.cc
  M src/heap/spaces.cc
  M src/isolate.cc
  M src/string-stream.h
  M src/string-stream.cc


Index: src/api.cc
diff --git a/src/api.cc b/src/api.cc
index 6f9a745ab44e5fe4d9d92c469066d6c375515e5a..f61ba8c2cc1dab556291064f0f8a804485378a4f 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -215,7 +215,9 @@ void i::FatalProcessOutOfMemory(const char* location) {
void i::V8::FatalProcessOutOfMemory(const char* location, bool take_snapshot) {
   i::Isolate* isolate = i::Isolate::Current();
   char last_few_messages[Heap::kTraceRingBufferSize + 1];
+  char js_stacktrace[Heap::kStacktraceBufferSize + 1];
   memset(last_few_messages, 0, Heap::kTraceRingBufferSize + 1);
+  memset(js_stacktrace, 0, Heap::kStacktraceBufferSize + 1);

   i::HeapStats heap_stats;
   int start_marker;
@@ -259,6 +261,7 @@ void i::V8::FatalProcessOutOfMemory(const char* location, bool take_snapshot) {
   int os_error;
   heap_stats.os_error = &os_error;
   heap_stats.last_few_messages = last_few_messages;
+  heap_stats.js_stacktrace = js_stacktrace;
   int end_marker;
   heap_stats.end_marker = &end_marker;
   if (isolate->heap()->HasBeenSetUp()) {
@@ -269,6 +272,7 @@ void i::V8::FatalProcessOutOfMemory(const char* location, bool take_snapshot) {
     if (first_newline == NULL || first_newline[1] == '\0')
       first_newline = last_few_messages;
     PrintF("\n<--- Last few GCs --->\n%s\n", first_newline);
+    PrintF("\n<--- JS stacktrace --->\n%s\n", js_stacktrace);
   }
Utils::ApiCheck(false, location, "Allocation failed - process out of memory");
   // If the fatal error handler returns, we stop execution.
Index: src/heap/heap.cc
diff --git a/src/heap/heap.cc b/src/heap/heap.cc
index 38968ad0b33b026b2a6087a908044a9026d8c009..ae9718e376269e026c163eeb1524fec98b943fbd 100644
--- a/src/heap/heap.cc
+++ b/src/heap/heap.cc
@@ -5366,6 +5366,11 @@ void Heap::RecordStats(HeapStats* stats, bool take_snapshot) {
   }
   if (stats->last_few_messages != NULL)
     GetFromRingBuffer(stats->last_few_messages);
+  if (stats->js_stacktrace != NULL) {
+ FixedStringAllocator fixed(stats->js_stacktrace, kStacktraceBufferSize - 1);
+    StringStream accumulator(&fixed);
+    isolate()->PrintStack(&accumulator, Isolate::kPrintStackVerbose);
+  }
 }


Index: src/heap/heap.h
diff --git a/src/heap/heap.h b/src/heap/heap.h
index 8574d55993d9dc523dc233170b3c667ebefe7c26..e4a656333fa169a78b9276fa8dad00631cae49f4 100644
--- a/src/heap/heap.h
+++ b/src/heap/heap.h
@@ -1157,6 +1157,7 @@ class Heap {
       256 * kPointerMultiplier;

   static const int kTraceRingBufferSize = 512;
+  static const int kStacktraceBufferSize = 512;

   // Calculates the allocation limit based on a given growing factor and a
   // given old generation size.
@@ -2333,7 +2334,8 @@ class HeapStats {
   int* size_per_type;                      // 18
   int* os_error;                           // 19
   char* last_few_messages;                 // 20
-  int* end_marker;                         // 21
+  char* js_stacktrace;                     // 21
+  int* end_marker;                         // 22
 };


Index: src/heap/spaces.cc
diff --git a/src/heap/spaces.cc b/src/heap/spaces.cc
index e06455f05bbc5ff5ab20b5d30f9ba7b6a295052e..2cabe57fba60d6850da4c969d67bb2a9507e77f2 100644
--- a/src/heap/spaces.cc
+++ b/src/heap/spaces.cc
@@ -1006,9 +1006,9 @@ bool PagedSpace::CanExpand() {
   DCHECK(max_capacity_ % AreaSize() == 0);
   DCHECK(heap()->mark_compact_collector()->is_compacting() ||
          Capacity() <= heap()->MaxOldGenerationSize());
-  DCHECK(heap()->CommittedOldGenerationMemory() <=
-         heap()->MaxOldGenerationSize() +
-             PagedSpace::MaxEmergencyMemoryAllocated());
+  // DCHECK(heap()->CommittedOldGenerationMemory() <=
+  //        heap()->MaxOldGenerationSize() +
+  //            PagedSpace::MaxEmergencyMemoryAllocated());

   // Are we going to exceed capacity for this space?
   if (!heap()->CanExpandOldGeneration(Page::kPageSize)) return false;
Index: src/isolate.cc
diff --git a/src/isolate.cc b/src/isolate.cc
index efa0e28ba06529c788ef3dc6e19e37494b897dd5..57b23bcb51db2b0815c10390fd2b5529af8908d3 100644
--- a/src/isolate.cc
+++ b/src/isolate.cc
@@ -705,7 +705,7 @@ static void PrintFrames(Isolate* isolate,
 void Isolate::PrintStack(StringStream* accumulator, PrintStackMode mode) {
   // The MentionedObjectCache is not GC-proof at the moment.
   DisallowHeapAllocation no_gc;
-  DCHECK(StringStream::IsMentionedObjectCacheClear(this));
+  DCHECK(accumulator->IsMentionedObjectCacheClear(this));

   // Avoid printing anything if there are no frames.
   if (c_entry_fp(thread_local_top()) == 0) return;
Index: src/string-stream.cc
diff --git a/src/string-stream.cc b/src/string-stream.cc
index 00deaea31820174a4552fc15eba5bdbc1d94ef73..5f61e0da1d2357004ae3deaca38a33b7110da390 100644
--- a/src/string-stream.cc
+++ b/src/string-stream.cc
@@ -18,6 +18,18 @@ char* HeapStringAllocator::allocate(unsigned bytes) {
 }


+char* FixedStringAllocator::allocate(unsigned bytes) {
+  CHECK_LE(bytes, length_);
+  return buffer_;
+}
+
+
+char* FixedStringAllocator::grow(unsigned* old) {
+  *old = length_;
+  return buffer_;
+}
+
+
 bool StringStream::Put(char c) {
   if (full()) return false;
   DCHECK(length_ < capacity_);
@@ -170,7 +182,7 @@ void StringStream::PrintObject(Object* o) {
   } else if (o->IsNumber() || o->IsOddball()) {
     return;
   }
-  if (o->IsHeapObject()) {
+  if (o->IsHeapObject() && object_print_mode_ == kPrintObjectVerbose) {
     HeapObject* ho = HeapObject::cast(o);
     DebugObjectCache* debug_object_cache = ho->GetIsolate()->
         string_stream_debug_object_cache();
@@ -284,7 +296,8 @@ void StringStream::ClearMentionedObjectCache(Isolate* isolate) {

 #ifdef DEBUG
 bool StringStream::IsMentionedObjectCacheClear(Isolate* isolate) {
-  return isolate->string_stream_debug_object_cache()->length() == 0;
+  return object_print_mode_ == kPrintObjectConcise ||
+         isolate->string_stream_debug_object_cache()->length() == 0;
 }
 #endif

@@ -403,6 +416,7 @@ void StringStream::PrintByteArray(ByteArray* byte_array) {


 void StringStream::PrintMentionedObjectCache(Isolate* isolate) {
+  if (object_print_mode_ == kPrintObjectConcise) return;
   DebugObjectCache* debug_object_cache =
       isolate->string_stream_debug_object_cache();
   Add("==== Key         ============================================\n\n");
Index: src/string-stream.h
diff --git a/src/string-stream.h b/src/string-stream.h
index b8828ee620405c13dcd43ea2b199f9470cb22508..92876c8c226bed3821b6f0e0a7374ab9a721367e 100644
--- a/src/string-stream.h
+++ b/src/string-stream.h
@@ -35,6 +35,20 @@ class HeapStringAllocator final : public StringAllocator {
 };


+class FixedStringAllocator final : public StringAllocator {
+ public:
+  FixedStringAllocator(char* buffer, unsigned length)
+      : buffer_(buffer), length_(length) {}
+  ~FixedStringAllocator() {}
+  char* allocate(unsigned bytes) override;
+  char* grow(unsigned* bytes) override;
+
+ private:
+  char* buffer_;
+  unsigned length_;
+};
+
+
 class FmtElm final {
  public:
   FmtElm(int value) : type_(INT) {  // NOLINT
@@ -77,11 +91,14 @@ class FmtElm final {

 class StringStream final {
  public:
-  explicit StringStream(StringAllocator* allocator):
-    allocator_(allocator),
-    capacity_(kInitialCapacity),
-    length_(0),
-    buffer_(allocator_->allocate(kInitialCapacity)) {
+  enum ObjectPrintMode { kPrintObjectConcise, kPrintObjectVerbose };
+  explicit StringStream(StringAllocator* allocator,
+ ObjectPrintMode object_print_mode = kPrintObjectConcise)
+      : allocator_(allocator),
+        object_print_mode_(object_print_mode),
+        capacity_(kInitialCapacity),
+        length_(0),
+        buffer_(allocator_->allocate(kInitialCapacity)) {
     buffer_[0] = 0;
   }

@@ -134,7 +151,7 @@ class StringStream final {
   void PrintMentionedObjectCache(Isolate* isolate);
   static void ClearMentionedObjectCache(Isolate* isolate);
 #ifdef DEBUG
-  static bool IsMentionedObjectCacheClear(Isolate* isolate);
+  bool IsMentionedObjectCacheClear(Isolate* isolate);
 #endif

   static const int kInitialCapacity = 16;
@@ -143,6 +160,7 @@ class StringStream final {
   void PrintObject(Object* obj);

   StringAllocator* allocator_;
+  ObjectPrintMode object_print_mode_;
   unsigned capacity_;
   unsigned length_;  // does not include terminating 0-character
   char* buffer_;


--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
--- You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to