Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/base/nsPresArena.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
 */
7
8
/* arena allocation for the frame tree and closely-related objects */
9
10
#include "nsPresArena.h"
11
12
#include "mozilla/Poison.h"
13
#include "nsDebug.h"
14
#include "nsPrintfCString.h"
15
#include "FrameLayerBuilder.h"
16
#include "mozilla/ArrayUtils.h"
17
#include "mozilla/ComputedStyle.h"
18
#include "mozilla/ComputedStyleInlines.h"
19
#include "nsWindowSizes.h"
20
21
#include <inttypes.h>
22
23
using namespace mozilla;
24
25
nsPresArena::nsPresArena()
26
0
{
27
0
}
28
29
nsPresArena::~nsPresArena()
30
0
{
31
0
  ClearArenaRefPtrs();
32
0
33
#if defined(MOZ_HAVE_MEM_CHECKS)
34
  for (FreeList* entry = mFreeLists; entry != ArrayEnd(mFreeLists); ++entry) {
35
    nsTArray<void*>::index_type len;
36
    while ((len = entry->mEntries.Length())) {
37
      void* result = entry->mEntries.ElementAt(len - 1);
38
      entry->mEntries.RemoveElementAt(len - 1);
39
      MOZ_MAKE_MEM_UNDEFINED(result, entry->mEntrySize);
40
    }
41
  }
42
#endif
43
}
44
45
/* inline */ void
46
nsPresArena::ClearArenaRefPtrWithoutDeregistering(void* aPtr,
47
                                                  ArenaObjectID aObjectID)
48
0
{
49
0
  switch (aObjectID) {
50
0
    // We use ArenaRefPtr<ComputedStyle>, which can be ComputedStyle
51
0
    // or GeckoComputedStyle. GeckoComputedStyle is actually arena managed,
52
0
    // but ComputedStyle isn't.
53
0
    case eArenaObjectID_GeckoComputedStyle:
54
0
      static_cast<ArenaRefPtr<ComputedStyle>*>(aPtr)->ClearWithoutDeregistering();
55
0
      return;
56
0
    default:
57
0
      MOZ_ASSERT(false, "unexpected ArenaObjectID value");
58
0
      break;
59
0
  }
60
0
}
61
62
void
63
nsPresArena::ClearArenaRefPtrs()
64
0
{
65
0
  for (auto iter = mArenaRefPtrs.Iter(); !iter.Done(); iter.Next()) {
66
0
    void* ptr = iter.Key();
67
0
    ArenaObjectID id = iter.UserData();
68
0
    ClearArenaRefPtrWithoutDeregistering(ptr, id);
69
0
  }
70
0
  mArenaRefPtrs.Clear();
71
0
}
72
73
void
74
nsPresArena::ClearArenaRefPtrs(ArenaObjectID aObjectID)
75
0
{
76
0
  for (auto iter = mArenaRefPtrs.Iter(); !iter.Done(); iter.Next()) {
77
0
    void* ptr = iter.Key();
78
0
    ArenaObjectID id = iter.UserData();
79
0
    if (id == aObjectID) {
80
0
      ClearArenaRefPtrWithoutDeregistering(ptr, id);
81
0
      iter.Remove();
82
0
    }
83
0
  }
84
0
}
85
86
void*
87
nsPresArena::Allocate(uint32_t aCode, size_t aSize)
88
0
{
89
0
  MOZ_ASSERT(NS_IsMainThread());
90
0
  MOZ_ASSERT(aSize > 0, "PresArena cannot allocate zero bytes");
91
0
  MOZ_ASSERT(aCode < ArrayLength(mFreeLists));
92
0
93
0
  // We only hand out aligned sizes
94
0
  aSize = mPool.AlignedSize(aSize);
95
0
96
0
  FreeList* list = &mFreeLists[aCode];
97
0
98
0
  nsTArray<void*>::index_type len = list->mEntries.Length();
99
0
  if (list->mEntrySize == 0) {
100
0
    MOZ_ASSERT(len == 0, "list with entries but no recorded size");
101
0
    list->mEntrySize = aSize;
102
0
  } else {
103
0
    MOZ_ASSERT(list->mEntrySize == aSize,
104
0
               "different sizes for same object type code");
105
0
  }
106
0
107
0
  void* result;
108
0
  if (len > 0) {
109
0
    // Remove from the end of the mEntries array to avoid memmoving entries,
110
0
    // and use SetLengthAndRetainStorage to avoid a lot of malloc/free
111
0
    // from ShrinkCapacity on smaller sizes.  500 pointers means the malloc size
112
0
    // for the array is 4096 bytes or more on a 64-bit system.  The next smaller
113
0
    // size is 2048 (with jemalloc), which we consider not worth compacting.
114
0
    result = list->mEntries.ElementAt(len - 1);
115
0
    if (list->mEntries.Capacity() > 500) {
116
0
      list->mEntries.RemoveElementAt(len - 1);
117
0
    } else {
118
0
      list->mEntries.SetLengthAndRetainStorage(len - 1);
119
0
    }
120
#if defined(DEBUG)
121
    {
122
      MOZ_MAKE_MEM_DEFINED(result, list->mEntrySize);
123
      char* p = reinterpret_cast<char*>(result);
124
      char* limit = p + list->mEntrySize;
125
      for (; p < limit; p += sizeof(uintptr_t)) {
126
        uintptr_t val = *reinterpret_cast<uintptr_t*>(p);
127
        if (val != mozPoisonValue()) {
128
          MOZ_ReportAssertionFailure(
129
            nsPrintfCString("PresArena: poison overwritten; "
130
                            "wanted %.16" PRIx64 " "
131
                            "found %.16" PRIx64 " "
132
                            "errors in bits %.16" PRIx64 " ",
133
                            uint64_t(mozPoisonValue()),
134
                            uint64_t(val),
135
                            uint64_t(mozPoisonValue() ^ val)).get(),
136
            __FILE__, __LINE__);
137
          MOZ_CRASH();
138
        }
139
      }
140
    }
141
#endif
142
0
    MOZ_MAKE_MEM_UNDEFINED(result, list->mEntrySize);
143
0
    return result;
144
0
  }
145
0
146
0
  // Allocate a new chunk from the arena
147
0
  list->mEntriesEverAllocated++;
148
0
  return mPool.Allocate(aSize);
149
0
}
150
151
void
152
nsPresArena::Free(uint32_t aCode, void* aPtr)
153
0
{
154
0
  MOZ_ASSERT(NS_IsMainThread());
155
0
  MOZ_ASSERT(aCode < ArrayLength(mFreeLists));
156
0
157
0
  // Try to recycle this entry.
158
0
  FreeList* list = &mFreeLists[aCode];
159
0
  MOZ_ASSERT(list->mEntrySize > 0, "object of this type was never allocated");
160
0
161
0
  mozWritePoison(aPtr, list->mEntrySize);
162
0
163
0
  MOZ_MAKE_MEM_NOACCESS(aPtr, list->mEntrySize);
164
0
  list->mEntries.AppendElement(aPtr);
165
0
}
166
167
void
168
nsPresArena::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const
169
0
{
170
0
  // We do a complicated dance here because we want to measure the
171
0
  // space taken up by the different kinds of objects in the arena,
172
0
  // but we don't have pointers to those objects.  And even if we did,
173
0
  // we wouldn't be able to use mMallocSizeOf on them, since they were
174
0
  // allocated out of malloc'd chunks of memory.  So we compute the
175
0
  // size of the arena as known by malloc and we add up the sizes of
176
0
  // all the objects that we care about.  Subtracting these two
177
0
  // quantities gives us a catch-all "other" number, which includes
178
0
  // slop in the arena itself as well as the size of objects that
179
0
  // we've not measured explicitly.
180
0
181
0
  size_t mallocSize = mPool.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
182
0
183
0
  size_t totalSizeInFreeLists = 0;
184
0
  for (const FreeList* entry = mFreeLists;
185
0
       entry != ArrayEnd(mFreeLists);
186
0
       ++entry) {
187
0
    mallocSize += entry->SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
188
0
189
0
    // Note that we're not measuring the size of the entries on the free
190
0
    // list here.  The free list knows how many objects we've allocated
191
0
    // ever (which includes any objects that may be on the FreeList's
192
0
    // |mEntries| at this point) and we're using that to determine the
193
0
    // total size of objects allocated with a given ID.
194
0
    size_t totalSize = entry->mEntrySize * entry->mEntriesEverAllocated;
195
0
196
0
    switch (entry - mFreeLists) {
197
0
#define FRAME_ID(classname, ...) \
198
0
      case nsQueryFrame::classname##_id: \
199
0
        aSizes.mArenaSizes.NS_ARENA_SIZES_FIELD(classname) += totalSize; \
200
0
        break;
201
0
#define ABSTRACT_FRAME_ID(...)
202
0
#include "nsFrameIdList.h"
203
0
#undef FRAME_ID
204
0
#undef ABSTRACT_FRAME_ID
205
0
      case eArenaObjectID_nsLineBox:
206
0
        aSizes.mArenaSizes.mLineBoxes += totalSize;
207
0
        break;
208
0
      default:
209
0
        continue;
210
0
    }
211
0
212
0
    totalSizeInFreeLists += totalSize;
213
0
  }
214
0
215
0
  aSizes.mLayoutPresShellSize += mallocSize - totalSizeInFreeLists;
216
0
}