Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/base/StackArena.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 file,
5
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "StackArena.h"
8
#include "nsAlgorithm.h"
9
#include "nsDebug.h"
10
11
namespace mozilla {
12
13
// A block of memory that the stack will chop up and hand out.
14
struct StackBlock {
15
  // Subtract sizeof(StackBlock*) to give space for the |mNext| field.
16
  static const size_t MAX_USABLE_SIZE = 4096 - sizeof(StackBlock*);
17
18
  // A block of memory.
19
  char mBlock[MAX_USABLE_SIZE];
20
21
  // Another block of memory that would only be created if our stack
22
  // overflowed.
23
  StackBlock* mNext;
24
25
0
  StackBlock() : mNext(nullptr) { }
26
0
  ~StackBlock() { }
27
};
28
29
static_assert(sizeof(StackBlock) == 4096, "StackBlock must be 4096 bytes");
30
31
// We hold an array of marks. A push pushes a mark on the stack.
32
// A pop pops it off.
33
struct StackMark {
34
  // The block of memory from which we are currently handing out chunks.
35
  StackBlock* mBlock;
36
37
  // Our current position in the block.
38
  size_t mPos;
39
};
40
41
StackArena* AutoStackArena::gStackArena;
42
43
StackArena::StackArena()
44
0
{
45
0
  mMarkLength = 0;
46
0
  mMarks = nullptr;
47
0
48
0
  // Allocate our stack memory.
49
0
  mBlocks = new StackBlock();
50
0
  mCurBlock = mBlocks;
51
0
52
0
  mStackTop = 0;
53
0
  mPos = 0;
54
0
}
55
56
StackArena::~StackArena()
57
0
{
58
0
  // Free up our data.
59
0
  delete [] mMarks;
60
0
  while (mBlocks) {
61
0
    StackBlock* toDelete = mBlocks;
62
0
    mBlocks = mBlocks->mNext;
63
0
    delete toDelete;
64
0
  }
65
0
}
66
67
size_t
68
StackArena::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
69
0
{
70
0
  size_t n = 0;
71
0
  StackBlock *block = mBlocks;
72
0
  while (block) {
73
0
    n += aMallocSizeOf(block);
74
0
    block = block->mNext;
75
0
  }
76
0
  n += aMallocSizeOf(mMarks);
77
0
  return n;
78
0
}
79
80
static const int STACK_ARENA_MARK_INCREMENT = 50;
81
82
void
83
StackArena::Push()
84
0
{
85
0
  // Resize the mark array if we overrun it.  Failure to allocate the
86
0
  // mark array is not fatal; we just won't free to that mark.  This
87
0
  // allows callers not to worry about error checking.
88
0
  if (mStackTop >= mMarkLength) {
89
0
    uint32_t newLength = mStackTop + STACK_ARENA_MARK_INCREMENT;
90
0
    StackMark* newMarks = new StackMark[newLength];
91
0
    if (newMarks) {
92
0
      if (mMarkLength) {
93
0
        memcpy(newMarks, mMarks, sizeof(StackMark)*mMarkLength);
94
0
      }
95
0
      // Fill in any marks that we couldn't allocate during a prior call
96
0
      // to Push().
97
0
      for (; mMarkLength < mStackTop; ++mMarkLength) {
98
0
        MOZ_ASSERT_UNREACHABLE("should only hit this on out-of-memory");
99
0
        newMarks[mMarkLength].mBlock = mCurBlock;
100
0
        newMarks[mMarkLength].mPos = mPos;
101
0
      }
102
0
      delete [] mMarks;
103
0
      mMarks = newMarks;
104
0
      mMarkLength = newLength;
105
0
    }
106
0
  }
107
0
108
0
  // Set a mark at the top (if we can).
109
0
  NS_ASSERTION(mStackTop < mMarkLength, "out of memory");
110
0
  if (mStackTop < mMarkLength) {
111
0
    mMarks[mStackTop].mBlock = mCurBlock;
112
0
    mMarks[mStackTop].mPos = mPos;
113
0
  }
114
0
115
0
  mStackTop++;
116
0
}
117
118
void*
119
StackArena::Allocate(size_t aSize)
120
0
{
121
0
  NS_ASSERTION(mStackTop > 0, "Allocate called without Push");
122
0
123
0
  // Align to a multiple of 8.
124
0
  aSize = NS_ROUNDUP<size_t>(aSize, 8);
125
0
126
0
  // On stack overflow, grab another block.
127
0
  if (mPos + aSize >= StackBlock::MAX_USABLE_SIZE) {
128
0
    NS_ASSERTION(aSize <= StackBlock::MAX_USABLE_SIZE,
129
0
                 "Requested memory is greater that our block size!!");
130
0
    if (mCurBlock->mNext == nullptr) {
131
0
      mCurBlock->mNext = new StackBlock();
132
0
    }
133
0
134
0
    mCurBlock = mCurBlock->mNext;
135
0
    mPos = 0;
136
0
  }
137
0
138
0
  // Return the chunk they need.
139
0
  void *result = mCurBlock->mBlock + mPos;
140
0
  mPos += aSize;
141
0
142
0
  return result;
143
0
}
144
145
void
146
StackArena::Pop()
147
0
{
148
0
  // Pop off the mark.
149
0
  NS_ASSERTION(mStackTop > 0, "unmatched pop");
150
0
  mStackTop--;
151
0
152
0
  if (mStackTop >= mMarkLength) {
153
0
    // We couldn't allocate the marks array at the time of the push, so
154
0
    // we don't know where we're freeing to.
155
0
    MOZ_ASSERT_UNREACHABLE("out of memory");
156
0
    if (mStackTop == 0) {
157
0
      // But we do know if we've completely pushed the stack.
158
0
      mCurBlock = mBlocks;
159
0
      mPos = 0;
160
0
    }
161
0
    return;
162
0
  }
163
0
164
#ifdef DEBUG
165
  // Mark the "freed" memory with 0xdd to help with debugging of memory
166
  // allocation problems.
167
  {
168
    StackBlock *block = mMarks[mStackTop].mBlock, *block_end = mCurBlock;
169
    size_t pos = mMarks[mStackTop].mPos;
170
    for (; block != block_end; block = block->mNext, pos = 0) {
171
      memset(block->mBlock + pos, 0xdd, sizeof(block->mBlock) - pos);
172
    }
173
    memset(block->mBlock + pos, 0xdd, mPos - pos);
174
  }
175
#endif
176
177
0
  mCurBlock = mMarks[mStackTop].mBlock;
178
0
  mPos      = mMarks[mStackTop].mPos;
179
0
}
180
181
} // namespace mozilla