Bug 938794 - Annotate OOM size as infallible string or data structures abort. r=froydnj, a=bajaj
authorBenjamin Smedberg <benjamin@smedbergs.us>
Mon, 25 Nov 2013 15:06:17 -0500
changeset 167606 ec081a4840ca07a399158f89c507655305c01d28
parent 167605 cdd11c4a3224fe4ba0c78c27e6e867acf8e7227b
child 167607 881c0899f01326becf25f56e052b31c5cdbb354b
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj, bajaj
bugs938794
milestone27.0a2
Bug 938794 - Annotate OOM size as infallible string or data structures abort. r=froydnj, a=bajaj
toolkit/crashreporter/nsExceptionHandler.h
xpcom/base/nsDebugImpl.cpp
xpcom/ds/nsAtomTable.cpp
xpcom/glue/nsBaseHashtable.h
xpcom/glue/nsDebug.h
xpcom/glue/nsDeque.h
xpcom/glue/nsTArray-inl.h
xpcom/glue/nsTArray.h
xpcom/glue/nsTHashtable.h
xpcom/string/public/nsTSubstring.h
xpcom/string/src/nsReadableUtils.cpp
xpcom/string/src/nsTStringObsolete.cpp
xpcom/string/src/nsTSubstring.cpp
--- a/toolkit/crashreporter/nsExceptionHandler.h
+++ b/toolkit/crashreporter/nsExceptionHandler.h
@@ -37,16 +37,17 @@ nsresult SetMinidumpPath(const nsAString
 
 
 // AnnotateCrashReport and AppendAppNotesToCrashReport may be called from any
 // thread in a chrome process, but may only be called from the main thread in
 // a content process.
 nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data);
 nsresult AppendAppNotesToCrashReport(const nsACString& data);
 
+void AnnotateOOMAllocationSize(size_t size);
 nsresult SetGarbageCollecting(bool collecting);
 
 nsresult SetRestartArgs(int argc, char** argv);
 nsresult SetupExtraData(nsIFile* aAppDataDirectory,
                         const nsACString& aBuildID);
 bool GetLastRunCrashID(nsAString& id);
 
 // Registers an additional memory region to be included in the minidump
--- a/xpcom/base/nsDebugImpl.cpp
+++ b/xpcom/base/nsDebugImpl.cpp
@@ -591,10 +591,17 @@ NS_ErrorAccordingToNSPR()
       case PR_NO_DEVICE_SPACE_ERROR:            return NS_ERROR_FILE_NO_DEVICE_SPACE;
       case PR_NAME_TOO_LONG_ERROR:              return NS_ERROR_FILE_NAME_TOO_LONG;
       case PR_DIRECTORY_NOT_EMPTY_ERROR:        return NS_ERROR_FILE_DIR_NOT_EMPTY;
       case PR_NO_ACCESS_RIGHTS_ERROR:           return NS_ERROR_FILE_ACCESS_DENIED;
       default:                                  return NS_ERROR_FAILURE;
     }
 }
 
-////////////////////////////////////////////////////////////////////////////////
+void
+NS_ABORT_OOM(size_t size)
+{
+#ifdef MOZ_CRASHREPORTER
+  CrashReporter::AnnotateOOMAllocationSize(size);
+#endif
+  MOZ_CRASH();
+}
 
--- a/xpcom/ds/nsAtomTable.cpp
+++ b/xpcom/ds/nsAtomTable.cpp
@@ -470,59 +470,53 @@ NS_SizeOfAtomTablesIncludingThis(MallocS
     n += gStaticAtomTable->SizeOfIncludingThis(SizeOfStaticAtomTableEntryExcludingThis,
                                                aMallocSizeOf);
   }
   return n;
 }
 
 #define ATOM_HASHTABLE_INITIAL_SIZE  4096
 
-static void HandleOOM()
-{
-  fputs("Out of memory allocating atom hashtable.\n", stderr);
-  MOZ_CRASH();
-}
-
 static inline void
 EnsureTableExists()
 {
   if (!gAtomTable.ops &&
       !PL_DHashTableInit(&gAtomTable, &AtomTableOps, 0,
                          sizeof(AtomTableEntry), ATOM_HASHTABLE_INITIAL_SIZE)) {
     // Initialization failed.
-    HandleOOM();
+    NS_ABORT_OOM(ATOM_HASHTABLE_INITIAL_SIZE * sizeof(AtomTableEntry));
   }
 }
 
 static inline AtomTableEntry*
 GetAtomHashEntry(const char* aString, uint32_t aLength)
 {
   MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
   EnsureTableExists();
   AtomTableKey key(aString, aLength);
   AtomTableEntry* e =
     static_cast<AtomTableEntry*>
                (PL_DHashTableOperate(&gAtomTable, &key, PL_DHASH_ADD));
   if (!e) {
-    HandleOOM();
+    NS_ABORT_OOM(gAtomTable.entryCount * gAtomTable.entrySize);
   }
   return e;
 }
 
 static inline AtomTableEntry*
 GetAtomHashEntry(const PRUnichar* aString, uint32_t aLength)
 {
   MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
   EnsureTableExists();
   AtomTableKey key(aString, aLength);
   AtomTableEntry* e =
     static_cast<AtomTableEntry*>
                (PL_DHashTableOperate(&gAtomTable, &key, PL_DHASH_ADD));
   if (!e) {
-    HandleOOM();
+    NS_ABORT_OOM(gAtomTable.entryCount * gAtomTable.entrySize);
   }
   return e;
 }
 
 class CheckStaticAtomSizes
 {
   CheckStaticAtomSizes() {
     static_assert((sizeof(nsFakeStringBuffer<1>().mRefCnt) ==
--- a/xpcom/glue/nsBaseHashtable.h
+++ b/xpcom/glue/nsBaseHashtable.h
@@ -117,17 +117,17 @@ public:
    * put a new value for the associated key
    * @param aKey the key to put
    * @param aData the new data
    * @return always true, unless memory allocation failed
    */
   void Put(KeyType aKey, const UserDataType& aData)
   {
     if (!Put(aKey, aData, fallible_t()))
-      NS_RUNTIMEABORT("OOM");
+      NS_ABORT_OOM(this->mTable.entrySize * this->mTable.entryCount);
   }
 
   bool Put(KeyType aKey, const UserDataType& aData, const fallible_t&) NS_WARN_UNUSED_RESULT
   {
     EntryType* ent = this->PutEntry(aKey);
 
     if (!ent)
       return false;
--- a/xpcom/glue/nsDebug.h
+++ b/xpcom/glue/nsDebug.h
@@ -349,16 +349,25 @@
   #define NS_CheckThreadSafe(owningThread, msg)
 #else
   #define NS_CheckThreadSafe(owningThread, msg)                 \
     if (MOZ_UNLIKELY(owningThread != PR_GetCurrentThread())) {  \
       MOZ_CRASH(msg);                                           \
     }
 #endif
 
+#ifdef MOZILLA_INTERNAL_API
+void NS_ABORT_OOM(size_t size);
+#else
+inline void NS_ABORT_OOM(size_t)
+{
+  MOZ_CRASH();
+}
+#endif
+
 /* When compiling the XPCOM Glue on Windows, we pretend that it's going to
  * be linked with a static CRT (-MT) even when it's not. This means that we
  * cannot link to data exports from the CRT, only function exports. So,
  * instead of referencing "stderr" directly, use fdopen.
  */
 #ifdef __cplusplus
 extern "C" {
 #endif
--- a/xpcom/glue/nsDeque.h
+++ b/xpcom/glue/nsDeque.h
@@ -75,30 +75,30 @@ class NS_COM_GLUE nsDeque {
 
   /**
    * Appends new member at the end of the deque.
    *
    * @param   item to store in deque
    */
   void Push(void* aItem) {
     if (!Push(aItem, fallible_t())) {
-      NS_RUNTIMEABORT("OOM");
+      NS_ABORT_OOM(mSize);
     }
   }
 
   bool Push(void* aItem, const fallible_t&) NS_WARN_UNUSED_RESULT;
 
   /**
    * Inserts new member at the front of the deque.
    *
    * @param   item to store in deque
    */
   void PushFront(void* aItem) {
     if (!PushFront(aItem, fallible_t())) {
-      NS_RUNTIMEABORT("OOM");
+      NS_ABORT_OOM(mSize);
     }
   }
 
   bool PushFront(void* aItem, const fallible_t&) NS_WARN_UNUSED_RESULT;
 
   /**
    * Remove and return the last item in the container.
    *
--- a/xpcom/glue/nsTArray-inl.h
+++ b/xpcom/glue/nsTArray-inl.h
@@ -104,17 +104,17 @@ nsTArray_base<Alloc, Copy>::EnsureCapaci
     return Alloc::SuccessResult();
 
   // If the requested memory allocation exceeds size_type(-1)/2, then
   // our doubling algorithm may not be able to allocate it.
   // Additionally we couldn't fit in the Header::mCapacity
   // member. Just bail out in cases like that.  We don't want to be
   // allocating 2 GB+ arrays anyway.
   if ((uint64_t)capacity * elemSize > size_type(-1)/2) {
-    Alloc::SizeTooBig();
+    Alloc::SizeTooBig((size_t)capacity * elemSize);
     return Alloc::FailureResult();
   }
 
   if (mHdr == EmptyHdr()) {
     // Malloc() new data
     Header *header = static_cast<Header*>
                      (Alloc::Malloc(sizeof(Header) + capacity * elemSize));
     if (!header)
--- a/xpcom/glue/nsTArray.h
+++ b/xpcom/glue/nsTArray.h
@@ -186,17 +186,17 @@ struct nsTArrayFallibleAllocator : nsTAr
   static void* Realloc(void* ptr, size_t size) {
     return moz_realloc(ptr, size);
   }
 
   static void Free(void* ptr) {
     moz_free(ptr);
   }
 
-  static void SizeTooBig() {
+  static void SizeTooBig(size_t) {
   }
 };
 
 struct nsTArrayInfallibleAllocator : nsTArrayInfallibleAllocatorBase
 {
   static void* Malloc(size_t size) {
     return moz_xmalloc(size);
   }
@@ -204,18 +204,18 @@ struct nsTArrayInfallibleAllocator : nsT
   static void* Realloc(void* ptr, size_t size) {
     return moz_xrealloc(ptr, size);
   }
 
   static void Free(void* ptr) {
     moz_free(ptr);
   }
 
-  static void SizeTooBig() {
-    mozalloc_abort("Trying to allocate an infallible array that's too big");
+  static void SizeTooBig(size_t size) {
+    NS_ABORT_OOM(size);
   }
 };
 
 #else
 #include <stdlib.h>
 
 struct nsTArrayFallibleAllocator : nsTArrayFallibleAllocatorBase
 {
@@ -226,50 +226,44 @@ struct nsTArrayFallibleAllocator : nsTAr
   static void* Realloc(void* ptr, size_t size) {
     return realloc(ptr, size);
   }
 
   static void Free(void* ptr) {
     free(ptr);
   }
 
-  static void SizeTooBig() {
+  static void SizeTooBig(size_t) {
   }
 };
 
 struct nsTArrayInfallibleAllocator : nsTArrayInfallibleAllocatorBase
 {
   static void* Malloc(size_t size) {
     void* ptr = malloc(size);
     if (MOZ_UNLIKELY(!ptr)) {
-      HandleOOM();
+      NS_ABORT_OOM(size);
     }
     return ptr;
   }
 
   static void* Realloc(void* ptr, size_t size) {
     void* newptr = realloc(ptr, size);
     if (MOZ_UNLIKELY(!ptr && size)) {
-      HandleOOM();
+      NS_ABORT_OOM(size);
     }
     return newptr;
   }
 
   static void Free(void* ptr) {
     free(ptr);
   }
 
-  static void SizeTooBig() {
-    HandleOOM();
-  }
-
-private:
-  static void HandleOOM() {
-    fputs("Out of memory allocating nsTArray buffer.\n", stderr);
-    MOZ_CRASH();
+  static void SizeTooBig(size_t size) {
+    NS_ABORT_OOM(size);
   }
 };
 
 #endif
 
 // nsTArray_base stores elements into the space allocated beyond
 // sizeof(*this).  This is done to minimize the size of the nsTArray
 // object when it is empty.
--- a/xpcom/glue/nsTHashtable.h
+++ b/xpcom/glue/nsTHashtable.h
@@ -157,24 +157,24 @@ public:
    * @param     aKey the key to retrieve
    * @return    pointer to the entry class retreived; nullptr only if memory
                 can't be allocated
    */
   EntryType* PutEntry(KeyType aKey)
   {
     EntryType* e = PutEntry(aKey, fallible_t());
     if (!e)
-      NS_RUNTIMEABORT("OOM");
+      NS_ABORT_OOM(mTable.entrySize * mTable.entryCount);
     return e;
   }
 
   EntryType* PutEntry(KeyType aKey, const fallible_t&) NS_WARN_UNUSED_RESULT
   {
     NS_ASSERTION(mTable.entrySize, "nsTHashtable was not initialized properly.");
-    
+
     return static_cast<EntryType*>
                       (PL_DHashTableOperate(
                             &mTable,
                             EntryType::KeyToPointer(aKey),
                             PL_DHASH_ADD));
   }
 
   /**
@@ -404,17 +404,17 @@ nsTHashtable<EntryType>::Init(uint32_t a
     s_InitEntry
   };
 
   if (!EntryType::ALLOW_MEMMOVE) {
     sOps.moveEntry = s_CopyEntry;
   }
   
   if (!PL_DHashTableInit(&mTable, &sOps, nullptr, sizeof(EntryType), aInitSize)) {
-    NS_RUNTIMEABORT("OOM");
+    NS_ABORT_OOM(sizeof(EntryType) * aInitSize);
   }
 }
 
 // static definitions
 
 template<class EntryType>
 PLDHashNumber
 nsTHashtable<EntryType>::s_HashKey(PLDHashTable  *table,
--- a/xpcom/string/public/nsTSubstring.h
+++ b/xpcom/string/public/nsTSubstring.h
@@ -125,30 +125,30 @@ class nsTSubstring_CharT
 
         /**
          * writing iterators
          */
       
       char_iterator BeginWriting()
         {
           if (!EnsureMutable())
-            NS_RUNTIMEABORT("OOM");
+            NS_ABORT_OOM(mLength);
 
           return mData;
         }
 
       char_iterator BeginWriting( const fallible_t& )
         {
           return EnsureMutable() ? mData : char_iterator(0);
         }
 
       char_iterator EndWriting()
         {
           if (!EnsureMutable())
-            NS_RUNTIMEABORT("OOM");
+            NS_ABORT_OOM(mLength);
 
           return mData + mLength;
         }
 
       char_iterator EndWriting( const fallible_t& )
         {
           return EnsureMutable() ? (mData + mLength) : char_iterator(0);
         }
@@ -516,31 +516,31 @@ class nsTSubstring_CharT
          *
          * @returns The length of the buffer in characters.
          */
       inline size_type GetData( const char_type** data ) const
         {
           *data = mData;
           return mLength;
         }
-        
+
         /**
          * Get a pointer to the string's internal buffer, optionally resizing
          * the buffer first.  If size_type(-1) is passed for newLen, then the
          * current length of the string is used.  The caller MAY modify the
          * characters at the returned address (up to but not exceeding the
          * length of the string).
          *
          * @returns The length of the buffer in characters or 0 if unable to
          * satisfy the request due to low-memory conditions.
          */
       size_type GetMutableData( char_type** data, size_type newLen = size_type(-1) )
         {
           if (!EnsureMutable(newLen))
-            NS_RUNTIMEABORT("OOM");
+            NS_ABORT_OOM(newLen == size_type(-1) ? mLength : newLen);
 
           *data = mData;
           return mLength;
         }
 
       size_type GetMutableData( char_type** data, size_type newLen, const fallible_t& )
         {
           if (!EnsureMutable(newLen))
--- a/xpcom/string/src/nsReadableUtils.cpp
+++ b/xpcom/string/src/nsReadableUtils.cpp
@@ -152,17 +152,17 @@ AppendUTF16toUTF8( const nsAString& aSou
                      "ConvertUTF16toUTF8");
       }
   }
 
 void
 AppendUTF8toUTF16( const nsACString& aSource, nsAString& aDest )
 {
   if (!AppendUTF8toUTF16(aSource, aDest, mozilla::fallible_t())) {
-    NS_RUNTIMEABORT("OOM");
+    NS_ABORT_OOM(aDest.Length() + aSource.Length());
   }
 }
 
 bool
 AppendUTF8toUTF16( const nsACString& aSource, nsAString& aDest,
                    const mozilla::fallible_t& )
   {
     nsACString::const_iterator source_start, source_end;
--- a/xpcom/string/src/nsTStringObsolete.cpp
+++ b/xpcom/string/src/nsTStringObsolete.cpp
@@ -381,32 +381,32 @@ nsTString_CharT::Mid( self_type& aResult
 
 bool
 nsTString_CharT::SetCharAt( PRUnichar aChar, uint32_t aIndex )
   {
     if (aIndex >= mLength)
       return false;
 
     if (!EnsureMutable())
-      NS_RUNTIMEABORT("OOM");
+      NS_ABORT_OOM(mLength);
 
     mData[aIndex] = CharT(aChar);
     return true;
   }
 
  
   /**
    * nsTString::StripChars,StripChar,StripWhitespace
    */
 
 void
 nsTString_CharT::StripChars( const char* aSet )
   {
     if (!EnsureMutable())
-      NS_RUNTIMEABORT("OOM");
+      NS_ABORT_OOM(mLength);
 
     mLength = nsBufferRoutines<CharT>::strip_chars(mData, mLength, aSet);
   }
 
 void
 nsTString_CharT::StripWhitespace()
   {
     StripChars(kWhitespace);
@@ -416,30 +416,30 @@ nsTString_CharT::StripWhitespace()
   /**
    * nsTString::ReplaceChar,ReplaceSubstring
    */
 
 void
 nsTString_CharT::ReplaceChar( char_type aOldChar, char_type aNewChar )
   {
     if (!EnsureMutable()) // XXX do this lazily?
-      NS_RUNTIMEABORT("OOM");
+      NS_ABORT_OOM(mLength);
 
     for (uint32_t i=0; i<mLength; ++i)
       {
         if (mData[i] == aOldChar)
           mData[i] = aNewChar;
       }
   }
 
 void
 nsTString_CharT::ReplaceChar( const char* aSet, char_type aNewChar )
   {
     if (!EnsureMutable()) // XXX do this lazily?
-      NS_RUNTIMEABORT("OOM");
+      NS_ABORT_OOM(mLength);
 
     char_type* data = mData;
     uint32_t lenRemaining = mLength;
 
     while (lenRemaining)
       {
         int32_t i = ::FindCharInSet(data, lenRemaining, aSet);
         if (i == kNotFound)
--- a/xpcom/string/src/nsTSubstring.cpp
+++ b/xpcom/string/src/nsTSubstring.cpp
@@ -265,17 +265,17 @@ nsTSubstring_CharT::EnsureMutable( size_
 
 // ---------------------------------------------------------------------------
 
   // This version of Assign is optimized for single-character assignment.
 void
 nsTSubstring_CharT::Assign( char_type c )
   {
     if (!ReplacePrep(0, mLength, 1))
-      NS_RUNTIMEABORT("OOM");
+      NS_ABORT_OOM(mLength);
 
     *mData = c;
   }
 
 bool
 nsTSubstring_CharT::Assign( char_type c, const fallible_t& )
   {
     if (!ReplacePrep(0, mLength, 1))
@@ -284,24 +284,24 @@ nsTSubstring_CharT::Assign( char_type c,
     *mData = c;
     return true;
   }
 
 void
 nsTSubstring_CharT::Assign( const char_type* data )
   {
     if (!Assign(data, size_type(-1), fallible_t()))
-      NS_RUNTIMEABORT("OOM");
+      NS_ABORT_OOM(char_traits::length(data));
   }
 
 void
 nsTSubstring_CharT::Assign( const char_type* data, size_type length )
   {
     if (!Assign(data, length, fallible_t()))
-      NS_RUNTIMEABORT("OOM");
+      NS_ABORT_OOM(length);
   }
 
 bool
 nsTSubstring_CharT::Assign( const char_type* data, size_type length, const fallible_t& )
   {
     if (!data)
       {
         Truncate();
@@ -322,17 +322,17 @@ nsTSubstring_CharT::Assign( const char_t
     char_traits::copy(mData, data, length);
     return true;
   }
 
 void
 nsTSubstring_CharT::AssignASCII( const char* data, size_type length )
   {
     if (!AssignASCII(data, length, fallible_t()))
-      NS_RUNTIMEABORT("OOM");
+      NS_ABORT_OOM(length);
   }
 
 bool
 nsTSubstring_CharT::AssignASCII( const char* data, size_type length, const fallible_t& )
   {
     // A Unicode string can't depend on an ASCII string buffer,
     // so this dependence check only applies to CStrings.
 #ifdef CharT_is_char
@@ -348,17 +348,17 @@ nsTSubstring_CharT::AssignASCII( const c
     char_traits::copyASCII(mData, data, length);
     return true;
   }
 
 void
 nsTSubstring_CharT::Assign( const self_type& str )
 {
   if (!Assign(str, fallible_t()))
-    NS_RUNTIMEABORT("OOM");
+    NS_ABORT_OOM(str.Length());
 }
 
 bool
 nsTSubstring_CharT::Assign( const self_type& str, const fallible_t& )
   {
     // |str| could be sharable.  we need to check its flags to know how to
     // deal with it.
 
@@ -393,17 +393,17 @@ nsTSubstring_CharT::Assign( const self_t
     // else, treat this like an ordinary assignment.
     return Assign(str.Data(), str.Length(), fallible_t());
   }
 
 void
 nsTSubstring_CharT::Assign( const substring_tuple_type& tuple )
   {
     if (!Assign(tuple, fallible_t()))
-      NS_RUNTIMEABORT("OOM");
+      NS_ABORT_OOM(tuple.Length());
   }
 
 bool
 nsTSubstring_CharT::Assign( const substring_tuple_type& tuple, const fallible_t& )
   {
     if (tuple.IsDependentOn(mData, mData + mLength))
       {
         // take advantage of sharing here...
@@ -533,17 +533,17 @@ nsTSubstring_CharT::Replace( index_type 
     if (ReplacePrep(cutStart, cutLength, length) && length > 0)
       tuple.WriteTo(mData + cutStart, length);
   }
 
 void
 nsTSubstring_CharT::SetCapacity( size_type capacity )
   {
     if (!SetCapacity(capacity, fallible_t()))
-      NS_RUNTIMEABORT("OOM");
+      NS_ABORT_OOM(capacity);
   }
 
 bool
 nsTSubstring_CharT::SetCapacity( size_type capacity, const fallible_t& )
   {
     // capacity does not include room for the terminating null char
 
     // if our capacity is reduced to zero, then free our buffer.
@@ -704,17 +704,17 @@ nsTSubstring_CharT::FindChar( char_type 
 
 void
 nsTSubstring_CharT::StripChar( char_type aChar, int32_t aOffset )
   {
     if (mLength == 0 || aOffset >= int32_t(mLength))
       return;
 
     if (!EnsureMutable()) // XXX do this lazily?
-      NS_RUNTIMEABORT("OOM");
+      NS_ABORT_OOM(mLength);
 
     // XXX(darin): this code should defer writing until necessary.
 
     char_type* to   = mData + aOffset;
     char_type* from = mData + aOffset;
     char_type* end  = mData + mLength;
 
     while (from < end)
@@ -729,17 +729,17 @@ nsTSubstring_CharT::StripChar( char_type
 
 void
 nsTSubstring_CharT::StripChars( const char_type* aChars, uint32_t aOffset )
   {
     if (aOffset >= uint32_t(mLength))
       return;
 
     if (!EnsureMutable()) // XXX do this lazily?
-      NS_RUNTIMEABORT("OOM");
+      NS_ABORT_OOM(mLength);
 
     // XXX(darin): this code should defer writing until necessary.
 
     char_type* to   = mData + aOffset;
     char_type* from = mData + aOffset;
     char_type* end  = mData + mLength;
 
     while (from < end)