summaryrefslogtreecommitdiffstats
path: root/layout/base/nsPresArena.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/base/nsPresArena.cpp')
-rw-r--r--layout/base/nsPresArena.cpp247
1 files changed, 247 insertions, 0 deletions
diff --git a/layout/base/nsPresArena.cpp b/layout/base/nsPresArena.cpp
new file mode 100644
index 000000000..2b1f072df
--- /dev/null
+++ b/layout/base/nsPresArena.cpp
@@ -0,0 +1,247 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=2 sw=2 et tw=78:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/* arena allocation for the frame tree and closely-related objects */
+
+// Even on 32-bit systems, we allocate objects from the frame arena
+// that require 8-byte alignment. The cast to uintptr_t is needed
+// because plarena isn't as careful about mask construction as it
+// ought to be.
+#define ALIGN_SHIFT 3
+#define PL_ARENA_CONST_ALIGN_MASK ((uintptr_t(1) << ALIGN_SHIFT) - 1)
+#include "plarena.h"
+// plarena.h needs to be included first to make it use the above
+// PL_ARENA_CONST_ALIGN_MASK in this file.
+
+#include "nsPresArena.h"
+
+#include "mozilla/Poison.h"
+#include "nsDebug.h"
+#include "nsArenaMemoryStats.h"
+#include "nsPrintfCString.h"
+#include "nsStyleContext.h"
+
+#include <inttypes.h>
+
+using namespace mozilla;
+
+// Size to use for PLArena block allocations.
+static const size_t ARENA_PAGE_SIZE = 8192;
+
+nsPresArena::nsPresArena()
+{
+ PL_INIT_ARENA_POOL(&mPool, "PresArena", ARENA_PAGE_SIZE);
+}
+
+nsPresArena::~nsPresArena()
+{
+ ClearArenaRefPtrs();
+
+#if defined(MOZ_HAVE_MEM_CHECKS)
+ for (auto iter = mFreeLists.Iter(); !iter.Done(); iter.Next()) {
+ FreeList* entry = iter.Get();
+ nsTArray<void*>::index_type len;
+ while ((len = entry->mEntries.Length())) {
+ void* result = entry->mEntries.ElementAt(len - 1);
+ entry->mEntries.RemoveElementAt(len - 1);
+ MOZ_MAKE_MEM_UNDEFINED(result, entry->mEntrySize);
+ }
+ }
+#endif
+
+ PL_FinishArenaPool(&mPool);
+}
+
+/* inline */ void
+nsPresArena::ClearArenaRefPtrWithoutDeregistering(void* aPtr,
+ ArenaObjectID aObjectID)
+{
+ switch (aObjectID) {
+#define PRES_ARENA_OBJECT_WITH_ARENAREFPTR_SUPPORT(name_) \
+ case eArenaObjectID_##name_: \
+ static_cast<ArenaRefPtr<name_>*>(aPtr)->ClearWithoutDeregistering(); \
+ return;
+#include "nsPresArenaObjectList.h"
+#undef PRES_ARENA_OBJECT_WITH_ARENAREFPTR_SUPPORT
+ default:
+ break;
+ }
+ switch (aObjectID) {
+#define PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT(name_) \
+ case eArenaObjectID_##name_: \
+ MOZ_ASSERT(false, #name_ " must be declared in nsPresArenaObjectList.h "\
+ "with PRES_ARENA_OBJECT_SUPPORTS_ARENAREFPTR"); \
+ break;
+#include "nsPresArenaObjectList.h"
+#undef PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT
+ default:
+ MOZ_ASSERT(false, "unexpected ArenaObjectID value");
+ break;
+ }
+}
+
+void
+nsPresArena::ClearArenaRefPtrs()
+{
+ for (auto iter = mArenaRefPtrs.Iter(); !iter.Done(); iter.Next()) {
+ void* ptr = iter.Key();
+ ArenaObjectID id = iter.UserData();
+ ClearArenaRefPtrWithoutDeregistering(ptr, id);
+ }
+ mArenaRefPtrs.Clear();
+}
+
+void
+nsPresArena::ClearArenaRefPtrs(ArenaObjectID aObjectID)
+{
+ for (auto iter = mArenaRefPtrs.Iter(); !iter.Done(); iter.Next()) {
+ void* ptr = iter.Key();
+ ArenaObjectID id = iter.UserData();
+ if (id == aObjectID) {
+ ClearArenaRefPtrWithoutDeregistering(ptr, id);
+ iter.Remove();
+ }
+ }
+}
+
+void*
+nsPresArena::Allocate(uint32_t aCode, size_t aSize)
+{
+ MOZ_ASSERT(aSize > 0, "PresArena cannot allocate zero bytes");
+
+ // We only hand out aligned sizes
+ aSize = PL_ARENA_ALIGN(&mPool, aSize);
+
+ // If there is no free-list entry for this type already, we have
+ // to create one now, to record its size.
+ FreeList* list = mFreeLists.PutEntry(aCode);
+
+ nsTArray<void*>::index_type len = list->mEntries.Length();
+ if (list->mEntrySize == 0) {
+ MOZ_ASSERT(len == 0, "list with entries but no recorded size");
+ list->mEntrySize = aSize;
+ } else {
+ MOZ_ASSERT(list->mEntrySize == aSize,
+ "different sizes for same object type code");
+ }
+
+ void* result;
+ if (len > 0) {
+ // LIFO behavior for best cache utilization
+ result = list->mEntries.ElementAt(len - 1);
+ list->mEntries.RemoveElementAt(len - 1);
+#if defined(DEBUG)
+ {
+ MOZ_MAKE_MEM_DEFINED(result, list->mEntrySize);
+ char* p = reinterpret_cast<char*>(result);
+ char* limit = p + list->mEntrySize;
+ for (; p < limit; p += sizeof(uintptr_t)) {
+ uintptr_t val = *reinterpret_cast<uintptr_t*>(p);
+ if (val != mozPoisonValue()) {
+ MOZ_ReportAssertionFailure(
+ nsPrintfCString("PresArena: poison overwritten; "
+ "wanted %.16" PRIx64 " "
+ "found %.16" PRIx64 " "
+ "errors in bits %.16" PRIx64 " ",
+ uint64_t(mozPoisonValue()),
+ uint64_t(val),
+ uint64_t(mozPoisonValue() ^ val)).get(),
+ __FILE__, __LINE__);
+ MOZ_CRASH();
+ }
+ }
+ }
+#endif
+ MOZ_MAKE_MEM_UNDEFINED(result, list->mEntrySize);
+ return result;
+ }
+
+ // Allocate a new chunk from the arena
+ list->mEntriesEverAllocated++;
+ PL_ARENA_ALLOCATE(result, &mPool, aSize);
+ if (!result) {
+ NS_ABORT_OOM(aSize);
+ }
+ return result;
+}
+
+void
+nsPresArena::Free(uint32_t aCode, void* aPtr)
+{
+ // Try to recycle this entry.
+ FreeList* list = mFreeLists.GetEntry(aCode);
+ MOZ_ASSERT(list, "no free list for pres arena object");
+ MOZ_ASSERT(list->mEntrySize > 0, "PresArena cannot free zero bytes");
+
+ mozWritePoison(aPtr, list->mEntrySize);
+
+ MOZ_MAKE_MEM_NOACCESS(aPtr, list->mEntrySize);
+ list->mEntries.AppendElement(aPtr);
+}
+
+void
+nsPresArena::AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ nsArenaMemoryStats* aArenaStats)
+{
+ // We do a complicated dance here because we want to measure the
+ // space taken up by the different kinds of objects in the arena,
+ // but we don't have pointers to those objects. And even if we did,
+ // we wouldn't be able to use aMallocSizeOf on them, since they were
+ // allocated out of malloc'd chunks of memory. So we compute the
+ // size of the arena as known by malloc and we add up the sizes of
+ // all the objects that we care about. Subtracting these two
+ // quantities gives us a catch-all "other" number, which includes
+ // slop in the arena itself as well as the size of objects that
+ // we've not measured explicitly.
+
+ size_t mallocSize = PL_SizeOfArenaPoolExcludingPool(&mPool, aMallocSizeOf);
+ mallocSize += mFreeLists.SizeOfExcludingThis(aMallocSizeOf);
+
+ size_t totalSizeInFreeLists = 0;
+ for (auto iter = mFreeLists.Iter(); !iter.Done(); iter.Next()) {
+ FreeList* entry = iter.Get();
+
+ // Note that we're not measuring the size of the entries on the free
+ // list here. The free list knows how many objects we've allocated
+ // ever (which includes any objects that may be on the FreeList's
+ // |mEntries| at this point) and we're using that to determine the
+ // total size of objects allocated with a given ID.
+ size_t totalSize = entry->mEntrySize * entry->mEntriesEverAllocated;
+ size_t* p;
+
+ switch (NS_PTR_TO_INT32(entry->mKey)) {
+#define FRAME_ID(classname) \
+ case nsQueryFrame::classname##_id: \
+ p = &aArenaStats->FRAME_ID_STAT_FIELD(classname); \
+ break;
+#include "nsFrameIdList.h"
+#undef FRAME_ID
+ case eArenaObjectID_nsLineBox:
+ p = &aArenaStats->mLineBoxes;
+ break;
+ case eArenaObjectID_nsRuleNode:
+ p = &aArenaStats->mRuleNodes;
+ break;
+ case eArenaObjectID_nsStyleContext:
+ p = &aArenaStats->mStyleContexts;
+ break;
+#define STYLE_STRUCT(name_, checkdata_cb_) \
+ case eArenaObjectID_nsStyle##name_:
+#include "nsStyleStructList.h"
+#undef STYLE_STRUCT
+ p = &aArenaStats->mStyleStructs;
+ break;
+ default:
+ continue;
+ }
+
+ *p += totalSize;
+ totalSizeInFreeLists += totalSize;
+ }
+
+ aArenaStats->mOther += mallocSize - totalSizeInFreeLists;
+}