blob: 3caacc0dfb5b1beb5d6e304e3e9da9c7ccfaea92 [file] [log] [blame]
Austin Schuh745610d2015-09-06 18:19:50 -07001// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
2// Copyright 2009 Google Inc. All Rights Reserved.
3// Author: fikes@google.com (Andrew Fikes)
4//
5// Use of this source code is governed by a BSD-style license that can
6// be found in the LICENSE file.
7
8#include "config_for_unittests.h"
Brian Silverman20350ac2021-11-17 18:19:55 -08009
10#include <stdio.h>
11
12#include <memory>
13
Austin Schuh745610d2015-09-06 18:19:50 -070014#include "page_heap.h"
15#include "system-alloc.h"
Austin Schuh745610d2015-09-06 18:19:50 -070016#include "base/logging.h"
17#include "common.h"
18
19DECLARE_int64(tcmalloc_heap_limit_mb);
20
21namespace {
22
23// The system will only release memory if the block size is equal or hight than
24// system page size.
25static bool HaveSystemRelease =
26 TCMalloc_SystemRelease(
27 TCMalloc_SystemAlloc(getpagesize(), NULL, 0), getpagesize());
28
29static void CheckStats(const tcmalloc::PageHeap* ph,
30 uint64_t system_pages,
31 uint64_t free_pages,
32 uint64_t unmapped_pages) {
33 tcmalloc::PageHeap::Stats stats = ph->stats();
34
35 if (!HaveSystemRelease) {
36 free_pages += unmapped_pages;
37 unmapped_pages = 0;
38 }
39
40 EXPECT_EQ(system_pages, stats.system_bytes >> kPageShift);
41 EXPECT_EQ(free_pages, stats.free_bytes >> kPageShift);
42 EXPECT_EQ(unmapped_pages, stats.unmapped_bytes >> kPageShift);
43}
44
45static void TestPageHeap_Stats() {
Brian Silverman20350ac2021-11-17 18:19:55 -080046 std::unique_ptr<tcmalloc::PageHeap> ph(new tcmalloc::PageHeap());
Austin Schuh745610d2015-09-06 18:19:50 -070047
48 // Empty page heap
Brian Silverman20350ac2021-11-17 18:19:55 -080049 CheckStats(ph.get(), 0, 0, 0);
Austin Schuh745610d2015-09-06 18:19:50 -070050
51 // Allocate a span 's1'
52 tcmalloc::Span* s1 = ph->New(256);
Brian Silverman20350ac2021-11-17 18:19:55 -080053 CheckStats(ph.get(), 256, 0, 0);
Austin Schuh745610d2015-09-06 18:19:50 -070054
55 // Split span 's1' into 's1', 's2'. Delete 's2'
56 tcmalloc::Span* s2 = ph->Split(s1, 128);
57 ph->Delete(s2);
Brian Silverman20350ac2021-11-17 18:19:55 -080058 CheckStats(ph.get(), 256, 128, 0);
Austin Schuh745610d2015-09-06 18:19:50 -070059
60 // Unmap deleted span 's2'
61 ph->ReleaseAtLeastNPages(1);
Brian Silverman20350ac2021-11-17 18:19:55 -080062 CheckStats(ph.get(), 256, 0, 128);
Austin Schuh745610d2015-09-06 18:19:50 -070063
64 // Delete span 's1'
65 ph->Delete(s1);
Brian Silverman20350ac2021-11-17 18:19:55 -080066 CheckStats(ph.get(), 256, 128, 128);
67}
Austin Schuh745610d2015-09-06 18:19:50 -070068
Brian Silverman20350ac2021-11-17 18:19:55 -080069// The number of kMaxPages-sized Spans we will allocate and free during the
70// tests.
71// We will also do twice this many kMaxPages/2-sized ones.
72static constexpr int kNumberMaxPagesSpans = 10;
73
74// Allocates all the last-level page tables we will need. Doing this before
75// calculating the base heap usage is necessary, because otherwise if any of
76// these are allocated during the main test it will throw the heap usage
77// calculations off and cause the test to fail.
78static void AllocateAllPageTables() {
79 // Make a separate PageHeap from the main test so the test can start without
80 // any pages in the lists.
81 std::unique_ptr<tcmalloc::PageHeap> ph(new tcmalloc::PageHeap());
82 tcmalloc::Span *spans[kNumberMaxPagesSpans * 2];
83 for (int i = 0; i < kNumberMaxPagesSpans; ++i) {
84 spans[i] = ph->New(kMaxPages);
85 EXPECT_NE(spans[i], NULL);
86 }
87 for (int i = 0; i < kNumberMaxPagesSpans; ++i) {
88 ph->Delete(spans[i]);
89 }
90 for (int i = 0; i < kNumberMaxPagesSpans * 2; ++i) {
91 spans[i] = ph->New(kMaxPages >> 1);
92 EXPECT_NE(spans[i], NULL);
93 }
94 for (int i = 0; i < kNumberMaxPagesSpans * 2; ++i) {
95 ph->Delete(spans[i]);
96 }
Austin Schuh745610d2015-09-06 18:19:50 -070097}
98
99static void TestPageHeap_Limit() {
Brian Silverman20350ac2021-11-17 18:19:55 -0800100 AllocateAllPageTables();
101
102 std::unique_ptr<tcmalloc::PageHeap> ph(new tcmalloc::PageHeap());
Austin Schuh745610d2015-09-06 18:19:50 -0700103
104 CHECK_EQ(kMaxPages, 1 << (20 - kPageShift));
105
106 // We do not know much is taken from the system for other purposes,
107 // so we detect the proper limit:
108 {
109 FLAGS_tcmalloc_heap_limit_mb = 1;
110 tcmalloc::Span* s = NULL;
111 while((s = ph->New(kMaxPages)) == NULL) {
112 FLAGS_tcmalloc_heap_limit_mb++;
113 }
Brian Silverman20350ac2021-11-17 18:19:55 -0800114 FLAGS_tcmalloc_heap_limit_mb += kNumberMaxPagesSpans - 1;
Austin Schuh745610d2015-09-06 18:19:50 -0700115 ph->Delete(s);
116 // We are [10, 11) mb from the limit now.
117 }
118
119 // Test AllocLarge and GrowHeap first:
120 {
Brian Silverman20350ac2021-11-17 18:19:55 -0800121 tcmalloc::Span * spans[kNumberMaxPagesSpans];
122 for (int i=0; i<kNumberMaxPagesSpans; ++i) {
Austin Schuh745610d2015-09-06 18:19:50 -0700123 spans[i] = ph->New(kMaxPages);
124 EXPECT_NE(spans[i], NULL);
125 }
126 EXPECT_EQ(ph->New(kMaxPages), NULL);
127
Brian Silverman20350ac2021-11-17 18:19:55 -0800128 for (int i=0; i<kNumberMaxPagesSpans; i += 2) {
Austin Schuh745610d2015-09-06 18:19:50 -0700129 ph->Delete(spans[i]);
130 }
131
Brian Silverman20350ac2021-11-17 18:19:55 -0800132 tcmalloc::Span *defragmented =
133 ph->New(kNumberMaxPagesSpans / 2 * kMaxPages);
Austin Schuh745610d2015-09-06 18:19:50 -0700134
135 if (HaveSystemRelease) {
136 // EnsureLimit should release deleted normal spans
137 EXPECT_NE(defragmented, NULL);
138 EXPECT_TRUE(ph->CheckExpensive());
139 ph->Delete(defragmented);
140 }
141 else
142 {
143 EXPECT_EQ(defragmented, NULL);
144 EXPECT_TRUE(ph->CheckExpensive());
145 }
146
Brian Silverman20350ac2021-11-17 18:19:55 -0800147 for (int i=1; i<kNumberMaxPagesSpans; i += 2) {
Austin Schuh745610d2015-09-06 18:19:50 -0700148 ph->Delete(spans[i]);
149 }
150 }
151
152 // Once again, testing small lists this time (twice smaller spans):
153 {
Brian Silverman20350ac2021-11-17 18:19:55 -0800154 tcmalloc::Span * spans[kNumberMaxPagesSpans * 2];
155 for (int i=0; i<kNumberMaxPagesSpans * 2; ++i) {
Austin Schuh745610d2015-09-06 18:19:50 -0700156 spans[i] = ph->New(kMaxPages >> 1);
157 EXPECT_NE(spans[i], NULL);
158 }
159 // one more half size allocation may be possible:
160 tcmalloc::Span * lastHalf = ph->New(kMaxPages >> 1);
161 EXPECT_EQ(ph->New(kMaxPages >> 1), NULL);
162
Brian Silverman20350ac2021-11-17 18:19:55 -0800163 for (int i=0; i<kNumberMaxPagesSpans * 2; i += 2) {
Austin Schuh745610d2015-09-06 18:19:50 -0700164 ph->Delete(spans[i]);
165 }
166
Brian Silverman20350ac2021-11-17 18:19:55 -0800167 for (Length len = kMaxPages >> 2;
168 len < kNumberMaxPagesSpans / 2 * kMaxPages; len = len << 1) {
Austin Schuh745610d2015-09-06 18:19:50 -0700169 if(len <= kMaxPages >> 1 || HaveSystemRelease) {
170 tcmalloc::Span *s = ph->New(len);
171 EXPECT_NE(s, NULL);
172 ph->Delete(s);
173 }
174 }
175
176 EXPECT_TRUE(ph->CheckExpensive());
177
Brian Silverman20350ac2021-11-17 18:19:55 -0800178 for (int i=1; i<kNumberMaxPagesSpans * 2; i += 2) {
Austin Schuh745610d2015-09-06 18:19:50 -0700179 ph->Delete(spans[i]);
180 }
181
182 if (lastHalf != NULL) {
183 ph->Delete(lastHalf);
184 }
185 }
Austin Schuh745610d2015-09-06 18:19:50 -0700186}
187
188} // namespace
189
190int main(int argc, char **argv) {
191 TestPageHeap_Stats();
192 TestPageHeap_Limit();
193 printf("PASS\n");
194 // on windows as part of library destructors we call getenv which
195 // calls malloc which fails due to our exhausted heap limit. It then
196 // causes fancy stack overflow because log message we're printing
197 // for failed allocation somehow cause malloc calls too
198 //
199 // To keep us out of trouble we just drop malloc limit
200 FLAGS_tcmalloc_heap_limit_mb = 0;
201 return 0;
202}