Bug 938794 - Annotate OOM size as infallible string or data structures abort, r=froydnj
authorBenjamin Smedberg <benjamin@smedbergs.us>
Mon, 25 Nov 2013 15:06:17 -0500
changeset 157535 3bee396bb681e8818345ba7059e185692ebe7faa
parent 157534 ecf073f6394b871b4dc24d460c5266cd09956d9f
child 157536 3ad9666da2bc686d422b6d4d98590a946b843a18
push id25716
push userkwierso@gmail.com
push dateWed, 27 Nov 2013 01:32:11 +0000
treeherdermozilla-central@d822990ba9ee [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs938794
milestone28.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 938794 - Annotate OOM size as infallible string or data structures abort, r=froydnj
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
@@ -587,10 +587,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
@@ -489,59 +489,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, uint32_t& aHash)
 {
   MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
   EnsureTableExists();
   AtomTableKey key(aString, aLength, aHash);
   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, uint32_t& aHash)
 {
   MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
   EnsureTableExists();
   AtomTableKey key(aString, aLength, aHash);
   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
@@ -381,16 +381,25 @@ inline bool NS_warn_if_impl(bool conditi
   #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));
   }
 
   /**
@@ -400,17 +400,17 @@ nsTHashtable<EntryType>::Init(uint32_t a
     s_MatchEntry,
     EntryType::ALLOW_MEMMOVE ? ::PL_DHashMoveEntryStub : s_CopyEntry,
     s_ClearEntry,
     ::PL_DHashFinalizeStub,
     s_InitEntry
   };
 
   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);
         }
@@ -512,31 +512,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
@@ -267,17 +267,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))
@@ -286,24 +286,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();
@@ -324,17 +324,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
@@ -350,17 +350,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.
 
@@ -395,17 +395,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...
@@ -535,17 +535,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.
@@ -706,17 +706,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)
@@ -731,17 +731,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)